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
@@ -1,39 +0,0 @@
1
- // eslint-disable-next-line no-unused-vars
2
- const DisTube = require("./DisTube");
3
- // eslint-disable-next-line no-unused-vars
4
- const Discord = require("discord.js");
5
-
6
- /** @private */
7
- class DisTubeBase {
8
- /** @param {DisTube} distube distube */
9
- constructor(distube) {
10
- /**
11
- * DisTube
12
- * @type {DisTube}
13
- * @private
14
- */
15
- this.distube = distube;
16
- /**
17
- * DisTube options
18
- * @type {DisTube.DisTubeOptions}
19
- * @private
20
- */
21
- this.options = this.distube.options;
22
- /**
23
- * Discord.js client
24
- * @type {Discord.Client}
25
- * @private
26
- */
27
- this.client = this.distube.client;
28
- }
29
- /**
30
- * Redirect emitter
31
- * @private
32
- * @param {...any} args arguments
33
- */
34
- emit(...args) {
35
- this.distube.emit(...args);
36
- }
37
- }
38
-
39
- module.exports = DisTubeBase;
@@ -1,440 +0,0 @@
1
- const ytdl = require("@distube/ytdl"),
2
- ytpl = require("@distube/ytpl"),
3
- Song = require("./Song"),
4
- SearchResult = require("./SearchResult"),
5
- Playlist = require("./Playlist"),
6
- { parseNumber, isURL } = require("./util"),
7
- youtube_dl = require("@distube/youtube-dl"),
8
- DisTubeBase = require("./DisTubeBase"),
9
- // eslint-disable-next-line no-unused-vars
10
- Queue = require("./Queue"),
11
- // eslint-disable-next-line no-unused-vars
12
- { opus } = require("prism-media"),
13
- // eslint-disable-next-line no-unused-vars
14
- Discord = require("discord.js");
15
-
16
-
17
- /**
18
- * DisTube's Handler
19
- * @extends DisTubeBase
20
- * @private
21
- */
22
- class DisTubeHandler extends DisTubeBase {
23
- constructor(distube) {
24
- super(distube);
25
- const requestOptions = this.options.youtubeCookie ? { headers: { cookie: this.options.youtubeCookie, "x-youtube-identity-token": this.options.youtubeIdentityToken } } : undefined;
26
- this.ytdlOptions = Object.assign(this.options.ytdlOptions, { requestOptions });
27
- }
28
-
29
- /**
30
- * Emit error event
31
- * @param {Discord.TextChannel} channel Text channel where the error is encountered.
32
- * @param {Error} error error
33
- * @private
34
- */
35
- emitError(channel, error) {
36
- this.distube.emitError(channel, error);
37
- }
38
-
39
- /**
40
- * Delete a guild queue
41
- * @param {Discord.Snowflake|Discord.Message|Queue} queue A message from guild channel | Queue
42
- */
43
- deleteQueue(queue) {
44
- this.distube._deleteQueue(queue);
45
- }
46
-
47
- /**
48
- * @param {string} url url
49
- * @returns {Promise<ytdl.videoInfo>}
50
- */
51
- getYouTubeInfo(url) {
52
- return ytdl.getInfo(url, this.ytdlOptions);
53
- }
54
-
55
- /**
56
- * Resolve a Song
57
- * @param {Discord.Message|Discord.GuildMember} message A message from guild channel | A guild member
58
- * @param {string|Song|SearchResult|Playlist} song YouTube url | Search string | {@link Song}
59
- * @returns {Promise<Song|Array<Song>|Playlist>} Resolved Song
60
- */
61
- async resolveSong(message, song) {
62
- if (!song) return null;
63
- const member = message?.member || message;
64
- if (song instanceof Song || song instanceof Playlist) return song;
65
- if (song instanceof SearchResult) {
66
- if (song.type === "video") return new Song(await this.getYouTubeInfo(song.url), member);
67
- else if (song.type === "playlist") return this.resolvePlaylist(message, song.url);
68
- throw new Error("Invalid SearchResult");
69
- }
70
- if (typeof song === "object") return new Song(song, member);
71
- if (ytdl.validateURL(song)) return new Song(await this.getYouTubeInfo(song), member);
72
- if (isURL(song)) {
73
- for (const plugin of this.distube.extractorPlugins) if (await plugin.validate(song)) return plugin.resolve(song, member);
74
- if (!this.options.youtubeDL) throw new Error("Not Supported URL!");
75
- const info = await youtube_dl(song, {
76
- dumpJson: true,
77
- noWarnings: true,
78
- }).catch(e => { throw new Error(`[youtube-dl] ${e.stderr || e}`) });
79
- if (Array.isArray(info) && info.length > 0) return this.resolvePlaylist(message, info.map(i => new Song(i, member, i.extractor)));
80
- return new Song(info, member, info.extractor);
81
- }
82
- if (typeof song !== "string") throw new TypeError("song is not a valid type");
83
- if (message instanceof Discord.GuildMember) song = (await this.distube.search(song, { limit: 1 }))[0];
84
- else song = await this.searchSong(message, song);
85
- return this.resolveSong(message, song);
86
- }
87
-
88
- /**
89
- * Resole Song[] or url to a Playlist
90
- * @param {Discord.Message|Discord.GuildMember} message A message from guild channel | A guild member
91
- * @param {Array<Song>|string} playlist Resolvable playlist
92
- * @returns {Promise<Playlist>}
93
- */
94
- async resolvePlaylist(message, playlist) {
95
- const member = message?.member || message;
96
- if (typeof playlist === "string") {
97
- playlist = await ytpl(playlist, { limit: Infinity });
98
- playlist.items = playlist.items.filter(v => !v.thumbnail.includes("no_thumbnail")).map(v => new Song(v, member));
99
- }
100
- if (!(playlist instanceof Playlist)) playlist = new Playlist(playlist, member);
101
- return playlist;
102
- }
103
-
104
- /**
105
- * Create a custom playlist
106
- * @returns {Promise<Playlist>}
107
- * @param {Discord.Message|Discord.GuildMember} message A message from guild channel | A guild member
108
- * @param {Array<string|Song|SearchResult>} songs Array of url, Song or SearchResult
109
- * @param {Object} [properties={}] Additional properties such as `name`
110
- * @param {boolean} [parallel=true] Whether or not fetch the songs in parallel
111
- */
112
- async createCustomPlaylist(message, songs, properties = {}, parallel = true) {
113
- const member = message?.member || message;
114
- if (!Array.isArray(songs)) throw new TypeError("songs must be an array of url");
115
- if (!songs.length) throw new Error("songs is an empty array");
116
- songs = songs.filter(song => song instanceof Song || song instanceof SearchResult || isURL(song));
117
- if (!songs.length) throw new Error("songs does not have any valid Song, SearchResult or url");
118
- if (parallel) {
119
- songs = songs.map(song => this.resolveSong(member, song).catch(() => undefined));
120
- songs = await Promise.all(songs);
121
- } else {
122
- const resolved = [];
123
- for (const song of songs) resolved.push(await this.resolveSong(member, song).catch(() => undefined));
124
- songs = resolved;
125
- }
126
- songs = songs.filter(song => song);
127
- return new Playlist(songs, member, properties);
128
- }
129
-
130
- /**
131
- * Play / add a playlist
132
- * @returns {Promise<void>}
133
- * @param {Discord.Message|Discord.VoiceChannel|Discord.StageChannel} message A message from guild channel | a voice channel
134
- * @param {Playlist|string} playlist A YouTube playlist url | a Playlist
135
- * @param {boolean} [textChannel] The default text channel of the queue
136
- * @param {boolean} [skip=false] Skip the current song
137
- */
138
- async handlePlaylist(message, playlist, textChannel = false, skip = false) {
139
- if (typeof textChannel === "boolean") {
140
- skip = textChannel;
141
- textChannel = message.channel;
142
- }
143
- if (!playlist || !(playlist instanceof Playlist)) throw Error("Invalid Playlist");
144
- if (this.options.nsfw && !textChannel?.nsfw) {
145
- playlist.songs = playlist.songs.filter(s => !s.age_restricted);
146
- }
147
- if (!playlist.songs.length) {
148
- if (this.options.nsfw && !textChannel?.nsfw) {
149
- throw new Error("No valid video in the playlist.\nMaybe age-restricted contents is filtered because you are in non-NSFW channel.");
150
- }
151
- throw Error("No valid video in the playlist");
152
- }
153
- const songs = playlist.songs;
154
- let queue = this.distube.getQueue(message);
155
- if (queue) {
156
- queue.addToQueue(songs, skip);
157
- if (skip) queue.skip();
158
- else this.emit("addList", queue, playlist);
159
- } else {
160
- queue = await this.distube._newQueue(message, songs, textChannel);
161
- if (queue !== true) this.emit("playSong", queue, queue.songs[0]);
162
- }
163
- }
164
-
165
- /**
166
- * Search for a song, fire {@link DisTube#event:error} if not found.
167
- * @param {Discord.Message} message A message from guild channel
168
- * @param {string} query The query string
169
- * @returns {Promise<Song?>} Song info
170
- */
171
- async searchSong(message, query) {
172
- const results = await this.distube.search(query, {
173
- limit: this.options.searchSongs || 1,
174
- safeSearch: this.options.nsfw ? false : !message.channel?.nsfw,
175
- }).catch(() => undefined);
176
- if (!results?.length) {
177
- this.emit("searchNoResult", message, query);
178
- return null;
179
- }
180
- let result = results[0];
181
- if (this.options.searchSongs && this.options.searchSongs > 1) {
182
- this.emit("searchResult", message, results, query);
183
- const answers = await message.channel.awaitMessages(m => m.author.id === message.author.id, {
184
- max: 1,
185
- time: this.options.searchCooldown * 1000,
186
- errors: ["time"],
187
- }).catch(() => undefined);
188
- const ans = answers?.first();
189
- if (!ans) {
190
- this.emit("searchCancel", message, query);
191
- return null;
192
- }
193
- const index = parseInt(ans.content, 10);
194
- if (isNaN(index) || index > results.length || index < 1) {
195
- this.emit("searchCancel", message, query);
196
- return null;
197
- }
198
- this.emit("searchDone", message, ans, query);
199
- result = results[index - 1];
200
- }
201
- return result;
202
- }
203
-
204
- /**
205
- * Join the voice channel
206
- * @param {Queue} queue A message from guild channel
207
- * @param {Discord.VoiceChannel|Discord.StageChannel} voice The string search for
208
- * @param {boolean} retried retried?
209
- * @throws {Error}
210
- * @returns {Promise<Queue|true>} `true` if queue is not generated
211
- */
212
- async joinVoiceChannel(queue, voice, retried = false) {
213
- try {
214
- queue.connection = await voice.join();
215
- this.emit("connect", queue);
216
- queue.connection.on("disconnect", () => {
217
- this.emit("disconnect", queue);
218
- try { queue.stop() } catch { this.deleteQueue(queue) }
219
- }).on("error", e => {
220
- e.name = "VoiceConnection";
221
- this.emitError(queue.textChannel, e);
222
- try { queue.stop() } catch { this.deleteQueue(queue) }
223
- });
224
- const err = await this.playSong(queue);
225
- return err || queue;
226
- } catch (e) {
227
- this.deleteQueue(queue);
228
- e.name = "JoinVoiceChannel";
229
- if (retried) throw e;
230
- return this.joinVoiceChannel(queue, voice, true);
231
- }
232
- }
233
-
234
- /**
235
- * Get related songs
236
- * @param {Song} song song
237
- * @returns {Array<ytdl.relatedVideo>} Related videos
238
- * @throws {NoRelated}
239
- */
240
- async getRelatedVideo(song) {
241
- if (song.source !== "youtube") throw new Error("NoRelated");
242
- let related = song.related;
243
- if (!related) related = (await ytdl.getBasicInfo(song.url, this.ytdlOptions)).related_videos;
244
- if (!related || !related.length) throw new Error("NoRelated");
245
- return related;
246
- }
247
-
248
- /**
249
- * Create a ytdl stream
250
- * @param {Queue} queue Queue
251
- * @returns {opus.Encoder}
252
- */
253
- createStream(queue) {
254
- const song = queue.songs[0];
255
- const filterArgs = [];
256
- queue.filters.forEach(filter => filterArgs.push(this.distube.filters[filter]));
257
- const encoderArgs = queue.filters?.length ? ["-af", filterArgs.join(",")] : null;
258
- const seek = song.duration ? queue.beginTime : undefined;
259
- const streamOptions = {
260
- opusEncoded: true,
261
- filter: song.isLive ? "audioandvideo" : "audioonly",
262
- quality: "highestaudio",
263
- encoderArgs,
264
- seek,
265
- };
266
- Object.assign(streamOptions, this.ytdlOptions);
267
- if (song.source === "youtube") return ytdl(song.info, streamOptions);
268
- return ytdl.arbitraryStream(song.streamURL, streamOptions);
269
- }
270
-
271
- async checkYouTubeInfo(song) {
272
- if (!song.info) {
273
- const { videoDetails } = song.info = await this.getYouTubeInfo(song.url);
274
- song.views = parseNumber(videoDetails.viewCount);
275
- song.likes = parseNumber(videoDetails.likes);
276
- song.dislikes = parseNumber(videoDetails.dislikes);
277
- if (song.info.formats.length) {
278
- song.streamURL = ytdl.chooseFormat(song.info.formats, {
279
- filter: song.isLive ? "audioandvideo" : "audioonly",
280
- quality: "highestaudio",
281
- }).url;
282
- }
283
- }
284
- const err = require("ytdl-core/lib/utils").playError(song.info.player_response, ["UNPLAYABLE", "LIVE_STREAM_OFFLINE", "LOGIN_REQUIRED"]);
285
- if (err) throw err;
286
- if (!song.info.formats.length) throw new Error("This video is unavailable");
287
- }
288
-
289
- /**
290
- * Whether or not emit playSong event
291
- * @param {Queue} queue Queue
292
- * @private
293
- * @returns {boolean}
294
- */
295
- _emitPlaySong(queue) {
296
- if (
297
- !this.options.emitNewSongOnly ||
298
- (
299
- queue.repeatMode !== 1 &&
300
- queue.songs[0]?.id !== queue.songs[1]?.id
301
- )
302
- ) return true;
303
- return false;
304
- }
305
-
306
- /**
307
- * Play a song on voice connection
308
- * @param {Queue} queue The guild queue
309
- * @returns {Promise<boolean>} error?
310
- */
311
- async playSong(queue) {
312
- if (!queue) return true;
313
- if (!queue.songs.length) {
314
- this.deleteQueue(queue);
315
- return true;
316
- }
317
- const song = queue.songs[0];
318
- try {
319
- let errorEmitted = false;
320
- if (song.source === "youtube") await this.checkYouTubeInfo(song);
321
- const stream = this.createStream(queue).on("error", e => {
322
- errorEmitted = true;
323
- e.name = "Stream";
324
- e.message = `${e.message}\nID: ${song.id}\nName: ${song.name}`;
325
- this.emitError(queue.textChannel, e);
326
- });
327
- queue.dispatcher = queue.connection.play(stream, {
328
- highWaterMark: 1,
329
- type: "opus",
330
- volume: queue.volume / 100,
331
- bitrate: "auto",
332
- }).on("finish", () => { this._handleSongFinish(queue) })
333
- .on("error", e => { this._handlePlayingError(queue, errorEmitted ? null : e) });
334
- if (queue.stream) queue.stream.destroy();
335
- queue.stream = stream;
336
- return false;
337
- } catch (e) {
338
- this._handlePlayingError(queue, e);
339
- return true;
340
- }
341
- }
342
-
343
- /**
344
- * Handle the queue when a Song finish
345
- * @private
346
- * @param {Queue} queue queue
347
- * @returns {Promise<void>}
348
- */
349
- async _handleSongFinish(queue) {
350
- this.emit("finishSong", queue, queue.songs[0]);
351
- if (queue.stopped) {
352
- this.deleteQueue(queue);
353
- return;
354
- }
355
- if (queue.repeatMode === 2 && !queue.prev) queue.songs.push(queue.songs[0]);
356
- if (queue.prev) {
357
- if (queue.repeatMode === 2) queue.songs.unshift(queue.songs.pop());
358
- else queue.songs.unshift(queue.previousSongs.pop());
359
- }
360
- if (queue.songs.length <= 1 && (queue.next || !queue.repeatMode)) {
361
- if (queue.autoplay) try { await queue.addRelatedVideo() } catch { this.emit("noRelated", queue) }
362
- if (queue.songs.length <= 1) {
363
- if (this.options.leaveOnFinish) queue.connection.channel.leave();
364
- if (!queue.autoplay) this.emit("finish", queue);
365
- this.deleteQueue(queue);
366
- return;
367
- }
368
- }
369
- const emitPlaySong = this._emitPlaySong(queue);
370
- if (!queue.prev && (queue.repeatMode !== 1 || queue.next)) {
371
- const prev = queue.songs.shift();
372
- if (this.options.savePreviousSongs) queue.previousSongs.push(prev);
373
- }
374
- queue.next = queue.prev = false;
375
- queue.beginTime = 0;
376
- const err = await this.playSong(queue);
377
- if (!err && emitPlaySong) this.emit("playSong", queue, queue.songs[0]);
378
- }
379
-
380
- /**
381
- * Handle error while playing
382
- * @private
383
- * @param {Queue} queue queue
384
- * @param {Error} error error
385
- */
386
- _handlePlayingError(queue, error = null) {
387
- const song = queue.songs.shift();
388
- if (error) {
389
- error.name = "Playing";
390
- error.message = `${error.message}\nID: ${song.id}\nName: ${song.name}`;
391
- this.emitError(queue.textChannel, error);
392
- }
393
- if (queue.songs.length > 0) {
394
- this.playSong(queue).then(e => {
395
- if (!e) this.emit("playSong", queue, queue.songs[0]);
396
- });
397
- } else try { queue.stop() } catch { this.deleteQueue(queue) }
398
- }
399
-
400
- /**
401
- * Play a song from url without creating a {@link Queue}
402
- * @param {Discord.VoiceChannel|Discord.StageChannel} voiceChannel The voice channel will be joined
403
- * @param {string|Song|SearchResult} song YouTube url | {@link Song} | {@link SearchResult}
404
- * @returns {Promise<Discord.StreamDispatcher>}
405
- */
406
- async playWithoutQueue(voiceChannel, song) {
407
- if (!["voice", "stage"].includes(voiceChannel?.type)) {
408
- throw new TypeError("voiceChannel is not a Discord.VoiceChannel or a Discord.StageChannel.");
409
- }
410
- try {
411
- if (ytpl.validateID(song)) throw new Error("Cannot play a playlist with this method.");
412
- song = await this.resolveSong(voiceChannel.guild.me, song);
413
- if (!song) throw new Error("Cannot resolve this song.");
414
- if (song instanceof Playlist || Array.isArray(song)) throw new Error("Cannot play a playlist with this method.");
415
- const connection = await voiceChannel.join();
416
- if (song.source === "youtube") await this.checkYouTubeInfo(song);
417
- const streamOptions = {
418
- opusEncoded: true,
419
- filter: song.isLive ? "audioandvideo" : "audioonly",
420
- quality: "highestaudio",
421
- };
422
- Object.assign(streamOptions, this.ytdlOptions);
423
- let stream;
424
- if (song.source === "youtube") stream = ytdl(song.info, streamOptions);
425
- else stream = ytdl.arbitraryStream(song.streamURL, streamOptions);
426
- const dispatcher = connection.play(stream, {
427
- highWaterMark: 1,
428
- type: "opus",
429
- bitrate: "auto",
430
- }).on("finish", () => { try { stream.destroy() } catch { } });
431
- return dispatcher;
432
- } catch (e) {
433
- e.name = "playWithoutQueue";
434
- e.message = `${song?.url || song}\n${e.message}`;
435
- throw e;
436
- }
437
- }
438
- }
439
-
440
- module.exports = DisTubeHandler;
@@ -1,82 +0,0 @@
1
- const { mergeObject } = require("./util");
2
- const defaultOptions = {
3
- emitNewSongOnly: false,
4
- leaveOnEmpty: true,
5
- leaveOnFinish: false,
6
- leaveOnStop: true,
7
- savePreviousSongs: true,
8
- youtubeDL: true,
9
- updateYouTubeDL: true,
10
- searchSongs: 0,
11
- youtubeCookie: null,
12
- youtubeIdentityToken: null,
13
- customFilters: {},
14
- ytdlOptions: {
15
- highWaterMark: 1 << 24,
16
- },
17
- searchCooldown: 60,
18
- emptyCooldown: 60,
19
- plugins: [],
20
- nsfw: false,
21
- };
22
-
23
- module.exports = class DisTubeOptions {
24
- constructor(options) {
25
- const opt = mergeObject(defaultOptions, options);
26
- for (const key in opt) {
27
- this[key] = opt[key];
28
- }
29
- this._validateOptions();
30
- }
31
-
32
- _validateOptions(options = this) {
33
- if (typeof options.emitNewSongOnly !== "boolean") {
34
- throw new TypeError("DisTubeOptions.emitNewSongOnly must be a boolean");
35
- }
36
- if (typeof options.leaveOnEmpty !== "boolean") {
37
- throw new TypeError("DisTubeOptions.leaveOnEmpty must be a boolean");
38
- }
39
- if (typeof options.leaveOnFinish !== "boolean") {
40
- throw new TypeError("DisTubeOptions.leaveOnFinish must be a boolean");
41
- }
42
- if (typeof options.leaveOnStop !== "boolean") {
43
- throw new TypeError("DisTubeOptions.leaveOnStop must be a boolean");
44
- }
45
- if (typeof options.savePreviousSongs !== "boolean") {
46
- throw new TypeError("DisTubeOptions.savePreviousSongs must be a boolean");
47
- }
48
- if (typeof options.youtubeDL !== "boolean") {
49
- throw new TypeError("DisTubeOptions.youtubeDL must be a boolean");
50
- }
51
- if (typeof options.updateYouTubeDL !== "boolean") {
52
- throw new TypeError("DisTubeOptions.updateYouTubeDL must be a boolean");
53
- }
54
- if (options.youtubeCookie !== null && typeof options.youtubeCookie !== "string") {
55
- throw new TypeError("DisTubeOptions.youtubeCookie must be a string");
56
- }
57
- if (options.youtubeIdentityToken !== null && typeof options.youtubeIdentityToken !== "string") {
58
- throw new TypeError("DisTubeOptions.youtubeIdentityToken must be a string");
59
- }
60
- if (typeof options.customFilters !== "object" || Array.isArray(options.customFilters)) {
61
- throw new TypeError("DisTubeOptions.customFilters must be an object");
62
- }
63
- if (typeof options.ytdlOptions !== "object" || Array.isArray(options.ytdlOptions)) {
64
- throw new TypeError("DisTubeOptions.customFilters must be an object");
65
- }
66
- if (typeof options.searchCooldown !== "number" || isNaN(options.searchCooldown)) {
67
- throw new TypeError("DisTubeOptions.searchCooldown must be a number");
68
- }
69
- if (typeof options.emptyCooldown !== "number" || isNaN(options.emptyCooldown)) {
70
- throw new TypeError("DisTubeOptions.emptyCooldown must be a number");
71
- }
72
- if (typeof options.searchSongs !== "number" || isNaN(options.emptyCooldown)) {
73
- throw new TypeError("DisTubeOptions.searchSongs must be a number");
74
- }
75
- if (!Array.isArray(options.plugins)) {
76
- throw new TypeError("DisTubeOptions.plugins must be an array of Plugin.");
77
- }
78
- if (typeof options.nsfw !== "boolean") {
79
- throw new TypeError("DisTubeOptions.nsfw must be a boolean");
80
- }
81
- }
82
- };
package/src/Filter.js DELETED
@@ -1,36 +0,0 @@
1
- /**
2
- * Default DisTube audio filters.
3
- * @typedef {Object} DefaultFilters
4
- * @prop {string} 3d 3d
5
- * @prop {string} bassboost bassboost
6
- * @prop {string} echo echo
7
- * @prop {string} karaoke karaoke
8
- * @prop {string} nightcore nightcore
9
- * @prop {string} vaporwave vaporwave
10
- * @prop {string} flanger flanger
11
- * @prop {string} gate gate
12
- * @prop {string} haas haas
13
- * @prop {string} reverse reverse
14
- * @prop {string} surround surround
15
- * @prop {string} mcompand mcompand
16
- * @prop {string} phaser phaser
17
- * @prop {string} tremolo tremolo
18
- * @prop {string} earwax earwax
19
- */
20
- module.exports = {
21
- "3d": "apulsator=hz=0.125",
22
- bassboost: "bass=g=10,dynaudnorm=f=150:g=15",
23
- echo: "aecho=0.8:0.9:1000:0.3",
24
- flanger: "flanger",
25
- gate: "agate",
26
- haas: "haas",
27
- karaoke: "stereotools=mlev=0.1",
28
- nightcore: "asetrate=48000*1.25,aresample=48000,bass=g=5",
29
- reverse: "areverse",
30
- vaporwave: "asetrate=48000*0.8,aresample=48000,atempo=1.1",
31
- mcompand: "mcompand",
32
- phaser: "aphaser",
33
- tremolo: "tremolo",
34
- surround: "surround",
35
- earwax: "earwax",
36
- };
package/src/Playlist.js DELETED
@@ -1,75 +0,0 @@
1
- const { formatDuration } = require("./util"),
2
- // eslint-disable-next-line no-unused-vars
3
- Song = require("./Song"),
4
- // eslint-disable-next-line no-unused-vars
5
- Discord = require("discord.js");
6
-
7
-
8
- /** Class representing a playlist. */
9
- class Playlist {
10
- /**
11
- * Create a playlist
12
- * @param {Array<Song>|Object} playlist Playlist
13
- * @param {Discord.GuildMember} member Requested user
14
- * @param {Object} properties Custom properties
15
- */
16
- constructor(playlist, member, properties = {}) {
17
- if (typeof properties !== "object") throw new TypeError("Custom properties must be an object");
18
- /**
19
- * The source of the playlist
20
- * @type {string}
21
- */
22
- this.source = playlist.source || properties.source || "youtube";
23
- /**
24
- * User requested.
25
- * @type {Discord.GuildMember}
26
- */
27
- this.member = member || playlist.member;
28
- /**
29
- * User requested.
30
- * @type {Discord.User}
31
- */
32
- this.user = this.member?.user;
33
- /**
34
- * Playlist songs.
35
- * @type {Array<Song>}
36
- */
37
- this.songs = Array.isArray(playlist) ? playlist : playlist.items || playlist.songs;
38
- if (!Array.isArray(this.songs) || !this.songs.length) throw new Error("Playlist is empty!");
39
- this.songs.map(s => s.constructor.name === "Song" && s._patchPlaylist(this, this.member));
40
- /**
41
- * Playlist name.
42
- * @type {string}
43
- */
44
- this.name = playlist.name || playlist.title || this.songs[0].playlist_title || `${this.songs[0].name} and ${this.songs.length - 1} more songs.`;
45
- /**
46
- * Playlist URL.
47
- * @type {string}
48
- */
49
- this.url = playlist.url;
50
- /**
51
- * Playlist thumbnail.
52
- * @type {string}
53
- */
54
- this.thumbnail = playlist.thumbnail || this.songs[0].thumbnail;
55
- for (const [key, value] of Object.entries(properties)) this[key] = value;
56
- }
57
-
58
- /**
59
- * Playlist duration in second.
60
- * @type {number}
61
- */
62
- get duration() {
63
- return this.songs?.reduce((prev, next) => prev + (next.duration || 0), 0) || 0;
64
- }
65
-
66
- /**
67
- * Formatted duration string `hh:mm:ss`.
68
- * @type {string}
69
- */
70
- get formattedDuration() {
71
- return formatDuration(this.duration);
72
- }
73
- }
74
-
75
- module.exports = Playlist;
@@ -1,26 +0,0 @@
1
- /* eslint-disable */
2
- const Plugin = require("./Plugin"),
3
- Discord = require("discord.js");
4
-
5
- /**
6
- * Custom Plugin
7
- * @extends Plugin
8
- */
9
- class CustomPlugin extends Plugin {
10
- /** Create a custom plugin */
11
- constructor() {
12
- super("custom");
13
- }
14
- /**
15
- * Execute if the url is validated
16
- * @param {Discord.VoiceChannel|Discord.StageChannel} voiceChannel The voice channel will be joined
17
- * @param {string} url Validated url
18
- * @param {Discord.GuildMember} member Requested user
19
- * @param {Discord.TextChannel?} textChannel Default {@link Queue#textChannel}
20
- * @param {boolean} skip Skip the playing song (if exists)
21
- * @returns {Promise<void>}
22
- */
23
- async play(voiceChannel, url, member, textChannel, skip) { }
24
- }
25
-
26
- module.exports = CustomPlugin;