distube 3.0.0-beta.8 → 3.0.2

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.
Files changed (158) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +64 -51
  3. package/dist/DisTube.d.ts +522 -0
  4. package/dist/DisTube.d.ts.map +1 -0
  5. package/dist/DisTube.js +794 -0
  6. package/dist/DisTube.js.map +1 -0
  7. package/dist/constant.d.ts +130 -0
  8. package/dist/constant.d.ts.map +1 -0
  9. package/dist/constant.js +150 -0
  10. package/dist/constant.js.map +1 -0
  11. package/dist/core/DisTubeBase.d.ts +55 -0
  12. package/dist/core/DisTubeBase.d.ts.map +1 -0
  13. package/dist/core/DisTubeBase.js +76 -0
  14. package/dist/core/DisTubeBase.js.map +1 -0
  15. package/dist/core/DisTubeHandler.d.ts +95 -0
  16. package/dist/core/DisTubeHandler.d.ts.map +1 -0
  17. package/dist/core/DisTubeHandler.js +337 -0
  18. package/dist/core/DisTubeHandler.js.map +1 -0
  19. package/dist/core/DisTubeOptions.d.ts +26 -0
  20. package/dist/core/DisTubeOptions.d.ts.map +1 -0
  21. package/dist/core/DisTubeOptions.js +93 -0
  22. package/dist/core/DisTubeOptions.js.map +1 -0
  23. package/dist/core/DisTubeStream.d.ts +52 -0
  24. package/dist/core/DisTubeStream.d.ts.map +1 -0
  25. package/dist/core/DisTubeStream.js +111 -0
  26. package/dist/core/DisTubeStream.js.map +1 -0
  27. package/dist/core/index.d.ts +7 -0
  28. package/dist/core/index.d.ts.map +1 -0
  29. package/dist/core/index.js +19 -0
  30. package/dist/core/index.js.map +1 -0
  31. package/dist/core/manager/BaseManager.d.ts +18 -0
  32. package/dist/core/manager/BaseManager.d.ts.map +1 -0
  33. package/dist/core/manager/BaseManager.js +44 -0
  34. package/dist/core/manager/BaseManager.js.map +1 -0
  35. package/dist/core/manager/QueueManager.d.ts +60 -0
  36. package/dist/core/manager/QueueManager.d.ts.map +1 -0
  37. package/dist/core/manager/QueueManager.js +202 -0
  38. package/dist/core/manager/QueueManager.js.map +1 -0
  39. package/dist/core/manager/index.d.ts +3 -0
  40. package/dist/core/manager/index.d.ts.map +1 -0
  41. package/dist/core/manager/index.js +15 -0
  42. package/dist/core/manager/index.js.map +1 -0
  43. package/dist/core/voice/DJSAdapter.d.ts +4 -0
  44. package/dist/core/voice/DJSAdapter.d.ts.map +1 -0
  45. package/dist/core/voice/DJSAdapter.js +61 -0
  46. package/dist/core/voice/DJSAdapter.js.map +1 -0
  47. package/dist/core/voice/DisTubeVoice.d.ts +85 -0
  48. package/dist/core/voice/DisTubeVoice.d.ts.map +1 -0
  49. package/dist/core/voice/DisTubeVoice.js +246 -0
  50. package/dist/core/voice/DisTubeVoice.js.map +1 -0
  51. package/dist/core/voice/DisTubeVoiceManager.d.ts +41 -0
  52. package/dist/core/voice/DisTubeVoiceManager.d.ts.map +1 -0
  53. package/dist/core/voice/DisTubeVoiceManager.js +67 -0
  54. package/dist/core/voice/DisTubeVoiceManager.js.map +1 -0
  55. package/dist/core/voice/index.d.ts +4 -0
  56. package/dist/core/voice/index.d.ts.map +1 -0
  57. package/dist/core/voice/index.js +16 -0
  58. package/dist/core/voice/index.js.map +1 -0
  59. package/dist/index.d.ts +8 -0
  60. package/dist/index.d.ts.map +1 -0
  61. package/dist/index.js +23 -0
  62. package/dist/index.js.map +1 -0
  63. package/dist/plugin/http.d.ts +8 -0
  64. package/dist/plugin/http.d.ts.map +1 -0
  65. package/dist/plugin/http.js +20 -0
  66. package/dist/plugin/http.js.map +1 -0
  67. package/dist/plugin/https.d.ts +14 -0
  68. package/dist/plugin/https.d.ts.map +1 -0
  69. package/dist/plugin/https.js +50 -0
  70. package/dist/plugin/https.js.map +1 -0
  71. package/dist/plugin/index.d.ts +4 -0
  72. package/dist/plugin/index.d.ts.map +1 -0
  73. package/dist/plugin/index.js +16 -0
  74. package/dist/plugin/index.js.map +1 -0
  75. package/dist/plugin/youtube-dl.d.ts +11 -0
  76. package/dist/plugin/youtube-dl.d.ts.map +1 -0
  77. package/dist/plugin/youtube-dl.js +75 -0
  78. package/dist/plugin/youtube-dl.js.map +1 -0
  79. package/dist/struct/CustomPlugin.d.ts +27 -0
  80. package/dist/struct/CustomPlugin.d.ts.map +1 -0
  81. package/dist/struct/CustomPlugin.js +35 -0
  82. package/dist/struct/CustomPlugin.js.map +1 -0
  83. package/dist/struct/DisTubeError.d.ts +56 -0
  84. package/dist/struct/DisTubeError.d.ts.map +1 -0
  85. package/dist/struct/DisTubeError.js +75 -0
  86. package/dist/struct/DisTubeError.js.map +1 -0
  87. package/dist/struct/ExtractorPlugin.d.ts +29 -0
  88. package/dist/struct/ExtractorPlugin.d.ts.map +1 -0
  89. package/dist/struct/ExtractorPlugin.js +32 -0
  90. package/dist/struct/ExtractorPlugin.js.map +1 -0
  91. package/dist/struct/Playlist.d.ts +42 -0
  92. package/dist/struct/Playlist.d.ts.map +1 -0
  93. package/dist/struct/Playlist.js +104 -0
  94. package/dist/struct/Playlist.js.map +1 -0
  95. package/dist/struct/Plugin.d.ts +82 -0
  96. package/dist/struct/Plugin.d.ts.map +1 -0
  97. package/dist/struct/Plugin.js +108 -0
  98. package/dist/struct/Plugin.js.map +1 -0
  99. package/dist/struct/Queue.d.ts +217 -0
  100. package/dist/struct/Queue.d.ts.map +1 -0
  101. package/dist/struct/Queue.js +481 -0
  102. package/dist/struct/Queue.js.map +1 -0
  103. package/dist/struct/SearchResult.d.ts +28 -0
  104. package/dist/struct/SearchResult.d.ts.map +1 -0
  105. package/dist/struct/SearchResult.js +79 -0
  106. package/dist/struct/SearchResult.js.map +1 -0
  107. package/dist/struct/Song.d.ts +68 -0
  108. package/dist/struct/Song.d.ts.map +1 -0
  109. package/dist/struct/Song.js +229 -0
  110. package/dist/struct/Song.js.map +1 -0
  111. package/dist/struct/TaskQueue.d.ts +33 -0
  112. package/dist/struct/TaskQueue.d.ts.map +1 -0
  113. package/dist/struct/TaskQueue.js +58 -0
  114. package/dist/struct/TaskQueue.js.map +1 -0
  115. package/dist/struct/index.d.ts +10 -0
  116. package/dist/struct/index.d.ts.map +1 -0
  117. package/dist/struct/index.js +22 -0
  118. package/dist/struct/index.js.map +1 -0
  119. package/dist/tsconfig.tsbuildinfo +1 -0
  120. package/dist/type.d.ts +159 -0
  121. package/dist/type.d.ts.map +1 -0
  122. package/dist/type.js +3 -0
  123. package/dist/type.js.map +1 -0
  124. package/dist/util.d.ts +47 -0
  125. package/dist/util.d.ts.map +1 -0
  126. package/dist/util.js +205 -0
  127. package/dist/util.js.map +1 -0
  128. package/package.json +88 -62
  129. package/src/DisTube.js +0 -851
  130. package/src/DisTubeBase.js +0 -39
  131. package/src/DisTubeHandler.js +0 -440
  132. package/src/DisTubeOptions.js +0 -82
  133. package/src/Filter.js +0 -36
  134. package/src/Playlist.js +0 -75
  135. package/src/Plugin/CustomPlugin.js +0 -26
  136. package/src/Plugin/ExtractorPlugin.js +0 -25
  137. package/src/Plugin/Plugin.js +0 -36
  138. package/src/Plugin/http.js +0 -27
  139. package/src/Plugin/https.js +0 -27
  140. package/src/Queue.js +0 -340
  141. package/src/SearchResult.js +0 -57
  142. package/src/Song.js +0 -169
  143. package/src/util.js +0 -65
  144. package/typings/DisTube.d.ts +0 -518
  145. package/typings/DisTubeBase.d.ts +0 -31
  146. package/typings/DisTubeHandler.d.ts +0 -130
  147. package/typings/DisTubeOptions.d.ts +0 -5
  148. package/typings/Filter.d.ts +0 -83
  149. package/typings/Playlist.d.ts +0 -58
  150. package/typings/Plugin/CustomPlugin.d.ts +0 -21
  151. package/typings/Plugin/ExtractorPlugin.d.ts +0 -20
  152. package/typings/Plugin/Plugin.d.ts +0 -31
  153. package/typings/Plugin/http.d.ts +0 -4
  154. package/typings/Plugin/https.d.ts +0 -4
  155. package/typings/Queue.d.ts +0 -227
  156. package/typings/SearchResult.d.ts +0 -51
  157. package/typings/Song.d.ts +0 -153
  158. package/typings/util.d.ts +0 -6
