magmastream 2.9.0-dev.32 → 2.9.0-dev.34
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 +130 -14
- package/dist/structures/Enums.js +3 -0
- package/dist/structures/Manager.js +15 -2
- package/dist/structures/Node.js +88 -4
- package/dist/structures/Utils.js +296 -314
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -266,6 +266,9 @@ declare enum ManagerEventTypes {
|
|
|
266
266
|
ChapterStarted = "chapterStarted",
|
|
267
267
|
ChaptersLoaded = "chaptersLoaded",
|
|
268
268
|
Debug = "debug",
|
|
269
|
+
LyricsFoundEvent = "lyricsFoundEvent",
|
|
270
|
+
LyricsLineEvent = "lyricsLineEvent",
|
|
271
|
+
LyricsNotFoundEvent = "lyricsNotFoundEvent",
|
|
269
272
|
NodeConnect = "nodeConnect",
|
|
270
273
|
NodeCreate = "nodeCreate",
|
|
271
274
|
NodeDestroy = "nodeDestroy",
|
|
@@ -736,15 +739,40 @@ interface LavaPlayer {
|
|
|
736
739
|
filters: Record<string, unknown>;
|
|
737
740
|
}
|
|
738
741
|
/**
|
|
739
|
-
* Search Result
|
|
742
|
+
* Error or Empty Search Result
|
|
740
743
|
*/
|
|
741
|
-
interface
|
|
744
|
+
interface ErrorOrEmptySearchResult {
|
|
742
745
|
/** The load type of the result. */
|
|
743
|
-
loadType: LoadTypes;
|
|
744
|
-
|
|
746
|
+
loadType: LoadTypes.Empty | LoadTypes.Error;
|
|
747
|
+
}
|
|
748
|
+
/**
|
|
749
|
+
* Track Search Result
|
|
750
|
+
*/
|
|
751
|
+
interface TrackSearchResult {
|
|
752
|
+
/** The load type is always 'track' */
|
|
753
|
+
loadType: LoadTypes.Track;
|
|
754
|
+
/** The track obtained */
|
|
755
|
+
tracks: [Track];
|
|
756
|
+
}
|
|
757
|
+
/**
|
|
758
|
+
* Search Result
|
|
759
|
+
*/
|
|
760
|
+
interface SearchSearchResult {
|
|
761
|
+
/** The load type is always 'search' */
|
|
762
|
+
loadType: LoadTypes.Search;
|
|
763
|
+
/** The tracks of the search result */
|
|
764
|
+
tracks: Track[];
|
|
765
|
+
}
|
|
766
|
+
/**
|
|
767
|
+
* Playlist Search Result
|
|
768
|
+
*/
|
|
769
|
+
interface PlaylistSearchResult {
|
|
770
|
+
/** The playlist load type */
|
|
771
|
+
loadType: LoadTypes.Playlist;
|
|
772
|
+
/** The tracks of the playlist */
|
|
745
773
|
tracks: Track[];
|
|
746
|
-
/** The playlist info
|
|
747
|
-
playlist
|
|
774
|
+
/** The playlist info */
|
|
775
|
+
playlist: PlaylistData;
|
|
748
776
|
}
|
|
749
777
|
/**
|
|
750
778
|
* Playlist Data
|
|
@@ -783,6 +811,9 @@ interface ManagerEvents {
|
|
|
783
811
|
[ManagerEventTypes.ChapterStarted]: [player: Player, track: Track, payload: SponsorBlockChapterStarted];
|
|
784
812
|
[ManagerEventTypes.ChaptersLoaded]: [player: Player, track: Track, payload: SponsorBlockChaptersLoaded];
|
|
785
813
|
[ManagerEventTypes.Debug]: [info: string];
|
|
814
|
+
[ManagerEventTypes.LyricsFoundEvent]: [player: Player, track: Track, payload: LyricsFoundEvent];
|
|
815
|
+
[ManagerEventTypes.LyricsLineEvent]: [player: Player, track: Track, payload: LyricsLineEvent];
|
|
816
|
+
[ManagerEventTypes.LyricsNotFoundEvent]: [player: Player, track: Track, payload: LyricsNotFoundEvent];
|
|
786
817
|
[ManagerEventTypes.NodeConnect]: [node: Node];
|
|
787
818
|
[ManagerEventTypes.NodeCreate]: [node: Node];
|
|
788
819
|
[ManagerEventTypes.NodeDestroy]: [node: Node];
|
|
@@ -1153,6 +1184,31 @@ interface Lyrics {
|
|
|
1153
1184
|
lines: LyricsLine[];
|
|
1154
1185
|
plugin: object[];
|
|
1155
1186
|
}
|
|
1187
|
+
/**
|
|
1188
|
+
* LyricsFoundEvent interface
|
|
1189
|
+
*/
|
|
1190
|
+
interface LyricsFoundEvent extends PlayerEvent {
|
|
1191
|
+
type: "LyricsFoundEvent";
|
|
1192
|
+
guildId: string;
|
|
1193
|
+
lyrics: Lyrics;
|
|
1194
|
+
}
|
|
1195
|
+
/**
|
|
1196
|
+
* LyricsNotFoundEvent interface
|
|
1197
|
+
*/
|
|
1198
|
+
interface LyricsNotFoundEvent extends PlayerEvent {
|
|
1199
|
+
type: "LyricsNotFoundEvent";
|
|
1200
|
+
guildId: string;
|
|
1201
|
+
}
|
|
1202
|
+
/**
|
|
1203
|
+
* LyricsLineEvent interface
|
|
1204
|
+
*/
|
|
1205
|
+
interface LyricsLineEvent extends PlayerEvent {
|
|
1206
|
+
type: "LyricsLineEvent";
|
|
1207
|
+
guildId: string;
|
|
1208
|
+
lineIndex: number;
|
|
1209
|
+
line: LyricsLine;
|
|
1210
|
+
skipped: boolean;
|
|
1211
|
+
}
|
|
1156
1212
|
/**
|
|
1157
1213
|
* NodeLink Get Lyrics Multiple interface
|
|
1158
1214
|
*/
|
|
@@ -1338,7 +1394,7 @@ type TrackEndReason = keyof typeof TrackEndReasonTypes;
|
|
|
1338
1394
|
/**
|
|
1339
1395
|
* Player Event Type Enum type
|
|
1340
1396
|
*/
|
|
1341
|
-
type PlayerEventType = "TrackStartEvent" | "TrackEndEvent" | "TrackExceptionEvent" | "TrackStuckEvent" | "WebSocketClosedEvent" | "SegmentSkipped" | "SegmentsLoaded" | "ChaptersLoaded" | "ChapterStarted";
|
|
1397
|
+
type PlayerEventType = "TrackStartEvent" | "TrackEndEvent" | "TrackExceptionEvent" | "TrackStuckEvent" | "WebSocketClosedEvent" | "SegmentSkipped" | "SegmentsLoaded" | "ChaptersLoaded" | "ChapterStarted" | "LyricsFoundEvent" | "LyricsNotFoundEvent" | "LyricsLineEvent";
|
|
1342
1398
|
/**
|
|
1343
1399
|
* Severity Types Enum type
|
|
1344
1400
|
*/
|
|
@@ -1354,7 +1410,7 @@ type SponsorBlockSegmentEventType = "SegmentSkipped" | "SegmentsLoaded" | "Chapt
|
|
|
1354
1410
|
/**
|
|
1355
1411
|
* Player Events Enum type
|
|
1356
1412
|
*/
|
|
1357
|
-
type PlayerEvents = TrackStartEvent | TrackEndEvent | TrackStuckEvent | TrackExceptionEvent | WebSocketClosedEvent | SponsorBlockSegmentEvents;
|
|
1413
|
+
type PlayerEvents = TrackStartEvent | TrackEndEvent | TrackStuckEvent | TrackExceptionEvent | WebSocketClosedEvent | SponsorBlockSegmentEvents | LyricsEvent;
|
|
1358
1414
|
/**
|
|
1359
1415
|
* Load Type Enum type
|
|
1360
1416
|
*/
|
|
@@ -1367,6 +1423,18 @@ type NodeLinkGetLyrics = NodeLinkGetLyricsSingle | NodeLinkGetLyricsMultiple | N
|
|
|
1367
1423
|
* Voice Receiver Event Enum type
|
|
1368
1424
|
*/
|
|
1369
1425
|
type VoiceReceiverEvent = StartSpeakingEventVoiceReceiver | EndSpeakingEventVoiceReceiver;
|
|
1426
|
+
/**
|
|
1427
|
+
* Search Result Enum type
|
|
1428
|
+
*/
|
|
1429
|
+
type SearchResult = TrackSearchResult | SearchSearchResult | PlaylistSearchResult | ErrorOrEmptySearchResult;
|
|
1430
|
+
/**
|
|
1431
|
+
* Lyrics Event Enum type
|
|
1432
|
+
*/
|
|
1433
|
+
type LyricsEvent = LyricsFoundEvent | LyricsNotFoundEvent | LyricsLineEvent;
|
|
1434
|
+
/**
|
|
1435
|
+
* Lyrics Event Type Enum type
|
|
1436
|
+
*/
|
|
1437
|
+
type LyricsEventType = "LyricsFoundEvent" | "LyricsNotFoundEvent" | "LyricsLineEvent";
|
|
1370
1438
|
|
|
1371
1439
|
declare class Node$1 {
|
|
1372
1440
|
manager: Manager;
|
|
@@ -1593,14 +1661,32 @@ declare class Node$1 {
|
|
|
1593
1661
|
queueEnd(player: Player, track: Track, payload: TrackEndEvent): Promise<void>;
|
|
1594
1662
|
/**
|
|
1595
1663
|
* Fetches the lyrics of a track from the Lavalink node.
|
|
1596
|
-
*
|
|
1597
|
-
* If the
|
|
1664
|
+
*
|
|
1665
|
+
* If the node is a NodeLink, it will use the `NodeLinkGetLyrics` method to fetch the lyrics.
|
|
1666
|
+
*
|
|
1667
|
+
* Requires the `lavalyrics-plugin` to be present in the Lavalink node.
|
|
1668
|
+
* Requires the `lavasrc-plugin` or `java-lyrics-plugin` to be present in the Lavalink node.
|
|
1598
1669
|
*
|
|
1599
1670
|
* @param {Track} track - The track to fetch the lyrics for.
|
|
1600
1671
|
* @param {boolean} [skipTrackSource=false] - Whether to skip using the track's source URL.
|
|
1601
1672
|
* @returns {Promise<Lyrics | NodeLinkGetLyrics>} A promise that resolves with the lyrics data.
|
|
1602
1673
|
*/
|
|
1603
1674
|
getLyrics(track: Track, skipTrackSource?: boolean): Promise<Lyrics | NodeLinkGetLyrics>;
|
|
1675
|
+
/**
|
|
1676
|
+
* Subscribes to lyrics for a player.
|
|
1677
|
+
* @param {string} guildId - The ID of the guild to subscribe to lyrics for.
|
|
1678
|
+
* @param {boolean} [skipTrackSource=false] - Whether to skip using the track's source URL.
|
|
1679
|
+
* @returns {Promise<unknown>} A promise that resolves when the subscription is complete.
|
|
1680
|
+
* @throws {RangeError} If the node is not connected to the lavalink server or if the java-lyrics-plugin is not available.
|
|
1681
|
+
*/
|
|
1682
|
+
lyricsSubscribe(guildId: string, skipTrackSource?: boolean): Promise<unknown>;
|
|
1683
|
+
/**
|
|
1684
|
+
* Unsubscribes from lyrics for a player.
|
|
1685
|
+
* @param {string} guildId - The ID of the guild to unsubscribe from lyrics for.
|
|
1686
|
+
* @returns {Promise<unknown>} A promise that resolves when the unsubscription is complete.
|
|
1687
|
+
* @throws {RangeError} If the node is not connected to the lavalink server or if the java-lyrics-plugin is not available.
|
|
1688
|
+
*/
|
|
1689
|
+
lyricsUnsubscribe(guildId: string): Promise<unknown>;
|
|
1604
1690
|
/**
|
|
1605
1691
|
* Handles the event when a track becomes stuck during playback.
|
|
1606
1692
|
* Stops the current track and emits a `trackStuck` event.
|
|
@@ -1662,6 +1748,30 @@ declare class Node$1 {
|
|
|
1662
1748
|
* @param {SponsorBlockChapterStarted} payload - The event payload containing additional data about the chapter started event.
|
|
1663
1749
|
*/
|
|
1664
1750
|
private sponsorBlockChapterStarted;
|
|
1751
|
+
/**
|
|
1752
|
+
* Emitted when lyrics for a track are found.
|
|
1753
|
+
* The payload of the event will contain the lyrics.
|
|
1754
|
+
* @param {Player} player - The player associated with the lyrics.
|
|
1755
|
+
* @param {Track} track - The track associated with the lyrics.
|
|
1756
|
+
* @param {LyricsFoundEvent} payload - The event payload containing additional data about the lyrics found event.
|
|
1757
|
+
*/
|
|
1758
|
+
private lyricsFound;
|
|
1759
|
+
/**
|
|
1760
|
+
* Emitted when lyrics for a track are not found.
|
|
1761
|
+
* The payload of the event will contain the track.
|
|
1762
|
+
* @param {Player} player - The player associated with the lyrics.
|
|
1763
|
+
* @param {Track} track - The track associated with the lyrics.
|
|
1764
|
+
* @param {LyricsNotFoundEvent} payload - The event payload containing additional data about the lyrics not found event.
|
|
1765
|
+
*/
|
|
1766
|
+
private lyricsNotFound;
|
|
1767
|
+
/**
|
|
1768
|
+
* Emitted when a line of lyrics for a track is received.
|
|
1769
|
+
* The payload of the event will contain the lyrics line.
|
|
1770
|
+
* @param {Player} player - The player associated with the lyrics line.
|
|
1771
|
+
* @param {Track} track - The track associated with the lyrics line.
|
|
1772
|
+
* @param {LyricsLineEvent} payload - The event payload containing additional data about the lyrics line event.
|
|
1773
|
+
*/
|
|
1774
|
+
private lyricsLine;
|
|
1665
1775
|
/**
|
|
1666
1776
|
* Fetches Lavalink node information.
|
|
1667
1777
|
* @returns {Promise<LavalinkInfo>} A promise that resolves to the Lavalink node information.
|
|
@@ -2732,6 +2842,12 @@ declare abstract class TrackUtils {
|
|
|
2732
2842
|
* @returns The built Track.
|
|
2733
2843
|
*/
|
|
2734
2844
|
static build<T = User | ClientUser>(data: TrackData, requester?: T): Track;
|
|
2845
|
+
/**
|
|
2846
|
+
* Validates a search result.
|
|
2847
|
+
* @param result The search result to validate.
|
|
2848
|
+
* @returns Whether the search result is valid.
|
|
2849
|
+
*/
|
|
2850
|
+
static isErrorOrEmptySearchResult(result: SearchResult): result is ErrorOrEmptySearchResult;
|
|
2735
2851
|
}
|
|
2736
2852
|
declare abstract class AutoPlayUtils {
|
|
2737
2853
|
private static manager;
|
|
@@ -2740,7 +2856,7 @@ declare abstract class AutoPlayUtils {
|
|
|
2740
2856
|
* @param manager The manager instance to use.
|
|
2741
2857
|
* @hidden
|
|
2742
2858
|
*/
|
|
2743
|
-
static init(manager: Manager): void
|
|
2859
|
+
static init(manager: Manager): Promise<void>;
|
|
2744
2860
|
/**
|
|
2745
2861
|
* Gets recommended tracks for the given track.
|
|
2746
2862
|
* @param track The track to get recommended tracks for.
|
|
@@ -2760,8 +2876,8 @@ declare abstract class AutoPlayUtils {
|
|
|
2760
2876
|
* @param platform The source to get recommended tracks from.
|
|
2761
2877
|
* @returns An array of recommended tracks.
|
|
2762
2878
|
*/
|
|
2763
|
-
static getRecommendedTracksFromSource(track: Track, platform:
|
|
2764
|
-
static
|
|
2879
|
+
static getRecommendedTracksFromSource(track: Track, platform: AutoPlayPlatform): Promise<Track[]>;
|
|
2880
|
+
static buildTracksFromResponse<T>(recommendedResult: LavalinkResponse, requester?: T): Track[];
|
|
2765
2881
|
}
|
|
2766
2882
|
/** Gets or extends structures to extend the built in, or already extended, classes to add more functionality. */
|
|
2767
2883
|
declare abstract class Structure {
|
|
@@ -2815,4 +2931,4 @@ declare class OceanicManager extends Manager {
|
|
|
2815
2931
|
}
|
|
2816
2932
|
|
|
2817
2933
|
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 };
|
|
2818
|
-
export type { CPUStats, DiscordPacket, EndSpeakingEventVoiceReceiver, EndSpeakingEventVoiceReceiverData, EqualizerBand, Exception, Extendable, FrameStats, IQueue, LavaPlayer, LavalinkInfo, LavalinkResponse, LoadType, Lyrics, LyricsLine, ManagerEvents, ManagerInitOptions, ManagerOptions, MemoryStats, NodeLinkGetLyrics, NodeLinkGetLyricsEmpty, NodeLinkGetLyricsError, NodeLinkGetLyricsMultiple, NodeLinkGetLyricsSingle, NodeMessage, NodeOptions, NodeStats, Payload, PlayOptions, PlayerEvent, PlayerEventType, PlayerEvents, PlayerOptions, PlayerStateUpdateEvent, PlayerUpdate, PlayerUpdateVoiceState, PlaylistData, PlaylistInfoData, PlaylistRawData, RedisConfig, SearchQuery, SearchResult, Severity, Sizes, SponsorBlockChapterStarted, SponsorBlockChaptersLoaded, SponsorBlockSegmentEventType, SponsorBlockSegmentEvents, SponsorBlockSegmentSkipped, SponsorBlockSegmentsLoaded, StartSpeakingEventVoiceReceiver, StartSpeakingEventVoiceReceiverData, StateStorageOptions, Track, TrackData, TrackDataInfo, TrackEndEvent, TrackEndReason, TrackExceptionEvent, TrackPluginInfo, TrackSourceName, TrackStartEvent, TrackStuckEvent, UseNodeOption, VoicePacket, VoiceReceiverEvent, VoiceServer, VoiceServerUpdate, VoiceState, WebSocketClosedEvent };
|
|
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 };
|
package/dist/structures/Enums.js
CHANGED
|
@@ -141,6 +141,9 @@ var ManagerEventTypes;
|
|
|
141
141
|
ManagerEventTypes["ChapterStarted"] = "chapterStarted";
|
|
142
142
|
ManagerEventTypes["ChaptersLoaded"] = "chaptersLoaded";
|
|
143
143
|
ManagerEventTypes["Debug"] = "debug";
|
|
144
|
+
ManagerEventTypes["LyricsFoundEvent"] = "lyricsFoundEvent";
|
|
145
|
+
ManagerEventTypes["LyricsLineEvent"] = "lyricsLineEvent";
|
|
146
|
+
ManagerEventTypes["LyricsNotFoundEvent"] = "lyricsNotFoundEvent";
|
|
144
147
|
ManagerEventTypes["NodeConnect"] = "nodeConnect";
|
|
145
148
|
ManagerEventTypes["NodeCreate"] = "nodeCreate";
|
|
146
149
|
ManagerEventTypes["NodeDestroy"] = "nodeDestroy";
|
|
@@ -47,7 +47,6 @@ class Manager extends events_1.EventEmitter {
|
|
|
47
47
|
// Initialize structures
|
|
48
48
|
Utils_1.Structure.get("Player").init(this);
|
|
49
49
|
Utils_1.TrackUtils.init(this);
|
|
50
|
-
Utils_1.AutoPlayUtils.init(this);
|
|
51
50
|
if (options.trackPartial) {
|
|
52
51
|
Utils_1.TrackUtils.setTrackPartial(options.trackPartial);
|
|
53
52
|
delete options.trackPartial;
|
|
@@ -77,6 +76,7 @@ class Manager extends events_1.EventEmitter {
|
|
|
77
76
|
stateStorage: { type: Enums_1.StateStorageType.Collection },
|
|
78
77
|
...options,
|
|
79
78
|
};
|
|
79
|
+
Utils_1.AutoPlayUtils.init(this);
|
|
80
80
|
if (this.options.nodes) {
|
|
81
81
|
for (const nodeOptions of this.options.nodes)
|
|
82
82
|
new Node_1.Node(this, nodeOptions);
|
|
@@ -216,7 +216,20 @@ class Manager extends events_1.EventEmitter {
|
|
|
216
216
|
tracks = tracks.map(processTrack);
|
|
217
217
|
}
|
|
218
218
|
}
|
|
219
|
-
|
|
219
|
+
let result;
|
|
220
|
+
switch (res.loadType) {
|
|
221
|
+
case Enums_1.LoadTypes.Playlist:
|
|
222
|
+
result = { loadType: res.loadType, tracks, playlist };
|
|
223
|
+
break;
|
|
224
|
+
case Enums_1.LoadTypes.Search:
|
|
225
|
+
result = { loadType: res.loadType, tracks };
|
|
226
|
+
break;
|
|
227
|
+
case Enums_1.LoadTypes.Track:
|
|
228
|
+
result = { loadType: res.loadType, tracks: [tracks[0]] };
|
|
229
|
+
break;
|
|
230
|
+
default:
|
|
231
|
+
return { loadType: res.loadType };
|
|
232
|
+
}
|
|
220
233
|
this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Result ${_source} search for: ${_query.query}: ${JSON.stringify(result)}`);
|
|
221
234
|
return result;
|
|
222
235
|
}
|
package/dist/structures/Node.js
CHANGED
|
@@ -485,6 +485,15 @@ class Node {
|
|
|
485
485
|
case "ChapterStarted":
|
|
486
486
|
this.sponsorBlockChapterStarted(player, track, payload);
|
|
487
487
|
break;
|
|
488
|
+
case "LyricsFoundEvent":
|
|
489
|
+
this.lyricsFound(player, track, payload);
|
|
490
|
+
break;
|
|
491
|
+
case "LyricsNotFoundEvent":
|
|
492
|
+
this.lyricsNotFound(player, track, payload);
|
|
493
|
+
break;
|
|
494
|
+
case "LyricsLineEvent":
|
|
495
|
+
this.lyricsLine(player, track, payload);
|
|
496
|
+
break;
|
|
488
497
|
default:
|
|
489
498
|
error = new Error(`Node#event unknown event '${type}'.`);
|
|
490
499
|
this.manager.emit(Enums_1.ManagerEventTypes.NodeError, this, error);
|
|
@@ -734,19 +743,28 @@ class Node {
|
|
|
734
743
|
}
|
|
735
744
|
/**
|
|
736
745
|
* Fetches the lyrics of a track from the Lavalink node.
|
|
737
|
-
*
|
|
738
|
-
* If the
|
|
746
|
+
*
|
|
747
|
+
* If the node is a NodeLink, it will use the `NodeLinkGetLyrics` method to fetch the lyrics.
|
|
748
|
+
*
|
|
749
|
+
* Requires the `lavalyrics-plugin` to be present in the Lavalink node.
|
|
750
|
+
* Requires the `lavasrc-plugin` or `java-lyrics-plugin` to be present in the Lavalink node.
|
|
739
751
|
*
|
|
740
752
|
* @param {Track} track - The track to fetch the lyrics for.
|
|
741
753
|
* @param {boolean} [skipTrackSource=false] - Whether to skip using the track's source URL.
|
|
742
754
|
* @returns {Promise<Lyrics | NodeLinkGetLyrics>} A promise that resolves with the lyrics data.
|
|
743
755
|
*/
|
|
744
756
|
async getLyrics(track, skipTrackSource = false) {
|
|
757
|
+
if (!this.connected)
|
|
758
|
+
throw new RangeError(`The node is not connected to the lavalink server: ${this.options.identifier}`);
|
|
745
759
|
if (this.isNodeLink) {
|
|
746
760
|
return (await this.rest.get(`/v4/lyrics?track=${encodeURIComponent(track.track)}&skipTrackSource=${skipTrackSource}`));
|
|
747
761
|
}
|
|
748
|
-
if (!this.info.plugins.some((plugin) => plugin.name === "lavalyrics-plugin"))
|
|
749
|
-
throw new RangeError(`
|
|
762
|
+
if (!this.info.plugins.some((plugin) => plugin.name === "lavalyrics-plugin")) {
|
|
763
|
+
throw new RangeError(`The plugin "lavalyrics-plugin" must be present in the lavalink node: ${this.options.identifier}`);
|
|
764
|
+
}
|
|
765
|
+
if (!this.info.plugins.some((plugin) => plugin.name === "lavasrc-plugin" || plugin.name === "java-lyrics-plugin")) {
|
|
766
|
+
throw new RangeError(`One of the following plugins must also be present in the lavalink node: "lavasrc-plugin" or "java-lyrics-plugin" (Node: ${this.options.identifier})`);
|
|
767
|
+
}
|
|
750
768
|
return ((await this.rest.get(`/v4/lyrics?track=${encodeURIComponent(track.track)}&skipTrackSource=${skipTrackSource}`)) || {
|
|
751
769
|
source: null,
|
|
752
770
|
provider: null,
|
|
@@ -755,6 +773,42 @@ class Node {
|
|
|
755
773
|
plugin: [],
|
|
756
774
|
});
|
|
757
775
|
}
|
|
776
|
+
/**
|
|
777
|
+
* Subscribes to lyrics for a player.
|
|
778
|
+
* @param {string} guildId - The ID of the guild to subscribe to lyrics for.
|
|
779
|
+
* @param {boolean} [skipTrackSource=false] - Whether to skip using the track's source URL.
|
|
780
|
+
* @returns {Promise<unknown>} A promise that resolves when the subscription is complete.
|
|
781
|
+
* @throws {RangeError} If the node is not connected to the lavalink server or if the java-lyrics-plugin is not available.
|
|
782
|
+
*/
|
|
783
|
+
async lyricsSubscribe(guildId, skipTrackSource = false) {
|
|
784
|
+
if (!this.connected)
|
|
785
|
+
throw new RangeError(`The node is not connected to the lavalink server: ${this.options.identifier}`);
|
|
786
|
+
if (this.isNodeLink)
|
|
787
|
+
throw new RangeError(`The node is a node link: ${this.options.identifier}`);
|
|
788
|
+
if (!this.info.plugins.some((plugin) => plugin.name === "lavalyrics-plugin")) {
|
|
789
|
+
throw new RangeError(`The plugin "lavalyrics-plugin" must be present in the lavalink node: ${this.options.identifier}`);
|
|
790
|
+
}
|
|
791
|
+
if (!this.info.plugins.some((plugin) => plugin.name === "lavasrc-plugin" || plugin.name === "java-lyrics-plugin")) {
|
|
792
|
+
throw new RangeError(`One of the following plugins must also be present in the lavalink node: "lavasrc-plugin" or "java-lyrics-plugin" (Node: ${this.options.identifier})`);
|
|
793
|
+
}
|
|
794
|
+
return await this.rest.post(`/v4/sessions/${this.sessionId}/players/${guildId}/lyrics/subscribe?skipTrackSource=${skipTrackSource}`, {});
|
|
795
|
+
}
|
|
796
|
+
/**
|
|
797
|
+
* Unsubscribes from lyrics for a player.
|
|
798
|
+
* @param {string} guildId - The ID of the guild to unsubscribe from lyrics for.
|
|
799
|
+
* @returns {Promise<unknown>} A promise that resolves when the unsubscription is complete.
|
|
800
|
+
* @throws {RangeError} If the node is not connected to the lavalink server or if the java-lyrics-plugin is not available.
|
|
801
|
+
*/
|
|
802
|
+
async lyricsUnsubscribe(guildId) {
|
|
803
|
+
if (!this.connected)
|
|
804
|
+
throw new RangeError(`The node is not connected to the lavalink server: ${this.options.identifier}`);
|
|
805
|
+
if (this.isNodeLink)
|
|
806
|
+
throw new RangeError(`The node is a node link: ${this.options.identifier}`);
|
|
807
|
+
if (!this.info.plugins.some((plugin) => plugin.name === "java-lyrics-plugin")) {
|
|
808
|
+
throw new RangeError(`there is no java-lyrics-plugin available in the lavalink node: ${this.options.identifier}`);
|
|
809
|
+
}
|
|
810
|
+
return await this.rest.delete(`/v4/sessions/${this.sessionId}/players/${guildId}/lyrics/subscribe`);
|
|
811
|
+
}
|
|
758
812
|
/**
|
|
759
813
|
* Handles the event when a track becomes stuck during playback.
|
|
760
814
|
* Stops the current track and emits a `trackStuck` event.
|
|
@@ -833,6 +887,36 @@ class Node {
|
|
|
833
887
|
sponsorBlockChapterStarted(player, track, payload) {
|
|
834
888
|
return this.manager.emit(Enums_1.ManagerEventTypes.ChapterStarted, player, track, payload);
|
|
835
889
|
}
|
|
890
|
+
/**
|
|
891
|
+
* Emitted when lyrics for a track are found.
|
|
892
|
+
* The payload of the event will contain the lyrics.
|
|
893
|
+
* @param {Player} player - The player associated with the lyrics.
|
|
894
|
+
* @param {Track} track - The track associated with the lyrics.
|
|
895
|
+
* @param {LyricsFoundEvent} payload - The event payload containing additional data about the lyrics found event.
|
|
896
|
+
*/
|
|
897
|
+
lyricsFound(player, track, payload) {
|
|
898
|
+
return this.manager.emit(Enums_1.ManagerEventTypes.LyricsFoundEvent, player, track, payload);
|
|
899
|
+
}
|
|
900
|
+
/**
|
|
901
|
+
* Emitted when lyrics for a track are not found.
|
|
902
|
+
* The payload of the event will contain the track.
|
|
903
|
+
* @param {Player} player - The player associated with the lyrics.
|
|
904
|
+
* @param {Track} track - The track associated with the lyrics.
|
|
905
|
+
* @param {LyricsNotFoundEvent} payload - The event payload containing additional data about the lyrics not found event.
|
|
906
|
+
*/
|
|
907
|
+
lyricsNotFound(player, track, payload) {
|
|
908
|
+
return this.manager.emit(Enums_1.ManagerEventTypes.LyricsNotFoundEvent, player, track, payload);
|
|
909
|
+
}
|
|
910
|
+
/**
|
|
911
|
+
* Emitted when a line of lyrics for a track is received.
|
|
912
|
+
* The payload of the event will contain the lyrics line.
|
|
913
|
+
* @param {Player} player - The player associated with the lyrics line.
|
|
914
|
+
* @param {Track} track - The track associated with the lyrics line.
|
|
915
|
+
* @param {LyricsLineEvent} payload - The event payload containing additional data about the lyrics line event.
|
|
916
|
+
*/
|
|
917
|
+
lyricsLine(player, track, payload) {
|
|
918
|
+
return this.manager.emit(Enums_1.ManagerEventTypes.LyricsLineEvent, player, track, payload);
|
|
919
|
+
}
|
|
836
920
|
/**
|
|
837
921
|
* Fetches Lavalink node information.
|
|
838
922
|
* @returns {Promise<LavalinkInfo>} A promise that resolves to the Lavalink node information.
|
package/dist/structures/Utils.js
CHANGED
|
@@ -4,8 +4,8 @@ exports.Structure = exports.AutoPlayUtils = exports.TrackUtils = void 0;
|
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
const axios_1 = tslib_1.__importDefault(require("axios"));
|
|
6
6
|
const jsdom_1 = require("jsdom");
|
|
7
|
-
const crypto_1 = tslib_1.__importDefault(require("crypto"));
|
|
8
7
|
const Enums_1 = require("./Enums");
|
|
8
|
+
// import playwright from "playwright";
|
|
9
9
|
/** @hidden */
|
|
10
10
|
const SIZES = ["0", "1", "2", "3", "default", "mqdefault", "hqdefault", "maxresdefault"];
|
|
11
11
|
class TrackUtils {
|
|
@@ -129,19 +129,32 @@ class TrackUtils {
|
|
|
129
129
|
throw new RangeError(`Argument "data" is not a valid track: ${error.message}`);
|
|
130
130
|
}
|
|
131
131
|
}
|
|
132
|
+
/**
|
|
133
|
+
* Validates a search result.
|
|
134
|
+
* @param result The search result to validate.
|
|
135
|
+
* @returns Whether the search result is valid.
|
|
136
|
+
*/
|
|
137
|
+
static isErrorOrEmptySearchResult(result) {
|
|
138
|
+
return result.loadType === Enums_1.LoadTypes.Empty || result.loadType === Enums_1.LoadTypes.Error;
|
|
139
|
+
}
|
|
132
140
|
}
|
|
133
141
|
exports.TrackUtils = TrackUtils;
|
|
134
142
|
class AutoPlayUtils {
|
|
135
143
|
static manager;
|
|
144
|
+
// private static cachedAccessToken: string | null = null;
|
|
145
|
+
// private static cachedAccessTokenExpiresAt: number = 0;
|
|
136
146
|
/**
|
|
137
147
|
* Initializes the AutoPlayUtils class with the given manager.
|
|
138
148
|
* @param manager The manager instance to use.
|
|
139
149
|
* @hidden
|
|
140
150
|
*/
|
|
141
|
-
static init(manager) {
|
|
151
|
+
static async init(manager) {
|
|
142
152
|
if (!manager)
|
|
143
153
|
throw new Error("AutoPlayUtils.init() requires a valid Manager instance.");
|
|
144
154
|
this.manager = manager;
|
|
155
|
+
// if (this.manager.options.autoPlaySearchPlatforms.includes(AutoPlayPlatform.Spotify)) {
|
|
156
|
+
// await this.getSpotifyAccessToken();
|
|
157
|
+
// }
|
|
145
158
|
}
|
|
146
159
|
/**
|
|
147
160
|
* Gets recommended tracks for the given track.
|
|
@@ -190,15 +203,25 @@ class AutoPlayUtils {
|
|
|
190
203
|
return [];
|
|
191
204
|
}
|
|
192
205
|
const randomTrack = response.data.toptracks.track[Math.floor(Math.random() * response.data.toptracks.track.length)];
|
|
193
|
-
const
|
|
194
|
-
if (
|
|
206
|
+
const searchResult = await this.manager.search({ query: `${randomTrack.artist.name} - ${randomTrack.name}`, source: this.manager.options.defaultSearchPlatform }, track.requester);
|
|
207
|
+
if (TrackUtils.isErrorOrEmptySearchResult(searchResult)) {
|
|
195
208
|
return [];
|
|
196
209
|
}
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
210
|
+
let resolvedTracks;
|
|
211
|
+
switch (searchResult.loadType) {
|
|
212
|
+
case Enums_1.LoadTypes.Playlist:
|
|
213
|
+
resolvedTracks = searchResult.playlist.tracks;
|
|
214
|
+
break;
|
|
215
|
+
case Enums_1.LoadTypes.Track:
|
|
216
|
+
case Enums_1.LoadTypes.Search:
|
|
217
|
+
resolvedTracks = searchResult.tracks;
|
|
218
|
+
break;
|
|
219
|
+
default:
|
|
220
|
+
return [];
|
|
200
221
|
}
|
|
201
|
-
|
|
222
|
+
if (!resolvedTracks.length)
|
|
223
|
+
return [];
|
|
224
|
+
return resolvedTracks;
|
|
202
225
|
}
|
|
203
226
|
if (!artist) {
|
|
204
227
|
// No artist provided, search for the track title
|
|
@@ -228,11 +251,25 @@ class AutoPlayUtils {
|
|
|
228
251
|
return [];
|
|
229
252
|
}
|
|
230
253
|
const randomTrack = retryResponse.data.toptracks.track[Math.floor(Math.random() * retryResponse.data.toptracks.track.length)];
|
|
231
|
-
const
|
|
232
|
-
if (
|
|
254
|
+
const searchResult = await this.manager.search({ query: `${randomTrack.artist.name} - ${randomTrack.name}`, source: this.manager.options.defaultSearchPlatform }, track.requester);
|
|
255
|
+
if (TrackUtils.isErrorOrEmptySearchResult(searchResult)) {
|
|
233
256
|
return [];
|
|
234
257
|
}
|
|
235
|
-
|
|
258
|
+
let resolvedTracks;
|
|
259
|
+
switch (searchResult.loadType) {
|
|
260
|
+
case Enums_1.LoadTypes.Playlist:
|
|
261
|
+
resolvedTracks = searchResult.playlist.tracks;
|
|
262
|
+
break;
|
|
263
|
+
case Enums_1.LoadTypes.Track:
|
|
264
|
+
case Enums_1.LoadTypes.Search:
|
|
265
|
+
resolvedTracks = searchResult.tracks;
|
|
266
|
+
break;
|
|
267
|
+
default:
|
|
268
|
+
return [];
|
|
269
|
+
}
|
|
270
|
+
if (!resolvedTracks.length)
|
|
271
|
+
return [];
|
|
272
|
+
const filteredTracks = resolvedTracks.filter((t) => t.uri !== track.uri);
|
|
236
273
|
if (!filteredTracks.length) {
|
|
237
274
|
return [];
|
|
238
275
|
}
|
|
@@ -242,16 +279,25 @@ class AutoPlayUtils {
|
|
|
242
279
|
if (!randomTrack) {
|
|
243
280
|
return [];
|
|
244
281
|
}
|
|
245
|
-
const
|
|
246
|
-
if (
|
|
282
|
+
const searchResult = await this.manager.search({ query: `${randomTrack.artist.name} - ${randomTrack.name}`, source: this.manager.options.defaultSearchPlatform }, track.requester);
|
|
283
|
+
if (TrackUtils.isErrorOrEmptySearchResult(searchResult)) {
|
|
247
284
|
return [];
|
|
248
285
|
}
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
286
|
+
let resolvedTracks;
|
|
287
|
+
switch (searchResult.loadType) {
|
|
288
|
+
case Enums_1.LoadTypes.Playlist:
|
|
289
|
+
resolvedTracks = searchResult.playlist.tracks;
|
|
290
|
+
break;
|
|
291
|
+
case Enums_1.LoadTypes.Track:
|
|
292
|
+
case Enums_1.LoadTypes.Search:
|
|
293
|
+
resolvedTracks = searchResult.tracks;
|
|
294
|
+
break;
|
|
295
|
+
default:
|
|
296
|
+
return [];
|
|
253
297
|
}
|
|
254
|
-
|
|
298
|
+
if (!resolvedTracks.length)
|
|
299
|
+
return [];
|
|
300
|
+
return resolvedTracks;
|
|
255
301
|
}
|
|
256
302
|
/**
|
|
257
303
|
* Gets recommended tracks from the given source.
|
|
@@ -260,157 +306,90 @@ class AutoPlayUtils {
|
|
|
260
306
|
* @returns An array of recommended tracks.
|
|
261
307
|
*/
|
|
262
308
|
static async getRecommendedTracksFromSource(track, platform) {
|
|
309
|
+
const requester = track.requester;
|
|
263
310
|
switch (platform) {
|
|
264
|
-
case
|
|
311
|
+
case Enums_1.AutoPlayPlatform.Spotify:
|
|
265
312
|
{
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
if (res.loadType === Enums_1.LoadTypes.Empty || res.loadType === Enums_1.LoadTypes.Error) {
|
|
270
|
-
return [];
|
|
271
|
-
}
|
|
272
|
-
if (res.loadType === Enums_1.LoadTypes.Playlist) {
|
|
273
|
-
res.tracks = res.playlist.tracks;
|
|
274
|
-
}
|
|
275
|
-
if (!res.tracks.length) {
|
|
276
|
-
return [];
|
|
277
|
-
}
|
|
278
|
-
track = res.tracks[0];
|
|
279
|
-
}
|
|
280
|
-
const [totp, timestamp] = this.generateTotp();
|
|
281
|
-
const params = {
|
|
282
|
-
reason: "init",
|
|
283
|
-
productType: "web-player",
|
|
284
|
-
totp: totp,
|
|
285
|
-
totpVer: 5,
|
|
286
|
-
ts: timestamp,
|
|
287
|
-
};
|
|
288
|
-
let body;
|
|
289
|
-
try {
|
|
290
|
-
const response = await axios_1.default.get("https://open.spotify.com/api/token", {
|
|
291
|
-
params,
|
|
292
|
-
headers: {
|
|
293
|
-
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.6998.178 Spotify/1.2.65.255 Safari/537.36",
|
|
294
|
-
"App-Platform": "WebPlayer",
|
|
295
|
-
Referer: "https://open.spotify.com/",
|
|
296
|
-
Origin: "https://open.spotify.com",
|
|
297
|
-
"Accept-Language": "en",
|
|
298
|
-
},
|
|
299
|
-
});
|
|
300
|
-
body = response.data;
|
|
301
|
-
}
|
|
302
|
-
catch (error) {
|
|
303
|
-
const status = error.response?.status ?? "No response";
|
|
304
|
-
console.error("[AutoPlay] Failed to get spotify access token:", status);
|
|
305
|
-
return [];
|
|
306
|
-
}
|
|
307
|
-
let json;
|
|
308
|
-
try {
|
|
309
|
-
const response = await axios_1.default.get(`https://api.spotify.com/v1/recommendations`, {
|
|
310
|
-
params: { limit: 10, seed_tracks: track.identifier },
|
|
311
|
-
headers: {
|
|
312
|
-
Authorization: `Bearer ${body.accessToken}`,
|
|
313
|
-
"Content-Type": "application/json",
|
|
314
|
-
},
|
|
315
|
-
});
|
|
316
|
-
json = response.data;
|
|
317
|
-
}
|
|
318
|
-
catch (error) {
|
|
319
|
-
console.error("[AutoPlay] Failed to fetch spotify recommendations:", error.response?.status);
|
|
320
|
-
return [];
|
|
321
|
-
}
|
|
322
|
-
if (!json.tracks || !json.tracks.length) {
|
|
323
|
-
return [];
|
|
324
|
-
}
|
|
325
|
-
const recommendedTrackId = json.tracks[Math.floor(Math.random() * json.tracks.length)].id;
|
|
326
|
-
const res = await this.manager.search({ query: `https://open.spotify.com/track/${recommendedTrackId}`, source: Enums_1.SearchPlatform.Spotify }, track.requester);
|
|
327
|
-
if (res.loadType === Enums_1.LoadTypes.Empty || res.loadType === Enums_1.LoadTypes.Error) {
|
|
313
|
+
if (!track.uri.includes("spotify")) {
|
|
314
|
+
const res = await this.manager.search({ query: `${track.author} - ${track.title}`, source: Enums_1.SearchPlatform.Spotify }, requester);
|
|
315
|
+
if (TrackUtils.isErrorOrEmptySearchResult(res))
|
|
328
316
|
return [];
|
|
317
|
+
let resolvedTrack;
|
|
318
|
+
switch (res.loadType) {
|
|
319
|
+
case Enums_1.LoadTypes.Playlist:
|
|
320
|
+
resolvedTrack = res.playlist.tracks[0];
|
|
321
|
+
break;
|
|
322
|
+
case Enums_1.LoadTypes.Track:
|
|
323
|
+
case Enums_1.LoadTypes.Search:
|
|
324
|
+
resolvedTrack = res.tracks[0];
|
|
325
|
+
break;
|
|
326
|
+
default:
|
|
327
|
+
return [];
|
|
329
328
|
}
|
|
330
|
-
if (
|
|
331
|
-
res.tracks = res.playlist.tracks;
|
|
332
|
-
}
|
|
333
|
-
if (!res.tracks.length) {
|
|
329
|
+
if (!resolvedTrack)
|
|
334
330
|
return [];
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
return [];
|
|
341
|
-
}
|
|
331
|
+
track = resolvedTrack;
|
|
332
|
+
}
|
|
333
|
+
const extractSpotifyArtistID = (url) => {
|
|
334
|
+
const regex = /https:\/\/open\.spotify\.com\/artist\/([a-zA-Z0-9]+)/;
|
|
335
|
+
const match = url.match(regex);
|
|
336
|
+
return match ? match[1] : null;
|
|
337
|
+
};
|
|
338
|
+
const identifier = `sprec:seed_artists=${extractSpotifyArtistID(track.pluginInfo.artistUrl)}&seed_tracks=${track.identifier}`;
|
|
339
|
+
const recommendedResult = (await this.manager.useableNode.rest.get(`/v4/loadtracks?identifier=${encodeURIComponent(identifier)}`));
|
|
340
|
+
const tracks = this.buildTracksFromResponse(recommendedResult, requester);
|
|
341
|
+
return tracks;
|
|
342
342
|
}
|
|
343
343
|
break;
|
|
344
|
-
case
|
|
344
|
+
case Enums_1.AutoPlayPlatform.Deezer:
|
|
345
345
|
{
|
|
346
346
|
if (!track.uri.includes("deezer")) {
|
|
347
|
-
const res = await this.manager.search({ query: `${track.author} - ${track.title}`, source: Enums_1.SearchPlatform.Deezer },
|
|
348
|
-
if (
|
|
347
|
+
const res = await this.manager.search({ query: `${track.author} - ${track.title}`, source: Enums_1.SearchPlatform.Deezer }, requester);
|
|
348
|
+
if (TrackUtils.isErrorOrEmptySearchResult(res))
|
|
349
349
|
return [];
|
|
350
|
+
let resolvedTrack;
|
|
351
|
+
switch (res.loadType) {
|
|
352
|
+
case Enums_1.LoadTypes.Playlist:
|
|
353
|
+
resolvedTrack = res.playlist.tracks[0];
|
|
354
|
+
break;
|
|
355
|
+
case Enums_1.LoadTypes.Track:
|
|
356
|
+
case Enums_1.LoadTypes.Search:
|
|
357
|
+
resolvedTrack = res.tracks[0];
|
|
358
|
+
break;
|
|
359
|
+
default:
|
|
360
|
+
return [];
|
|
350
361
|
}
|
|
351
|
-
if (
|
|
352
|
-
res.tracks = res.playlist.tracks;
|
|
353
|
-
}
|
|
354
|
-
if (!res.tracks.length) {
|
|
362
|
+
if (!resolvedTrack)
|
|
355
363
|
return [];
|
|
356
|
-
|
|
357
|
-
track = res.tracks[0];
|
|
364
|
+
track = resolvedTrack;
|
|
358
365
|
}
|
|
359
366
|
const identifier = `dzrec:${track.identifier}`;
|
|
360
367
|
const recommendedResult = (await this.manager.useableNode.rest.get(`/v4/loadtracks?identifier=${encodeURIComponent(identifier)}`));
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
}
|
|
364
|
-
let tracks = [];
|
|
365
|
-
let playlist = null;
|
|
366
|
-
const requester = track.requester;
|
|
367
|
-
switch (recommendedResult.loadType) {
|
|
368
|
-
case Enums_1.LoadTypes.Search:
|
|
369
|
-
tracks = recommendedResult.data.map((track) => TrackUtils.build(track, requester));
|
|
370
|
-
break;
|
|
371
|
-
case Enums_1.LoadTypes.Track:
|
|
372
|
-
tracks = [TrackUtils.build(recommendedResult.data, requester)];
|
|
373
|
-
break;
|
|
374
|
-
case Enums_1.LoadTypes.Playlist: {
|
|
375
|
-
const playlistData = recommendedResult.data;
|
|
376
|
-
tracks = playlistData.tracks.map((track) => TrackUtils.build(track, requester));
|
|
377
|
-
playlist = {
|
|
378
|
-
name: playlistData.info.name,
|
|
379
|
-
playlistInfo: playlistData.pluginInfo,
|
|
380
|
-
requester: requester,
|
|
381
|
-
tracks,
|
|
382
|
-
duration: tracks.reduce((acc, cur) => acc + (cur.duration || 0), 0),
|
|
383
|
-
};
|
|
384
|
-
break;
|
|
385
|
-
}
|
|
386
|
-
}
|
|
387
|
-
const result = { loadType: recommendedResult.loadType, tracks, playlist };
|
|
388
|
-
if (result.loadType === Enums_1.LoadTypes.Empty || result.loadType === Enums_1.LoadTypes.Error) {
|
|
389
|
-
return [];
|
|
390
|
-
}
|
|
391
|
-
if (result.loadType === Enums_1.LoadTypes.Playlist) {
|
|
392
|
-
result.tracks = result.playlist.tracks;
|
|
393
|
-
}
|
|
394
|
-
if (!result.tracks.length) {
|
|
395
|
-
return [];
|
|
396
|
-
}
|
|
397
|
-
return result.tracks;
|
|
368
|
+
const tracks = this.buildTracksFromResponse(recommendedResult, requester);
|
|
369
|
+
return tracks;
|
|
398
370
|
}
|
|
399
371
|
break;
|
|
400
|
-
case
|
|
372
|
+
case Enums_1.AutoPlayPlatform.SoundCloud:
|
|
401
373
|
{
|
|
402
374
|
if (!track.uri.includes("soundcloud")) {
|
|
403
|
-
const res = await this.manager.search({ query: `${track.author} - ${track.title}`, source: Enums_1.SearchPlatform.SoundCloud },
|
|
404
|
-
if (
|
|
375
|
+
const res = await this.manager.search({ query: `${track.author} - ${track.title}`, source: Enums_1.SearchPlatform.SoundCloud }, requester);
|
|
376
|
+
if (TrackUtils.isErrorOrEmptySearchResult(res))
|
|
405
377
|
return [];
|
|
378
|
+
let resolvedTrack;
|
|
379
|
+
switch (res.loadType) {
|
|
380
|
+
case Enums_1.LoadTypes.Playlist:
|
|
381
|
+
resolvedTrack = res.playlist.tracks[0];
|
|
382
|
+
break;
|
|
383
|
+
case Enums_1.LoadTypes.Track:
|
|
384
|
+
case Enums_1.LoadTypes.Search:
|
|
385
|
+
resolvedTrack = res.tracks[0];
|
|
386
|
+
break;
|
|
387
|
+
default:
|
|
388
|
+
return [];
|
|
406
389
|
}
|
|
407
|
-
if (
|
|
408
|
-
res.tracks = res.playlist.tracks;
|
|
409
|
-
}
|
|
410
|
-
if (!res.tracks.length) {
|
|
390
|
+
if (!resolvedTrack)
|
|
411
391
|
return [];
|
|
412
|
-
|
|
413
|
-
track = res.tracks[0];
|
|
392
|
+
track = resolvedTrack;
|
|
414
393
|
}
|
|
415
394
|
try {
|
|
416
395
|
const recommendedRes = await axios_1.default.get(`${track.uri}/recommended`).catch((err) => {
|
|
@@ -440,17 +419,22 @@ class AutoPlayUtils {
|
|
|
440
419
|
return [];
|
|
441
420
|
}
|
|
442
421
|
const randomUrl = urls[Math.floor(Math.random() * urls.length)];
|
|
443
|
-
const res = await this.manager.search({ query: randomUrl, source: Enums_1.SearchPlatform.SoundCloud },
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
422
|
+
const res = await this.manager.search({ query: randomUrl, source: Enums_1.SearchPlatform.SoundCloud }, requester);
|
|
423
|
+
let resolvedTrack;
|
|
424
|
+
switch (res.loadType) {
|
|
425
|
+
case Enums_1.LoadTypes.Playlist:
|
|
426
|
+
resolvedTrack = res.playlist.tracks[0];
|
|
427
|
+
break;
|
|
428
|
+
case Enums_1.LoadTypes.Track:
|
|
429
|
+
case Enums_1.LoadTypes.Search:
|
|
430
|
+
resolvedTrack = res.tracks[0];
|
|
431
|
+
break;
|
|
432
|
+
default:
|
|
433
|
+
return [];
|
|
449
434
|
}
|
|
450
|
-
if (!
|
|
435
|
+
if (!resolvedTrack)
|
|
451
436
|
return [];
|
|
452
|
-
|
|
453
|
-
return res.tracks;
|
|
437
|
+
return [resolvedTrack];
|
|
454
438
|
}
|
|
455
439
|
catch (error) {
|
|
456
440
|
console.error("[AutoPlay] Error occurred while fetching soundcloud recommendations:", error);
|
|
@@ -458,7 +442,7 @@ class AutoPlayUtils {
|
|
|
458
442
|
}
|
|
459
443
|
}
|
|
460
444
|
break;
|
|
461
|
-
case
|
|
445
|
+
case Enums_1.AutoPlayPlatform.YouTube:
|
|
462
446
|
{
|
|
463
447
|
const hasYouTubeURL = ["youtube.com", "youtu.be"].some((url) => track.uri.includes(url));
|
|
464
448
|
let videoID = null;
|
|
@@ -466,8 +450,25 @@ class AutoPlayUtils {
|
|
|
466
450
|
videoID = track.uri.split("=").pop();
|
|
467
451
|
}
|
|
468
452
|
else {
|
|
469
|
-
const searchResult = await this.manager.search({ query: `${track.author} - ${track.title}`, source: Enums_1.SearchPlatform.YouTube },
|
|
470
|
-
|
|
453
|
+
const searchResult = await this.manager.search({ query: `${track.author} - ${track.title}`, source: Enums_1.SearchPlatform.YouTube }, requester);
|
|
454
|
+
if (TrackUtils.isErrorOrEmptySearchResult(searchResult)) {
|
|
455
|
+
return [];
|
|
456
|
+
}
|
|
457
|
+
let resolvedTrack;
|
|
458
|
+
switch (searchResult.loadType) {
|
|
459
|
+
case Enums_1.LoadTypes.Playlist:
|
|
460
|
+
resolvedTrack = searchResult.playlist.tracks[0];
|
|
461
|
+
break;
|
|
462
|
+
case Enums_1.LoadTypes.Track:
|
|
463
|
+
case Enums_1.LoadTypes.Search:
|
|
464
|
+
resolvedTrack = searchResult.tracks[0];
|
|
465
|
+
break;
|
|
466
|
+
default:
|
|
467
|
+
return [];
|
|
468
|
+
}
|
|
469
|
+
if (!resolvedTrack)
|
|
470
|
+
return [];
|
|
471
|
+
videoID = resolvedTrack.uri.split("=").pop();
|
|
471
472
|
}
|
|
472
473
|
if (!videoID) {
|
|
473
474
|
return [];
|
|
@@ -478,200 +479,181 @@ class AutoPlayUtils {
|
|
|
478
479
|
randomIndex = Math.floor(Math.random() * 23) + 2;
|
|
479
480
|
searchURI = `https://www.youtube.com/watch?v=${videoID}&list=RD${videoID}&index=${randomIndex}`;
|
|
480
481
|
} while (track.uri.includes(searchURI));
|
|
481
|
-
const res = await this.manager.search({ query: searchURI, source: Enums_1.SearchPlatform.YouTube },
|
|
482
|
-
if (
|
|
482
|
+
const res = await this.manager.search({ query: searchURI, source: Enums_1.SearchPlatform.YouTube }, requester);
|
|
483
|
+
if (TrackUtils.isErrorOrEmptySearchResult(res)) {
|
|
483
484
|
return [];
|
|
484
485
|
}
|
|
485
|
-
|
|
486
|
+
let resolvedTracks;
|
|
487
|
+
switch (res.loadType) {
|
|
488
|
+
case Enums_1.LoadTypes.Playlist:
|
|
489
|
+
resolvedTracks = res.playlist.tracks;
|
|
490
|
+
break;
|
|
491
|
+
case Enums_1.LoadTypes.Track:
|
|
492
|
+
case Enums_1.LoadTypes.Search:
|
|
493
|
+
resolvedTracks = res.tracks;
|
|
494
|
+
break;
|
|
495
|
+
default:
|
|
496
|
+
return [];
|
|
497
|
+
}
|
|
498
|
+
const filteredTracks = resolvedTracks.filter((t) => t.uri !== track.uri);
|
|
486
499
|
return filteredTracks;
|
|
487
500
|
}
|
|
488
501
|
break;
|
|
489
|
-
case
|
|
502
|
+
case Enums_1.AutoPlayPlatform.Tidal:
|
|
490
503
|
{
|
|
491
504
|
if (!track.uri.includes("tidal")) {
|
|
492
|
-
const res = await this.manager.search({ query: `${track.author} - ${track.title}`, source: Enums_1.SearchPlatform.Tidal },
|
|
493
|
-
if (
|
|
505
|
+
const res = await this.manager.search({ query: `${track.author} - ${track.title}`, source: Enums_1.SearchPlatform.Tidal }, requester);
|
|
506
|
+
if (TrackUtils.isErrorOrEmptySearchResult(res))
|
|
494
507
|
return [];
|
|
508
|
+
let resolvedTrack;
|
|
509
|
+
switch (res.loadType) {
|
|
510
|
+
case Enums_1.LoadTypes.Playlist:
|
|
511
|
+
resolvedTrack = res.playlist.tracks[0];
|
|
512
|
+
break;
|
|
513
|
+
case Enums_1.LoadTypes.Track:
|
|
514
|
+
case Enums_1.LoadTypes.Search:
|
|
515
|
+
resolvedTrack = res.tracks[0];
|
|
516
|
+
break;
|
|
517
|
+
default:
|
|
518
|
+
return [];
|
|
495
519
|
}
|
|
496
|
-
if (
|
|
497
|
-
res.tracks = res.playlist.tracks;
|
|
498
|
-
}
|
|
499
|
-
if (!res.tracks.length) {
|
|
520
|
+
if (!resolvedTrack)
|
|
500
521
|
return [];
|
|
501
|
-
|
|
502
|
-
track = res.tracks[0];
|
|
522
|
+
track = resolvedTrack;
|
|
503
523
|
}
|
|
504
524
|
const identifier = `tdrec:${track.identifier}`;
|
|
505
525
|
const recommendedResult = (await this.manager.useableNode.rest.get(`/v4/loadtracks?identifier=${encodeURIComponent(identifier)}`));
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
}
|
|
509
|
-
let tracks = [];
|
|
510
|
-
let playlist = null;
|
|
511
|
-
const requester = track.requester;
|
|
512
|
-
switch (recommendedResult.loadType) {
|
|
513
|
-
case Enums_1.LoadTypes.Search:
|
|
514
|
-
tracks = recommendedResult.data.map((track) => TrackUtils.build(track, requester));
|
|
515
|
-
break;
|
|
516
|
-
case Enums_1.LoadTypes.Track:
|
|
517
|
-
tracks = [TrackUtils.build(recommendedResult.data, requester)];
|
|
518
|
-
break;
|
|
519
|
-
case Enums_1.LoadTypes.Playlist: {
|
|
520
|
-
const playlistData = recommendedResult.data;
|
|
521
|
-
tracks = playlistData.tracks.map((track) => TrackUtils.build(track, requester));
|
|
522
|
-
playlist = {
|
|
523
|
-
name: playlistData.info.name,
|
|
524
|
-
playlistInfo: playlistData.pluginInfo,
|
|
525
|
-
requester: requester,
|
|
526
|
-
tracks,
|
|
527
|
-
duration: tracks.reduce((acc, cur) => acc + (cur.duration || 0), 0),
|
|
528
|
-
};
|
|
529
|
-
break;
|
|
530
|
-
}
|
|
531
|
-
}
|
|
532
|
-
const result = { loadType: recommendedResult.loadType, tracks, playlist };
|
|
533
|
-
if (result.loadType === Enums_1.LoadTypes.Empty || result.loadType === Enums_1.LoadTypes.Error) {
|
|
534
|
-
return [];
|
|
535
|
-
}
|
|
536
|
-
if (result.loadType === Enums_1.LoadTypes.Playlist) {
|
|
537
|
-
result.tracks = result.playlist.tracks;
|
|
538
|
-
}
|
|
539
|
-
if (!result.tracks.length) {
|
|
540
|
-
return [];
|
|
541
|
-
}
|
|
542
|
-
return result.tracks;
|
|
526
|
+
const tracks = this.buildTracksFromResponse(recommendedResult, requester);
|
|
527
|
+
return tracks;
|
|
543
528
|
}
|
|
544
529
|
break;
|
|
545
|
-
case
|
|
530
|
+
case Enums_1.AutoPlayPlatform.VKMusic:
|
|
546
531
|
{
|
|
547
532
|
if (!track.uri.includes("vk.com") && !track.uri.includes("vk.ru")) {
|
|
548
|
-
const res = await this.manager.search({ query: `${track.author} - ${track.title}`, source: Enums_1.SearchPlatform.VKMusic },
|
|
549
|
-
if (
|
|
533
|
+
const res = await this.manager.search({ query: `${track.author} - ${track.title}`, source: Enums_1.SearchPlatform.VKMusic }, requester);
|
|
534
|
+
if (TrackUtils.isErrorOrEmptySearchResult(res))
|
|
550
535
|
return [];
|
|
536
|
+
let resolvedTrack;
|
|
537
|
+
switch (res.loadType) {
|
|
538
|
+
case Enums_1.LoadTypes.Playlist:
|
|
539
|
+
resolvedTrack = res.playlist.tracks[0];
|
|
540
|
+
break;
|
|
541
|
+
case Enums_1.LoadTypes.Track:
|
|
542
|
+
case Enums_1.LoadTypes.Search:
|
|
543
|
+
resolvedTrack = res.tracks[0];
|
|
544
|
+
break;
|
|
545
|
+
default:
|
|
546
|
+
return [];
|
|
551
547
|
}
|
|
552
|
-
if (
|
|
553
|
-
res.tracks = res.playlist.tracks;
|
|
554
|
-
}
|
|
555
|
-
if (!res.tracks.length) {
|
|
548
|
+
if (!resolvedTrack)
|
|
556
549
|
return [];
|
|
557
|
-
|
|
558
|
-
track = res.tracks[0];
|
|
550
|
+
track = resolvedTrack;
|
|
559
551
|
}
|
|
560
552
|
const identifier = `vkrec:${track.identifier}`;
|
|
561
553
|
const recommendedResult = (await this.manager.useableNode.rest.get(`/v4/loadtracks?identifier=${encodeURIComponent(identifier)}`));
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
}
|
|
565
|
-
let tracks = [];
|
|
566
|
-
let playlist = null;
|
|
567
|
-
const requester = track.requester;
|
|
568
|
-
switch (recommendedResult.loadType) {
|
|
569
|
-
case Enums_1.LoadTypes.Search:
|
|
570
|
-
tracks = recommendedResult.data.map((track) => TrackUtils.build(track, requester));
|
|
571
|
-
break;
|
|
572
|
-
case Enums_1.LoadTypes.Track:
|
|
573
|
-
tracks = [TrackUtils.build(recommendedResult.data, requester)];
|
|
574
|
-
break;
|
|
575
|
-
case Enums_1.LoadTypes.Playlist: {
|
|
576
|
-
const playlistData = recommendedResult.data;
|
|
577
|
-
tracks = playlistData.tracks.map((track) => TrackUtils.build(track, requester));
|
|
578
|
-
playlist = {
|
|
579
|
-
name: playlistData.info.name,
|
|
580
|
-
playlistInfo: playlistData.pluginInfo,
|
|
581
|
-
requester: requester,
|
|
582
|
-
tracks,
|
|
583
|
-
duration: tracks.reduce((acc, cur) => acc + (cur.duration || 0), 0),
|
|
584
|
-
};
|
|
585
|
-
break;
|
|
586
|
-
}
|
|
587
|
-
}
|
|
588
|
-
const result = { loadType: recommendedResult.loadType, tracks, playlist };
|
|
589
|
-
if (result.loadType === Enums_1.LoadTypes.Empty || result.loadType === Enums_1.LoadTypes.Error) {
|
|
590
|
-
return [];
|
|
591
|
-
}
|
|
592
|
-
if (result.loadType === Enums_1.LoadTypes.Playlist) {
|
|
593
|
-
result.tracks = result.playlist.tracks;
|
|
594
|
-
}
|
|
595
|
-
if (!result.tracks.length) {
|
|
596
|
-
return [];
|
|
597
|
-
}
|
|
598
|
-
return result.tracks;
|
|
554
|
+
const tracks = this.buildTracksFromResponse(recommendedResult, requester);
|
|
555
|
+
return tracks;
|
|
599
556
|
}
|
|
600
557
|
break;
|
|
601
|
-
case
|
|
558
|
+
case Enums_1.AutoPlayPlatform.Qobuz:
|
|
602
559
|
{
|
|
603
560
|
if (!track.uri.includes("qobuz.com")) {
|
|
604
|
-
const res = await this.manager.search({ query: `${track.author} - ${track.title}`, source: Enums_1.SearchPlatform.Qobuz },
|
|
605
|
-
if (
|
|
561
|
+
const res = await this.manager.search({ query: `${track.author} - ${track.title}`, source: Enums_1.SearchPlatform.Qobuz }, requester);
|
|
562
|
+
if (TrackUtils.isErrorOrEmptySearchResult(res))
|
|
606
563
|
return [];
|
|
564
|
+
let resolvedTrack;
|
|
565
|
+
switch (res.loadType) {
|
|
566
|
+
case Enums_1.LoadTypes.Playlist:
|
|
567
|
+
resolvedTrack = res.playlist.tracks[0];
|
|
568
|
+
break;
|
|
569
|
+
case Enums_1.LoadTypes.Track:
|
|
570
|
+
case Enums_1.LoadTypes.Search:
|
|
571
|
+
resolvedTrack = res.tracks[0];
|
|
572
|
+
break;
|
|
573
|
+
default:
|
|
574
|
+
return [];
|
|
607
575
|
}
|
|
608
|
-
if (
|
|
609
|
-
res.tracks = res.playlist.tracks;
|
|
610
|
-
}
|
|
611
|
-
if (!res.tracks.length) {
|
|
576
|
+
if (!resolvedTrack)
|
|
612
577
|
return [];
|
|
613
|
-
|
|
614
|
-
track = res.tracks[0];
|
|
578
|
+
track = resolvedTrack;
|
|
615
579
|
}
|
|
616
580
|
const identifier = `qbrec:${track.identifier}`;
|
|
617
581
|
const recommendedResult = (await this.manager.useableNode.rest.get(`/v4/loadtracks?identifier=${encodeURIComponent(identifier)}`));
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
}
|
|
621
|
-
let tracks = [];
|
|
622
|
-
let playlist = null;
|
|
623
|
-
const requester = track.requester;
|
|
624
|
-
switch (recommendedResult.loadType) {
|
|
625
|
-
case Enums_1.LoadTypes.Search:
|
|
626
|
-
tracks = recommendedResult.data.map((track) => TrackUtils.build(track, requester));
|
|
627
|
-
break;
|
|
628
|
-
case Enums_1.LoadTypes.Track:
|
|
629
|
-
tracks = [TrackUtils.build(recommendedResult.data, requester)];
|
|
630
|
-
break;
|
|
631
|
-
case Enums_1.LoadTypes.Playlist: {
|
|
632
|
-
const playlistData = recommendedResult.data;
|
|
633
|
-
tracks = playlistData.tracks.map((track) => TrackUtils.build(track, requester));
|
|
634
|
-
playlist = {
|
|
635
|
-
name: playlistData.info.name,
|
|
636
|
-
playlistInfo: playlistData.pluginInfo,
|
|
637
|
-
requester: requester,
|
|
638
|
-
tracks,
|
|
639
|
-
duration: tracks.reduce((acc, cur) => acc + (cur.duration || 0), 0),
|
|
640
|
-
};
|
|
641
|
-
break;
|
|
642
|
-
}
|
|
643
|
-
}
|
|
644
|
-
const result = { loadType: recommendedResult.loadType, tracks, playlist };
|
|
645
|
-
if (result.loadType === Enums_1.LoadTypes.Empty || result.loadType === Enums_1.LoadTypes.Error) {
|
|
646
|
-
return [];
|
|
647
|
-
}
|
|
648
|
-
if (result.loadType === Enums_1.LoadTypes.Playlist) {
|
|
649
|
-
result.tracks = result.playlist.tracks;
|
|
650
|
-
}
|
|
651
|
-
if (!result.tracks.length) {
|
|
652
|
-
return [];
|
|
653
|
-
}
|
|
654
|
-
return result.tracks;
|
|
582
|
+
const tracks = this.buildTracksFromResponse(recommendedResult, requester);
|
|
583
|
+
return tracks;
|
|
655
584
|
}
|
|
656
585
|
break;
|
|
657
586
|
default:
|
|
658
587
|
return [];
|
|
659
588
|
}
|
|
660
589
|
}
|
|
661
|
-
static
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
590
|
+
// static async getSpotifyAccessToken() {
|
|
591
|
+
// const timeoutMs = 15000;
|
|
592
|
+
// let browser;
|
|
593
|
+
// let timeout;
|
|
594
|
+
// try {
|
|
595
|
+
// browser = await playwright.chromium.launch({ headless: true, args: ["--no-sandbox", "--disable-gpu", "--disable-dev-shm-usage"] });
|
|
596
|
+
// const page = await browser.newPage();
|
|
597
|
+
// let tokenCaptured = false;
|
|
598
|
+
// timeout = setTimeout(async () => {
|
|
599
|
+
// if (!tokenCaptured) {
|
|
600
|
+
// console.warn("[Spotify] Token request timeout — did Spotify change their internals?");
|
|
601
|
+
// await browser?.close();
|
|
602
|
+
// }
|
|
603
|
+
// }, timeoutMs);
|
|
604
|
+
// page.on("requestfinished", async (request) => {
|
|
605
|
+
// if (!request.url().includes("/api/token")) return;
|
|
606
|
+
// tokenCaptured = true;
|
|
607
|
+
// try {
|
|
608
|
+
// const response = await request.response();
|
|
609
|
+
// if (response && response.ok()) {
|
|
610
|
+
// const data = await response.json();
|
|
611
|
+
// this.cachedAccessToken = data?.accessToken ?? null;
|
|
612
|
+
// this.cachedAccessTokenExpiresAt = data?.accessTokenExpirationTimestampMs ?? 0;
|
|
613
|
+
// }
|
|
614
|
+
// } catch (err) {
|
|
615
|
+
// console.error("[Spotify] Error reading token response:", err);
|
|
616
|
+
// }
|
|
617
|
+
// clearTimeout(timeout);
|
|
618
|
+
// page.removeAllListeners();
|
|
619
|
+
// await browser.close();
|
|
620
|
+
// });
|
|
621
|
+
// try {
|
|
622
|
+
// await page.goto("https://open.spotify.com/", { waitUntil: "domcontentloaded" });
|
|
623
|
+
// } catch (err) {
|
|
624
|
+
// clearTimeout(timeout);
|
|
625
|
+
// await browser.close();
|
|
626
|
+
// console.error("[Spotify] Failed to navigate:", err);
|
|
627
|
+
// return [];
|
|
628
|
+
// }
|
|
629
|
+
// } catch (err) {
|
|
630
|
+
// clearTimeout(timeout);
|
|
631
|
+
// await browser?.close();
|
|
632
|
+
// console.error("[Spotify] Failed to launch Playwright:", err);
|
|
633
|
+
// }
|
|
634
|
+
// }
|
|
635
|
+
static buildTracksFromResponse(recommendedResult, requester) {
|
|
636
|
+
if (!recommendedResult)
|
|
637
|
+
return [];
|
|
638
|
+
if (TrackUtils.isErrorOrEmptySearchResult(recommendedResult))
|
|
639
|
+
return [];
|
|
640
|
+
switch (recommendedResult.loadType) {
|
|
641
|
+
case Enums_1.LoadTypes.Search: {
|
|
642
|
+
const tracks = recommendedResult.data.map((t) => TrackUtils.build(t, requester));
|
|
643
|
+
return tracks;
|
|
644
|
+
}
|
|
645
|
+
case Enums_1.LoadTypes.Track: {
|
|
646
|
+
const track = TrackUtils.build(recommendedResult.data, requester);
|
|
647
|
+
return [track];
|
|
648
|
+
}
|
|
649
|
+
case Enums_1.LoadTypes.Playlist: {
|
|
650
|
+
const playlistData = recommendedResult.data;
|
|
651
|
+
const tracks = playlistData.tracks.map((t) => TrackUtils.build(t, requester));
|
|
652
|
+
return tracks;
|
|
653
|
+
}
|
|
654
|
+
default:
|
|
655
|
+
throw new Error(`Unsupported loadType: ${recommendedResult.loadType}`);
|
|
656
|
+
}
|
|
675
657
|
}
|
|
676
658
|
}
|
|
677
659
|
exports.AutoPlayUtils = AutoPlayUtils;
|