distube 4.1.1 → 4.2.1
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 +19 -29
- package/dist/index.d.ts +804 -510
- package/dist/index.js +568 -465
- package/dist/index.js.map +1 -1
- package/package.json +40 -64
package/dist/index.js
CHANGED
|
@@ -60,10 +60,10 @@ var __privateMethod = (obj, member, method) => {
|
|
|
60
60
|
|
|
61
61
|
// package.json
|
|
62
62
|
var require_package = __commonJS({
|
|
63
|
-
"package.json"(
|
|
63
|
+
"package.json"(exports2, module2) {
|
|
64
64
|
module2.exports = {
|
|
65
65
|
name: "distube",
|
|
66
|
-
version: "4.
|
|
66
|
+
version: "4.2.1",
|
|
67
67
|
description: "A Discord.js module to simplify your music commands and play songs with audio filters on Discord without any API key.",
|
|
68
68
|
main: "./dist/index.js",
|
|
69
69
|
types: "./dist/index.d.ts",
|
|
@@ -77,16 +77,16 @@ var require_package = __commonJS({
|
|
|
77
77
|
],
|
|
78
78
|
scripts: {
|
|
79
79
|
test: "jest",
|
|
80
|
-
docs: "
|
|
80
|
+
docs: "typedoc",
|
|
81
81
|
lint: "prettier --check . && eslint .",
|
|
82
82
|
"lint:fix": "eslint . --fix",
|
|
83
83
|
prettier: 'prettier --write "**/*.{ts,json,yml,yaml,md}"',
|
|
84
84
|
build: "tsup",
|
|
85
85
|
"build:check": "tsc --noEmit",
|
|
86
|
-
update: "
|
|
87
|
-
postinstall: "husky
|
|
88
|
-
prepublishOnly: "
|
|
89
|
-
prepack: "
|
|
86
|
+
update: "pnpm up -L",
|
|
87
|
+
postinstall: "husky",
|
|
88
|
+
prepublishOnly: "pnpm run lint && pnpm run test",
|
|
89
|
+
prepack: "pnpm run build && pinst --disable",
|
|
90
90
|
postpack: "pinst --enable",
|
|
91
91
|
"dev:add-docs-to-worktree": "git worktree add --track -b docs docs origin/docs"
|
|
92
92
|
},
|
|
@@ -112,70 +112,50 @@ var require_package = __commonJS({
|
|
|
112
112
|
bugs: {
|
|
113
113
|
url: "https://github.com/skick1234/DisTube/issues"
|
|
114
114
|
},
|
|
115
|
-
funding:
|
|
116
|
-
{
|
|
117
|
-
type: "individual",
|
|
118
|
-
url: "https://paypal.me/Skickkk"
|
|
119
|
-
},
|
|
120
|
-
{
|
|
121
|
-
type: "patreon",
|
|
122
|
-
url: "https://patreon.com/DisTube"
|
|
123
|
-
}
|
|
124
|
-
],
|
|
115
|
+
funding: "https://github.com/skick1234/DisTube?sponsor=1",
|
|
125
116
|
homepage: "https://distube.js.org/",
|
|
126
117
|
dependencies: {
|
|
127
|
-
"@distube/ytdl-core": "^4.13.
|
|
118
|
+
"@distube/ytdl-core": "^4.13.3",
|
|
128
119
|
"@distube/ytpl": "^1.2.1",
|
|
129
|
-
"@distube/ytsr": "^
|
|
130
|
-
"prism-media": "npm:@distube/prism-media@latest",
|
|
120
|
+
"@distube/ytsr": "^2.0.0",
|
|
131
121
|
"tiny-typed-emitter": "^2.1.0",
|
|
132
122
|
"tough-cookie": "^4.1.3",
|
|
133
123
|
tslib: "^2.6.2",
|
|
134
|
-
undici: "^
|
|
124
|
+
undici: "^6.13.0"
|
|
135
125
|
},
|
|
136
126
|
devDependencies: {
|
|
137
|
-
"@babel/core": "^7.
|
|
138
|
-
"@babel/plugin-
|
|
139
|
-
"@babel/plugin-
|
|
140
|
-
"@babel/plugin-transform-private-methods": "^7.
|
|
141
|
-
"@babel/preset-env": "^7.
|
|
142
|
-
"@babel/preset-typescript": "^7.
|
|
143
|
-
"@commitlint/cli": "^
|
|
144
|
-
"@commitlint/config-conventional": "^
|
|
145
|
-
"@discordjs/voice": "^0.16.
|
|
146
|
-
"@
|
|
147
|
-
"@types/
|
|
148
|
-
"@types/node": "^20.9.0",
|
|
127
|
+
"@babel/core": "^7.24.4",
|
|
128
|
+
"@babel/plugin-transform-class-properties": "^7.24.1",
|
|
129
|
+
"@babel/plugin-transform-object-rest-spread": "^7.24.1",
|
|
130
|
+
"@babel/plugin-transform-private-methods": "^7.24.1",
|
|
131
|
+
"@babel/preset-env": "^7.24.4",
|
|
132
|
+
"@babel/preset-typescript": "^7.24.1",
|
|
133
|
+
"@commitlint/cli": "^19.2.2",
|
|
134
|
+
"@commitlint/config-conventional": "^19.2.2",
|
|
135
|
+
"@discordjs/voice": "^0.16.1",
|
|
136
|
+
"@types/jest": "^29.5.12",
|
|
137
|
+
"@types/node": "^20.12.7",
|
|
149
138
|
"@types/tough-cookie": "^4.0.5",
|
|
150
|
-
"@typescript-eslint/eslint-plugin": "^
|
|
151
|
-
"@typescript-eslint/parser": "^
|
|
139
|
+
"@typescript-eslint/eslint-plugin": "^7.7.0",
|
|
140
|
+
"@typescript-eslint/parser": "^7.7.0",
|
|
152
141
|
"babel-jest": "^29.7.0",
|
|
153
|
-
"discord.js": "^14.
|
|
154
|
-
eslint: "^8.
|
|
155
|
-
"eslint-config-distube": "^1.
|
|
156
|
-
|
|
157
|
-
"eslint-plugin-deprecation": "^2.0.0",
|
|
158
|
-
"eslint-plugin-jsdoc": "^46.8.2",
|
|
159
|
-
husky: "^8.0.3",
|
|
142
|
+
"discord.js": "^14.14.1",
|
|
143
|
+
eslint: "^8.57.0",
|
|
144
|
+
"eslint-config-distube": "^1.7.0",
|
|
145
|
+
husky: "^9.0.11",
|
|
160
146
|
jest: "^29.7.0",
|
|
161
|
-
"jsdoc-babel": "^0.5.0",
|
|
162
147
|
"nano-staged": "^0.8.0",
|
|
163
|
-
"npm-check-updates": "^16.14.6",
|
|
164
148
|
pinst: "^3.0.0",
|
|
165
|
-
prettier: "^3.
|
|
166
|
-
tsup: "^
|
|
167
|
-
|
|
149
|
+
prettier: "^3.2.5",
|
|
150
|
+
tsup: "^8.0.2",
|
|
151
|
+
typedoc: "^0.25.13",
|
|
152
|
+
"typedoc-material-theme": "^1.0.2",
|
|
153
|
+
typescript: "^5.4.5"
|
|
168
154
|
},
|
|
169
155
|
peerDependencies: {
|
|
170
|
-
"@discordjs/opus": "*",
|
|
171
156
|
"@discordjs/voice": "*",
|
|
172
157
|
"discord.js": "14"
|
|
173
158
|
},
|
|
174
|
-
peerDependenciesMeta: {
|
|
175
|
-
"@discordjs/opus": {
|
|
176
|
-
optional: true
|
|
177
|
-
}
|
|
178
|
-
},
|
|
179
159
|
"nano-staged": {
|
|
180
160
|
"*.ts": [
|
|
181
161
|
"prettier --write",
|
|
@@ -186,9 +166,8 @@ var require_package = __commonJS({
|
|
|
186
166
|
]
|
|
187
167
|
},
|
|
188
168
|
engines: {
|
|
189
|
-
node: ">=
|
|
190
|
-
}
|
|
191
|
-
packageManager: "yarn@3.6.1"
|
|
169
|
+
node: ">=18.17"
|
|
170
|
+
}
|
|
192
171
|
};
|
|
193
172
|
}
|
|
194
173
|
});
|
|
@@ -223,6 +202,7 @@ __export(src_exports, {
|
|
|
223
202
|
Song: () => Song,
|
|
224
203
|
StreamType: () => StreamType,
|
|
225
204
|
TaskQueue: () => TaskQueue,
|
|
205
|
+
checkFFmpeg: () => checkFFmpeg,
|
|
226
206
|
checkIntents: () => checkIntents,
|
|
227
207
|
checkInvalidKey: () => checkInvalidKey,
|
|
228
208
|
chooseBestVideoFormat: () => chooseBestVideoFormat,
|
|
@@ -290,6 +270,7 @@ var Events = /* @__PURE__ */ ((Events2) => {
|
|
|
290
270
|
Events2["SEARCH_DONE"] = "searchDone";
|
|
291
271
|
Events2["SEARCH_INVALID_ANSWER"] = "searchInvalidAnswer";
|
|
292
272
|
Events2["SEARCH_RESULT"] = "searchResult";
|
|
273
|
+
Events2["FFMPEG_DEBUG"] = "ffmpegDebug";
|
|
293
274
|
return Events2;
|
|
294
275
|
})(Events || {});
|
|
295
276
|
|
|
@@ -351,6 +332,8 @@ var ERROR_MESSAGES = {
|
|
|
351
332
|
VOICE_RECONNECT_FAILED: "Cannot reconnect to the voice channel",
|
|
352
333
|
VOICE_DIFFERENT_GUILD: "Cannot join a voice channel in a different guild",
|
|
353
334
|
VOICE_DIFFERENT_CLIENT: "Cannot join a voice channel created by a different client",
|
|
335
|
+
FFMPEG_EXITED: (code) => `ffmpeg exited with code ${code}`,
|
|
336
|
+
FFMPEG_NOT_INSTALLED: (path) => `ffmpeg is not installed at '${path}' path`,
|
|
354
337
|
NO_QUEUE: "There is no playing queue in this guild",
|
|
355
338
|
QUEUE_EXIST: "This guild has a Queue already",
|
|
356
339
|
PAUSED: "The queue has been paused already",
|
|
@@ -411,15 +394,13 @@ var _TaskQueue = class _TaskQueue {
|
|
|
411
394
|
constructor() {
|
|
412
395
|
/**
|
|
413
396
|
* The task array
|
|
414
|
-
* @type {Task[]}
|
|
415
|
-
* @private
|
|
416
397
|
*/
|
|
417
398
|
__privateAdd(this, _tasks, []);
|
|
418
399
|
}
|
|
419
400
|
/**
|
|
420
401
|
* Waits for last task finished and queues a new task
|
|
421
|
-
*
|
|
422
|
-
* @
|
|
402
|
+
*
|
|
403
|
+
* @param resolveInfo - Whether the task is a resolving info task
|
|
423
404
|
*/
|
|
424
405
|
queuing(resolveInfo = false) {
|
|
425
406
|
const next = this.remaining ? __privateGet(this, _tasks)[__privateGet(this, _tasks).length - 1].promise : Promise.resolve();
|
|
@@ -434,14 +415,12 @@ var _TaskQueue = class _TaskQueue {
|
|
|
434
415
|
}
|
|
435
416
|
/**
|
|
436
417
|
* The remaining number of tasks
|
|
437
|
-
* @type {number}
|
|
438
418
|
*/
|
|
439
419
|
get remaining() {
|
|
440
420
|
return __privateGet(this, _tasks).length;
|
|
441
421
|
}
|
|
442
422
|
/**
|
|
443
423
|
* Whether or not having a resolving info task
|
|
444
|
-
* @type {boolean}
|
|
445
424
|
*/
|
|
446
425
|
get hasResolveTask() {
|
|
447
426
|
return __privateGet(this, _tasks).some((t) => t.resolveInfo);
|
|
@@ -456,11 +435,9 @@ var _metadata, _member;
|
|
|
456
435
|
var _Playlist = class _Playlist {
|
|
457
436
|
/**
|
|
458
437
|
* Create a playlist
|
|
459
|
-
*
|
|
460
|
-
* @param
|
|
461
|
-
* @param
|
|
462
|
-
* @param {Object} [options.properties] Custom properties
|
|
463
|
-
* @param {T} [options.metadata] Playlist metadata
|
|
438
|
+
*
|
|
439
|
+
* @param playlist - Playlist
|
|
440
|
+
* @param options - Optional options
|
|
464
441
|
*/
|
|
465
442
|
constructor(playlist, options = {}) {
|
|
466
443
|
__publicField(this, "source");
|
|
@@ -504,21 +481,18 @@ var _Playlist = class _Playlist {
|
|
|
504
481
|
}
|
|
505
482
|
/**
|
|
506
483
|
* Playlist duration in second.
|
|
507
|
-
* @type {number}
|
|
508
484
|
*/
|
|
509
485
|
get duration() {
|
|
510
486
|
return this.songs.reduce((prev, next) => prev + next.duration, 0);
|
|
511
487
|
}
|
|
512
488
|
/**
|
|
513
489
|
* Formatted duration string `hh:mm:ss`.
|
|
514
|
-
* @type {string}
|
|
515
490
|
*/
|
|
516
491
|
get formattedDuration() {
|
|
517
492
|
return formatDuration(this.duration);
|
|
518
493
|
}
|
|
519
494
|
/**
|
|
520
495
|
* User requested.
|
|
521
|
-
* @type {Discord.GuildMember?}
|
|
522
496
|
*/
|
|
523
497
|
get member() {
|
|
524
498
|
return __privateGet(this, _member);
|
|
@@ -531,7 +505,6 @@ var _Playlist = class _Playlist {
|
|
|
531
505
|
}
|
|
532
506
|
/**
|
|
533
507
|
* User requested.
|
|
534
|
-
* @type {Discord.User?}
|
|
535
508
|
*/
|
|
536
509
|
get user() {
|
|
537
510
|
return this.member?.user;
|
|
@@ -553,7 +526,8 @@ var Playlist = _Playlist;
|
|
|
553
526
|
var _ISearchResult = class _ISearchResult {
|
|
554
527
|
/**
|
|
555
528
|
* Create a search result
|
|
556
|
-
*
|
|
529
|
+
*
|
|
530
|
+
* @param info - ytsr result
|
|
557
531
|
*/
|
|
558
532
|
constructor(info) {
|
|
559
533
|
__publicField(this, "source");
|
|
@@ -621,11 +595,9 @@ var _metadata2, _member2, _playlist;
|
|
|
621
595
|
var _Song = class _Song {
|
|
622
596
|
/**
|
|
623
597
|
* Create a Song
|
|
624
|
-
*
|
|
625
|
-
* @param
|
|
626
|
-
* @param
|
|
627
|
-
* @param {string} [options.source="youtube"] Song source
|
|
628
|
-
* @param {T} [options.metadata] Song metadata
|
|
598
|
+
*
|
|
599
|
+
* @param info - Raw info
|
|
600
|
+
* @param options - Optional options
|
|
629
601
|
*/
|
|
630
602
|
constructor(info, options = {}) {
|
|
631
603
|
__publicField(this, "source");
|
|
@@ -702,8 +674,8 @@ var _Song = class _Song {
|
|
|
702
674
|
}
|
|
703
675
|
/**
|
|
704
676
|
* Patch data from other source
|
|
705
|
-
*
|
|
706
|
-
* @
|
|
677
|
+
*
|
|
678
|
+
* @param info - Video info
|
|
707
679
|
*/
|
|
708
680
|
_patchOther(info) {
|
|
709
681
|
this.id = info.id;
|
|
@@ -737,7 +709,6 @@ var _Song = class _Song {
|
|
|
737
709
|
}
|
|
738
710
|
/**
|
|
739
711
|
* The playlist added this song
|
|
740
|
-
* @type {Playlist?}
|
|
741
712
|
*/
|
|
742
713
|
get playlist() {
|
|
743
714
|
return __privateGet(this, _playlist);
|
|
@@ -750,7 +721,6 @@ var _Song = class _Song {
|
|
|
750
721
|
}
|
|
751
722
|
/**
|
|
752
723
|
* User requested.
|
|
753
|
-
* @type {Discord.GuildMember?}
|
|
754
724
|
*/
|
|
755
725
|
get member() {
|
|
756
726
|
return __privateGet(this, _member2);
|
|
@@ -761,7 +731,6 @@ var _Song = class _Song {
|
|
|
761
731
|
}
|
|
762
732
|
/**
|
|
763
733
|
* User requested.
|
|
764
|
-
* @type {Discord.User?}
|
|
765
734
|
*/
|
|
766
735
|
get user() {
|
|
767
736
|
return this.member?.user;
|
|
@@ -787,57 +756,48 @@ var _DisTubeBase = class _DisTubeBase {
|
|
|
787
756
|
}
|
|
788
757
|
/**
|
|
789
758
|
* Emit the {@link DisTube} of this base
|
|
790
|
-
*
|
|
791
|
-
* @param
|
|
792
|
-
* @
|
|
759
|
+
*
|
|
760
|
+
* @param eventName - Event name
|
|
761
|
+
* @param args - arguments
|
|
793
762
|
*/
|
|
794
763
|
emit(eventName, ...args) {
|
|
795
764
|
return this.distube.emit(eventName, ...args);
|
|
796
765
|
}
|
|
797
766
|
/**
|
|
798
767
|
* Emit error event
|
|
799
|
-
*
|
|
800
|
-
* @param
|
|
768
|
+
*
|
|
769
|
+
* @param error - error
|
|
770
|
+
* @param channel - Text channel where the error is encountered.
|
|
801
771
|
*/
|
|
802
772
|
emitError(error, channel) {
|
|
803
773
|
this.distube.emitError(error, channel);
|
|
804
774
|
}
|
|
805
775
|
/**
|
|
806
776
|
* The queue manager
|
|
807
|
-
* @type {QueueManager}
|
|
808
|
-
* @readonly
|
|
809
777
|
*/
|
|
810
778
|
get queues() {
|
|
811
779
|
return this.distube.queues;
|
|
812
780
|
}
|
|
813
781
|
/**
|
|
814
782
|
* The voice manager
|
|
815
|
-
* @type {DisTubeVoiceManager}
|
|
816
|
-
* @readonly
|
|
817
783
|
*/
|
|
818
784
|
get voices() {
|
|
819
785
|
return this.distube.voices;
|
|
820
786
|
}
|
|
821
787
|
/**
|
|
822
788
|
* Discord.js client
|
|
823
|
-
* @type {Discord.Client}
|
|
824
|
-
* @readonly
|
|
825
789
|
*/
|
|
826
790
|
get client() {
|
|
827
791
|
return this.distube.client;
|
|
828
792
|
}
|
|
829
793
|
/**
|
|
830
794
|
* DisTube options
|
|
831
|
-
* @type {DisTubeOptions}
|
|
832
|
-
* @readonly
|
|
833
795
|
*/
|
|
834
796
|
get options() {
|
|
835
797
|
return this.distube.options;
|
|
836
798
|
}
|
|
837
799
|
/**
|
|
838
800
|
* DisTube handler
|
|
839
|
-
* @type {DisTubeHandler}
|
|
840
|
-
* @readonly
|
|
841
801
|
*/
|
|
842
802
|
get handler() {
|
|
843
803
|
return this.distube.handler;
|
|
@@ -863,6 +823,7 @@ var _DisTubeVoice = class _DisTubeVoice extends import_tiny_typed_emitter.TypedE
|
|
|
863
823
|
__publicField(this, "audioResource");
|
|
864
824
|
__publicField(this, "emittedError");
|
|
865
825
|
__publicField(this, "isDisconnected", false);
|
|
826
|
+
__publicField(this, "stream");
|
|
866
827
|
__privateAdd(this, _channel, void 0);
|
|
867
828
|
__privateAdd(this, _volume, 100);
|
|
868
829
|
this.voices = voiceManager;
|
|
@@ -906,7 +867,6 @@ var _DisTubeVoice = class _DisTubeVoice extends import_tiny_typed_emitter.TypedE
|
|
|
906
867
|
}
|
|
907
868
|
/**
|
|
908
869
|
* The voice channel id the bot is in
|
|
909
|
-
* @type {Snowflake?}
|
|
910
870
|
*/
|
|
911
871
|
get channelId() {
|
|
912
872
|
return this.connection?.joinConfig?.channelId ?? void 0;
|
|
@@ -949,8 +909,8 @@ var _DisTubeVoice = class _DisTubeVoice extends import_tiny_typed_emitter.TypedE
|
|
|
949
909
|
}
|
|
950
910
|
/**
|
|
951
911
|
* Join a voice channel with this connection
|
|
952
|
-
*
|
|
953
|
-
* @
|
|
912
|
+
*
|
|
913
|
+
* @param channel - A voice channel
|
|
954
914
|
*/
|
|
955
915
|
async join(channel) {
|
|
956
916
|
const TIMEOUT = 3e4;
|
|
@@ -970,7 +930,8 @@ var _DisTubeVoice = class _DisTubeVoice extends import_tiny_typed_emitter.TypedE
|
|
|
970
930
|
}
|
|
971
931
|
/**
|
|
972
932
|
* Leave the voice channel of this connection
|
|
973
|
-
*
|
|
933
|
+
*
|
|
934
|
+
* @param error - Optional, an error to emit with 'error' event.
|
|
974
935
|
*/
|
|
975
936
|
leave(error) {
|
|
976
937
|
this.stop(true);
|
|
@@ -984,33 +945,36 @@ var _DisTubeVoice = class _DisTubeVoice extends import_tiny_typed_emitter.TypedE
|
|
|
984
945
|
}
|
|
985
946
|
/**
|
|
986
947
|
* Stop the playing stream
|
|
987
|
-
*
|
|
988
|
-
*
|
|
989
|
-
* @
|
|
948
|
+
*
|
|
949
|
+
* @param force - If true, will force the {@link DisTubeVoice#audioPlayer} to enter the Idle state even
|
|
950
|
+
* if the {@link DisTubeVoice#audioResource} has silence padding frames.
|
|
990
951
|
*/
|
|
991
952
|
stop(force = false) {
|
|
992
953
|
this.audioPlayer.stop(force);
|
|
954
|
+
this.stream?.kill?.();
|
|
993
955
|
}
|
|
994
956
|
/**
|
|
995
|
-
* Play a
|
|
996
|
-
*
|
|
997
|
-
* @param
|
|
957
|
+
* Play a {@link DisTubeStream}
|
|
958
|
+
*
|
|
959
|
+
* @param dtStream - DisTubeStream
|
|
998
960
|
*/
|
|
999
|
-
play(
|
|
961
|
+
play(dtStream) {
|
|
1000
962
|
this.emittedError = false;
|
|
1001
|
-
|
|
963
|
+
dtStream.on("error", (error) => {
|
|
1002
964
|
if (this.emittedError || error.code === "ERR_STREAM_PREMATURE_CLOSE")
|
|
1003
965
|
return;
|
|
1004
966
|
this.emittedError = true;
|
|
1005
967
|
this.emit("error", error);
|
|
1006
968
|
});
|
|
1007
|
-
this.audioResource = (0, import_voice.createAudioResource)(
|
|
1008
|
-
inputType:
|
|
969
|
+
this.audioResource = (0, import_voice.createAudioResource)(dtStream.stream, {
|
|
970
|
+
inputType: dtStream.type,
|
|
1009
971
|
inlineVolume: true
|
|
1010
972
|
});
|
|
1011
973
|
this.volume = __privateGet(this, _volume);
|
|
1012
974
|
if (this.audioPlayer.state.status !== import_voice.AudioPlayerStatus.Paused)
|
|
1013
975
|
this.audioPlayer.play(this.audioResource);
|
|
976
|
+
this.stream?.kill?.();
|
|
977
|
+
this.stream = dtStream;
|
|
1014
978
|
}
|
|
1015
979
|
set volume(volume) {
|
|
1016
980
|
if (typeof volume !== "number" || isNaN(volume)) {
|
|
@@ -1027,7 +991,6 @@ var _DisTubeVoice = class _DisTubeVoice extends import_tiny_typed_emitter.TypedE
|
|
|
1027
991
|
}
|
|
1028
992
|
/**
|
|
1029
993
|
* Playback duration of the audio resource in seconds
|
|
1030
|
-
* @type {number}
|
|
1031
994
|
*/
|
|
1032
995
|
get playbackDuration() {
|
|
1033
996
|
return (this.audioResource?.playbackDuration ?? 0) / 1e3;
|
|
@@ -1046,22 +1009,22 @@ var _DisTubeVoice = class _DisTubeVoice extends import_tiny_typed_emitter.TypedE
|
|
|
1046
1009
|
}
|
|
1047
1010
|
/**
|
|
1048
1011
|
* Whether the bot is self-deafened
|
|
1049
|
-
* @type {boolean}
|
|
1050
1012
|
*/
|
|
1051
1013
|
get selfDeaf() {
|
|
1052
1014
|
return this.connection.joinConfig.selfDeaf;
|
|
1053
1015
|
}
|
|
1054
1016
|
/**
|
|
1055
1017
|
* Whether the bot is self-muted
|
|
1056
|
-
* @type {boolean}
|
|
1057
1018
|
*/
|
|
1058
1019
|
get selfMute() {
|
|
1059
1020
|
return this.connection.joinConfig.selfMute;
|
|
1060
1021
|
}
|
|
1061
1022
|
/**
|
|
1062
1023
|
* Self-deafens/undeafens the bot.
|
|
1063
|
-
*
|
|
1064
|
-
* @
|
|
1024
|
+
*
|
|
1025
|
+
* @param selfDeaf - Whether or not the bot should be self-deafened
|
|
1026
|
+
*
|
|
1027
|
+
* @returns true if the voice state was successfully updated, otherwise false
|
|
1065
1028
|
*/
|
|
1066
1029
|
setSelfDeaf(selfDeaf) {
|
|
1067
1030
|
if (typeof selfDeaf !== "boolean") {
|
|
@@ -1074,8 +1037,10 @@ var _DisTubeVoice = class _DisTubeVoice extends import_tiny_typed_emitter.TypedE
|
|
|
1074
1037
|
}
|
|
1075
1038
|
/**
|
|
1076
1039
|
* Self-mutes/unmutes the bot.
|
|
1077
|
-
*
|
|
1078
|
-
* @
|
|
1040
|
+
*
|
|
1041
|
+
* @param selfMute - Whether or not the bot should be self-muted
|
|
1042
|
+
*
|
|
1043
|
+
* @returns true if the voice state was successfully updated, otherwise false
|
|
1079
1044
|
*/
|
|
1080
1045
|
setSelfMute(selfMute) {
|
|
1081
1046
|
if (typeof selfMute !== "boolean") {
|
|
@@ -1088,7 +1053,6 @@ var _DisTubeVoice = class _DisTubeVoice extends import_tiny_typed_emitter.TypedE
|
|
|
1088
1053
|
}
|
|
1089
1054
|
/**
|
|
1090
1055
|
* The voice state of this connection
|
|
1091
|
-
* @type {Discord.VoiceState?}
|
|
1092
1056
|
*/
|
|
1093
1057
|
get voiceState() {
|
|
1094
1058
|
return this.channel?.guild?.members?.me?.voice;
|
|
@@ -1114,60 +1078,134 @@ __name(_DisTubeVoice, "DisTubeVoice");
|
|
|
1114
1078
|
var DisTubeVoice = _DisTubeVoice;
|
|
1115
1079
|
|
|
1116
1080
|
// src/core/DisTubeStream.ts
|
|
1117
|
-
var
|
|
1081
|
+
var import_node_stream = require("stream");
|
|
1082
|
+
var import_child_process = require("child_process");
|
|
1083
|
+
var import_tiny_typed_emitter2 = require("tiny-typed-emitter");
|
|
1118
1084
|
var import_voice2 = require("@discordjs/voice");
|
|
1119
1085
|
var chooseBestVideoFormat = /* @__PURE__ */ __name(({ duration, formats, isLive }) => formats && formats.filter((f) => f.hasAudio && (duration < 10 * 60 || f.hasVideo) && (!isLive || f.isHLS)).sort((a, b) => Number(b.audioBitrate) - Number(a.audioBitrate) || Number(a.bitrate) - Number(b.bitrate))[0], "chooseBestVideoFormat");
|
|
1120
|
-
var
|
|
1086
|
+
var checked = process.env.NODE_ENV === "test";
|
|
1087
|
+
var checkFFmpeg = /* @__PURE__ */ __name((distube) => {
|
|
1088
|
+
if (checked)
|
|
1089
|
+
return;
|
|
1090
|
+
const path = distube.options.ffmpeg.path;
|
|
1091
|
+
const debug = /* @__PURE__ */ __name((str) => distube.emit("ffmpegDebug" /* FFMPEG_DEBUG */, str), "debug");
|
|
1092
|
+
try {
|
|
1093
|
+
debug(`[test] spawn ffmpeg at '${path}' path`);
|
|
1094
|
+
const process2 = (0, import_child_process.spawnSync)(path, ["-h"], { windowsHide: true, shell: true, encoding: "utf-8" });
|
|
1095
|
+
if (process2.error)
|
|
1096
|
+
throw process2.error;
|
|
1097
|
+
if (process2.stderr && !process2.stdout)
|
|
1098
|
+
throw new Error(process2.stderr);
|
|
1099
|
+
const result = process2.output.join("\n");
|
|
1100
|
+
const version2 = /ffmpeg version (\S+)/iu.exec(result)?.[1];
|
|
1101
|
+
if (!version2)
|
|
1102
|
+
throw new Error("Invalid FFmpeg version");
|
|
1103
|
+
debug(`[test] ffmpeg version: ${version2}`);
|
|
1104
|
+
if (result.includes("--enable-libopus")) {
|
|
1105
|
+
debug("[test] ffmpeg supports libopus");
|
|
1106
|
+
} else {
|
|
1107
|
+
debug("[test] ffmpeg does not support libopus");
|
|
1108
|
+
distube.options.streamType = 1 /* RAW */;
|
|
1109
|
+
}
|
|
1110
|
+
} catch (e) {
|
|
1111
|
+
debug(`[test] failed to spawn ffmpeg at '${path}': ${e?.stack ?? e}`);
|
|
1112
|
+
throw new DisTubeError("FFMPEG_NOT_INSTALLED", path);
|
|
1113
|
+
}
|
|
1114
|
+
checked = true;
|
|
1115
|
+
}, "checkFFmpeg");
|
|
1116
|
+
var _DisTubeStream = class _DisTubeStream extends import_tiny_typed_emitter2.TypedEmitter {
|
|
1121
1117
|
/**
|
|
1122
1118
|
* Create a DisTubeStream to play with {@link DisTubeVoice}
|
|
1123
|
-
*
|
|
1124
|
-
* @param
|
|
1125
|
-
* @
|
|
1119
|
+
*
|
|
1120
|
+
* @param url - Stream URL
|
|
1121
|
+
* @param options - Stream options
|
|
1126
1122
|
*/
|
|
1127
|
-
constructor(url,
|
|
1128
|
-
|
|
1123
|
+
constructor(url, { ffmpeg, seek, type }) {
|
|
1124
|
+
super();
|
|
1125
|
+
__publicField(this, "killed", false);
|
|
1126
|
+
__publicField(this, "process");
|
|
1129
1127
|
__publicField(this, "stream");
|
|
1128
|
+
__publicField(this, "type");
|
|
1130
1129
|
__publicField(this, "url");
|
|
1131
1130
|
this.url = url;
|
|
1132
|
-
this.type = !
|
|
1133
|
-
const
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
url,
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
"
|
|
1148
|
-
"
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
if (
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1131
|
+
this.type = !type ? import_voice2.StreamType.OggOpus : import_voice2.StreamType.Raw;
|
|
1132
|
+
const opts = {
|
|
1133
|
+
reconnect: 1,
|
|
1134
|
+
reconnect_streamed: 1,
|
|
1135
|
+
reconnect_delay_max: 5,
|
|
1136
|
+
analyzeduration: 0,
|
|
1137
|
+
hide_banner: true,
|
|
1138
|
+
...ffmpeg.args.global,
|
|
1139
|
+
...ffmpeg.args.input,
|
|
1140
|
+
i: url,
|
|
1141
|
+
ar: 48e3,
|
|
1142
|
+
ac: 2,
|
|
1143
|
+
...ffmpeg.args.output
|
|
1144
|
+
};
|
|
1145
|
+
if (!type) {
|
|
1146
|
+
opts.f = "opus";
|
|
1147
|
+
opts.acodec = "libopus";
|
|
1148
|
+
} else {
|
|
1149
|
+
opts.f = "s16le";
|
|
1150
|
+
}
|
|
1151
|
+
if (typeof seek === "number" && seek > 0)
|
|
1152
|
+
opts.ss = seek.toString();
|
|
1153
|
+
if (typeof ffmpeg.args === "object")
|
|
1154
|
+
Object.assign(opts, ffmpeg.args);
|
|
1155
|
+
this.process = (0, import_child_process.spawn)(
|
|
1156
|
+
ffmpeg.path,
|
|
1157
|
+
[
|
|
1158
|
+
...Object.entries(opts).flatMap(
|
|
1159
|
+
([key, value]) => Array.isArray(value) ? value.filter(Boolean).map((v) => [`-${key}`, String(v)]) : value == null || value === false ? [] : [value === true ? `-${key}` : [`-${key}`, String(value)]]
|
|
1160
|
+
).flat(),
|
|
1161
|
+
"pipe:1"
|
|
1162
|
+
],
|
|
1163
|
+
{ stdio: ["ignore", "pipe", "pipe"], shell: false, windowsHide: true }
|
|
1164
|
+
).on("error", (err) => {
|
|
1165
|
+
this.debug(`[process] error: ${err.message}`);
|
|
1166
|
+
this.emit("error", err);
|
|
1167
|
+
}).on("exit", (code, signal) => {
|
|
1168
|
+
this.debug(`[process] exit: code=${code ?? "unknown"} signal=${signal ?? "unknown"}`);
|
|
1169
|
+
if (!code || [0, 255].includes(code))
|
|
1170
|
+
return;
|
|
1171
|
+
this.debug(`[process] error: ffmpeg exited with code ${code}`);
|
|
1172
|
+
this.emit("error", new DisTubeError("FFMPEG_EXITED", code));
|
|
1173
|
+
});
|
|
1174
|
+
if (!this.process.stdout || !this.process.stderr) {
|
|
1175
|
+
this.kill();
|
|
1176
|
+
throw new Error("Failed to create ffmpeg process");
|
|
1177
|
+
}
|
|
1178
|
+
this.stream = new import_node_stream.PassThrough();
|
|
1179
|
+
this.stream.on("close", () => this.kill()).on("error", (err) => {
|
|
1180
|
+
this.debug(`[stream] error: ${err.message}`);
|
|
1181
|
+
this.emit("error", err);
|
|
1182
|
+
}).on("finish", () => this.debug("[stream] log: stream finished"));
|
|
1183
|
+
this.process.stdout.pipe(this.stream);
|
|
1184
|
+
this.process.stderr.setEncoding("utf8")?.on("data", (data) => {
|
|
1185
|
+
const lines = data.split(/\r\n|\r|\n/u);
|
|
1186
|
+
for (const line of lines) {
|
|
1187
|
+
if (/^\s*$/.test(line))
|
|
1188
|
+
continue;
|
|
1189
|
+
this.debug(`[ffmpeg] log: ${line}`);
|
|
1190
|
+
}
|
|
1191
|
+
});
|
|
1192
|
+
}
|
|
1193
|
+
debug(debug) {
|
|
1194
|
+
this.emit("debug", debug);
|
|
1195
|
+
}
|
|
1196
|
+
kill() {
|
|
1197
|
+
if (this.killed)
|
|
1198
|
+
return;
|
|
1199
|
+
this.process.kill("SIGKILL");
|
|
1200
|
+
this.killed = true;
|
|
1201
|
+
}
|
|
1202
|
+
/**
|
|
1203
|
+
* Create a stream from a YouTube {@link Song}
|
|
1204
|
+
*
|
|
1205
|
+
* @param song - A YouTube Song
|
|
1206
|
+
* @param options - options
|
|
1207
|
+
*/
|
|
1208
|
+
static YouTube(song, options) {
|
|
1171
1209
|
if (song.source !== "youtube")
|
|
1172
1210
|
throw new DisTubeError("INVALID_TYPE", "youtube", song.source, "Song#source");
|
|
1173
1211
|
if (!song.formats?.length)
|
|
@@ -1182,18 +1220,17 @@ var _DisTubeStream = class _DisTubeStream {
|
|
|
1182
1220
|
}
|
|
1183
1221
|
/**
|
|
1184
1222
|
* Create a stream from a stream url
|
|
1185
|
-
*
|
|
1186
|
-
* @param
|
|
1187
|
-
* @
|
|
1188
|
-
* @private
|
|
1223
|
+
*
|
|
1224
|
+
* @param url - stream url
|
|
1225
|
+
* @param options - options
|
|
1189
1226
|
*/
|
|
1190
|
-
static DirectLink(url, options
|
|
1191
|
-
if (!options || typeof options !== "object" || Array.isArray(options)) {
|
|
1192
|
-
throw new DisTubeError("INVALID_TYPE", "object", options, "options");
|
|
1193
|
-
}
|
|
1227
|
+
static DirectLink(url, options) {
|
|
1194
1228
|
if (typeof url !== "string" || !isURL(url)) {
|
|
1195
1229
|
throw new DisTubeError("INVALID_TYPE", "an URL", url);
|
|
1196
1230
|
}
|
|
1231
|
+
if (!options || typeof options !== "object" || Array.isArray(options)) {
|
|
1232
|
+
throw new DisTubeError("INVALID_TYPE", "object", options, "options");
|
|
1233
|
+
}
|
|
1197
1234
|
return new _DisTubeStream(url, options);
|
|
1198
1235
|
}
|
|
1199
1236
|
};
|
|
@@ -1233,7 +1270,7 @@ var _DisTubeHandler = class _DisTubeHandler extends DisTubeBase {
|
|
|
1233
1270
|
delete queue._emptyTimeout;
|
|
1234
1271
|
if (isVoiceChannelEmpty(oldState)) {
|
|
1235
1272
|
queue.voice.leave();
|
|
1236
|
-
this.emit("empty"
|
|
1273
|
+
this.emit("empty" /* EMPTY */, queue);
|
|
1237
1274
|
if (queue.stopped)
|
|
1238
1275
|
queue.remove();
|
|
1239
1276
|
}
|
|
@@ -1248,7 +1285,7 @@ var _DisTubeHandler = class _DisTubeHandler extends DisTubeBase {
|
|
|
1248
1285
|
const cookies = __privateSet(this, _cookie, this.options.youtubeCookie);
|
|
1249
1286
|
if (typeof cookies === "string") {
|
|
1250
1287
|
console.warn(
|
|
1251
|
-
"\x1B[33mWARNING:\x1B[0m You are using the old YouTube cookie format, please use the new one instead. (https://
|
|
1288
|
+
"\x1B[33mWARNING:\x1B[0m You are using the old YouTube cookie format, please use the new one instead. (https://github.com/skick1234/DisTube/wiki/YouTube-Cookies)"
|
|
1252
1289
|
);
|
|
1253
1290
|
options.agent = import_ytdl_core.default.createAgent(
|
|
1254
1291
|
cookies.split(";").map((c) => import_tough_cookie.Cookie.parse(c)).filter(isTruthy)
|
|
@@ -1267,9 +1304,8 @@ var _DisTubeHandler = class _DisTubeHandler extends DisTubeBase {
|
|
|
1267
1304
|
return jar.getCookieStringSync("https://www.youtube.com");
|
|
1268
1305
|
}
|
|
1269
1306
|
/**
|
|
1270
|
-
* @param
|
|
1271
|
-
* @param
|
|
1272
|
-
* @returns {Promise<ytdl.videoInfo>}
|
|
1307
|
+
* @param url - url
|
|
1308
|
+
* @param basic - getBasicInfo?
|
|
1273
1309
|
*/
|
|
1274
1310
|
getYouTubeInfo(url, basic = false) {
|
|
1275
1311
|
if (basic)
|
|
@@ -1278,10 +1314,13 @@ var _DisTubeHandler = class _DisTubeHandler extends DisTubeBase {
|
|
|
1278
1314
|
}
|
|
1279
1315
|
/**
|
|
1280
1316
|
* Resolve a url or a supported object to a {@link Song} or {@link Playlist}
|
|
1281
|
-
*
|
|
1282
|
-
* @
|
|
1283
|
-
*
|
|
1284
|
-
* @
|
|
1317
|
+
*
|
|
1318
|
+
* @throws {@link DisTubeError}
|
|
1319
|
+
*
|
|
1320
|
+
* @param song - URL | {@link Song}| {@link SearchResult} | {@link Playlist}
|
|
1321
|
+
* @param options - Optional options
|
|
1322
|
+
*
|
|
1323
|
+
* @returns Resolved
|
|
1285
1324
|
*/
|
|
1286
1325
|
async resolve(song, options = {}) {
|
|
1287
1326
|
if (song instanceof Song || song instanceof Playlist) {
|
|
@@ -1315,9 +1354,9 @@ var _DisTubeHandler = class _DisTubeHandler extends DisTubeBase {
|
|
|
1315
1354
|
}
|
|
1316
1355
|
/**
|
|
1317
1356
|
* Resolve Song[] or YouTube playlist url to a Playlist
|
|
1318
|
-
*
|
|
1319
|
-
* @param
|
|
1320
|
-
* @
|
|
1357
|
+
*
|
|
1358
|
+
* @param playlist - Resolvable playlist
|
|
1359
|
+
* @param options - Optional options
|
|
1321
1360
|
*/
|
|
1322
1361
|
async resolvePlaylist(playlist, options = {}) {
|
|
1323
1362
|
const { member, source, metadata } = { source: "youtube", ...options };
|
|
@@ -1346,11 +1385,14 @@ var _DisTubeHandler = class _DisTubeHandler extends DisTubeBase {
|
|
|
1346
1385
|
return new Playlist(playlist, { member, properties: { source }, metadata });
|
|
1347
1386
|
}
|
|
1348
1387
|
/**
|
|
1349
|
-
* Search for a song, fire {@link DisTube#
|
|
1350
|
-
*
|
|
1351
|
-
* @
|
|
1352
|
-
*
|
|
1353
|
-
* @
|
|
1388
|
+
* Search for a song, fire {@link DisTube#error} if not found.
|
|
1389
|
+
*
|
|
1390
|
+
* @throws {@link DisTubeError}
|
|
1391
|
+
*
|
|
1392
|
+
* @param message - The original message from an user
|
|
1393
|
+
* @param query - The query string
|
|
1394
|
+
*
|
|
1395
|
+
* @returns Song info
|
|
1354
1396
|
*/
|
|
1355
1397
|
async searchSong(message, query) {
|
|
1356
1398
|
if (!isMessageInstance(message))
|
|
@@ -1364,7 +1406,7 @@ var _DisTubeHandler = class _DisTubeHandler extends DisTubeBase {
|
|
|
1364
1406
|
limit,
|
|
1365
1407
|
safeSearch: this.options.nsfw ? false : !isNsfwChannel(message.channel)
|
|
1366
1408
|
}).catch(() => {
|
|
1367
|
-
if (!this.emit("searchNoResult"
|
|
1409
|
+
if (!this.emit("searchNoResult" /* SEARCH_NO_RESULT */, message, query)) {
|
|
1368
1410
|
console.warn("searchNoResult event does not have any listeners! Emits `error` event instead.");
|
|
1369
1411
|
throw new DisTubeError("NO_RESULT");
|
|
1370
1412
|
}
|
|
@@ -1376,13 +1418,16 @@ var _DisTubeHandler = class _DisTubeHandler extends DisTubeBase {
|
|
|
1376
1418
|
/**
|
|
1377
1419
|
* Create a message collector for selecting search results.
|
|
1378
1420
|
*
|
|
1379
|
-
* Needed events: {@link DisTube#
|
|
1380
|
-
* {@link DisTube#
|
|
1381
|
-
*
|
|
1382
|
-
* @
|
|
1383
|
-
*
|
|
1384
|
-
* @
|
|
1385
|
-
* @
|
|
1421
|
+
* Needed events: {@link DisTube#searchResult}, {@link DisTube#searchCancel},
|
|
1422
|
+
* {@link DisTube#searchInvalidAnswer}, {@link DisTube#searchDone}.
|
|
1423
|
+
*
|
|
1424
|
+
* @throws {@link DisTubeError}
|
|
1425
|
+
*
|
|
1426
|
+
* @param message - The original message from an user
|
|
1427
|
+
* @param results - The search results
|
|
1428
|
+
* @param query - The query string
|
|
1429
|
+
*
|
|
1430
|
+
* @returns Selected result
|
|
1386
1431
|
*/
|
|
1387
1432
|
async createSearchMessageCollector(message, results, query) {
|
|
1388
1433
|
if (!isMessageInstance(message))
|
|
@@ -1392,11 +1437,11 @@ var _DisTubeHandler = class _DisTubeHandler extends DisTubeBase {
|
|
|
1392
1437
|
}
|
|
1393
1438
|
if (this.options.searchSongs > 1) {
|
|
1394
1439
|
const searchEvents = [
|
|
1395
|
-
"searchNoResult"
|
|
1396
|
-
"searchResult"
|
|
1397
|
-
"searchCancel"
|
|
1398
|
-
"searchInvalidAnswer"
|
|
1399
|
-
"searchDone"
|
|
1440
|
+
"searchNoResult" /* SEARCH_NO_RESULT */,
|
|
1441
|
+
"searchResult" /* SEARCH_RESULT */,
|
|
1442
|
+
"searchCancel" /* SEARCH_CANCEL */,
|
|
1443
|
+
"searchInvalidAnswer" /* SEARCH_INVALID_ANSWER */,
|
|
1444
|
+
"searchDone" /* SEARCH_DONE */
|
|
1400
1445
|
];
|
|
1401
1446
|
for (const evn of searchEvents) {
|
|
1402
1447
|
if (this.distube.listenerCount(evn) === 0) {
|
|
@@ -1413,7 +1458,7 @@ var _DisTubeHandler = class _DisTubeHandler extends DisTubeBase {
|
|
|
1413
1458
|
let result = results[0];
|
|
1414
1459
|
if (limit > 1) {
|
|
1415
1460
|
results.splice(limit);
|
|
1416
|
-
this.emit("searchResult"
|
|
1461
|
+
this.emit("searchResult" /* SEARCH_RESULT */, message, results, query);
|
|
1417
1462
|
const answers = await message.channel.awaitMessages({
|
|
1418
1463
|
filter: (m) => m.author.id === message.author.id,
|
|
1419
1464
|
max: 1,
|
|
@@ -1422,26 +1467,27 @@ var _DisTubeHandler = class _DisTubeHandler extends DisTubeBase {
|
|
|
1422
1467
|
}).catch(() => void 0);
|
|
1423
1468
|
const ans = answers?.first();
|
|
1424
1469
|
if (!ans) {
|
|
1425
|
-
this.emit("searchCancel"
|
|
1470
|
+
this.emit("searchCancel" /* SEARCH_CANCEL */, message, query);
|
|
1426
1471
|
return null;
|
|
1427
1472
|
}
|
|
1428
1473
|
const index = parseInt(ans.content, 10);
|
|
1429
1474
|
if (isNaN(index) || index > results.length || index < 1) {
|
|
1430
|
-
this.emit("searchInvalidAnswer"
|
|
1475
|
+
this.emit("searchInvalidAnswer" /* SEARCH_INVALID_ANSWER */, message, ans, query);
|
|
1431
1476
|
return null;
|
|
1432
1477
|
}
|
|
1433
|
-
this.emit("searchDone"
|
|
1478
|
+
this.emit("searchDone" /* SEARCH_DONE */, message, ans, query);
|
|
1434
1479
|
result = results[index - 1];
|
|
1435
1480
|
}
|
|
1436
1481
|
return result;
|
|
1437
1482
|
}
|
|
1438
1483
|
/**
|
|
1439
1484
|
* Play or add a {@link Playlist} to the queue.
|
|
1440
|
-
*
|
|
1441
|
-
* @
|
|
1442
|
-
*
|
|
1443
|
-
* @
|
|
1444
|
-
* @
|
|
1485
|
+
*
|
|
1486
|
+
* @throws {@link DisTubeError}
|
|
1487
|
+
*
|
|
1488
|
+
* @param voiceChannel - A voice channel
|
|
1489
|
+
* @param playlist - A YouTube playlist url | a Playlist
|
|
1490
|
+
* @param options - Optional options
|
|
1445
1491
|
*/
|
|
1446
1492
|
async playPlaylist(voiceChannel, playlist, options = {}) {
|
|
1447
1493
|
const { textChannel, skip } = { skip: false, ...options };
|
|
@@ -1464,23 +1510,24 @@ var _DisTubeHandler = class _DisTubeHandler extends DisTubeBase {
|
|
|
1464
1510
|
if (skip)
|
|
1465
1511
|
queue.skip();
|
|
1466
1512
|
else
|
|
1467
|
-
this.emit("addList"
|
|
1513
|
+
this.emit("addList" /* ADD_LIST */, queue, playlist);
|
|
1468
1514
|
} else {
|
|
1469
1515
|
const newQueue = await this.queues.create(voiceChannel, playlist.songs, textChannel);
|
|
1470
1516
|
if (newQueue instanceof Queue) {
|
|
1471
1517
|
if (this.options.emitAddListWhenCreatingQueue)
|
|
1472
|
-
this.emit("addList"
|
|
1473
|
-
this.emit("playSong"
|
|
1518
|
+
this.emit("addList" /* ADD_LIST */, newQueue, playlist);
|
|
1519
|
+
this.emit("playSong" /* PLAY_SONG */, newQueue, newQueue.songs[0]);
|
|
1474
1520
|
}
|
|
1475
1521
|
}
|
|
1476
1522
|
}
|
|
1477
1523
|
/**
|
|
1478
1524
|
* Play or add a {@link Song} to the queue.
|
|
1479
|
-
*
|
|
1480
|
-
* @
|
|
1481
|
-
*
|
|
1482
|
-
* @
|
|
1483
|
-
* @
|
|
1525
|
+
*
|
|
1526
|
+
* @throws {@link DisTubeError}
|
|
1527
|
+
*
|
|
1528
|
+
* @param voiceChannel - A voice channel
|
|
1529
|
+
* @param song - A YouTube playlist url | a Playlist
|
|
1530
|
+
* @param options - Optional options
|
|
1484
1531
|
*/
|
|
1485
1532
|
async playSong(voiceChannel, song, options = {}) {
|
|
1486
1533
|
if (!(song instanceof Song))
|
|
@@ -1498,27 +1545,26 @@ var _DisTubeHandler = class _DisTubeHandler extends DisTubeBase {
|
|
|
1498
1545
|
if (skip)
|
|
1499
1546
|
queue.skip();
|
|
1500
1547
|
else
|
|
1501
|
-
this.emit("addSong"
|
|
1548
|
+
this.emit("addSong" /* ADD_SONG */, queue, song);
|
|
1502
1549
|
} else {
|
|
1503
1550
|
const newQueue = await this.queues.create(voiceChannel, song, textChannel);
|
|
1504
1551
|
if (newQueue instanceof Queue) {
|
|
1505
1552
|
if (this.options.emitAddSongWhenCreatingQueue)
|
|
1506
|
-
this.emit("addSong"
|
|
1507
|
-
this.emit("playSong"
|
|
1553
|
+
this.emit("addSong" /* ADD_SONG */, newQueue, song);
|
|
1554
|
+
this.emit("playSong" /* PLAY_SONG */, newQueue, song);
|
|
1508
1555
|
}
|
|
1509
1556
|
}
|
|
1510
1557
|
}
|
|
1511
1558
|
/**
|
|
1512
1559
|
* Get {@link Song}'s stream info and attach it to the song.
|
|
1513
|
-
*
|
|
1560
|
+
*
|
|
1561
|
+
* @param song - A Song
|
|
1514
1562
|
*/
|
|
1515
1563
|
async attachStreamInfo(song) {
|
|
1516
|
-
const { url, source
|
|
1564
|
+
const { url, source } = song;
|
|
1517
1565
|
if (source === "youtube") {
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
}
|
|
1521
|
-
} else if (!streamURL) {
|
|
1566
|
+
song._patchYouTube(await this.handler.getYouTubeInfo(url));
|
|
1567
|
+
} else {
|
|
1522
1568
|
for (const plugin of [...this.distube.extractorPlugins, ...this.distube.customPlugins]) {
|
|
1523
1569
|
if (await plugin.validate(url)) {
|
|
1524
1570
|
const info = [plugin.getStreamURL(url), plugin.getRelatedSongs(url)];
|
|
@@ -1536,10 +1582,11 @@ __name(_DisTubeHandler, "DisTubeHandler");
|
|
|
1536
1582
|
var DisTubeHandler = _DisTubeHandler;
|
|
1537
1583
|
|
|
1538
1584
|
// src/core/DisTubeOptions.ts
|
|
1539
|
-
var _validateOptions, validateOptions_fn;
|
|
1585
|
+
var _validateOptions, validateOptions_fn, _ffmpegOption, ffmpegOption_fn;
|
|
1540
1586
|
var _Options = class _Options {
|
|
1541
1587
|
constructor(options) {
|
|
1542
1588
|
__privateAdd(this, _validateOptions);
|
|
1589
|
+
__privateAdd(this, _ffmpegOption);
|
|
1543
1590
|
__publicField(this, "plugins");
|
|
1544
1591
|
__publicField(this, "emitNewSongOnly");
|
|
1545
1592
|
__publicField(this, "leaveOnFinish");
|
|
@@ -1558,6 +1605,11 @@ var _Options = class _Options {
|
|
|
1558
1605
|
__publicField(this, "joinNewVoiceChannel");
|
|
1559
1606
|
__publicField(this, "streamType");
|
|
1560
1607
|
__publicField(this, "directLink");
|
|
1608
|
+
/** @deprecated */
|
|
1609
|
+
__publicField(this, "ffmpegPath");
|
|
1610
|
+
/** @deprecated */
|
|
1611
|
+
__publicField(this, "ffmpegDefaultArgs");
|
|
1612
|
+
__publicField(this, "ffmpeg");
|
|
1561
1613
|
if (typeof options !== "object" || Array.isArray(options)) {
|
|
1562
1614
|
throw new DisTubeError("INVALID_TYPE", "object", options, "DisTubeOptions");
|
|
1563
1615
|
}
|
|
@@ -1580,6 +1632,7 @@ var _Options = class _Options {
|
|
|
1580
1632
|
this.joinNewVoiceChannel = opts.joinNewVoiceChannel;
|
|
1581
1633
|
this.streamType = opts.streamType;
|
|
1582
1634
|
this.directLink = opts.directLink;
|
|
1635
|
+
this.ffmpeg = __privateMethod(this, _ffmpegOption, ffmpegOption_fn).call(this, options);
|
|
1583
1636
|
checkInvalidKey(opts, this, "DisTubeOptions");
|
|
1584
1637
|
__privateMethod(this, _validateOptions, validateOptions_fn).call(this);
|
|
1585
1638
|
}
|
|
@@ -1600,7 +1653,7 @@ validateOptions_fn = /* @__PURE__ */ __name(function(options = this) {
|
|
|
1600
1653
|
]);
|
|
1601
1654
|
const numberOptions = /* @__PURE__ */ new Set(["searchCooldown", "emptyCooldown", "searchSongs"]);
|
|
1602
1655
|
const stringOptions = /* @__PURE__ */ new Set();
|
|
1603
|
-
const objectOptions = /* @__PURE__ */ new Set(["customFilters", "ytdlOptions"]);
|
|
1656
|
+
const objectOptions = /* @__PURE__ */ new Set(["customFilters", "ytdlOptions", "ffmpeg"]);
|
|
1604
1657
|
const optionalOptions = /* @__PURE__ */ new Set(["youtubeCookie", "customFilters"]);
|
|
1605
1658
|
for (const [key, value] of Object.entries(options)) {
|
|
1606
1659
|
if (value === void 0 && optionalOptions.has(key))
|
|
@@ -1630,6 +1683,29 @@ validateOptions_fn = /* @__PURE__ */ __name(function(options = this) {
|
|
|
1630
1683
|
}
|
|
1631
1684
|
}
|
|
1632
1685
|
}, "#validateOptions");
|
|
1686
|
+
_ffmpegOption = new WeakSet();
|
|
1687
|
+
ffmpegOption_fn = /* @__PURE__ */ __name(function(opts) {
|
|
1688
|
+
let path;
|
|
1689
|
+
const args = { global: {}, input: {}, output: {} };
|
|
1690
|
+
if (opts.ffmpegPath) {
|
|
1691
|
+
console.warn("`DisTubeOptions.ffmpegPath` is deprecated. Use `ffmpeg.path` instead.");
|
|
1692
|
+
path = opts.ffmpegPath;
|
|
1693
|
+
}
|
|
1694
|
+
if (opts.ffmpegDefaultArgs) {
|
|
1695
|
+
console.warn("`DisTubeOptions.ffmpegDefaultArgs` is deprecated. Use `ffmpeg.args` instead.");
|
|
1696
|
+
args.global = opts.ffmpegDefaultArgs;
|
|
1697
|
+
}
|
|
1698
|
+
path ??= opts.ffmpeg?.path ?? `ffmpeg${process.platform === "win32" ? ".exe" : ""}`;
|
|
1699
|
+
if (opts.ffmpeg?.args) {
|
|
1700
|
+
if (opts.ffmpeg.args.global)
|
|
1701
|
+
args.global = opts.ffmpeg.args.global;
|
|
1702
|
+
if (opts.ffmpeg.args.input)
|
|
1703
|
+
args.input = opts.ffmpeg.args.input;
|
|
1704
|
+
if (opts.ffmpeg.args.output)
|
|
1705
|
+
args.output = opts.ffmpeg.args.output;
|
|
1706
|
+
}
|
|
1707
|
+
return { path, args };
|
|
1708
|
+
}, "#ffmpegOption");
|
|
1633
1709
|
__name(_Options, "Options");
|
|
1634
1710
|
var Options = _Options;
|
|
1635
1711
|
|
|
@@ -1640,14 +1716,11 @@ var _BaseManager = class _BaseManager extends DisTubeBase {
|
|
|
1640
1716
|
super(...arguments);
|
|
1641
1717
|
/**
|
|
1642
1718
|
* The collection of items for this manager.
|
|
1643
|
-
* @type {Collection}
|
|
1644
|
-
* @name BaseManager#collection
|
|
1645
1719
|
*/
|
|
1646
1720
|
__publicField(this, "collection", new import_discord2.Collection());
|
|
1647
1721
|
}
|
|
1648
1722
|
/**
|
|
1649
1723
|
* The size of the collection.
|
|
1650
|
-
* @type {number}
|
|
1651
1724
|
*/
|
|
1652
1725
|
get size() {
|
|
1653
1726
|
return this.collection.size;
|
|
@@ -1663,7 +1736,8 @@ var _GuildIdManager = class _GuildIdManager extends BaseManager {
|
|
|
1663
1736
|
const existing = this.get(id);
|
|
1664
1737
|
if (existing)
|
|
1665
1738
|
return this;
|
|
1666
|
-
|
|
1739
|
+
this.collection.set(id, data);
|
|
1740
|
+
return this;
|
|
1667
1741
|
}
|
|
1668
1742
|
get(idOrInstance) {
|
|
1669
1743
|
return this.collection.get(resolveGuildId(idOrInstance));
|
|
@@ -1683,21 +1757,16 @@ var import_voice3 = require("@discordjs/voice");
|
|
|
1683
1757
|
var _DisTubeVoiceManager = class _DisTubeVoiceManager extends GuildIdManager {
|
|
1684
1758
|
/**
|
|
1685
1759
|
* Get a {@link DisTubeVoice}.
|
|
1686
|
-
*
|
|
1687
|
-
* @
|
|
1688
|
-
* @param {GuildIdResolvable} guild The queue resolvable to resolve
|
|
1689
|
-
* @returns {DisTubeVoice?}
|
|
1760
|
+
*
|
|
1761
|
+
* @param guild - The queue resolvable to resolve
|
|
1690
1762
|
*/
|
|
1691
1763
|
/**
|
|
1692
1764
|
* Collection of {@link DisTubeVoice}.
|
|
1693
|
-
* @name DisTubeVoiceManager#collection
|
|
1694
|
-
* @type {Discord.Collection<string, DisTubeVoice>}
|
|
1695
1765
|
*/
|
|
1696
1766
|
/**
|
|
1697
1767
|
* Create a {@link DisTubeVoice}
|
|
1698
|
-
*
|
|
1699
|
-
* @
|
|
1700
|
-
* @private
|
|
1768
|
+
*
|
|
1769
|
+
* @param channel - A voice channel to join
|
|
1701
1770
|
*/
|
|
1702
1771
|
create(channel) {
|
|
1703
1772
|
const existing = this.get(channel.guildId);
|
|
@@ -1709,8 +1778,8 @@ var _DisTubeVoiceManager = class _DisTubeVoiceManager extends GuildIdManager {
|
|
|
1709
1778
|
}
|
|
1710
1779
|
/**
|
|
1711
1780
|
* Join a voice channel
|
|
1712
|
-
*
|
|
1713
|
-
* @
|
|
1781
|
+
*
|
|
1782
|
+
* @param channel - A voice channel to join
|
|
1714
1783
|
*/
|
|
1715
1784
|
join(channel) {
|
|
1716
1785
|
const existing = this.get(channel.guildId);
|
|
@@ -1720,7 +1789,8 @@ var _DisTubeVoiceManager = class _DisTubeVoiceManager extends GuildIdManager {
|
|
|
1720
1789
|
}
|
|
1721
1790
|
/**
|
|
1722
1791
|
* Leave the connected voice channel in a guild
|
|
1723
|
-
*
|
|
1792
|
+
*
|
|
1793
|
+
* @param guild - Queue Resolvable
|
|
1724
1794
|
*/
|
|
1725
1795
|
leave(guild) {
|
|
1726
1796
|
const voice = this.get(guild);
|
|
@@ -1747,17 +1817,15 @@ var _FilterManager = class _FilterManager extends BaseManager {
|
|
|
1747
1817
|
__privateAdd(this, _removeFn);
|
|
1748
1818
|
/**
|
|
1749
1819
|
* Collection of {@link Filter}.
|
|
1750
|
-
* @name FilterManager#collection
|
|
1751
|
-
* @type {Discord.Collection<string, DisTubeVoice>}
|
|
1752
1820
|
*/
|
|
1753
1821
|
__publicField(this, "queue");
|
|
1754
1822
|
this.queue = queue;
|
|
1755
1823
|
}
|
|
1756
1824
|
/**
|
|
1757
1825
|
* Enable a filter or multiple filters to the manager
|
|
1758
|
-
*
|
|
1759
|
-
* @param
|
|
1760
|
-
* @
|
|
1826
|
+
*
|
|
1827
|
+
* @param filterOrFilters - The filter or filters to enable
|
|
1828
|
+
* @param override - Wether or not override the applied filter with new filter value
|
|
1761
1829
|
*/
|
|
1762
1830
|
add(filterOrFilters, override = false) {
|
|
1763
1831
|
if (Array.isArray(filterOrFilters)) {
|
|
@@ -1776,15 +1844,14 @@ var _FilterManager = class _FilterManager extends BaseManager {
|
|
|
1776
1844
|
}
|
|
1777
1845
|
/**
|
|
1778
1846
|
* Clear enabled filters of the manager
|
|
1779
|
-
* @returns {FilterManager}
|
|
1780
1847
|
*/
|
|
1781
1848
|
clear() {
|
|
1782
1849
|
return this.set([]);
|
|
1783
1850
|
}
|
|
1784
1851
|
/**
|
|
1785
1852
|
* Set the filters applied to the manager
|
|
1786
|
-
*
|
|
1787
|
-
* @
|
|
1853
|
+
*
|
|
1854
|
+
* @param filters - The filters to apply
|
|
1788
1855
|
*/
|
|
1789
1856
|
set(filters) {
|
|
1790
1857
|
if (!Array.isArray(filters))
|
|
@@ -1799,8 +1866,8 @@ var _FilterManager = class _FilterManager extends BaseManager {
|
|
|
1799
1866
|
}
|
|
1800
1867
|
/**
|
|
1801
1868
|
* Disable a filter or multiple filters
|
|
1802
|
-
*
|
|
1803
|
-
* @
|
|
1869
|
+
*
|
|
1870
|
+
* @param filterOrFilters - The filter or filters to disable
|
|
1804
1871
|
*/
|
|
1805
1872
|
remove(filterOrFilters) {
|
|
1806
1873
|
if (Array.isArray(filterOrFilters))
|
|
@@ -1812,30 +1879,26 @@ var _FilterManager = class _FilterManager extends BaseManager {
|
|
|
1812
1879
|
}
|
|
1813
1880
|
/**
|
|
1814
1881
|
* Check whether a filter enabled or not
|
|
1815
|
-
*
|
|
1816
|
-
* @
|
|
1882
|
+
*
|
|
1883
|
+
* @param filter - The filter to check
|
|
1817
1884
|
*/
|
|
1818
1885
|
has(filter) {
|
|
1819
1886
|
return this.collection.has(typeof filter === "string" ? filter : __privateMethod(this, _resolve, resolve_fn).call(this, filter).name);
|
|
1820
1887
|
}
|
|
1821
1888
|
/**
|
|
1822
1889
|
* Array of enabled filter names
|
|
1823
|
-
* @type {Array<string>}
|
|
1824
|
-
* @readonly
|
|
1825
1890
|
*/
|
|
1826
1891
|
get names() {
|
|
1827
1892
|
return [...this.collection.keys()];
|
|
1828
1893
|
}
|
|
1829
1894
|
/**
|
|
1830
1895
|
* Array of enabled filters
|
|
1831
|
-
* @type {Array<Filter>}
|
|
1832
|
-
* @readonly
|
|
1833
1896
|
*/
|
|
1834
1897
|
get values() {
|
|
1835
1898
|
return [...this.collection.values()];
|
|
1836
1899
|
}
|
|
1837
1900
|
get ffmpegArgs() {
|
|
1838
|
-
return this.size ?
|
|
1901
|
+
return this.size ? { af: this.values.map((f) => f.value).join(",") } : {};
|
|
1839
1902
|
}
|
|
1840
1903
|
toString() {
|
|
1841
1904
|
return this.names.toString();
|
|
@@ -1873,51 +1936,46 @@ var _QueueManager = class _QueueManager extends GuildIdManager {
|
|
|
1873
1936
|
super(...arguments);
|
|
1874
1937
|
/**
|
|
1875
1938
|
* Get a Queue from this QueueManager.
|
|
1876
|
-
*
|
|
1877
|
-
* @
|
|
1878
|
-
* @param {GuildIdResolvable} guild Resolvable thing from a guild
|
|
1879
|
-
* @returns {Queue?}
|
|
1939
|
+
*
|
|
1940
|
+
* @param guild - Resolvable thing from a guild
|
|
1880
1941
|
*/
|
|
1881
1942
|
/**
|
|
1882
1943
|
* Listen to DisTubeVoice events and handle the Queue
|
|
1883
|
-
*
|
|
1884
|
-
* @param
|
|
1944
|
+
*
|
|
1945
|
+
* @param queue - Queue
|
|
1885
1946
|
*/
|
|
1886
1947
|
__privateAdd(this, _voiceEventHandler);
|
|
1887
1948
|
/**
|
|
1888
1949
|
* Whether or not emit playSong event
|
|
1889
|
-
*
|
|
1890
|
-
* @
|
|
1891
|
-
* @returns {boolean}
|
|
1950
|
+
*
|
|
1951
|
+
* @param queue - Queue
|
|
1892
1952
|
*/
|
|
1893
1953
|
__privateAdd(this, _emitPlaySong);
|
|
1894
1954
|
/**
|
|
1895
1955
|
* Handle the queue when a Song finish
|
|
1896
|
-
*
|
|
1897
|
-
* @param
|
|
1898
|
-
* @returns {Promise<void>}
|
|
1956
|
+
*
|
|
1957
|
+
* @param queue - queue
|
|
1899
1958
|
*/
|
|
1900
1959
|
__privateAdd(this, _handleSongFinish);
|
|
1901
1960
|
/**
|
|
1902
1961
|
* Handle error while playing
|
|
1903
|
-
*
|
|
1904
|
-
* @param
|
|
1905
|
-
* @param
|
|
1962
|
+
*
|
|
1963
|
+
* @param queue - queue
|
|
1964
|
+
* @param error - error
|
|
1906
1965
|
*/
|
|
1907
1966
|
__privateAdd(this, _handlePlayingError);
|
|
1908
1967
|
}
|
|
1909
1968
|
/**
|
|
1910
1969
|
* Collection of {@link Queue}.
|
|
1911
|
-
* @name QueueManager#collection
|
|
1912
|
-
* @type {Discord.Collection<string, Queue>}
|
|
1913
1970
|
*/
|
|
1914
1971
|
/**
|
|
1915
1972
|
* Create a {@link Queue}
|
|
1916
|
-
*
|
|
1917
|
-
* @param
|
|
1918
|
-
* @param
|
|
1919
|
-
* @param
|
|
1920
|
-
*
|
|
1973
|
+
*
|
|
1974
|
+
* @param channel - A voice channel
|
|
1975
|
+
* @param song - First song
|
|
1976
|
+
* @param textChannel - Default text channel
|
|
1977
|
+
*
|
|
1978
|
+
* @returns Returns `true` if encounter an error
|
|
1921
1979
|
*/
|
|
1922
1980
|
async create(channel, song, textChannel) {
|
|
1923
1981
|
if (this.has(channel.guildId))
|
|
@@ -1926,10 +1984,11 @@ var _QueueManager = class _QueueManager extends GuildIdManager {
|
|
|
1926
1984
|
const queue = new Queue(this.distube, voice, song, textChannel);
|
|
1927
1985
|
await queue._taskQueue.queuing();
|
|
1928
1986
|
try {
|
|
1987
|
+
checkFFmpeg(this.distube);
|
|
1929
1988
|
await voice.join();
|
|
1930
1989
|
__privateMethod(this, _voiceEventHandler, voiceEventHandler_fn).call(this, queue);
|
|
1931
1990
|
this.add(queue.id, queue);
|
|
1932
|
-
this.emit("initQueue"
|
|
1991
|
+
this.emit("initQueue" /* INIT_QUEUE */, queue);
|
|
1933
1992
|
const err = await this.playSong(queue);
|
|
1934
1993
|
return err || queue;
|
|
1935
1994
|
} finally {
|
|
@@ -1938,26 +1997,36 @@ var _QueueManager = class _QueueManager extends GuildIdManager {
|
|
|
1938
1997
|
}
|
|
1939
1998
|
/**
|
|
1940
1999
|
* Create a ytdl stream
|
|
1941
|
-
*
|
|
1942
|
-
* @
|
|
2000
|
+
*
|
|
2001
|
+
* @param queue - Queue
|
|
1943
2002
|
*/
|
|
1944
2003
|
createStream(queue) {
|
|
1945
2004
|
const song = queue.songs[0];
|
|
1946
2005
|
const { duration, source, streamURL } = song;
|
|
1947
2006
|
const streamOptions = {
|
|
1948
|
-
|
|
2007
|
+
ffmpeg: {
|
|
2008
|
+
path: this.options.ffmpeg.path,
|
|
2009
|
+
args: {
|
|
2010
|
+
global: { ...this.options.ffmpeg.args.global },
|
|
2011
|
+
input: { ...this.options.ffmpeg.args.input },
|
|
2012
|
+
output: { ...this.options.ffmpeg.args.output, ...queue.filters.ffmpegArgs }
|
|
2013
|
+
}
|
|
2014
|
+
},
|
|
1949
2015
|
seek: duration ? queue.beginTime : void 0,
|
|
1950
2016
|
type: this.options.streamType
|
|
1951
2017
|
};
|
|
1952
2018
|
if (source === "youtube")
|
|
1953
2019
|
return DisTubeStream.YouTube(song, streamOptions);
|
|
2020
|
+
if (!streamURL)
|
|
2021
|
+
throw new Error("No streamURL, something went wrong");
|
|
1954
2022
|
return DisTubeStream.DirectLink(streamURL, streamOptions);
|
|
1955
2023
|
}
|
|
1956
2024
|
/**
|
|
1957
2025
|
* Play a song on voice connection
|
|
1958
|
-
*
|
|
1959
|
-
* @param
|
|
1960
|
-
*
|
|
2026
|
+
*
|
|
2027
|
+
* @param queue - The guild queue
|
|
2028
|
+
*
|
|
2029
|
+
* @returns error?
|
|
1961
2030
|
*/
|
|
1962
2031
|
async playSong(queue) {
|
|
1963
2032
|
if (!queue)
|
|
@@ -1974,6 +2043,7 @@ var _QueueManager = class _QueueManager extends GuildIdManager {
|
|
|
1974
2043
|
return true;
|
|
1975
2044
|
}
|
|
1976
2045
|
const stream = this.createStream(queue);
|
|
2046
|
+
stream.on("debug", (data) => this.emit("ffmpegDebug" /* FFMPEG_DEBUG */, `[${queue.id}]: ${data}`));
|
|
1977
2047
|
queue.voice.play(stream);
|
|
1978
2048
|
song.streamURL = stream.url;
|
|
1979
2049
|
return false;
|
|
@@ -1988,7 +2058,7 @@ voiceEventHandler_fn = /* @__PURE__ */ __name(function(queue) {
|
|
|
1988
2058
|
queue._listeners = {
|
|
1989
2059
|
disconnect: (error) => {
|
|
1990
2060
|
queue.remove();
|
|
1991
|
-
this.emit("disconnect"
|
|
2061
|
+
this.emit("disconnect" /* DISCONNECT */, queue);
|
|
1992
2062
|
if (error)
|
|
1993
2063
|
this.emitError(error, queue.textChannel);
|
|
1994
2064
|
},
|
|
@@ -2005,7 +2075,7 @@ emitPlaySong_fn = /* @__PURE__ */ __name(function(queue) {
|
|
|
2005
2075
|
}, "#emitPlaySong");
|
|
2006
2076
|
_handleSongFinish = new WeakSet();
|
|
2007
2077
|
handleSongFinish_fn = /* @__PURE__ */ __name(async function(queue) {
|
|
2008
|
-
this.emit("finishSong"
|
|
2078
|
+
this.emit("finishSong" /* FINISH_SONG */, queue, queue.songs[0]);
|
|
2009
2079
|
await queue._taskQueue.queuing();
|
|
2010
2080
|
try {
|
|
2011
2081
|
if (queue.stopped)
|
|
@@ -2023,14 +2093,14 @@ handleSongFinish_fn = /* @__PURE__ */ __name(async function(queue) {
|
|
|
2023
2093
|
try {
|
|
2024
2094
|
await queue.addRelatedSong();
|
|
2025
2095
|
} catch {
|
|
2026
|
-
this.emit("noRelated"
|
|
2096
|
+
this.emit("noRelated" /* NO_RELATED */, queue);
|
|
2027
2097
|
}
|
|
2028
2098
|
}
|
|
2029
2099
|
if (queue.songs.length <= 1) {
|
|
2030
2100
|
if (this.options.leaveOnFinish)
|
|
2031
2101
|
queue.voice.leave();
|
|
2032
2102
|
if (!queue.autoplay)
|
|
2033
|
-
this.emit("finish"
|
|
2103
|
+
this.emit("finish" /* FINISH */, queue);
|
|
2034
2104
|
queue.remove();
|
|
2035
2105
|
return;
|
|
2036
2106
|
}
|
|
@@ -2049,7 +2119,7 @@ handleSongFinish_fn = /* @__PURE__ */ __name(async function(queue) {
|
|
|
2049
2119
|
queue.beginTime = 0;
|
|
2050
2120
|
const err = await this.playSong(queue);
|
|
2051
2121
|
if (!err && emitPlaySong)
|
|
2052
|
-
this.emit("playSong"
|
|
2122
|
+
this.emit("playSong" /* PLAY_SONG */, queue, queue.songs[0]);
|
|
2053
2123
|
} finally {
|
|
2054
2124
|
queue._taskQueue.resolve();
|
|
2055
2125
|
}
|
|
@@ -2070,7 +2140,7 @@ Name: ${song.name}`;
|
|
|
2070
2140
|
queue.beginTime = 0;
|
|
2071
2141
|
this.playSong(queue).then((e) => {
|
|
2072
2142
|
if (!e)
|
|
2073
|
-
this.emit("playSong"
|
|
2143
|
+
this.emit("playSong" /* PLAY_SONG */, queue, queue.songs[0]);
|
|
2074
2144
|
});
|
|
2075
2145
|
} else {
|
|
2076
2146
|
queue.stop();
|
|
@@ -2084,10 +2154,11 @@ var _filters;
|
|
|
2084
2154
|
var _Queue = class _Queue extends DisTubeBase {
|
|
2085
2155
|
/**
|
|
2086
2156
|
* Create a queue for the guild
|
|
2087
|
-
*
|
|
2088
|
-
* @param
|
|
2089
|
-
* @param
|
|
2090
|
-
* @param
|
|
2157
|
+
*
|
|
2158
|
+
* @param distube - DisTube
|
|
2159
|
+
* @param voice - Voice connection
|
|
2160
|
+
* @param song - First song(s)
|
|
2161
|
+
* @param textChannel - Default text channel
|
|
2091
2162
|
*/
|
|
2092
2163
|
constructor(distube, voice, song, textChannel) {
|
|
2093
2164
|
super(distube);
|
|
@@ -2129,55 +2200,42 @@ var _Queue = class _Queue extends DisTubeBase {
|
|
|
2129
2200
|
}
|
|
2130
2201
|
/**
|
|
2131
2202
|
* The client user as a `GuildMember` of this queue's guild
|
|
2132
|
-
* @type {Discord.GuildMember?}
|
|
2133
2203
|
*/
|
|
2134
2204
|
get clientMember() {
|
|
2135
2205
|
return this.voice.channel.guild.members.me ?? void 0;
|
|
2136
2206
|
}
|
|
2137
2207
|
/**
|
|
2138
2208
|
* The filter manager of the queue
|
|
2139
|
-
* @type {FilterManager}
|
|
2140
|
-
* @readonly
|
|
2141
2209
|
*/
|
|
2142
2210
|
get filters() {
|
|
2143
2211
|
return __privateGet(this, _filters);
|
|
2144
2212
|
}
|
|
2145
2213
|
/**
|
|
2146
2214
|
* Formatted duration string.
|
|
2147
|
-
* @type {string}
|
|
2148
|
-
* @readonly
|
|
2149
2215
|
*/
|
|
2150
2216
|
get formattedDuration() {
|
|
2151
2217
|
return formatDuration(this.duration);
|
|
2152
2218
|
}
|
|
2153
2219
|
/**
|
|
2154
2220
|
* Queue's duration.
|
|
2155
|
-
* @type {number}
|
|
2156
|
-
* @readonly
|
|
2157
2221
|
*/
|
|
2158
2222
|
get duration() {
|
|
2159
2223
|
return this.songs.length ? this.songs.reduce((prev, next) => prev + next.duration, 0) : 0;
|
|
2160
2224
|
}
|
|
2161
2225
|
/**
|
|
2162
2226
|
* What time in the song is playing (in seconds).
|
|
2163
|
-
* @type {number}
|
|
2164
|
-
* @readonly
|
|
2165
2227
|
*/
|
|
2166
2228
|
get currentTime() {
|
|
2167
2229
|
return this.voice.playbackDuration + this.beginTime;
|
|
2168
2230
|
}
|
|
2169
2231
|
/**
|
|
2170
2232
|
* Formatted {@link Queue#currentTime} string.
|
|
2171
|
-
* @type {string}
|
|
2172
|
-
* @readonly
|
|
2173
2233
|
*/
|
|
2174
2234
|
get formattedCurrentTime() {
|
|
2175
2235
|
return formatDuration(this.currentTime);
|
|
2176
2236
|
}
|
|
2177
2237
|
/**
|
|
2178
2238
|
* The voice channel playing in.
|
|
2179
|
-
* @type {Discord.VoiceChannel|Discord.StageChannel|null}
|
|
2180
|
-
* @readonly
|
|
2181
2239
|
*/
|
|
2182
2240
|
get voiceChannel() {
|
|
2183
2241
|
return this.clientMember?.voice?.channel ?? null;
|
|
@@ -2189,12 +2247,12 @@ var _Queue = class _Queue extends DisTubeBase {
|
|
|
2189
2247
|
this.voice.volume = value;
|
|
2190
2248
|
}
|
|
2191
2249
|
/**
|
|
2192
|
-
* @
|
|
2193
|
-
*
|
|
2194
|
-
* @param
|
|
2195
|
-
* @param
|
|
2196
|
-
*
|
|
2197
|
-
* @returns
|
|
2250
|
+
* @throws {DisTubeError}
|
|
2251
|
+
*
|
|
2252
|
+
* @param song - Song to add
|
|
2253
|
+
* @param position - Position to add, \<= 0 to add to the end of the queue
|
|
2254
|
+
*
|
|
2255
|
+
* @returns The guild queue
|
|
2198
2256
|
*/
|
|
2199
2257
|
addToQueue(song, position = 0) {
|
|
2200
2258
|
if (!song || Array.isArray(song) && !song.length) {
|
|
@@ -2221,7 +2279,8 @@ var _Queue = class _Queue extends DisTubeBase {
|
|
|
2221
2279
|
}
|
|
2222
2280
|
/**
|
|
2223
2281
|
* Pause the guild stream
|
|
2224
|
-
*
|
|
2282
|
+
*
|
|
2283
|
+
* @returns The guild queue
|
|
2225
2284
|
*/
|
|
2226
2285
|
pause() {
|
|
2227
2286
|
if (this.paused)
|
|
@@ -2233,7 +2292,8 @@ var _Queue = class _Queue extends DisTubeBase {
|
|
|
2233
2292
|
}
|
|
2234
2293
|
/**
|
|
2235
2294
|
* Resume the guild stream
|
|
2236
|
-
*
|
|
2295
|
+
*
|
|
2296
|
+
* @returns The guild queue
|
|
2237
2297
|
*/
|
|
2238
2298
|
resume() {
|
|
2239
2299
|
if (this.playing)
|
|
@@ -2245,19 +2305,21 @@ var _Queue = class _Queue extends DisTubeBase {
|
|
|
2245
2305
|
}
|
|
2246
2306
|
/**
|
|
2247
2307
|
* Set the guild stream's volume
|
|
2248
|
-
*
|
|
2249
|
-
* @
|
|
2308
|
+
*
|
|
2309
|
+
* @param percent - The percentage of volume you want to set
|
|
2310
|
+
*
|
|
2311
|
+
* @returns The guild queue
|
|
2250
2312
|
*/
|
|
2251
2313
|
setVolume(percent) {
|
|
2252
2314
|
this.volume = percent;
|
|
2253
2315
|
return this;
|
|
2254
2316
|
}
|
|
2255
2317
|
/**
|
|
2256
|
-
* Skip the playing song if there is a next song in the queue.
|
|
2257
|
-
*
|
|
2258
|
-
*
|
|
2259
|
-
*
|
|
2260
|
-
* @
|
|
2318
|
+
* Skip the playing song if there is a next song in the queue. <info>If {@link
|
|
2319
|
+
* Queue#autoplay} is `true` and there is no up next song, DisTube will add and
|
|
2320
|
+
* play a related song.</info>
|
|
2321
|
+
*
|
|
2322
|
+
* @returns The song will skip to
|
|
2261
2323
|
*/
|
|
2262
2324
|
async skip() {
|
|
2263
2325
|
await this._taskQueue.queuing();
|
|
@@ -2278,8 +2340,8 @@ var _Queue = class _Queue extends DisTubeBase {
|
|
|
2278
2340
|
}
|
|
2279
2341
|
/**
|
|
2280
2342
|
* Play the previous song if exists
|
|
2281
|
-
*
|
|
2282
|
-
* @
|
|
2343
|
+
*
|
|
2344
|
+
* @returns The guild queue
|
|
2283
2345
|
*/
|
|
2284
2346
|
async previous() {
|
|
2285
2347
|
await this._taskQueue.queuing();
|
|
@@ -2299,7 +2361,8 @@ var _Queue = class _Queue extends DisTubeBase {
|
|
|
2299
2361
|
}
|
|
2300
2362
|
/**
|
|
2301
2363
|
* Shuffle the queue's songs
|
|
2302
|
-
*
|
|
2364
|
+
*
|
|
2365
|
+
* @returns The guild queue
|
|
2303
2366
|
*/
|
|
2304
2367
|
async shuffle() {
|
|
2305
2368
|
await this._taskQueue.queuing();
|
|
@@ -2318,12 +2381,13 @@ var _Queue = class _Queue extends DisTubeBase {
|
|
|
2318
2381
|
}
|
|
2319
2382
|
}
|
|
2320
2383
|
/**
|
|
2321
|
-
* Jump to the song position in the queue.
|
|
2322
|
-
*
|
|
2323
|
-
*
|
|
2324
|
-
*
|
|
2325
|
-
* @
|
|
2326
|
-
*
|
|
2384
|
+
* Jump to the song position in the queue. The next one is 1, 2,... The previous
|
|
2385
|
+
* one is -1, -2,...
|
|
2386
|
+
* if `num` is invalid number
|
|
2387
|
+
*
|
|
2388
|
+
* @param position - The song position to play
|
|
2389
|
+
*
|
|
2390
|
+
* @returns The new Song will be played
|
|
2327
2391
|
*/
|
|
2328
2392
|
async jump(position) {
|
|
2329
2393
|
await this._taskQueue.queuing();
|
|
@@ -2359,10 +2423,12 @@ var _Queue = class _Queue extends DisTubeBase {
|
|
|
2359
2423
|
}
|
|
2360
2424
|
}
|
|
2361
2425
|
/**
|
|
2362
|
-
* Set the repeat mode of the guild queue
|
|
2426
|
+
* Set the repeat mode of the guild queue.
|
|
2363
2427
|
* Toggle mode `(Disabled -> Song -> Queue -> Disabled ->...)` if `mode` is `undefined`
|
|
2364
|
-
*
|
|
2365
|
-
* @
|
|
2428
|
+
*
|
|
2429
|
+
* @param mode - The repeat modes (toggle if `undefined`)
|
|
2430
|
+
*
|
|
2431
|
+
* @returns The new repeat mode
|
|
2366
2432
|
*/
|
|
2367
2433
|
setRepeatMode(mode) {
|
|
2368
2434
|
if (mode !== void 0 && !Object.values(RepeatMode).includes(mode)) {
|
|
@@ -2378,8 +2444,10 @@ var _Queue = class _Queue extends DisTubeBase {
|
|
|
2378
2444
|
}
|
|
2379
2445
|
/**
|
|
2380
2446
|
* Set the playing time to another position
|
|
2381
|
-
*
|
|
2382
|
-
* @
|
|
2447
|
+
*
|
|
2448
|
+
* @param time - Time in seconds
|
|
2449
|
+
*
|
|
2450
|
+
* @returns The guild queue
|
|
2383
2451
|
*/
|
|
2384
2452
|
seek(time) {
|
|
2385
2453
|
if (typeof time !== "number")
|
|
@@ -2392,8 +2460,8 @@ var _Queue = class _Queue extends DisTubeBase {
|
|
|
2392
2460
|
}
|
|
2393
2461
|
/**
|
|
2394
2462
|
* Add a related song of the playing song to the queue
|
|
2395
|
-
*
|
|
2396
|
-
* @
|
|
2463
|
+
*
|
|
2464
|
+
* @returns The added song
|
|
2397
2465
|
*/
|
|
2398
2466
|
async addRelatedSong() {
|
|
2399
2467
|
if (!this.songs?.[0])
|
|
@@ -2426,9 +2494,8 @@ var _Queue = class _Queue extends DisTubeBase {
|
|
|
2426
2494
|
}
|
|
2427
2495
|
}
|
|
2428
2496
|
/**
|
|
2429
|
-
* Remove the queue from the manager
|
|
2430
|
-
*
|
|
2431
|
-
* @private
|
|
2497
|
+
* Remove the queue from the manager (This does not leave the voice channel even if
|
|
2498
|
+
* {@link DisTubeOptions | DisTubeOptions.leaveOnStop} is enabled)
|
|
2432
2499
|
*/
|
|
2433
2500
|
remove() {
|
|
2434
2501
|
this.stopped = true;
|
|
@@ -2440,11 +2507,12 @@ var _Queue = class _Queue extends DisTubeBase {
|
|
|
2440
2507
|
}
|
|
2441
2508
|
}
|
|
2442
2509
|
this.queues.remove(this.id);
|
|
2443
|
-
this.emit("deleteQueue"
|
|
2510
|
+
this.emit("deleteQueue" /* DELETE_QUEUE */, this);
|
|
2444
2511
|
}
|
|
2445
2512
|
/**
|
|
2446
2513
|
* Toggle autoplay mode
|
|
2447
|
-
*
|
|
2514
|
+
*
|
|
2515
|
+
* @returns Autoplay mode state
|
|
2448
2516
|
*/
|
|
2449
2517
|
toggleAutoplay() {
|
|
2450
2518
|
this.autoplay = !this.autoplay;
|
|
@@ -2465,70 +2533,59 @@ var _Plugin = class _Plugin {
|
|
|
2465
2533
|
}
|
|
2466
2534
|
/**
|
|
2467
2535
|
* Type of the plugin
|
|
2468
|
-
* @name Plugin#type
|
|
2469
|
-
* @type {PluginType}
|
|
2470
2536
|
*/
|
|
2471
2537
|
/**
|
|
2472
2538
|
* Emit an event to the {@link DisTube} class
|
|
2473
|
-
*
|
|
2474
|
-
* @param
|
|
2475
|
-
* @
|
|
2539
|
+
*
|
|
2540
|
+
* @param eventName - Event name
|
|
2541
|
+
* @param args - arguments
|
|
2476
2542
|
*/
|
|
2477
2543
|
emit(eventName, ...args) {
|
|
2478
2544
|
return this.distube.emit(eventName, ...args);
|
|
2479
2545
|
}
|
|
2480
2546
|
/**
|
|
2481
2547
|
* Emit error event to the {@link DisTube} class
|
|
2482
|
-
*
|
|
2483
|
-
* @param
|
|
2548
|
+
*
|
|
2549
|
+
* @param error - error
|
|
2550
|
+
* @param channel - Text channel where the error is encountered.
|
|
2484
2551
|
*/
|
|
2485
2552
|
emitError(error, channel) {
|
|
2486
2553
|
this.distube.emitError(error, channel);
|
|
2487
2554
|
}
|
|
2488
2555
|
/**
|
|
2489
2556
|
* The queue manager
|
|
2490
|
-
* @type {QueueManager}
|
|
2491
|
-
* @readonly
|
|
2492
2557
|
*/
|
|
2493
2558
|
get queues() {
|
|
2494
2559
|
return this.distube.queues;
|
|
2495
2560
|
}
|
|
2496
2561
|
/**
|
|
2497
2562
|
* The voice manager
|
|
2498
|
-
* @type {DisTubeVoiceManager}
|
|
2499
|
-
* @readonly
|
|
2500
2563
|
*/
|
|
2501
2564
|
get voices() {
|
|
2502
2565
|
return this.distube.voices;
|
|
2503
2566
|
}
|
|
2504
2567
|
/**
|
|
2505
2568
|
* Discord.js client
|
|
2506
|
-
* @type {Discord.Client}
|
|
2507
|
-
* @readonly
|
|
2508
2569
|
*/
|
|
2509
2570
|
get client() {
|
|
2510
2571
|
return this.distube.client;
|
|
2511
2572
|
}
|
|
2512
2573
|
/**
|
|
2513
2574
|
* DisTube options
|
|
2514
|
-
* @type {DisTubeOptions}
|
|
2515
|
-
* @readonly
|
|
2516
2575
|
*/
|
|
2517
2576
|
get options() {
|
|
2518
2577
|
return this.distube.options;
|
|
2519
2578
|
}
|
|
2520
2579
|
/**
|
|
2521
2580
|
* DisTube handler
|
|
2522
|
-
* @type {DisTubeHandler}
|
|
2523
|
-
* @readonly
|
|
2524
2581
|
*/
|
|
2525
2582
|
get handler() {
|
|
2526
2583
|
return this.distube.handler;
|
|
2527
2584
|
}
|
|
2528
2585
|
/**
|
|
2529
2586
|
* Check if the string is working with this plugin
|
|
2530
|
-
*
|
|
2531
|
-
* @
|
|
2587
|
+
*
|
|
2588
|
+
* @param _string - Input string
|
|
2532
2589
|
*/
|
|
2533
2590
|
validate(_string) {
|
|
2534
2591
|
return false;
|
|
@@ -2536,17 +2593,18 @@ var _Plugin = class _Plugin {
|
|
|
2536
2593
|
/**
|
|
2537
2594
|
* Get the stream url from {@link Song#url}. Returns {@link Song#url} by default.
|
|
2538
2595
|
* Not needed if the plugin plays song from YouTube.
|
|
2539
|
-
*
|
|
2540
|
-
* @
|
|
2596
|
+
*
|
|
2597
|
+
* @param url - Input url
|
|
2541
2598
|
*/
|
|
2542
2599
|
getStreamURL(url) {
|
|
2543
2600
|
return url;
|
|
2544
2601
|
}
|
|
2545
2602
|
/**
|
|
2546
|
-
* Get related songs from a supported url. {@link Song#member} should be
|
|
2547
|
-
* Not needed to add {@link Song#related} because it will be added
|
|
2548
|
-
*
|
|
2549
|
-
*
|
|
2603
|
+
* Get related songs from a supported url. {@link Song#member} should be
|
|
2604
|
+
* `undefined`. Not needed to add {@link Song#related} because it will be added
|
|
2605
|
+
* with this function later.
|
|
2606
|
+
*
|
|
2607
|
+
* @param _url - Input url
|
|
2550
2608
|
*/
|
|
2551
2609
|
getRelatedSongs(_url) {
|
|
2552
2610
|
return [];
|
|
@@ -2747,15 +2805,9 @@ var _DirectLinkPlugin = class _DirectLinkPlugin extends ExtractorPlugin {
|
|
|
2747
2805
|
return false;
|
|
2748
2806
|
}
|
|
2749
2807
|
resolve(url, options = {}) {
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
name: url.substring(url.lastIndexOf("/") + 1).replace(/((\?|#).*)?$/, "") || url,
|
|
2754
|
-
url,
|
|
2755
|
-
src: "direct_link"
|
|
2756
|
-
},
|
|
2757
|
-
options
|
|
2758
|
-
);
|
|
2808
|
+
const u = new URL(url);
|
|
2809
|
+
const name = u.pathname.split("/").pop() || u.href;
|
|
2810
|
+
return new Song({ name, url, src: "direct_link" }, options);
|
|
2759
2811
|
}
|
|
2760
2812
|
};
|
|
2761
2813
|
__name(_DirectLinkPlugin, "DirectLinkPlugin");
|
|
@@ -2763,11 +2815,11 @@ var DirectLinkPlugin = _DirectLinkPlugin;
|
|
|
2763
2815
|
|
|
2764
2816
|
// src/DisTube.ts
|
|
2765
2817
|
var import_ytsr = __toESM(require("@distube/ytsr"));
|
|
2766
|
-
var
|
|
2818
|
+
var import_tiny_typed_emitter3 = require("tiny-typed-emitter");
|
|
2767
2819
|
var { version } = require_package();
|
|
2768
2820
|
var _getQueue, getQueue_fn;
|
|
2769
|
-
var _DisTube = class _DisTube extends
|
|
2770
|
-
constructor(client,
|
|
2821
|
+
var _DisTube = class _DisTube extends import_tiny_typed_emitter3.TypedEmitter {
|
|
2822
|
+
constructor(client, opts = {}) {
|
|
2771
2823
|
super();
|
|
2772
2824
|
__privateAdd(this, _getQueue);
|
|
2773
2825
|
__publicField(this, "handler");
|
|
@@ -2783,7 +2835,7 @@ var _DisTube = class _DisTube extends import_tiny_typed_emitter2.TypedEmitter {
|
|
|
2783
2835
|
throw new DisTubeError("INVALID_TYPE", "Discord.Client", client, "client");
|
|
2784
2836
|
this.client = client;
|
|
2785
2837
|
checkIntents(client.options);
|
|
2786
|
-
this.options = new Options(
|
|
2838
|
+
this.options = new Options(opts);
|
|
2787
2839
|
this.voices = new DisTubeVoiceManager(this);
|
|
2788
2840
|
this.handler = new DisTubeHandler(this);
|
|
2789
2841
|
this.queues = new QueueManager(this);
|
|
@@ -2799,21 +2851,16 @@ var _DisTube = class _DisTube extends import_tiny_typed_emitter2.TypedEmitter {
|
|
|
2799
2851
|
}
|
|
2800
2852
|
/**
|
|
2801
2853
|
* DisTube version
|
|
2802
|
-
* @type {string}
|
|
2803
2854
|
*/
|
|
2804
2855
|
get version() {
|
|
2805
2856
|
return version;
|
|
2806
2857
|
}
|
|
2807
2858
|
/**
|
|
2808
|
-
* Play / add a song or playlist from url. Search and play a song if it is not a
|
|
2859
|
+
* Play / add a song or playlist from url. Search and play a song if it is not a
|
|
2860
|
+
* valid url.
|
|
2809
2861
|
*
|
|
2810
|
-
* @param {Discord.BaseGuildVoiceChannel} voiceChannel The channel will be joined if the bot isn't in any channels,
|
|
2811
|
-
* the bot will be moved to this channel if {@link DisTubeOptions}.joinNewVoiceChannel is `true`
|
|
2812
|
-
* @param {string|Song|SearchResult|Playlist} song URL | Search string |
|
|
2813
|
-
* {@link Song} | {@link SearchResult} | {@link Playlist}
|
|
2814
|
-
* @param {PlayOptions} [options] Optional options
|
|
2815
|
-
* @throws {DisTubeError}
|
|
2816
2862
|
* @example
|
|
2863
|
+
* ```ts
|
|
2817
2864
|
* client.on('message', (message) => {
|
|
2818
2865
|
* if (!message.content.startsWith(config.prefix)) return;
|
|
2819
2866
|
* const args = message.content.slice(config.prefix.length).trim().split(/ +/g);
|
|
@@ -2825,7 +2872,14 @@ var _DisTube = class _DisTube extends import_tiny_typed_emitter2.TypedEmitter {
|
|
|
2825
2872
|
* message
|
|
2826
2873
|
* });
|
|
2827
2874
|
* });
|
|
2828
|
-
*
|
|
2875
|
+
* ```ts
|
|
2876
|
+
*
|
|
2877
|
+
* @throws {@link DisTubeError}
|
|
2878
|
+
*
|
|
2879
|
+
* @param voiceChannel - The channel will be joined if the bot isn't in any channels, the bot will be
|
|
2880
|
+
* moved to this channel if {@link DisTubeOptions}.joinNewVoiceChannel is `true`
|
|
2881
|
+
* @param song - URL | Search string | {@link Song} | {@link SearchResult} | {@link Playlist}
|
|
2882
|
+
* @param options - Optional options
|
|
2829
2883
|
*/
|
|
2830
2884
|
async play(voiceChannel, song, options = {}) {
|
|
2831
2885
|
if (!isSupportedVoiceChannel(voiceChannel)) {
|
|
@@ -2895,10 +2949,9 @@ ${e.message}`;
|
|
|
2895
2949
|
}
|
|
2896
2950
|
/**
|
|
2897
2951
|
* Create a custom playlist
|
|
2898
|
-
*
|
|
2899
|
-
* @param {Array<string|Song|SearchResult>} songs Array of url, Song or SearchResult
|
|
2900
|
-
* @param {CustomPlaylistOptions} [options] Optional options
|
|
2952
|
+
*
|
|
2901
2953
|
* @example
|
|
2954
|
+
* ```ts
|
|
2902
2955
|
* const songs = ["https://www.youtube.com/watch?v=xxx", "https://www.youtube.com/watch?v=yyy"];
|
|
2903
2956
|
* const playlist = await distube.createCustomPlaylist(songs, {
|
|
2904
2957
|
* member: message.member,
|
|
@@ -2906,6 +2959,10 @@ ${e.message}`;
|
|
|
2906
2959
|
* parallel: true
|
|
2907
2960
|
* });
|
|
2908
2961
|
* distube.play(voiceChannel, playlist, { ... });
|
|
2962
|
+
* ```ts
|
|
2963
|
+
*
|
|
2964
|
+
* @param songs - Array of url, Song or SearchResult
|
|
2965
|
+
* @param options - Optional options
|
|
2909
2966
|
*/
|
|
2910
2967
|
async createCustomPlaylist(songs, options = {}) {
|
|
2911
2968
|
const { member, properties, parallel, metadata } = { parallel: true, ...options };
|
|
@@ -2926,13 +2983,14 @@ ${e.message}`;
|
|
|
2926
2983
|
const promises = filteredSongs.map(
|
|
2927
2984
|
(song) => this.handler.resolve(song, { member, metadata }).catch(() => void 0)
|
|
2928
2985
|
);
|
|
2929
|
-
resolvedSongs = (await Promise.all(promises)).filter((s) =>
|
|
2986
|
+
resolvedSongs = (await Promise.all(promises)).filter((s) => s instanceof Song);
|
|
2930
2987
|
} else {
|
|
2931
|
-
|
|
2988
|
+
resolvedSongs = [];
|
|
2932
2989
|
for (const song of filteredSongs) {
|
|
2933
|
-
resolved
|
|
2990
|
+
const resolved = await this.handler.resolve(song, { member, metadata }).catch(() => void 0);
|
|
2991
|
+
if (resolved instanceof Song)
|
|
2992
|
+
resolvedSongs.push(resolved);
|
|
2934
2993
|
}
|
|
2935
|
-
resolvedSongs = resolved.filter((s) => Boolean(s));
|
|
2936
2994
|
}
|
|
2937
2995
|
return new Playlist(resolvedSongs, { member, properties, metadata });
|
|
2938
2996
|
}
|
|
@@ -2940,13 +2998,13 @@ ${e.message}`;
|
|
|
2940
2998
|
* Search for a song. You can customize how user answers instead of send a number.
|
|
2941
2999
|
* Then use {@link DisTube#play} to play it.
|
|
2942
3000
|
*
|
|
2943
|
-
* @param
|
|
2944
|
-
* @param
|
|
2945
|
-
* @param
|
|
2946
|
-
* @param
|
|
2947
|
-
* @param
|
|
2948
|
-
*
|
|
2949
|
-
* @returns
|
|
3001
|
+
* @param string - The string search for
|
|
3002
|
+
* @param options - Search options
|
|
3003
|
+
* @param options.limit - Limit the results
|
|
3004
|
+
* @param options.type - Type of results (`video` or `playlist`).
|
|
3005
|
+
* @param options.safeSearch - Whether or not use safe search (YouTube restricted mode)
|
|
3006
|
+
*
|
|
3007
|
+
* @returns Array of results
|
|
2950
3008
|
*/
|
|
2951
3009
|
async search(string, options = {}) {
|
|
2952
3010
|
const opts = { type: "video" /* VIDEO */, limit: 10, safeSearch: false, ...options };
|
|
@@ -2979,10 +3037,9 @@ ${e.message}`;
|
|
|
2979
3037
|
}
|
|
2980
3038
|
/**
|
|
2981
3039
|
* Get the guild queue
|
|
2982
|
-
*
|
|
2983
|
-
* @returns {Queue?}
|
|
2984
|
-
* @throws {Error}
|
|
3040
|
+
*
|
|
2985
3041
|
* @example
|
|
3042
|
+
* ```ts
|
|
2986
3043
|
* client.on('message', (message) => {
|
|
2987
3044
|
* if (!message.content.startsWith(config.prefix)) return;
|
|
2988
3045
|
* const args = message.content.slice(config.prefix.length).trim().split(/ +/g);
|
|
@@ -2994,34 +3051,38 @@ ${e.message}`;
|
|
|
2994
3051
|
* ).join("\n"));
|
|
2995
3052
|
* }
|
|
2996
3053
|
* });
|
|
3054
|
+
* ```ts
|
|
3055
|
+
*
|
|
3056
|
+
* @param guild - The type can be resolved to give a {@link Queue}
|
|
2997
3057
|
*/
|
|
2998
3058
|
getQueue(guild) {
|
|
2999
3059
|
return this.queues.get(guild);
|
|
3000
3060
|
}
|
|
3001
3061
|
/**
|
|
3002
3062
|
* Pause the guild stream
|
|
3003
|
-
*
|
|
3004
|
-
* @
|
|
3005
|
-
*
|
|
3063
|
+
*
|
|
3064
|
+
* @param guild - The type can be resolved to give a {@link Queue}
|
|
3065
|
+
*
|
|
3066
|
+
* @returns The guild queue
|
|
3006
3067
|
*/
|
|
3007
3068
|
pause(guild) {
|
|
3008
3069
|
return __privateMethod(this, _getQueue, getQueue_fn).call(this, guild).pause();
|
|
3009
3070
|
}
|
|
3010
3071
|
/**
|
|
3011
3072
|
* Resume the guild stream
|
|
3012
|
-
*
|
|
3013
|
-
* @
|
|
3014
|
-
*
|
|
3073
|
+
*
|
|
3074
|
+
* @param guild - The type can be resolved to give a {@link Queue}
|
|
3075
|
+
*
|
|
3076
|
+
* @returns The guild queue
|
|
3015
3077
|
*/
|
|
3016
3078
|
resume(guild) {
|
|
3017
3079
|
return __privateMethod(this, _getQueue, getQueue_fn).call(this, guild).resume();
|
|
3018
3080
|
}
|
|
3019
3081
|
/**
|
|
3020
3082
|
* Stop the guild stream
|
|
3021
|
-
*
|
|
3022
|
-
* @returns {Promise<void>}
|
|
3023
|
-
* @throws {Error}
|
|
3083
|
+
*
|
|
3024
3084
|
* @example
|
|
3085
|
+
* ```ts
|
|
3025
3086
|
* client.on('message', (message) => {
|
|
3026
3087
|
* if (!message.content.startsWith(config.prefix)) return;
|
|
3027
3088
|
* const args = message.content.slice(config.prefix.length).trim().split(/ +/g);
|
|
@@ -3031,17 +3092,18 @@ ${e.message}`;
|
|
|
3031
3092
|
* message.channel.send("Stopped the queue!");
|
|
3032
3093
|
* }
|
|
3033
3094
|
* });
|
|
3095
|
+
* ```ts
|
|
3096
|
+
*
|
|
3097
|
+
* @param guild - The type can be resolved to give a {@link Queue}
|
|
3034
3098
|
*/
|
|
3035
3099
|
stop(guild) {
|
|
3036
3100
|
return __privateMethod(this, _getQueue, getQueue_fn).call(this, guild).stop();
|
|
3037
3101
|
}
|
|
3038
3102
|
/**
|
|
3039
3103
|
* Set the guild stream's volume
|
|
3040
|
-
*
|
|
3041
|
-
* @param {number} percent The percentage of volume you want to set
|
|
3042
|
-
* @returns {Queue} The guild queue
|
|
3043
|
-
* @throws {Error}
|
|
3104
|
+
*
|
|
3044
3105
|
* @example
|
|
3106
|
+
* ```ts
|
|
3045
3107
|
* client.on('message', (message) => {
|
|
3046
3108
|
* if (!message.content.startsWith(config.prefix)) return;
|
|
3047
3109
|
* const args = message.content.slice(config.prefix.length).trim().split(/ +/g);
|
|
@@ -3049,18 +3111,23 @@ ${e.message}`;
|
|
|
3049
3111
|
* if (command == "volume")
|
|
3050
3112
|
* distube.setVolume(message, Number(args[0]));
|
|
3051
3113
|
* });
|
|
3114
|
+
* ```ts
|
|
3115
|
+
*
|
|
3116
|
+
* @param guild - The type can be resolved to give a {@link Queue}
|
|
3117
|
+
* @param percent - The percentage of volume you want to set
|
|
3118
|
+
*
|
|
3119
|
+
* @returns The guild queue
|
|
3052
3120
|
*/
|
|
3053
3121
|
setVolume(guild, percent) {
|
|
3054
3122
|
return __privateMethod(this, _getQueue, getQueue_fn).call(this, guild).setVolume(percent);
|
|
3055
3123
|
}
|
|
3056
3124
|
/**
|
|
3057
|
-
* Skip the playing song if there is a next song in the queue.
|
|
3058
|
-
*
|
|
3059
|
-
*
|
|
3060
|
-
*
|
|
3061
|
-
* @returns {Promise<Song>} The new Song will be played
|
|
3062
|
-
* @throws {Error}
|
|
3125
|
+
* Skip the playing song if there is a next song in the queue. <info>If {@link
|
|
3126
|
+
* Queue#autoplay} is `true` and there is no up next song, DisTube will add and
|
|
3127
|
+
* play a related song.</info>
|
|
3128
|
+
*
|
|
3063
3129
|
* @example
|
|
3130
|
+
* ```ts
|
|
3064
3131
|
* client.on('message', (message) => {
|
|
3065
3132
|
* if (!message.content.startsWith(config.prefix)) return;
|
|
3066
3133
|
* const args = message.content.slice(config.prefix.length).trim().split(/ +/g);
|
|
@@ -3068,16 +3135,20 @@ ${e.message}`;
|
|
|
3068
3135
|
* if (command == "skip")
|
|
3069
3136
|
* distube.skip(message);
|
|
3070
3137
|
* });
|
|
3138
|
+
* ```ts
|
|
3139
|
+
*
|
|
3140
|
+
* @param guild - The type can be resolved to give a {@link Queue}
|
|
3141
|
+
*
|
|
3142
|
+
* @returns The new Song will be played
|
|
3071
3143
|
*/
|
|
3072
3144
|
skip(guild) {
|
|
3073
3145
|
return __privateMethod(this, _getQueue, getQueue_fn).call(this, guild).skip();
|
|
3074
3146
|
}
|
|
3075
3147
|
/**
|
|
3076
3148
|
* Play the previous song
|
|
3077
|
-
*
|
|
3078
|
-
* @returns {Promise<Song>} The new Song will be played
|
|
3079
|
-
* @throws {Error}
|
|
3149
|
+
*
|
|
3080
3150
|
* @example
|
|
3151
|
+
* ```ts
|
|
3081
3152
|
* client.on('message', (message) => {
|
|
3082
3153
|
* if (!message.content.startsWith(config.prefix)) return;
|
|
3083
3154
|
* const args = message.content.slice(config.prefix.length).trim().split(/ +/g);
|
|
@@ -3085,15 +3156,20 @@ ${e.message}`;
|
|
|
3085
3156
|
* if (command == "previous")
|
|
3086
3157
|
* distube.previous(message);
|
|
3087
3158
|
* });
|
|
3159
|
+
* ```ts
|
|
3160
|
+
*
|
|
3161
|
+
* @param guild - The type can be resolved to give a {@link Queue}
|
|
3162
|
+
*
|
|
3163
|
+
* @returns The new Song will be played
|
|
3088
3164
|
*/
|
|
3089
3165
|
previous(guild) {
|
|
3090
3166
|
return __privateMethod(this, _getQueue, getQueue_fn).call(this, guild).previous();
|
|
3091
3167
|
}
|
|
3092
3168
|
/**
|
|
3093
3169
|
* Shuffle the guild queue songs
|
|
3094
|
-
*
|
|
3095
|
-
* @returns {Promise<Queue>} The guild queue
|
|
3170
|
+
*
|
|
3096
3171
|
* @example
|
|
3172
|
+
* ```ts
|
|
3097
3173
|
* client.on('message', (message) => {
|
|
3098
3174
|
* if (!message.content.startsWith(config.prefix)) return;
|
|
3099
3175
|
* const args = message.content.slice(config.prefix.length).trim().split(/ +/g);
|
|
@@ -3101,19 +3177,21 @@ ${e.message}`;
|
|
|
3101
3177
|
* if (command == "shuffle")
|
|
3102
3178
|
* distube.shuffle(message);
|
|
3103
3179
|
* });
|
|
3180
|
+
* ```ts
|
|
3181
|
+
*
|
|
3182
|
+
* @param guild - The type can be resolved to give a {@link Queue}
|
|
3183
|
+
*
|
|
3184
|
+
* @returns The guild queue
|
|
3104
3185
|
*/
|
|
3105
3186
|
shuffle(guild) {
|
|
3106
3187
|
return __privateMethod(this, _getQueue, getQueue_fn).call(this, guild).shuffle();
|
|
3107
3188
|
}
|
|
3108
3189
|
/**
|
|
3109
|
-
* Jump to the song number in the queue.
|
|
3110
|
-
*
|
|
3111
|
-
*
|
|
3112
|
-
* @param {GuildIdResolvable} guild The type can be resolved to give a {@link Queue}
|
|
3113
|
-
* @param {number} num The song number to play
|
|
3114
|
-
* @returns {Promise<Song>} The new Song will be played
|
|
3115
|
-
* @throws {Error} if `num` is invalid number (0 < num < {@link Queue#songs}.length)
|
|
3190
|
+
* Jump to the song number in the queue. The next one is 1, 2,... The previous one
|
|
3191
|
+
* is -1, -2,...
|
|
3192
|
+
*
|
|
3116
3193
|
* @example
|
|
3194
|
+
* ```ts
|
|
3117
3195
|
* client.on('message', (message) => {
|
|
3118
3196
|
* if (!message.content.startsWith(config.prefix)) return;
|
|
3119
3197
|
* const args = message.content.slice(config.prefix.length).trim().split(/ +/g);
|
|
@@ -3122,17 +3200,22 @@ ${e.message}`;
|
|
|
3122
3200
|
* distube.jump(message, parseInt(args[0]))
|
|
3123
3201
|
* .catch(err => message.channel.send("Invalid song number."));
|
|
3124
3202
|
* });
|
|
3203
|
+
* ```ts
|
|
3204
|
+
*
|
|
3205
|
+
* @param guild - The type can be resolved to give a {@link Queue}
|
|
3206
|
+
* @param num - The song number to play
|
|
3207
|
+
*
|
|
3208
|
+
* @returns The new Song will be played
|
|
3125
3209
|
*/
|
|
3126
3210
|
jump(guild, num) {
|
|
3127
3211
|
return __privateMethod(this, _getQueue, getQueue_fn).call(this, guild).jump(num);
|
|
3128
3212
|
}
|
|
3129
3213
|
/**
|
|
3130
|
-
* Set the repeat mode of the guild queue
|
|
3214
|
+
* Set the repeat mode of the guild queue.
|
|
3131
3215
|
* Toggle mode `(Disabled -> Song -> Queue -> Disabled ->...)` if `mode` is `undefined`
|
|
3132
|
-
*
|
|
3133
|
-
* @param {RepeatMode?} [mode] The repeat modes (toggle if `undefined`)
|
|
3134
|
-
* @returns {RepeatMode} The new repeat mode
|
|
3216
|
+
*
|
|
3135
3217
|
* @example
|
|
3218
|
+
* ```ts
|
|
3136
3219
|
* client.on('message', (message) => {
|
|
3137
3220
|
* if (!message.content.startsWith(config.prefix)) return;
|
|
3138
3221
|
* const args = message.content.slice(config.prefix.length).trim().split(/ +/g);
|
|
@@ -3143,7 +3226,9 @@ ${e.message}`;
|
|
|
3143
3226
|
* message.channel.send("Set repeat mode to `" + mode + "`");
|
|
3144
3227
|
* }
|
|
3145
3228
|
* });
|
|
3229
|
+
* ```ts
|
|
3146
3230
|
* @example
|
|
3231
|
+
* ```ts
|
|
3147
3232
|
* const { RepeatMode } = require("distube");
|
|
3148
3233
|
* let mode;
|
|
3149
3234
|
* switch(distube.setRepeatMode(message, parseInt(args[0]))) {
|
|
@@ -3158,16 +3243,21 @@ ${e.message}`;
|
|
|
3158
3243
|
* break;
|
|
3159
3244
|
* }
|
|
3160
3245
|
* message.channel.send("Set repeat mode to `" + mode + "`");
|
|
3246
|
+
* ```ts
|
|
3247
|
+
*
|
|
3248
|
+
* @param guild - The type can be resolved to give a {@link Queue}
|
|
3249
|
+
* @param mode - The repeat modes (toggle if `undefined`)
|
|
3250
|
+
*
|
|
3251
|
+
* @returns The new repeat mode
|
|
3161
3252
|
*/
|
|
3162
3253
|
setRepeatMode(guild, mode) {
|
|
3163
3254
|
return __privateMethod(this, _getQueue, getQueue_fn).call(this, guild).setRepeatMode(mode);
|
|
3164
3255
|
}
|
|
3165
3256
|
/**
|
|
3166
3257
|
* Toggle autoplay mode
|
|
3167
|
-
*
|
|
3168
|
-
* @returns {boolean} Autoplay mode state
|
|
3169
|
-
* @throws {Error}
|
|
3258
|
+
*
|
|
3170
3259
|
* @example
|
|
3260
|
+
* ```ts
|
|
3171
3261
|
* client.on('message', (message) => {
|
|
3172
3262
|
* if (!message.content.startsWith(config.prefix)) return;
|
|
3173
3263
|
* const args = message.content.slice(config.prefix.length).trim().split(/ +/g);
|
|
@@ -3177,6 +3267,11 @@ ${e.message}`;
|
|
|
3177
3267
|
* message.channel.send("Set autoplay mode to `" + (mode ? "On" : "Off") + "`");
|
|
3178
3268
|
* }
|
|
3179
3269
|
* });
|
|
3270
|
+
* ```ts
|
|
3271
|
+
*
|
|
3272
|
+
* @param guild - The type can be resolved to give a {@link Queue}
|
|
3273
|
+
*
|
|
3274
|
+
* @returns Autoplay mode state
|
|
3180
3275
|
*/
|
|
3181
3276
|
toggleAutoplay(guild) {
|
|
3182
3277
|
const queue = __privateMethod(this, _getQueue, getQueue_fn).call(this, guild);
|
|
@@ -3185,18 +3280,19 @@ ${e.message}`;
|
|
|
3185
3280
|
}
|
|
3186
3281
|
/**
|
|
3187
3282
|
* Add related song to the queue
|
|
3188
|
-
*
|
|
3189
|
-
* @
|
|
3283
|
+
*
|
|
3284
|
+
* @param guild - The type can be resolved to give a {@link Queue}
|
|
3285
|
+
*
|
|
3286
|
+
* @returns The guild queue
|
|
3190
3287
|
*/
|
|
3191
3288
|
addRelatedSong(guild) {
|
|
3192
3289
|
return __privateMethod(this, _getQueue, getQueue_fn).call(this, guild).addRelatedSong();
|
|
3193
3290
|
}
|
|
3194
3291
|
/**
|
|
3195
3292
|
* Set the playing time to another position
|
|
3196
|
-
*
|
|
3197
|
-
* @param {number} time Time in seconds
|
|
3198
|
-
* @returns {Queue} Seeked queue
|
|
3293
|
+
*
|
|
3199
3294
|
* @example
|
|
3295
|
+
* ```ts
|
|
3200
3296
|
* client.on('message', message => {
|
|
3201
3297
|
* if (!message.content.startsWith(config.prefix)) return;
|
|
3202
3298
|
* const args = message.content.slice(config.prefix.length).trim().split(/ +/g);
|
|
@@ -3204,24 +3300,30 @@ ${e.message}`;
|
|
|
3204
3300
|
* if (command = 'seek')
|
|
3205
3301
|
* distube.seek(message, Number(args[0]));
|
|
3206
3302
|
* });
|
|
3303
|
+
* ```ts
|
|
3304
|
+
*
|
|
3305
|
+
* @param guild - The type can be resolved to give a {@link Queue}
|
|
3306
|
+
* @param time - Time in seconds
|
|
3307
|
+
*
|
|
3308
|
+
* @returns Seeked queue
|
|
3207
3309
|
*/
|
|
3208
3310
|
seek(guild, time) {
|
|
3209
3311
|
return __privateMethod(this, _getQueue, getQueue_fn).call(this, guild).seek(time);
|
|
3210
3312
|
}
|
|
3211
3313
|
/**
|
|
3212
3314
|
* Emit error event
|
|
3213
|
-
*
|
|
3214
|
-
* @param
|
|
3215
|
-
* @
|
|
3315
|
+
*
|
|
3316
|
+
* @param error - error
|
|
3317
|
+
* @param channel - Text channel where the error is encountered.
|
|
3216
3318
|
*/
|
|
3217
3319
|
emitError(error, channel) {
|
|
3218
|
-
if (this.listeners("error").length) {
|
|
3219
|
-
this.emit("error"
|
|
3320
|
+
if (this.listeners("error" /* ERROR */).length) {
|
|
3321
|
+
this.emit("error" /* ERROR */, channel, error);
|
|
3220
3322
|
} else {
|
|
3221
3323
|
console.error(error);
|
|
3222
3324
|
console.warn("Unhandled 'error' event.");
|
|
3223
3325
|
console.warn(
|
|
3224
|
-
"See: https://distube.js.org
|
|
3326
|
+
"See: https://distube.js.org/classes/DisTube.html#error and https://nodejs.org/api/events.html#events_error_events"
|
|
3225
3327
|
);
|
|
3226
3328
|
}
|
|
3227
3329
|
}
|
|
@@ -3264,6 +3366,7 @@ var DisTube = _DisTube;
|
|
|
3264
3366
|
Song,
|
|
3265
3367
|
StreamType,
|
|
3266
3368
|
TaskQueue,
|
|
3369
|
+
checkFFmpeg,
|
|
3267
3370
|
checkIntents,
|
|
3268
3371
|
checkInvalidKey,
|
|
3269
3372
|
chooseBestVideoFormat,
|