magmastream 2.9.0-dev.32 → 2.9.0-dev.33
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 +45 -10
- package/dist/structures/Manager.js +15 -2
- package/dist/structures/Utils.js +296 -314
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -736,15 +736,40 @@ interface LavaPlayer {
|
|
|
736
736
|
filters: Record<string, unknown>;
|
|
737
737
|
}
|
|
738
738
|
/**
|
|
739
|
-
* Search Result
|
|
739
|
+
* Error or Empty Search Result
|
|
740
740
|
*/
|
|
741
|
-
interface
|
|
741
|
+
interface ErrorOrEmptySearchResult {
|
|
742
742
|
/** The load type of the result. */
|
|
743
|
-
loadType: LoadTypes;
|
|
744
|
-
|
|
743
|
+
loadType: LoadTypes.Empty | LoadTypes.Error;
|
|
744
|
+
}
|
|
745
|
+
/**
|
|
746
|
+
* Track Search Result
|
|
747
|
+
*/
|
|
748
|
+
interface TrackSearchResult {
|
|
749
|
+
/** The load type is always 'track' */
|
|
750
|
+
loadType: LoadTypes.Track;
|
|
751
|
+
/** The track obtained */
|
|
752
|
+
tracks: [Track];
|
|
753
|
+
}
|
|
754
|
+
/**
|
|
755
|
+
* Search Result
|
|
756
|
+
*/
|
|
757
|
+
interface SearchSearchResult {
|
|
758
|
+
/** The load type is always 'search' */
|
|
759
|
+
loadType: LoadTypes.Search;
|
|
760
|
+
/** The tracks of the search result */
|
|
745
761
|
tracks: Track[];
|
|
746
|
-
|
|
747
|
-
|
|
762
|
+
}
|
|
763
|
+
/**
|
|
764
|
+
* Playlist Search Result
|
|
765
|
+
*/
|
|
766
|
+
interface PlaylistSearchResult {
|
|
767
|
+
/** The playlist load type */
|
|
768
|
+
loadType: LoadTypes.Playlist;
|
|
769
|
+
/** The tracks of the playlist */
|
|
770
|
+
tracks: Track[];
|
|
771
|
+
/** The playlist info */
|
|
772
|
+
playlist: PlaylistData;
|
|
748
773
|
}
|
|
749
774
|
/**
|
|
750
775
|
* Playlist Data
|
|
@@ -1367,6 +1392,10 @@ type NodeLinkGetLyrics = NodeLinkGetLyricsSingle | NodeLinkGetLyricsMultiple | N
|
|
|
1367
1392
|
* Voice Receiver Event Enum type
|
|
1368
1393
|
*/
|
|
1369
1394
|
type VoiceReceiverEvent = StartSpeakingEventVoiceReceiver | EndSpeakingEventVoiceReceiver;
|
|
1395
|
+
/**
|
|
1396
|
+
* Search Result Enum type
|
|
1397
|
+
*/
|
|
1398
|
+
type SearchResult = TrackSearchResult | SearchSearchResult | PlaylistSearchResult | ErrorOrEmptySearchResult;
|
|
1370
1399
|
|
|
1371
1400
|
declare class Node$1 {
|
|
1372
1401
|
manager: Manager;
|
|
@@ -2732,6 +2761,12 @@ declare abstract class TrackUtils {
|
|
|
2732
2761
|
* @returns The built Track.
|
|
2733
2762
|
*/
|
|
2734
2763
|
static build<T = User | ClientUser>(data: TrackData, requester?: T): Track;
|
|
2764
|
+
/**
|
|
2765
|
+
* Validates a search result.
|
|
2766
|
+
* @param result The search result to validate.
|
|
2767
|
+
* @returns Whether the search result is valid.
|
|
2768
|
+
*/
|
|
2769
|
+
static isErrorOrEmptySearchResult(result: SearchResult): result is ErrorOrEmptySearchResult;
|
|
2735
2770
|
}
|
|
2736
2771
|
declare abstract class AutoPlayUtils {
|
|
2737
2772
|
private static manager;
|
|
@@ -2740,7 +2775,7 @@ declare abstract class AutoPlayUtils {
|
|
|
2740
2775
|
* @param manager The manager instance to use.
|
|
2741
2776
|
* @hidden
|
|
2742
2777
|
*/
|
|
2743
|
-
static init(manager: Manager): void
|
|
2778
|
+
static init(manager: Manager): Promise<void>;
|
|
2744
2779
|
/**
|
|
2745
2780
|
* Gets recommended tracks for the given track.
|
|
2746
2781
|
* @param track The track to get recommended tracks for.
|
|
@@ -2760,8 +2795,8 @@ declare abstract class AutoPlayUtils {
|
|
|
2760
2795
|
* @param platform The source to get recommended tracks from.
|
|
2761
2796
|
* @returns An array of recommended tracks.
|
|
2762
2797
|
*/
|
|
2763
|
-
static getRecommendedTracksFromSource(track: Track, platform:
|
|
2764
|
-
static
|
|
2798
|
+
static getRecommendedTracksFromSource(track: Track, platform: AutoPlayPlatform): Promise<Track[]>;
|
|
2799
|
+
static buildTracksFromResponse<T>(recommendedResult: LavalinkResponse, requester?: T): Track[];
|
|
2765
2800
|
}
|
|
2766
2801
|
/** Gets or extends structures to extend the built in, or already extended, classes to add more functionality. */
|
|
2767
2802
|
declare abstract class Structure {
|
|
@@ -2815,4 +2850,4 @@ declare class OceanicManager extends Manager {
|
|
|
2815
2850
|
}
|
|
2816
2851
|
|
|
2817
2852
|
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 };
|
|
2853
|
+
export type { CPUStats, DiscordPacket, EndSpeakingEventVoiceReceiver, EndSpeakingEventVoiceReceiverData, EqualizerBand, ErrorOrEmptySearchResult, 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, 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 };
|
|
@@ -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/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;
|