streamify-audio 2.3.0 → 2.3.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 (39) hide show
  1. package/index.d.ts +9 -0
  2. package/package.json +1 -1
  3. package/src/config.js +11 -0
  4. package/src/discord/Manager.js +1 -1
  5. package/src/discord/Stream.js +9 -12
  6. package/src/providers/spotify.js +2 -2
  7. package/docs/README.md +0 -31
  8. package/docs/automation.md +0 -186
  9. package/docs/configuration.md +0 -198
  10. package/docs/discord/events.md +0 -206
  11. package/docs/discord/manager.md +0 -195
  12. package/docs/discord/player.md +0 -263
  13. package/docs/discord/queue.md +0 -197
  14. package/docs/examples/advanced-bot.md +0 -391
  15. package/docs/examples/basic-bot.md +0 -182
  16. package/docs/examples/lavalink.md +0 -156
  17. package/docs/filters.md +0 -347
  18. package/docs/http/endpoints.md +0 -224
  19. package/docs/http/server.md +0 -174
  20. package/docs/plans/2026-02-22-stream-revamp-design.md +0 -88
  21. package/docs/plans/2026-02-22-stream-revamp-plan.md +0 -814
  22. package/docs/quick-start.md +0 -92
  23. package/docs/sources.md +0 -189
  24. package/docs/sponsorblock.md +0 -95
  25. package/tests/cache.test.js +0 -234
  26. package/tests/config.test.js +0 -44
  27. package/tests/error-handling.test.js +0 -318
  28. package/tests/ffmpeg.test.js +0 -66
  29. package/tests/filters-edge.test.js +0 -333
  30. package/tests/http.test.js +0 -24
  31. package/tests/integration.test.js +0 -325
  32. package/tests/local.test.js +0 -37
  33. package/tests/queue.test.js +0 -94
  34. package/tests/spotify.test.js +0 -238
  35. package/tests/stream.test.js +0 -217
  36. package/tests/twitch.test.js +0 -42
  37. package/tests/utils.test.js +0 -60
  38. package/tests/youtube.test.js +0 -219
  39. package/youtube-cookies.txt +0 -26
