aqualink 2.9.13 → 2.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +94 -58
- package/build/handlers/autoplay.js +39 -44
- package/build/structures/Aqua.js +483 -493
- package/build/structures/Connection.js +91 -88
- package/build/structures/Filters.js +178 -167
- package/build/structures/Node.js +99 -96
- package/build/structures/Player.js +275 -296
- package/build/structures/Rest.js +265 -146
- package/build/structures/Track.js +51 -126
- package/package.json +17 -2
|
@@ -29,58 +29,55 @@ const FAILURE_REASONS = new Set(['LOAD_FAILED', 'CLEANUP'])
|
|
|
29
29
|
const RECONNECT_CODES = new Set([4015, 4009, 4006])
|
|
30
30
|
const FAIL_LOAD_TYPES = new Set(['error', 'empty', 'LOAD_FAILED', 'NO_MATCHES'])
|
|
31
31
|
|
|
32
|
-
const
|
|
33
|
-
const
|
|
32
|
+
const clamp = v => Math.max(0, Math.min(200, +v || 0))
|
|
33
|
+
const isValidVolume = v => typeof v === 'number' && v >= 0 && v <= 200
|
|
34
|
+
const isValidPosition = p => typeof p === 'number' && !isNaN(p) && p >= 0
|
|
35
|
+
const getRandomIndex = arr => (Math.random() * arr.length) | 0
|
|
34
36
|
|
|
35
|
-
|
|
36
|
-
const _isValidVolume = volume => VOLUME_REGEX.test(String(volume))
|
|
37
|
-
const _isValidPosition = position => POSITION_REGEX.test(String(position))
|
|
38
|
-
|
|
39
|
-
class OptimizedUpdateBatcher {
|
|
37
|
+
class MicrotaskUpdateBatcher {
|
|
40
38
|
constructor(player) {
|
|
41
39
|
this.player = player
|
|
42
40
|
this.updates = null
|
|
43
|
-
this.
|
|
44
|
-
this.hasPending = false
|
|
41
|
+
this.isScheduled = false
|
|
45
42
|
}
|
|
46
43
|
|
|
47
44
|
batch(data, immediate = false) {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
Object.assign(this.updates, data)
|
|
51
|
-
this.hasPending = true
|
|
45
|
+
this.updates ??= Object.create(null)
|
|
52
46
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
47
|
+
const keys = Object.keys(data)
|
|
48
|
+
for (let i = 0; i < keys.length; i++) {
|
|
49
|
+
const key = keys[i]
|
|
50
|
+
this.updates[key] = data[key]
|
|
56
51
|
}
|
|
57
52
|
|
|
58
53
|
if (immediate || data.track) {
|
|
59
54
|
return this._flush()
|
|
60
55
|
}
|
|
61
56
|
|
|
62
|
-
|
|
57
|
+
if (!this.isScheduled) {
|
|
58
|
+
this.isScheduled = true
|
|
59
|
+
queueMicrotask(() => {
|
|
60
|
+
this._flush()
|
|
61
|
+
this.isScheduled = false
|
|
62
|
+
})
|
|
63
|
+
}
|
|
64
|
+
|
|
63
65
|
return Promise.resolve()
|
|
64
66
|
}
|
|
65
67
|
|
|
66
68
|
_flush() {
|
|
67
|
-
if (!this.
|
|
69
|
+
if (!this.updates) return
|
|
68
70
|
|
|
69
71
|
const updates = this.updates
|
|
70
72
|
this.updates = null
|
|
71
|
-
this.
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
return this.player.updatePlayer(updates)
|
|
73
|
+
this.player.updatePlayer(updates).catch(err => {
|
|
74
|
+
console.error('Update player error:', err)
|
|
75
|
+
})
|
|
75
76
|
}
|
|
76
77
|
|
|
77
78
|
destroy() {
|
|
78
|
-
if (this.timeoutId) {
|
|
79
|
-
clearTimeout(this.timeoutId)
|
|
80
|
-
this.timeoutId = 0
|
|
81
|
-
}
|
|
82
79
|
this.updates = null
|
|
83
|
-
this.
|
|
80
|
+
this.isScheduled = false
|
|
84
81
|
}
|
|
85
82
|
}
|
|
86
83
|
|
|
@@ -99,9 +96,7 @@ class CircularBuffer {
|
|
|
99
96
|
}
|
|
100
97
|
|
|
101
98
|
getLast() {
|
|
102
|
-
return this.count
|
|
103
|
-
? this.buffer[(this.index - 1 + this.size) % this.size]
|
|
104
|
-
: null
|
|
99
|
+
return this.count ? this.buffer[(this.index - 1 + this.size) % this.size] : null
|
|
105
100
|
}
|
|
106
101
|
|
|
107
102
|
clear() {
|
|
@@ -115,6 +110,18 @@ class Player extends EventEmitter {
|
|
|
115
110
|
static EVENT_HANDLERS = EVENT_HANDLERS
|
|
116
111
|
static validModes = VALID_MODES
|
|
117
112
|
|
|
113
|
+
playing = false
|
|
114
|
+
paused = false
|
|
115
|
+
connected = false
|
|
116
|
+
isAutoplayEnabled = false
|
|
117
|
+
isAutoplay = false
|
|
118
|
+
autoplaySeed = null
|
|
119
|
+
current = null
|
|
120
|
+
position = 0
|
|
121
|
+
timestamp = 0
|
|
122
|
+
ping = 0
|
|
123
|
+
nowPlayingMessage = null
|
|
124
|
+
|
|
118
125
|
constructor(aqua, nodes, options = {}) {
|
|
119
126
|
super()
|
|
120
127
|
|
|
@@ -124,37 +131,19 @@ class Player extends EventEmitter {
|
|
|
124
131
|
this.textChannel = options.textChannel
|
|
125
132
|
this.voiceChannel = options.voiceChannel
|
|
126
133
|
|
|
127
|
-
this.connection = new Connection(this
|
|
128
|
-
sessionId: options.sessionId,
|
|
129
|
-
token: options.token,
|
|
130
|
-
endpoint: options.endpoint
|
|
131
|
-
})
|
|
132
|
-
|
|
134
|
+
this.connection = new Connection(this)
|
|
133
135
|
this.filters = new Filters(this)
|
|
134
136
|
this.queue = new Queue()
|
|
135
137
|
|
|
136
138
|
const vol = options.defaultVolume ?? 100
|
|
137
|
-
this.volume =
|
|
139
|
+
this.volume = isValidVolume(vol) ? vol : clamp(vol)
|
|
138
140
|
|
|
139
141
|
this.loop = VALID_MODES.has(options.loop) ? options.loop : LOOP_MODES.NONE
|
|
140
|
-
this.shouldDeleteMessage = !!
|
|
141
|
-
this.leaveOnEnd = !!
|
|
142
|
+
this.shouldDeleteMessage = !!aqua.options?.shouldDeleteMessage
|
|
143
|
+
this.leaveOnEnd = !!aqua.options?.leaveOnEnd
|
|
142
144
|
|
|
143
145
|
this.previousTracks = new CircularBuffer(50)
|
|
144
|
-
|
|
145
|
-
this.playing = false
|
|
146
|
-
this.paused = false
|
|
147
|
-
this.connected = false
|
|
148
|
-
this.isAutoplayEnabled = false
|
|
149
|
-
this.isAutoplay = false
|
|
150
|
-
|
|
151
|
-
this.current = null
|
|
152
|
-
this.position = 0
|
|
153
|
-
this.timestamp = 0
|
|
154
|
-
this.ping = 0
|
|
155
|
-
this.nowPlayingMessage = null
|
|
156
|
-
|
|
157
|
-
this._updateBatcher = new OptimizedUpdateBatcher(this)
|
|
146
|
+
this._updateBatcher = new MicrotaskUpdateBatcher(this)
|
|
158
147
|
this._dataStore = new Map()
|
|
159
148
|
|
|
160
149
|
this._boundPlayerUpdate = this._handlePlayerUpdate.bind(this)
|
|
@@ -165,11 +154,11 @@ class Player extends EventEmitter {
|
|
|
165
154
|
}
|
|
166
155
|
|
|
167
156
|
_handlePlayerUpdate(packet) {
|
|
168
|
-
const
|
|
169
|
-
this.position = position
|
|
170
|
-
this.connected = connected
|
|
171
|
-
this.ping = ping
|
|
172
|
-
this.timestamp = time
|
|
157
|
+
const state = packet.state
|
|
158
|
+
this.position = state.position
|
|
159
|
+
this.connected = state.connected
|
|
160
|
+
this.ping = state.ping
|
|
161
|
+
this.timestamp = state.time
|
|
173
162
|
this.aqua.emit('playerUpdate', this, packet)
|
|
174
163
|
}
|
|
175
164
|
|
|
@@ -178,7 +167,7 @@ class Player extends EventEmitter {
|
|
|
178
167
|
|
|
179
168
|
if (!handlerName || typeof this[handlerName] !== 'function') {
|
|
180
169
|
this.aqua.emit('nodeError', this, new Error(`Unknown event: ${payload.type}`))
|
|
181
|
-
return
|
|
170
|
+
return
|
|
182
171
|
}
|
|
183
172
|
|
|
184
173
|
try {
|
|
@@ -188,76 +177,21 @@ class Player extends EventEmitter {
|
|
|
188
177
|
}
|
|
189
178
|
}
|
|
190
179
|
|
|
191
|
-
get previous() {
|
|
192
|
-
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
get currenttrack() {
|
|
196
|
-
return this.current
|
|
197
|
-
}
|
|
180
|
+
get previous() { return this.previousTracks.getLast() }
|
|
181
|
+
get currenttrack() { return this.current }
|
|
182
|
+
getQueue() { return this.queue }
|
|
198
183
|
|
|
199
184
|
batchUpdatePlayer(data, immediate = false) {
|
|
200
185
|
return this._updateBatcher.batch(data, immediate)
|
|
201
186
|
}
|
|
202
187
|
|
|
203
|
-
async autoplay() {
|
|
204
|
-
if (!this.isAutoplayEnabled || !this.previous) return this
|
|
205
|
-
|
|
206
|
-
this.isAutoplay = true
|
|
207
|
-
const prevInfo = this.previous.info
|
|
208
|
-
const { sourceName, identifier, uri, requester } = prevInfo
|
|
209
|
-
|
|
210
|
-
try {
|
|
211
|
-
let query = null
|
|
212
|
-
let source = null
|
|
213
|
-
|
|
214
|
-
if (sourceName === 'youtube') {
|
|
215
|
-
query = `https://www.youtube.com/watch?v=${identifier}&list=RD${identifier}`
|
|
216
|
-
source = 'ytmsearch'
|
|
217
|
-
} else if (sourceName === 'soundcloud') {
|
|
218
|
-
const scResults = await scAutoPlay(uri)
|
|
219
|
-
if (!scResults?.length) return this
|
|
220
|
-
query = scResults[0]
|
|
221
|
-
source = 'scsearch'
|
|
222
|
-
} else if (sourceName === 'spotify') {
|
|
223
|
-
const spResult = await spAutoPlay(identifier)
|
|
224
|
-
if (!spResult) return this
|
|
225
|
-
query = `https://open.spotify.com/track/${spResult}`
|
|
226
|
-
source = 'spotify'
|
|
227
|
-
} else {
|
|
228
|
-
return this
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
const response = await this.aqua.resolve({ query, source, requester })
|
|
232
|
-
|
|
233
|
-
if (!response?.tracks?.length || FAIL_LOAD_TYPES.has(response.loadType)) {
|
|
234
|
-
return this.stop()
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
const tracks = response.tracks
|
|
238
|
-
const track = tracks[Math.floor(Math.random() * tracks.length)]
|
|
239
|
-
|
|
240
|
-
if (!track?.info?.title) {
|
|
241
|
-
throw new Error('Invalid track object')
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
track.requester = this.previous.requester || { id: 'Unknown' }
|
|
245
|
-
this.queue.push(track)
|
|
246
|
-
await this.play()
|
|
247
|
-
|
|
248
|
-
return this
|
|
249
|
-
} catch {
|
|
250
|
-
return this.stop()
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
|
|
254
188
|
setAutoplay(enabled) {
|
|
255
189
|
this.isAutoplayEnabled = !!enabled
|
|
256
190
|
return this
|
|
257
191
|
}
|
|
258
192
|
|
|
259
193
|
async play() {
|
|
260
|
-
if (!this.connected || !this.queue.length) return
|
|
194
|
+
if (!this.connected || !this.queue.length) return
|
|
261
195
|
|
|
262
196
|
const item = this.queue.shift()
|
|
263
197
|
this.current = item.track ? item : await item.resolve(this.aqua)
|
|
@@ -269,7 +203,6 @@ class Player extends EventEmitter {
|
|
|
269
203
|
|
|
270
204
|
connect(options = this) {
|
|
271
205
|
const { guildId, voiceChannel, deaf = true, mute = false } = options
|
|
272
|
-
|
|
273
206
|
this.deaf = deaf
|
|
274
207
|
this.mute = mute
|
|
275
208
|
this.connected = true
|
|
@@ -280,45 +213,41 @@ class Player extends EventEmitter {
|
|
|
280
213
|
self_deaf: deaf,
|
|
281
214
|
self_mute: mute
|
|
282
215
|
})
|
|
283
|
-
|
|
284
216
|
return this
|
|
285
217
|
}
|
|
286
218
|
|
|
287
219
|
destroy() {
|
|
288
220
|
if (!this.connected) return this
|
|
289
221
|
|
|
290
|
-
this.
|
|
222
|
+
this.connected = false
|
|
223
|
+
this._updateBatcher?.destroy()
|
|
291
224
|
|
|
292
225
|
this.send({ guild_id: this.guildId, channel_id: null })
|
|
293
|
-
this.connected = false
|
|
294
|
-
this.voiceChannel = null
|
|
295
226
|
|
|
296
227
|
if (this.nowPlayingMessage) {
|
|
297
|
-
this.nowPlayingMessage.delete().catch(() => {})
|
|
228
|
+
this.nowPlayingMessage.delete().catch(() => { })
|
|
298
229
|
this.nowPlayingMessage = null
|
|
299
230
|
}
|
|
300
231
|
|
|
232
|
+
this.voiceChannel = null
|
|
301
233
|
this.isAutoplay = false
|
|
302
|
-
this.aqua.destroyPlayer(this.guildId)
|
|
303
234
|
|
|
235
|
+
this.aqua.destroyPlayer(this.guildId)
|
|
304
236
|
if (this.nodes?.connected) {
|
|
305
|
-
|
|
306
|
-
this.nodes.rest.destroyPlayer(this.guildId)
|
|
307
|
-
} catch (error) {
|
|
237
|
+
this.nodes.rest.destroyPlayer(this.guildId).catch(error => {
|
|
308
238
|
if (!error.message.includes('ECONNREFUSED')) {
|
|
309
|
-
console.error(
|
|
239
|
+
console.error(`[Player ${this.guildId}] Destroy error:`, error.message)
|
|
310
240
|
}
|
|
311
|
-
}
|
|
241
|
+
})
|
|
312
242
|
}
|
|
313
243
|
|
|
314
|
-
this.previousTracks
|
|
315
|
-
this._dataStore
|
|
244
|
+
this.previousTracks?.clear()
|
|
245
|
+
this._dataStore?.clear()
|
|
246
|
+
|
|
316
247
|
this.removeAllListeners()
|
|
317
248
|
|
|
318
|
-
this.queue =
|
|
319
|
-
|
|
320
|
-
this.connection = null
|
|
321
|
-
this.filters = null
|
|
249
|
+
this.queue = this.previousTracks = this.connection = this.filters =
|
|
250
|
+
this._updateBatcher = this._dataStore = null
|
|
322
251
|
|
|
323
252
|
return this
|
|
324
253
|
}
|
|
@@ -330,44 +259,11 @@ class Player extends EventEmitter {
|
|
|
330
259
|
return this
|
|
331
260
|
}
|
|
332
261
|
|
|
333
|
-
async getLyrics(options = {}) {
|
|
334
|
-
const { query, useCurrentTrack = true, skipTrackSource = false } = options
|
|
335
|
-
|
|
336
|
-
if (query) {
|
|
337
|
-
return this.nodes.rest.getLyrics({
|
|
338
|
-
track: { info: { title: query } },
|
|
339
|
-
skipTrackSource
|
|
340
|
-
})
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
if (useCurrentTrack && this.playing && this.current?.info) {
|
|
344
|
-
return this.nodes.rest.getLyrics({
|
|
345
|
-
track: {
|
|
346
|
-
info: this.current.info,
|
|
347
|
-
identifier: this.current.info.identifier,
|
|
348
|
-
guild_id: this.guildId
|
|
349
|
-
},
|
|
350
|
-
skipTrackSource
|
|
351
|
-
})
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
return null
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
subscribeLiveLyrics() {
|
|
358
|
-
return this.nodes.rest.subscribeLiveLyrics(this.guildId, false)
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
unsubscribeLiveLyrics() {
|
|
362
|
-
return this.nodes.rest.unsubscribeLiveLyrics(this.guildId)
|
|
363
|
-
}
|
|
364
|
-
|
|
365
262
|
seek(position) {
|
|
366
|
-
if (!this.playing || !
|
|
263
|
+
if (!this.playing || !isValidPosition(position)) return this
|
|
367
264
|
|
|
368
265
|
const maxPos = this.current?.info?.length
|
|
369
|
-
this.position =
|
|
370
|
-
|
|
266
|
+
this.position = maxPos ? Math.min(Math.max(0, position), maxPos) : Math.max(0, position)
|
|
371
267
|
this.batchUpdatePlayer({ position: this.position })
|
|
372
268
|
return this
|
|
373
269
|
}
|
|
@@ -381,20 +277,16 @@ class Player extends EventEmitter {
|
|
|
381
277
|
}
|
|
382
278
|
|
|
383
279
|
setVolume(volume) {
|
|
384
|
-
if (!
|
|
385
|
-
|
|
386
|
-
const vol = _clampVolume(volume)
|
|
280
|
+
if (!isValidVolume(volume)) return this
|
|
281
|
+
const vol = clamp(volume)
|
|
387
282
|
if (this.volume === vol) return this
|
|
388
|
-
|
|
389
283
|
this.volume = vol
|
|
390
284
|
this.batchUpdatePlayer({ volume: vol })
|
|
391
285
|
return this
|
|
392
286
|
}
|
|
393
287
|
|
|
394
288
|
setLoop(mode) {
|
|
395
|
-
if (!VALID_MODES.has(mode))
|
|
396
|
-
throw new Error('Invalid loop mode')
|
|
397
|
-
}
|
|
289
|
+
if (!VALID_MODES.has(mode)) throw new Error('Invalid loop mode')
|
|
398
290
|
this.loop = mode
|
|
399
291
|
this.batchUpdatePlayer({ loop: mode })
|
|
400
292
|
return this
|
|
@@ -417,7 +309,6 @@ class Player extends EventEmitter {
|
|
|
417
309
|
deaf: this.deaf,
|
|
418
310
|
guildId: this.guildId,
|
|
419
311
|
voiceChannel: channel,
|
|
420
|
-
textChannel: this.textChannel,
|
|
421
312
|
mute: this.mute
|
|
422
313
|
})
|
|
423
314
|
return this
|
|
@@ -435,29 +326,125 @@ class Player extends EventEmitter {
|
|
|
435
326
|
const queue = this.queue
|
|
436
327
|
const len = queue.length
|
|
437
328
|
|
|
438
|
-
if (len <= 1) return this
|
|
439
|
-
|
|
440
329
|
for (let i = len - 1; i > 0; i--) {
|
|
441
|
-
const j =
|
|
442
|
-
|
|
330
|
+
const j = (Math.random() * (i + 1)) | 0
|
|
331
|
+
;[queue[i], queue[j]] = [queue[j], queue[i]]
|
|
443
332
|
}
|
|
444
|
-
|
|
445
333
|
return this
|
|
446
334
|
}
|
|
447
335
|
|
|
448
|
-
|
|
449
|
-
return this.queue
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
replay() {
|
|
453
|
-
return this.seek(0)
|
|
454
|
-
}
|
|
336
|
+
replay() { return this.seek(0) }
|
|
455
337
|
|
|
456
338
|
skip() {
|
|
457
339
|
this.stop()
|
|
458
340
|
return this.playing ? this.play() : undefined
|
|
459
341
|
}
|
|
460
342
|
|
|
343
|
+
async getLyrics({ query, useCurrentTrack = true, skipTrackSource = false } = {}) {
|
|
344
|
+
if (query) {
|
|
345
|
+
return this.nodes.rest.getLyrics({
|
|
346
|
+
track: { info: { title: query } },
|
|
347
|
+
skipTrackSource
|
|
348
|
+
})
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
if (useCurrentTrack && this.playing && this.current) {
|
|
352
|
+
const currentInfo = this.current.info
|
|
353
|
+
return this.nodes.rest.getLyrics({
|
|
354
|
+
track: {
|
|
355
|
+
info: currentInfo,
|
|
356
|
+
encoded: this.current.track,
|
|
357
|
+
identifier: currentInfo.identifier,
|
|
358
|
+
guild_id: this.guildId
|
|
359
|
+
},
|
|
360
|
+
skipTrackSource
|
|
361
|
+
})
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
return null
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
subscribeLiveLyrics() {
|
|
368
|
+
return this.nodes.rest.subscribeLiveLyrics(this.guildId, false)
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
unsubscribeLiveLyrics() {
|
|
372
|
+
return this.nodes.rest.unsubscribeLiveLyrics(this.guildId)
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
async autoplay() {
|
|
376
|
+
if (!this.isAutoplayEnabled || !this.previous) return this
|
|
377
|
+
|
|
378
|
+
this.isAutoplay = true
|
|
379
|
+
const prevInfo = this.previous.info
|
|
380
|
+
const sourceName = prevInfo.sourceName
|
|
381
|
+
const identifier = prevInfo.identifier
|
|
382
|
+
const uri = prevInfo.uri
|
|
383
|
+
const requester = prevInfo.requester
|
|
384
|
+
const author = prevInfo.author
|
|
385
|
+
|
|
386
|
+
try {
|
|
387
|
+
let query = null
|
|
388
|
+
let source = null
|
|
389
|
+
let resolved = null
|
|
390
|
+
let track = null
|
|
391
|
+
|
|
392
|
+
if (sourceName === 'youtube') {
|
|
393
|
+
query = `https://www.youtube.com/watch?v=${identifier}&list=RD${identifier}`
|
|
394
|
+
source = 'ytmsearch'
|
|
395
|
+
} else if (sourceName === 'soundcloud') {
|
|
396
|
+
const scResults = await scAutoPlay(uri)
|
|
397
|
+
if (!scResults?.length) return this
|
|
398
|
+
query = scResults[0]
|
|
399
|
+
source = 'scsearch'
|
|
400
|
+
} else if (sourceName === 'spotify') {
|
|
401
|
+
this.previousIdentifiers ??= []
|
|
402
|
+
|
|
403
|
+
if (this.previous) {
|
|
404
|
+
this.previousIdentifiers.unshift(this.previous.identifier)
|
|
405
|
+
if (this.previousIdentifiers.length >= 20) this.previousIdentifiers.pop()
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
if (!this.autoplaySeed) {
|
|
409
|
+
this.autoplaySeed = {
|
|
410
|
+
trackId: identifier,
|
|
411
|
+
artistIds: Array.isArray(author) ? author.join(',') : author
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
resolved = await spAutoPlay(
|
|
416
|
+
this.autoplaySeed,
|
|
417
|
+
this,
|
|
418
|
+
requester,
|
|
419
|
+
this.previousIdentifiers
|
|
420
|
+
)
|
|
421
|
+
if (!resolved?.length) return this
|
|
422
|
+
} else {
|
|
423
|
+
return this
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
if (resolved) {
|
|
427
|
+
track = resolved[getRandomIndex(resolved)]
|
|
428
|
+
} else {
|
|
429
|
+
const response = await this.aqua.resolve({ query, source, requester })
|
|
430
|
+
if (!response?.tracks?.length || FAIL_LOAD_TYPES.has(response.loadType)) {
|
|
431
|
+
return this.stop()
|
|
432
|
+
}
|
|
433
|
+
track = response.tracks[getRandomIndex(response.tracks)]
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
if (!track?.info?.title) throw new Error('Invalid track object')
|
|
437
|
+
|
|
438
|
+
track.requester = this.previous.requester || { id: 'Unknown' }
|
|
439
|
+
this.queue.push(track)
|
|
440
|
+
await this.play()
|
|
441
|
+
return this
|
|
442
|
+
} catch (err) {
|
|
443
|
+
console.error('Autoplay failed:', err)
|
|
444
|
+
return this.stop()
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
461
448
|
async trackStart(player, track) {
|
|
462
449
|
this.playing = true
|
|
463
450
|
this.paused = false
|
|
@@ -465,26 +452,24 @@ class Player extends EventEmitter {
|
|
|
465
452
|
}
|
|
466
453
|
|
|
467
454
|
async trackEnd(player, track, payload) {
|
|
468
|
-
if (track)
|
|
469
|
-
this.previousTracks.push(track)
|
|
470
|
-
}
|
|
455
|
+
if (track) this.previousTracks.push(track)
|
|
471
456
|
|
|
472
457
|
if (this.shouldDeleteMessage && this.nowPlayingMessage) {
|
|
473
|
-
this.nowPlayingMessage.delete().catch(() => {})
|
|
458
|
+
this.nowPlayingMessage.delete().catch(() => { })
|
|
474
459
|
this.nowPlayingMessage = null
|
|
475
460
|
}
|
|
476
461
|
|
|
477
|
-
const
|
|
462
|
+
const reason = payload.reason
|
|
463
|
+
|
|
478
464
|
if (FAILURE_REASONS.has(reason)) {
|
|
479
|
-
if (this.queue.length
|
|
480
|
-
this.
|
|
481
|
-
this._dataStore.clear()
|
|
465
|
+
if (!this.queue.length) {
|
|
466
|
+
this.clearData()
|
|
482
467
|
this.aqua.emit('queueEnd', player)
|
|
483
468
|
} else {
|
|
484
469
|
this.aqua.emit('trackEnd', player, track, reason)
|
|
485
470
|
await player.play()
|
|
486
471
|
}
|
|
487
|
-
return
|
|
472
|
+
return
|
|
488
473
|
}
|
|
489
474
|
|
|
490
475
|
if (this.loop === LOOP_MODES.TRACK) {
|
|
@@ -499,8 +484,7 @@ class Player extends EventEmitter {
|
|
|
499
484
|
} else {
|
|
500
485
|
this.playing = false
|
|
501
486
|
if (this.leaveOnEnd) {
|
|
502
|
-
this.
|
|
503
|
-
this._dataStore.clear()
|
|
487
|
+
this.clearData()
|
|
504
488
|
this.destroy()
|
|
505
489
|
}
|
|
506
490
|
this.aqua.emit('queueEnd', player)
|
|
@@ -522,100 +506,105 @@ class Player extends EventEmitter {
|
|
|
522
506
|
}
|
|
523
507
|
|
|
524
508
|
async socketClosed(player, track, payload) {
|
|
525
|
-
|
|
509
|
+
if (payload.code === 4014) return this.destroy()
|
|
526
510
|
|
|
527
|
-
if (
|
|
511
|
+
if (payload.code === 4015) {
|
|
528
512
|
try {
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
const savedConnectionState = {
|
|
534
|
-
sessionId: this.connection?.sessionId,
|
|
535
|
-
endpoint: this.connection?.endpoint,
|
|
536
|
-
token: this.connection?.token,
|
|
537
|
-
region: this.connection?.region,
|
|
538
|
-
volume: this.volume,
|
|
539
|
-
position: this.position,
|
|
540
|
-
paused: this.paused,
|
|
541
|
-
loop: this.loop,
|
|
542
|
-
isAutoplayEnabled: this.isAutoplayEnabled
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
if (!voiceChannelId) {
|
|
546
|
-
this.aqua.emit('socketClosed', player, payload)
|
|
513
|
+
if (this.connection) {
|
|
514
|
+
this.connection._updatePlayerVoiceData(true);
|
|
515
|
+
this.aqua.emit('debug', `[Player ${this.guildId}] Attempting resume...`);
|
|
547
516
|
return;
|
|
548
517
|
}
|
|
518
|
+
} catch (error) {
|
|
519
|
+
console.error('Resume failed, falling back to reconnect', error);
|
|
520
|
+
}
|
|
521
|
+
}
|
|
549
522
|
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
523
|
+
if (!RECONNECT_CODES.has(payload.code)) {
|
|
524
|
+
this.aqua.emit('socketClosed', player, payload)
|
|
525
|
+
return
|
|
526
|
+
}
|
|
554
527
|
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
defaultVolume: savedConnectionState.volume,
|
|
562
|
-
sessionId: savedConnectionState.sessionId,
|
|
563
|
-
token: savedConnectionState.token,
|
|
564
|
-
endpoint: savedConnectionState.endpoint
|
|
565
|
-
})
|
|
566
|
-
|
|
567
|
-
if (newPlayer) {
|
|
568
|
-
newPlayer.loop = savedConnectionState.loop
|
|
569
|
-
newPlayer.isAutoplayEnabled = savedConnectionState.isAutoplayEnabled
|
|
570
|
-
|
|
571
|
-
if (savedConnectionState.sessionId && newPlayer.connection) {
|
|
572
|
-
Object.assign(newPlayer.connection, {
|
|
573
|
-
sessionId: savedConnectionState.sessionId,
|
|
574
|
-
endpoint: savedConnectionState.endpoint,
|
|
575
|
-
token: savedConnectionState.token,
|
|
576
|
-
region: savedConnectionState.region
|
|
577
|
-
})
|
|
578
|
-
}
|
|
528
|
+
try {
|
|
529
|
+
const voiceChannelId = this.voiceChannel?.id || this.voiceChannel
|
|
530
|
+
if (!voiceChannelId) {
|
|
531
|
+
this.aqua.emit('socketClosed', player, payload)
|
|
532
|
+
return
|
|
533
|
+
}
|
|
579
534
|
|
|
580
|
-
|
|
581
|
-
|
|
535
|
+
const connection = this.connection
|
|
536
|
+
const savedState = {
|
|
537
|
+
sessionId: connection?.sessionId,
|
|
538
|
+
endpoint: connection?.endpoint,
|
|
539
|
+
token: connection?.token,
|
|
540
|
+
region: connection?.region,
|
|
541
|
+
volume: this.volume,
|
|
542
|
+
position: this.position,
|
|
543
|
+
paused: this.paused,
|
|
544
|
+
loop: this.loop,
|
|
545
|
+
isAutoplayEnabled: this.isAutoplayEnabled,
|
|
546
|
+
currentTrack: this.current,
|
|
547
|
+
queue: [...this.queue]
|
|
548
|
+
}
|
|
582
549
|
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
550
|
+
if (!player.destroyed) {
|
|
551
|
+
await player.destroy()
|
|
552
|
+
this.aqua.emit('playerDestroy', player)
|
|
553
|
+
}
|
|
586
554
|
|
|
587
|
-
|
|
555
|
+
const newPlayer = await this.aqua.createConnection({
|
|
556
|
+
guildId: payload.guildId,
|
|
557
|
+
voiceChannel: voiceChannelId,
|
|
558
|
+
textChannel: this.textChannel?.id || this.textChannel,
|
|
559
|
+
deaf: this.deaf,
|
|
560
|
+
mute: this.mute,
|
|
561
|
+
defaultVolume: savedState.volume,
|
|
562
|
+
})
|
|
588
563
|
|
|
589
|
-
|
|
590
|
-
setTimeout(() => {
|
|
591
|
-
newPlayer.seek(savedConnectionState.position)
|
|
592
|
-
}, 1000)
|
|
593
|
-
}
|
|
564
|
+
if (!newPlayer) throw new Error('Failed to create a new player during reconnection.')
|
|
594
565
|
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
newPlayer.pause(true)
|
|
598
|
-
}, 1500)
|
|
599
|
-
}
|
|
600
|
-
}
|
|
566
|
+
newPlayer.loop = savedState.loop
|
|
567
|
+
newPlayer.isAutoplayEnabled = savedState.isAutoplayEnabled
|
|
601
568
|
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
569
|
+
if (savedState.sessionId && newPlayer.connection) {
|
|
570
|
+
const newConnection = newPlayer.connection
|
|
571
|
+
newConnection.sessionId = savedState.sessionId
|
|
572
|
+
newConnection.endpoint = savedState.endpoint
|
|
573
|
+
newConnection.token = savedState.token
|
|
574
|
+
newConnection.region = savedState.region
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
if (savedState.currentTrack) {
|
|
578
|
+
newPlayer.queue.add(savedState.currentTrack)
|
|
579
|
+
const queue = savedState.queue
|
|
580
|
+
for (let i = 0; i < queue.length; i++) {
|
|
581
|
+
newPlayer.queue.add(queue[i])
|
|
607
582
|
}
|
|
608
583
|
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
584
|
+
await newPlayer.play()
|
|
585
|
+
|
|
586
|
+
if (savedState.position > 5000) {
|
|
587
|
+
setTimeout(() => newPlayer.seek(savedState.position), 1000)
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
if (savedState.paused) {
|
|
591
|
+
setTimeout(() => newPlayer.pause(true), 1500)
|
|
592
|
+
}
|
|
614
593
|
}
|
|
615
|
-
return;
|
|
616
|
-
}
|
|
617
594
|
|
|
618
|
-
|
|
595
|
+
this.aqua.emit('playerReconnected', newPlayer, {
|
|
596
|
+
oldPlayer: player,
|
|
597
|
+
restoredState: savedState
|
|
598
|
+
})
|
|
599
|
+
} catch (error) {
|
|
600
|
+
console.error('Reconnection failed:', error)
|
|
601
|
+
this.aqua.emit('reconnectionFailed', player, {
|
|
602
|
+
error,
|
|
603
|
+
code: payload.code,
|
|
604
|
+
payload
|
|
605
|
+
})
|
|
606
|
+
this.aqua.emit('socketClosed', player, payload)
|
|
607
|
+
}
|
|
619
608
|
}
|
|
620
609
|
|
|
621
610
|
async lyricsLine(player, track, payload) {
|
|
@@ -630,21 +619,13 @@ class Player extends EventEmitter {
|
|
|
630
619
|
this.aqua.emit('lyricsNotFound', player, track, payload)
|
|
631
620
|
}
|
|
632
621
|
|
|
633
|
-
send(data) {
|
|
634
|
-
|
|
635
|
-
}
|
|
636
|
-
|
|
637
|
-
set(key, value) {
|
|
638
|
-
this._dataStore.set(key, value)
|
|
639
|
-
}
|
|
640
|
-
|
|
641
|
-
get(key) {
|
|
642
|
-
return this._dataStore.get(key)
|
|
643
|
-
}
|
|
622
|
+
send(data) { this.aqua.send({ op: 4, d: data }) }
|
|
623
|
+
set(key, value) { this._dataStore.set(key, value) }
|
|
624
|
+
get(key) { return this._dataStore.get(key) }
|
|
644
625
|
|
|
645
626
|
clearData() {
|
|
646
|
-
this.previousTracks
|
|
647
|
-
this._dataStore
|
|
627
|
+
this.previousTracks?.clear()
|
|
628
|
+
this._dataStore?.clear()
|
|
648
629
|
return this
|
|
649
630
|
}
|
|
650
631
|
|
|
@@ -653,9 +634,7 @@ class Player extends EventEmitter {
|
|
|
653
634
|
}
|
|
654
635
|
|
|
655
636
|
async cleanup() {
|
|
656
|
-
if (!this.playing && !this.paused && this.queue.isEmpty())
|
|
657
|
-
this.destroy()
|
|
658
|
-
}
|
|
637
|
+
if (!this.playing && !this.paused && this.queue.isEmpty()) this.destroy()
|
|
659
638
|
}
|
|
660
639
|
}
|
|
661
640
|
|