magmastream 2.6.0-beta.7 → 2.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/index.d.ts +5 -1
- package/dist/structures/Manager.js +1 -1
- package/dist/structures/Node.js +101 -61
- package/dist/utils/managerCheck.js +4 -1
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -44,6 +44,7 @@ Also you can join the [Discord Support Server](https://discord.com/invite/HV59Z3
|
|
|
44
44
|
| [Lunio](https://discord.com/api/oauth2/authorize?client_id=945030475779551415&permissions=61991952&scope=bot+applications.commands) | vexi |
|
|
45
45
|
| [JukeDisc](https://discord.com/oauth2/authorize?client_id=1109751797549105176&permissions=968552214080&scope=bot+applications.commands) | Theo |
|
|
46
46
|
| [Cool Music](https://discord.com/oauth2/authorize?client_id=923529398425096193&permissions=12888394808&redirect_uri=https%3A%2F%2Fdiscord.gg%2Fcool-music-support-925619107460698202&response_type=code&scope=bot%20identify%20applications.commands) | Itz Random |
|
|
47
|
+
| [Soundy](https://dsc.gg/sndy) | iaMJ |
|
|
47
48
|
|
|
48
49
|
If you want to add your own bot create a pull request with your bot added. Please add your full name.
|
|
49
50
|
|
package/dist/index.d.ts
CHANGED
|
@@ -527,7 +527,6 @@ declare class Node {
|
|
|
527
527
|
extractSpotifyTrackID(url: string): string | null;
|
|
528
528
|
extractSpotifyArtistID(url: string): string | null;
|
|
529
529
|
private handleAutoplay;
|
|
530
|
-
private handleSpotifyAutoplay;
|
|
531
530
|
private handleFailedTrack;
|
|
532
531
|
private handleRepeatedTrack;
|
|
533
532
|
private playNextTrack;
|
|
@@ -997,6 +996,11 @@ interface ManagerOptions {
|
|
|
997
996
|
defaultSearchPlatform?: SearchPlatform;
|
|
998
997
|
/** Whether the YouTube video titles should be replaced if the Author does not exactly match. */
|
|
999
998
|
replaceYouTubeCredentials?: boolean;
|
|
999
|
+
/** The last.fm API key.
|
|
1000
|
+
* If you need to create one go here: https://www.last.fm/api/account/create.
|
|
1001
|
+
* If you already have one, get it from here: https://www.last.fm/api/accounts.
|
|
1002
|
+
*/
|
|
1003
|
+
lastFmApiKey: string;
|
|
1000
1004
|
/**
|
|
1001
1005
|
* Function to send data to the websocket.
|
|
1002
1006
|
* @param id
|
|
@@ -146,7 +146,7 @@ class Manager extends events_1.EventEmitter {
|
|
|
146
146
|
if (state.dynamicRepeat) {
|
|
147
147
|
player.setDynamicRepeat(state.dynamicRepeat, state.dynamicLoopInterval._idleTimeout);
|
|
148
148
|
}
|
|
149
|
-
if (state.isAutoplay) {
|
|
149
|
+
if (state.isAutoplay && state?.data?.Internal_BotUser) {
|
|
150
150
|
player.setAutoplay(state.isAutoplay, state.data.Internal_BotUser);
|
|
151
151
|
}
|
|
152
152
|
}
|
package/dist/structures/Node.js
CHANGED
|
@@ -8,6 +8,7 @@ const nodeCheck_1 = tslib_1.__importDefault(require("../utils/nodeCheck"));
|
|
|
8
8
|
const ws_1 = tslib_1.__importDefault(require("ws"));
|
|
9
9
|
const fs_1 = tslib_1.__importDefault(require("fs"));
|
|
10
10
|
const path_1 = tslib_1.__importDefault(require("path"));
|
|
11
|
+
const axios_1 = tslib_1.__importDefault(require("axios"));
|
|
11
12
|
exports.validSponsorBlocks = ["sponsor", "selfpromo", "interaction", "intro", "outro", "preview", "music_offtopic", "filler"];
|
|
12
13
|
const sessionIdsFilePath = path_1.default.join(process.cwd(), "node_modules", "magmastream", "dist", "sessionData", "sessionIds.json");
|
|
13
14
|
let sessionIdsMap = new Map();
|
|
@@ -367,72 +368,102 @@ class Node {
|
|
|
367
368
|
return match ? match[1] : null;
|
|
368
369
|
}
|
|
369
370
|
// Handle autoplay
|
|
370
|
-
async handleAutoplay(player, track) {
|
|
371
|
+
async handleAutoplay(player, track, attempts = 0) {
|
|
372
|
+
if (!player.isAutoplay || attempts > 3 || !player.queue.previous)
|
|
373
|
+
return false;
|
|
371
374
|
const previousTrack = player.queue.previous;
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
375
|
+
const apiKey = this.manager.options.lastFmApiKey;
|
|
376
|
+
// If Last.fm API is not available and YouTube is not supported
|
|
377
|
+
if (!apiKey && !this.info.sourceManagers.includes("youtube"))
|
|
378
|
+
return false;
|
|
379
|
+
// Handle YouTube autoplay logic
|
|
380
|
+
if ((!apiKey && this.info.sourceManagers.includes("youtube")) || (attempts > 2 && this.info.sourceManagers.includes("youtube"))) {
|
|
381
|
+
const hasYouTubeURL = ["youtube.com", "youtu.be"].some((url) => previousTrack.uri.includes(url));
|
|
382
|
+
const videoID = hasYouTubeURL
|
|
383
|
+
? previousTrack.uri.split("=").pop()
|
|
384
|
+
: (await player.search(`${previousTrack.author} - ${previousTrack.title}`, player.get("Internal_BotUser"))).tracks[0]?.uri.split("=").pop();
|
|
385
|
+
if (!videoID)
|
|
386
|
+
return false;
|
|
387
|
+
let randomIndex;
|
|
388
|
+
let searchURI;
|
|
389
|
+
do {
|
|
390
|
+
randomIndex = Math.floor(Math.random() * 23) + 2;
|
|
391
|
+
searchURI = `https://www.youtube.com/watch?v=${videoID}&list=RD${videoID}&index=${randomIndex}`;
|
|
392
|
+
} while (track.uri.includes(searchURI));
|
|
393
|
+
const res = await player.search(searchURI, player.get("Internal_BotUser"));
|
|
394
|
+
if (res.loadType === "empty" || res.loadType === "error")
|
|
395
|
+
return false;
|
|
396
|
+
const foundTrack = res.tracks.find((t) => t.uri !== track.uri && t.author !== track.author && t.title !== track.title);
|
|
397
|
+
if (!foundTrack)
|
|
398
|
+
return false;
|
|
399
|
+
player.queue.add(foundTrack);
|
|
400
|
+
player.play();
|
|
401
|
+
return true;
|
|
379
402
|
}
|
|
380
|
-
|
|
381
|
-
let
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
403
|
+
// Handle Last.fm-based autoplay logic
|
|
404
|
+
let { author: artist } = previousTrack;
|
|
405
|
+
const { title, uri } = previousTrack;
|
|
406
|
+
const enabledSources = this.info.sourceManagers;
|
|
407
|
+
const isSpotifyEnabled = enabledSources.includes("spotify");
|
|
408
|
+
const isSpotifyUri = uri.includes("spotify.com");
|
|
409
|
+
let selectedSource = null;
|
|
410
|
+
if (isSpotifyEnabled && isSpotifyUri) {
|
|
411
|
+
selectedSource = "spotify";
|
|
385
412
|
}
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
413
|
+
else {
|
|
414
|
+
selectedSource = this.manager.options.defaultSearchPlatform;
|
|
415
|
+
}
|
|
416
|
+
if (!artist || !title) {
|
|
417
|
+
if (!title) {
|
|
418
|
+
const noTitleUrl = `https://ws.audioscrobbler.com/2.0/?method=artist.getTopTracks&artist=${artist}&autocorrect=1&api_key=${apiKey}&format=json`;
|
|
419
|
+
const response = await axios_1.default.get(noTitleUrl);
|
|
420
|
+
if (response.data.error || !response.data.toptracks?.track?.length)
|
|
421
|
+
return false;
|
|
422
|
+
const randomTrack = response.data.toptracks.track[Math.floor(Math.random() * response.data.toptracks.track.length)];
|
|
423
|
+
const res = await player.search({ query: `${randomTrack.artist.name} - ${randomTrack.name}`, source: selectedSource }, player.get("Internal_BotUser"));
|
|
424
|
+
if (res.loadType === "empty" || res.loadType === "error")
|
|
425
|
+
return false;
|
|
426
|
+
const foundTrack = res.tracks.find((t) => t.uri !== track.uri);
|
|
427
|
+
if (!foundTrack)
|
|
428
|
+
return false;
|
|
429
|
+
player.queue.add(foundTrack);
|
|
430
|
+
player.play();
|
|
431
|
+
return true;
|
|
432
|
+
}
|
|
433
|
+
else if (!artist) {
|
|
434
|
+
const noArtistUrl = `https://ws.audioscrobbler.com/2.0/?method=track.search&track=${title}&api_key=${apiKey}&format=json`;
|
|
435
|
+
const response = await axios_1.default.get(noArtistUrl);
|
|
436
|
+
artist = response.data.results.trackmatches?.track?.[0]?.artist;
|
|
437
|
+
if (!artist)
|
|
438
|
+
return false;
|
|
408
439
|
}
|
|
409
440
|
}
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
441
|
+
const url = `https://ws.audioscrobbler.com/2.0/?method=track.getSimilar&artist=${artist}&track=${title}&limit=10&autocorrect=1&api_key=${apiKey}&format=json`;
|
|
442
|
+
const response = await axios_1.default.get(url);
|
|
443
|
+
if (response.data.error || !response.data.similartracks?.track?.length) {
|
|
444
|
+
const retryUrl = `https://ws.audioscrobbler.com/2.0/?method=artist.getTopTracks&artist=${artist}&autocorrect=1&api_key=${apiKey}&format=json`;
|
|
445
|
+
const retryResponse = await axios_1.default.get(retryUrl);
|
|
446
|
+
if (retryResponse.data.error || !retryResponse.data.toptracks?.track?.length)
|
|
447
|
+
return false;
|
|
448
|
+
const randomTrack = retryResponse.data.toptracks.track[Math.floor(Math.random() * retryResponse.data.toptracks.track.length)];
|
|
449
|
+
const res = await player.search({ query: `${randomTrack.artist.name} - ${randomTrack.name}`, source: selectedSource }, player.get("Internal_BotUser"));
|
|
450
|
+
if (res.loadType === "empty" || res.loadType === "error")
|
|
451
|
+
return false;
|
|
452
|
+
const foundTrack = res.tracks.find((t) => t.uri !== track.uri);
|
|
453
|
+
if (!foundTrack)
|
|
454
|
+
return false;
|
|
455
|
+
player.queue.add(foundTrack);
|
|
456
|
+
player.play();
|
|
457
|
+
return true;
|
|
425
458
|
}
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
if (recommendedResult.loadType !== "playlist")
|
|
459
|
+
const randomTrack = response.data.similartracks.track[Math.floor(Math.random() * response.data.similartracks.track.length)];
|
|
460
|
+
const res = await player.search({ query: `${randomTrack.artist.name} - ${randomTrack.name}`, source: selectedSource }, player.get("Internal_BotUser"));
|
|
461
|
+
if (res.loadType === "empty" || res.loadType === "error")
|
|
430
462
|
return false;
|
|
431
|
-
const
|
|
432
|
-
|
|
433
|
-
if (!recommendedTrack)
|
|
463
|
+
const foundTrack = res.tracks.find((t) => t.uri !== track.uri);
|
|
464
|
+
if (!foundTrack)
|
|
434
465
|
return false;
|
|
435
|
-
player.queue.add(
|
|
466
|
+
player.queue.add(foundTrack);
|
|
436
467
|
player.play();
|
|
437
468
|
return true;
|
|
438
469
|
}
|
|
@@ -480,13 +511,22 @@ class Node {
|
|
|
480
511
|
player.queue.previous = player.queue.current;
|
|
481
512
|
player.queue.current = null;
|
|
482
513
|
if (!player.isAutoplay) {
|
|
483
|
-
player.queue.previous = player.queue.current;
|
|
484
|
-
player.queue.current = null;
|
|
485
514
|
player.playing = false;
|
|
486
515
|
this.manager.emit("queueEnd", player, track, payload);
|
|
487
516
|
return;
|
|
488
517
|
}
|
|
489
|
-
|
|
518
|
+
let attempts = 1;
|
|
519
|
+
let success = false;
|
|
520
|
+
while (attempts <= 3) {
|
|
521
|
+
success = await this.handleAutoplay(player, track, attempts);
|
|
522
|
+
if (success)
|
|
523
|
+
return;
|
|
524
|
+
attempts++;
|
|
525
|
+
}
|
|
526
|
+
// If all attempts fail, reset the player state and emit queueEnd
|
|
527
|
+
player.queue.previous = null;
|
|
528
|
+
player.playing = false;
|
|
529
|
+
this.manager.emit("queueEnd", player, track, payload);
|
|
490
530
|
}
|
|
491
531
|
trackStuck(player, track, payload) {
|
|
492
532
|
player.stop();
|
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
function managerCheck(options) {
|
|
4
4
|
if (!options)
|
|
5
5
|
throw new TypeError("ManagerOptions must not be empty.");
|
|
6
|
-
const { autoPlay, clientId, clientName, defaultSearchPlatform, nodes, plugins, send, trackPartial, usePriority, useNode, replaceYouTubeCredentials } = options;
|
|
6
|
+
const { autoPlay, clientId, clientName, defaultSearchPlatform, nodes, plugins, send, trackPartial, usePriority, useNode, replaceYouTubeCredentials, lastFmApiKey, } = options;
|
|
7
7
|
if (typeof autoPlay !== "undefined" && typeof autoPlay !== "boolean") {
|
|
8
8
|
throw new TypeError('Manager option "autoPlay" must be a boolean.');
|
|
9
9
|
}
|
|
@@ -49,5 +49,8 @@ function managerCheck(options) {
|
|
|
49
49
|
if (typeof replaceYouTubeCredentials !== "undefined" && typeof replaceYouTubeCredentials !== "boolean") {
|
|
50
50
|
throw new TypeError('Manager option "replaceYouTubeCredentials" must be a boolean.');
|
|
51
51
|
}
|
|
52
|
+
if (typeof lastFmApiKey !== "undefined" && (typeof lastFmApiKey !== "string" || lastFmApiKey.trim().length === 0)) {
|
|
53
|
+
throw new TypeError('Manager option "lastFmApiKey" must be a string.');
|
|
54
|
+
}
|
|
52
55
|
}
|
|
53
56
|
exports.default = managerCheck;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "magmastream",
|
|
3
|
-
"version": "2.6.0
|
|
3
|
+
"version": "2.6.0",
|
|
4
4
|
"description": "A user-friendly Lavalink client designed for NodeJS.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -15,9 +15,9 @@
|
|
|
15
15
|
},
|
|
16
16
|
"devDependencies": {
|
|
17
17
|
"@favware/rollup-type-bundler": "^3.3.0",
|
|
18
|
-
"@types/lodash": "^4.17.
|
|
19
|
-
"@types/node": "^20.
|
|
20
|
-
"@types/ws": "^8.5.
|
|
18
|
+
"@types/lodash": "^4.17.13",
|
|
19
|
+
"@types/node": "^20.17.9",
|
|
20
|
+
"@types/ws": "^8.5.13",
|
|
21
21
|
"@typescript-eslint/eslint-plugin": "^7.18.0",
|
|
22
22
|
"@typescript-eslint/parser": "^7.18.0",
|
|
23
23
|
"eslint": "^8.57.1",
|
|
@@ -28,10 +28,10 @@
|
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"@discordjs/collection": "^2.1.1",
|
|
31
|
-
"axios": "^1.7.
|
|
31
|
+
"axios": "^1.7.9",
|
|
32
32
|
"events": "^3.3.0",
|
|
33
33
|
"lodash": "^4.17.21",
|
|
34
|
-
"tslib": "^2.
|
|
34
|
+
"tslib": "^2.8.1",
|
|
35
35
|
"ws": "^8.18.0"
|
|
36
36
|
},
|
|
37
37
|
"peerDependencies": {
|