magmastream 2.9.0-dev.35 → 2.9.0-dev.37
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/dist/index.d.ts +680 -168
- package/dist/index.js +6 -1
- package/dist/statestorage/JsonQueue.js +436 -0
- package/dist/{structures/Queue.js → statestorage/MemoryQueue.js} +71 -16
- package/dist/{structures → statestorage}/RedisQueue.js +107 -2
- package/dist/structures/Enums.js +19 -1
- package/dist/structures/Manager.js +211 -68
- package/dist/structures/Node.js +15 -9
- package/dist/structures/Player.js +18 -10
- package/dist/structures/Plugin.js +4 -1
- package/dist/structures/Rest.js +2 -1
- package/dist/structures/Utils.js +208 -318
- package/dist/wrappers/seyfert.js +43 -0
- package/package.json +11 -10
package/dist/structures/Utils.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Structure = exports.AutoPlayUtils = exports.TrackUtils = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
|
+
/* eslint-disable @typescript-eslint/no-require-imports */
|
|
5
6
|
const axios_1 = tslib_1.__importDefault(require("axios"));
|
|
6
7
|
const jsdom_1 = require("jsdom");
|
|
7
8
|
const Enums_1 = require("./Enums");
|
|
@@ -167,6 +168,10 @@ class AutoPlayUtils {
|
|
|
167
168
|
throw new Error("No available nodes.");
|
|
168
169
|
}
|
|
169
170
|
const apiKey = this.manager.options.lastFmApiKey;
|
|
171
|
+
// Check if Last.fm API is available
|
|
172
|
+
if (apiKey) {
|
|
173
|
+
return await this.getRecommendedTracksFromLastFm(track, apiKey);
|
|
174
|
+
}
|
|
170
175
|
const enabledSources = node.info.sourceManagers;
|
|
171
176
|
const autoPlaySearchPlatforms = this.manager.options.autoPlaySearchPlatforms;
|
|
172
177
|
// Iterate over autoplay platforms in order of priority
|
|
@@ -179,10 +184,6 @@ class AutoPlayUtils {
|
|
|
179
184
|
}
|
|
180
185
|
}
|
|
181
186
|
}
|
|
182
|
-
// Check if Last.fm API is available
|
|
183
|
-
if (apiKey) {
|
|
184
|
-
return await this.getRecommendedTracksFromLastFm(track, apiKey);
|
|
185
|
-
}
|
|
186
187
|
return [];
|
|
187
188
|
}
|
|
188
189
|
/**
|
|
@@ -203,22 +204,7 @@ class AutoPlayUtils {
|
|
|
203
204
|
return [];
|
|
204
205
|
}
|
|
205
206
|
const randomTrack = response.data.toptracks.track[Math.floor(Math.random() * response.data.toptracks.track.length)];
|
|
206
|
-
const
|
|
207
|
-
if (TrackUtils.isErrorOrEmptySearchResult(searchResult)) {
|
|
208
|
-
return [];
|
|
209
|
-
}
|
|
210
|
-
let resolvedTracks;
|
|
211
|
-
switch (searchResult.loadType) {
|
|
212
|
-
case Enums_1.LoadTypes.Playlist:
|
|
213
|
-
resolvedTracks = searchResult.playlist.tracks;
|
|
214
|
-
break;
|
|
215
|
-
case Enums_1.LoadTypes.Track:
|
|
216
|
-
case Enums_1.LoadTypes.Search:
|
|
217
|
-
resolvedTracks = searchResult.tracks;
|
|
218
|
-
break;
|
|
219
|
-
default:
|
|
220
|
-
return [];
|
|
221
|
-
}
|
|
207
|
+
const resolvedTracks = await this.resolveTracksFromQuery(`${randomTrack.artist.name} - ${randomTrack.name}`, this.manager.options.defaultSearchPlatform, track.requester);
|
|
222
208
|
if (!resolvedTracks.length)
|
|
223
209
|
return [];
|
|
224
210
|
return resolvedTracks;
|
|
@@ -251,22 +237,7 @@ class AutoPlayUtils {
|
|
|
251
237
|
return [];
|
|
252
238
|
}
|
|
253
239
|
const randomTrack = retryResponse.data.toptracks.track[Math.floor(Math.random() * retryResponse.data.toptracks.track.length)];
|
|
254
|
-
const
|
|
255
|
-
if (TrackUtils.isErrorOrEmptySearchResult(searchResult)) {
|
|
256
|
-
return [];
|
|
257
|
-
}
|
|
258
|
-
let resolvedTracks;
|
|
259
|
-
switch (searchResult.loadType) {
|
|
260
|
-
case Enums_1.LoadTypes.Playlist:
|
|
261
|
-
resolvedTracks = searchResult.playlist.tracks;
|
|
262
|
-
break;
|
|
263
|
-
case Enums_1.LoadTypes.Track:
|
|
264
|
-
case Enums_1.LoadTypes.Search:
|
|
265
|
-
resolvedTracks = searchResult.tracks;
|
|
266
|
-
break;
|
|
267
|
-
default:
|
|
268
|
-
return [];
|
|
269
|
-
}
|
|
240
|
+
const resolvedTracks = await this.resolveTracksFromQuery(`${randomTrack.artist.name} - ${randomTrack.name}`, this.manager.options.defaultSearchPlatform, track.requester);
|
|
270
241
|
if (!resolvedTracks.length)
|
|
271
242
|
return [];
|
|
272
243
|
const filteredTracks = resolvedTracks.filter((t) => t.uri !== track.uri);
|
|
@@ -279,22 +250,7 @@ class AutoPlayUtils {
|
|
|
279
250
|
if (!randomTrack) {
|
|
280
251
|
return [];
|
|
281
252
|
}
|
|
282
|
-
const
|
|
283
|
-
if (TrackUtils.isErrorOrEmptySearchResult(searchResult)) {
|
|
284
|
-
return [];
|
|
285
|
-
}
|
|
286
|
-
let resolvedTracks;
|
|
287
|
-
switch (searchResult.loadType) {
|
|
288
|
-
case Enums_1.LoadTypes.Playlist:
|
|
289
|
-
resolvedTracks = searchResult.playlist.tracks;
|
|
290
|
-
break;
|
|
291
|
-
case Enums_1.LoadTypes.Track:
|
|
292
|
-
case Enums_1.LoadTypes.Search:
|
|
293
|
-
resolvedTracks = searchResult.tracks;
|
|
294
|
-
break;
|
|
295
|
-
default:
|
|
296
|
-
return [];
|
|
297
|
-
}
|
|
253
|
+
const resolvedTracks = await this.resolveTracksFromQuery(`${randomTrack.artist.name} - ${randomTrack.name}`, this.manager.options.defaultSearchPlatform, track.requester);
|
|
298
254
|
if (!resolvedTracks.length)
|
|
299
255
|
return [];
|
|
300
256
|
return resolvedTracks;
|
|
@@ -308,285 +264,219 @@ class AutoPlayUtils {
|
|
|
308
264
|
static async getRecommendedTracksFromSource(track, platform) {
|
|
309
265
|
const requester = track.requester;
|
|
310
266
|
switch (platform) {
|
|
311
|
-
case Enums_1.AutoPlayPlatform.Spotify:
|
|
312
|
-
{
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
let resolvedTrack;
|
|
318
|
-
switch (res.loadType) {
|
|
319
|
-
case Enums_1.LoadTypes.Playlist:
|
|
320
|
-
resolvedTrack = res.playlist.tracks[0];
|
|
321
|
-
break;
|
|
322
|
-
case Enums_1.LoadTypes.Track:
|
|
323
|
-
case Enums_1.LoadTypes.Search:
|
|
324
|
-
resolvedTrack = res.tracks[0];
|
|
325
|
-
break;
|
|
326
|
-
default:
|
|
327
|
-
return [];
|
|
328
|
-
}
|
|
329
|
-
if (!resolvedTrack)
|
|
330
|
-
return [];
|
|
331
|
-
track = resolvedTrack;
|
|
332
|
-
}
|
|
333
|
-
const extractSpotifyArtistID = (url) => {
|
|
334
|
-
const regex = /https:\/\/open\.spotify\.com\/artist\/([a-zA-Z0-9]+)/;
|
|
335
|
-
const match = url.match(regex);
|
|
336
|
-
return match ? match[1] : null;
|
|
337
|
-
};
|
|
338
|
-
const identifier = `sprec:seed_artists=${extractSpotifyArtistID(track.pluginInfo.artistUrl)}&seed_tracks=${track.identifier}`;
|
|
339
|
-
const recommendedResult = (await this.manager.useableNode.rest.get(`/v4/loadtracks?identifier=${encodeURIComponent(identifier)}`));
|
|
340
|
-
const tracks = this.buildTracksFromResponse(recommendedResult, requester);
|
|
341
|
-
return tracks;
|
|
267
|
+
case Enums_1.AutoPlayPlatform.Spotify: {
|
|
268
|
+
if (!track.uri.includes("spotify")) {
|
|
269
|
+
const resolvedTrack = await this.resolveFirstTrackFromQuery(`${track.author} - ${track.title}`, Enums_1.SearchPlatform.Spotify, requester);
|
|
270
|
+
if (!resolvedTrack)
|
|
271
|
+
return [];
|
|
272
|
+
track = resolvedTrack;
|
|
342
273
|
}
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
default:
|
|
360
|
-
return [];
|
|
361
|
-
}
|
|
362
|
-
if (!resolvedTrack)
|
|
363
|
-
return [];
|
|
364
|
-
track = resolvedTrack;
|
|
365
|
-
}
|
|
366
|
-
const identifier = `dzrec:${track.identifier}`;
|
|
367
|
-
const recommendedResult = (await this.manager.useableNode.rest.get(`/v4/loadtracks?identifier=${encodeURIComponent(identifier)}`));
|
|
368
|
-
const tracks = this.buildTracksFromResponse(recommendedResult, requester);
|
|
369
|
-
return tracks;
|
|
274
|
+
const extractSpotifyArtistID = (url) => {
|
|
275
|
+
const regex = /https:\/\/open\.spotify\.com\/artist\/([a-zA-Z0-9]+)/;
|
|
276
|
+
const match = url.match(regex);
|
|
277
|
+
return match ? match[1] : null;
|
|
278
|
+
};
|
|
279
|
+
const identifier = `sprec:seed_artists=${extractSpotifyArtistID(track.pluginInfo.artistUrl)}&seed_tracks=${track.identifier}`;
|
|
280
|
+
const recommendedResult = (await this.manager.useableNode.rest.get(`/v4/loadtracks?identifier=${encodeURIComponent(identifier)}`));
|
|
281
|
+
const tracks = this.buildTracksFromResponse(recommendedResult, requester);
|
|
282
|
+
return tracks;
|
|
283
|
+
}
|
|
284
|
+
case Enums_1.AutoPlayPlatform.Deezer: {
|
|
285
|
+
if (!track.uri.includes("deezer")) {
|
|
286
|
+
const resolvedTrack = await this.resolveFirstTrackFromQuery(`${track.author} - ${track.title}`, Enums_1.SearchPlatform.Deezer, requester);
|
|
287
|
+
if (!resolvedTrack)
|
|
288
|
+
return [];
|
|
289
|
+
track = resolvedTrack;
|
|
370
290
|
}
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
case Enums_1.LoadTypes.Playlist:
|
|
381
|
-
resolvedTrack = res.playlist.tracks[0];
|
|
382
|
-
break;
|
|
383
|
-
case Enums_1.LoadTypes.Track:
|
|
384
|
-
case Enums_1.LoadTypes.Search:
|
|
385
|
-
resolvedTrack = res.tracks[0];
|
|
386
|
-
break;
|
|
387
|
-
default:
|
|
388
|
-
return [];
|
|
389
|
-
}
|
|
390
|
-
if (!resolvedTrack)
|
|
391
|
-
return [];
|
|
392
|
-
track = resolvedTrack;
|
|
393
|
-
}
|
|
394
|
-
try {
|
|
395
|
-
const recommendedRes = await axios_1.default.get(`${track.uri}/recommended`).catch((err) => {
|
|
396
|
-
console.error(`[AutoPlay] Failed to fetch SoundCloud recommendations. Status: ${err.response?.status || "Unknown"}`, err.message);
|
|
397
|
-
return null;
|
|
398
|
-
});
|
|
399
|
-
if (!recommendedRes) {
|
|
400
|
-
return [];
|
|
401
|
-
}
|
|
402
|
-
const html = recommendedRes.data;
|
|
403
|
-
const dom = new jsdom_1.JSDOM(html);
|
|
404
|
-
const document = dom.window.document;
|
|
405
|
-
const secondNoscript = document.querySelectorAll("noscript")[1];
|
|
406
|
-
const sectionElement = secondNoscript.querySelector("section");
|
|
407
|
-
const articleElements = sectionElement.querySelectorAll("article");
|
|
408
|
-
if (!articleElements || articleElements.length === 0) {
|
|
409
|
-
return [];
|
|
410
|
-
}
|
|
411
|
-
const urls = Array.from(articleElements)
|
|
412
|
-
.map((articleElement) => {
|
|
413
|
-
const h2Element = articleElement.querySelector('h2[itemprop="name"]');
|
|
414
|
-
const aElement = h2Element?.querySelector('a[itemprop="url"]');
|
|
415
|
-
return aElement ? `https://soundcloud.com${aElement.getAttribute("href")}` : null;
|
|
416
|
-
})
|
|
417
|
-
.filter(Boolean);
|
|
418
|
-
if (!urls.length) {
|
|
419
|
-
return [];
|
|
420
|
-
}
|
|
421
|
-
const randomUrl = urls[Math.floor(Math.random() * urls.length)];
|
|
422
|
-
const res = await this.manager.search({ query: randomUrl, source: Enums_1.SearchPlatform.SoundCloud }, requester);
|
|
423
|
-
let resolvedTrack;
|
|
424
|
-
switch (res.loadType) {
|
|
425
|
-
case Enums_1.LoadTypes.Playlist:
|
|
426
|
-
resolvedTrack = res.playlist.tracks[0];
|
|
427
|
-
break;
|
|
428
|
-
case Enums_1.LoadTypes.Track:
|
|
429
|
-
case Enums_1.LoadTypes.Search:
|
|
430
|
-
resolvedTrack = res.tracks[0];
|
|
431
|
-
break;
|
|
432
|
-
default:
|
|
433
|
-
return [];
|
|
434
|
-
}
|
|
435
|
-
if (!resolvedTrack)
|
|
436
|
-
return [];
|
|
437
|
-
return [resolvedTrack];
|
|
438
|
-
}
|
|
439
|
-
catch (error) {
|
|
440
|
-
console.error("[AutoPlay] Error occurred while fetching soundcloud recommendations:", error);
|
|
291
|
+
const identifier = `dzrec:${track.identifier}`;
|
|
292
|
+
const recommendedResult = (await this.manager.useableNode.rest.get(`/v4/loadtracks?identifier=${encodeURIComponent(identifier)}`));
|
|
293
|
+
const tracks = this.buildTracksFromResponse(recommendedResult, requester);
|
|
294
|
+
return tracks;
|
|
295
|
+
}
|
|
296
|
+
case Enums_1.AutoPlayPlatform.SoundCloud: {
|
|
297
|
+
if (!track.uri.includes("soundcloud")) {
|
|
298
|
+
const resolvedTrack = await this.resolveFirstTrackFromQuery(`${track.author} - ${track.title}`, Enums_1.SearchPlatform.SoundCloud, requester);
|
|
299
|
+
if (!resolvedTrack)
|
|
441
300
|
return [];
|
|
442
|
-
|
|
301
|
+
track = resolvedTrack;
|
|
443
302
|
}
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
if (
|
|
450
|
-
videoID = track.uri.split("=").pop();
|
|
451
|
-
}
|
|
452
|
-
else {
|
|
453
|
-
const searchResult = await this.manager.search({ query: `${track.author} - ${track.title}`, source: Enums_1.SearchPlatform.YouTube }, requester);
|
|
454
|
-
if (TrackUtils.isErrorOrEmptySearchResult(searchResult)) {
|
|
455
|
-
return [];
|
|
456
|
-
}
|
|
457
|
-
let resolvedTrack;
|
|
458
|
-
switch (searchResult.loadType) {
|
|
459
|
-
case Enums_1.LoadTypes.Playlist:
|
|
460
|
-
resolvedTrack = searchResult.playlist.tracks[0];
|
|
461
|
-
break;
|
|
462
|
-
case Enums_1.LoadTypes.Track:
|
|
463
|
-
case Enums_1.LoadTypes.Search:
|
|
464
|
-
resolvedTrack = searchResult.tracks[0];
|
|
465
|
-
break;
|
|
466
|
-
default:
|
|
467
|
-
return [];
|
|
468
|
-
}
|
|
469
|
-
if (!resolvedTrack)
|
|
470
|
-
return [];
|
|
471
|
-
videoID = resolvedTrack.uri.split("=").pop();
|
|
472
|
-
}
|
|
473
|
-
if (!videoID) {
|
|
303
|
+
try {
|
|
304
|
+
const recommendedRes = await axios_1.default.get(`${track.uri}/recommended`).catch((err) => {
|
|
305
|
+
console.error(`[AutoPlay] Failed to fetch SoundCloud recommendations. Status: ${err.response?.status || "Unknown"}`, err.message);
|
|
306
|
+
return null;
|
|
307
|
+
});
|
|
308
|
+
if (!recommendedRes) {
|
|
474
309
|
return [];
|
|
475
310
|
}
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
const res = await this.manager.search({ query: searchURI, source: Enums_1.SearchPlatform.YouTube }, requester);
|
|
483
|
-
if (TrackUtils.isErrorOrEmptySearchResult(res)) {
|
|
311
|
+
const html = recommendedRes.data;
|
|
312
|
+
const dom = new jsdom_1.JSDOM(html);
|
|
313
|
+
const window = dom.window;
|
|
314
|
+
// Narrow the element types using instanceof
|
|
315
|
+
const secondNoscript = window.querySelectorAll("noscript")[1];
|
|
316
|
+
if (!secondNoscript || !(secondNoscript instanceof window.Element))
|
|
484
317
|
return [];
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
318
|
+
const sectionElement = secondNoscript.querySelector("section");
|
|
319
|
+
if (!sectionElement || !(sectionElement instanceof window.HTMLElement))
|
|
320
|
+
return [];
|
|
321
|
+
const articleElements = sectionElement.querySelectorAll("article");
|
|
322
|
+
if (!articleElements || articleElements.length === 0)
|
|
323
|
+
return [];
|
|
324
|
+
const urls = Array.from(articleElements)
|
|
325
|
+
.map((element) => {
|
|
326
|
+
const h2 = element.querySelector('h2[itemprop="name"]');
|
|
327
|
+
if (!h2)
|
|
328
|
+
return null;
|
|
329
|
+
const a = h2.querySelector('a[itemprop="url"]');
|
|
330
|
+
if (!a)
|
|
331
|
+
return null;
|
|
332
|
+
const href = a.getAttribute("href");
|
|
333
|
+
return href ? `https://soundcloud.com${href}` : null;
|
|
334
|
+
})
|
|
335
|
+
.filter(Boolean);
|
|
336
|
+
if (!urls.length)
|
|
337
|
+
return [];
|
|
338
|
+
const randomUrl = urls[Math.floor(Math.random() * urls.length)];
|
|
339
|
+
const resolvedTrack = await this.resolveFirstTrackFromQuery(randomUrl, Enums_1.SearchPlatform.SoundCloud, requester);
|
|
340
|
+
return resolvedTrack ? [resolvedTrack] : [];
|
|
500
341
|
}
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
if (!track.uri.includes("tidal")) {
|
|
505
|
-
const res = await this.manager.search({ query: `${track.author} - ${track.title}`, source: Enums_1.SearchPlatform.Tidal }, requester);
|
|
506
|
-
if (TrackUtils.isErrorOrEmptySearchResult(res))
|
|
507
|
-
return [];
|
|
508
|
-
let resolvedTrack;
|
|
509
|
-
switch (res.loadType) {
|
|
510
|
-
case Enums_1.LoadTypes.Playlist:
|
|
511
|
-
resolvedTrack = res.playlist.tracks[0];
|
|
512
|
-
break;
|
|
513
|
-
case Enums_1.LoadTypes.Track:
|
|
514
|
-
case Enums_1.LoadTypes.Search:
|
|
515
|
-
resolvedTrack = res.tracks[0];
|
|
516
|
-
break;
|
|
517
|
-
default:
|
|
518
|
-
return [];
|
|
519
|
-
}
|
|
520
|
-
if (!resolvedTrack)
|
|
521
|
-
return [];
|
|
522
|
-
track = resolvedTrack;
|
|
523
|
-
}
|
|
524
|
-
const identifier = `tdrec:${track.identifier}`;
|
|
525
|
-
const recommendedResult = (await this.manager.useableNode.rest.get(`/v4/loadtracks?identifier=${encodeURIComponent(identifier)}`));
|
|
526
|
-
const tracks = this.buildTracksFromResponse(recommendedResult, requester);
|
|
527
|
-
return tracks;
|
|
342
|
+
catch (error) {
|
|
343
|
+
console.error("[AutoPlay] Error occurred while fetching soundcloud recommendations:", error);
|
|
344
|
+
return [];
|
|
528
345
|
}
|
|
529
|
-
|
|
530
|
-
case Enums_1.AutoPlayPlatform.
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
return [];
|
|
536
|
-
let resolvedTrack;
|
|
537
|
-
switch (res.loadType) {
|
|
538
|
-
case Enums_1.LoadTypes.Playlist:
|
|
539
|
-
resolvedTrack = res.playlist.tracks[0];
|
|
540
|
-
break;
|
|
541
|
-
case Enums_1.LoadTypes.Track:
|
|
542
|
-
case Enums_1.LoadTypes.Search:
|
|
543
|
-
resolvedTrack = res.tracks[0];
|
|
544
|
-
break;
|
|
545
|
-
default:
|
|
546
|
-
return [];
|
|
547
|
-
}
|
|
548
|
-
if (!resolvedTrack)
|
|
549
|
-
return [];
|
|
550
|
-
track = resolvedTrack;
|
|
551
|
-
}
|
|
552
|
-
const identifier = `vkrec:${track.identifier}`;
|
|
553
|
-
const recommendedResult = (await this.manager.useableNode.rest.get(`/v4/loadtracks?identifier=${encodeURIComponent(identifier)}`));
|
|
554
|
-
const tracks = this.buildTracksFromResponse(recommendedResult, requester);
|
|
555
|
-
return tracks;
|
|
346
|
+
}
|
|
347
|
+
case Enums_1.AutoPlayPlatform.YouTube: {
|
|
348
|
+
const hasYouTubeURL = ["youtube.com", "youtu.be"].some((url) => track.uri.includes(url));
|
|
349
|
+
let videoID = null;
|
|
350
|
+
if (hasYouTubeURL) {
|
|
351
|
+
videoID = track.uri.split("=").pop();
|
|
556
352
|
}
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
353
|
+
else {
|
|
354
|
+
const resolvedTrack = await this.resolveFirstTrackFromQuery(`${track.author} - ${track.title}`, Enums_1.SearchPlatform.YouTube, requester);
|
|
355
|
+
if (!resolvedTrack)
|
|
356
|
+
return [];
|
|
357
|
+
videoID = resolvedTrack.uri.split("=").pop();
|
|
358
|
+
}
|
|
359
|
+
if (!videoID) {
|
|
360
|
+
return [];
|
|
361
|
+
}
|
|
362
|
+
let randomIndex;
|
|
363
|
+
let searchURI;
|
|
364
|
+
do {
|
|
365
|
+
randomIndex = Math.floor(Math.random() * 23) + 2;
|
|
366
|
+
searchURI = `https://www.youtube.com/watch?v=${videoID}&list=RD${videoID}&index=${randomIndex}`;
|
|
367
|
+
} while (track.uri.includes(searchURI));
|
|
368
|
+
const resolvedTracks = await this.resolveTracksFromQuery(searchURI, Enums_1.SearchPlatform.YouTube, requester);
|
|
369
|
+
const filteredTracks = resolvedTracks.filter((t) => t.uri !== track.uri);
|
|
370
|
+
return filteredTracks;
|
|
371
|
+
}
|
|
372
|
+
case Enums_1.AutoPlayPlatform.Tidal: {
|
|
373
|
+
if (!track.uri.includes("tidal")) {
|
|
374
|
+
const resolvedTrack = await this.resolveFirstTrackFromQuery(`${track.author} - ${track.title}`, Enums_1.SearchPlatform.Tidal, requester);
|
|
375
|
+
if (!resolvedTrack)
|
|
376
|
+
return [];
|
|
377
|
+
track = resolvedTrack;
|
|
378
|
+
}
|
|
379
|
+
const identifier = `tdrec:${track.identifier}`;
|
|
380
|
+
const recommendedResult = (await this.manager.useableNode.rest.get(`/v4/loadtracks?identifier=${encodeURIComponent(identifier)}`));
|
|
381
|
+
const tracks = this.buildTracksFromResponse(recommendedResult, requester);
|
|
382
|
+
return tracks;
|
|
383
|
+
}
|
|
384
|
+
case Enums_1.AutoPlayPlatform.VKMusic: {
|
|
385
|
+
if (!track.uri.includes("vk.com") && !track.uri.includes("vk.ru")) {
|
|
386
|
+
const resolvedTrack = await this.resolveFirstTrackFromQuery(`${track.author} - ${track.title}`, Enums_1.SearchPlatform.VKMusic, requester);
|
|
387
|
+
if (!resolvedTrack)
|
|
388
|
+
return [];
|
|
389
|
+
track = resolvedTrack;
|
|
584
390
|
}
|
|
585
|
-
|
|
391
|
+
const identifier = `vkrec:${track.identifier}`;
|
|
392
|
+
const recommendedResult = (await this.manager.useableNode.rest.get(`/v4/loadtracks?identifier=${encodeURIComponent(identifier)}`));
|
|
393
|
+
const tracks = this.buildTracksFromResponse(recommendedResult, requester);
|
|
394
|
+
return tracks;
|
|
395
|
+
}
|
|
396
|
+
case Enums_1.AutoPlayPlatform.Qobuz: {
|
|
397
|
+
if (!track.uri.includes("qobuz.com")) {
|
|
398
|
+
const resolvedTrack = await this.resolveFirstTrackFromQuery(`${track.author} - ${track.title}`, Enums_1.SearchPlatform.Qobuz, requester);
|
|
399
|
+
if (!resolvedTrack)
|
|
400
|
+
return [];
|
|
401
|
+
track = resolvedTrack;
|
|
402
|
+
}
|
|
403
|
+
const identifier = `qbrec:${track.identifier}`;
|
|
404
|
+
const recommendedResult = (await this.manager.useableNode.rest.get(`/v4/loadtracks?identifier=${encodeURIComponent(identifier)}`));
|
|
405
|
+
const tracks = this.buildTracksFromResponse(recommendedResult, requester);
|
|
406
|
+
return tracks;
|
|
407
|
+
}
|
|
586
408
|
default:
|
|
587
409
|
return [];
|
|
588
410
|
}
|
|
589
411
|
}
|
|
412
|
+
/**
|
|
413
|
+
* Searches for a track using the manager and returns resolved tracks.
|
|
414
|
+
* @param query The search query (artist - title).
|
|
415
|
+
* @param requester The requester who initiated the search.
|
|
416
|
+
* @returns An array of resolved tracks, or an empty array if not found or error occurred.
|
|
417
|
+
*/
|
|
418
|
+
static async resolveTracksFromQuery(query, source, requester) {
|
|
419
|
+
try {
|
|
420
|
+
const searchResult = await this.manager.search({ query, source }, requester);
|
|
421
|
+
if (TrackUtils.isErrorOrEmptySearchResult(searchResult)) {
|
|
422
|
+
return [];
|
|
423
|
+
}
|
|
424
|
+
switch (searchResult.loadType) {
|
|
425
|
+
case Enums_1.LoadTypes.Album:
|
|
426
|
+
case Enums_1.LoadTypes.Artist:
|
|
427
|
+
case Enums_1.LoadTypes.Station:
|
|
428
|
+
case Enums_1.LoadTypes.Podcast:
|
|
429
|
+
case Enums_1.LoadTypes.Show:
|
|
430
|
+
case Enums_1.LoadTypes.Playlist:
|
|
431
|
+
return searchResult.playlist.tracks;
|
|
432
|
+
case Enums_1.LoadTypes.Track:
|
|
433
|
+
case Enums_1.LoadTypes.Search:
|
|
434
|
+
case Enums_1.LoadTypes.Short:
|
|
435
|
+
return searchResult.tracks;
|
|
436
|
+
default:
|
|
437
|
+
return [];
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
catch (error) {
|
|
441
|
+
console.error("[TrackResolver] Failed to resolve query:", query, error);
|
|
442
|
+
return [];
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
/**
|
|
446
|
+
* Resolves the first available track from a search query using the specified source.
|
|
447
|
+
* Useful for normalizing tracks that lack platform-specific metadata or URIs.
|
|
448
|
+
*
|
|
449
|
+
* @param query - The search query string (usually "Artist - Title").
|
|
450
|
+
* @param source - The search platform to use (e.g., Spotify, Deezer, YouTube).
|
|
451
|
+
* @param requester - The requester object, used for context or attribution.
|
|
452
|
+
* @returns A single resolved {@link Track} object if found, or `null` if the search fails or returns no results.
|
|
453
|
+
*/
|
|
454
|
+
static async resolveFirstTrackFromQuery(query, source, requester) {
|
|
455
|
+
try {
|
|
456
|
+
const searchResult = await this.manager.search({ query, source }, requester);
|
|
457
|
+
if (TrackUtils.isErrorOrEmptySearchResult(searchResult))
|
|
458
|
+
return null;
|
|
459
|
+
switch (searchResult.loadType) {
|
|
460
|
+
case Enums_1.LoadTypes.Album:
|
|
461
|
+
case Enums_1.LoadTypes.Artist:
|
|
462
|
+
case Enums_1.LoadTypes.Station:
|
|
463
|
+
case Enums_1.LoadTypes.Podcast:
|
|
464
|
+
case Enums_1.LoadTypes.Show:
|
|
465
|
+
case Enums_1.LoadTypes.Playlist:
|
|
466
|
+
return searchResult.playlist.tracks[0] || null;
|
|
467
|
+
case Enums_1.LoadTypes.Track:
|
|
468
|
+
case Enums_1.LoadTypes.Search:
|
|
469
|
+
case Enums_1.LoadTypes.Short:
|
|
470
|
+
return searchResult.tracks[0] || null;
|
|
471
|
+
default:
|
|
472
|
+
return null;
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
catch (err) {
|
|
476
|
+
console.error(`[AutoPlay] Failed to resolve track from query: "${query}" on source: ${source}`, err);
|
|
477
|
+
return null;
|
|
478
|
+
}
|
|
479
|
+
}
|
|
590
480
|
// static async getSpotifyAccessToken() {
|
|
591
481
|
// const timeoutMs = 15000;
|
|
592
482
|
// let browser;
|
|
@@ -652,7 +542,7 @@ class AutoPlayUtils {
|
|
|
652
542
|
return tracks;
|
|
653
543
|
}
|
|
654
544
|
default:
|
|
655
|
-
throw new Error(`Unsupported loadType: ${recommendedResult.loadType}`);
|
|
545
|
+
throw new Error(`[TrackBuilder] Unsupported loadType: ${recommendedResult.loadType}`);
|
|
656
546
|
}
|
|
657
547
|
}
|
|
658
548
|
}
|
|
@@ -685,7 +575,7 @@ class Structure {
|
|
|
685
575
|
exports.Structure = Structure;
|
|
686
576
|
const structures = {
|
|
687
577
|
Player: require("./Player").Player,
|
|
688
|
-
Queue: require("
|
|
578
|
+
Queue: require("../statestorage/MemoryQueue").MemoryQueue,
|
|
689
579
|
Node: require("./Node").Node,
|
|
690
580
|
Filters: require("./Filters").Filters,
|
|
691
581
|
Manager: require("./Manager").Manager,
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SeyfertManager = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const Manager_1 = require("../structures/Manager");
|
|
6
|
+
const common_1 = require("seyfert/lib/common");
|
|
7
|
+
tslib_1.__exportStar(require("../index"), exports);
|
|
8
|
+
/**
|
|
9
|
+
* Seyfert wrapper for Magmastream.
|
|
10
|
+
*
|
|
11
|
+
* @note This wrapper does require the manual implementation of the "raw" and "ready" events, to call the `updateVoiceState` and `init` methods respectively.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```typescript
|
|
15
|
+
* const client = new Client();
|
|
16
|
+
* const manager = new SeyfertManager(client, options);
|
|
17
|
+
*
|
|
18
|
+
* client.events.values.RAW = {
|
|
19
|
+
* data: { name: "raw" },
|
|
20
|
+
* run: async (data) => {
|
|
21
|
+
* await manager.updateVoiceState(data);
|
|
22
|
+
* }
|
|
23
|
+
* }
|
|
24
|
+
*
|
|
25
|
+
* client.events.values.READY = {
|
|
26
|
+
* data: { name: "ready" },
|
|
27
|
+
* run: async (user, client) => {
|
|
28
|
+
* await manager.init({ clientId: client.botId });
|
|
29
|
+
* }
|
|
30
|
+
* }
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
class SeyfertManager extends Manager_1.Manager {
|
|
34
|
+
client;
|
|
35
|
+
constructor(client, options) {
|
|
36
|
+
super(options);
|
|
37
|
+
this.client = client;
|
|
38
|
+
}
|
|
39
|
+
send(packet) {
|
|
40
|
+
this.client.gateway.send((0, common_1.calculateShardId)(packet.d.guild_id), packet);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
exports.SeyfertManager = SeyfertManager;
|