magmastream 2.9.0-dev.34 → 2.9.0-dev.35
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 +57 -19
- package/dist/structures/Manager.js +125 -63
- package/dist/structures/Node.js +6 -3
- package/dist/structures/Player.js +31 -20
- package/dist/structures/Queue.js +16 -8
- package/dist/structures/RedisQueue.js +20 -7
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -549,37 +549,68 @@ interface RedisConfig {
|
|
|
549
549
|
/**
|
|
550
550
|
* Player State Update Event
|
|
551
551
|
*/
|
|
552
|
-
|
|
553
|
-
changeType: PlayerStateEventTypes;
|
|
554
|
-
details
|
|
555
|
-
}
|
|
552
|
+
type PlayerStateUpdateEvent = {
|
|
553
|
+
changeType: PlayerStateEventTypes.TrackChange;
|
|
554
|
+
details: TrackChangeEvent;
|
|
555
|
+
} | {
|
|
556
|
+
changeType: PlayerStateEventTypes.PauseChange;
|
|
557
|
+
details: PauseChangeEvent;
|
|
558
|
+
} | {
|
|
559
|
+
changeType: PlayerStateEventTypes.QueueChange;
|
|
560
|
+
details: QueueChangeEvent;
|
|
561
|
+
} | {
|
|
562
|
+
changeType: PlayerStateEventTypes.ConnectionChange;
|
|
563
|
+
details: ConnectionChangeEvent;
|
|
564
|
+
} | {
|
|
565
|
+
changeType: PlayerStateEventTypes.AutoPlayChange;
|
|
566
|
+
details: AutoplayChangeEvent;
|
|
567
|
+
} | {
|
|
568
|
+
changeType: PlayerStateEventTypes.ChannelChange;
|
|
569
|
+
details: ChannelChangeEvent;
|
|
570
|
+
} | {
|
|
571
|
+
changeType: PlayerStateEventTypes.VolumeChange;
|
|
572
|
+
details: VolumeChangeEvent;
|
|
573
|
+
} | {
|
|
574
|
+
changeType: PlayerStateEventTypes.RepeatChange;
|
|
575
|
+
details: RepeatChangeEvent;
|
|
576
|
+
};
|
|
577
|
+
/**
|
|
578
|
+
* Player State Change Data
|
|
579
|
+
*/
|
|
580
|
+
type PlayerStateChangeData = AutoplayChangeEvent | ConnectionChangeEvent | RepeatChangeEvent | PauseChangeEvent | QueueChangeEvent | TrackChangeEvent | VolumeChangeEvent | ChannelChangeEvent;
|
|
556
581
|
/**
|
|
557
582
|
* Autoplay Change Event
|
|
558
583
|
*/
|
|
559
584
|
interface AutoplayChangeEvent {
|
|
560
|
-
|
|
561
|
-
|
|
585
|
+
type: "autoplay";
|
|
586
|
+
action: "toggle";
|
|
587
|
+
previousAutoplay: boolean | null;
|
|
588
|
+
currentAutoplay: boolean | null;
|
|
562
589
|
}
|
|
563
590
|
/**
|
|
564
591
|
* Connection Change Event
|
|
565
592
|
*/
|
|
566
593
|
interface ConnectionChangeEvent {
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
594
|
+
type: "connection";
|
|
595
|
+
action: "connect" | "disconnect";
|
|
596
|
+
previousConnection: boolean | null;
|
|
597
|
+
currentConnection: boolean | null;
|
|
570
598
|
}
|
|
571
599
|
/**
|
|
572
600
|
* Repeat Change Event
|
|
573
601
|
*/
|
|
574
602
|
interface RepeatChangeEvent {
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
603
|
+
type: "repeat";
|
|
604
|
+
action: "dynamic" | "track" | "queue" | "none";
|
|
605
|
+
previousRepeat: "dynamic" | "track" | "queue" | null;
|
|
606
|
+
currentRepeat: "dynamic" | "track" | "queue" | null;
|
|
578
607
|
}
|
|
579
608
|
/**
|
|
580
609
|
* Pause Change Event
|
|
581
610
|
*/
|
|
582
611
|
interface PauseChangeEvent {
|
|
612
|
+
type: "pause";
|
|
613
|
+
action: "pause" | "resume" | "toggle";
|
|
583
614
|
previousPause: boolean | null;
|
|
584
615
|
currentPause: boolean | null;
|
|
585
616
|
}
|
|
@@ -587,14 +618,18 @@ interface PauseChangeEvent {
|
|
|
587
618
|
* Queue Change Event
|
|
588
619
|
*/
|
|
589
620
|
interface QueueChangeEvent {
|
|
590
|
-
|
|
621
|
+
type: "queue";
|
|
622
|
+
action: "add" | "remove" | "clear" | "shuffle" | "roundRobin" | "userBlock" | "autoPlayAdd";
|
|
623
|
+
previousQueueLength: number | null;
|
|
624
|
+
currentQueueLength: number | null;
|
|
591
625
|
tracks?: Track[];
|
|
592
626
|
}
|
|
593
627
|
/**
|
|
594
628
|
* Track Change Event
|
|
595
629
|
*/
|
|
596
630
|
interface TrackChangeEvent {
|
|
597
|
-
|
|
631
|
+
type: "track";
|
|
632
|
+
action: "start" | "end" | "previous" | "timeUpdate" | "autoPlay";
|
|
598
633
|
track: Track;
|
|
599
634
|
previousTime?: number | null;
|
|
600
635
|
currentTime?: number | null;
|
|
@@ -603,6 +638,8 @@ interface TrackChangeEvent {
|
|
|
603
638
|
* Volume Change Event
|
|
604
639
|
*/
|
|
605
640
|
interface VolumeChangeEvent {
|
|
641
|
+
type: "volume";
|
|
642
|
+
action: "adjust";
|
|
606
643
|
previousVolume: number | null;
|
|
607
644
|
currentVolume: number | null;
|
|
608
645
|
}
|
|
@@ -610,7 +647,8 @@ interface VolumeChangeEvent {
|
|
|
610
647
|
* Channel Change Event
|
|
611
648
|
*/
|
|
612
649
|
interface ChannelChangeEvent {
|
|
613
|
-
|
|
650
|
+
type: "channel";
|
|
651
|
+
action: "text" | "voice";
|
|
614
652
|
previousChannel: string | null;
|
|
615
653
|
currentChannel: string | null;
|
|
616
654
|
}
|
|
@@ -1599,7 +1637,7 @@ declare class Node$1 {
|
|
|
1599
1637
|
* @param {TrackEndEvent} payload - The payload of the event emitted by the node.
|
|
1600
1638
|
* @private
|
|
1601
1639
|
*/
|
|
1602
|
-
|
|
1640
|
+
trackEnd(player: Player, track: Track, payload: TrackEndEvent): Promise<void>;
|
|
1603
1641
|
/**
|
|
1604
1642
|
* Handles autoplay logic for a player.
|
|
1605
1643
|
* This method is responsible for selecting an appropriate method of autoplay
|
|
@@ -1816,7 +1854,7 @@ declare class Node$1 {
|
|
|
1816
1854
|
}
|
|
1817
1855
|
|
|
1818
1856
|
/**
|
|
1819
|
-
* The main hub for interacting with Lavalink and using Magmastream
|
|
1857
|
+
* The main hub for interacting with Lavalink and using Magmastream.
|
|
1820
1858
|
*/
|
|
1821
1859
|
declare class Manager extends EventEmitter {
|
|
1822
1860
|
/** The map of players. */
|
|
@@ -2018,7 +2056,7 @@ declare class Manager extends EventEmitter {
|
|
|
2018
2056
|
* @param player The Player instance to serialize
|
|
2019
2057
|
* @returns The serialized Player instance
|
|
2020
2058
|
*/
|
|
2021
|
-
serializePlayer(player: Player): Record<string, unknown
|
|
2059
|
+
serializePlayer(player: Player): Promise<Record<string, unknown>>;
|
|
2022
2060
|
/**
|
|
2023
2061
|
* Checks for players that are no longer active and deletes their saved state files.
|
|
2024
2062
|
* This is done to prevent stale state files from accumulating on the file system.
|
|
@@ -2931,4 +2969,4 @@ declare class OceanicManager extends Manager {
|
|
|
2931
2969
|
}
|
|
2932
2970
|
|
|
2933
2971
|
export { AutoPlayPlatform, AutoPlayUtils, AvailableFilters, DetritusManager, DiscordJSManager, ErisManager, Filters, LoadTypes, Manager, ManagerEventTypes, Node$1 as Node, OceanicManager, Player, PlayerStateEventTypes, Plugin$1 as Plugin, Queue, Rest, SearchPlatform, SeverityTypes, SponsorBlockSegment, StateStorageType, StateTypes, Structure, TrackEndReasonTypes, TrackPartial, TrackSourceTypes, TrackUtils, UseNodeOptions };
|
|
2934
|
-
export type { CPUStats, DiscordPacket, EndSpeakingEventVoiceReceiver, EndSpeakingEventVoiceReceiverData, EqualizerBand, ErrorOrEmptySearchResult, Exception, Extendable, FrameStats, IQueue, LavaPlayer, LavalinkInfo, LavalinkResponse, LoadType, Lyrics, LyricsEvent, LyricsEventType, LyricsFoundEvent, LyricsLine, LyricsLineEvent, LyricsNotFoundEvent, ManagerEvents, ManagerInitOptions, ManagerOptions, MemoryStats, NodeLinkGetLyrics, NodeLinkGetLyricsEmpty, NodeLinkGetLyricsError, NodeLinkGetLyricsMultiple, NodeLinkGetLyricsSingle, NodeMessage, NodeOptions, NodeStats, Payload, PlayOptions, PlayerEvent, PlayerEventType, PlayerEvents, PlayerOptions, PlayerStateUpdateEvent, PlayerUpdate, PlayerUpdateVoiceState, PlaylistData, PlaylistInfoData, PlaylistRawData, PlaylistSearchResult, RedisConfig, SearchQuery, SearchResult, SearchSearchResult, Severity, Sizes, SponsorBlockChapterStarted, SponsorBlockChaptersLoaded, SponsorBlockSegmentEventType, SponsorBlockSegmentEvents, SponsorBlockSegmentSkipped, SponsorBlockSegmentsLoaded, StartSpeakingEventVoiceReceiver, StartSpeakingEventVoiceReceiverData, StateStorageOptions, Track, TrackData, TrackDataInfo, TrackEndEvent, TrackEndReason, TrackExceptionEvent, TrackPluginInfo, TrackSearchResult, TrackSourceName, TrackStartEvent, TrackStuckEvent, UseNodeOption, VoicePacket, VoiceReceiverEvent, VoiceServer, VoiceServerUpdate, VoiceState, WebSocketClosedEvent };
|
|
2972
|
+
export type { CPUStats, DiscordPacket, EndSpeakingEventVoiceReceiver, EndSpeakingEventVoiceReceiverData, EqualizerBand, ErrorOrEmptySearchResult, Exception, Extendable, FrameStats, IQueue, LavaPlayer, LavalinkInfo, LavalinkResponse, LoadType, Lyrics, LyricsEvent, LyricsEventType, LyricsFoundEvent, LyricsLine, LyricsLineEvent, LyricsNotFoundEvent, ManagerEvents, ManagerInitOptions, ManagerOptions, MemoryStats, NodeLinkGetLyrics, NodeLinkGetLyricsEmpty, NodeLinkGetLyricsError, NodeLinkGetLyricsMultiple, NodeLinkGetLyricsSingle, NodeMessage, NodeOptions, NodeStats, Payload, PlayOptions, PlayerEvent, PlayerEventType, PlayerEvents, PlayerOptions, PlayerStateChangeData, PlayerStateUpdateEvent, PlayerUpdate, PlayerUpdateVoiceState, PlaylistData, PlaylistInfoData, PlaylistRawData, PlaylistSearchResult, RedisConfig, SearchQuery, SearchResult, SearchSearchResult, Severity, Sizes, SponsorBlockChapterStarted, SponsorBlockChaptersLoaded, SponsorBlockSegmentEventType, SponsorBlockSegmentEvents, SponsorBlockSegmentSkipped, SponsorBlockSegmentsLoaded, StartSpeakingEventVoiceReceiver, StartSpeakingEventVoiceReceiverData, StateStorageOptions, Track, TrackData, TrackDataInfo, TrackEndEvent, TrackEndReason, TrackExceptionEvent, TrackPluginInfo, TrackSearchResult, TrackSourceName, TrackStartEvent, TrackStuckEvent, UseNodeOption, VoicePacket, VoiceReceiverEvent, VoiceServer, VoiceServerUpdate, VoiceState, WebSocketClosedEvent };
|
|
@@ -14,7 +14,7 @@ const path_1 = tslib_1.__importDefault(require("path"));
|
|
|
14
14
|
const ioredis_1 = tslib_1.__importDefault(require("ioredis"));
|
|
15
15
|
const Enums_1 = require("./Enums");
|
|
16
16
|
/**
|
|
17
|
-
* The main hub for interacting with Lavalink and using Magmastream
|
|
17
|
+
* The main hub for interacting with Lavalink and using Magmastream.
|
|
18
18
|
*/
|
|
19
19
|
class Manager extends events_1.EventEmitter {
|
|
20
20
|
/** The map of players. */
|
|
@@ -394,7 +394,7 @@ class Manager extends events_1.EventEmitter {
|
|
|
394
394
|
console.warn(`Skipping save for inactive player: ${guildId}`);
|
|
395
395
|
return;
|
|
396
396
|
}
|
|
397
|
-
const serializedPlayer = this.serializePlayer(player);
|
|
397
|
+
const serializedPlayer = await this.serializePlayer(player);
|
|
398
398
|
await promises_1.default.writeFile(playerStateFilePath, JSON.stringify(serializedPlayer, null, 2), "utf-8");
|
|
399
399
|
this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Player state saved: ${guildId}`);
|
|
400
400
|
}
|
|
@@ -411,7 +411,7 @@ class Manager extends events_1.EventEmitter {
|
|
|
411
411
|
console.warn(`Skipping save for inactive player: ${guildId}`);
|
|
412
412
|
return;
|
|
413
413
|
}
|
|
414
|
-
const serializedPlayer = this.serializePlayer(player);
|
|
414
|
+
const serializedPlayer = await this.serializePlayer(player);
|
|
415
415
|
const redisKey = `${this.options.stateStorage.redisConfig.prefix?.endsWith(":")
|
|
416
416
|
? this.options.stateStorage.redisConfig.prefix
|
|
417
417
|
: this.options.stateStorage.redisConfig.prefix ?? "magmastream:"}playerstore:${guildId}`;
|
|
@@ -491,39 +491,72 @@ class Manager extends events_1.EventEmitter {
|
|
|
491
491
|
const tracks = [];
|
|
492
492
|
const currentTrack = state.queue.current;
|
|
493
493
|
const queueTracks = state.queue.tracks;
|
|
494
|
-
if (
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
494
|
+
if (state.isAutoplay) {
|
|
495
|
+
Object.setPrototypeOf(state.data.clientUser, { constructor: { name: "User" } });
|
|
496
|
+
player.setAutoplay(true, state.data.clientUser, state.autoplayTries);
|
|
497
|
+
}
|
|
498
|
+
if (lavaPlayer?.track) {
|
|
499
|
+
// If lavaPlayer has a track, push all queue tracks
|
|
500
|
+
tracks.push(...queueTracks);
|
|
501
|
+
// Set current track if matches lavaPlayer's track URI
|
|
502
|
+
if (currentTrack && currentTrack.uri === lavaPlayer.track.info.uri) {
|
|
503
|
+
await player.queue.setCurrent(Utils_1.TrackUtils.build(lavaPlayer.track, currentTrack.requester));
|
|
500
504
|
}
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
};
|
|
506
|
-
await node.queueEnd(player, currentTrack, payload);
|
|
507
|
-
}
|
|
508
|
-
else {
|
|
509
|
-
tracks.push(currentTrack, ...queueTracks);
|
|
510
|
-
}
|
|
505
|
+
// Add tracks to queue
|
|
506
|
+
if (tracks.length > 0) {
|
|
507
|
+
await player.queue.clear();
|
|
508
|
+
await player.queue.add(tracks);
|
|
511
509
|
}
|
|
512
510
|
}
|
|
513
511
|
else {
|
|
514
|
-
|
|
515
|
-
|
|
512
|
+
// LavaPlayer missing track or lavaPlayer is falsy
|
|
513
|
+
if (currentTrack) {
|
|
514
|
+
if (queueTracks.length > 0) {
|
|
515
|
+
tracks.push(...queueTracks);
|
|
516
|
+
await player.queue.clear();
|
|
517
|
+
await player.queue.add(tracks);
|
|
518
|
+
}
|
|
519
|
+
await node.trackEnd(player, currentTrack, {
|
|
516
520
|
reason: Enums_1.TrackEndReasonTypes.Finished,
|
|
517
|
-
|
|
518
|
-
|
|
521
|
+
type: "TrackEndEvent",
|
|
522
|
+
});
|
|
519
523
|
}
|
|
520
524
|
else {
|
|
521
|
-
|
|
525
|
+
// No current track, check previous queue for last track
|
|
526
|
+
const previousQueue = await player.queue.getPrevious();
|
|
527
|
+
const lastTrack = previousQueue?.at(-1);
|
|
528
|
+
if (lastTrack) {
|
|
529
|
+
if (queueTracks.length === 0) {
|
|
530
|
+
// If no tracks in queue, end last track
|
|
531
|
+
await node.trackEnd(player, lastTrack, {
|
|
532
|
+
reason: Enums_1.TrackEndReasonTypes.Finished,
|
|
533
|
+
type: "TrackEndEvent",
|
|
534
|
+
});
|
|
535
|
+
}
|
|
536
|
+
else {
|
|
537
|
+
// If there are queued tracks, add them
|
|
538
|
+
tracks.push(...queueTracks);
|
|
539
|
+
if (tracks.length > 0) {
|
|
540
|
+
await player.queue.clear();
|
|
541
|
+
await player.queue.add(tracks);
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
else {
|
|
546
|
+
if (queueTracks.length > 0) {
|
|
547
|
+
tracks.push(...queueTracks);
|
|
548
|
+
if (tracks.length > 0) {
|
|
549
|
+
await player.queue.clear();
|
|
550
|
+
await player.queue.add(tracks);
|
|
551
|
+
}
|
|
552
|
+
await node.trackEnd(player, lastTrack, {
|
|
553
|
+
reason: Enums_1.TrackEndReasonTypes.Finished,
|
|
554
|
+
type: "TrackEndEvent",
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
}
|
|
522
558
|
}
|
|
523
559
|
}
|
|
524
|
-
if (tracks.length > 0) {
|
|
525
|
-
await player.queue.add(tracks);
|
|
526
|
-
}
|
|
527
560
|
if (state.queue.previous.length > 0) {
|
|
528
561
|
await player.queue.addPrevious(state.queue.previous);
|
|
529
562
|
}
|
|
@@ -543,10 +576,6 @@ class Manager extends events_1.EventEmitter {
|
|
|
543
576
|
if (state.dynamicRepeat) {
|
|
544
577
|
player.setDynamicRepeat(state.dynamicRepeat, state.dynamicLoopInterval._idleTimeout);
|
|
545
578
|
}
|
|
546
|
-
if (state.isAutoplay) {
|
|
547
|
-
Object.setPrototypeOf(state.data.clientUser, { constructor: { name: "User" } });
|
|
548
|
-
player.setAutoplay(true, state.data.clientUser, state.autoplayTries);
|
|
549
|
-
}
|
|
550
579
|
if (state.data) {
|
|
551
580
|
for (const [name, value] of Object.entries(state.data)) {
|
|
552
581
|
player.set(name, value);
|
|
@@ -661,39 +690,72 @@ class Manager extends events_1.EventEmitter {
|
|
|
661
690
|
const tracks = [];
|
|
662
691
|
const currentTrack = state.queue.current;
|
|
663
692
|
const queueTracks = state.queue.tracks;
|
|
664
|
-
if (
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
693
|
+
if (state.isAutoplay) {
|
|
694
|
+
Object.setPrototypeOf(state.data.clientUser, { constructor: { name: "User" } });
|
|
695
|
+
player.setAutoplay(true, state.data.clientUser, state.autoplayTries);
|
|
696
|
+
}
|
|
697
|
+
if (lavaPlayer?.track) {
|
|
698
|
+
// If lavaPlayer has a track, push all queue tracks
|
|
699
|
+
tracks.push(...queueTracks);
|
|
700
|
+
// Set current track if matches lavaPlayer's track URI
|
|
701
|
+
if (currentTrack && currentTrack.uri === lavaPlayer.track.info.uri) {
|
|
702
|
+
await player.queue.setCurrent(Utils_1.TrackUtils.build(lavaPlayer.track, currentTrack.requester));
|
|
670
703
|
}
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
};
|
|
676
|
-
await node.queueEnd(player, currentTrack, payload);
|
|
677
|
-
}
|
|
678
|
-
else {
|
|
679
|
-
tracks.push(currentTrack, ...queueTracks);
|
|
680
|
-
}
|
|
704
|
+
// Add tracks to queue
|
|
705
|
+
if (tracks.length > 0) {
|
|
706
|
+
await player.queue.clear();
|
|
707
|
+
await player.queue.add(tracks);
|
|
681
708
|
}
|
|
682
709
|
}
|
|
683
710
|
else {
|
|
684
|
-
|
|
685
|
-
|
|
711
|
+
// LavaPlayer missing track or lavaPlayer is falsy
|
|
712
|
+
if (currentTrack) {
|
|
713
|
+
if (queueTracks.length > 0) {
|
|
714
|
+
tracks.push(...queueTracks);
|
|
715
|
+
await player.queue.clear();
|
|
716
|
+
await player.queue.add(tracks);
|
|
717
|
+
}
|
|
718
|
+
await node.trackEnd(player, currentTrack, {
|
|
686
719
|
reason: Enums_1.TrackEndReasonTypes.Finished,
|
|
687
|
-
|
|
688
|
-
|
|
720
|
+
type: "TrackEndEvent",
|
|
721
|
+
});
|
|
689
722
|
}
|
|
690
723
|
else {
|
|
691
|
-
|
|
724
|
+
// No current track, check previous queue for last track
|
|
725
|
+
const previousQueue = await player.queue.getPrevious();
|
|
726
|
+
const lastTrack = previousQueue?.at(-1);
|
|
727
|
+
if (lastTrack) {
|
|
728
|
+
if (queueTracks.length === 0) {
|
|
729
|
+
// If no tracks in queue, end last track
|
|
730
|
+
await node.trackEnd(player, lastTrack, {
|
|
731
|
+
reason: Enums_1.TrackEndReasonTypes.Finished,
|
|
732
|
+
type: "TrackEndEvent",
|
|
733
|
+
});
|
|
734
|
+
}
|
|
735
|
+
else {
|
|
736
|
+
// If there are queued tracks, add them
|
|
737
|
+
tracks.push(...queueTracks);
|
|
738
|
+
if (tracks.length > 0) {
|
|
739
|
+
await player.queue.clear();
|
|
740
|
+
await player.queue.add(tracks);
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
else {
|
|
745
|
+
if (queueTracks.length > 0) {
|
|
746
|
+
tracks.push(...queueTracks);
|
|
747
|
+
if (tracks.length > 0) {
|
|
748
|
+
await player.queue.clear();
|
|
749
|
+
await player.queue.add(tracks);
|
|
750
|
+
}
|
|
751
|
+
await node.trackEnd(player, lastTrack, {
|
|
752
|
+
reason: Enums_1.TrackEndReasonTypes.Finished,
|
|
753
|
+
type: "TrackEndEvent",
|
|
754
|
+
});
|
|
755
|
+
}
|
|
756
|
+
}
|
|
692
757
|
}
|
|
693
758
|
}
|
|
694
|
-
if (tracks.length > 0) {
|
|
695
|
-
await player.queue.add(tracks);
|
|
696
|
-
}
|
|
697
759
|
if (state.queue.previous.length > 0) {
|
|
698
760
|
await player.queue.addPrevious(state.queue.previous);
|
|
699
761
|
}
|
|
@@ -713,10 +775,6 @@ class Manager extends events_1.EventEmitter {
|
|
|
713
775
|
if (state.dynamicRepeat) {
|
|
714
776
|
player.setDynamicRepeat(state.dynamicRepeat, state.dynamicLoopInterval._idleTimeout);
|
|
715
777
|
}
|
|
716
|
-
if (state.isAutoplay) {
|
|
717
|
-
Object.setPrototypeOf(state.data.clientUser, { constructor: { name: "User" } });
|
|
718
|
-
player.setAutoplay(true, state.data.clientUser, state.autoplayTries);
|
|
719
|
-
}
|
|
720
778
|
if (state.data) {
|
|
721
779
|
for (const [name, value] of Object.entries(state.data)) {
|
|
722
780
|
player.set(name, value);
|
|
@@ -988,8 +1046,12 @@ class Manager extends events_1.EventEmitter {
|
|
|
988
1046
|
* @param player The Player instance to serialize
|
|
989
1047
|
* @returns The serialized Player instance
|
|
990
1048
|
*/
|
|
991
|
-
serializePlayer(player) {
|
|
1049
|
+
async serializePlayer(player) {
|
|
992
1050
|
const seen = new WeakSet();
|
|
1051
|
+
// Fetch async queue data once before serializing
|
|
1052
|
+
const current = await player.queue.getCurrent();
|
|
1053
|
+
const tracks = Array.isArray(await player.queue.getTracks()) ? await player.queue.getTracks() : [];
|
|
1054
|
+
const previous = Array.isArray(await player.queue.getPrevious()) ? await player.queue.getPrevious() : [];
|
|
993
1055
|
/**
|
|
994
1056
|
* Recursively serializes an object, avoiding circular references.
|
|
995
1057
|
* @param obj The object to serialize
|
|
@@ -1025,9 +1087,9 @@ class Manager extends events_1.EventEmitter {
|
|
|
1025
1087
|
}
|
|
1026
1088
|
if (key === "queue") {
|
|
1027
1089
|
return {
|
|
1028
|
-
current
|
|
1029
|
-
tracks
|
|
1030
|
-
previous
|
|
1090
|
+
current,
|
|
1091
|
+
tracks,
|
|
1092
|
+
previous,
|
|
1031
1093
|
};
|
|
1032
1094
|
}
|
|
1033
1095
|
if (key === "data") {
|
package/dist/structures/Node.js
CHANGED
|
@@ -517,7 +517,8 @@ class Node {
|
|
|
517
517
|
this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, player, {
|
|
518
518
|
changeType: Enums_1.PlayerStateEventTypes.TrackChange,
|
|
519
519
|
details: {
|
|
520
|
-
|
|
520
|
+
type: "track",
|
|
521
|
+
action: "autoPlay",
|
|
521
522
|
track: track,
|
|
522
523
|
},
|
|
523
524
|
});
|
|
@@ -526,7 +527,8 @@ class Node {
|
|
|
526
527
|
this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, player, {
|
|
527
528
|
changeType: Enums_1.PlayerStateEventTypes.TrackChange,
|
|
528
529
|
details: {
|
|
529
|
-
|
|
530
|
+
type: "track",
|
|
531
|
+
action: "start",
|
|
530
532
|
track: track,
|
|
531
533
|
},
|
|
532
534
|
});
|
|
@@ -593,7 +595,8 @@ class Node {
|
|
|
593
595
|
this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, player, {
|
|
594
596
|
changeType: Enums_1.PlayerStateEventTypes.TrackChange,
|
|
595
597
|
details: {
|
|
596
|
-
|
|
598
|
+
type: "track",
|
|
599
|
+
action: "end",
|
|
597
600
|
track: track,
|
|
598
601
|
},
|
|
599
602
|
});
|
|
@@ -8,7 +8,6 @@ const Utils_1 = require("./Utils");
|
|
|
8
8
|
const _ = tslib_1.__importStar(require("lodash"));
|
|
9
9
|
const playerCheck_1 = tslib_1.__importDefault(require("../utils/playerCheck"));
|
|
10
10
|
const RedisQueue_1 = require("./RedisQueue");
|
|
11
|
-
// import { IQueue, Lyrics, PlayerOptions, PlayerUpdateVoiceState, PlayOptions, SearchQuery, SearchResult, Track, VoiceState } from "./Types";
|
|
12
11
|
const Enums_1 = require("./Enums");
|
|
13
12
|
const ws_1 = require("ws");
|
|
14
13
|
class Player {
|
|
@@ -181,7 +180,8 @@ class Player {
|
|
|
181
180
|
this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this, {
|
|
182
181
|
changeType: Enums_1.PlayerStateEventTypes.ConnectionChange,
|
|
183
182
|
details: {
|
|
184
|
-
|
|
183
|
+
type: "connection",
|
|
184
|
+
action: "connect",
|
|
185
185
|
previousConnection: oldPlayer?.state === Enums_1.StateTypes.Connected,
|
|
186
186
|
currentConnection: true,
|
|
187
187
|
},
|
|
@@ -216,7 +216,8 @@ class Player {
|
|
|
216
216
|
this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this, {
|
|
217
217
|
changeType: Enums_1.PlayerStateEventTypes.ConnectionChange,
|
|
218
218
|
details: {
|
|
219
|
-
|
|
219
|
+
type: "connection",
|
|
220
|
+
action: "disconnect",
|
|
220
221
|
previousConnection: oldPlayer.state === Enums_1.StateTypes.Connected,
|
|
221
222
|
currentConnection: false,
|
|
222
223
|
},
|
|
@@ -231,7 +232,6 @@ class Player {
|
|
|
231
232
|
* @emits {PlayerStateUpdate} - Emitted when the player state is updated.
|
|
232
233
|
*/
|
|
233
234
|
async destroy(disconnect = true) {
|
|
234
|
-
const oldPlayer = this ? { ...this } : null;
|
|
235
235
|
this.state = Enums_1.StateTypes.Destroying;
|
|
236
236
|
if (disconnect) {
|
|
237
237
|
await this.disconnect().catch((err) => {
|
|
@@ -244,9 +244,6 @@ class Player {
|
|
|
244
244
|
await this.queue.clear();
|
|
245
245
|
await this.queue.clearPrevious();
|
|
246
246
|
await this.queue.setCurrent(null);
|
|
247
|
-
this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, null, {
|
|
248
|
-
changeType: Enums_1.PlayerStateEventTypes.PlayerDestroy,
|
|
249
|
-
});
|
|
250
247
|
this.manager.emit(Enums_1.ManagerEventTypes.PlayerDestroy, this);
|
|
251
248
|
const deleted = this.manager.players.delete(this.guildId);
|
|
252
249
|
return deleted;
|
|
@@ -270,7 +267,8 @@ class Player {
|
|
|
270
267
|
this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this, {
|
|
271
268
|
changeType: Enums_1.PlayerStateEventTypes.ChannelChange,
|
|
272
269
|
details: {
|
|
273
|
-
|
|
270
|
+
type: "channel",
|
|
271
|
+
action: "voice",
|
|
274
272
|
previousChannel: oldPlayer.voiceChannelId || null,
|
|
275
273
|
currentChannel: this.voiceChannelId,
|
|
276
274
|
},
|
|
@@ -299,7 +297,8 @@ class Player {
|
|
|
299
297
|
this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this, {
|
|
300
298
|
changeType: Enums_1.PlayerStateEventTypes.ChannelChange,
|
|
301
299
|
details: {
|
|
302
|
-
|
|
300
|
+
type: "channel",
|
|
301
|
+
action: "text",
|
|
303
302
|
previousChannel: oldPlayer.textChannelId || null,
|
|
304
303
|
currentChannel: this.textChannelId,
|
|
305
304
|
},
|
|
@@ -378,6 +377,8 @@ class Player {
|
|
|
378
377
|
this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this, {
|
|
379
378
|
changeType: Enums_1.PlayerStateEventTypes.AutoPlayChange,
|
|
380
379
|
details: {
|
|
380
|
+
type: "autoplay",
|
|
381
|
+
action: "toggle",
|
|
381
382
|
previousAutoplay: oldPlayer.isAutoplay,
|
|
382
383
|
currentAutoplay: this.isAutoplay,
|
|
383
384
|
},
|
|
@@ -410,17 +411,19 @@ class Player {
|
|
|
410
411
|
if (volume < 0 || volume > 1000)
|
|
411
412
|
throw new RangeError("Volume must be between 0 and 1000.");
|
|
412
413
|
const oldVolume = this.volume;
|
|
414
|
+
const oldPlayer = { ...this };
|
|
413
415
|
await this.node.rest.updatePlayer({
|
|
414
416
|
guildId: this.options.guildId,
|
|
415
417
|
data: { volume },
|
|
416
418
|
});
|
|
417
419
|
this.volume = volume;
|
|
418
|
-
this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, {
|
|
420
|
+
this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this, {
|
|
419
421
|
changeType: Enums_1.PlayerStateEventTypes.VolumeChange,
|
|
420
422
|
details: {
|
|
423
|
+
type: "volume",
|
|
424
|
+
action: "adjust",
|
|
421
425
|
previousVolume: oldVolume,
|
|
422
426
|
currentVolume: this.volume,
|
|
423
|
-
isGradual: false,
|
|
424
427
|
},
|
|
425
428
|
});
|
|
426
429
|
return this;
|
|
@@ -477,8 +480,9 @@ class Player {
|
|
|
477
480
|
// Emit an event indicating the repeat mode has changed
|
|
478
481
|
this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this, {
|
|
479
482
|
changeType: Enums_1.PlayerStateEventTypes.RepeatChange,
|
|
480
|
-
|
|
481
|
-
|
|
483
|
+
details: {
|
|
484
|
+
type: "repeat",
|
|
485
|
+
action: "track",
|
|
482
486
|
previousRepeat: this.getRepeatState(oldPlayer),
|
|
483
487
|
currentRepeat: this.getRepeatState(this),
|
|
484
488
|
},
|
|
@@ -511,8 +515,9 @@ class Player {
|
|
|
511
515
|
// Emit the player state update event
|
|
512
516
|
this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this, {
|
|
513
517
|
changeType: Enums_1.PlayerStateEventTypes.RepeatChange,
|
|
514
|
-
|
|
515
|
-
|
|
518
|
+
details: {
|
|
519
|
+
type: "repeat",
|
|
520
|
+
action: "queue",
|
|
516
521
|
previousRepeat: this.getRepeatState(oldPlayer),
|
|
517
522
|
currentRepeat: this.getRepeatState(this),
|
|
518
523
|
},
|
|
@@ -567,8 +572,9 @@ class Player {
|
|
|
567
572
|
// Emit a player state update event
|
|
568
573
|
this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this, {
|
|
569
574
|
changeType: Enums_1.PlayerStateEventTypes.RepeatChange,
|
|
570
|
-
|
|
571
|
-
|
|
575
|
+
details: {
|
|
576
|
+
type: "repeat",
|
|
577
|
+
action: "dynamic",
|
|
572
578
|
previousRepeat: this.getRepeatState(oldPlayer),
|
|
573
579
|
currentRepeat: this.getRepeatState(this),
|
|
574
580
|
},
|
|
@@ -622,7 +628,8 @@ class Player {
|
|
|
622
628
|
this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this, {
|
|
623
629
|
changeType: Enums_1.PlayerStateEventTypes.QueueChange,
|
|
624
630
|
details: {
|
|
625
|
-
|
|
631
|
+
type: "queue",
|
|
632
|
+
action: "remove",
|
|
626
633
|
tracks: removedTracks,
|
|
627
634
|
},
|
|
628
635
|
});
|
|
@@ -657,6 +664,8 @@ class Player {
|
|
|
657
664
|
this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this, {
|
|
658
665
|
changeType: Enums_1.PlayerStateEventTypes.PauseChange,
|
|
659
666
|
details: {
|
|
667
|
+
type: "pause",
|
|
668
|
+
action: "pause",
|
|
660
669
|
previousPause: oldPlayer.paused,
|
|
661
670
|
currentPause: this.paused,
|
|
662
671
|
},
|
|
@@ -684,7 +693,8 @@ class Player {
|
|
|
684
693
|
this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this, {
|
|
685
694
|
changeType: Enums_1.PlayerStateEventTypes.TrackChange,
|
|
686
695
|
details: {
|
|
687
|
-
|
|
696
|
+
type: "track",
|
|
697
|
+
action: "previous",
|
|
688
698
|
track: lastTrack,
|
|
689
699
|
},
|
|
690
700
|
});
|
|
@@ -724,7 +734,8 @@ class Player {
|
|
|
724
734
|
this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this, {
|
|
725
735
|
changeType: Enums_1.PlayerStateEventTypes.TrackChange,
|
|
726
736
|
details: {
|
|
727
|
-
|
|
737
|
+
type: "track",
|
|
738
|
+
action: "timeUpdate",
|
|
728
739
|
previousTime: oldPlayer.position,
|
|
729
740
|
currentTime: this.position,
|
|
730
741
|
},
|
package/dist/structures/Queue.js
CHANGED
|
@@ -137,7 +137,8 @@ class Queue extends Array {
|
|
|
137
137
|
this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this.manager.players.get(this.guildId), {
|
|
138
138
|
changeType: Enums_1.PlayerStateEventTypes.QueueChange,
|
|
139
139
|
details: {
|
|
140
|
-
|
|
140
|
+
type: "queue",
|
|
141
|
+
action: "autoPlayAdd",
|
|
141
142
|
tracks: Array.isArray(track) ? track : [track],
|
|
142
143
|
},
|
|
143
144
|
});
|
|
@@ -149,7 +150,8 @@ class Queue extends Array {
|
|
|
149
150
|
this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this.manager.players.get(this.guildId), {
|
|
150
151
|
changeType: Enums_1.PlayerStateEventTypes.QueueChange,
|
|
151
152
|
details: {
|
|
152
|
-
|
|
153
|
+
type: "queue",
|
|
154
|
+
action: "add",
|
|
153
155
|
tracks: Array.isArray(track) ? track : [track],
|
|
154
156
|
},
|
|
155
157
|
});
|
|
@@ -169,7 +171,8 @@ class Queue extends Array {
|
|
|
169
171
|
this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this.manager.players.get(this.guildId), {
|
|
170
172
|
changeType: Enums_1.PlayerStateEventTypes.QueueChange,
|
|
171
173
|
details: {
|
|
172
|
-
|
|
174
|
+
type: "queue",
|
|
175
|
+
action: "remove",
|
|
173
176
|
tracks: removedTracks,
|
|
174
177
|
},
|
|
175
178
|
});
|
|
@@ -183,7 +186,8 @@ class Queue extends Array {
|
|
|
183
186
|
this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this.manager.players.get(this.guildId), {
|
|
184
187
|
changeType: Enums_1.PlayerStateEventTypes.QueueChange,
|
|
185
188
|
details: {
|
|
186
|
-
|
|
189
|
+
type: "queue",
|
|
190
|
+
action: "remove",
|
|
187
191
|
tracks: tracksToEmit,
|
|
188
192
|
},
|
|
189
193
|
});
|
|
@@ -202,7 +206,8 @@ class Queue extends Array {
|
|
|
202
206
|
this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this.manager.players.get(this.guildId), {
|
|
203
207
|
changeType: Enums_1.PlayerStateEventTypes.QueueChange,
|
|
204
208
|
details: {
|
|
205
|
-
|
|
209
|
+
type: "queue",
|
|
210
|
+
action: "clear",
|
|
206
211
|
tracks: [], // No tracks are left after clearing
|
|
207
212
|
},
|
|
208
213
|
});
|
|
@@ -225,7 +230,8 @@ class Queue extends Array {
|
|
|
225
230
|
this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this.manager.players.get(this.guildId), {
|
|
226
231
|
changeType: Enums_1.PlayerStateEventTypes.QueueChange,
|
|
227
232
|
details: {
|
|
228
|
-
|
|
233
|
+
type: "queue",
|
|
234
|
+
action: "shuffle",
|
|
229
235
|
},
|
|
230
236
|
});
|
|
231
237
|
// Emit a debug message indicating the queue has been shuffled for a specific guild ID.
|
|
@@ -265,7 +271,8 @@ class Queue extends Array {
|
|
|
265
271
|
this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this.manager.players.get(this.guildId), {
|
|
266
272
|
changeType: Enums_1.PlayerStateEventTypes.QueueChange,
|
|
267
273
|
details: {
|
|
268
|
-
|
|
274
|
+
type: "queue",
|
|
275
|
+
action: "userBlock",
|
|
269
276
|
},
|
|
270
277
|
});
|
|
271
278
|
// Emit a debug message indicating the queue has been shuffled for a specific guild ID.
|
|
@@ -315,7 +322,8 @@ class Queue extends Array {
|
|
|
315
322
|
this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this.manager.players.get(this.guildId), {
|
|
316
323
|
changeType: Enums_1.PlayerStateEventTypes.QueueChange,
|
|
317
324
|
details: {
|
|
318
|
-
|
|
325
|
+
type: "queue",
|
|
326
|
+
action: "roundRobin",
|
|
319
327
|
},
|
|
320
328
|
});
|
|
321
329
|
// Emit a debug message indicating the queue has been shuffled for a specific guild ID.
|
|
@@ -101,7 +101,8 @@ class RedisQueue {
|
|
|
101
101
|
this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this.manager.players.get(this.guildId), {
|
|
102
102
|
changeType: Enums_1.PlayerStateEventTypes.QueueChange,
|
|
103
103
|
details: {
|
|
104
|
-
|
|
104
|
+
type: "queue",
|
|
105
|
+
action: "autoPlayAdd",
|
|
105
106
|
tracks: Array.isArray(track) ? track : [track],
|
|
106
107
|
},
|
|
107
108
|
});
|
|
@@ -112,7 +113,8 @@ class RedisQueue {
|
|
|
112
113
|
this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this.manager.players.get(this.guildId), {
|
|
113
114
|
changeType: Enums_1.PlayerStateEventTypes.QueueChange,
|
|
114
115
|
details: {
|
|
115
|
-
|
|
116
|
+
type: "queue",
|
|
117
|
+
action: "add",
|
|
116
118
|
tracks,
|
|
117
119
|
},
|
|
118
120
|
});
|
|
@@ -140,7 +142,8 @@ class RedisQueue {
|
|
|
140
142
|
this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this.manager.players.get(this.guildId), {
|
|
141
143
|
changeType: Enums_1.PlayerStateEventTypes.QueueChange,
|
|
142
144
|
details: {
|
|
143
|
-
|
|
145
|
+
type: "queue",
|
|
146
|
+
action: "remove",
|
|
144
147
|
tracks: deserialized,
|
|
145
148
|
},
|
|
146
149
|
});
|
|
@@ -152,7 +155,8 @@ class RedisQueue {
|
|
|
152
155
|
this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this.manager.players.get(this.guildId), {
|
|
153
156
|
changeType: Enums_1.PlayerStateEventTypes.QueueChange,
|
|
154
157
|
details: {
|
|
155
|
-
|
|
158
|
+
type: "queue",
|
|
159
|
+
action: "clear",
|
|
156
160
|
tracks: [],
|
|
157
161
|
},
|
|
158
162
|
});
|
|
@@ -192,7 +196,10 @@ class RedisQueue {
|
|
|
192
196
|
}
|
|
193
197
|
this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this.manager.players.get(this.guildId), {
|
|
194
198
|
changeType: Enums_1.PlayerStateEventTypes.QueueChange,
|
|
195
|
-
details: {
|
|
199
|
+
details: {
|
|
200
|
+
type: "queue",
|
|
201
|
+
action: "shuffle",
|
|
202
|
+
},
|
|
196
203
|
});
|
|
197
204
|
this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[QUEUE] Shuffled the queue for: ${this.guildId}`);
|
|
198
205
|
}
|
|
@@ -219,7 +226,10 @@ class RedisQueue {
|
|
|
219
226
|
await this.redis.rpush(this.queueKey, ...shuffledQueue.map(this.serialize));
|
|
220
227
|
this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this.manager.players.get(this.guildId), {
|
|
221
228
|
changeType: Enums_1.PlayerStateEventTypes.QueueChange,
|
|
222
|
-
details: {
|
|
229
|
+
details: {
|
|
230
|
+
type: "queue",
|
|
231
|
+
action: "userBlock",
|
|
232
|
+
},
|
|
223
233
|
});
|
|
224
234
|
this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[QUEUE] userBlockShuffled the queue for: ${this.guildId}`);
|
|
225
235
|
}
|
|
@@ -255,7 +265,10 @@ class RedisQueue {
|
|
|
255
265
|
await this.redis.rpush(this.queueKey, ...shuffledQueue.map(this.serialize));
|
|
256
266
|
this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this.manager.players.get(this.guildId), {
|
|
257
267
|
changeType: Enums_1.PlayerStateEventTypes.QueueChange,
|
|
258
|
-
details: {
|
|
268
|
+
details: {
|
|
269
|
+
type: "queue",
|
|
270
|
+
action: "roundRobin",
|
|
271
|
+
},
|
|
259
272
|
});
|
|
260
273
|
this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[QUEUE] roundRobinShuffled the queue for: ${this.guildId}`);
|
|
261
274
|
}
|