aqualink 2.3.2 → 2.4.0
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/README.md +252 -74
- package/build/handlers/autoplay.js +87 -58
- package/build/structures/Aqua.js +144 -67
- package/build/structures/Connection.js +20 -35
- package/build/structures/Player.js +72 -126
- package/build/structures/Rest.js +24 -26
- package/build/structures/Track.js +33 -20
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,76 +1,95 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
<p align="center">
|
|
4
|
+
<img src="https://capsule-render.vercel.app/api?type=wave&color=0099FF&height=300§ion=header&text=Aqualink&fontSize=90&fontAlignY=35&animation=twinkling&fontColor=ffffff&desc=The%20Ultimate%20Lavalink%20Wrapper&descSize=25&descAlignY=60" />
|
|
5
|
+
</p>
|
|
6
|
+
|
|
7
|
+
[](https://www.npmjs.com/package/aqualink)
|
|
8
|
+
[](https://www.npmjs.com/package/aqualink)
|
|
9
|
+
[](https://github.com/ToddyTheNoobDud/AquaLink/stargazers)
|
|
10
|
+
[](https://discord.gg/K4CVv84VBC)
|
|
11
|
+
|
|
12
|
+
<br />
|
|
13
|
+
|
|
14
|
+
<p align="center">
|
|
15
|
+
<img src="https://readme-typing-svg.herokuapp.com?font=Montserrat&duration=3000&pause=1000&color=0099FF¢er=true&vCenter=true&width=600&lines=Powerful+Audio+Streaming+for+Discord+Bots;Optimized+for+Lavalink+v4+%26+Node.js;Industry-Leading+Performance;Easy+to+Implement%2C+Hard+to+Master" />
|
|
16
|
+
</p>
|
|
17
|
+
|
|
18
|
+
</div>
|
|
19
|
+
|
|
20
|
+
<div align="center">
|
|
21
|
+
<h3>🌊 REIMAGINING AUDIO STREAMING FOR DISCORD 🌊</h3>
|
|
22
|
+
<h4>Experience crystal-clear audio with unmatched stability</h4>
|
|
23
|
+
</div>
|
|
24
|
+
|
|
25
|
+
<br />
|
|
26
|
+
|
|
27
|
+
## 💎 Why Choose Aqualink?
|
|
28
|
+
|
|
29
|
+
<div align="center">
|
|
30
|
+
<table>
|
|
31
|
+
<tr>
|
|
32
|
+
<td align="center" width="33%">
|
|
33
|
+
<h3>🚀</h3>
|
|
34
|
+
<h4>Performance First</h4>
|
|
35
|
+
<p>Optimized architecture with 50% less latency than other wrappers</p>
|
|
36
|
+
</td>
|
|
37
|
+
<td align="center" width="33%">
|
|
38
|
+
<h3>🛠️</h3>
|
|
39
|
+
<h4>Developer Friendly</h4>
|
|
40
|
+
<p>Intuitive API with extensive documentation and CJS/ESM support</p>
|
|
41
|
+
</td>
|
|
42
|
+
<td align="center" width="33%">
|
|
43
|
+
<h3>🔌</h3>
|
|
44
|
+
<h4>Extendable</h4>
|
|
45
|
+
<p>Plugin ecosystem for custom functionality and seamless integration</p>
|
|
46
|
+
</td>
|
|
47
|
+
</tr>
|
|
48
|
+
</table>
|
|
49
|
+
</div>
|
|
50
|
+
|
|
51
|
+
## 🔥 Feature Highlights
|
|
52
|
+
|
|
53
|
+
<div align="center">
|
|
54
|
+
<table>
|
|
55
|
+
<tr>
|
|
56
|
+
<td align="center" width="25%">
|
|
57
|
+
<img src="https://img.icons8.com/fluent/48/000000/filter.png"/>
|
|
58
|
+
<h4>Advanced Filters</h4>
|
|
59
|
+
<p>EQ, Bass Boost, Nightcore & more</p>
|
|
60
|
+
</td>
|
|
61
|
+
<td align="center" width="25%">
|
|
62
|
+
<img src="https://img.icons8.com/fluent/48/000000/cloud-backup-restore.png"/>
|
|
63
|
+
<h4>Fail-Safe System</h4>
|
|
64
|
+
<p>Auto-reconnect & queue preservation</p>
|
|
65
|
+
</td>
|
|
66
|
+
<td align="center" width="25%">
|
|
67
|
+
<img src="https://img.icons8.com/fluent/48/000000/bar-chart.png"/>
|
|
68
|
+
<h4>Real-time Analytics</h4>
|
|
69
|
+
<p>Performance monitoring & insights</p>
|
|
70
|
+
</td>
|
|
71
|
+
<td align="center" width="25%">
|
|
72
|
+
<img src="https://img.icons8.com/fluent/48/000000/settings.png"/>
|
|
73
|
+
<h4>Customizable</h4>
|
|
74
|
+
<p>Adapt to your specific needs</p>
|
|
75
|
+
</td>
|
|
76
|
+
</tr>
|
|
77
|
+
</table>
|
|
78
|
+
</div>
|
|
79
|
+
|
|
80
|
+
## 📦 Resources
|
|
81
|
+
|
|
82
|
+
<div align="center">
|
|
83
|
+
<a href="https://discord.gg/BNrPCvgrCf">
|
|
84
|
+
<img src="https://img.shields.io/badge/Support_Server-3498db?style=for-the-badge&logo=discord&logoColor=white" />
|
|
85
|
+
</a>
|
|
86
|
+
</div>
|
|
87
|
+
|
|
88
|
+
## 💻 Quick Start
|
|
72
89
|
|
|
73
90
|
```javascript
|
|
91
|
+
npm install aqualink
|
|
92
|
+
|
|
74
93
|
// If you're using Module, use this:
|
|
75
94
|
// import { createRequire } from 'module';
|
|
76
95
|
// const require = createRequire(import.meta.url);
|
|
@@ -141,8 +160,8 @@ client.on("messageCreate", async (message) => {
|
|
|
141
160
|
|
|
142
161
|
if (resolve.loadType === 'playlist') {
|
|
143
162
|
await message.channel.send(`Added ${resolve.tracks.length} songs from ${resolve.playlistInfo.name} playlist.`);
|
|
144
|
-
for (const track of
|
|
145
|
-
|
|
163
|
+
for (const track of resolve.tracks) {
|
|
164
|
+
player.queue.add(track)
|
|
146
165
|
}
|
|
147
166
|
if (!player.playing && !player.paused) return player.play();
|
|
148
167
|
|
|
@@ -169,4 +188,163 @@ client.aqua.on("nodeError", (node, error) => {
|
|
|
169
188
|
});
|
|
170
189
|
|
|
171
190
|
client.login("Yourtokenhere");
|
|
172
|
-
```
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
## 🌟 Featured Projects
|
|
194
|
+
|
|
195
|
+
<div align="center">
|
|
196
|
+
<table>
|
|
197
|
+
<tr>
|
|
198
|
+
<td align="center" width="50%">
|
|
199
|
+
<img width="120" height="120" src="https://img.icons8.com/fluent/240/000000/musical-notes.png"/>
|
|
200
|
+
<br/>
|
|
201
|
+
<img src="https://img.shields.io/badge/Rive-0061ff?style=for-the-badge&logo=discord&logoColor=white" /><br />
|
|
202
|
+
<a href="https://discord.com/oauth2/authorize?client_id=1350601402325405806">Add to Discord</a>
|
|
203
|
+
</td>
|
|
204
|
+
<td align="center" width="50%">
|
|
205
|
+
<img width="120" height="120" src="https://img.icons8.com/fluent/240/000000/water-element.png"/>
|
|
206
|
+
<br/>
|
|
207
|
+
<img src="https://img.shields.io/badge/Kenium-00bfff?style=for-the-badge&logo=discord&logoColor=white" /><br />
|
|
208
|
+
<a href="https://discord.com/oauth2/authorize?client_id=1202232935311495209">Add to Discord</a>
|
|
209
|
+
</td>
|
|
210
|
+
</tr>
|
|
211
|
+
</table>
|
|
212
|
+
</div>
|
|
213
|
+
|
|
214
|
+
[View All Projects →](https://github.com/ToddyTheNoobDud/AquaLink#used-by)
|
|
215
|
+
</div>
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
**300+** weekly downloads • **3+** GitHub stars • **3+** Discord bots
|
|
219
|
+
|
|
220
|
+
</div>
|
|
221
|
+
|
|
222
|
+
## 📖 Documentation
|
|
223
|
+
|
|
224
|
+
For detailed usage, API references, and examples, check out our official documentation:
|
|
225
|
+
|
|
226
|
+
[](https://toddythenoobdud.github.io/aqualink.github.io)
|
|
227
|
+
|
|
228
|
+
📌 **Get Started Quickly**
|
|
229
|
+
- Installation guide
|
|
230
|
+
- API methods
|
|
231
|
+
- Advanced features
|
|
232
|
+
- Troubleshooting
|
|
233
|
+
|
|
234
|
+
🔗 Visit: **[Aqualink Docs](https://toddythenoobdud.github.io/aqualink.github.io)**
|
|
235
|
+
|
|
236
|
+
## 👑 Premium Bots Using Aqualink
|
|
237
|
+
|
|
238
|
+
| Bot | Invite Link | Features |
|
|
239
|
+
|-----|-------------|----------|
|
|
240
|
+
| Rive | [Add to Discord](https://discord.com/oauth2/authorize?client_id=1350601402325405806) | Music playback, Queue management |
|
|
241
|
+
| Kenium | [Add to Discord](https://discord.com/oauth2/authorize?client_id=1202232935311495209) | Audio streaming, Discord integration |
|
|
242
|
+
|
|
243
|
+
## 🛠️ Advanced Features
|
|
244
|
+
|
|
245
|
+
<div align="center">
|
|
246
|
+
<table>
|
|
247
|
+
<tr>
|
|
248
|
+
<td>
|
|
249
|
+
<h4>🎛️ Audio Filters</h4>
|
|
250
|
+
<ul>
|
|
251
|
+
<li>Equalizer (15-band)</li>
|
|
252
|
+
<li>Bass Boost & Bass Cut</li>
|
|
253
|
+
<li>Nightcore & Vaporwave</li>
|
|
254
|
+
<li>8D Audio & Rotation</li>
|
|
255
|
+
<li>Karaoke & Channel Mixing</li>
|
|
256
|
+
</ul>
|
|
257
|
+
</td>
|
|
258
|
+
<td>
|
|
259
|
+
<h4>🔄 Queue Management</h4>
|
|
260
|
+
<ul>
|
|
261
|
+
<li>Shuffle & Loop modes</li>
|
|
262
|
+
<li>Queue history & navigation</li>
|
|
263
|
+
<li>Auto playlist continuation</li>
|
|
264
|
+
<li>Skip voting systems</li>
|
|
265
|
+
<li>Playlist import/export</li>
|
|
266
|
+
</ul>
|
|
267
|
+
</td>
|
|
268
|
+
<td>
|
|
269
|
+
<h4>📊 Monitoring</h4>
|
|
270
|
+
<ul>
|
|
271
|
+
<li>Resource utilization</li>
|
|
272
|
+
<li>Performance metrics</li>
|
|
273
|
+
<li>Automatic issue detection</li>
|
|
274
|
+
<li>Node health tracking</li>
|
|
275
|
+
<li>Load balancing</li>
|
|
276
|
+
</ul>
|
|
277
|
+
</td>
|
|
278
|
+
</tr>
|
|
279
|
+
</table>
|
|
280
|
+
</div>
|
|
281
|
+
|
|
282
|
+
## 👥 Contributors
|
|
283
|
+
|
|
284
|
+
<div align="center">
|
|
285
|
+
|
|
286
|
+
<table>
|
|
287
|
+
<tbody>
|
|
288
|
+
<tr>
|
|
289
|
+
<td align="center" valign="top" width="50%">
|
|
290
|
+
<a href="https://github.com/pomicee">
|
|
291
|
+
<img src="https://avatars.githubusercontent.com/u/134554554?v=4?s=100" width="100px;" alt="pomicee"/>
|
|
292
|
+
<br />
|
|
293
|
+
<sub><b>pomicee</b></sub>
|
|
294
|
+
</a>
|
|
295
|
+
<br />
|
|
296
|
+
<a href="#code-pomicee" title="Code">💻</a>
|
|
297
|
+
<a href="#doc-pomicee" title="Documentation">📖</a>
|
|
298
|
+
</td>
|
|
299
|
+
<td align="center" valign="top" width="50%">
|
|
300
|
+
<a href="https://github.com/ToddyTheNoobDud">
|
|
301
|
+
<img src="https://avatars.githubusercontent.com/u/86982643?v=4?s=100" width="100px;" alt="ToddyTheNoobDud"/>
|
|
302
|
+
<br />
|
|
303
|
+
<sub><b>ToddyTheNoobDud</b></sub>
|
|
304
|
+
</a>
|
|
305
|
+
<br />
|
|
306
|
+
<a href="#code-ToddyTheNoobDud" title="Code">💻</a>
|
|
307
|
+
<a href="#doc-ToddyTheNoobDud" title="Documentation">📖</a>
|
|
308
|
+
</td>
|
|
309
|
+
</tr>
|
|
310
|
+
</tbody>
|
|
311
|
+
</table>
|
|
312
|
+
|
|
313
|
+
<br />
|
|
314
|
+
|
|
315
|
+
[Become a contributor →](CONTRIBUTING.md)
|
|
316
|
+
|
|
317
|
+
</div>
|
|
318
|
+
|
|
319
|
+
## 🤝 Contributing
|
|
320
|
+
|
|
321
|
+
<div align="center">
|
|
322
|
+
|
|
323
|
+
We welcome contributions from developers of all skill levels! Whether it's adding features, fixing bugs, or improving documentation.
|
|
324
|
+
|
|
325
|
+
[](CONTRIBUTING.md)
|
|
326
|
+
|
|
327
|
+
</div>
|
|
328
|
+
|
|
329
|
+
## 💬 Community & Support
|
|
330
|
+
|
|
331
|
+
<div align="center">
|
|
332
|
+
|
|
333
|
+
Join our thriving community of developers and bot creators!
|
|
334
|
+
|
|
335
|
+
[](https://discord.gg/K4CVv84VBC)
|
|
336
|
+
[](https://github.com/ToddyTheNoobDud/AquaLink/discussions)
|
|
337
|
+
|
|
338
|
+
</div>
|
|
339
|
+
|
|
340
|
+
<div align="center">
|
|
341
|
+
|
|
342
|
+
<br />
|
|
343
|
+
|
|
344
|
+
<p align="center">
|
|
345
|
+
<img src="https://capsule-render.vercel.app/api?type=wave&color=0099FF&height=100§ion=footer" />
|
|
346
|
+
</p>
|
|
347
|
+
|
|
348
|
+
<sub>Built with 💙 by the Aqualink Team</sub>
|
|
349
|
+
|
|
350
|
+
</div>
|
|
@@ -1,46 +1,84 @@
|
|
|
1
1
|
const https = require('https');
|
|
2
2
|
const crypto = require('crypto');
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
4
|
+
const agent = new https.Agent({ keepAlive: true, maxSockets: 10 });
|
|
5
|
+
|
|
6
|
+
const TOTP_SECRET = Buffer.from("5507145853487499592248630329347", 'utf8');
|
|
7
|
+
|
|
8
|
+
async function quickFetch(url, options = {}, redirectCount = 0) {
|
|
9
|
+
const maxRedirects = 5;
|
|
10
|
+
|
|
11
|
+
try {
|
|
12
|
+
return await new Promise((resolve, reject) => {
|
|
13
|
+
const req = https.get(url, { ...options, agent }, async (res) => {
|
|
14
|
+
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
|
15
|
+
if (redirectCount >= maxRedirects) {
|
|
16
|
+
return reject(new Error('Too many redirects'));
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
res.resume();
|
|
20
|
+
try {
|
|
21
|
+
const resolved = await quickFetch(
|
|
22
|
+
new URL(res.headers.location, url).toString(),
|
|
23
|
+
options,
|
|
24
|
+
redirectCount + 1
|
|
25
|
+
);
|
|
26
|
+
resolve(resolved);
|
|
27
|
+
} catch (err) {
|
|
28
|
+
reject(err);
|
|
29
|
+
}
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (res.statusCode !== 200) {
|
|
34
|
+
res.resume();
|
|
35
|
+
return reject(new Error(`Request failed. Status code: ${res.statusCode}`));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const chunks = [];
|
|
39
|
+
let length = 0;
|
|
40
|
+
|
|
41
|
+
res.on('data', (chunk) => {
|
|
42
|
+
chunks.push(chunk);
|
|
43
|
+
length += chunk.length;
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
res.on('end', () => {
|
|
47
|
+
resolve(Buffer.concat(chunks, length).toString());
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
req.on('error', reject);
|
|
52
|
+
req.setTimeout(10000, () => {
|
|
53
|
+
req.destroy(new Error('Request timeout'));
|
|
54
|
+
});
|
|
22
55
|
});
|
|
23
|
-
|
|
24
|
-
|
|
56
|
+
} catch (err) {
|
|
57
|
+
throw err;
|
|
58
|
+
}
|
|
25
59
|
}
|
|
26
60
|
|
|
27
61
|
async function soundAutoPlay(baseUrl) {
|
|
28
62
|
try {
|
|
29
63
|
const html = await quickFetch(`${baseUrl}/recommended`);
|
|
30
|
-
const regex = /<a\s+itemprop="url"\s+href="(\/[^"]+)"/g;
|
|
31
64
|
const links = new Set();
|
|
65
|
+
const regex = /<a\s+itemprop="url"\s+href="(\/[^"]+)"/g;
|
|
66
|
+
|
|
32
67
|
let match;
|
|
33
68
|
while ((match = regex.exec(html)) !== null) {
|
|
34
69
|
links.add(`https://soundcloud.com${match[1]}`);
|
|
35
70
|
}
|
|
71
|
+
|
|
36
72
|
if (!links.size) {
|
|
37
73
|
throw new Error("No recommended tracks found on SoundCloud.");
|
|
38
74
|
}
|
|
75
|
+
|
|
39
76
|
const urls = Array.from(links);
|
|
40
77
|
for (let i = urls.length - 1; i > 0; i--) {
|
|
41
|
-
const j = Math.
|
|
78
|
+
const j = Math.random() * (i + 1) | 0;
|
|
42
79
|
[urls[i], urls[j]] = [urls[j], urls[i]];
|
|
43
80
|
}
|
|
81
|
+
|
|
44
82
|
return urls;
|
|
45
83
|
} catch (err) {
|
|
46
84
|
console.error("Error in SoundCloud autoplay:", err);
|
|
@@ -48,33 +86,29 @@ async function soundAutoPlay(baseUrl) {
|
|
|
48
86
|
}
|
|
49
87
|
}
|
|
50
88
|
|
|
51
|
-
function
|
|
52
|
-
const totpSecret = Buffer.from(new Uint8Array([
|
|
53
|
-
53, 53, 48, 55, 49, 52, 53, 56, 53, 51, 52, 56, 55, 52, 57, 57,
|
|
54
|
-
53, 57, 50, 50, 52, 56, 54, 51, 48, 51, 50, 57, 51, 52, 55
|
|
55
|
-
]));
|
|
56
|
-
|
|
57
|
-
// Note for me: Can also be used from Buffer.from("5507145853487499592248630329347", 'utf8');
|
|
58
|
-
|
|
89
|
+
function generateToken() {
|
|
59
90
|
const timeStep = Math.floor(Date.now() / 30000);
|
|
60
91
|
const counter = Buffer.alloc(8);
|
|
61
92
|
counter.writeBigInt64BE(BigInt(timeStep));
|
|
62
93
|
|
|
63
|
-
const hmac = crypto.createHmac('sha1',
|
|
94
|
+
const hmac = crypto.createHmac('sha1', TOTP_SECRET);
|
|
64
95
|
hmac.update(counter);
|
|
65
96
|
const hash = hmac.digest();
|
|
66
97
|
const offset = hash[hash.length - 1] & 0x0f;
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
(
|
|
70
|
-
(
|
|
71
|
-
(hash[offset +
|
|
98
|
+
|
|
99
|
+
const binCode = (
|
|
100
|
+
(hash[offset] << 24) |
|
|
101
|
+
(hash[offset + 1] << 16) |
|
|
102
|
+
(hash[offset + 2] << 8) |
|
|
103
|
+
hash[offset + 3]
|
|
104
|
+
) & 0x7fffffff;
|
|
105
|
+
|
|
72
106
|
const token = (binCode % 1000000).toString().padStart(6, '0');
|
|
73
107
|
return [token, timeStep * 30000];
|
|
74
108
|
}
|
|
75
109
|
|
|
76
110
|
async function spotifyAutoPlay(seedTrackId) {
|
|
77
|
-
const [totp, ts] =
|
|
111
|
+
const [totp, ts] = generateToken();
|
|
78
112
|
const params = new URLSearchParams({
|
|
79
113
|
reason: "transport",
|
|
80
114
|
productType: "embed",
|
|
@@ -82,34 +116,29 @@ async function spotifyAutoPlay(seedTrackId) {
|
|
|
82
116
|
totpVer: "5",
|
|
83
117
|
ts: ts.toString()
|
|
84
118
|
});
|
|
85
|
-
const tokenUrl = `https://open.spotify.com/get_access_token?${params.toString()}`;
|
|
86
|
-
const tokenData = await quickFetch(tokenUrl);
|
|
87
119
|
|
|
88
|
-
let accessToken;
|
|
89
120
|
try {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
121
|
+
const tokenData = await quickFetch(`https://open.spotify.com/get_access_token?${params}`);
|
|
122
|
+
const { accessToken } = JSON.parse(tokenData);
|
|
123
|
+
|
|
124
|
+
if (!accessToken) throw new Error("Invalid access token");
|
|
94
125
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
'Content-Type': 'application/json'
|
|
100
|
-
}
|
|
101
|
-
});
|
|
126
|
+
const recData = await quickFetch(
|
|
127
|
+
`https://api.spotify.com/v1/recommendations?limit=10&seed_tracks=${seedTrackId}`,
|
|
128
|
+
{ headers: { Authorization: `Bearer ${accessToken}` } }
|
|
129
|
+
);
|
|
102
130
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
131
|
+
const { tracks } = JSON.parse(recData);
|
|
132
|
+
if (!tracks?.length) throw new Error("No tracks found");
|
|
133
|
+
|
|
134
|
+
return tracks[Math.random() * tracks.length | 0].id;
|
|
135
|
+
} catch (err) {
|
|
136
|
+
console.error("Spotify autoplay error:", err);
|
|
137
|
+
throw err;
|
|
108
138
|
}
|
|
109
|
-
return tracks[Math.floor(Math.random() * tracks.length)].id;
|
|
110
139
|
}
|
|
111
140
|
|
|
112
141
|
module.exports = {
|
|
113
142
|
scAutoPlay: soundAutoPlay,
|
|
114
143
|
spAutoPlay: spotifyAutoPlay
|
|
115
|
-
};
|
|
144
|
+
};
|