aqualink 2.19.0 → 2.20.0
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/build/handlers/autoplay.js +22 -6
- package/build/handlers/fetchImage.js +2 -4
- package/build/index.d.ts +383 -5
- package/build/index.js +48 -0
- package/build/structures/Aqua.js +265 -74
- package/build/structures/AqualinkEvents.js +0 -2
- package/build/structures/Connection.js +145 -24
- package/build/structures/Filters.js +53 -5
- package/build/structures/Node.js +40 -5
- package/build/structures/Player.js +166 -58
- package/build/structures/Plugins.js +2 -2
- package/build/structures/Queue.js +13 -12
- package/build/structures/Rest.js +159 -142
- package/build/structures/Track.js +13 -6
- package/package.json +3 -4
package/build/structures/Aqua.js
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
1
|
const fs = require('node:fs')
|
|
4
2
|
const readline = require('node:readline')
|
|
5
|
-
const { EventEmitter } = require('
|
|
3
|
+
const { EventEmitter } = require('node:events')
|
|
6
4
|
const { AqualinkEvents } = require('./AqualinkEvents')
|
|
7
5
|
const Node = require('./Node')
|
|
8
6
|
const Player = require('./Player')
|
|
@@ -23,7 +21,6 @@ const MAX_CONCURRENT_OPS = 10
|
|
|
23
21
|
const BROKEN_PLAYER_TTL = 300000
|
|
24
22
|
const FAILOVER_CLEANUP_TTL = 600000
|
|
25
23
|
const PLAYER_BATCH_SIZE = 20
|
|
26
|
-
const SEEK_DELAY = 120
|
|
27
24
|
const RECONNECT_DELAY = 400
|
|
28
25
|
const CACHE_VALID_TIME = 12000
|
|
29
26
|
const NODE_TIMEOUT = 30000
|
|
@@ -31,8 +28,7 @@ const MAX_CACHE_SIZE = 20
|
|
|
31
28
|
const MAX_FAILOVER_QUEUE = 50
|
|
32
29
|
const MAX_REBUILD_LOCKS = 100
|
|
33
30
|
const WRITE_BUFFER_SIZE = 100
|
|
34
|
-
const
|
|
35
|
-
const MAX_TRACKS_RESTORE = 20
|
|
31
|
+
const TRACE_BUFFER_SIZE = 3000
|
|
36
32
|
|
|
37
33
|
const DEFAULT_OPTIONS = Object.freeze({
|
|
38
34
|
shouldDeleteMessage: false,
|
|
@@ -44,6 +40,10 @@ const DEFAULT_OPTIONS = Object.freeze({
|
|
|
44
40
|
infiniteReconnects: true,
|
|
45
41
|
loadBalancer: 'leastLoad',
|
|
46
42
|
useHttp2: false,
|
|
43
|
+
debugTrace: false,
|
|
44
|
+
traceMaxEntries: TRACE_BUFFER_SIZE,
|
|
45
|
+
traceSink: null,
|
|
46
|
+
autoRegionMigrate: false,
|
|
47
47
|
failoverOptions: Object.freeze({
|
|
48
48
|
enabled: true,
|
|
49
49
|
maxRetries: 3,
|
|
@@ -126,10 +126,22 @@ class Aqua extends EventEmitter {
|
|
|
126
126
|
this.restrictedDomains = merged.restrictedDomains || []
|
|
127
127
|
this.allowedDomains = merged.allowedDomains || []
|
|
128
128
|
this.loadBalancer = merged.loadBalancer
|
|
129
|
+
this.autoRegionMigrate = merged.autoRegionMigrate
|
|
129
130
|
this.useHttp2 = merged.useHttp2
|
|
130
131
|
this.maxQueueSave = merged.maxQueueSave
|
|
131
132
|
this.maxTracksRestore = merged.maxTracksRestore
|
|
132
133
|
this.send = merged.send || this._createDefaultSend()
|
|
134
|
+
this.debugTrace = !!merged.debugTrace
|
|
135
|
+
this.traceMaxEntries = Math.max(
|
|
136
|
+
100,
|
|
137
|
+
Number(merged.traceMaxEntries) || TRACE_BUFFER_SIZE
|
|
138
|
+
)
|
|
139
|
+
this.traceSink =
|
|
140
|
+
typeof merged.traceSink === 'function' ? merged.traceSink : null
|
|
141
|
+
this._traceBuffer = new Array(this.traceMaxEntries)
|
|
142
|
+
this._traceBufferCount = 0
|
|
143
|
+
this._traceBufferIndex = 0
|
|
144
|
+
this._traceSeq = 0
|
|
133
145
|
|
|
134
146
|
this._nodeStates = new Map()
|
|
135
147
|
this._failoverQueue = new Map()
|
|
@@ -145,6 +157,49 @@ class Aqua extends EventEmitter {
|
|
|
145
157
|
if (this.autoResume) this._bindEventHandlers()
|
|
146
158
|
}
|
|
147
159
|
|
|
160
|
+
_trace(event, data = null) {
|
|
161
|
+
if (!this.debugTrace) return
|
|
162
|
+
const entry = {
|
|
163
|
+
seq: ++this._traceSeq,
|
|
164
|
+
at: Date.now(),
|
|
165
|
+
event,
|
|
166
|
+
data
|
|
167
|
+
}
|
|
168
|
+
if (this._traceBuffer.length !== this.traceMaxEntries) {
|
|
169
|
+
this._traceBuffer = new Array(this.traceMaxEntries)
|
|
170
|
+
this._traceBufferCount = 0
|
|
171
|
+
this._traceBufferIndex = 0
|
|
172
|
+
}
|
|
173
|
+
this._traceBuffer[this._traceBufferIndex] = entry
|
|
174
|
+
this._traceBufferIndex = (this._traceBufferIndex + 1) % this.traceMaxEntries
|
|
175
|
+
if (this._traceBufferCount < this.traceMaxEntries) this._traceBufferCount++
|
|
176
|
+
if (this.traceSink) _functions.safeCall(() => this.traceSink(entry))
|
|
177
|
+
if (this.listenerCount(AqualinkEvents.Debug) > 0) {
|
|
178
|
+
this.emit(AqualinkEvents.Debug, 'trace', JSON.stringify(entry))
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
getTrace(limit = 300) {
|
|
183
|
+
const max = Math.max(1, Number(limit) || 300)
|
|
184
|
+
const count = Math.min(max, this._traceBufferCount)
|
|
185
|
+
if (!count) return []
|
|
186
|
+
const out = new Array(count)
|
|
187
|
+
let start =
|
|
188
|
+
(this._traceBufferIndex - count + this.traceMaxEntries) %
|
|
189
|
+
this.traceMaxEntries
|
|
190
|
+
for (let i = 0; i < count; i++) {
|
|
191
|
+
out[i] = this._traceBuffer[start]
|
|
192
|
+
start = (start + 1) % this.traceMaxEntries
|
|
193
|
+
}
|
|
194
|
+
return out
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
clearTrace() {
|
|
198
|
+
this._traceBuffer.fill(undefined)
|
|
199
|
+
this._traceBufferCount = 0
|
|
200
|
+
this._traceBufferIndex = 0
|
|
201
|
+
}
|
|
202
|
+
|
|
148
203
|
_createDefaultSend() {
|
|
149
204
|
return (packet) => {
|
|
150
205
|
const guildId = packet?.d?.guild_id
|
|
@@ -161,12 +216,13 @@ class Aqua extends EventEmitter {
|
|
|
161
216
|
|
|
162
217
|
_bindEventHandlers() {
|
|
163
218
|
this._eventHandlers = {
|
|
164
|
-
onNodeConnect:
|
|
219
|
+
onNodeConnect: (node) => {
|
|
220
|
+
this._trace('node.connect', { node: node?.name || node?.host })
|
|
165
221
|
this._invalidateCache()
|
|
166
|
-
await this._rebuildBrokenPlayers(node)
|
|
167
222
|
this._performCleanup()
|
|
168
223
|
},
|
|
169
224
|
onNodeDisconnect: (node) => {
|
|
225
|
+
this._trace('node.disconnect', { node: node?.name || node?.host })
|
|
170
226
|
this._invalidateCache()
|
|
171
227
|
queueMicrotask(() => {
|
|
172
228
|
this._storeBrokenPlayers(node)
|
|
@@ -174,15 +230,27 @@ class Aqua extends EventEmitter {
|
|
|
174
230
|
})
|
|
175
231
|
},
|
|
176
232
|
onNodeReady: (node, { resumed }) => {
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
233
|
+
this._trace('node.ready', {
|
|
234
|
+
node: node?.name || node?.host,
|
|
235
|
+
resumed: !!resumed,
|
|
236
|
+
players: this.players.size
|
|
237
|
+
})
|
|
238
|
+
if (resumed) {
|
|
239
|
+
const batch = []
|
|
240
|
+
for (const player of this.players.values()) {
|
|
241
|
+
if (player.nodes === node && player.connection) batch.push(player)
|
|
242
|
+
}
|
|
243
|
+
if (batch.length)
|
|
244
|
+
queueMicrotask(() =>
|
|
245
|
+
batch.forEach((p) => {
|
|
246
|
+
p.connection.resendVoiceUpdate()
|
|
247
|
+
})
|
|
248
|
+
)
|
|
249
|
+
return
|
|
181
250
|
}
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
)
|
|
251
|
+
queueMicrotask(() => {
|
|
252
|
+
this._rebuildBrokenPlayers(node).catch(_functions.noop)
|
|
253
|
+
})
|
|
186
254
|
}
|
|
187
255
|
}
|
|
188
256
|
this.on(AqualinkEvents.NodeConnect, this._eventHandlers.onNodeConnect)
|
|
@@ -257,7 +325,11 @@ class Aqua extends EventEmitter {
|
|
|
257
325
|
const id = node.name || node.host
|
|
258
326
|
const now = Date.now()
|
|
259
327
|
const cached = this._nodeLoadCache.get(id)
|
|
260
|
-
if (cached && now - cached.time < 5000)
|
|
328
|
+
if (cached && now - cached.time < 5000) {
|
|
329
|
+
this._nodeLoadCache.delete(id)
|
|
330
|
+
this._nodeLoadCache.set(id, cached)
|
|
331
|
+
return cached.load
|
|
332
|
+
}
|
|
261
333
|
const stats = node?.stats
|
|
262
334
|
if (!stats) return 0
|
|
263
335
|
const cores = Math.max(1, stats.cpu?.cores || 1)
|
|
@@ -267,11 +339,15 @@ class Aqua extends EventEmitter {
|
|
|
267
339
|
(stats.playingPlayers || 0) * 0.75 +
|
|
268
340
|
(stats.memory ? stats.memory.used / reservable : 0) * 40 +
|
|
269
341
|
(node.rest?.calls || 0) * 0.001
|
|
270
|
-
this._nodeLoadCache.
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
342
|
+
if (this._nodeLoadCache.size >= MAX_CACHE_SIZE) {
|
|
343
|
+
const iterator = this._nodeLoadCache.keys()
|
|
344
|
+
while (this._nodeLoadCache.size >= MAX_CACHE_SIZE) {
|
|
345
|
+
const oldest = iterator.next().value
|
|
346
|
+
if (!oldest) break
|
|
347
|
+
this._nodeLoadCache.delete(oldest)
|
|
348
|
+
}
|
|
274
349
|
}
|
|
350
|
+
this._nodeLoadCache.set(id, { load, time: now })
|
|
275
351
|
return load
|
|
276
352
|
}
|
|
277
353
|
|
|
@@ -329,9 +405,8 @@ class Aqua extends EventEmitter {
|
|
|
329
405
|
_destroyNode(id) {
|
|
330
406
|
const node = this.nodeMap.get(id)
|
|
331
407
|
if (!node) return
|
|
332
|
-
_functions.safeCall(() => node.destroy())
|
|
408
|
+
_functions.safeCall(() => node.destroy(true))
|
|
333
409
|
this._cleanupNode(id)
|
|
334
|
-
this.emit(AqualinkEvents.NodeDestroy, node)
|
|
335
410
|
}
|
|
336
411
|
|
|
337
412
|
_cleanupNode(id) {
|
|
@@ -420,18 +495,7 @@ class Aqua extends EventEmitter {
|
|
|
420
495
|
if (current && player?.queue?.add) {
|
|
421
496
|
player.queue.add(current)
|
|
422
497
|
await player.play()
|
|
423
|
-
|
|
424
|
-
const seekOnce = (p) => {
|
|
425
|
-
if (p.guildId === guildId) {
|
|
426
|
-
this.off(AqualinkEvents.TrackStart, seekOnce)
|
|
427
|
-
_functions.unrefTimeout(() => player.seek?.(state.position), 50)
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
this.once(AqualinkEvents.TrackStart, seekOnce)
|
|
431
|
-
player.once('destroy', () =>
|
|
432
|
-
this.off(AqualinkEvents.TrackStart, seekOnce)
|
|
433
|
-
)
|
|
434
|
-
}
|
|
498
|
+
this._seekAfterTrackStart(player, guildId, state.position, 50)
|
|
435
499
|
if (state.paused) player.pause(true)
|
|
436
500
|
}
|
|
437
501
|
return player
|
|
@@ -528,9 +592,111 @@ class Aqua extends EventEmitter {
|
|
|
528
592
|
return newPlayer
|
|
529
593
|
} catch (error) {
|
|
530
594
|
if (retry === maxRetries - 1) throw error
|
|
531
|
-
await _functions.delay(retryDelay *
|
|
595
|
+
await _functions.delay(retryDelay * 1.5 ** retry)
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
_regionMatches(configuredRegion, extractedRegion) {
|
|
601
|
+
if (!configuredRegion || !extractedRegion) return false
|
|
602
|
+
const configured = String(configuredRegion).trim().toLowerCase()
|
|
603
|
+
const extracted = String(extractedRegion).trim().toLowerCase()
|
|
604
|
+
if (!configured || !extracted) return false
|
|
605
|
+
return configured === extracted
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
_findBestNodeForRegion(region) {
|
|
609
|
+
if (!region) return null
|
|
610
|
+
const candidates = []
|
|
611
|
+
for (const node of this.nodeMap.values()) {
|
|
612
|
+
if (!node?.connected) continue
|
|
613
|
+
const regions = Array.isArray(node.regions) ? node.regions : []
|
|
614
|
+
if (regions.some((r) => this._regionMatches(r, region))) {
|
|
615
|
+
candidates.push(node)
|
|
532
616
|
}
|
|
533
617
|
}
|
|
618
|
+
if (!candidates.length) return null
|
|
619
|
+
return this._chooseLeastBusyNode(candidates)
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
async movePlayerToNode(guildId, targetNode, reason = 'region') {
|
|
623
|
+
const id = String(guildId)
|
|
624
|
+
const player = this.players.get(id)
|
|
625
|
+
if (!player || player.destroyed) throw new Error(`Player not found: ${id}`)
|
|
626
|
+
if (!targetNode?.connected) throw new Error('Target node is not connected')
|
|
627
|
+
if (player.nodes === targetNode || player.nodes?.name === targetNode.name)
|
|
628
|
+
return player
|
|
629
|
+
|
|
630
|
+
const state = this._capturePlayerState(player)
|
|
631
|
+
if (!state) throw new Error(`Failed to capture state for ${id}`)
|
|
632
|
+
const oldPlayer = player
|
|
633
|
+
const oldNode = oldPlayer.nodes
|
|
634
|
+
const oldMessage = oldPlayer.nowPlayingMessage || null
|
|
635
|
+
const oldConn = oldPlayer.connection
|
|
636
|
+
const oldVoice = oldConn
|
|
637
|
+
? {
|
|
638
|
+
sessionId: oldConn.sessionId || null,
|
|
639
|
+
endpoint: oldConn.endpoint || null,
|
|
640
|
+
token: oldConn.token || null,
|
|
641
|
+
region: oldConn.region || null,
|
|
642
|
+
channelId: oldConn.channelId || null
|
|
643
|
+
}
|
|
644
|
+
: null
|
|
645
|
+
|
|
646
|
+
oldPlayer.destroy({
|
|
647
|
+
preserveClient: true,
|
|
648
|
+
skipRemote: true,
|
|
649
|
+
preserveMessage: true,
|
|
650
|
+
preserveTracks: true,
|
|
651
|
+
preserveReconnecting: true
|
|
652
|
+
})
|
|
653
|
+
|
|
654
|
+
const newPlayer = this.createPlayer(targetNode, {
|
|
655
|
+
guildId: state.guildId,
|
|
656
|
+
textChannel: state.textChannel,
|
|
657
|
+
voiceChannel: state.voiceChannel,
|
|
658
|
+
defaultVolume: state.volume || 100,
|
|
659
|
+
deaf: state.deaf || false,
|
|
660
|
+
mute: oldPlayer.mute || false,
|
|
661
|
+
resuming: true,
|
|
662
|
+
preserveMessage: true
|
|
663
|
+
})
|
|
664
|
+
|
|
665
|
+
// Bootstrap voice on the new node using last known voice state to avoid
|
|
666
|
+
// "track queued, waiting for voice state" after region migration.
|
|
667
|
+
if (oldVoice && newPlayer.connection) {
|
|
668
|
+
if (oldVoice.sessionId)
|
|
669
|
+
newPlayer.connection.sessionId = oldVoice.sessionId
|
|
670
|
+
if (oldVoice.endpoint) newPlayer.connection.endpoint = oldVoice.endpoint
|
|
671
|
+
if (oldVoice.token) newPlayer.connection.token = oldVoice.token
|
|
672
|
+
if (oldVoice.region) newPlayer.connection.region = oldVoice.region
|
|
673
|
+
if (oldVoice.channelId)
|
|
674
|
+
newPlayer.connection.channelId = oldVoice.channelId
|
|
675
|
+
newPlayer.connection._lastVoiceDataUpdate = Date.now()
|
|
676
|
+
newPlayer.connection.resendVoiceUpdate(true)
|
|
677
|
+
this._trace('player.migrate.voiceBootstrap', {
|
|
678
|
+
guildId: id,
|
|
679
|
+
from: oldNode?.name || oldNode?.host,
|
|
680
|
+
to: targetNode?.name || targetNode?.host,
|
|
681
|
+
hasSessionId: !!newPlayer.connection.sessionId,
|
|
682
|
+
hasEndpoint: !!newPlayer.connection.endpoint,
|
|
683
|
+
hasToken: !!newPlayer.connection.token
|
|
684
|
+
})
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
await this._restorePlayerState(newPlayer, state)
|
|
688
|
+
if (oldMessage) newPlayer.nowPlayingMessage = oldMessage
|
|
689
|
+
|
|
690
|
+
this._trace('player.migrated', {
|
|
691
|
+
guildId: id,
|
|
692
|
+
reason,
|
|
693
|
+
from: oldNode?.name || oldNode?.host,
|
|
694
|
+
to: targetNode?.name || targetNode?.host,
|
|
695
|
+
region:
|
|
696
|
+
newPlayer?.connection?.region || oldPlayer?.connection?.region || null
|
|
697
|
+
})
|
|
698
|
+
this.emit(AqualinkEvents.PlayerMigrated, oldPlayer, newPlayer, targetNode)
|
|
699
|
+
return newPlayer
|
|
534
700
|
}
|
|
535
701
|
|
|
536
702
|
_capturePlayerState(player) {
|
|
@@ -569,6 +735,16 @@ class Aqua extends EventEmitter {
|
|
|
569
735
|
})
|
|
570
736
|
}
|
|
571
737
|
|
|
738
|
+
_seekAfterTrackStart(player, guildId, position, delay = 50) {
|
|
739
|
+
if (!player || !guildId || !(position > 0)) return
|
|
740
|
+
const seekOnce = (p) => {
|
|
741
|
+
if (p.guildId !== guildId) return
|
|
742
|
+
_functions.unrefTimeout(() => player.seek?.(position), delay)
|
|
743
|
+
}
|
|
744
|
+
this.once(AqualinkEvents.TrackStart, seekOnce)
|
|
745
|
+
player.once('destroy', () => this.off(AqualinkEvents.TrackStart, seekOnce))
|
|
746
|
+
}
|
|
747
|
+
|
|
572
748
|
async _restorePlayerState(newPlayer, state) {
|
|
573
749
|
const ops = []
|
|
574
750
|
if (typeof state.volume === 'number') {
|
|
@@ -579,26 +755,17 @@ class Aqua extends EventEmitter {
|
|
|
579
755
|
if (state.queue?.length && newPlayer.queue?.add)
|
|
580
756
|
newPlayer.queue.add(...state.queue)
|
|
581
757
|
if (state.current && this.failoverOptions.preservePosition) {
|
|
582
|
-
newPlayer.queue?.add?.(state.current, { toFront: true })
|
|
583
758
|
if (this.failoverOptions.resumePlayback) {
|
|
584
|
-
ops.push(newPlayer.play())
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
() => newPlayer.seek?.(state.position),
|
|
592
|
-
50
|
|
593
|
-
)
|
|
594
|
-
}
|
|
595
|
-
}
|
|
596
|
-
this.once(AqualinkEvents.TrackStart, seekOnce)
|
|
597
|
-
newPlayer.once('destroy', () =>
|
|
598
|
-
this.off(AqualinkEvents.TrackStart, seekOnce)
|
|
599
|
-
)
|
|
600
|
-
}
|
|
759
|
+
ops.push(newPlayer.play(state.current))
|
|
760
|
+
this._seekAfterTrackStart(
|
|
761
|
+
newPlayer,
|
|
762
|
+
newPlayer.guildId,
|
|
763
|
+
state.position,
|
|
764
|
+
50
|
|
765
|
+
)
|
|
601
766
|
if (state.paused) ops.push(newPlayer.pause(true))
|
|
767
|
+
} else if (newPlayer.queue?.add) {
|
|
768
|
+
newPlayer.queue.add(state.current)
|
|
602
769
|
}
|
|
603
770
|
}
|
|
604
771
|
newPlayer.loop = state.loop
|
|
@@ -614,6 +781,13 @@ class Aqua extends EventEmitter {
|
|
|
614
781
|
return
|
|
615
782
|
const player = this.players.get(String(d.guild_id))
|
|
616
783
|
if (!player) return
|
|
784
|
+
this._trace('voice.gateway', {
|
|
785
|
+
guildId: String(d.guild_id),
|
|
786
|
+
type: t,
|
|
787
|
+
hasSessionId: !!d.session_id,
|
|
788
|
+
hasEndpoint: !!d.endpoint,
|
|
789
|
+
hasChannelId: d.channel_id !== undefined
|
|
790
|
+
})
|
|
617
791
|
|
|
618
792
|
d.txId = player.txId
|
|
619
793
|
if (t === 'VOICE_STATE_UPDATE') {
|
|
@@ -676,6 +850,13 @@ class Aqua extends EventEmitter {
|
|
|
676
850
|
const player = new Player(this, node, options)
|
|
677
851
|
const guildId = String(options.guildId)
|
|
678
852
|
this.players.set(guildId, player)
|
|
853
|
+
this._trace('player.create', {
|
|
854
|
+
guildId,
|
|
855
|
+
node: node?.name || node?.host,
|
|
856
|
+
voiceChannel: options.voiceChannel,
|
|
857
|
+
textChannel: options.textChannel,
|
|
858
|
+
resuming: !!options.resuming
|
|
859
|
+
})
|
|
679
860
|
node?.players?.add?.(player)
|
|
680
861
|
player.once('destroy', () => this._handlePlayerDestroy(player))
|
|
681
862
|
player.connect(options)
|
|
@@ -687,6 +868,10 @@ class Aqua extends EventEmitter {
|
|
|
687
868
|
player.nodes?.players?.delete?.(player)
|
|
688
869
|
const guildId = String(player.guildId)
|
|
689
870
|
if (this.players.get(guildId) === player) this.players.delete(guildId)
|
|
871
|
+
this._trace('player.destroyed', {
|
|
872
|
+
guildId,
|
|
873
|
+
node: player?.nodes?.name || player?.nodes?.host
|
|
874
|
+
})
|
|
690
875
|
this.emit(AqualinkEvents.PlayerDestroyed, player)
|
|
691
876
|
}
|
|
692
877
|
|
|
@@ -694,9 +879,13 @@ class Aqua extends EventEmitter {
|
|
|
694
879
|
const id = String(guildId)
|
|
695
880
|
const player = this.players.get(id)
|
|
696
881
|
if (!player) return
|
|
882
|
+
|
|
883
|
+
// Guard against recursive destroy calls triggered by Player.destroy().
|
|
697
884
|
this.players.delete(id)
|
|
698
|
-
_functions.safeCall(() => player.removeAllListeners())
|
|
699
885
|
await _functions.safeCall(() => player.destroy())
|
|
886
|
+
|
|
887
|
+
// Fallback cleanup in case the player "destroy" listener was not attached.
|
|
888
|
+
if (player?.nodes?.players?.has?.(player)) this._handlePlayerDestroy(player)
|
|
700
889
|
}
|
|
701
890
|
|
|
702
891
|
async resolve({ query, source, requester, nodes }) {
|
|
@@ -774,7 +963,10 @@ class Aqua extends EventEmitter {
|
|
|
774
963
|
}
|
|
775
964
|
if (loadType === 'track' && data) {
|
|
776
965
|
base.pluginInfo =
|
|
777
|
-
data.
|
|
966
|
+
data.pluginInfo ||
|
|
967
|
+
data.info?.pluginInfo ||
|
|
968
|
+
rootPlugin ||
|
|
969
|
+
base.pluginInfo
|
|
778
970
|
base.tracks.push(_functions.makeTrack(data, requester, node))
|
|
779
971
|
} else if (loadType === 'playlist' && data) {
|
|
780
972
|
const info = data.info
|
|
@@ -858,7 +1050,7 @@ class Aqua extends EventEmitter {
|
|
|
858
1050
|
buffer.push(JSON.stringify(data))
|
|
859
1051
|
|
|
860
1052
|
if (buffer.length >= WRITE_BUFFER_SIZE) {
|
|
861
|
-
const chunk = buffer.join('\n')
|
|
1053
|
+
const chunk = `${buffer.join('\n')}\n`
|
|
862
1054
|
buffer.length = 0
|
|
863
1055
|
if (!ws.write(chunk)) {
|
|
864
1056
|
drainPromise = drainPromise.then(
|
|
@@ -868,7 +1060,7 @@ class Aqua extends EventEmitter {
|
|
|
868
1060
|
}
|
|
869
1061
|
}
|
|
870
1062
|
|
|
871
|
-
if (buffer.length) ws.write(buffer.join('\n')
|
|
1063
|
+
if (buffer.length) ws.write(`${buffer.join('\n')}\n`)
|
|
872
1064
|
await drainPromise
|
|
873
1065
|
await new Promise((resolve, reject) =>
|
|
874
1066
|
ws.end((err) => (err ? reject(err) : resolve()))
|
|
@@ -935,7 +1127,7 @@ class Aqua extends EventEmitter {
|
|
|
935
1127
|
try {
|
|
936
1128
|
const gId = String(p.g)
|
|
937
1129
|
const existing = this.players.get(gId)
|
|
938
|
-
if (existing
|
|
1130
|
+
if (existing?.playing) return
|
|
939
1131
|
|
|
940
1132
|
const player =
|
|
941
1133
|
existing ||
|
|
@@ -968,25 +1160,26 @@ class Aqua extends EventEmitter {
|
|
|
968
1160
|
else player.volume = p.vol
|
|
969
1161
|
}
|
|
970
1162
|
|
|
971
|
-
|
|
972
|
-
const seekOnce = (pl) => {
|
|
973
|
-
if (pl.guildId === gId) {
|
|
974
|
-
this.off(AqualinkEvents.TrackStart, seekOnce)
|
|
975
|
-
_functions.unrefTimeout(() => player.seek?.(p.p), 100)
|
|
976
|
-
}
|
|
977
|
-
}
|
|
978
|
-
this.on(AqualinkEvents.TrackStart, seekOnce)
|
|
979
|
-
player.once('destroy', () => this.off(AqualinkEvents.TrackStart, seekOnce))
|
|
980
|
-
}
|
|
1163
|
+
this._seekAfterTrackStart(player, gId, p.p, 100)
|
|
981
1164
|
|
|
982
1165
|
await player.play(undefined, { startTime: p.p, paused: p.pa })
|
|
983
1166
|
}
|
|
984
1167
|
if (p.nw && p.t) {
|
|
985
|
-
const channel = this.client.channels?.cache?.get(p.t)
|
|
986
|
-
if (channel?.messages)
|
|
1168
|
+
const channel = this.client.channels?.cache?.get?.(p.t)
|
|
1169
|
+
if (channel?.messages?.fetch) {
|
|
987
1170
|
player.nowPlayingMessage = await channel.messages
|
|
988
1171
|
.fetch(p.nw)
|
|
989
1172
|
.catch(() => null)
|
|
1173
|
+
} else if (this.client.messages?.fetch) {
|
|
1174
|
+
player.nowPlayingMessage = await this.client.messages
|
|
1175
|
+
.fetch(p.nw, p.t)
|
|
1176
|
+
.catch(() => null)
|
|
1177
|
+
}
|
|
1178
|
+
this._trace('player.nowPlaying.restore', {
|
|
1179
|
+
guildId: gId,
|
|
1180
|
+
messageId: p.nw,
|
|
1181
|
+
restored: !!player.nowPlayingMessage
|
|
1182
|
+
})
|
|
990
1183
|
}
|
|
991
1184
|
} catch (e) {
|
|
992
1185
|
console.error(
|
|
@@ -1067,9 +1260,7 @@ class Aqua extends EventEmitter {
|
|
|
1067
1260
|
}
|
|
1068
1261
|
break
|
|
1069
1262
|
}
|
|
1070
|
-
} catch {
|
|
1071
|
-
continue
|
|
1072
|
-
}
|
|
1263
|
+
} catch {}
|
|
1073
1264
|
}
|
|
1074
1265
|
} catch {
|
|
1075
1266
|
} finally {
|