magmastream 2.9.0-dev.2 → 2.9.0-dev.3
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/structures/Node.js +2 -0
- package/dist/structures/Utils.js +77 -149
- package/package.json +1 -1
package/dist/structures/Node.js
CHANGED
|
@@ -606,6 +606,8 @@ class Node {
|
|
|
606
606
|
return false;
|
|
607
607
|
const lastTrack = player.queue.previous[player.queue.previous.length - 1];
|
|
608
608
|
lastTrack.requester = player.get("Internal_BotUser");
|
|
609
|
+
if (!lastTrack)
|
|
610
|
+
return false;
|
|
609
611
|
const tracks = await Utils_1.AutoPlayUtils.getRecommendedTracks(player, lastTrack, attempt);
|
|
610
612
|
if (tracks.length) {
|
|
611
613
|
player.queue.add(tracks[0]);
|
package/dist/structures/Utils.js
CHANGED
|
@@ -144,29 +144,22 @@ class AutoPlayUtils {
|
|
|
144
144
|
this.manager = manager;
|
|
145
145
|
}
|
|
146
146
|
static async getRecommendedTracks(player, track, attempt = 0) {
|
|
147
|
-
console.log(`[AutoPlay] Attempt ${attempt} for track: ${track.title}`);
|
|
148
147
|
const node = this.manager.useableNode;
|
|
149
148
|
if (!node) {
|
|
150
|
-
console.error("[AutoPlay] No available nodes.");
|
|
151
149
|
throw new Error("No available nodes.");
|
|
152
150
|
}
|
|
153
151
|
if (!player.isAutoplay) {
|
|
154
|
-
console.log("[AutoPlay] Autoplay is disabled. Returning an empty array.");
|
|
155
152
|
return [];
|
|
156
153
|
}
|
|
157
154
|
if (attempt >= player.autoplayTries) {
|
|
158
|
-
console.warn(`[AutoPlay] Reached max autoplay attempts (${player.autoplayTries}).`);
|
|
159
155
|
return [];
|
|
160
156
|
}
|
|
161
157
|
if (!player.queue.previous.length) {
|
|
162
|
-
console.log("[AutoPlay] No previous tracks in the queue. Cannot generate recommendations.");
|
|
163
158
|
return [];
|
|
164
159
|
}
|
|
165
160
|
const apiKey = this.manager.options.lastFmApiKey;
|
|
166
161
|
const enabledSources = node.info.sourceManagers;
|
|
167
162
|
const { autoPlaySearchPlatform } = this.manager.options;
|
|
168
|
-
console.log(`[AutoPlay] Enabled sources: ${enabledSources.join(", ")}`);
|
|
169
|
-
console.log(`[AutoPlay] Preferred autoplay platform: ${autoPlaySearchPlatform}`);
|
|
170
163
|
const supportedPlatforms = ["spotify", "deezer", "soundcloud", "youtube"];
|
|
171
164
|
const platformMapping = {
|
|
172
165
|
[Manager_1.SearchPlatform.AppleMusic]: "applemusic",
|
|
@@ -183,125 +176,95 @@ class AutoPlayUtils {
|
|
|
183
176
|
const mappedPlatform = platformMapping[autoPlaySearchPlatform];
|
|
184
177
|
// Last attempt fallback to YouTube
|
|
185
178
|
if (attempt === player.autoplayTries - 1 && player.autoplayTries > 1 && enabledSources.includes("youtube")) {
|
|
186
|
-
console.log("[AutoPlay] Final attempt: Falling back to YouTube recommendations.");
|
|
187
179
|
return await this.getRecommendedTracksFromYouTube(track);
|
|
188
180
|
}
|
|
189
181
|
// Check if the preferred autoplay platform is supported and enabled
|
|
190
182
|
if (mappedPlatform && supportedPlatforms.includes(mappedPlatform) && enabledSources.includes(mappedPlatform)) {
|
|
191
|
-
console.log(`[AutoPlay] Using recommended platform: ${mappedPlatform}`);
|
|
192
183
|
return await this.getRecommendedTracksFromSource(track, mappedPlatform);
|
|
193
184
|
}
|
|
194
185
|
// Check if Last.fm API is available
|
|
195
186
|
if (apiKey) {
|
|
196
|
-
console.log("[AutoPlay] No preferred platform found. Using Last.fm recommendations.");
|
|
197
187
|
return await this.getRecommendedTracksFromLastFm(track, apiKey);
|
|
198
188
|
}
|
|
199
189
|
// Fallback to YouTube if all else fails
|
|
200
190
|
if (enabledSources.includes("youtube")) {
|
|
201
|
-
console.warn("[AutoPlay] No other sources available. Falling back to YouTube.");
|
|
202
191
|
return await this.getRecommendedTracksFromYouTube(track);
|
|
203
192
|
}
|
|
204
|
-
console.error("[AutoPlay] No suitable platform found. Returning an empty array.");
|
|
205
193
|
return [];
|
|
206
194
|
}
|
|
207
195
|
static async getRecommendedTracksFromLastFm(track, apiKey) {
|
|
208
196
|
const enabledSources = this.manager.useableNode.info.sourceManagers;
|
|
209
197
|
const selectedSource = this.selectPlatform(enabledSources);
|
|
210
|
-
console.log(`[AutoPlay] Selected source: ${selectedSource}`);
|
|
211
198
|
let { author: artist } = track;
|
|
212
199
|
const { title } = track;
|
|
213
|
-
console.log(`[AutoPlay] Searching for recommended tracks for: ${artist} - ${title}`);
|
|
214
200
|
if (!artist || !title) {
|
|
215
201
|
if (!title) {
|
|
216
202
|
// No title provided, search for the artist's top tracks
|
|
217
203
|
const noTitleUrl = `https://ws.audioscrobbler.com/2.0/?method=artist.getTopTracks&artist=${artist}&autocorrect=1&api_key=${apiKey}&format=json`;
|
|
218
|
-
console.log(`[AutoPlay] No title provided. Fetching artist's top tracks from: ${noTitleUrl}`);
|
|
219
204
|
const response = await axios_1.default.get(noTitleUrl);
|
|
220
205
|
if (response.data.error || !response.data.toptracks?.track?.length) {
|
|
221
|
-
console.error("[AutoPlay] Error or no tracks found for the artist. Returning an empty array.");
|
|
222
206
|
return [];
|
|
223
207
|
}
|
|
224
|
-
console.log("[AutoPlay] Successfully fetched artist's top tracks.");
|
|
225
208
|
const randomTrack = response.data.toptracks.track[Math.floor(Math.random() * response.data.toptracks.track.length)];
|
|
226
|
-
console.log(`[AutoPlay] Selected random track: ${randomTrack.artist.name} - ${randomTrack.name}`);
|
|
227
209
|
const res = await this.manager.search({ query: `${randomTrack.artist.name} - ${randomTrack.name}`, source: selectedSource }, track.requester);
|
|
228
210
|
if (res.loadType === LoadTypes.Empty || res.loadType === LoadTypes.Error) {
|
|
229
|
-
console.error("[AutoPlay] Search returned empty or error result. Returning an empty array.");
|
|
230
211
|
return [];
|
|
231
212
|
}
|
|
232
213
|
const filteredTracks = res.tracks.filter((t) => t.uri !== track.uri);
|
|
233
214
|
if (!filteredTracks.length) {
|
|
234
|
-
console.error("[AutoPlay] No suitable tracks found. Returning an empty array.");
|
|
235
215
|
return [];
|
|
236
216
|
}
|
|
237
|
-
console.log("[AutoPlay] Found suitable tracks.");
|
|
238
217
|
return filteredTracks;
|
|
239
218
|
}
|
|
240
219
|
if (!artist) {
|
|
241
220
|
// No artist provided, search for the track title
|
|
242
221
|
const noArtistUrl = `https://ws.audioscrobbler.com/2.0/?method=track.search&track=${title}&api_key=${apiKey}&format=json`;
|
|
243
|
-
console.log(`[AutoPlay] No artist provided. Searching for track: ${title} from: ${noArtistUrl}`);
|
|
244
222
|
const response = await axios_1.default.get(noArtistUrl);
|
|
245
223
|
artist = response.data.results.trackmatches?.track?.[0]?.artist;
|
|
246
224
|
if (!artist) {
|
|
247
|
-
console.error("[AutoPlay] No artist found for track. Returning an empty array.");
|
|
248
225
|
return [];
|
|
249
226
|
}
|
|
250
|
-
console.log(`[AutoPlay] Found artist for track: ${artist}`);
|
|
251
227
|
}
|
|
252
228
|
}
|
|
253
229
|
// Search for similar tracks to the current track
|
|
254
230
|
const url = `https://ws.audioscrobbler.com/2.0/?method=track.getSimilar&artist=${artist}&track=${title}&limit=10&autocorrect=1&api_key=${apiKey}&format=json`;
|
|
255
|
-
console.log(`[AutoPlay] Searching for similar tracks using URL: ${url}`);
|
|
256
231
|
let response;
|
|
257
232
|
try {
|
|
258
233
|
response = await axios_1.default.get(url);
|
|
259
|
-
console.log("[AutoPlay] Successfully fetched similar tracks.");
|
|
260
234
|
}
|
|
261
235
|
catch (error) {
|
|
262
|
-
console.error("[AutoPlay] Error fetching similar tracks. Returning an empty array.");
|
|
263
236
|
console.log(error);
|
|
264
237
|
return [];
|
|
265
238
|
}
|
|
266
239
|
if (response.data.error || !response.data.similartracks?.track?.length) {
|
|
267
|
-
console.error("[AutoPlay] Error or no similar tracks found. Retrying with top tracks.");
|
|
268
240
|
// Retry the request if the first attempt fails
|
|
269
241
|
const retryUrl = `https://ws.audioscrobbler.com/2.0/?method=artist.getTopTracks&artist=${artist}&autocorrect=1&api_key=${apiKey}&format=json`;
|
|
270
242
|
const retryResponse = await axios_1.default.get(retryUrl);
|
|
271
243
|
if (retryResponse.data.error || !retryResponse.data.toptracks?.track?.length) {
|
|
272
|
-
console.error("[AutoPlay] Retry failed. Returning an empty array.");
|
|
273
244
|
return [];
|
|
274
245
|
}
|
|
275
246
|
const randomTrack = retryResponse.data.toptracks.track[Math.floor(Math.random() * retryResponse.data.toptracks.track.length)];
|
|
276
|
-
console.log(`[AutoPlay] Selected random track from retry: ${randomTrack.artist.name} - ${randomTrack.name}`);
|
|
277
247
|
const res = await this.manager.search({ query: `${randomTrack.artist.name} - ${randomTrack.name}`, source: selectedSource }, track.requester);
|
|
278
248
|
if (res.loadType === LoadTypes.Empty || res.loadType === LoadTypes.Error) {
|
|
279
|
-
console.error("[AutoPlay] Retry search returned empty or error result. Returning an empty array.");
|
|
280
249
|
return [];
|
|
281
250
|
}
|
|
282
251
|
const filteredTracks = res.tracks.filter((t) => t.uri !== track.uri);
|
|
283
252
|
if (!filteredTracks.length) {
|
|
284
|
-
console.error("[AutoPlay] No suitable tracks found in retry. Returning an empty array.");
|
|
285
253
|
return [];
|
|
286
254
|
}
|
|
287
|
-
console.log("[AutoPlay] Found suitable tracks from retry.");
|
|
288
255
|
return filteredTracks;
|
|
289
256
|
}
|
|
290
257
|
const randomTrack = response.data.similartracks.track.sort(() => Math.random() - 0.5).shift();
|
|
291
258
|
if (!randomTrack) {
|
|
292
|
-
console.error("[AutoPlay] No similar tracks found after filtering. Returning an empty array.");
|
|
293
259
|
return [];
|
|
294
260
|
}
|
|
295
|
-
console.log(`[AutoPlay] Selected random track: ${randomTrack.name} - ${randomTrack.artist.name}`);
|
|
296
261
|
const res = await this.manager.search({ query: `${randomTrack.artist.name} - ${randomTrack.name}`, source: selectedSource }, track.requester);
|
|
297
262
|
if (res.loadType === LoadTypes.Empty || res.loadType === LoadTypes.Error) {
|
|
298
|
-
console.error("[AutoPlay] Final search returned empty or error result. Returning an empty array.");
|
|
299
263
|
return [];
|
|
300
264
|
}
|
|
301
265
|
if (res.loadType === LoadTypes.Playlist)
|
|
302
266
|
res.tracks = res.playlist.tracks;
|
|
303
267
|
if (!res.tracks.length) {
|
|
304
|
-
console.error("[AutoPlay] No tracks found in final search. Returning an empty array.");
|
|
305
268
|
return [];
|
|
306
269
|
}
|
|
307
270
|
return res.tracks;
|
|
@@ -309,108 +272,104 @@ class AutoPlayUtils {
|
|
|
309
272
|
static async getRecommendedTracksFromSource(track, mappedPlatform) {
|
|
310
273
|
switch (mappedPlatform) {
|
|
311
274
|
case "spotify":
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
275
|
+
try {
|
|
276
|
+
if (!track.uri.includes("spotify")) {
|
|
277
|
+
const res = await this.manager.search({ query: `${track.author} - ${track.title}`, source: Manager_1.SearchPlatform.Spotify }, track.requester);
|
|
278
|
+
if (res.loadType === LoadTypes.Empty || res.loadType === LoadTypes.Error) {
|
|
279
|
+
return [];
|
|
280
|
+
}
|
|
281
|
+
if (res.loadType === LoadTypes.Playlist) {
|
|
282
|
+
res.tracks = res.playlist.tracks;
|
|
283
|
+
}
|
|
284
|
+
if (!res.tracks.length) {
|
|
285
|
+
return [];
|
|
286
|
+
}
|
|
287
|
+
track = res.tracks[0];
|
|
288
|
+
}
|
|
289
|
+
const TOTP_SECRET = new Uint8Array([
|
|
290
|
+
53, 53, 48, 55, 49, 52, 53, 56, 53, 51, 52, 56, 55, 52, 57, 57, 53, 57, 50, 50, 52, 56, 54, 51, 48, 51, 50, 57, 51, 52, 55,
|
|
291
|
+
]);
|
|
292
|
+
const hmac = crypto_1.default.createHmac("sha1", TOTP_SECRET);
|
|
293
|
+
function generateTotp() {
|
|
294
|
+
const counter = Math.floor(Date.now() / 30000);
|
|
295
|
+
const counterBuffer = Buffer.alloc(8);
|
|
296
|
+
counterBuffer.writeBigInt64BE(BigInt(counter));
|
|
297
|
+
hmac.update(counterBuffer);
|
|
298
|
+
const hmacResult = hmac.digest();
|
|
299
|
+
const offset = hmacResult[hmacResult.length - 1] & 15;
|
|
300
|
+
const truncatedValue = ((hmacResult[offset] & 127) << 24) | ((hmacResult[offset + 1] & 255) << 16) | ((hmacResult[offset + 2] & 255) << 8) | (hmacResult[offset + 3] & 255);
|
|
301
|
+
const totp = (truncatedValue % 1000000).toString().padStart(6, "0");
|
|
302
|
+
return [totp, counter * 30000];
|
|
303
|
+
}
|
|
304
|
+
const [totp, timestamp] = generateTotp();
|
|
305
|
+
const params = {
|
|
306
|
+
reason: "transport",
|
|
307
|
+
productType: "embed",
|
|
308
|
+
totp: totp,
|
|
309
|
+
totpVer: 5,
|
|
310
|
+
ts: timestamp,
|
|
311
|
+
};
|
|
312
|
+
let body;
|
|
313
|
+
try {
|
|
314
|
+
const response = await axios_1.default.get("https://open.spotify.com/get_access_token", { params });
|
|
315
|
+
body = response.data;
|
|
316
|
+
}
|
|
317
|
+
catch (error) {
|
|
318
|
+
console.error("[AutoPlay] Failed to get access token:", error.response?.status, error.response?.data || error.message);
|
|
319
|
+
return [];
|
|
320
|
+
}
|
|
321
|
+
let json;
|
|
322
|
+
try {
|
|
323
|
+
const response = await axios_1.default.get(`https://api.spotify.com/v1/recommendations`, {
|
|
324
|
+
params: { limit: 10, seed_tracks: track.identifier },
|
|
325
|
+
headers: {
|
|
326
|
+
Authorization: `Bearer ${body.accessToken}`,
|
|
327
|
+
"Content-Type": "application/json",
|
|
328
|
+
},
|
|
329
|
+
});
|
|
330
|
+
json = response.data;
|
|
331
|
+
}
|
|
332
|
+
catch (error) {
|
|
333
|
+
console.error("[AutoPlay] Failed to fetch recommendations:", error.response?.status, error.response?.data || error.message);
|
|
334
|
+
return [];
|
|
335
|
+
}
|
|
336
|
+
if (!json.tracks || !json.tracks.length) {
|
|
337
|
+
return [];
|
|
338
|
+
}
|
|
339
|
+
const recommendedTrackId = json.tracks[Math.floor(Math.random() * json.tracks.length)].id;
|
|
340
|
+
const res = await this.manager.search({ query: `https://open.spotify.com/track/${recommendedTrackId}`, source: Manager_1.SearchPlatform.Spotify }, track.requester);
|
|
316
341
|
if (res.loadType === LoadTypes.Empty || res.loadType === LoadTypes.Error) {
|
|
317
|
-
console.error("[AutoPlay] Search returned empty or error result. Returning an empty array.");
|
|
318
342
|
return [];
|
|
319
343
|
}
|
|
320
344
|
if (res.loadType === LoadTypes.Playlist) {
|
|
321
|
-
console.log("[AutoPlay] Search returned a playlist. Flattening tracks.");
|
|
322
345
|
res.tracks = res.playlist.tracks;
|
|
323
346
|
}
|
|
324
347
|
if (!res.tracks.length) {
|
|
325
|
-
console.error("[AutoPlay] No tracks found in the search. Returning an empty array.");
|
|
326
348
|
return [];
|
|
327
349
|
}
|
|
328
|
-
|
|
329
|
-
track = res.tracks[0];
|
|
330
|
-
}
|
|
331
|
-
const TOTP_SECRET = new Uint8Array([
|
|
332
|
-
53, 53, 48, 55, 49, 52, 53, 56, 53, 51, 52, 56, 55, 52, 57, 57, 53, 57, 50, 50, 52, 56, 54, 51, 48, 51, 50, 57, 51, 52, 55,
|
|
333
|
-
]);
|
|
334
|
-
const hmac = crypto_1.default.createHmac("sha1", TOTP_SECRET);
|
|
335
|
-
function generateTotp() {
|
|
336
|
-
const counter = Math.floor(Date.now() / 30000);
|
|
337
|
-
const counterBuffer = Buffer.alloc(8);
|
|
338
|
-
counterBuffer.writeBigInt64BE(BigInt(counter));
|
|
339
|
-
hmac.update(counterBuffer);
|
|
340
|
-
const hmacResult = hmac.digest();
|
|
341
|
-
const offset = hmacResult[hmacResult.length - 1] & 15;
|
|
342
|
-
const truncatedValue = ((hmacResult[offset] & 127) << 24) | ((hmacResult[offset + 1] & 255) << 16) | ((hmacResult[offset + 2] & 255) << 8) | (hmacResult[offset + 3] & 255);
|
|
343
|
-
const totp = (truncatedValue % 1000000).toString().padStart(6, "0");
|
|
344
|
-
return [totp, counter * 30000];
|
|
345
|
-
}
|
|
346
|
-
const [totp, timestamp] = generateTotp();
|
|
347
|
-
console.log("[AutoPlay] Generated TOTP:", totp);
|
|
348
|
-
const params = {
|
|
349
|
-
reason: "transport",
|
|
350
|
-
productType: "embed",
|
|
351
|
-
totp: totp,
|
|
352
|
-
totpVer: 5,
|
|
353
|
-
ts: timestamp,
|
|
354
|
-
};
|
|
355
|
-
console.log("[AutoPlay] Sending request to get access token with params:", params);
|
|
356
|
-
const { data: body } = await axios_1.default.get("https://open.spotify.com/get_access_token", { params });
|
|
357
|
-
console.log("[AutoPlay] Access token received.");
|
|
358
|
-
const { data: json } = await axios_1.default.get(`https://api.spotify.com/v1/recommendations`, {
|
|
359
|
-
params: { limit: 10, seed_tracks: track.identifier },
|
|
360
|
-
headers: {
|
|
361
|
-
Authorization: `Bearer ${body.accessToken}`,
|
|
362
|
-
"Content-Type": "application/json",
|
|
363
|
-
},
|
|
364
|
-
});
|
|
365
|
-
if (!json.tracks || !json.tracks.length) {
|
|
366
|
-
console.error("[AutoPlay] No recommended tracks received from Spotify API. Returning an empty array.");
|
|
367
|
-
return [];
|
|
368
|
-
}
|
|
369
|
-
console.log("[AutoPlay] Recommended tracks received from Spotify.");
|
|
370
|
-
// Return a random recommended track ID
|
|
371
|
-
const recommendedTrackId = json.tracks[Math.floor(Math.random() * json.tracks.length)].id;
|
|
372
|
-
console.log(`[AutoPlay] Selected random recommended track ID: ${recommendedTrackId}`);
|
|
373
|
-
console.log("[AutoPlay] Searching for the recommended track:", recommendedTrackId);
|
|
374
|
-
const res = await this.manager.search({ query: `https://open.spotify.com/track/${recommendedTrackId}`, source: Manager_1.SearchPlatform.Spotify }, track.requester);
|
|
375
|
-
if (res.loadType === LoadTypes.Empty || res.loadType === LoadTypes.Error) {
|
|
376
|
-
console.error("[AutoPlay] Final search returned empty or error result. Returning an empty array.");
|
|
377
|
-
return [];
|
|
378
|
-
}
|
|
379
|
-
if (res.loadType === LoadTypes.Playlist) {
|
|
380
|
-
console.log("[AutoPlay] Final search returned a playlist. Flattening tracks.");
|
|
381
|
-
res.tracks = res.playlist.tracks;
|
|
350
|
+
return res.tracks;
|
|
382
351
|
}
|
|
383
|
-
|
|
384
|
-
console.error("[AutoPlay]
|
|
352
|
+
catch (error) {
|
|
353
|
+
console.error("[AutoPlay] Unexpected error:", error.message || error);
|
|
385
354
|
return [];
|
|
386
355
|
}
|
|
387
|
-
console.log("[AutoPlay] Recommended tracks found and ready to return.");
|
|
388
|
-
return res.tracks;
|
|
389
356
|
case "deezer":
|
|
390
|
-
console.log("[AutoPlay] Checking if track URI includes 'deezer':", track.uri);
|
|
391
357
|
if (!track.uri.includes("deezer")) {
|
|
392
|
-
console.log("[AutoPlay] Track URI does not include 'deezer'. Searching for track:", `${track.author} - ${track.title}`);
|
|
393
358
|
const res = await this.manager.search({ query: `${track.author} - ${track.title}`, source: Manager_1.SearchPlatform.Deezer }, track.requester);
|
|
394
359
|
if (res.loadType === LoadTypes.Empty || res.loadType === LoadTypes.Error) {
|
|
395
|
-
console.error("[AutoPlay] Search returned empty or error result. Returning an empty array.");
|
|
396
360
|
return [];
|
|
397
361
|
}
|
|
398
362
|
if (res.loadType === LoadTypes.Playlist) {
|
|
399
|
-
console.log("[AutoPlay] Search returned a playlist. Flattening tracks.");
|
|
400
363
|
res.tracks = res.playlist.tracks;
|
|
401
364
|
}
|
|
402
365
|
if (!res.tracks.length) {
|
|
403
|
-
console.error("[AutoPlay] No tracks found in the search. Returning an empty array.");
|
|
404
366
|
return [];
|
|
405
367
|
}
|
|
406
|
-
console.log("[AutoPlay] Track found in search:", res.tracks[0].uri);
|
|
407
368
|
track = res.tracks[0];
|
|
408
369
|
}
|
|
409
370
|
const identifier = `dzrec:${track.identifier}`;
|
|
410
|
-
console.log("[AutoPlay] Generating Deezer recommendation identifier:", identifier);
|
|
411
371
|
const recommendedResult = (await this.manager.useableNode.rest.get(`/v4/loadtracks?identifier=${encodeURIComponent(identifier)}`));
|
|
412
372
|
if (!recommendedResult) {
|
|
413
|
-
console.error("[AutoPlay] No recommended result received from Deezer. Returning an empty array.");
|
|
414
373
|
return [];
|
|
415
374
|
}
|
|
416
375
|
let tracks = [];
|
|
@@ -418,15 +377,12 @@ class AutoPlayUtils {
|
|
|
418
377
|
const requester = track.requester;
|
|
419
378
|
switch (recommendedResult.loadType) {
|
|
420
379
|
case LoadTypes.Search:
|
|
421
|
-
console.log("[AutoPlay] Recommended result is of type 'Search'. Building tracks.");
|
|
422
380
|
tracks = recommendedResult.data.map((track) => TrackUtils.build(track, requester));
|
|
423
381
|
break;
|
|
424
382
|
case LoadTypes.Track:
|
|
425
|
-
console.log("[AutoPlay] Recommended result is of type 'Track'. Building a single track.");
|
|
426
383
|
tracks = [TrackUtils.build(recommendedResult.data, requester)];
|
|
427
384
|
break;
|
|
428
385
|
case LoadTypes.Playlist: {
|
|
429
|
-
console.log("[AutoPlay] Recommended result is of type 'Playlist'. Building playlist.");
|
|
430
386
|
const playlistData = recommendedResult.data;
|
|
431
387
|
tracks = playlistData.tracks.map((track) => TrackUtils.build(track, requester));
|
|
432
388
|
playlist = {
|
|
@@ -441,42 +397,37 @@ class AutoPlayUtils {
|
|
|
441
397
|
}
|
|
442
398
|
const result = { loadType: recommendedResult.loadType, tracks, playlist };
|
|
443
399
|
if (result.loadType === LoadTypes.Empty || result.loadType === LoadTypes.Error) {
|
|
444
|
-
console.error("[AutoPlay] Final result load type is empty or error. Returning an empty array.");
|
|
445
400
|
return [];
|
|
446
401
|
}
|
|
447
402
|
if (result.loadType === LoadTypes.Playlist) {
|
|
448
|
-
console.log("[AutoPlay] Final result load type is Playlist. Flattening tracks.");
|
|
449
403
|
result.tracks = result.playlist.tracks;
|
|
450
404
|
}
|
|
451
405
|
if (!result.tracks.length) {
|
|
452
|
-
console.error("[AutoPlay] No tracks found in final result. Returning an empty array.");
|
|
453
406
|
return [];
|
|
454
407
|
}
|
|
455
|
-
console.log("[AutoPlay] Tracks found and ready to return.");
|
|
456
408
|
return result.tracks;
|
|
457
409
|
case "soundcloud":
|
|
458
|
-
console.log("[AutoPlay] Checking if track URI includes 'soundcloud':", track.uri);
|
|
459
410
|
if (!track.uri.includes("soundcloud")) {
|
|
460
|
-
console.log("[AutoPlay] Track URI does not include 'soundcloud'. Searching for track:", `${track.author} - ${track.title}`);
|
|
461
411
|
const res = await this.manager.search({ query: `${track.author} - ${track.title}`, source: Manager_1.SearchPlatform.SoundCloud }, track.requester);
|
|
462
412
|
if (res.loadType === LoadTypes.Empty || res.loadType === LoadTypes.Error) {
|
|
463
|
-
console.error("[AutoPlay] Search returned empty or error result. Returning an empty array.");
|
|
464
413
|
return [];
|
|
465
414
|
}
|
|
466
415
|
if (res.loadType === LoadTypes.Playlist) {
|
|
467
|
-
console.log("[AutoPlay] Search returned a playlist. Flattening tracks.");
|
|
468
416
|
res.tracks = res.playlist.tracks;
|
|
469
417
|
}
|
|
470
418
|
if (!res.tracks.length) {
|
|
471
|
-
console.error("[AutoPlay] No tracks found in the search. Returning an empty array.");
|
|
472
419
|
return [];
|
|
473
420
|
}
|
|
474
|
-
console.log("[AutoPlay] Track found in search:", res.tracks[0].uri);
|
|
475
421
|
track = res.tracks[0];
|
|
476
422
|
}
|
|
477
423
|
try {
|
|
478
|
-
|
|
479
|
-
|
|
424
|
+
const recommendedRes = await axios_1.default.get(`${track.uri}/recommended`).catch((err) => {
|
|
425
|
+
console.error(`[AutoPlay] Failed to fetch SoundCloud recommendations. Status: ${err.response?.status || "Unknown"}`, err.message);
|
|
426
|
+
return null;
|
|
427
|
+
});
|
|
428
|
+
if (!recommendedRes) {
|
|
429
|
+
return [];
|
|
430
|
+
}
|
|
480
431
|
const html = recommendedRes.data;
|
|
481
432
|
const dom = new jsdom_1.JSDOM(html);
|
|
482
433
|
const document = dom.window.document;
|
|
@@ -484,7 +435,6 @@ class AutoPlayUtils {
|
|
|
484
435
|
const sectionElement = secondNoscript.querySelector("section");
|
|
485
436
|
const articleElements = sectionElement.querySelectorAll("article");
|
|
486
437
|
if (!articleElements || articleElements.length === 0) {
|
|
487
|
-
console.error("[AutoPlay] No article elements found for recommendations. Returning an empty array.");
|
|
488
438
|
return [];
|
|
489
439
|
}
|
|
490
440
|
const urls = Array.from(articleElements)
|
|
@@ -495,77 +445,55 @@ class AutoPlayUtils {
|
|
|
495
445
|
})
|
|
496
446
|
.filter(Boolean);
|
|
497
447
|
if (!urls.length) {
|
|
498
|
-
console.error("[AutoPlay] No valid URLs found in the recommendations. Returning an empty array.");
|
|
499
448
|
return [];
|
|
500
449
|
}
|
|
501
450
|
const randomUrl = urls[Math.floor(Math.random() * urls.length)];
|
|
502
|
-
console.log("[AutoPlay] Selected random URL for recommended track:", randomUrl);
|
|
503
451
|
const res = await this.manager.search({ query: randomUrl, source: Manager_1.SearchPlatform.SoundCloud }, track.requester);
|
|
504
452
|
if (res.loadType === LoadTypes.Empty || res.loadType === LoadTypes.Error) {
|
|
505
|
-
console.error("[AutoPlay] Search for recommended track returned empty or error result. Returning an empty array.");
|
|
506
453
|
return [];
|
|
507
454
|
}
|
|
508
455
|
if (res.loadType === LoadTypes.Playlist) {
|
|
509
|
-
console.log("[AutoPlay] Search for recommended track returned a playlist. Flattening tracks.");
|
|
510
456
|
res.tracks = res.playlist.tracks;
|
|
511
457
|
}
|
|
512
458
|
if (!res.tracks.length) {
|
|
513
|
-
console.error("[AutoPlay] No tracks found in the search for recommended track. Returning an empty array.");
|
|
514
459
|
return [];
|
|
515
460
|
}
|
|
516
|
-
console.log("[AutoPlay] Found recommended tracks:", res.tracks.map((track) => track.uri));
|
|
517
461
|
return res.tracks;
|
|
518
462
|
}
|
|
519
463
|
catch (error) {
|
|
520
464
|
console.error("[AutoPlay] Error occurred while fetching recommendations:", error);
|
|
521
465
|
return [];
|
|
522
466
|
}
|
|
523
|
-
break;
|
|
524
467
|
case "youtube":
|
|
525
468
|
return this.getRecommendedTracksFromYouTube(track);
|
|
526
|
-
break;
|
|
527
469
|
default:
|
|
528
470
|
return [];
|
|
529
471
|
}
|
|
530
472
|
}
|
|
531
473
|
static async getRecommendedTracksFromYouTube(track) {
|
|
532
|
-
console.log("[YouTube Recommendation] Checking if track URI includes YouTube URL:", track.uri);
|
|
533
|
-
// Check if the previous track has a YouTube URL
|
|
534
474
|
const hasYouTubeURL = ["youtube.com", "youtu.be"].some((url) => track.uri.includes(url));
|
|
535
475
|
let videoID = null;
|
|
536
476
|
if (hasYouTubeURL) {
|
|
537
|
-
console.log("[YouTube Recommendation] Track contains a YouTube URL. Extracting video ID from URI.");
|
|
538
477
|
videoID = track.uri.split("=").pop();
|
|
539
478
|
}
|
|
540
479
|
else {
|
|
541
|
-
console.log("[YouTube Recommendation] Track does not contain a YouTube URL. Searching for the track on YouTube.");
|
|
542
480
|
const searchResult = await this.manager.search({ query: `${track.author} - ${track.title}`, source: Manager_1.SearchPlatform.YouTube }, track.requester);
|
|
543
481
|
videoID = searchResult.tracks[0]?.uri.split("=").pop();
|
|
544
482
|
}
|
|
545
483
|
if (!videoID) {
|
|
546
|
-
console.error("[YouTube Recommendation] Video ID not found. Returning an empty array.");
|
|
547
484
|
return [];
|
|
548
485
|
}
|
|
549
|
-
console.log("[YouTube Recommendation] Video ID extracted:", videoID);
|
|
550
|
-
// Get a random video index between 2 and 24
|
|
551
486
|
let randomIndex;
|
|
552
487
|
let searchURI;
|
|
553
488
|
do {
|
|
554
|
-
randomIndex = Math.floor(Math.random() * 23) + 2;
|
|
489
|
+
randomIndex = Math.floor(Math.random() * 23) + 2;
|
|
555
490
|
searchURI = `https://www.youtube.com/watch?v=${videoID}&list=RD${videoID}&index=${randomIndex}`;
|
|
556
|
-
console.log("[YouTube Recommendation] Generated random search URI:", searchURI);
|
|
557
491
|
} while (track.uri.includes(searchURI));
|
|
558
|
-
// Search for the video and return false if the search fails
|
|
559
|
-
console.log("[YouTube Recommendation] Searching for the video using search URI:", searchURI);
|
|
560
492
|
const res = await this.manager.search({ query: searchURI, source: Manager_1.SearchPlatform.YouTube }, track.requester);
|
|
561
493
|
if (res.loadType === LoadTypes.Empty || res.loadType === LoadTypes.Error) {
|
|
562
|
-
console.error("[YouTube Recommendation] Search failed or returned empty results. Returning an empty array.");
|
|
563
494
|
return [];
|
|
564
495
|
}
|
|
565
|
-
// Filter out tracks that have the same URI as the current track
|
|
566
|
-
console.log("[YouTube Recommendation] Filtering tracks that do not match the current track URI.");
|
|
567
496
|
const filteredTracks = res.tracks.filter((t) => t.uri !== track.uri);
|
|
568
|
-
console.log("[YouTube Recommendation] Returning filtered recommended tracks:", filteredTracks.map((t) => t.uri));
|
|
569
497
|
return filteredTracks;
|
|
570
498
|
}
|
|
571
499
|
static selectPlatform(enabledSources) {
|