aqualink 2.11.8 → 2.11.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/handlers/autoplay.js +103 -93
- package/build/structures/Aqua.js +130 -306
- package/build/structures/Connection.js +1 -22
- package/build/structures/Player.js +258 -294
- package/build/structures/Rest.js +0 -23
- package/package.json +1 -1
|
@@ -1,111 +1,121 @@
|
|
|
1
|
-
const https = require('https')
|
|
2
|
-
|
|
1
|
+
const https = require('https')
|
|
2
|
+
|
|
3
|
+
const AGENT_CONFIG = {
|
|
4
|
+
keepAlive: true,
|
|
5
|
+
maxSockets: 5,
|
|
6
|
+
maxFreeSockets: 2,
|
|
7
|
+
timeout: 8000,
|
|
8
|
+
freeSocketTimeout: 4000
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const agent = new https.Agent(AGENT_CONFIG)
|
|
3
12
|
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
});
|
|
13
|
+
const SC_LINK_RE = /<a\s+itemprop="url"\s+href="(\/[^"]+)"/g
|
|
14
|
+
const MAX_REDIRECTS = 3
|
|
15
|
+
const MAX_RESPONSE_BYTES = 5 * 1024 * 1024 // 5 MB
|
|
16
|
+
const MAX_SC_LINKS = 50
|
|
17
|
+
const MAX_SP_RESULTS = 5
|
|
18
|
+
const DEFAULT_TIMEOUT_MS = 8000
|
|
11
19
|
|
|
20
|
+
const fastFetch = (url, depth = 0) => new Promise((resolve, reject) => {
|
|
21
|
+
if (depth > MAX_REDIRECTS) return reject(new Error('Too many redirects'))
|
|
12
22
|
|
|
13
|
-
const
|
|
23
|
+
const req = https.get(url, { agent, timeout: DEFAULT_TIMEOUT_MS }, res => {
|
|
24
|
+
const { statusCode, headers } = res
|
|
14
25
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
[arr[i], arr[j]] = [arr[j], arr[i]];
|
|
26
|
+
if (statusCode >= 300 && statusCode < 400 && headers.location) {
|
|
27
|
+
res.resume()
|
|
28
|
+
return fastFetch(new URL(headers.location, url).href, depth + 1).then(resolve, reject)
|
|
19
29
|
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
return new Promise((resolve, reject) => {
|
|
25
|
-
const req = https.get(url, { ...options, agent }, (res) => {
|
|
26
|
-
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
|
27
|
-
res.resume();
|
|
28
|
-
return fastFetch(new URL(res.headers.location, url).href, options)
|
|
29
|
-
.then(resolve, reject);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
if (res.statusCode !== 200) {
|
|
33
|
-
res.resume();
|
|
34
|
-
return reject(new Error(`HTTP ${res.statusCode}`));
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const chunks = [];
|
|
38
|
-
res.on('data', chunk => chunks.push(chunk));
|
|
39
|
-
res.on('end', () => resolve(Buffer.concat(chunks).toString()));
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
req.on('error', reject);
|
|
43
|
-
req.setTimeout(8000, () => req.destroy(new Error('Timeout')));
|
|
44
|
-
});
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
const soundAutoPlay = async (baseUrl) => {
|
|
48
|
-
try {
|
|
49
|
-
const html = await fastFetch(`${baseUrl}/recommended`);
|
|
50
|
-
|
|
51
|
-
const links = [];
|
|
52
|
-
let match;
|
|
53
|
-
while ((match = SOUNDCLOUD_REGEX.exec(html)) && links.length < 50) {
|
|
54
|
-
links.push(`https://soundcloud.com${match[1]}`);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
if (!links.length) throw new Error("No tracks found");
|
|
58
|
-
|
|
59
|
-
return shuffleArray(links);
|
|
60
|
-
} catch (err) {
|
|
61
|
-
console.error("SoundCloud error:", err.message);
|
|
62
|
-
return [];
|
|
30
|
+
|
|
31
|
+
if (statusCode !== 200) {
|
|
32
|
+
res.resume()
|
|
33
|
+
return reject(new Error(`HTTP ${statusCode}`))
|
|
63
34
|
}
|
|
64
|
-
};
|
|
65
35
|
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
const response = await player.aqua.resolve({
|
|
77
|
-
query: seedQuery,
|
|
78
|
-
source: 'spsearch',
|
|
79
|
-
requester
|
|
36
|
+
const chunks = []
|
|
37
|
+
let received = 0
|
|
38
|
+
|
|
39
|
+
res.on('data', chunk => {
|
|
40
|
+
received += chunk.length
|
|
41
|
+
if (received > MAX_RESPONSE_BYTES) {
|
|
42
|
+
req.destroy(new Error('Response too large'))
|
|
43
|
+
return
|
|
44
|
+
}
|
|
45
|
+
chunks.push(chunk)
|
|
80
46
|
})
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
const { identifier } = track
|
|
89
|
-
if (seenIds.has(identifier)) continue
|
|
90
|
-
seenIds.add(identifier)
|
|
91
|
-
track.pluginInfo = {
|
|
92
|
-
...(track.pluginInfo || {}),
|
|
93
|
-
clientData: { fromAutoplay: true }
|
|
47
|
+
|
|
48
|
+
res.on('end', () => {
|
|
49
|
+
try {
|
|
50
|
+
const buf = Buffer.concat(chunks)
|
|
51
|
+
resolve(buf.toString())
|
|
52
|
+
} catch (err) {
|
|
53
|
+
reject(err)
|
|
94
54
|
}
|
|
95
|
-
|
|
96
|
-
|
|
55
|
+
})
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
req.on('error', reject)
|
|
59
|
+
req.setTimeout(DEFAULT_TIMEOUT_MS, () => req.destroy(new Error('Timeout')))
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
const shuffleInPlace = arr => {
|
|
63
|
+
for (let i = arr.length - 1; i > 0; i--) {
|
|
64
|
+
const j = Math.random() * (i + 1) | 0
|
|
65
|
+
const tmp = arr[i]
|
|
66
|
+
arr[i] = arr[j]
|
|
67
|
+
arr[j] = tmp
|
|
68
|
+
}
|
|
69
|
+
return arr
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const scAutoPlay = async baseUrl => {
|
|
73
|
+
try {
|
|
74
|
+
const html = await fastFetch(`${baseUrl}/recommended`)
|
|
75
|
+
const links = []
|
|
76
|
+
for (const m of html.matchAll(SC_LINK_RE)) {
|
|
77
|
+
if (!m[1]) continue
|
|
78
|
+
links.push(`https://soundcloud.com${m[1]}`)
|
|
79
|
+
if (links.length >= MAX_SC_LINKS) break
|
|
97
80
|
}
|
|
81
|
+
return links.length ? shuffleInPlace(links) : []
|
|
82
|
+
} catch (err) {
|
|
83
|
+
console.error('scAutoPlay error:', err?.message || err)
|
|
84
|
+
return []
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const spAutoPlay = async (seed, player, requester, excludedIds = []) => {
|
|
89
|
+
try {
|
|
90
|
+
if (!seed?.trackId) return null
|
|
91
|
+
|
|
92
|
+
const seedQuery = `seed_tracks=${seed.trackId}${seed.artistIds ? `&seed_artists=${seed.artistIds}` : ''}`
|
|
93
|
+
const res = await player.aqua.resolve({ query: seedQuery, source: 'spsearch', requester })
|
|
98
94
|
|
|
99
|
-
|
|
95
|
+
const candidates = res?.tracks || []
|
|
96
|
+
if (!candidates.length) return null
|
|
100
97
|
|
|
98
|
+
const seen = new Set(excludedIds)
|
|
99
|
+
const prevId = player.current?.identifier
|
|
100
|
+
if (prevId) seen.add(prevId)
|
|
101
|
+
|
|
102
|
+
const out = []
|
|
103
|
+
for (const t of candidates) {
|
|
104
|
+
if (seen.has(t.identifier)) continue
|
|
105
|
+
seen.add(t.identifier)
|
|
106
|
+
t.pluginInfo = { ...(t.pluginInfo || {}), clientData: { fromAutoplay: true } }
|
|
107
|
+
out.push(t)
|
|
108
|
+
if (out.length === MAX_SP_RESULTS) break
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return out.length ? out : null
|
|
101
112
|
} catch (err) {
|
|
102
|
-
console.error('
|
|
113
|
+
console.error('spAutoPlay error:', err)
|
|
103
114
|
return null
|
|
104
115
|
}
|
|
105
116
|
}
|
|
106
117
|
|
|
107
|
-
|
|
108
118
|
module.exports = {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
}
|
|
119
|
+
scAutoPlay,
|
|
120
|
+
spAutoPlay
|
|
121
|
+
}
|