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.
- package/index.d.ts +9 -0
- package/package.json +1 -1
- package/src/config.js +11 -0
- package/src/discord/Manager.js +1 -1
- package/src/discord/Stream.js +9 -12
- package/src/providers/spotify.js +2 -2
- package/docs/README.md +0 -31
- package/docs/automation.md +0 -186
- package/docs/configuration.md +0 -198
- package/docs/discord/events.md +0 -206
- package/docs/discord/manager.md +0 -195
- package/docs/discord/player.md +0 -263
- package/docs/discord/queue.md +0 -197
- package/docs/examples/advanced-bot.md +0 -391
- package/docs/examples/basic-bot.md +0 -182
- package/docs/examples/lavalink.md +0 -156
- package/docs/filters.md +0 -347
- package/docs/http/endpoints.md +0 -224
- package/docs/http/server.md +0 -174
- package/docs/plans/2026-02-22-stream-revamp-design.md +0 -88
- package/docs/plans/2026-02-22-stream-revamp-plan.md +0 -814
- package/docs/quick-start.md +0 -92
- package/docs/sources.md +0 -189
- package/docs/sponsorblock.md +0 -95
- package/tests/cache.test.js +0 -234
- package/tests/config.test.js +0 -44
- package/tests/error-handling.test.js +0 -318
- package/tests/ffmpeg.test.js +0 -66
- package/tests/filters-edge.test.js +0 -333
- package/tests/http.test.js +0 -24
- package/tests/integration.test.js +0 -325
- package/tests/local.test.js +0 -37
- package/tests/queue.test.js +0 -94
- package/tests/spotify.test.js +0 -238
- package/tests/stream.test.js +0 -217
- package/tests/twitch.test.js +0 -42
- package/tests/utils.test.js +0 -60
- package/tests/youtube.test.js +0 -219
- package/youtube-cookies.txt +0 -26
package/docs/discord/queue.md
DELETED
|
@@ -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 |
|