magmastream 2.9.0-dev.0 → 2.9.0-dev.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/dist/index.d.ts +178 -68
- package/dist/storage/CollectionPlayerStore.js +80 -0
- package/dist/storage/RedisPlayerStore.js +159 -0
- package/dist/structures/Manager.js +77 -40
- package/dist/structures/Node.js +67 -238
- package/dist/structures/Player.js +45 -152
- package/dist/structures/Queue.js +100 -47
- package/dist/structures/RedisQueue.js +301 -0
- package/dist/structures/Rest.js +1 -1
- package/dist/structures/Utils.js +345 -344
- package/dist/utils/logExecutionTime.js +11 -0
- package/dist/utils/managerCheck.js +24 -21
- package/dist/utils/nodeCheck.js +25 -25
- package/package.json +6 -5
package/dist/structures/Node.js
CHANGED
|
@@ -61,13 +61,13 @@ class Node {
|
|
|
61
61
|
this.options = {
|
|
62
62
|
port: 2333,
|
|
63
63
|
password: "youshallnotpass",
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
64
|
+
useSSL: false,
|
|
65
|
+
maxRetryAttempts: 30,
|
|
66
|
+
retryDelayMs: 60000,
|
|
67
|
+
nodePriority: 0,
|
|
68
68
|
...options,
|
|
69
69
|
};
|
|
70
|
-
if (this.options.
|
|
70
|
+
if (this.options.useSSL) {
|
|
71
71
|
this.options.port = 443;
|
|
72
72
|
}
|
|
73
73
|
this.options.identifier = options.identifier || options.host;
|
|
@@ -179,7 +179,7 @@ class Node {
|
|
|
179
179
|
* @remarks
|
|
180
180
|
* If the node is already connected, this method will do nothing.
|
|
181
181
|
* If the node has a session ID, it will be sent in the headers of the WebSocket connection.
|
|
182
|
-
* If the node has no session ID but the `
|
|
182
|
+
* If the node has no session ID but the `enableSessionResumeOption` option is true, it will use the session ID
|
|
183
183
|
* stored in the sessionIds.json file if it exists.
|
|
184
184
|
*/
|
|
185
185
|
connect() {
|
|
@@ -194,11 +194,11 @@ class Node {
|
|
|
194
194
|
if (this.sessionId) {
|
|
195
195
|
headers["Session-Id"] = this.sessionId;
|
|
196
196
|
}
|
|
197
|
-
else if (this.options.
|
|
197
|
+
else if (this.options.enableSessionResumeOption && sessionIdsMap.has(compositeKey)) {
|
|
198
198
|
this.sessionId = sessionIdsMap.get(compositeKey) || null;
|
|
199
199
|
headers["Session-Id"] = this.sessionId;
|
|
200
200
|
}
|
|
201
|
-
this.socket = new ws_1.default(`ws${this.options.
|
|
201
|
+
this.socket = new ws_1.default(`ws${this.options.useSSL ? "s" : ""}://${this.address}/v4/websocket`, { headers });
|
|
202
202
|
this.socket.on("open", this.open.bind(this));
|
|
203
203
|
this.socket.on("close", this.close.bind(this));
|
|
204
204
|
this.socket.on("message", this.message.bind(this));
|
|
@@ -210,7 +210,7 @@ class Node {
|
|
|
210
210
|
options: {
|
|
211
211
|
clientId: this.manager.options.clientId,
|
|
212
212
|
clientName: this.manager.options.clientName,
|
|
213
|
-
|
|
213
|
+
useSSL: this.options.useSSL,
|
|
214
214
|
identifier: this.options.identifier,
|
|
215
215
|
},
|
|
216
216
|
};
|
|
@@ -235,11 +235,11 @@ class Node {
|
|
|
235
235
|
identifier: this.options.identifier,
|
|
236
236
|
address: this.address,
|
|
237
237
|
sessionId: this.sessionId,
|
|
238
|
-
playerCount: this.manager.players.filter((p) => p.node == this).size,
|
|
238
|
+
playerCount: (await this.manager.players.filter((p) => p.node == this)).size,
|
|
239
239
|
};
|
|
240
240
|
this.manager.emit(Manager_1.ManagerEventTypes.Debug, `[NODE] Destroying node: ${JSON.stringify(debugInfo)}`);
|
|
241
241
|
// Automove all players connected to that node
|
|
242
|
-
const players = this.manager.players.filter((p) => p.node == this);
|
|
242
|
+
const players = await this.manager.players.filter((p) => p.node == this);
|
|
243
243
|
if (players.size) {
|
|
244
244
|
players.forEach(async (player) => {
|
|
245
245
|
await player.autoMoveNode();
|
|
@@ -279,17 +279,17 @@ class Node {
|
|
|
279
279
|
identifier: this.options.identifier,
|
|
280
280
|
connected: this.connected,
|
|
281
281
|
reconnectAttempts: this.reconnectAttempts,
|
|
282
|
-
|
|
283
|
-
|
|
282
|
+
maxRetryAttempts: this.options.maxRetryAttempts,
|
|
283
|
+
retryDelayMs: this.options.retryDelayMs,
|
|
284
284
|
};
|
|
285
285
|
// Emit a debug event indicating the node is attempting to reconnect
|
|
286
286
|
this.manager.emit(Manager_1.ManagerEventTypes.Debug, `[NODE] Reconnecting node: ${JSON.stringify(debugInfo)}`);
|
|
287
287
|
// Schedule the reconnection attempt after the specified retry delay
|
|
288
288
|
this.reconnectTimeout = setTimeout(async () => {
|
|
289
289
|
// Check if the maximum number of retry attempts has been reached
|
|
290
|
-
if (this.reconnectAttempts >= this.options.
|
|
290
|
+
if (this.reconnectAttempts >= this.options.maxRetryAttempts) {
|
|
291
291
|
// Emit an error event and destroy the node if retries are exhausted
|
|
292
|
-
const error = new Error(`Unable to connect after ${this.options.
|
|
292
|
+
const error = new Error(`Unable to connect after ${this.options.maxRetryAttempts} attempts.`);
|
|
293
293
|
this.manager.emit(Manager_1.ManagerEventTypes.NodeError, this, error);
|
|
294
294
|
return await this.destroy();
|
|
295
295
|
}
|
|
@@ -301,7 +301,7 @@ class Node {
|
|
|
301
301
|
this.connect();
|
|
302
302
|
// Increment the reconnect attempts counter
|
|
303
303
|
this.reconnectAttempts++;
|
|
304
|
-
}, this.options.
|
|
304
|
+
}, this.options.retryDelayMs);
|
|
305
305
|
}
|
|
306
306
|
/**
|
|
307
307
|
* Handles the "open" event emitted by the WebSocket connection.
|
|
@@ -350,7 +350,7 @@ class Node {
|
|
|
350
350
|
this.manager.emit(Manager_1.ManagerEventTypes.Debug, `[NODE] Disconnected node: ${JSON.stringify(debugInfo)}`);
|
|
351
351
|
// Try moving all players connected to that node to a useable one
|
|
352
352
|
if (this.manager.useableNode) {
|
|
353
|
-
const players = this.manager.players.filter((p) => p.node.options.identifier == this.options.identifier);
|
|
353
|
+
const players = await this.manager.players.filter((p) => p.node.options.identifier == this.options.identifier);
|
|
354
354
|
if (players.size) {
|
|
355
355
|
await Promise.all(Array.from(players.values(), (player) => player.autoMoveNode()));
|
|
356
356
|
}
|
|
@@ -405,7 +405,7 @@ class Node {
|
|
|
405
405
|
this.stats = { ...payload };
|
|
406
406
|
break;
|
|
407
407
|
case "playerUpdate":
|
|
408
|
-
player = this.manager.players.get(payload.guildId);
|
|
408
|
+
player = await this.manager.players.get(payload.guildId);
|
|
409
409
|
if (player && player.node.options.identifier !== this.options.identifier) {
|
|
410
410
|
return;
|
|
411
411
|
}
|
|
@@ -413,7 +413,7 @@ class Node {
|
|
|
413
413
|
player.position = payload.state.position || 0;
|
|
414
414
|
break;
|
|
415
415
|
case "event":
|
|
416
|
-
player = this.manager.players.get(payload.guildId);
|
|
416
|
+
player = await this.manager.players.get(payload.guildId);
|
|
417
417
|
if (player && player.node.options.identifier !== this.options.identifier) {
|
|
418
418
|
return;
|
|
419
419
|
}
|
|
@@ -431,10 +431,10 @@ class Node {
|
|
|
431
431
|
// Load player states from the JSON file
|
|
432
432
|
await this.manager.loadPlayerStates(this.options.identifier);
|
|
433
433
|
}
|
|
434
|
-
if (this.options.
|
|
434
|
+
if (this.options.enableSessionResumeOption) {
|
|
435
435
|
await this.rest.patch(`/v4/sessions/${this.sessionId}`, {
|
|
436
|
-
resuming: this.options.
|
|
437
|
-
timeout: this.options.
|
|
436
|
+
resuming: this.options.enableSessionResumeOption,
|
|
437
|
+
timeout: this.options.sessionTimeoutMs,
|
|
438
438
|
});
|
|
439
439
|
}
|
|
440
440
|
break;
|
|
@@ -452,10 +452,10 @@ class Node {
|
|
|
452
452
|
async handleEvent(payload) {
|
|
453
453
|
if (!payload.guildId)
|
|
454
454
|
return;
|
|
455
|
-
const player = this.manager.players.get(payload.guildId);
|
|
455
|
+
const player = await this.manager.players.get(payload.guildId);
|
|
456
456
|
if (!player)
|
|
457
457
|
return;
|
|
458
|
-
const track = player.queue.
|
|
458
|
+
const track = await player.queue.getCurrent();
|
|
459
459
|
const type = payload.type;
|
|
460
460
|
let error;
|
|
461
461
|
switch (type) {
|
|
@@ -478,16 +478,16 @@ class Node {
|
|
|
478
478
|
this.socketClosed(player, payload);
|
|
479
479
|
break;
|
|
480
480
|
case "SegmentsLoaded":
|
|
481
|
-
this.sponsorBlockSegmentLoaded(player,
|
|
481
|
+
this.sponsorBlockSegmentLoaded(player, track, payload);
|
|
482
482
|
break;
|
|
483
483
|
case "SegmentSkipped":
|
|
484
|
-
this.sponsorBlockSegmentSkipped(player,
|
|
484
|
+
this.sponsorBlockSegmentSkipped(player, track, payload);
|
|
485
485
|
break;
|
|
486
486
|
case "ChaptersLoaded":
|
|
487
|
-
this.sponsorBlockChaptersLoaded(player,
|
|
487
|
+
this.sponsorBlockChaptersLoaded(player, track, payload);
|
|
488
488
|
break;
|
|
489
489
|
case "ChapterStarted":
|
|
490
|
-
this.sponsorBlockChapterStarted(player,
|
|
490
|
+
this.sponsorBlockChapterStarted(player, track, payload);
|
|
491
491
|
break;
|
|
492
492
|
default:
|
|
493
493
|
error = new Error(`Node#event unknown event '${type}'.`);
|
|
@@ -536,12 +536,14 @@ class Node {
|
|
|
536
536
|
async trackEnd(player, track, payload) {
|
|
537
537
|
const { reason } = payload;
|
|
538
538
|
const skipFlag = player.get("skipFlag");
|
|
539
|
-
|
|
539
|
+
const previous = await player.queue.getPrevious();
|
|
540
|
+
const current = await player.queue.getCurrent();
|
|
541
|
+
if (!skipFlag && (previous.length === 0 || (previous[0] && previous[0].track !== current?.track))) {
|
|
540
542
|
// Store the current track in the previous tracks queue
|
|
541
|
-
player.queue.
|
|
543
|
+
await player.queue.addPrevious(await player.queue.getCurrent());
|
|
542
544
|
// Limit the previous tracks queue to maxPreviousTracks
|
|
543
|
-
if (player.queue.
|
|
544
|
-
player.queue.
|
|
545
|
+
if ((await player.queue.getPrevious()).length > this.manager.options.maxPreviousTracks) {
|
|
546
|
+
(await player.queue.getPrevious()).shift();
|
|
545
547
|
}
|
|
546
548
|
}
|
|
547
549
|
const oldPlayer = player;
|
|
@@ -557,7 +559,7 @@ class Node {
|
|
|
557
559
|
break;
|
|
558
560
|
case Utils_1.TrackEndReasonTypes.Stopped:
|
|
559
561
|
// If the track was forcibly replaced
|
|
560
|
-
if (player.queue.
|
|
562
|
+
if (await player.queue.size()) {
|
|
561
563
|
await this.playNextTrack(player, track, payload);
|
|
562
564
|
}
|
|
563
565
|
else {
|
|
@@ -571,7 +573,7 @@ class Node {
|
|
|
571
573
|
break;
|
|
572
574
|
}
|
|
573
575
|
// If there's another track in the queue
|
|
574
|
-
if (player.queue.
|
|
576
|
+
if (await player.queue.size()) {
|
|
575
577
|
await this.playNextTrack(player, track, payload);
|
|
576
578
|
}
|
|
577
579
|
else {
|
|
@@ -602,200 +604,23 @@ class Node {
|
|
|
602
604
|
*/
|
|
603
605
|
async handleAutoplay(player, attempt = 0) {
|
|
604
606
|
// If autoplay is not enabled or all attempts have failed, early exit
|
|
605
|
-
if (!player.isAutoplay || attempt
|
|
607
|
+
if (!player.isAutoplay || attempt > player.autoplayTries || !(await player.queue.getPrevious()).length)
|
|
606
608
|
return false;
|
|
607
|
-
const
|
|
609
|
+
const PreviousQueue = await player.queue.getPrevious();
|
|
610
|
+
const lastTrack = PreviousQueue?.at(-1);
|
|
608
611
|
lastTrack.requester = player.get("Internal_BotUser");
|
|
609
|
-
|
|
612
|
+
if (!lastTrack)
|
|
613
|
+
return false;
|
|
614
|
+
const tracks = await Utils_1.AutoPlayUtils.getRecommendedTracks(lastTrack);
|
|
610
615
|
if (tracks.length) {
|
|
611
|
-
player.queue.add(tracks[0]);
|
|
616
|
+
await player.queue.add(tracks[0]);
|
|
612
617
|
await player.play();
|
|
613
618
|
return true;
|
|
614
619
|
}
|
|
615
620
|
else {
|
|
616
621
|
return false;
|
|
617
622
|
}
|
|
618
|
-
// // Determine if YouTube should be used
|
|
619
|
-
// // If Last.fm is not available, use YouTube as a fallback
|
|
620
|
-
// // If YouTube is available and this is the last attempt, use YouTube
|
|
621
|
-
// const shouldUseYouTube =
|
|
622
|
-
// (!apiKey && enabledSources.includes("youtube")) || // Fallback to YouTube if Last.fm is not available
|
|
623
|
-
// (attempt === player.autoplayTries - 1 && player.autoplayTries > 1 && enabledSources.includes("youtube")); // Use YouTube on the last attempt
|
|
624
|
-
// if (shouldUseYouTube) {
|
|
625
|
-
// // Use YouTube-based autoplay
|
|
626
|
-
// return await this.handleYouTubeAutoplay(player, lastTrack);
|
|
627
|
-
// }
|
|
628
|
-
// // Handle Last.fm-based autoplay (or other platforms)
|
|
629
|
-
// const selectedSource = this.selectPlatform(enabledSources);
|
|
630
|
-
// if (selectedSource) {
|
|
631
|
-
// // Use the selected source to handle autoplay
|
|
632
|
-
// return await this.handlePlatformAutoplay(player, lastTrack, selectedSource, apiKey);
|
|
633
|
-
// }
|
|
634
|
-
// // If no source is available, return false
|
|
635
|
-
// return false;
|
|
636
|
-
return false;
|
|
637
623
|
}
|
|
638
|
-
// return response.data.similartracks.track.filter((t: { uri: string }) => t.uri !== track.uri);
|
|
639
|
-
// /**
|
|
640
|
-
// * Selects a platform from the given enabled sources.
|
|
641
|
-
// * @param {string[]} enabledSources - The enabled sources to select from.
|
|
642
|
-
// * @returns {SearchPlatform | null} - The selected platform or null if none was found.
|
|
643
|
-
// */
|
|
644
|
-
// public selectPlatform(enabledSources: string[]): SearchPlatform | null {
|
|
645
|
-
// const { autoPlaySearchPlatform } = this.manager.options;
|
|
646
|
-
// const platformMapping: { [key in SearchPlatform]: string } = {
|
|
647
|
-
// [SearchPlatform.AppleMusic]: "applemusic",
|
|
648
|
-
// [SearchPlatform.Bandcamp]: "bandcamp",
|
|
649
|
-
// [SearchPlatform.Deezer]: "deezer",
|
|
650
|
-
// [SearchPlatform.Jiosaavn]: "jiosaavn",
|
|
651
|
-
// [SearchPlatform.SoundCloud]: "soundcloud",
|
|
652
|
-
// [SearchPlatform.Spotify]: "spotify",
|
|
653
|
-
// [SearchPlatform.Tidal]: "tidal",
|
|
654
|
-
// [SearchPlatform.VKMusic]: "vkmusic",
|
|
655
|
-
// [SearchPlatform.YouTube]: "youtube",
|
|
656
|
-
// [SearchPlatform.YouTubeMusic]: "youtube",
|
|
657
|
-
// };
|
|
658
|
-
// // Try the autoPlaySearchPlatform first
|
|
659
|
-
// if (enabledSources.includes(platformMapping[autoPlaySearchPlatform])) {
|
|
660
|
-
// return autoPlaySearchPlatform;
|
|
661
|
-
// }
|
|
662
|
-
// // Fallback to other platforms in a predefined order
|
|
663
|
-
// const fallbackPlatforms = [
|
|
664
|
-
// SearchPlatform.Spotify,
|
|
665
|
-
// SearchPlatform.Deezer,
|
|
666
|
-
// SearchPlatform.SoundCloud,
|
|
667
|
-
// SearchPlatform.AppleMusic,
|
|
668
|
-
// SearchPlatform.Bandcamp,
|
|
669
|
-
// SearchPlatform.Jiosaavn,
|
|
670
|
-
// SearchPlatform.Tidal,
|
|
671
|
-
// SearchPlatform.VKMusic,
|
|
672
|
-
// SearchPlatform.YouTubeMusic,
|
|
673
|
-
// SearchPlatform.YouTube,
|
|
674
|
-
// ];
|
|
675
|
-
// for (const platform of fallbackPlatforms) {
|
|
676
|
-
// if (enabledSources.includes(platformMapping[platform])) {
|
|
677
|
-
// return platform;
|
|
678
|
-
// }
|
|
679
|
-
// }
|
|
680
|
-
// return null;
|
|
681
|
-
// }
|
|
682
|
-
// /**
|
|
683
|
-
// * Handles Last.fm-based autoplay.
|
|
684
|
-
// * @param {Player} player - The player instance.
|
|
685
|
-
// * @param {Track} previousTrack - The previous track.
|
|
686
|
-
// * @param {SearchPlatform} platform - The selected platform.
|
|
687
|
-
// * @param {string} apiKey - The Last.fm API key.
|
|
688
|
-
// * @returns {Promise<boolean>} - Whether the autoplay was successful.
|
|
689
|
-
// */
|
|
690
|
-
// private async handlePlatformAutoplay(player: Player, previousTrack: Track, platform: SearchPlatform, apiKey: string): Promise<boolean> {
|
|
691
|
-
// let { author: artist } = previousTrack;
|
|
692
|
-
// const { title } = previousTrack;
|
|
693
|
-
// if (!artist || !title) {
|
|
694
|
-
// if (!title) {
|
|
695
|
-
// // No title provided, search for the artist's top tracks
|
|
696
|
-
// const noTitleUrl = `https://ws.audioscrobbler.com/2.0/?method=artist.getTopTracks&artist=${artist}&autocorrect=1&api_key=${apiKey}&format=json`;
|
|
697
|
-
// const response = await axios.get(noTitleUrl);
|
|
698
|
-
// if (response.data.error || !response.data.toptracks?.track?.length) return false;
|
|
699
|
-
// const randomTrack = response.data.toptracks.track[Math.floor(Math.random() * response.data.toptracks.track.length)];
|
|
700
|
-
// const res = await player.search(
|
|
701
|
-
// { query: `${randomTrack.artist.name} - ${randomTrack.name}`, source: platform },
|
|
702
|
-
// player.get("Internal_BotUser") as User | ClientUser
|
|
703
|
-
// );
|
|
704
|
-
// if (res.loadType === LoadTypes.Empty || res.loadType === LoadTypes.Error) return false;
|
|
705
|
-
// const foundTrack = res.tracks.find((t) => t.uri !== previousTrack.uri);
|
|
706
|
-
// if (!foundTrack) return false;
|
|
707
|
-
// player.queue.add(foundTrack);
|
|
708
|
-
// await player.play();
|
|
709
|
-
// return true;
|
|
710
|
-
// }
|
|
711
|
-
// if (!artist) {
|
|
712
|
-
// // No artist provided, search for the track title
|
|
713
|
-
// const noArtistUrl = `https://ws.audioscrobbler.com/2.0/?method=track.search&track=${title}&api_key=${apiKey}&format=json`;
|
|
714
|
-
// const response = await axios.get(noArtistUrl);
|
|
715
|
-
// artist = response.data.results.trackmatches?.track?.[0]?.artist;
|
|
716
|
-
// if (!artist) return false;
|
|
717
|
-
// }
|
|
718
|
-
// }
|
|
719
|
-
// // Search for similar tracks to the current track
|
|
720
|
-
// const url = `https://ws.audioscrobbler.com/2.0/?method=track.getSimilar&artist=${artist}&track=${title}&limit=10&autocorrect=1&api_key=${apiKey}&format=json`;
|
|
721
|
-
// let response: axios.AxiosResponse;
|
|
722
|
-
// try {
|
|
723
|
-
// response = await axios.get(url);
|
|
724
|
-
// } catch (error) {
|
|
725
|
-
// if (error) return false;
|
|
726
|
-
// }
|
|
727
|
-
// if (response.data.error || !response.data.similartracks?.track?.length) {
|
|
728
|
-
// // Retry the request if the first attempt fails
|
|
729
|
-
// const retryUrl = `https://ws.audioscrobbler.com/2.0/?method=artist.getTopTracks&artist=${artist}&autocorrect=1&api_key=${apiKey}&format=json`;
|
|
730
|
-
// const retryResponse = await axios.get(retryUrl);
|
|
731
|
-
// if (retryResponse.data.error || !retryResponse.data.toptracks?.track?.length) return false;
|
|
732
|
-
// const randomTrack = retryResponse.data.toptracks.track[Math.floor(Math.random() * retryResponse.data.toptracks.track.length)];
|
|
733
|
-
// const res = await player.search(
|
|
734
|
-
// { query: `${randomTrack.artist.name} - ${randomTrack.name}`, source: platform },
|
|
735
|
-
// player.get("Internal_BotUser") as User | ClientUser
|
|
736
|
-
// );
|
|
737
|
-
// if (res.loadType === LoadTypes.Empty || res.loadType === LoadTypes.Error) return false;
|
|
738
|
-
// const foundTrack = res.tracks.find((t) => t.uri !== previousTrack.uri);
|
|
739
|
-
// if (!foundTrack) return false;
|
|
740
|
-
// player.queue.add(foundTrack);
|
|
741
|
-
// await player.play();
|
|
742
|
-
// return true;
|
|
743
|
-
// }
|
|
744
|
-
// const randomTrack = response.data.similartracks.track[Math.floor(Math.random() * response.data.similartracks.track.length)];
|
|
745
|
-
// const res = await player.search(
|
|
746
|
-
// { query: `${randomTrack.artist.name} - ${randomTrack.name}`, source: platform },
|
|
747
|
-
// player.get("Internal_BotUser") as User | ClientUser
|
|
748
|
-
// );
|
|
749
|
-
// if (res.loadType === LoadTypes.Empty || res.loadType === LoadTypes.Error) return false;
|
|
750
|
-
// const foundTrack = res.tracks.find((t) => t.uri !== previousTrack.uri);
|
|
751
|
-
// if (!foundTrack) return false;
|
|
752
|
-
// player.queue.add(foundTrack);
|
|
753
|
-
// await player.play();
|
|
754
|
-
// return true;
|
|
755
|
-
// }
|
|
756
|
-
// /**
|
|
757
|
-
// * Handles YouTube-based autoplay.
|
|
758
|
-
// * @param {Player} player - The player instance.
|
|
759
|
-
// * @param {Track} previousTrack - The previous track.
|
|
760
|
-
// * @returns {Promise<boolean>} - Whether the autoplay was successful.
|
|
761
|
-
// */
|
|
762
|
-
// private async handleYouTubeAutoplay(player: Player, previousTrack: Track): Promise<boolean> {
|
|
763
|
-
// // Check if the previous track has a YouTube URL
|
|
764
|
-
// const hasYouTubeURL = ["youtube.com", "youtu.be"].some((url) => previousTrack.uri.includes(url));
|
|
765
|
-
// // Get the video ID from the previous track's URL
|
|
766
|
-
// const videoID = hasYouTubeURL
|
|
767
|
-
// ? previousTrack.uri.split("=").pop()
|
|
768
|
-
// : (
|
|
769
|
-
// await this.manager.search(
|
|
770
|
-
// { query: `${previousTrack.author} - ${previousTrack.title}`, source: SearchPlatform.YouTube },
|
|
771
|
-
// player.get("Internal_BotUser") as User | ClientUser
|
|
772
|
-
// )
|
|
773
|
-
// ).tracks[0]?.uri
|
|
774
|
-
// .split("=")
|
|
775
|
-
// .pop();
|
|
776
|
-
// // If the video ID is not found, return false
|
|
777
|
-
// if (!videoID) return false;
|
|
778
|
-
// // Get a random video index between 2 and 24
|
|
779
|
-
// let randomIndex: number;
|
|
780
|
-
// let searchURI: string;
|
|
781
|
-
// do {
|
|
782
|
-
// // Generate a random index between 2 and 24
|
|
783
|
-
// randomIndex = Math.floor(Math.random() * 23) + 2;
|
|
784
|
-
// // Build the search URI
|
|
785
|
-
// searchURI = `https://www.youtube.com/watch?v=${videoID}&list=RD${videoID}&index=${randomIndex}`;
|
|
786
|
-
// } while (previousTrack.uri.includes(searchURI));
|
|
787
|
-
// // Search for the video and return false if the search fails
|
|
788
|
-
// const res = await this.manager.search({ query: searchURI, source: SearchPlatform.YouTube }, player.get("Internal_BotUser") as User | ClientUser);
|
|
789
|
-
// if (res.loadType === LoadTypes.Empty || res.loadType === LoadTypes.Error) return false;
|
|
790
|
-
// // Find a track that is not the same as the current track
|
|
791
|
-
// const foundTrack = res.tracks.find((t) => t.uri !== previousTrack.uri && t.author !== previousTrack.author && t.title !== previousTrack.title);
|
|
792
|
-
// // If no track is found, return false
|
|
793
|
-
// if (!foundTrack) return false;
|
|
794
|
-
// // Add the found track to the queue and play it
|
|
795
|
-
// player.queue.add(foundTrack);
|
|
796
|
-
// await player.play();
|
|
797
|
-
// return true;
|
|
798
|
-
// }
|
|
799
624
|
/**
|
|
800
625
|
* Handles the scenario when a track fails to play or load.
|
|
801
626
|
* Shifts the queue to the next track and emits a track end event.
|
|
@@ -809,13 +634,13 @@ class Node {
|
|
|
809
634
|
* @private
|
|
810
635
|
*/
|
|
811
636
|
async handleFailedTrack(player, track, payload) {
|
|
812
|
-
player.queue.
|
|
813
|
-
if (!player.queue.
|
|
637
|
+
await player.queue.setCurrent(await player.queue.dequeue());
|
|
638
|
+
if (!(await player.queue.getCurrent())) {
|
|
814
639
|
await this.queueEnd(player, track, payload);
|
|
815
640
|
return;
|
|
816
641
|
}
|
|
817
642
|
this.manager.emit(Manager_1.ManagerEventTypes.TrackEnd, player, track, payload);
|
|
818
|
-
if (this.manager.options.
|
|
643
|
+
if (this.manager.options.playNextOnEnd)
|
|
819
644
|
await player.play();
|
|
820
645
|
}
|
|
821
646
|
/**
|
|
@@ -832,30 +657,34 @@ class Node {
|
|
|
832
657
|
*/
|
|
833
658
|
async handleRepeatedTrack(player, track, payload) {
|
|
834
659
|
const { queue, trackRepeat, queueRepeat } = player;
|
|
835
|
-
const {
|
|
660
|
+
const { playNextOnEnd } = this.manager.options;
|
|
836
661
|
if (trackRepeat) {
|
|
837
662
|
// Prevent duplicate repeat insertion
|
|
838
|
-
if (queue[0] !== queue.
|
|
839
|
-
queue.
|
|
663
|
+
if (queue[0] !== (await queue.getCurrent())) {
|
|
664
|
+
await queue.enqueueFront(await queue.getCurrent());
|
|
840
665
|
}
|
|
841
666
|
}
|
|
842
667
|
else if (queueRepeat) {
|
|
843
668
|
// Prevent duplicate queue insertion
|
|
844
|
-
if (queue[queue.
|
|
845
|
-
queue.add(queue.
|
|
669
|
+
if (queue[(await queue.size()) - 1] !== (await queue.getCurrent())) {
|
|
670
|
+
await queue.add(await queue.getCurrent());
|
|
846
671
|
}
|
|
847
672
|
}
|
|
848
673
|
// Move to the next track
|
|
849
|
-
queue.
|
|
674
|
+
await queue.setCurrent(await queue.dequeue());
|
|
850
675
|
// Emit track end event
|
|
851
676
|
this.manager.emit(Manager_1.ManagerEventTypes.TrackEnd, player, track, payload);
|
|
852
677
|
// If the track was stopped manually and no more tracks exist, end the queue
|
|
853
|
-
if (payload.reason === Utils_1.TrackEndReasonTypes.Stopped
|
|
854
|
-
await
|
|
855
|
-
|
|
678
|
+
if (payload.reason === Utils_1.TrackEndReasonTypes.Stopped) {
|
|
679
|
+
const next = await queue.dequeue();
|
|
680
|
+
await queue.setCurrent(next ?? null);
|
|
681
|
+
if (!next) {
|
|
682
|
+
await this.queueEnd(player, track, payload);
|
|
683
|
+
return;
|
|
684
|
+
}
|
|
856
685
|
}
|
|
857
686
|
// If autoplay is enabled, play the next track
|
|
858
|
-
if (
|
|
687
|
+
if (playNextOnEnd)
|
|
859
688
|
await player.play();
|
|
860
689
|
}
|
|
861
690
|
/**
|
|
@@ -871,11 +700,11 @@ class Node {
|
|
|
871
700
|
*/
|
|
872
701
|
async playNextTrack(player, track, payload) {
|
|
873
702
|
// Shift the queue to set the next track as current
|
|
874
|
-
player.queue.
|
|
703
|
+
await player.queue.setCurrent(await player.queue.dequeue());
|
|
875
704
|
// Emit the track end event
|
|
876
705
|
this.manager.emit(Manager_1.ManagerEventTypes.TrackEnd, player, track, payload);
|
|
877
706
|
// If autoplay is enabled, play the next track
|
|
878
|
-
if (this.manager.options.
|
|
707
|
+
if (this.manager.options.playNextOnEnd)
|
|
879
708
|
await player.play();
|
|
880
709
|
}
|
|
881
710
|
/**
|
|
@@ -888,19 +717,19 @@ class Node {
|
|
|
888
717
|
* @returns {Promise<void>} A promise that resolves when the queue end processing is complete.
|
|
889
718
|
*/
|
|
890
719
|
async queueEnd(player, track, payload) {
|
|
891
|
-
player.queue.
|
|
720
|
+
await player.queue.setCurrent(null);
|
|
892
721
|
if (!player.isAutoplay) {
|
|
893
722
|
player.playing = false;
|
|
894
723
|
this.manager.emit(Manager_1.ManagerEventTypes.QueueEnd, player, track, payload);
|
|
895
724
|
return;
|
|
896
725
|
}
|
|
897
|
-
let
|
|
726
|
+
let attempt = 1;
|
|
898
727
|
let success = false;
|
|
899
|
-
while (
|
|
900
|
-
success = await this.handleAutoplay(player,
|
|
728
|
+
while (attempt <= player.autoplayTries) {
|
|
729
|
+
success = await this.handleAutoplay(player, attempt);
|
|
901
730
|
if (success)
|
|
902
731
|
return;
|
|
903
|
-
|
|
732
|
+
attempt++;
|
|
904
733
|
}
|
|
905
734
|
// If all attempts fail, reset the player state and emit queueEnd
|
|
906
735
|
player.playing = false;
|