lavalink-client 2.7.6 → 2.7.8
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 +1 -0
- package/dist/index.d.mts +285 -0
- package/dist/index.d.ts +285 -0
- package/dist/index.js +255 -2
- package/dist/index.mjs +255 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
- ✨ **Flexible Queue Stores:** Use the default in-memory store or bring your own (Redis, databases, etc.) to sync queues across multiple processes.
|
|
33
33
|
- 🎶 **Unresolved Tracks:** Supports unresolved track objects, fetching full data only when a track is about to play, saving API requests and resources.
|
|
34
34
|
- 🎚️ **Built-in Filters & EQ:** Easy-to-use management for audio filters and equalizers.
|
|
35
|
+
- 🔍 **Advanced Queue Filtering:** Search and filter tracks in the queue by title, author, duration, and more with powerful query options.
|
|
35
36
|
- ⚙️ **Advanced Player Options:** Fine-tune player behavior for disconnects, empty queues, volume handling, and more.
|
|
36
37
|
- 🛡️ **Lavalink-Side Validation:** Ensures you only use filters, plugins, and sources that your Lavalink node actually supports.
|
|
37
38
|
- 🔒 **Client-Side Validation:** Whitelist and blacklist URLs or domains to prevent unwanted requests and protect your bot.
|
package/dist/index.d.mts
CHANGED
|
@@ -704,6 +704,77 @@ declare class Queue {
|
|
|
704
704
|
* @returns {number}
|
|
705
705
|
*/
|
|
706
706
|
totalDuration: () => number;
|
|
707
|
+
/**
|
|
708
|
+
* Find tracks in the queue matching specific criteria.
|
|
709
|
+
* **This method DOES NOT MUTATE the queue** - it returns a new array without modifying the original queue.
|
|
710
|
+
* @param predicate Function to test each track, or an object with criteria to match
|
|
711
|
+
* @returns Array of matching tracks with their indexes
|
|
712
|
+
*
|
|
713
|
+
* @example
|
|
714
|
+
* ```ts
|
|
715
|
+
* // Find by author
|
|
716
|
+
* const artistTracks = player.queue.utils.filterTracks({ author: "Artist Name" });
|
|
717
|
+
*
|
|
718
|
+
* // Find by duration range (5-10 minutes)
|
|
719
|
+
* const longTracks = player.queue.utils.filterTracks({ duration: { min: 300000, max: 600000 } });
|
|
720
|
+
*
|
|
721
|
+
* // Find by title (partial match)
|
|
722
|
+
* const titleMatches = player.queue.utils.filterTracks({ title: "Never Gonna" });
|
|
723
|
+
*
|
|
724
|
+
* // Custom predicate
|
|
725
|
+
* const customFilter = player.queue.utils.filterTracks(track => track.info.isStream);
|
|
726
|
+
* ```
|
|
727
|
+
*/
|
|
728
|
+
filterTracks: (predicate: ((track: Track | UnresolvedTrack, index: number) => boolean) | {
|
|
729
|
+
title?: string;
|
|
730
|
+
author?: string;
|
|
731
|
+
duration?: number | {
|
|
732
|
+
min?: number;
|
|
733
|
+
max?: number;
|
|
734
|
+
};
|
|
735
|
+
uri?: string;
|
|
736
|
+
identifier?: string;
|
|
737
|
+
sourceName?: string;
|
|
738
|
+
isStream?: boolean;
|
|
739
|
+
isSeekable?: boolean;
|
|
740
|
+
}) => Array<{
|
|
741
|
+
track: Track | UnresolvedTrack;
|
|
742
|
+
index: number;
|
|
743
|
+
}>;
|
|
744
|
+
/**
|
|
745
|
+
* Find a single track in the queue matching specific criteria.
|
|
746
|
+
* **This method DOES NOT MUTATE the queue** - it searches without modifying the original queue.
|
|
747
|
+
* @param predicate Function to test each track, or an object with criteria to match
|
|
748
|
+
* @returns First matching track with its index, or null if not found
|
|
749
|
+
*
|
|
750
|
+
* @example
|
|
751
|
+
* ```ts
|
|
752
|
+
* // Find first track by author
|
|
753
|
+
* const track = player.queue.utils.findTrack({ author: "Artist Name" });
|
|
754
|
+
* if (track) {
|
|
755
|
+
* console.log(`Found at index ${track.index}: ${track.track.info.title}`);
|
|
756
|
+
* }
|
|
757
|
+
*
|
|
758
|
+
* // Find with custom predicate
|
|
759
|
+
* const liveStream = player.queue.utils.findTrack(track => track.info.isStream);
|
|
760
|
+
* ```
|
|
761
|
+
*/
|
|
762
|
+
findTrack: (predicate: ((track: Track | UnresolvedTrack, index: number) => boolean) | {
|
|
763
|
+
title?: string;
|
|
764
|
+
author?: string;
|
|
765
|
+
duration?: number | {
|
|
766
|
+
min?: number;
|
|
767
|
+
max?: number;
|
|
768
|
+
};
|
|
769
|
+
uri?: string;
|
|
770
|
+
identifier?: string;
|
|
771
|
+
sourceName?: string;
|
|
772
|
+
isStream?: boolean;
|
|
773
|
+
isSeekable?: boolean;
|
|
774
|
+
}) => {
|
|
775
|
+
track: Track | UnresolvedTrack;
|
|
776
|
+
index: number;
|
|
777
|
+
} | null;
|
|
707
778
|
};
|
|
708
779
|
/**
|
|
709
780
|
* Shuffles the current Queue, then saves it
|
|
@@ -773,6 +844,126 @@ declare class Queue {
|
|
|
773
844
|
* ```
|
|
774
845
|
*/
|
|
775
846
|
shiftPrevious(): Promise<Track>;
|
|
847
|
+
/**
|
|
848
|
+
* Find tracks in the queue matching specific criteria.
|
|
849
|
+
* **This method DOES NOT MUTATE the queue** - it returns a new array without modifying the original queue.
|
|
850
|
+
* @deprecated Use `player.queue.utils.filterTracks()` instead.
|
|
851
|
+
* @param predicate Function to test each track, or an object with criteria to match
|
|
852
|
+
* @returns Array of matching tracks with their indexes
|
|
853
|
+
*
|
|
854
|
+
* @example
|
|
855
|
+
* ```ts
|
|
856
|
+
* // Use the new method instead:
|
|
857
|
+
* const artistTracks = player.queue.utils.filterTracks({ author: "Artist Name" });
|
|
858
|
+
* ```
|
|
859
|
+
*/
|
|
860
|
+
filter(predicate: ((track: Track | UnresolvedTrack, index: number) => boolean) | {
|
|
861
|
+
title?: string;
|
|
862
|
+
author?: string;
|
|
863
|
+
duration?: number | {
|
|
864
|
+
min?: number;
|
|
865
|
+
max?: number;
|
|
866
|
+
};
|
|
867
|
+
uri?: string;
|
|
868
|
+
identifier?: string;
|
|
869
|
+
sourceName?: string;
|
|
870
|
+
isStream?: boolean;
|
|
871
|
+
isSeekable?: boolean;
|
|
872
|
+
}): Array<{
|
|
873
|
+
track: Track | UnresolvedTrack;
|
|
874
|
+
index: number;
|
|
875
|
+
}>;
|
|
876
|
+
/**
|
|
877
|
+
* Find a single track in the queue matching specific criteria.
|
|
878
|
+
* **This method DOES NOT MUTATE the queue** - it searches without modifying the original queue.
|
|
879
|
+
* @deprecated Use `player.queue.utils.findTrack()` instead.
|
|
880
|
+
* @param predicate Function to test each track, or an object with criteria to match
|
|
881
|
+
* @returns First matching track with its index, or null if not found
|
|
882
|
+
*
|
|
883
|
+
* @example
|
|
884
|
+
* ```ts
|
|
885
|
+
* // Use the new method instead:
|
|
886
|
+
* const track = player.queue.utils.findTrack({ author: "Artist Name" });
|
|
887
|
+
* ```
|
|
888
|
+
*/
|
|
889
|
+
find(predicate: ((track: Track | UnresolvedTrack, index: number) => boolean) | {
|
|
890
|
+
title?: string;
|
|
891
|
+
author?: string;
|
|
892
|
+
duration?: number | {
|
|
893
|
+
min?: number;
|
|
894
|
+
max?: number;
|
|
895
|
+
};
|
|
896
|
+
uri?: string;
|
|
897
|
+
identifier?: string;
|
|
898
|
+
sourceName?: string;
|
|
899
|
+
isStream?: boolean;
|
|
900
|
+
isSeekable?: boolean;
|
|
901
|
+
}): {
|
|
902
|
+
track: Track | UnresolvedTrack;
|
|
903
|
+
index: number;
|
|
904
|
+
} | null;
|
|
905
|
+
/**
|
|
906
|
+
* Sort the queue tracks by a specific property.
|
|
907
|
+
* **⚠️ This method MUTATES the queue** - it modifies the original queue in place.
|
|
908
|
+
* @param sortBy Property to sort by or custom comparator function
|
|
909
|
+
* @param order Sort order: 'asc' or 'desc' (default: 'asc')
|
|
910
|
+
* @returns The queue instance for chaining
|
|
911
|
+
*
|
|
912
|
+
* @example
|
|
913
|
+
* ```ts
|
|
914
|
+
* // Sort by duration (shortest first)
|
|
915
|
+
* await player.queue.sortBy("duration", "asc");
|
|
916
|
+
*
|
|
917
|
+
* // Sort by title alphabetically (Z-A)
|
|
918
|
+
* await player.queue.sortBy("title", "desc");
|
|
919
|
+
*
|
|
920
|
+
* // Custom sorting
|
|
921
|
+
* await player.queue.sortBy((a, b) => {
|
|
922
|
+
* return a.info.title.localeCompare(b.info.title);
|
|
923
|
+
* });
|
|
924
|
+
* ```
|
|
925
|
+
*/
|
|
926
|
+
sortBy(sortBy: "duration" | "title" | "author" | ((a: Track | UnresolvedTrack, b: Track | UnresolvedTrack) => number), order?: "asc" | "desc"): Promise<this>;
|
|
927
|
+
/**
|
|
928
|
+
* Get a sorted copy of the queue tracks without modifying the original queue.
|
|
929
|
+
* **This method DOES NOT MUTATE the queue** - it returns a new sorted array, similar to `Array.toSorted()`.
|
|
930
|
+
* @param sortBy Property to sort by or custom comparator function
|
|
931
|
+
* @param order Sort order: 'asc' or 'desc' (default: 'asc')
|
|
932
|
+
* @returns A new sorted array of tracks (does not modify the queue)
|
|
933
|
+
*
|
|
934
|
+
* @example
|
|
935
|
+
* ```ts
|
|
936
|
+
* // Get sorted copy by duration (shortest first)
|
|
937
|
+
* const sortedTracks = player.queue.toSortedBy("duration", "asc");
|
|
938
|
+
* // Original queue remains unchanged
|
|
939
|
+
*
|
|
940
|
+
* // Get sorted copy by title alphabetically (Z-A)
|
|
941
|
+
* const sortedByTitle = player.queue.toSortedBy("title", "desc");
|
|
942
|
+
*
|
|
943
|
+
* // Custom sorting
|
|
944
|
+
* const customSorted = player.queue.toSortedBy((a, b) => {
|
|
945
|
+
* return a.info.title.localeCompare(b.info.title);
|
|
946
|
+
* });
|
|
947
|
+
* ```
|
|
948
|
+
*/
|
|
949
|
+
toSortedBy(sortBy: "duration" | "title" | "author" | ((a: Track | UnresolvedTrack, b: Track | UnresolvedTrack) => number), order?: "asc" | "desc"): (Track | UnresolvedTrack)[];
|
|
950
|
+
/**
|
|
951
|
+
* Get a range of tracks from the queue.
|
|
952
|
+
* **This method DOES NOT MUTATE the queue** - it returns a new array slice, similar to `Array.slice()`.
|
|
953
|
+
* @param start Start index (inclusive)
|
|
954
|
+
* @param end End index (exclusive)
|
|
955
|
+
* @returns Array of tracks in the specified range
|
|
956
|
+
*
|
|
957
|
+
* @example
|
|
958
|
+
* ```ts
|
|
959
|
+
* // Get tracks 5-15
|
|
960
|
+
* const tracks = player.queue.getTracks(5, 15);
|
|
961
|
+
*
|
|
962
|
+
* // Get first 10 tracks
|
|
963
|
+
* const firstTen = player.queue.getTracks(0, 10);
|
|
964
|
+
* ```
|
|
965
|
+
*/
|
|
966
|
+
getTracks(start: number, end?: number): (Track | UnresolvedTrack)[];
|
|
776
967
|
}
|
|
777
968
|
|
|
778
969
|
declare class Player {
|
|
@@ -1959,6 +2150,66 @@ interface PlayOptions extends LavalinkPlayOptions {
|
|
|
1959
2150
|
clientTrack?: Track | UnresolvedTrack;
|
|
1960
2151
|
}
|
|
1961
2152
|
|
|
2153
|
+
type NodeLinkEventTypes = "PlayerCreatedEvent" | "PlayerDestroyedEvent" | "PlayerConnectedEvent" | "PlayerReconnectingEvent" | "VolumeChangedEvent" | "FiltersChangedEvent" | "SeekEvent" | "PauseEvent" | "ConnectionStatusEvent" | "MixStartedEvent" | "MixEndedEvent";
|
|
2154
|
+
interface NodeLinkBaseEvent {
|
|
2155
|
+
op: "event";
|
|
2156
|
+
type: NodeLinkEventTypes;
|
|
2157
|
+
guildId: string;
|
|
2158
|
+
}
|
|
2159
|
+
interface PlayerCreatedEvent extends NodeLinkBaseEvent {
|
|
2160
|
+
type: "PlayerCreatedEvent";
|
|
2161
|
+
}
|
|
2162
|
+
interface PlayerDestroyedEvent extends NodeLinkBaseEvent {
|
|
2163
|
+
type: "PlayerDestroyedEvent";
|
|
2164
|
+
}
|
|
2165
|
+
interface PlayerConnectedEvent extends NodeLinkBaseEvent {
|
|
2166
|
+
type: "PlayerConnectedEvent";
|
|
2167
|
+
}
|
|
2168
|
+
interface PlayerReconnectingEvent extends NodeLinkBaseEvent {
|
|
2169
|
+
type: "PlayerReconnectingEvent";
|
|
2170
|
+
}
|
|
2171
|
+
interface VolumeChangedEvent extends NodeLinkBaseEvent {
|
|
2172
|
+
type: "VolumeChangedEvent";
|
|
2173
|
+
/** New volume level (0-1000) */
|
|
2174
|
+
volume: number;
|
|
2175
|
+
}
|
|
2176
|
+
interface FiltersChangedEvent extends NodeLinkBaseEvent {
|
|
2177
|
+
type: "FiltersChangedEvent";
|
|
2178
|
+
filters: LavalinkFilterData;
|
|
2179
|
+
}
|
|
2180
|
+
interface SeekEvent extends NodeLinkBaseEvent {
|
|
2181
|
+
type: "SeekEvent";
|
|
2182
|
+
/** New position in milliseconds */
|
|
2183
|
+
position: number;
|
|
2184
|
+
}
|
|
2185
|
+
interface PauseEvent extends NodeLinkBaseEvent {
|
|
2186
|
+
type: "PauseEvent";
|
|
2187
|
+
/** Whether playback is now paused (true) or resumed (false) */
|
|
2188
|
+
paused: boolean;
|
|
2189
|
+
}
|
|
2190
|
+
interface ConnectionStatusEvent extends NodeLinkBaseEvent {
|
|
2191
|
+
type: "ConnectionStatusEvent";
|
|
2192
|
+
/** Current connection status */
|
|
2193
|
+
connected: boolean;
|
|
2194
|
+
}
|
|
2195
|
+
interface MixStartedEvent extends NodeLinkBaseEvent {
|
|
2196
|
+
type: "MixStartedEvent";
|
|
2197
|
+
/** Unique identifier for the mix layer */
|
|
2198
|
+
mixId: string;
|
|
2199
|
+
/** Full track information of the mixed layer */
|
|
2200
|
+
track: LavalinkTrack;
|
|
2201
|
+
/** Volume of the mixed layer (0.0 to 1.0) */
|
|
2202
|
+
volume: number;
|
|
2203
|
+
}
|
|
2204
|
+
interface MixEndedEvent extends NodeLinkBaseEvent {
|
|
2205
|
+
type: "MixEndedEvent";
|
|
2206
|
+
/** Unique identifier for the mix layer */
|
|
2207
|
+
mixId: string;
|
|
2208
|
+
/** Reason the mix layer ended (FINISHED, REMOVED, ERROR, MAIN_ENDED) */
|
|
2209
|
+
reason: "FINISHED" | "REMOVED" | "ERROR" | "MAIN_ENDED" | string;
|
|
2210
|
+
}
|
|
2211
|
+
type NodeLinkEventPayload<T extends NodeLinkEventTypes> = T extends "PlayerCreatedEvent" ? PlayerCreatedEvent : T extends "PlayerDestroyedEvent" ? PlayerDestroyedEvent : T extends "PlayerConnectedEvent" ? PlayerConnectedEvent : T extends "PlayerReconnectingEvent" ? PlayerReconnectingEvent : T extends "VolumeChangedEvent" ? VolumeChangedEvent : T extends "FiltersChangedEvent" ? FiltersChangedEvent : T extends "SeekEvent" ? SeekEvent : T extends "PauseEvent" ? PauseEvent : T extends "ConnectionStatusEvent" ? ConnectionStatusEvent : T extends "MixStartedEvent" ? MixStartedEvent : T extends "MixEndedEvent" ? MixEndedEvent : never;
|
|
2212
|
+
|
|
1962
2213
|
/** Ability to manipulate fetch requests */
|
|
1963
2214
|
type ModifyRequest = (options: RequestInit & {
|
|
1964
2215
|
path: string;
|
|
@@ -2224,6 +2475,25 @@ interface NodeManagerEvents {
|
|
|
2224
2475
|
sessionId: string;
|
|
2225
2476
|
op: "ready";
|
|
2226
2477
|
}, players: LavalinkPlayer[] | InvalidLavalinkRestRequest) => void;
|
|
2478
|
+
/**
|
|
2479
|
+
* Event Handler for Nodelink specific events https://nodelink.js.org/docs/api/websocket Fully typed and generic based on the eventName.
|
|
2480
|
+
* @event Manager.nodeManager#nodeLinkEvent
|
|
2481
|
+
* @example
|
|
2482
|
+
*
|
|
2483
|
+
* ```ts
|
|
2484
|
+
* this.nodeManager.on("nodeLinkEvent", (node, event, player, track, payload) => {
|
|
2485
|
+
* if (event === "SeekEvent") {
|
|
2486
|
+
* console.log("new position:", payload.position);
|
|
2487
|
+
* }
|
|
2488
|
+
* if (event === "FiltersChangedEvent") {
|
|
2489
|
+
* console.log("new filters state", payload.filters);
|
|
2490
|
+
* }
|
|
2491
|
+
* });
|
|
2492
|
+
* ```
|
|
2493
|
+
*/
|
|
2494
|
+
"nodeLinkEvent": (...args: {
|
|
2495
|
+
[K in NodeLinkEventTypes]: [node: LavalinkNode, event: K, player: Player, track: Track | null, payload: NodeLinkEventPayload<K>];
|
|
2496
|
+
}[NodeLinkEventTypes]) => void;
|
|
2227
2497
|
}
|
|
2228
2498
|
declare enum ReconnectionState {
|
|
2229
2499
|
IDLE = "IDLE",
|
|
@@ -2721,6 +2991,14 @@ declare class LavalinkNode {
|
|
|
2721
2991
|
private message;
|
|
2722
2992
|
/** @private middleware util function for handling all kind of events from websocket */
|
|
2723
2993
|
private handleEvent;
|
|
2994
|
+
/**
|
|
2995
|
+
* nodeLink specific events handling https://nodelink.js.org/docs/api/websocket#incoming-events-server--client
|
|
2996
|
+
* @param eventName
|
|
2997
|
+
* @param player
|
|
2998
|
+
* @param track
|
|
2999
|
+
* @param payload
|
|
3000
|
+
*/
|
|
3001
|
+
private nodeLinkEventHandler;
|
|
2724
3002
|
private getTrackOfPayload;
|
|
2725
3003
|
/** @private util function for handling trackStart event */
|
|
2726
3004
|
private trackStart;
|
|
@@ -2944,6 +3222,13 @@ interface LavalinkManagerEvents<CustomPlayerT extends Player = Player> {
|
|
|
2944
3222
|
* @event Manager#playerDisconnect
|
|
2945
3223
|
*/
|
|
2946
3224
|
"playerDisconnect": (player: CustomPlayerT, voiceChannelId: string) => void;
|
|
3225
|
+
/**
|
|
3226
|
+
* Emitted when a Player automatically reconnects after a disconnect.
|
|
3227
|
+
* This event is triggered when the player successfully reconnects to the voice channel
|
|
3228
|
+
* and resumes playback after being disconnected (requires onDisconnect.autoReconnect to be enabled).
|
|
3229
|
+
* @event Manager#playerReconnect
|
|
3230
|
+
*/
|
|
3231
|
+
"playerReconnect": (player: CustomPlayerT, voiceChannelId: string) => void;
|
|
2947
3232
|
/**
|
|
2948
3233
|
* Emitted when a Node-Socket got closed for a specific Player.
|
|
2949
3234
|
* Usually emits when the audio websocket to discord is closed, This can happen for various reasons (normal and abnormal), e.g. when using an expired voice server update. 4xxx codes are usually bad.
|
package/dist/index.d.ts
CHANGED
|
@@ -704,6 +704,77 @@ declare class Queue {
|
|
|
704
704
|
* @returns {number}
|
|
705
705
|
*/
|
|
706
706
|
totalDuration: () => number;
|
|
707
|
+
/**
|
|
708
|
+
* Find tracks in the queue matching specific criteria.
|
|
709
|
+
* **This method DOES NOT MUTATE the queue** - it returns a new array without modifying the original queue.
|
|
710
|
+
* @param predicate Function to test each track, or an object with criteria to match
|
|
711
|
+
* @returns Array of matching tracks with their indexes
|
|
712
|
+
*
|
|
713
|
+
* @example
|
|
714
|
+
* ```ts
|
|
715
|
+
* // Find by author
|
|
716
|
+
* const artistTracks = player.queue.utils.filterTracks({ author: "Artist Name" });
|
|
717
|
+
*
|
|
718
|
+
* // Find by duration range (5-10 minutes)
|
|
719
|
+
* const longTracks = player.queue.utils.filterTracks({ duration: { min: 300000, max: 600000 } });
|
|
720
|
+
*
|
|
721
|
+
* // Find by title (partial match)
|
|
722
|
+
* const titleMatches = player.queue.utils.filterTracks({ title: "Never Gonna" });
|
|
723
|
+
*
|
|
724
|
+
* // Custom predicate
|
|
725
|
+
* const customFilter = player.queue.utils.filterTracks(track => track.info.isStream);
|
|
726
|
+
* ```
|
|
727
|
+
*/
|
|
728
|
+
filterTracks: (predicate: ((track: Track | UnresolvedTrack, index: number) => boolean) | {
|
|
729
|
+
title?: string;
|
|
730
|
+
author?: string;
|
|
731
|
+
duration?: number | {
|
|
732
|
+
min?: number;
|
|
733
|
+
max?: number;
|
|
734
|
+
};
|
|
735
|
+
uri?: string;
|
|
736
|
+
identifier?: string;
|
|
737
|
+
sourceName?: string;
|
|
738
|
+
isStream?: boolean;
|
|
739
|
+
isSeekable?: boolean;
|
|
740
|
+
}) => Array<{
|
|
741
|
+
track: Track | UnresolvedTrack;
|
|
742
|
+
index: number;
|
|
743
|
+
}>;
|
|
744
|
+
/**
|
|
745
|
+
* Find a single track in the queue matching specific criteria.
|
|
746
|
+
* **This method DOES NOT MUTATE the queue** - it searches without modifying the original queue.
|
|
747
|
+
* @param predicate Function to test each track, or an object with criteria to match
|
|
748
|
+
* @returns First matching track with its index, or null if not found
|
|
749
|
+
*
|
|
750
|
+
* @example
|
|
751
|
+
* ```ts
|
|
752
|
+
* // Find first track by author
|
|
753
|
+
* const track = player.queue.utils.findTrack({ author: "Artist Name" });
|
|
754
|
+
* if (track) {
|
|
755
|
+
* console.log(`Found at index ${track.index}: ${track.track.info.title}`);
|
|
756
|
+
* }
|
|
757
|
+
*
|
|
758
|
+
* // Find with custom predicate
|
|
759
|
+
* const liveStream = player.queue.utils.findTrack(track => track.info.isStream);
|
|
760
|
+
* ```
|
|
761
|
+
*/
|
|
762
|
+
findTrack: (predicate: ((track: Track | UnresolvedTrack, index: number) => boolean) | {
|
|
763
|
+
title?: string;
|
|
764
|
+
author?: string;
|
|
765
|
+
duration?: number | {
|
|
766
|
+
min?: number;
|
|
767
|
+
max?: number;
|
|
768
|
+
};
|
|
769
|
+
uri?: string;
|
|
770
|
+
identifier?: string;
|
|
771
|
+
sourceName?: string;
|
|
772
|
+
isStream?: boolean;
|
|
773
|
+
isSeekable?: boolean;
|
|
774
|
+
}) => {
|
|
775
|
+
track: Track | UnresolvedTrack;
|
|
776
|
+
index: number;
|
|
777
|
+
} | null;
|
|
707
778
|
};
|
|
708
779
|
/**
|
|
709
780
|
* Shuffles the current Queue, then saves it
|
|
@@ -773,6 +844,126 @@ declare class Queue {
|
|
|
773
844
|
* ```
|
|
774
845
|
*/
|
|
775
846
|
shiftPrevious(): Promise<Track>;
|
|
847
|
+
/**
|
|
848
|
+
* Find tracks in the queue matching specific criteria.
|
|
849
|
+
* **This method DOES NOT MUTATE the queue** - it returns a new array without modifying the original queue.
|
|
850
|
+
* @deprecated Use `player.queue.utils.filterTracks()` instead.
|
|
851
|
+
* @param predicate Function to test each track, or an object with criteria to match
|
|
852
|
+
* @returns Array of matching tracks with their indexes
|
|
853
|
+
*
|
|
854
|
+
* @example
|
|
855
|
+
* ```ts
|
|
856
|
+
* // Use the new method instead:
|
|
857
|
+
* const artistTracks = player.queue.utils.filterTracks({ author: "Artist Name" });
|
|
858
|
+
* ```
|
|
859
|
+
*/
|
|
860
|
+
filter(predicate: ((track: Track | UnresolvedTrack, index: number) => boolean) | {
|
|
861
|
+
title?: string;
|
|
862
|
+
author?: string;
|
|
863
|
+
duration?: number | {
|
|
864
|
+
min?: number;
|
|
865
|
+
max?: number;
|
|
866
|
+
};
|
|
867
|
+
uri?: string;
|
|
868
|
+
identifier?: string;
|
|
869
|
+
sourceName?: string;
|
|
870
|
+
isStream?: boolean;
|
|
871
|
+
isSeekable?: boolean;
|
|
872
|
+
}): Array<{
|
|
873
|
+
track: Track | UnresolvedTrack;
|
|
874
|
+
index: number;
|
|
875
|
+
}>;
|
|
876
|
+
/**
|
|
877
|
+
* Find a single track in the queue matching specific criteria.
|
|
878
|
+
* **This method DOES NOT MUTATE the queue** - it searches without modifying the original queue.
|
|
879
|
+
* @deprecated Use `player.queue.utils.findTrack()` instead.
|
|
880
|
+
* @param predicate Function to test each track, or an object with criteria to match
|
|
881
|
+
* @returns First matching track with its index, or null if not found
|
|
882
|
+
*
|
|
883
|
+
* @example
|
|
884
|
+
* ```ts
|
|
885
|
+
* // Use the new method instead:
|
|
886
|
+
* const track = player.queue.utils.findTrack({ author: "Artist Name" });
|
|
887
|
+
* ```
|
|
888
|
+
*/
|
|
889
|
+
find(predicate: ((track: Track | UnresolvedTrack, index: number) => boolean) | {
|
|
890
|
+
title?: string;
|
|
891
|
+
author?: string;
|
|
892
|
+
duration?: number | {
|
|
893
|
+
min?: number;
|
|
894
|
+
max?: number;
|
|
895
|
+
};
|
|
896
|
+
uri?: string;
|
|
897
|
+
identifier?: string;
|
|
898
|
+
sourceName?: string;
|
|
899
|
+
isStream?: boolean;
|
|
900
|
+
isSeekable?: boolean;
|
|
901
|
+
}): {
|
|
902
|
+
track: Track | UnresolvedTrack;
|
|
903
|
+
index: number;
|
|
904
|
+
} | null;
|
|
905
|
+
/**
|
|
906
|
+
* Sort the queue tracks by a specific property.
|
|
907
|
+
* **⚠️ This method MUTATES the queue** - it modifies the original queue in place.
|
|
908
|
+
* @param sortBy Property to sort by or custom comparator function
|
|
909
|
+
* @param order Sort order: 'asc' or 'desc' (default: 'asc')
|
|
910
|
+
* @returns The queue instance for chaining
|
|
911
|
+
*
|
|
912
|
+
* @example
|
|
913
|
+
* ```ts
|
|
914
|
+
* // Sort by duration (shortest first)
|
|
915
|
+
* await player.queue.sortBy("duration", "asc");
|
|
916
|
+
*
|
|
917
|
+
* // Sort by title alphabetically (Z-A)
|
|
918
|
+
* await player.queue.sortBy("title", "desc");
|
|
919
|
+
*
|
|
920
|
+
* // Custom sorting
|
|
921
|
+
* await player.queue.sortBy((a, b) => {
|
|
922
|
+
* return a.info.title.localeCompare(b.info.title);
|
|
923
|
+
* });
|
|
924
|
+
* ```
|
|
925
|
+
*/
|
|
926
|
+
sortBy(sortBy: "duration" | "title" | "author" | ((a: Track | UnresolvedTrack, b: Track | UnresolvedTrack) => number), order?: "asc" | "desc"): Promise<this>;
|
|
927
|
+
/**
|
|
928
|
+
* Get a sorted copy of the queue tracks without modifying the original queue.
|
|
929
|
+
* **This method DOES NOT MUTATE the queue** - it returns a new sorted array, similar to `Array.toSorted()`.
|
|
930
|
+
* @param sortBy Property to sort by or custom comparator function
|
|
931
|
+
* @param order Sort order: 'asc' or 'desc' (default: 'asc')
|
|
932
|
+
* @returns A new sorted array of tracks (does not modify the queue)
|
|
933
|
+
*
|
|
934
|
+
* @example
|
|
935
|
+
* ```ts
|
|
936
|
+
* // Get sorted copy by duration (shortest first)
|
|
937
|
+
* const sortedTracks = player.queue.toSortedBy("duration", "asc");
|
|
938
|
+
* // Original queue remains unchanged
|
|
939
|
+
*
|
|
940
|
+
* // Get sorted copy by title alphabetically (Z-A)
|
|
941
|
+
* const sortedByTitle = player.queue.toSortedBy("title", "desc");
|
|
942
|
+
*
|
|
943
|
+
* // Custom sorting
|
|
944
|
+
* const customSorted = player.queue.toSortedBy((a, b) => {
|
|
945
|
+
* return a.info.title.localeCompare(b.info.title);
|
|
946
|
+
* });
|
|
947
|
+
* ```
|
|
948
|
+
*/
|
|
949
|
+
toSortedBy(sortBy: "duration" | "title" | "author" | ((a: Track | UnresolvedTrack, b: Track | UnresolvedTrack) => number), order?: "asc" | "desc"): (Track | UnresolvedTrack)[];
|
|
950
|
+
/**
|
|
951
|
+
* Get a range of tracks from the queue.
|
|
952
|
+
* **This method DOES NOT MUTATE the queue** - it returns a new array slice, similar to `Array.slice()`.
|
|
953
|
+
* @param start Start index (inclusive)
|
|
954
|
+
* @param end End index (exclusive)
|
|
955
|
+
* @returns Array of tracks in the specified range
|
|
956
|
+
*
|
|
957
|
+
* @example
|
|
958
|
+
* ```ts
|
|
959
|
+
* // Get tracks 5-15
|
|
960
|
+
* const tracks = player.queue.getTracks(5, 15);
|
|
961
|
+
*
|
|
962
|
+
* // Get first 10 tracks
|
|
963
|
+
* const firstTen = player.queue.getTracks(0, 10);
|
|
964
|
+
* ```
|
|
965
|
+
*/
|
|
966
|
+
getTracks(start: number, end?: number): (Track | UnresolvedTrack)[];
|
|
776
967
|
}
|
|
777
968
|
|
|
778
969
|
declare class Player {
|
|
@@ -1959,6 +2150,66 @@ interface PlayOptions extends LavalinkPlayOptions {
|
|
|
1959
2150
|
clientTrack?: Track | UnresolvedTrack;
|
|
1960
2151
|
}
|
|
1961
2152
|
|
|
2153
|
+
type NodeLinkEventTypes = "PlayerCreatedEvent" | "PlayerDestroyedEvent" | "PlayerConnectedEvent" | "PlayerReconnectingEvent" | "VolumeChangedEvent" | "FiltersChangedEvent" | "SeekEvent" | "PauseEvent" | "ConnectionStatusEvent" | "MixStartedEvent" | "MixEndedEvent";
|
|
2154
|
+
interface NodeLinkBaseEvent {
|
|
2155
|
+
op: "event";
|
|
2156
|
+
type: NodeLinkEventTypes;
|
|
2157
|
+
guildId: string;
|
|
2158
|
+
}
|
|
2159
|
+
interface PlayerCreatedEvent extends NodeLinkBaseEvent {
|
|
2160
|
+
type: "PlayerCreatedEvent";
|
|
2161
|
+
}
|
|
2162
|
+
interface PlayerDestroyedEvent extends NodeLinkBaseEvent {
|
|
2163
|
+
type: "PlayerDestroyedEvent";
|
|
2164
|
+
}
|
|
2165
|
+
interface PlayerConnectedEvent extends NodeLinkBaseEvent {
|
|
2166
|
+
type: "PlayerConnectedEvent";
|
|
2167
|
+
}
|
|
2168
|
+
interface PlayerReconnectingEvent extends NodeLinkBaseEvent {
|
|
2169
|
+
type: "PlayerReconnectingEvent";
|
|
2170
|
+
}
|
|
2171
|
+
interface VolumeChangedEvent extends NodeLinkBaseEvent {
|
|
2172
|
+
type: "VolumeChangedEvent";
|
|
2173
|
+
/** New volume level (0-1000) */
|
|
2174
|
+
volume: number;
|
|
2175
|
+
}
|
|
2176
|
+
interface FiltersChangedEvent extends NodeLinkBaseEvent {
|
|
2177
|
+
type: "FiltersChangedEvent";
|
|
2178
|
+
filters: LavalinkFilterData;
|
|
2179
|
+
}
|
|
2180
|
+
interface SeekEvent extends NodeLinkBaseEvent {
|
|
2181
|
+
type: "SeekEvent";
|
|
2182
|
+
/** New position in milliseconds */
|
|
2183
|
+
position: number;
|
|
2184
|
+
}
|
|
2185
|
+
interface PauseEvent extends NodeLinkBaseEvent {
|
|
2186
|
+
type: "PauseEvent";
|
|
2187
|
+
/** Whether playback is now paused (true) or resumed (false) */
|
|
2188
|
+
paused: boolean;
|
|
2189
|
+
}
|
|
2190
|
+
interface ConnectionStatusEvent extends NodeLinkBaseEvent {
|
|
2191
|
+
type: "ConnectionStatusEvent";
|
|
2192
|
+
/** Current connection status */
|
|
2193
|
+
connected: boolean;
|
|
2194
|
+
}
|
|
2195
|
+
interface MixStartedEvent extends NodeLinkBaseEvent {
|
|
2196
|
+
type: "MixStartedEvent";
|
|
2197
|
+
/** Unique identifier for the mix layer */
|
|
2198
|
+
mixId: string;
|
|
2199
|
+
/** Full track information of the mixed layer */
|
|
2200
|
+
track: LavalinkTrack;
|
|
2201
|
+
/** Volume of the mixed layer (0.0 to 1.0) */
|
|
2202
|
+
volume: number;
|
|
2203
|
+
}
|
|
2204
|
+
interface MixEndedEvent extends NodeLinkBaseEvent {
|
|
2205
|
+
type: "MixEndedEvent";
|
|
2206
|
+
/** Unique identifier for the mix layer */
|
|
2207
|
+
mixId: string;
|
|
2208
|
+
/** Reason the mix layer ended (FINISHED, REMOVED, ERROR, MAIN_ENDED) */
|
|
2209
|
+
reason: "FINISHED" | "REMOVED" | "ERROR" | "MAIN_ENDED" | string;
|
|
2210
|
+
}
|
|
2211
|
+
type NodeLinkEventPayload<T extends NodeLinkEventTypes> = T extends "PlayerCreatedEvent" ? PlayerCreatedEvent : T extends "PlayerDestroyedEvent" ? PlayerDestroyedEvent : T extends "PlayerConnectedEvent" ? PlayerConnectedEvent : T extends "PlayerReconnectingEvent" ? PlayerReconnectingEvent : T extends "VolumeChangedEvent" ? VolumeChangedEvent : T extends "FiltersChangedEvent" ? FiltersChangedEvent : T extends "SeekEvent" ? SeekEvent : T extends "PauseEvent" ? PauseEvent : T extends "ConnectionStatusEvent" ? ConnectionStatusEvent : T extends "MixStartedEvent" ? MixStartedEvent : T extends "MixEndedEvent" ? MixEndedEvent : never;
|
|
2212
|
+
|
|
1962
2213
|
/** Ability to manipulate fetch requests */
|
|
1963
2214
|
type ModifyRequest = (options: RequestInit & {
|
|
1964
2215
|
path: string;
|
|
@@ -2224,6 +2475,25 @@ interface NodeManagerEvents {
|
|
|
2224
2475
|
sessionId: string;
|
|
2225
2476
|
op: "ready";
|
|
2226
2477
|
}, players: LavalinkPlayer[] | InvalidLavalinkRestRequest) => void;
|
|
2478
|
+
/**
|
|
2479
|
+
* Event Handler for Nodelink specific events https://nodelink.js.org/docs/api/websocket Fully typed and generic based on the eventName.
|
|
2480
|
+
* @event Manager.nodeManager#nodeLinkEvent
|
|
2481
|
+
* @example
|
|
2482
|
+
*
|
|
2483
|
+
* ```ts
|
|
2484
|
+
* this.nodeManager.on("nodeLinkEvent", (node, event, player, track, payload) => {
|
|
2485
|
+
* if (event === "SeekEvent") {
|
|
2486
|
+
* console.log("new position:", payload.position);
|
|
2487
|
+
* }
|
|
2488
|
+
* if (event === "FiltersChangedEvent") {
|
|
2489
|
+
* console.log("new filters state", payload.filters);
|
|
2490
|
+
* }
|
|
2491
|
+
* });
|
|
2492
|
+
* ```
|
|
2493
|
+
*/
|
|
2494
|
+
"nodeLinkEvent": (...args: {
|
|
2495
|
+
[K in NodeLinkEventTypes]: [node: LavalinkNode, event: K, player: Player, track: Track | null, payload: NodeLinkEventPayload<K>];
|
|
2496
|
+
}[NodeLinkEventTypes]) => void;
|
|
2227
2497
|
}
|
|
2228
2498
|
declare enum ReconnectionState {
|
|
2229
2499
|
IDLE = "IDLE",
|
|
@@ -2721,6 +2991,14 @@ declare class LavalinkNode {
|
|
|
2721
2991
|
private message;
|
|
2722
2992
|
/** @private middleware util function for handling all kind of events from websocket */
|
|
2723
2993
|
private handleEvent;
|
|
2994
|
+
/**
|
|
2995
|
+
* nodeLink specific events handling https://nodelink.js.org/docs/api/websocket#incoming-events-server--client
|
|
2996
|
+
* @param eventName
|
|
2997
|
+
* @param player
|
|
2998
|
+
* @param track
|
|
2999
|
+
* @param payload
|
|
3000
|
+
*/
|
|
3001
|
+
private nodeLinkEventHandler;
|
|
2724
3002
|
private getTrackOfPayload;
|
|
2725
3003
|
/** @private util function for handling trackStart event */
|
|
2726
3004
|
private trackStart;
|
|
@@ -2944,6 +3222,13 @@ interface LavalinkManagerEvents<CustomPlayerT extends Player = Player> {
|
|
|
2944
3222
|
* @event Manager#playerDisconnect
|
|
2945
3223
|
*/
|
|
2946
3224
|
"playerDisconnect": (player: CustomPlayerT, voiceChannelId: string) => void;
|
|
3225
|
+
/**
|
|
3226
|
+
* Emitted when a Player automatically reconnects after a disconnect.
|
|
3227
|
+
* This event is triggered when the player successfully reconnects to the voice channel
|
|
3228
|
+
* and resumes playback after being disconnected (requires onDisconnect.autoReconnect to be enabled).
|
|
3229
|
+
* @event Manager#playerReconnect
|
|
3230
|
+
*/
|
|
3231
|
+
"playerReconnect": (player: CustomPlayerT, voiceChannelId: string) => void;
|
|
2947
3232
|
/**
|
|
2948
3233
|
* Emitted when a Node-Socket got closed for a specific Player.
|
|
2949
3234
|
* Usually emits when the audio websocket to discord is closed, This can happen for various reasons (normal and abnormal), e.g. when using an expired voice server update. 4xxx codes are usually bad.
|
package/dist/index.js
CHANGED
|
@@ -1433,7 +1433,6 @@ var LavalinkNode = class {
|
|
|
1433
1433
|
functionLayer: "LavalinkNode > nodeEvent > stats > heartBeat()"
|
|
1434
1434
|
});
|
|
1435
1435
|
this.resetAckTimeouts(false, true);
|
|
1436
|
-
if (this.pingTimeout) clearTimeout(this.pingTimeout);
|
|
1437
1436
|
this.pingTimeout = setTimeout(() => {
|
|
1438
1437
|
this.pingTimeout = null;
|
|
1439
1438
|
if (!this.socket) {
|
|
@@ -1958,9 +1957,10 @@ var LavalinkNode = class {
|
|
|
1958
1957
|
}, this.options.retryDelay || 1e3);
|
|
1959
1958
|
}
|
|
1960
1959
|
get reconnectionAttemptCount() {
|
|
1960
|
+
if (!Array.isArray(this.reconnectAttempts)) this.reconnectAttempts = [];
|
|
1961
1961
|
const maxAllowedTimestan = this.options.retryTimespan || -1;
|
|
1962
1962
|
if (maxAllowedTimestan <= 0) return this.reconnectAttempts.length;
|
|
1963
|
-
return this.reconnectAttempts
|
|
1963
|
+
return this.reconnectAttempts?.filter((timestamp) => Date.now() - timestamp <= maxAllowedTimestan).length || 0;
|
|
1964
1964
|
}
|
|
1965
1965
|
/**
|
|
1966
1966
|
* Private Utility function to execute the reconnection
|
|
@@ -1973,6 +1973,7 @@ var LavalinkNode = class {
|
|
|
1973
1973
|
this.destroy("NodeReconnectFail" /* NodeReconnectFail */);
|
|
1974
1974
|
return;
|
|
1975
1975
|
}
|
|
1976
|
+
if (!Array.isArray(this.reconnectAttempts)) this.reconnectAttempts = [];
|
|
1976
1977
|
this.reconnectAttempts.push(Date.now());
|
|
1977
1978
|
this.reconnectionState = "RECONNECTING" /* RECONNECTING */;
|
|
1978
1979
|
this.NodeManager.emit("reconnecting", this);
|
|
@@ -2157,6 +2158,23 @@ var LavalinkNode = class {
|
|
|
2157
2158
|
if (!payload?.guildId) return;
|
|
2158
2159
|
const player = this._LManager.getPlayer(payload.guildId);
|
|
2159
2160
|
if (!player) return;
|
|
2161
|
+
const NodeLinkEventType = payload.type;
|
|
2162
|
+
const NodeLinkExclusiveEvents = [
|
|
2163
|
+
"PlayerCreatedEvent",
|
|
2164
|
+
"PlayerDestroyedEvent",
|
|
2165
|
+
"PlayerConnectedEvent",
|
|
2166
|
+
"PlayerReconnectingEvent",
|
|
2167
|
+
"VolumeChangedEvent",
|
|
2168
|
+
"FiltersChangedEvent",
|
|
2169
|
+
"SeekEvent",
|
|
2170
|
+
"PauseEvent",
|
|
2171
|
+
"ConnectionStatusEvent",
|
|
2172
|
+
"MixStartedEvent",
|
|
2173
|
+
"MixEndedEvent"
|
|
2174
|
+
];
|
|
2175
|
+
if (NodeLinkExclusiveEvents.includes(NodeLinkEventType) && (!this.info || this.info.isNodelink)) {
|
|
2176
|
+
return this.nodeLinkEventHandler(NodeLinkEventType, player, player.queue.current, payload);
|
|
2177
|
+
}
|
|
2160
2178
|
switch (payload.type) {
|
|
2161
2179
|
case "TrackStartEvent":
|
|
2162
2180
|
this.trackStart(player, player.queue.current, payload);
|
|
@@ -2200,6 +2218,16 @@ var LavalinkNode = class {
|
|
|
2200
2218
|
}
|
|
2201
2219
|
return;
|
|
2202
2220
|
}
|
|
2221
|
+
/**
|
|
2222
|
+
* nodeLink specific events handling https://nodelink.js.org/docs/api/websocket#incoming-events-server--client
|
|
2223
|
+
* @param eventName
|
|
2224
|
+
* @param player
|
|
2225
|
+
* @param track
|
|
2226
|
+
* @param payload
|
|
2227
|
+
*/
|
|
2228
|
+
async nodeLinkEventHandler(eventName, player, track, payload) {
|
|
2229
|
+
this.NodeManager.emit("nodeLinkEvent", this, eventName, player, track, payload);
|
|
2230
|
+
}
|
|
2203
2231
|
getTrackOfPayload(payload) {
|
|
2204
2232
|
return "track" in payload ? this._LManager.utils.buildTrack(payload.track, void 0) : null;
|
|
2205
2233
|
}
|
|
@@ -3790,6 +3818,87 @@ var Queue = class {
|
|
|
3790
3818
|
*/
|
|
3791
3819
|
totalDuration: () => {
|
|
3792
3820
|
return this.tracks.reduce((acc, cur) => acc + (cur.info.duration || 0), this.current?.info.duration || 0);
|
|
3821
|
+
},
|
|
3822
|
+
/**
|
|
3823
|
+
* Find tracks in the queue matching specific criteria.
|
|
3824
|
+
* **This method DOES NOT MUTATE the queue** - it returns a new array without modifying the original queue.
|
|
3825
|
+
* @param predicate Function to test each track, or an object with criteria to match
|
|
3826
|
+
* @returns Array of matching tracks with their indexes
|
|
3827
|
+
*
|
|
3828
|
+
* @example
|
|
3829
|
+
* ```ts
|
|
3830
|
+
* // Find by author
|
|
3831
|
+
* const artistTracks = player.queue.utils.filterTracks({ author: "Artist Name" });
|
|
3832
|
+
*
|
|
3833
|
+
* // Find by duration range (5-10 minutes)
|
|
3834
|
+
* const longTracks = player.queue.utils.filterTracks({ duration: { min: 300000, max: 600000 } });
|
|
3835
|
+
*
|
|
3836
|
+
* // Find by title (partial match)
|
|
3837
|
+
* const titleMatches = player.queue.utils.filterTracks({ title: "Never Gonna" });
|
|
3838
|
+
*
|
|
3839
|
+
* // Custom predicate
|
|
3840
|
+
* const customFilter = player.queue.utils.filterTracks(track => track.info.isStream);
|
|
3841
|
+
* ```
|
|
3842
|
+
*/
|
|
3843
|
+
filterTracks: (predicate) => {
|
|
3844
|
+
if (typeof predicate === "function") {
|
|
3845
|
+
return this.tracks.map((track, index) => ({ track, index })).filter(({ track, index }) => predicate(track, index));
|
|
3846
|
+
}
|
|
3847
|
+
return this.tracks.map((track, index) => ({ track, index })).filter(({ track }) => {
|
|
3848
|
+
if (predicate.title && !track.info?.title?.toLowerCase().includes(predicate.title.toLowerCase())) {
|
|
3849
|
+
return false;
|
|
3850
|
+
}
|
|
3851
|
+
if (predicate.author && !track.info?.author?.toLowerCase().includes(predicate.author.toLowerCase())) {
|
|
3852
|
+
return false;
|
|
3853
|
+
}
|
|
3854
|
+
if (predicate.duration !== void 0) {
|
|
3855
|
+
const duration = track.info?.duration || 0;
|
|
3856
|
+
if (typeof predicate.duration === "number") {
|
|
3857
|
+
if (duration !== predicate.duration) return false;
|
|
3858
|
+
} else {
|
|
3859
|
+
if (predicate.duration.min !== void 0 && duration < predicate.duration.min) return false;
|
|
3860
|
+
if (predicate.duration.max !== void 0 && duration > predicate.duration.max) return false;
|
|
3861
|
+
}
|
|
3862
|
+
}
|
|
3863
|
+
if (predicate.uri && track.info?.uri !== predicate.uri) {
|
|
3864
|
+
return false;
|
|
3865
|
+
}
|
|
3866
|
+
if (predicate.identifier && track.info?.identifier !== predicate.identifier) {
|
|
3867
|
+
return false;
|
|
3868
|
+
}
|
|
3869
|
+
if (predicate.sourceName && track.info?.sourceName?.toLowerCase() !== predicate.sourceName.toLowerCase()) {
|
|
3870
|
+
return false;
|
|
3871
|
+
}
|
|
3872
|
+
if (predicate.isStream !== void 0 && track.info?.isStream !== predicate.isStream) {
|
|
3873
|
+
return false;
|
|
3874
|
+
}
|
|
3875
|
+
if (predicate.isSeekable !== void 0 && track.info?.isSeekable !== predicate.isSeekable) {
|
|
3876
|
+
return false;
|
|
3877
|
+
}
|
|
3878
|
+
return true;
|
|
3879
|
+
});
|
|
3880
|
+
},
|
|
3881
|
+
/**
|
|
3882
|
+
* Find a single track in the queue matching specific criteria.
|
|
3883
|
+
* **This method DOES NOT MUTATE the queue** - it searches without modifying the original queue.
|
|
3884
|
+
* @param predicate Function to test each track, or an object with criteria to match
|
|
3885
|
+
* @returns First matching track with its index, or null if not found
|
|
3886
|
+
*
|
|
3887
|
+
* @example
|
|
3888
|
+
* ```ts
|
|
3889
|
+
* // Find first track by author
|
|
3890
|
+
* const track = player.queue.utils.findTrack({ author: "Artist Name" });
|
|
3891
|
+
* if (track) {
|
|
3892
|
+
* console.log(`Found at index ${track.index}: ${track.track.info.title}`);
|
|
3893
|
+
* }
|
|
3894
|
+
*
|
|
3895
|
+
* // Find with custom predicate
|
|
3896
|
+
* const liveStream = player.queue.utils.findTrack(track => track.info.isStream);
|
|
3897
|
+
* ```
|
|
3898
|
+
*/
|
|
3899
|
+
findTrack: (predicate) => {
|
|
3900
|
+
const results = this.utils.filterTracks(predicate);
|
|
3901
|
+
return results.length > 0 ? results[0] : null;
|
|
3793
3902
|
}
|
|
3794
3903
|
};
|
|
3795
3904
|
/**
|
|
@@ -3971,6 +4080,149 @@ var Queue = class {
|
|
|
3971
4080
|
if (removed) await this.utils.save();
|
|
3972
4081
|
return removed ?? null;
|
|
3973
4082
|
}
|
|
4083
|
+
/**
|
|
4084
|
+
* Find tracks in the queue matching specific criteria.
|
|
4085
|
+
* **This method DOES NOT MUTATE the queue** - it returns a new array without modifying the original queue.
|
|
4086
|
+
* @deprecated Use `player.queue.utils.filterTracks()` instead.
|
|
4087
|
+
* @param predicate Function to test each track, or an object with criteria to match
|
|
4088
|
+
* @returns Array of matching tracks with their indexes
|
|
4089
|
+
*
|
|
4090
|
+
* @example
|
|
4091
|
+
* ```ts
|
|
4092
|
+
* // Use the new method instead:
|
|
4093
|
+
* const artistTracks = player.queue.utils.filterTracks({ author: "Artist Name" });
|
|
4094
|
+
* ```
|
|
4095
|
+
*/
|
|
4096
|
+
filter(predicate) {
|
|
4097
|
+
return this.utils.filterTracks(predicate);
|
|
4098
|
+
}
|
|
4099
|
+
/**
|
|
4100
|
+
* Find a single track in the queue matching specific criteria.
|
|
4101
|
+
* **This method DOES NOT MUTATE the queue** - it searches without modifying the original queue.
|
|
4102
|
+
* @deprecated Use `player.queue.utils.findTrack()` instead.
|
|
4103
|
+
* @param predicate Function to test each track, or an object with criteria to match
|
|
4104
|
+
* @returns First matching track with its index, or null if not found
|
|
4105
|
+
*
|
|
4106
|
+
* @example
|
|
4107
|
+
* ```ts
|
|
4108
|
+
* // Use the new method instead:
|
|
4109
|
+
* const track = player.queue.utils.findTrack({ author: "Artist Name" });
|
|
4110
|
+
* ```
|
|
4111
|
+
*/
|
|
4112
|
+
find(predicate) {
|
|
4113
|
+
return this.utils.findTrack(predicate);
|
|
4114
|
+
}
|
|
4115
|
+
/**
|
|
4116
|
+
* Sort the queue tracks by a specific property.
|
|
4117
|
+
* **⚠️ This method MUTATES the queue** - it modifies the original queue in place.
|
|
4118
|
+
* @param sortBy Property to sort by or custom comparator function
|
|
4119
|
+
* @param order Sort order: 'asc' or 'desc' (default: 'asc')
|
|
4120
|
+
* @returns The queue instance for chaining
|
|
4121
|
+
*
|
|
4122
|
+
* @example
|
|
4123
|
+
* ```ts
|
|
4124
|
+
* // Sort by duration (shortest first)
|
|
4125
|
+
* await player.queue.sortBy("duration", "asc");
|
|
4126
|
+
*
|
|
4127
|
+
* // Sort by title alphabetically (Z-A)
|
|
4128
|
+
* await player.queue.sortBy("title", "desc");
|
|
4129
|
+
*
|
|
4130
|
+
* // Custom sorting
|
|
4131
|
+
* await player.queue.sortBy((a, b) => {
|
|
4132
|
+
* return a.info.title.localeCompare(b.info.title);
|
|
4133
|
+
* });
|
|
4134
|
+
* ```
|
|
4135
|
+
*/
|
|
4136
|
+
async sortBy(sortBy, order = "asc") {
|
|
4137
|
+
const oldStored = typeof this.queueChanges?.tracksAdd === "function" ? this.utils.toJSON() : null;
|
|
4138
|
+
if (typeof sortBy === "function") {
|
|
4139
|
+
this.tracks.sort(sortBy);
|
|
4140
|
+
} else {
|
|
4141
|
+
this.tracks.sort((a, b) => {
|
|
4142
|
+
let comparison = 0;
|
|
4143
|
+
switch (sortBy) {
|
|
4144
|
+
case "duration":
|
|
4145
|
+
comparison = (a.info?.duration || 0) - (b.info?.duration || 0);
|
|
4146
|
+
break;
|
|
4147
|
+
case "title":
|
|
4148
|
+
comparison = (a.info?.title || "").localeCompare(b.info?.title || "");
|
|
4149
|
+
break;
|
|
4150
|
+
case "author":
|
|
4151
|
+
comparison = (a.info?.author || "").localeCompare(b.info?.author || "");
|
|
4152
|
+
break;
|
|
4153
|
+
default:
|
|
4154
|
+
return 0;
|
|
4155
|
+
}
|
|
4156
|
+
return order === "desc" ? -comparison : comparison;
|
|
4157
|
+
});
|
|
4158
|
+
}
|
|
4159
|
+
await this.utils.save();
|
|
4160
|
+
return this;
|
|
4161
|
+
}
|
|
4162
|
+
/**
|
|
4163
|
+
* Get a sorted copy of the queue tracks without modifying the original queue.
|
|
4164
|
+
* **This method DOES NOT MUTATE the queue** - it returns a new sorted array, similar to `Array.toSorted()`.
|
|
4165
|
+
* @param sortBy Property to sort by or custom comparator function
|
|
4166
|
+
* @param order Sort order: 'asc' or 'desc' (default: 'asc')
|
|
4167
|
+
* @returns A new sorted array of tracks (does not modify the queue)
|
|
4168
|
+
*
|
|
4169
|
+
* @example
|
|
4170
|
+
* ```ts
|
|
4171
|
+
* // Get sorted copy by duration (shortest first)
|
|
4172
|
+
* const sortedTracks = player.queue.toSortedBy("duration", "asc");
|
|
4173
|
+
* // Original queue remains unchanged
|
|
4174
|
+
*
|
|
4175
|
+
* // Get sorted copy by title alphabetically (Z-A)
|
|
4176
|
+
* const sortedByTitle = player.queue.toSortedBy("title", "desc");
|
|
4177
|
+
*
|
|
4178
|
+
* // Custom sorting
|
|
4179
|
+
* const customSorted = player.queue.toSortedBy((a, b) => {
|
|
4180
|
+
* return a.info.title.localeCompare(b.info.title);
|
|
4181
|
+
* });
|
|
4182
|
+
* ```
|
|
4183
|
+
*/
|
|
4184
|
+
toSortedBy(sortBy, order = "asc") {
|
|
4185
|
+
const tracksCopy = [...this.tracks];
|
|
4186
|
+
if (typeof sortBy === "function") {
|
|
4187
|
+
return tracksCopy.sort(sortBy);
|
|
4188
|
+
}
|
|
4189
|
+
return tracksCopy.sort((a, b) => {
|
|
4190
|
+
let comparison = 0;
|
|
4191
|
+
switch (sortBy) {
|
|
4192
|
+
case "duration":
|
|
4193
|
+
comparison = (a.info?.duration || 0) - (b.info?.duration || 0);
|
|
4194
|
+
break;
|
|
4195
|
+
case "title":
|
|
4196
|
+
comparison = (a.info?.title || "").localeCompare(b.info?.title || "");
|
|
4197
|
+
break;
|
|
4198
|
+
case "author":
|
|
4199
|
+
comparison = (a.info?.author || "").localeCompare(b.info?.author || "");
|
|
4200
|
+
break;
|
|
4201
|
+
default:
|
|
4202
|
+
return 0;
|
|
4203
|
+
}
|
|
4204
|
+
return order === "desc" ? -comparison : comparison;
|
|
4205
|
+
});
|
|
4206
|
+
}
|
|
4207
|
+
/**
|
|
4208
|
+
* Get a range of tracks from the queue.
|
|
4209
|
+
* **This method DOES NOT MUTATE the queue** - it returns a new array slice, similar to `Array.slice()`.
|
|
4210
|
+
* @param start Start index (inclusive)
|
|
4211
|
+
* @param end End index (exclusive)
|
|
4212
|
+
* @returns Array of tracks in the specified range
|
|
4213
|
+
*
|
|
4214
|
+
* @example
|
|
4215
|
+
* ```ts
|
|
4216
|
+
* // Get tracks 5-15
|
|
4217
|
+
* const tracks = player.queue.getTracks(5, 15);
|
|
4218
|
+
*
|
|
4219
|
+
* // Get first 10 tracks
|
|
4220
|
+
* const firstTen = player.queue.getTracks(0, 10);
|
|
4221
|
+
* ```
|
|
4222
|
+
*/
|
|
4223
|
+
getTracks(start, end) {
|
|
4224
|
+
return this.tracks.slice(start, end);
|
|
4225
|
+
}
|
|
3974
4226
|
};
|
|
3975
4227
|
|
|
3976
4228
|
// src/structures/Player.ts
|
|
@@ -5235,6 +5487,7 @@ var LavalinkManager = class extends import_events2.EventEmitter {
|
|
|
5235
5487
|
});
|
|
5236
5488
|
if (!autoReconnectOnlyWithTracks || autoReconnectOnlyWithTracks && (player.queue.current || player.queue.tracks.length)) {
|
|
5237
5489
|
await player.connect();
|
|
5490
|
+
this.emit("playerReconnect", player, player.voiceChannelId);
|
|
5238
5491
|
}
|
|
5239
5492
|
if (player.queue.current) {
|
|
5240
5493
|
return void await player.play({ position: previousPosition, paused: previousPaused, clientTrack: player.queue.current });
|
package/dist/index.mjs
CHANGED
|
@@ -1372,7 +1372,6 @@ var LavalinkNode = class {
|
|
|
1372
1372
|
functionLayer: "LavalinkNode > nodeEvent > stats > heartBeat()"
|
|
1373
1373
|
});
|
|
1374
1374
|
this.resetAckTimeouts(false, true);
|
|
1375
|
-
if (this.pingTimeout) clearTimeout(this.pingTimeout);
|
|
1376
1375
|
this.pingTimeout = setTimeout(() => {
|
|
1377
1376
|
this.pingTimeout = null;
|
|
1378
1377
|
if (!this.socket) {
|
|
@@ -1897,9 +1896,10 @@ var LavalinkNode = class {
|
|
|
1897
1896
|
}, this.options.retryDelay || 1e3);
|
|
1898
1897
|
}
|
|
1899
1898
|
get reconnectionAttemptCount() {
|
|
1899
|
+
if (!Array.isArray(this.reconnectAttempts)) this.reconnectAttempts = [];
|
|
1900
1900
|
const maxAllowedTimestan = this.options.retryTimespan || -1;
|
|
1901
1901
|
if (maxAllowedTimestan <= 0) return this.reconnectAttempts.length;
|
|
1902
|
-
return this.reconnectAttempts
|
|
1902
|
+
return this.reconnectAttempts?.filter((timestamp) => Date.now() - timestamp <= maxAllowedTimestan).length || 0;
|
|
1903
1903
|
}
|
|
1904
1904
|
/**
|
|
1905
1905
|
* Private Utility function to execute the reconnection
|
|
@@ -1912,6 +1912,7 @@ var LavalinkNode = class {
|
|
|
1912
1912
|
this.destroy("NodeReconnectFail" /* NodeReconnectFail */);
|
|
1913
1913
|
return;
|
|
1914
1914
|
}
|
|
1915
|
+
if (!Array.isArray(this.reconnectAttempts)) this.reconnectAttempts = [];
|
|
1915
1916
|
this.reconnectAttempts.push(Date.now());
|
|
1916
1917
|
this.reconnectionState = "RECONNECTING" /* RECONNECTING */;
|
|
1917
1918
|
this.NodeManager.emit("reconnecting", this);
|
|
@@ -2096,6 +2097,23 @@ var LavalinkNode = class {
|
|
|
2096
2097
|
if (!payload?.guildId) return;
|
|
2097
2098
|
const player = this._LManager.getPlayer(payload.guildId);
|
|
2098
2099
|
if (!player) return;
|
|
2100
|
+
const NodeLinkEventType = payload.type;
|
|
2101
|
+
const NodeLinkExclusiveEvents = [
|
|
2102
|
+
"PlayerCreatedEvent",
|
|
2103
|
+
"PlayerDestroyedEvent",
|
|
2104
|
+
"PlayerConnectedEvent",
|
|
2105
|
+
"PlayerReconnectingEvent",
|
|
2106
|
+
"VolumeChangedEvent",
|
|
2107
|
+
"FiltersChangedEvent",
|
|
2108
|
+
"SeekEvent",
|
|
2109
|
+
"PauseEvent",
|
|
2110
|
+
"ConnectionStatusEvent",
|
|
2111
|
+
"MixStartedEvent",
|
|
2112
|
+
"MixEndedEvent"
|
|
2113
|
+
];
|
|
2114
|
+
if (NodeLinkExclusiveEvents.includes(NodeLinkEventType) && (!this.info || this.info.isNodelink)) {
|
|
2115
|
+
return this.nodeLinkEventHandler(NodeLinkEventType, player, player.queue.current, payload);
|
|
2116
|
+
}
|
|
2099
2117
|
switch (payload.type) {
|
|
2100
2118
|
case "TrackStartEvent":
|
|
2101
2119
|
this.trackStart(player, player.queue.current, payload);
|
|
@@ -2139,6 +2157,16 @@ var LavalinkNode = class {
|
|
|
2139
2157
|
}
|
|
2140
2158
|
return;
|
|
2141
2159
|
}
|
|
2160
|
+
/**
|
|
2161
|
+
* nodeLink specific events handling https://nodelink.js.org/docs/api/websocket#incoming-events-server--client
|
|
2162
|
+
* @param eventName
|
|
2163
|
+
* @param player
|
|
2164
|
+
* @param track
|
|
2165
|
+
* @param payload
|
|
2166
|
+
*/
|
|
2167
|
+
async nodeLinkEventHandler(eventName, player, track, payload) {
|
|
2168
|
+
this.NodeManager.emit("nodeLinkEvent", this, eventName, player, track, payload);
|
|
2169
|
+
}
|
|
2142
2170
|
getTrackOfPayload(payload) {
|
|
2143
2171
|
return "track" in payload ? this._LManager.utils.buildTrack(payload.track, void 0) : null;
|
|
2144
2172
|
}
|
|
@@ -3729,6 +3757,87 @@ var Queue = class {
|
|
|
3729
3757
|
*/
|
|
3730
3758
|
totalDuration: () => {
|
|
3731
3759
|
return this.tracks.reduce((acc, cur) => acc + (cur.info.duration || 0), this.current?.info.duration || 0);
|
|
3760
|
+
},
|
|
3761
|
+
/**
|
|
3762
|
+
* Find tracks in the queue matching specific criteria.
|
|
3763
|
+
* **This method DOES NOT MUTATE the queue** - it returns a new array without modifying the original queue.
|
|
3764
|
+
* @param predicate Function to test each track, or an object with criteria to match
|
|
3765
|
+
* @returns Array of matching tracks with their indexes
|
|
3766
|
+
*
|
|
3767
|
+
* @example
|
|
3768
|
+
* ```ts
|
|
3769
|
+
* // Find by author
|
|
3770
|
+
* const artistTracks = player.queue.utils.filterTracks({ author: "Artist Name" });
|
|
3771
|
+
*
|
|
3772
|
+
* // Find by duration range (5-10 minutes)
|
|
3773
|
+
* const longTracks = player.queue.utils.filterTracks({ duration: { min: 300000, max: 600000 } });
|
|
3774
|
+
*
|
|
3775
|
+
* // Find by title (partial match)
|
|
3776
|
+
* const titleMatches = player.queue.utils.filterTracks({ title: "Never Gonna" });
|
|
3777
|
+
*
|
|
3778
|
+
* // Custom predicate
|
|
3779
|
+
* const customFilter = player.queue.utils.filterTracks(track => track.info.isStream);
|
|
3780
|
+
* ```
|
|
3781
|
+
*/
|
|
3782
|
+
filterTracks: (predicate) => {
|
|
3783
|
+
if (typeof predicate === "function") {
|
|
3784
|
+
return this.tracks.map((track, index) => ({ track, index })).filter(({ track, index }) => predicate(track, index));
|
|
3785
|
+
}
|
|
3786
|
+
return this.tracks.map((track, index) => ({ track, index })).filter(({ track }) => {
|
|
3787
|
+
if (predicate.title && !track.info?.title?.toLowerCase().includes(predicate.title.toLowerCase())) {
|
|
3788
|
+
return false;
|
|
3789
|
+
}
|
|
3790
|
+
if (predicate.author && !track.info?.author?.toLowerCase().includes(predicate.author.toLowerCase())) {
|
|
3791
|
+
return false;
|
|
3792
|
+
}
|
|
3793
|
+
if (predicate.duration !== void 0) {
|
|
3794
|
+
const duration = track.info?.duration || 0;
|
|
3795
|
+
if (typeof predicate.duration === "number") {
|
|
3796
|
+
if (duration !== predicate.duration) return false;
|
|
3797
|
+
} else {
|
|
3798
|
+
if (predicate.duration.min !== void 0 && duration < predicate.duration.min) return false;
|
|
3799
|
+
if (predicate.duration.max !== void 0 && duration > predicate.duration.max) return false;
|
|
3800
|
+
}
|
|
3801
|
+
}
|
|
3802
|
+
if (predicate.uri && track.info?.uri !== predicate.uri) {
|
|
3803
|
+
return false;
|
|
3804
|
+
}
|
|
3805
|
+
if (predicate.identifier && track.info?.identifier !== predicate.identifier) {
|
|
3806
|
+
return false;
|
|
3807
|
+
}
|
|
3808
|
+
if (predicate.sourceName && track.info?.sourceName?.toLowerCase() !== predicate.sourceName.toLowerCase()) {
|
|
3809
|
+
return false;
|
|
3810
|
+
}
|
|
3811
|
+
if (predicate.isStream !== void 0 && track.info?.isStream !== predicate.isStream) {
|
|
3812
|
+
return false;
|
|
3813
|
+
}
|
|
3814
|
+
if (predicate.isSeekable !== void 0 && track.info?.isSeekable !== predicate.isSeekable) {
|
|
3815
|
+
return false;
|
|
3816
|
+
}
|
|
3817
|
+
return true;
|
|
3818
|
+
});
|
|
3819
|
+
},
|
|
3820
|
+
/**
|
|
3821
|
+
* Find a single track in the queue matching specific criteria.
|
|
3822
|
+
* **This method DOES NOT MUTATE the queue** - it searches without modifying the original queue.
|
|
3823
|
+
* @param predicate Function to test each track, or an object with criteria to match
|
|
3824
|
+
* @returns First matching track with its index, or null if not found
|
|
3825
|
+
*
|
|
3826
|
+
* @example
|
|
3827
|
+
* ```ts
|
|
3828
|
+
* // Find first track by author
|
|
3829
|
+
* const track = player.queue.utils.findTrack({ author: "Artist Name" });
|
|
3830
|
+
* if (track) {
|
|
3831
|
+
* console.log(`Found at index ${track.index}: ${track.track.info.title}`);
|
|
3832
|
+
* }
|
|
3833
|
+
*
|
|
3834
|
+
* // Find with custom predicate
|
|
3835
|
+
* const liveStream = player.queue.utils.findTrack(track => track.info.isStream);
|
|
3836
|
+
* ```
|
|
3837
|
+
*/
|
|
3838
|
+
findTrack: (predicate) => {
|
|
3839
|
+
const results = this.utils.filterTracks(predicate);
|
|
3840
|
+
return results.length > 0 ? results[0] : null;
|
|
3732
3841
|
}
|
|
3733
3842
|
};
|
|
3734
3843
|
/**
|
|
@@ -3910,6 +4019,149 @@ var Queue = class {
|
|
|
3910
4019
|
if (removed) await this.utils.save();
|
|
3911
4020
|
return removed ?? null;
|
|
3912
4021
|
}
|
|
4022
|
+
/**
|
|
4023
|
+
* Find tracks in the queue matching specific criteria.
|
|
4024
|
+
* **This method DOES NOT MUTATE the queue** - it returns a new array without modifying the original queue.
|
|
4025
|
+
* @deprecated Use `player.queue.utils.filterTracks()` instead.
|
|
4026
|
+
* @param predicate Function to test each track, or an object with criteria to match
|
|
4027
|
+
* @returns Array of matching tracks with their indexes
|
|
4028
|
+
*
|
|
4029
|
+
* @example
|
|
4030
|
+
* ```ts
|
|
4031
|
+
* // Use the new method instead:
|
|
4032
|
+
* const artistTracks = player.queue.utils.filterTracks({ author: "Artist Name" });
|
|
4033
|
+
* ```
|
|
4034
|
+
*/
|
|
4035
|
+
filter(predicate) {
|
|
4036
|
+
return this.utils.filterTracks(predicate);
|
|
4037
|
+
}
|
|
4038
|
+
/**
|
|
4039
|
+
* Find a single track in the queue matching specific criteria.
|
|
4040
|
+
* **This method DOES NOT MUTATE the queue** - it searches without modifying the original queue.
|
|
4041
|
+
* @deprecated Use `player.queue.utils.findTrack()` instead.
|
|
4042
|
+
* @param predicate Function to test each track, or an object with criteria to match
|
|
4043
|
+
* @returns First matching track with its index, or null if not found
|
|
4044
|
+
*
|
|
4045
|
+
* @example
|
|
4046
|
+
* ```ts
|
|
4047
|
+
* // Use the new method instead:
|
|
4048
|
+
* const track = player.queue.utils.findTrack({ author: "Artist Name" });
|
|
4049
|
+
* ```
|
|
4050
|
+
*/
|
|
4051
|
+
find(predicate) {
|
|
4052
|
+
return this.utils.findTrack(predicate);
|
|
4053
|
+
}
|
|
4054
|
+
/**
|
|
4055
|
+
* Sort the queue tracks by a specific property.
|
|
4056
|
+
* **⚠️ This method MUTATES the queue** - it modifies the original queue in place.
|
|
4057
|
+
* @param sortBy Property to sort by or custom comparator function
|
|
4058
|
+
* @param order Sort order: 'asc' or 'desc' (default: 'asc')
|
|
4059
|
+
* @returns The queue instance for chaining
|
|
4060
|
+
*
|
|
4061
|
+
* @example
|
|
4062
|
+
* ```ts
|
|
4063
|
+
* // Sort by duration (shortest first)
|
|
4064
|
+
* await player.queue.sortBy("duration", "asc");
|
|
4065
|
+
*
|
|
4066
|
+
* // Sort by title alphabetically (Z-A)
|
|
4067
|
+
* await player.queue.sortBy("title", "desc");
|
|
4068
|
+
*
|
|
4069
|
+
* // Custom sorting
|
|
4070
|
+
* await player.queue.sortBy((a, b) => {
|
|
4071
|
+
* return a.info.title.localeCompare(b.info.title);
|
|
4072
|
+
* });
|
|
4073
|
+
* ```
|
|
4074
|
+
*/
|
|
4075
|
+
async sortBy(sortBy, order = "asc") {
|
|
4076
|
+
const oldStored = typeof this.queueChanges?.tracksAdd === "function" ? this.utils.toJSON() : null;
|
|
4077
|
+
if (typeof sortBy === "function") {
|
|
4078
|
+
this.tracks.sort(sortBy);
|
|
4079
|
+
} else {
|
|
4080
|
+
this.tracks.sort((a, b) => {
|
|
4081
|
+
let comparison = 0;
|
|
4082
|
+
switch (sortBy) {
|
|
4083
|
+
case "duration":
|
|
4084
|
+
comparison = (a.info?.duration || 0) - (b.info?.duration || 0);
|
|
4085
|
+
break;
|
|
4086
|
+
case "title":
|
|
4087
|
+
comparison = (a.info?.title || "").localeCompare(b.info?.title || "");
|
|
4088
|
+
break;
|
|
4089
|
+
case "author":
|
|
4090
|
+
comparison = (a.info?.author || "").localeCompare(b.info?.author || "");
|
|
4091
|
+
break;
|
|
4092
|
+
default:
|
|
4093
|
+
return 0;
|
|
4094
|
+
}
|
|
4095
|
+
return order === "desc" ? -comparison : comparison;
|
|
4096
|
+
});
|
|
4097
|
+
}
|
|
4098
|
+
await this.utils.save();
|
|
4099
|
+
return this;
|
|
4100
|
+
}
|
|
4101
|
+
/**
|
|
4102
|
+
* Get a sorted copy of the queue tracks without modifying the original queue.
|
|
4103
|
+
* **This method DOES NOT MUTATE the queue** - it returns a new sorted array, similar to `Array.toSorted()`.
|
|
4104
|
+
* @param sortBy Property to sort by or custom comparator function
|
|
4105
|
+
* @param order Sort order: 'asc' or 'desc' (default: 'asc')
|
|
4106
|
+
* @returns A new sorted array of tracks (does not modify the queue)
|
|
4107
|
+
*
|
|
4108
|
+
* @example
|
|
4109
|
+
* ```ts
|
|
4110
|
+
* // Get sorted copy by duration (shortest first)
|
|
4111
|
+
* const sortedTracks = player.queue.toSortedBy("duration", "asc");
|
|
4112
|
+
* // Original queue remains unchanged
|
|
4113
|
+
*
|
|
4114
|
+
* // Get sorted copy by title alphabetically (Z-A)
|
|
4115
|
+
* const sortedByTitle = player.queue.toSortedBy("title", "desc");
|
|
4116
|
+
*
|
|
4117
|
+
* // Custom sorting
|
|
4118
|
+
* const customSorted = player.queue.toSortedBy((a, b) => {
|
|
4119
|
+
* return a.info.title.localeCompare(b.info.title);
|
|
4120
|
+
* });
|
|
4121
|
+
* ```
|
|
4122
|
+
*/
|
|
4123
|
+
toSortedBy(sortBy, order = "asc") {
|
|
4124
|
+
const tracksCopy = [...this.tracks];
|
|
4125
|
+
if (typeof sortBy === "function") {
|
|
4126
|
+
return tracksCopy.sort(sortBy);
|
|
4127
|
+
}
|
|
4128
|
+
return tracksCopy.sort((a, b) => {
|
|
4129
|
+
let comparison = 0;
|
|
4130
|
+
switch (sortBy) {
|
|
4131
|
+
case "duration":
|
|
4132
|
+
comparison = (a.info?.duration || 0) - (b.info?.duration || 0);
|
|
4133
|
+
break;
|
|
4134
|
+
case "title":
|
|
4135
|
+
comparison = (a.info?.title || "").localeCompare(b.info?.title || "");
|
|
4136
|
+
break;
|
|
4137
|
+
case "author":
|
|
4138
|
+
comparison = (a.info?.author || "").localeCompare(b.info?.author || "");
|
|
4139
|
+
break;
|
|
4140
|
+
default:
|
|
4141
|
+
return 0;
|
|
4142
|
+
}
|
|
4143
|
+
return order === "desc" ? -comparison : comparison;
|
|
4144
|
+
});
|
|
4145
|
+
}
|
|
4146
|
+
/**
|
|
4147
|
+
* Get a range of tracks from the queue.
|
|
4148
|
+
* **This method DOES NOT MUTATE the queue** - it returns a new array slice, similar to `Array.slice()`.
|
|
4149
|
+
* @param start Start index (inclusive)
|
|
4150
|
+
* @param end End index (exclusive)
|
|
4151
|
+
* @returns Array of tracks in the specified range
|
|
4152
|
+
*
|
|
4153
|
+
* @example
|
|
4154
|
+
* ```ts
|
|
4155
|
+
* // Get tracks 5-15
|
|
4156
|
+
* const tracks = player.queue.getTracks(5, 15);
|
|
4157
|
+
*
|
|
4158
|
+
* // Get first 10 tracks
|
|
4159
|
+
* const firstTen = player.queue.getTracks(0, 10);
|
|
4160
|
+
* ```
|
|
4161
|
+
*/
|
|
4162
|
+
getTracks(start, end) {
|
|
4163
|
+
return this.tracks.slice(start, end);
|
|
4164
|
+
}
|
|
3913
4165
|
};
|
|
3914
4166
|
|
|
3915
4167
|
// src/structures/Player.ts
|
|
@@ -5174,6 +5426,7 @@ var LavalinkManager = class extends EventEmitter2 {
|
|
|
5174
5426
|
});
|
|
5175
5427
|
if (!autoReconnectOnlyWithTracks || autoReconnectOnlyWithTracks && (player.queue.current || player.queue.tracks.length)) {
|
|
5176
5428
|
await player.connect();
|
|
5429
|
+
this.emit("playerReconnect", player, player.voiceChannelId);
|
|
5177
5430
|
}
|
|
5178
5431
|
if (player.queue.current) {
|
|
5179
5432
|
return void await player.play({ position: previousPosition, paused: previousPaused, clientTrack: player.queue.current });
|
package/package.json
CHANGED