@@ -1,197 +0,0 @@
1
- # Queue
2
-
3
- The Queue manages tracks for a player.
4
-
5
- ## Accessing the Queue
6
-
7
- ```javascript
8
- const player = manager.get(guildId);
9
- const queue = player.queue;
10
- ```
11
-
12
- ## Properties
13
-
14
- ```javascript
15
- queue.current // Currently playing track (or null)
16
- queue.tracks // Array of upcoming tracks
17
- queue.previous // Array of previously played tracks
18
- queue.size // Number of upcoming tracks
19
- queue.isEmpty // true if no upcoming tracks
20
- queue.totalDuration // Total duration in ms (current + upcoming)
21
- queue.repeatMode // 'off', 'track', or 'queue'
22
- ```
23
-
24
- ## Adding Tracks
25
-
26
- ### add(track, position?)
27
-
28
- Add a single track.
29
-
30
- ```javascript
31
- // Add to end
32
- queue.add(track);
33
-
34
- // Add at specific position
35
- queue.add(track, 0); // Next up
36
- queue.add(track, 2); // Third in queue
37
- ```
38
-
39
- ### addMany(tracks, position?)
40
-
41
- Add multiple tracks.
42
-
43
- ```javascript
44
- // Add to end
45
- queue.addMany(tracks);
46
-
47
- // Add at position
48
- queue.addMany(tracks, 0);
49
- ```
50
-
51
- ## Removing Tracks
52
-
53
- ### remove(index)
54
-
55
- Remove a track by index.
56
-
57
- ```javascript
58
- const removed = queue.remove(0); // Remove next track
59
- console.log(removed.title);
60
- ```
61
-
62
- ### clear()
63
-
64
- Clear all upcoming tracks.
65
-
66
- ```javascript
67
- const count = queue.clear();
68
- console.log(`Cleared ${count} tracks`);
69
- ```
70
-
71
- ## Reordering
72
-
73
- ### shuffle()
74
-
75
- Randomize track order.
76
-
77
- ```javascript
78
- queue.shuffle();
79
- ```
80
-
81
- ### move(from, to)
82
-
83
- Move a track to a different position.
84
-
85
- ```javascript
86
- queue.move(5, 0); // Move track 5 to next up
87
- queue.move(0, 3); // Move next track to position 3
88
- ```
89
-
90
- ## Loop Mode
91
-
92
- ```javascript
93
- queue.setRepeatMode('off'); // No looping
94
- queue.setRepeatMode('track'); // Repeat current track
95
- queue.setRepeatMode('queue'); // Repeat entire queue
96
- ```
97
-
98
- Or use the player shorthand:
99
-
100
- ```javascript
101
- player.setLoop('queue');
102
- ```
103
-
104
- ## Internal Methods
105
-
106
- These are used internally by the Player:
107
-
108
- ### shift()
109
-
110
- Moves current to previous, gets next track as current.
111
-
112
- ```javascript
113
- const next = queue.shift(); // Returns next track or null
114
- ```
115
-
116
- ### unshift()
117
-
118
- Goes back to previous track.
119
-
120
- ```javascript
121
- const prev = queue.unshift(); // Returns previous track or null
122
- ```
123
-
124
- ### setCurrent(track)
125
-
126
- Sets the current track directly.
127
-
128
- ```javascript
129
- queue.setCurrent(track);
130
- ```
131
-
132
- ## Example: Queue Display
133
-
134
- ```javascript
135
- function displayQueue(player) {
136
- const { current, tracks } = player.queue;
137
-
138
- let text = '';
139
-
140
- if (current) {
141
- text += `**Now Playing:** ${current.title}\n\n`;
142
- }
143
-
144
- if (tracks.length > 0) {
145
- text += '**Up Next:**\n';
146
- tracks.slice(0, 10).forEach((track, i) => {
147
- text += `${i + 1}. ${track.title}\n`;
148
- });
149
- if (tracks.length > 10) {
150
- text += `... and ${tracks.length - 10} more`;
151
- }
152
- } else {
153
- text += 'Queue is empty.';
154
- }
155
-
156
- return text;
157
- }
158
- ```
159
-
160
- ## Example: Playlist Loading
161
-
162
- ```javascript
163
- async function loadPlaylist(player, url) {
164
- const result = await manager.loadPlaylist(url);
165
-
166
- if (result.loadType === 'error') {
167
- throw new Error(result.error);
168
- }
169
-
170
- const tracks = result.tracks;
171
-
172
- if (player.queue.current) {
173
- // Add all to queue
174
- player.queue.addMany(tracks);
175
- return `Added ${tracks.length} tracks to queue`;
176
- } else {
177
- // Play first, queue rest
178
- const first = tracks.shift();
179
- player.queue.addMany(tracks);
180
- await player.play(first);
181
- return `Playing **${result.playlist.title}** (${tracks.length + 1} tracks)`;
182
- }
183
- }
184
- ```
185
-
186
- ## Serialization
187
-
188
- ```javascript
189
- const json = queue.toJSON();
190
- // {
191
- // current: { id, title, ... },
192
- // tracks: [...],
193
- // previous: [...],
194
- // repeatMode: 'off',
195
- // size: 5
196
- // }
197
- ```
@@ -1,391 +0,0 @@
1
- # Advanced Discord Bot
2
-
3
- A full-featured music bot with queue management, filters, and automation.
4
-
5
- ```javascript
6
- const { Client, GatewayIntentBits, EmbedBuilder } = require('discord.js');
7
- const Streamify = require('streamify-audio');
8
-
9
- const client = new Client({
10
- intents: [
11
- GatewayIntentBits.Guilds,
12
- GatewayIntentBits.GuildVoiceStates,
13
- GatewayIntentBits.GuildMessages,
14
- GatewayIntentBits.MessageContent
15
- ]
16
- });
17
-
18
- const manager = new Streamify.Manager(client, {
19
- ytdlpPath: '/usr/local/bin/yt-dlp',
20
- ffmpegPath: '/usr/bin/ffmpeg',
21
- cookiesPath: './cookies.txt',
22
- spotify: {
23
- clientId: process.env.SPOTIFY_CLIENT_ID,
24
- clientSecret: process.env.SPOTIFY_CLIENT_SECRET
25
- },
26
- defaultVolume: 80,
27
- sponsorblock: {
28
- enabled: true,
29
- categories: ['sponsor', 'selfpromo']
30
- },
31
- autoLeave: {
32
- enabled: true,
33
- emptyDelay: 30000,
34
- inactivityTimeout: 300000
35
- },
36
- autoPause: {
37
- enabled: true,
38
- minUsers: 1
39
- },
40
- autoplay: {
41
- enabled: false,
42
- maxTracks: 5
43
- }
44
- });
45
-
46
- client.on('ready', () => {
47
- console.log(`Logged in as ${client.user.tag}`);
48
- });
49
-
50
- client.on('messageCreate', async (message) => {
51
- if (message.author.bot) return;
52
- if (!message.content.startsWith('!')) return;
53
-
54
- const [command, ...args] = message.content.slice(1).split(' ');
55
- const query = args.join(' ');
56
-
57
- let player = manager.get(message.guild.id);
58
-
59
- switch (command) {
60
- case 'play':
61
- case 'p': {
62
- if (!query) return message.reply('Provide a search query or URL.');
63
-
64
- const vc = message.member.voice.channel;
65
- if (!vc) return message.reply('Join a voice channel first.');
66
-
67
- await message.react('🔍');
68
-
69
- try {
70
- // Check if playlist
71
- const isPlaylist = query.includes('playlist') || query.includes('/album/');
72
-
73
- if (isPlaylist) {
74
- const result = await manager.loadPlaylist(query);
75
- if (result.loadType === 'error') {
76
- await message.reactions.removeAll();
77
- return message.reply(`Failed: ${result.error}`);
78
- }
79
-
80
- if (!player) {
81
- player = await manager.create(message.guild.id, vc.id, message.channel.id);
82
- setupEvents(player, message.channel);
83
- }
84
-
85
- const tracks = result.tracks;
86
- const first = tracks.shift();
87
- first.requestedBy = message.author;
88
-
89
- if (tracks.length > 0) {
90
- tracks.forEach(t => t.requestedBy = message.author);
91
- player.queue.addMany(tracks);
92
- }
93
-
94
- await message.reactions.removeAll();
95
- message.reply(`📋 Loaded **${result.playlist.title}** (${tracks.length + 1} tracks)`);
96
- await player.play(first);
97
- return;
98
- }
99
-
100
- // Single track
101
- const result = await manager.resolve(query);
102
- if (!result.tracks.length) {
103
- await message.reactions.removeAll();
104
- return message.reply('No results found.');
105
- }
106
-
107
- const track = result.tracks[0];
108
- track.requestedBy = message.author;
109
-
110
- if (!player) {
111
- player = await manager.create(message.guild.id, vc.id, message.channel.id);
112
- setupEvents(player, message.channel);
113
- }
114
-
115
- await message.reactions.removeAll();
116
- await player.play(track);
117
-
118
- } catch (error) {
119
- await message.reactions.removeAll();
120
- message.reply(`Error: ${error.message}`);
121
- }
122
- break;
123
- }
124
-
125
- case 'add':
126
- case 'a': {
127
- if (!player) return message.reply('No active player.');
128
- if (!query) return message.reply('Provide a search query.');
129
-
130
- const result = await manager.search(query);
131
- if (!result.tracks.length) return message.reply('No results.');
132
-
133
- const track = result.tracks[0];
134
- track.requestedBy = message.author;
135
- player.queue.add(track);
136
-
137
- message.reply(`Added **${track.title}** to queue (#${player.queue.size})`);
138
- break;
139
- }
140
-
141
- case 'skip':
142
- case 's': {
143
- if (!player?.playing) return message.reply('Nothing playing.');
144
- await player.skip();
145
- message.react('⏭️');
146
- break;
147
- }
148
-
149
- case 'prev':
150
- case 'previous': {
151
- if (!player) return message.reply('No player.');
152
- const prev = await player.previous();
153
- if (prev) message.react('⏮️');
154
- else message.reply('No previous track.');
155
- break;
156
- }
157
-
158
- case 'stop': {
159
- if (!player) return message.reply('No player.');
160
- player.stop();
161
- message.react('⏹️');
162
- break;
163
- }
164
-
165
- case 'pause': {
166
- if (!player?.playing) return message.reply('Nothing playing.');
167
- player.pause();
168
- message.react('⏸️');
169
- break;
170
- }
171
-
172
- case 'resume': {
173
- if (!player?.paused) return message.reply('Not paused.');
174
- await player.resume();
175
- message.react('▶️');
176
- break;
177
- }
178
-
179
- case 'seek': {
180
- if (!player?.playing) return message.reply('Nothing playing.');
181
- const seconds = parseInt(query);
182
- if (isNaN(seconds)) return message.reply('Provide seconds.');
183
- await player.seek(seconds * 1000);
184
- message.reply(`Seeked to ${seconds}s`);
185
- break;
186
- }
187
-
188
- case 'queue':
189
- case 'q': {
190
- if (!player) return message.reply('No player.');
191
-
192
- const { current, tracks } = player.queue;
193
- let desc = current
194
- ? `**Now Playing:** ${current.title}\n\n`
195
- : 'Nothing playing.\n\n';
196
-
197
- if (tracks.length > 0) {
198
- desc += '**Up Next:**\n';
199
- desc += tracks.slice(0, 10).map((t, i) =>
200
- `${i + 1}. ${t.title}${t.isAutoplay ? ' 📻' : ''}`
201
- ).join('\n');
202
- if (tracks.length > 10) {
203
- desc += `\n... and ${tracks.length - 10} more`;
204
- }
205
- } else {
206
- desc += 'Queue is empty.';
207
- }
208
-
209
- message.reply({
210
- embeds: [new EmbedBuilder()
211
- .setTitle('Queue')
212
- .setDescription(desc)
213
- .setFooter({ text: `Loop: ${player.queue.repeatMode} | Autoplay: ${player.autoplay.enabled ? 'on' : 'off'}` })
214
- ]
215
- });
216
- break;
217
- }
218
-
219
- case 'shuffle': {
220
- if (!player) return message.reply('No player.');
221
- player.queue.shuffle();
222
- message.react('🔀');
223
- break;
224
- }
225
-
226
- case 'loop': {
227
- if (!player) return message.reply('No player.');
228
- const modes = ['off', 'track', 'queue'];
229
- const current = modes.indexOf(player.queue.repeatMode);
230
- const next = modes[(current + 1) % 3];
231
- player.setLoop(next);
232
- message.reply(`Loop: **${next}**`);
233
- break;
234
- }
235
-
236
- case 'autoplay':
237
- case 'ap': {
238
- if (!player) return message.reply('No player.');
239
- const enabled = player.setAutoplay(!player.autoplay.enabled);
240
- message.reply(`Autoplay: **${enabled ? 'on' : 'off'}**`);
241
- break;
242
- }
243
-
244
- case 'vol':
245
- case 'volume': {
246
- if (!player) return message.reply('No player.');
247
- const vol = parseInt(query);
248
- if (isNaN(vol)) return message.reply(`Volume: ${player.volume}%`);
249
- player.setVolume(vol);
250
- message.react('🔊');
251
- break;
252
- }
253
-
254
- case 'bass': {
255
- if (!player) return message.reply('No player.');
256
- await player.setFilter('bass', parseInt(query) || 10);
257
- message.reply(`Bass: ${parseInt(query) || 10}`);
258
- break;
259
- }
260
-
261
- case 'nightcore':
262
- case 'nc': {
263
- if (!player) return message.reply('No player.');
264
- await player.setFilter('nightcore', true);
265
- message.reply('Nightcore enabled');
266
- break;
267
- }
268
-
269
- case 'vaporwave':
270
- case 'vw': {
271
- if (!player) return message.reply('No player.');
272
- await player.setFilter('vaporwave', true);
273
- message.reply('Vaporwave enabled');
274
- break;
275
- }
276
-
277
- case '8d': {
278
- if (!player) return message.reply('No player.');
279
- await player.setFilter('8d', true);
280
- message.reply('8D enabled');
281
- break;
282
- }
283
-
284
- case 'karaoke': {
285
- if (!player) return message.reply('No player.');
286
- await player.setFilter('karaoke', true);
287
- message.reply('Karaoke enabled');
288
- break;
289
- }
290
-
291
- case 'clearfilters':
292
- case 'cf': {
293
- if (!player) return message.reply('No player.');
294
- await player.clearFilters();
295
- message.reply('Filters cleared');
296
- break;
297
- }
298
-
299
- case 'np':
300
- case 'nowplaying': {
301
- if (!player?.queue.current) return message.reply('Nothing playing.');
302
- const t = player.queue.current;
303
- const pos = Math.floor(player.position / 1000);
304
- const filters = Object.keys(player.filters).filter(k => player.filters[k] && k !== 'volume');
305
-
306
- message.reply({
307
- embeds: [new EmbedBuilder()
308
- .setTitle(t.isAutoplay ? '📻 Autoplay' : '▶️ Now Playing')
309
- .setDescription(`**${t.title}**\nby ${t.author}`)
310
- .setThumbnail(t.thumbnail)
311
- .addFields(
312
- { name: 'Position', value: `${pos}s / ${t.duration}s`, inline: true },
313
- { name: 'Volume', value: `${player.volume}%`, inline: true },
314
- { name: 'Filters', value: filters.length ? filters.join(', ') : 'none', inline: true }
315
- )
316
- ]
317
- });
318
- break;
319
- }
320
-
321
- case 'leave':
322
- case 'dc': {
323
- if (!player) return message.reply('Not connected.');
324
- player.destroy();
325
- message.react('👋');
326
- break;
327
- }
328
- }
329
- });
330
-
331
- function setupEvents(player, channel) {
332
- player.on('trackStart', (track) => {
333
- channel.send({
334
- embeds: [new EmbedBuilder()
335
- .setColor(track.isAutoplay ? 0x9b59b6 : 0x00ff00)
336
- .setTitle(track.isAutoplay ? '📻 Autoplay' : '▶️ Now Playing')
337
- .setDescription(`**${track.title}**\nby ${track.author}`)
338
- .setThumbnail(track.thumbnail)
339
- .setFooter({ text: track.requestedBy ? `Requested by ${track.requestedBy.tag}` : '' })
340
- ]
341
- });
342
- });
343
-
344
- player.on('trackError', (track, error) => {
345
- channel.send(`❌ Error: ${error.message}`);
346
- });
347
-
348
- player.on('queueEnd', () => {
349
- channel.send('📭 Queue ended.');
350
- });
351
-
352
- player.on('channelEmpty', () => {
353
- channel.send('⚠️ Channel empty, leaving in 30s...');
354
- });
355
-
356
- player.on('autoPause', () => {
357
- channel.send('⏸️ Auto-paused (empty channel)');
358
- });
359
-
360
- player.on('autoResume', () => {
361
- channel.send('▶️ Resumed');
362
- });
363
- }
364
-
365
- client.login(process.env.DISCORD_TOKEN);
366
- ```
367
-
368
- ## Commands
369
-
370
- | Command | Description |
371
- |---------|-------------|
372
- | `!play <query>` | Play song or playlist |
373
- | `!add <query>` | Add to queue |
374
- | `!skip` | Skip |
375
- | `!prev` | Previous track |
376
- | `!stop` | Stop |
377
- | `!pause` / `!resume` | Pause/Resume |
378
- | `!seek <seconds>` | Seek |
379
- | `!queue` | View queue |
380
- | `!shuffle` | Shuffle |
381
- | `!loop` | Toggle loop |
382
- | `!autoplay` | Toggle autoplay |
383
- | `!vol <0-200>` | Volume |
384
- | `!bass <-20 to 20>` | Bass filter |
385
- | `!nightcore` | Nightcore |
386
- | `!vaporwave` | Vaporwave |
387
- | `!8d` | 8D audio |
388
- | `!karaoke` | Karaoke |
389
- | `!clearfilters` | Clear filters |
390
- | `!np` | Now playing |
391
- | `!leave` | Disconnect |