package/src/DisTube.js DELETED
@@ -1,851 +0,0 @@
1
- const ytsr = require("@distube/ytsr"),
2
- ytpl = require("@distube/ytpl"),
3
- { EventEmitter } = require("events"),
4
- Queue = require("./Queue"),
5
- SearchResult = require("./SearchResult"),
6
- Playlist = require("./Playlist"),
7
- { isVoiceChannelEmpty } = require("./util"),
8
- Discord = require("discord.js"),
9
- DisTubeOption = require("./DisTubeOptions"),
10
- DisTubeHandler = require("./DisTubeHandler"),
11
- Song = require("./Song"),
12
- // eslint-disable-next-line no-unused-vars
13
- Plugin = require("./Plugin/Plugin"),
14
- CustomPlugin = require("./Plugin/CustomPlugin"),
15
- ExtractorPlugin = require("./Plugin/ExtractorPlugin");
16
-
17
- /**
18
- * FFmpeg Filters
19
- * ```
20
- * {
21
- * "Filter Name": "Filter Value",
22
- * "bassboost": "bass=g=10,dynaudnorm=f=150:g=15"
23
- * }
24
- * ```
25
- * @typedef {Object.<string, string>} Filters
26
- * @see {@link DefaultFilters}
27
- */
28
-
29
- /**
30
- * DisTube options.
31
- * @typedef {Object} DisTubeOptions
32
- * @prop {Array<Plugin>} [plugins] DisTube plugins.
33
- * @prop {boolean} [emitNewSongOnly=false] If `true`, {@link DisTube#event:playSong} will not be emitted when looping a song or next song is the same as the previous one
34
- * @prop {boolean} [leaveOnEmpty=true] Whether or not leaving voice channel if channel is empty in 60s. (Avoid accident leaving)
35
- * @prop {boolean} [leaveOnFinish=false] Whether or not leaving voice channel when the queue ends.
36
- * @prop {boolean} [leaveOnStop=true] Whether or not leaving voice channel after using {@link DisTube#stop|stop()} function.
37
- * @prop {boolean} [savePreviousSongs=true] Whether or not saving the previous songs of the queue and enable {@link DisTube#previous|previous()} method
38
- * @prop {number} [searchSongs=0] Limit of search results emits in {@link DisTube#event:searchResult} event when {@link DisTube#play|play()} method executed. If `searchSongs <= 1`, play the first result
39
- * @prop {string} [youtubeCookie=null] YouTube cookies. Read how to get it in {@link https://github.com/fent/node-ytdl-core/blob/997efdd5dd9063363f6ef668bb364e83970756e7/example/cookies.js#L6-L12|YTDL's Example}
40
- * @prop {string} [youtubeIdentityToken=null] If not given; ytdl-core will try to find it. You can find this by going to a video's watch page; viewing the source; and searching for "ID_TOKEN".
41
- * @prop {boolean} [youtubeDL=true] Whether or not using youtube-dl.
42
- * @prop {boolean} [updateYouTubeDL=true] Whether or not updating youtube-dl automatically.
43
- * @prop {Filters} [customFilters] Override {@link DefaultFilters} or add more ffmpeg filters. Example=`{ "Filter name"="Filter value"; "8d"="apulsator=hz=0.075" }`
44
- * @prop {Object} [ytdlOptions] `ytdl-core` options
45
- * @prop {number} [searchCooldown=60] Built-in search cooldown in seconds (When searchSongs is bigger than 0)
46
- * @prop {number} [emptyCooldown=60] Built-in leave on empty cooldown in seconds (When leaveOnEmpty is true)
47
- * @prop {boolean} [nsfw=false] Whether or not playing age-restricted content in non-NSFW channel
48
- */
49
-
50
- /**
51
- * DisTube class
52
- * @extends EventEmitter
53
- */
54
- class DisTube extends EventEmitter {
55
- /**
56
- * DisTube's current version.
57
- * @type {string}
58
- */
59
- get version() { return require("../package.json").version }
60
- static get version() { return require("../package.json").version }
61
- /**
62
- * Create a new DisTube class.
63
- * @param {Discord.Client} client Discord.JS client
64
- * @param {DisTubeOptions} [otp] Custom DisTube options
65
- * @example
66
- * const Discord = require('discord.js'),
67
- * DisTube = require('distube'),
68
- * client = new Discord.Client();
69
- * // Create a new DisTube
70
- * const distube = new DisTube(client, { searchSongs: 10 });
71
- * // client.DisTube = distube // make it access easily
72
- * client.login("Your Discord Bot Token")
73
- */
74
- constructor(client, otp = {}) {
75
- super();
76
- if (!client || typeof client.user === "undefined") throw new TypeError("Invalid Discord.Client");
77
-
78
- /**
79
- * Discord.JS client
80
- * @type {Discord.Client}
81
- */
82
- this.client = client;
83
-
84
- /**
85
- * Collection of guild queues
86
- * @type {Discord.Collection<string, Queue>}
87
- */
88
- this.guildQueues = new Discord.Collection();
89
-
90
- /**
91
- * DisTube options
92
- * @type {DisTubeOptions}
93
- */
94
- this.options = new DisTubeOption(otp);
95
-
96
- /**
97
- * DisTube's Handler
98
- * @type {DisTubeHandler}
99
- * @private
100
- */
101
- this.handler = new DisTubeHandler(this);
102
-
103
- /**
104
- * DisTube filters
105
- * @type {Filters}
106
- */
107
- this.filters = require("./Filter");
108
- if (typeof this.options.customFilters === "object") Object.assign(this.filters, this.options.customFilters);
109
-
110
- if (this.options.leaveOnEmpty) {
111
- client.on("voiceStateUpdate", oldState => {
112
- const queue = this.guildQueues.find(gQueue => gQueue.connection && gQueue.connection.channel.id === oldState.channelID);
113
- if (!queue) return;
114
- if (oldState?.channel) {
115
- if (queue.emptyTimeout) {
116
- clearTimeout(queue.emptyTimeout);
117
- queue.emptyTimeout = null;
118
- }
119
- if (isVoiceChannelEmpty(queue)) {
120
- queue.emptyTimeout = setTimeout(() => {
121
- const guildID = queue.connection.channel.guild.id;
122
- if (this.guildQueues.has(guildID) && isVoiceChannelEmpty(queue)) {
123
- queue.connection.channel.leave();
124
- this.emit("empty", queue);
125
- this._deleteQueue(queue);
126
- }
127
- }, this.options.emptyCooldown * 1000);
128
- }
129
- }
130
- });
131
- }
132
-
133
- if (this.options.updateYouTubeDL) {
134
- require("@distube/youtube-dl/src/download")()
135
- .then(version => console.log(`[DisTube] Updated youtube-dl to ${version}!`))
136
- .catch(console.error)
137
- .catch(() => console.log("[DisTube] Unable to update youtube-dl, using default version."));
138
- }
139
-
140
- // Default plugin
141
- const HTTPPlugin = require("./Plugin/http"),
142
- HTTPSPlugin = require("./Plugin/https");
143
- this.options.plugins.push(new HTTPPlugin(), new HTTPSPlugin());
144
- this.options.plugins.map(p => p.init(this));
145
- /**
146
- * Extractor Plugins
147
- * @type {Array<ExtractorPlugin>}
148
- * @private
149
- */
150
- this.extractorPlugins = this.options.plugins.filter(p => p.type === "extractor");
151
- /**
152
- * Custom Plugins
153
- * @type {Array<CustomPlugin>}
154
- * @private
155
- */
156
- this.customPlugins = this.options.plugins.filter(p => p.type === "custom");
157
- }
158
-
159
- /**
160
- * Play / add a song or playlist from url. Search and play a song if it is not a valid url.
161
- * Emit {@link DisTube#addList}, {@link DisTube#addSong} or {@link DisTube#playSong} after executing
162
- * @returns {Promise<void>}
163
- * @param {Discord.Message} message A message from guild channel
164
- * @param {string|Song|SearchResult|Playlist} song YouTube url | Search string | {@link Song} | {@link SearchResult} | {@link Playlist}
165
- * @param {boolean} skip Whether or not skipping the playing song
166
- * @example
167
- * client.on('message', (message) => {
168
- * if (!message.content.startsWith(config.prefix)) return;
169
- * const args = message.content.slice(config.prefix.length).trim().split(/ +/g);
170
- * const command = args.shift();
171
- * if (command == "play")
172
- * distube.play(message, args.join(" "));
173
- * });
174
- */
175
- async play(message, song, skip = false) {
176
- if (!song) return;
177
- if (!(message instanceof Discord.Message)) throw new TypeError("message is not a Discord.Message.");
178
- if (typeof skip !== "boolean") throw new TypeError("skip is not a boolean");
179
- try {
180
- const voiceChannel = message.member.voice.channel;
181
- if (!voiceChannel) throw new Error("User is not in any voice channel.");
182
- await this.playVoiceChannel(voiceChannel, song, {
183
- member: message.member,
184
- textChannel: message.channel,
185
- skip,
186
- });
187
- } catch (e) {
188
- e.name = "PlayError";
189
- e.message = `${song?.url || song}\n${e.message}`;
190
- this.emitError(message.channel, e);
191
- }
192
- }
193
-
194
- /**
195
- * Play / add a song or playlist from url. Search and play a song if it is not a valid url.
196
- * Emit {@link DisTube#addList}, {@link DisTube#addSong} or {@link DisTube#playSong} after executing
197
- * @returns {Promise<void>}
198
- * @param {Discord.VoiceChannel|Discord.StageChannel} voiceChannel The voice channel will be joined
199
- * @param {string|Song|SearchResult|Playlist} song YouTube url | Search string | {@link Song} | {@link SearchResult} | {@link Playlist}
200
- * @param {Object} [options] Optional options
201
- * @param {Discord.GuildMember} [options.member] Requested user (default is your bot)
202
- * @param {Discord.TextChannel} [options.textChannel] Default {@link Queue#textChannel} (if the queue wasn't created)
203
- * @param {boolean} [options.skip] Skip the playing song (if exists)
204
- */
205
- async playVoiceChannel(voiceChannel, song, options = {}) {
206
- if (!["voice", "stage"].includes(voiceChannel?.type)) {
207
- throw new TypeError("voiceChannel is not a Discord.VoiceChannel or a Discord.StageChannel.");
208
- }
209
- const { textChannel, member, skip } = Object.assign({
210
- member: voiceChannel.guild.me,
211
- skip: false,
212
- }, options);
213
- try {
214
- if (typeof song === "string") {
215
- for (const plugin of this.customPlugins) {
216
- if (await plugin.validate(song)) {
217
- await plugin.play(voiceChannel, song, member, textChannel, skip);
218
- return;
219
- }
220
- }
221
- }
222
- if (song instanceof SearchResult && song.type === "playlist") song = song.url;
223
- if (ytpl.validateID(song)) song = await this.handler.resolvePlaylist(member, song);
224
- song = await this.handler.resolveSong(member, song);
225
- if (!song) return;
226
- if (song instanceof Playlist) await this.handler.handlePlaylist(voiceChannel, song, textChannel, skip);
227
- else if (!this.options.nsfw && song.age_restricted && !textChannel?.nsfw) {
228
- throw new Error("Cannot play age-restricted content in non-NSFW channel.");
229
- } else {
230
- let queue = this.getQueue(voiceChannel);
231
- if (queue) {
232
- queue.addToQueue(song, skip);
233
- if (skip) queue.skip();
234
- else this.emit("addSong", queue, song);
235
- } else {
236
- queue = await this._newQueue(voiceChannel, song, textChannel);
237
- if (queue instanceof Queue) this.emit("playSong", queue, song);
238
- }
239
- }
240
- } catch (e) {
241
- e.name = "PlayError";
242
- e.message = `${song?.url || song}\n${e.message}`;
243
- this.emitError(textChannel, e);
244
- }
245
- }
246
-
247
- /**
248
- * Skip the playing song and play a song or playlist
249
- * @returns {Promise<void>}
250
- * @param {Discord.Message} message A message from guild channel
251
- * @param {string|Song|SearchResult|Playlist} song YouTube url | Search string | {@link Song} | {@link SearchResult} | {@link Playlist}
252
- * @example
253
- * client.on('message', (message) => {
254
- * if (!message.content.startsWith(config.prefix)) return;
255
- * const args = message.content.slice(config.prefix.length).trim().split(/ +/g);
256
- * const command = args.shift();
257
- * if (command == "playSkip")
258
- * distube.playSkip(message, args.join(" "));
259
- * });
260
- */
261
- playSkip(message, song) {
262
- return this.play(message, song, true);
263
- }
264
-
265
- /**
266
- * Play or add array of video urls.
267
- * {@link DisTube#event:playList} or {@link DisTube#event:addList} will be emitted
268
- * with `playlist`'s properties include `properties` parameter's properties such as
269
- * `user`, `songs`, `duration`, `formattedDuration`, `thumbnail` like {@link Playlist}
270
- * @returns {Promise<void>}
271
- * @param {Discord.Message} message A message from guild channel
272
- * @param {Array<string|Song|SearchResult>} songs Array of url, Song or SearchResult
273
- * @param {Object} [properties={}] Additional properties such as `name`
274
- * @param {boolean} [playSkip=false] Whether or not play this playlist instantly
275
- * @param {boolean} [parallel=true] Whether or not fetch the songs in parallel
276
- * @example
277
- * let songs = ["https://www.youtube.com/watch?v=xxx", "https://www.youtube.com/watch?v=yyy"];
278
- * distube.playCustomPlaylist(message, songs, { name: "My playlist name" });
279
- * // Fetching custom playlist sequentially (reduce lag for low specs)
280
- * distube.playCustomPlaylist(message, songs, { name: "My playlist name" }, false, false);
281
- */
282
- async playCustomPlaylist(message, songs, properties = {}, playSkip = false, parallel = true) {
283
- try {
284
- const playlist = this.handler.createCustomPlaylist(message, songs, properties, parallel);
285
- await this.handler.handlePlaylist(message, playlist, playSkip);
286
- } catch (e) {
287
- this.emitError(message.channel, e);
288
- }
289
- }
290
-
291
- /**
292
- * Search for a song.
293
- * You can customize how user answers instead of send a number.
294
- * Then use {@link DisTube#play|play(message, aResultFromSearch)} or {@link DisTube#playSkip|playSkip()} to play it.
295
- * @param {string} string The string search for
296
- * @param {Object} options Search options
297
- * @param {number} [options.limit=10] Limit the results
298
- * @param {'video'|'playlist'} [options.type='video'] Type of search (`video` or `playlist`).
299
- * @param {boolean} [options.safeSearch=false] Type of search (`video` or `playlist`).
300
- * @param {boolean} retried Retried?
301
- * @throws {Error}
302
- * @returns {Promise<Array<SearchResult>>} Array of results
303
- */
304
- async search(string, options = {}, retried = false) {
305
- const opts = Object.assign({ type: "video", limit: 10, safeSearch: false }, options);
306
- if (typeof opts.type !== "string" || !["video", "playlist"].includes(opts.type)) throw new Error("options.type must be 'video' or 'playlist'.");
307
- if (typeof opts.limit !== "number") throw new Error("options.limit must be a number");
308
- if (opts.limit < 1) throw new Error("option.limit must be bigger or equal to 1");
309
- if (typeof opts.safeSearch !== "boolean") throw new TypeError("options.safeSearch must be a boolean.");
310
-
311
- try {
312
- const search = await ytsr(string, opts);
313
- const results = search.items.map(i => new SearchResult(i));
314
- if (results.length === 0) throw Error("No result!");
315
- return results;
316
- } catch (e) {
317
- if (retried) throw e;
318
- return this.search(string, options, true);
319
- }
320
- }
321
-
322
- /**
323
- * Create a new guild queue
324
- * @private
325
- * @param {Discord.Message|Discord.VoiceChannel|Discord.StageChannel} message A message from guild channel | a voice channel
326
- * @param {Song|Array<Song>} song Song to play
327
- * @param {Discord.TextChannel} textChannel A text channel of the queue
328
- * @throws {Error}
329
- * @returns {Promise<Queue|true>} `true` if queue is not generated
330
- */
331
- _newQueue(message, song, textChannel = message?.channel) {
332
- const voice = message?.member?.voice?.channel || message;
333
- if (!voice || voice instanceof Discord.Message) throw new Error("User is not in a voice channel.");
334
- if (!["voice", "stage"].includes(voice?.type)) {
335
- throw new TypeError("User is not in a Discord.VoiceChannel or a Discord.StageChannel.");
336
- }
337
- const queue = new Queue(this, message, song, textChannel);
338
- this.emit("initQueue", queue);
339
- this.guildQueues.set(message.guild.id, queue);
340
- return this.handler.joinVoiceChannel(queue, voice);
341
- }
342
-
343
- /**
344
- * Delete a guild queue
345
- * @private
346
- * @param {Discord.Snowflake|Discord.Message|Queue} queue A message from guild channel | Queue
347
- */
348
- _deleteQueue(queue) {
349
- if (!(queue instanceof Queue)) queue = this.getQueue(queue);
350
- if (!queue) return;
351
- this.emit("deleteQueue", queue);
352
- if (queue.dispatcher) try { queue.dispatcher.destroy() } catch { }
353
- if (queue.stream) try { queue.stream.destroy() } catch { }
354
- this.guildQueues.delete(queue.id);
355
- }
356
-
357
- /**
358
- * Get the guild queue
359
- * @param {Discord.Snowflake|Discord.Message|Discord.VoiceChannel|Discord.StageChannel} message A guild ID | a message from guild channel | a voice channel.
360
- * @returns {Queue} The guild queue
361
- * @throws {Error}
362
- * @example
363
- * client.on('message', (message) => {
364
- * if (!message.content.startsWith(config.prefix)) return;
365
- * const args = message.content.slice(config.prefix.length).trim().split(/ +/g);
366
- * const command = args.shift();
367
- * if (command == "queue") {
368
- * const queue = distube.getQueue(message);
369
- * message.channel.send('Current queue:\n' + queue.songs.map((song, id) =>
370
- * `**${id+1}**. [${song.name}](${song.url}) - \`${song.formattedDuration}\``
371
- * ).join("\n"));
372
- * }
373
- * });
374
- */
375
- getQueue(message) {
376
- const guildID = message?.guild?.id || message;
377
- if (typeof guildID !== "string") throw TypeError("Parameter should be a Discord.Message, a Discord.VoiceChannel or a server ID!");
378
- return this.guildQueues.get(guildID);
379
- }
380
-
381
- /**
382
- * Pause the guild stream
383
- * @param {Discord.Snowflake|Discord.Message} message A message from guild channel
384
- * @returns {Queue} The guild queue
385
- * @throws {Error}
386
- */
387
- pause(message) {
388
- const queue = this.getQueue(message);
389
- if (!queue) throw new Error("Cannot find the playing queue.");
390
- return queue.pause();
391
- }
392
-
393
- /**
394
- * Resume the guild stream
395
- * @param {Discord.Snowflake|Discord.Message} message A message from guild channel
396
- * @returns {Queue} The guild queue
397
- * @throws {Error}
398
- */
399
- resume(message) {
400
- const queue = this.getQueue(message);
401
- if (!queue) throw new Error("Cannot find the playing queue.");
402
- return queue.resume();
403
- }
404
-
405
- /**
406
- * Stop the guild stream
407
- * @param {Discord.Snowflake|Discord.Message} message A message from guild channel or Queue
408
- * @throws {Error}
409
- * @example
410
- * client.on('message', (message) => {
411
- * if (!message.content.startsWith(config.prefix)) return;
412
- * const args = message.content.slice(config.prefix.length).trim().split(/ +/g);
413
- * const command = args.shift();
414
- * if (command == "stop") {
415
- * distube.stop(message);
416
- * message.channel.send("Stopped the queue!");
417
- * }
418
- * });
419
- */
420
- stop(message) {
421
- const queue = this.getQueue(message);
422
- if (!queue) throw new Error("Cannot find the playing queue.");
423
- queue.stop();
424
- }
425
-
426
- /**
427
- * Set the guild stream's volume
428
- * @param {Discord.Snowflake|Discord.Message} message A message from guild channel
429
- * @param {number} percent The percentage of volume you want to set
430
- * @returns {Queue} The guild queue
431
- * @throws {Error}
432
- * @example
433
- * client.on('message', (message) => {
434
- * if (!message.content.startsWith(config.prefix)) return;
435
- * const args = message.content.slice(config.prefix.length).trim().split(/ +/g);
436
- * const command = args.shift();
437
- * if (command == "volume")
438
- * distube.setVolume(message, args[0]);
439
- * });
440
- */
441
- setVolume(message, percent) {
442
- const queue = this.getQueue(message);
443
- if (!queue) throw new Error("Cannot find the playing queue.");
444
- return queue.setVolume(percent);
445
- }
446
-
447
- /**
448
- * Skip the playing song
449
- *
450
- * @param {Discord.Snowflake|Discord.Message} message A message from guild channel
451
- * @returns {Queue} The guild queue
452
- * @throws {Error}
453
- * @example
454
- * client.on('message', (message) => {
455
- * if (!message.content.startsWith(config.prefix)) return;
456
- * const args = message.content.slice(config.prefix.length).trim().split(/ +/g);
457
- * const command = args.shift();
458
- * if (command == "skip")
459
- * distube.skip(message);
460
- * });
461
- */
462
- skip(message) {
463
- const queue = this.getQueue(message);
464
- if (!queue) throw new Error("Cannot find the playing queue.");
465
- return queue.skip();
466
- }
467
-
468
- /**
469
- * Play the previous song
470
- *
471
- * @param {Discord.Snowflake|Discord.Message} message A message from guild channel
472
- * @returns {Queue} The guild queue
473
- * @throws {Error}
474
- * @example
475
- * client.on('message', (message) => {
476
- * if (!message.content.startsWith(config.prefix)) return;
477
- * const args = message.content.slice(config.prefix.length).trim().split(/ +/g);
478
- * const command = args.shift();
479
- * if (command == "previous")
480
- * distube.previous(message);
481
- * });
482
- */
483
- previous(message) {
484
- if (!this.options.savePreviousSongs) throw new Error("Disabled");
485
- const queue = this.getQueue(message);
486
- if (!queue) throw new Error("Cannot find the playing queue.");
487
- return queue.previous();
488
- }
489
-
490
- /**
491
- * Shuffle the guild queue songs
492
- * @param {Discord.Snowflake|Discord.Message} message A message from guild channel
493
- * @returns {Queue} The guild queue
494
- * @example
495
- * client.on('message', (message) => {
496
- * if (!message.content.startsWith(config.prefix)) return;
497
- * const args = message.content.slice(config.prefix.length).trim().split(/ +/g);
498
- * const command = args.shift();
499
- * if (command == "shuffle")
500
- * distube.shuffle(message);
501
- * });
502
- */
503
- shuffle(message) {
504
- const queue = this.getQueue(message);
505
- if (!queue) throw new Error("Cannot find the playing queue.");
506
- return queue.shuffle();
507
- }
508
-
509
- /**
510
- * Jump to the song number in the queue.
511
- * The next one is 1, 2,...
512
- * The previous one is -1, -2,...
513
- * @param {Discord.Snowflake|Discord.Message} message A message from guild channel
514
- * @param {number} num The song number to play
515
- * @returns {Queue} The guild queue
516
- * @throws {InvalidSong} if `num` is invalid number (0 < num < {@link Queue#songs}.length)
517
- * @example
518
- * client.on('message', (message) => {
519
- * if (!message.content.startsWith(config.prefix)) return;
520
- * const args = message.content.slice(config.prefix.length).trim().split(/ +/g);
521
- * const command = args.shift();
522
- * if (command == "jump")
523
- * distube.jump(message, parseInt(args[0]))
524
- * .catch(err => message.channel.send("Invalid song number."));
525
- * });
526
- */
527
- jump(message, num) {
528
- const queue = this.getQueue(message);
529
- if (!queue) throw new Error("Cannot find the playing queue.");
530
- return queue.jump(num);
531
- }
532
-
533
- /**
534
- * Set the repeat mode of the guild queue.
535
- * Turn off if repeat mode is the same value as new mode.
536
- * Toggle mode: `mode = null` `(0 -> 1 -> 2 -> 0...)`
537
- *
538
- * @param {Discord.Snowflake|Discord.Message} message A message from guild channel
539
- * @param {number} mode The repeat modes `(0: disabled, 1: Repeat a song, 2: Repeat all the queue)`
540
- * @returns {number} The new repeat mode
541
- *
542
- * @example
543
- * client.on('message', (message) => {
544
- * if (!message.content.startsWith(config.prefix)) return;
545
- * const args = message.content.slice(config.prefix.length).trim().split(/ +/g);
546
- * const command = args.shift();
547
- * if (command == "repeat") {
548
- * let mode = distube.setRepeatMode(message, parseInt(args[0]));
549
- * mode = mode ? mode == 2 ? "Repeat queue" : "Repeat song" : "Off";
550
- * message.channel.send("Set repeat mode to `" + mode + "`");
551
- * }
552
- * });
553
- */
554
- setRepeatMode(message, mode = null) {
555
- const queue = this.getQueue(message);
556
- if (!queue) throw new Error("Cannot find the playing queue.");
557
- return queue.setRepeatMode(mode);
558
- }
559
-
560
- /**
561
- * Toggle autoplay mode
562
- * @param {Discord.Snowflake|Discord.Message} message A message from guild channel
563
- * @returns {boolean} Autoplay mode state
564
- * @throws {Error}
565
- * @example
566
- * client.on('message', (message) => {
567
- * if (!message.content.startsWith(config.prefix)) return;
568
- * const args = message.content.slice(config.prefix.length).trim().split(/ +/g);
569
- * const command = args.shift();
570
- * if (command == "autoplay") {
571
- * let mode = distube.toggleAutoplay(message);
572
- * message.channel.send("Set autoplay mode to `" + (mode ? "On" : "Off") + "`");
573
- * }
574
- * });
575
- */
576
- toggleAutoplay(message) {
577
- const queue = this.getQueue(message);
578
- if (!queue) throw new Error("Cannot find the playing queue.");
579
- return queue.toggleAutoplay();
580
- }
581
-
582
- /**
583
- * Whether or not a guild is playing music.
584
- * @param {Discord.Snowflake|Discord.Message} message A message from guild channel to check
585
- * @returns {boolean} Whether or not the guild is playing song(s)
586
- */
587
- isPlaying(message) {
588
- const queue = this.getQueue(message);
589
- return queue ? queue.playing || !queue.paused : false;
590
- }
591
-
592
- /**
593
- * Whether or not the guild queue is paused
594
- * @param {Discord.Snowflake|Discord.Message} message A message from guild channel to check
595
- * @returns {boolean} Whether or not the guild queue is paused
596
- */
597
- isPaused(message) {
598
- const queue = this.getQueue(message);
599
- return queue ? queue.paused : false;
600
- }
601
-
602
- /**
603
- * Add related song to the queue
604
- * @param {Discord.Snowflake|Discord.Message} message A message from guild channel
605
- * @returns {Promise<Queue>} The guild queue
606
- */
607
- addRelatedVideo(message) {
608
- const queue = this.getQueue(message);
609
- if (!queue) throw new Error("Cannot find the playing queue.");
610
- return queue.addRelatedVideo();
611
- }
612
-
613
- /**
614
- * Enable or disable a filter of the queue.
615
- * Available filters: {@link Filters}
616
- *
617
- * @param {Discord.Snowflake|Discord.Message} message A message from guild channel
618
- * @param {string|false} filter A filter name, `false` to clear all the filters
619
- * @returns {Array<string>} Enabled filters.
620
- * @example
621
- * client.on('message', (message) => {
622
- * if (!message.content.startsWith(config.prefix)) return;
623
- * const args = message.content.slice(config.prefix.length).trim().split(/ +/g);
624
- * const command = args.shift();
625
- * if ([`3d`, `bassboost`, `echo`, `karaoke`, `nightcore`, `vaporwave`].includes(command)) {
626
- * let filter = distube.setFilter(message, command);
627
- * message.channel.send("Current queue filter: " + (filter.join(", ") || "Off"));
628
- * }
629
- * });
630
- */
631
- setFilter(message, filter) {
632
- const queue = this.getQueue(message);
633
- if (!queue) throw new Error("Cannot find the playing queue.");
634
- return queue.setFilter(filter);
635
- }
636
-
637
- /**
638
- * Set the playing time to another position
639
- * @param {Discord.Snowflake|Discord.Message} message A message from guild channel
640
- * @param {number} time Time in seconds
641
- * @returns {Queue}
642
- * @example
643
- * client.on('message', message => {
644
- * if (!message.content.startsWith(config.prefix)) return;
645
- * const args = message.content.slice(config.prefix.length).trim().split(/ +/g);
646
- * const command = args.shift();
647
- * if (command = 'seek')
648
- * distube.seek(message, Number(args[0]));
649
- * });
650
- */
651
- seek(message, time) {
652
- const queue = this.getQueue(message);
653
- if (!queue) throw new Error("Cannot find the playing queue.");
654
- return queue.seek(time);
655
- }
656
-
657
- /**
658
- * Emit error event
659
- * @param {Discord.TextChannel} channel Text channel where the error is encountered.
660
- * @param {Error} error error
661
- * @private
662
- */
663
- emitError(channel, error) {
664
- if (!channel || !(channel instanceof Discord.TextChannel)) {
665
- console.error(error);
666
- console.warn("This is logged because <Queue>.textChannel is null");
667
- } else if (this.listeners("error").length) this.emit("error", channel, error);
668
- else this.emit("error", error);
669
- }
670
- }
671
-
672
- DisTube.CustomPlugin = CustomPlugin;
673
- DisTube.ExtractorPlugin = ExtractorPlugin;
674
- DisTube.Playlist = require("./Playlist");
675
- DisTube.Song = Song;
676
- module.exports = DisTube;
677
-
678
- /**
679
- * Emitted after DisTube add a new playlist to the playing {@link Queue}
680
- *
681
- * @event DisTube#addList
682
- * @param {Queue} queue The guild queue
683
- * @param {Playlist} playlist Playlist info
684
- * @example
685
- * distube.on("addList", (queue, playlist) => queue.textChannel.send(
686
- * `Added \`${playlist.name}\` playlist (${playlist.songs.length} songs) to the queue!`
687
- * ));
688
- */
689
-
690
- /**
691
- * Emitted after DisTube add a new song to the playing {@link Queue}
692
- *
693
- * @event DisTube#addSong
694
- * @param {Queue} queue The guild queue
695
- * @param {Song} song Added song
696
- * @example
697
- * distube.on("addSong", (queue, song) => queue.textChannel.send(
698
- * `Added ${song.name} - \`${song.formattedDuration}\` to the queue by ${song.user}.`
699
- * ));
700
- */
701
-
702
- /**
703
- * Emitted when there is no user in VoiceChannel and {@link DisTubeOptions}.leaveOnEmpty is `true`.
704
- *
705
- * @event DisTube#empty
706
- * @param {Queue} queue The guild queue
707
- * @example
708
- * distube.on("empty", queue => queue.textChannel.send("Channel is empty. Leaving the channel"))
709
- */
710
-
711
- /**
712
- * Emitted when {@link DisTube} encounters an error.
713
- *
714
- * @event DisTube#error
715
- * @param {Discord.TextChannel} channel Text channel where the error is encountered.
716
- * @param {Error} error The error encountered
717
- * @example
718
- * distube.on("error", (channel, error) => channel.send(
719
- * "An error encountered: " + error
720
- * ));
721
- */
722
-
723
- /**
724
- * Emitted when there is no more song in the queue and {@link Queue#autoplay} is `false`.
725
- * DisTube will leave voice channel if {@link DisTubeOptions}.leaveOnFinish is `true`
726
- *
727
- * @event DisTube#finish
728
- * @param {Queue} queue The guild queue
729
- * @example
730
- * distube.on("finish", queue => queue.textChannel.send("No more song in queue"));
731
- */
732
-
733
- /**
734
- * Emitted when DisTube initialize a queue to change queue default properties.
735
- *
736
- * @event DisTube#initQueue
737
- * @param {Queue} queue The guild queue
738
- * @example
739
- * distube.on("initQueue", queue => {
740
- * queue.autoplay = false;
741
- * queue.volume = 100;
742
- * });
743
- */
744
-
745
- /**
746
- * Emitted when {@link Queue#autoplay} is `true`, the {@link Queue#songs} is empty and
747
- * DisTube cannot find related songs to play
748
- *
749
- * @event DisTube#noRelated
750
- * @param {Queue} queue The guild queue
751
- * @example
752
- * distube.on("noRelated", queue => queue.textChannel.send("Can't find related video to play. Stop playing music."));
753
- */
754
-
755
- /**
756
- * Emitted when DisTube play a song.
757
- * If {@link DisTubeOptions}.emitNewSongOnly is `true`, event is not emitted when looping a song or next song is the previous one
758
- *
759
- * @event DisTube#playSong
760
- * @param {Queue} queue The guild queue
761
- * @param {Song} song Playing song
762
- * @example
763
- * const status = (queue) => `Volume: \`${queue.volume}%\` | Loop: \`${queue.repeatMode ? queue.repeatMode == 2 ? "Server Queue" : "This Song" : "Off"}\` | Autoplay: \`${queue.autoplay ? "On" : "Off"}\``;
764
- * distube.on("playSong", (queue, song) => queue.textChannel.send(
765
- * `Playing \`${song.name}\` - \`${song.formattedDuration}\`\nRequested by: ${song.user}\n${status(queue)}`
766
- * ));
767
- */
768
-
769
- /**
770
- * Emitted when {@link DisTubeOptions|DisTubeOptions.searchSongs} bigger than 0
771
- * and DisTube cannot find any results for the query
772
- *
773
- * @event DisTube#searchNoResult
774
- * @param {Discord.Message} message A message called play method
775
- * @param {string} query The search query
776
- * @example
777
- * // DisTubeOptions.searchSongs > 0
778
- * distube.on("searchNoResult", (message, query) => message.channel.send(`No result found for ${query}!`));
779
- */
780
-
781
- /**
782
- * Emitted when {@link DisTubeOptions|DisTubeOptions.searchSongs} bigger than 0
783
- * and the search canceled due to user's next message is invalid number or timeout
784
- *
785
- * @event DisTube#searchCancel
786
- * @param {Discord.Message} message A message called play method
787
- * @param {string} query The search query
788
- * @example
789
- * // DisTubeOptions.searchSongs > 0
790
- * distube.on("searchCancel", (message) => message.channel.send(`Searching canceled`));
791
- */
792
-
793
- /**
794
- * Emitted when {@link DisTubeOptions|DisTubeOptions.searchSongs} bigger than 0
795
- * and song param of {@link DisTube#play|play()} is invalid url.
796
- * DisTube will wait for user's next message to choose song manually.
797
- *
798
- * @event DisTube#searchResult
799
- * @param {Discord.Message} message A message called play method
800
- * @param {Array<SearchResult>} results Searched results
801
- * @param {string} query The search query
802
- * @example
803
- * // DisTubeOptions.searchSongs > 0
804
- * distube.on("searchResult", (message, results) => {
805
- * message.channel.send(`**Choose an option from below**\n${results.map((song, i) => `**${i + 1}**. ${song.name} - \`${song.formattedDuration}\``).join("\n")}\n*Enter anything else or wait 60 seconds to cancel*`);
806
- * });
807
- */
808
-
809
- /**
810
- * Emitted when {@link DisTubeOptions|DisTubeOptions.searchSongs} bigger than 0
811
- * and the user chose a search result to play
812
- *
813
- * @event DisTube#searchDone
814
- * @param {Discord.Message} message A message called play method
815
- * @param {Discord.Message} answer The answer message
816
- * @param {string} query The search query
817
- * @example
818
- * // DisTubeOptions.searchSongs > 0
819
- * distube.on("searchCancel", (message) => message.channel.send(`Searching canceled`));
820
- */
821
-
822
- /**
823
- * Emitted when the bot is connected to the voice channel
824
- *
825
- * @event DisTube#connect
826
- * @param {Queue} queue The guild queue
827
- */
828
-
829
-
830
- /**
831
- * Emitted when the bot is disconnected to the voice channel
832
- *
833
- * @event DisTube#disconnect
834
- * @param {Queue} queue The guild queue
835
- */
836
-
837
- /**
838
- * Emitted when a {@link Queue} is deleted with any reasons.
839
- *
840
- * @event DisTube#deleteQueue
841
- * @param {Queue} queue The guild queue
842
- */
843
-
844
- /**
845
- * Emitted when DisTube finished a song
846
- *
847
- * @event DisTube#finishSong
848
- * @param {Queue} queue The guild queue
849
- * @param {Song} song Finished song
850
- */
851
-