aqualink 2.19.1 → 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.
@@ -1,4 +1,4 @@
1
- const { EventEmitter } = require('tseep')
1
+ const { EventEmitter } = require('node:events')
2
2
  const { AqualinkEvents } = require('./AqualinkEvents')
3
3
  const Connection = require('./Connection')
4
4
  const Filters = require('./Filters')
@@ -42,6 +42,7 @@ const RECONNECT_MAX = 15
42
42
  const MUTE_TOGGLE_DELAY = 300
43
43
  const SEEK_DELAY = 800
44
44
  const PAUSE_DELAY = 1200
45
+ const VOICE_TRACE_INTERVAL = 15000
45
46
  const RETRY_BACKOFF_BASE = 1500
46
47
  const RETRY_BACKOFF_MAX = 5000
47
48
  const PREVIOUS_TRACKS_SIZE = 50
@@ -53,7 +54,7 @@ const INVALID_LOADS = new Set(['error', 'empty', 'LOAD_FAILED', 'NO_MATCHES'])
53
54
  const _functions = {
54
55
  clamp(v) {
55
56
  const n = +v
56
- return Number.isNaN(n) ? 100 : n < 0 ? 0 : n > 200 ? 200 : n
57
+ return Number.isNaN(n) ? 100 : n < 0 ? 0 : n > 1000 ? 1000 : n
57
58
  },
58
59
  randIdx: (len) => (Math.random() * len) | 0,
59
60
  toId: (v) => v?.id || v || null,
@@ -213,6 +214,8 @@ class Player extends EventEmitter {
213
214
  this._voiceRequestAt = 0
214
215
  this._voiceRequestChannel = null
215
216
  this._suppressResumeUntil = 0
217
+ this._deferredStart = false
218
+ this._lastVoiceUpTraceAt = 0
216
219
  this._bindEvents()
217
220
  this._startWatchdog()
218
221
  }
@@ -254,13 +257,25 @@ class Player extends EventEmitter {
254
257
  _handlePlayerUpdate(packet) {
255
258
  if (this.destroyed || !packet?.state) return
256
259
  const s = packet.state
260
+ const wasConnected = this.connected
257
261
  this.position = _functions.isNum(s.position) ? s.position : 0
258
262
  this.connected = !!s.connected
259
263
  this.ping = _functions.isNum(s.ping) ? s.ping : 0
260
264
  this.timestamp = _functions.isNum(s.time) ? s.time : Date.now()
261
265
 
262
266
  if (!this.connected) {
263
- if (!this._voiceDownSince && !this._reconnecting && !this._voiceRecovering) {
267
+ if (wasConnected || !this._voiceDownSince) {
268
+ this.aqua?._trace?.('player.voice.down', {
269
+ guildId: this.guildId,
270
+ reconnecting: !!this._reconnecting,
271
+ recovering: !!this._voiceRecovering
272
+ })
273
+ }
274
+ if (
275
+ !this._voiceDownSince &&
276
+ !this._reconnecting &&
277
+ !this._voiceRecovering
278
+ ) {
264
279
  this._voiceDownSince = Date.now()
265
280
  this._createTimer(() => {
266
281
  if (
@@ -277,6 +292,17 @@ class Player extends EventEmitter {
277
292
  } else {
278
293
  this._voiceDownSince = 0
279
294
  this.state = PLAYER_STATE.READY
295
+ const now = Date.now()
296
+ if (
297
+ !wasConnected ||
298
+ now - this._lastVoiceUpTraceAt >= VOICE_TRACE_INTERVAL
299
+ ) {
300
+ this._lastVoiceUpTraceAt = now
301
+ this.aqua?._trace?.('player.voice.up', {
302
+ guildId: this.guildId,
303
+ ping: this.ping
304
+ })
305
+ }
280
306
  }
281
307
 
282
308
  this.aqua.emit(AqualinkEvents.PlayerUpdate, this, packet)
@@ -294,7 +320,11 @@ class Player extends EventEmitter {
294
320
  return
295
321
  }
296
322
  try {
297
- await this[handler](this, this.current, payload)
323
+ const trackArg =
324
+ payload.type === 'TrackStartEvent'
325
+ ? payload.track || this.current
326
+ : this.current
327
+ await this[handler](this, trackArg, payload)
298
328
  } catch (error) {
299
329
  this.aqua.emit(AqualinkEvents.Error, error)
300
330
  }
@@ -334,23 +364,48 @@ class Player extends EventEmitter {
334
364
  if (!item) return this
335
365
 
336
366
  try {
337
- this.current = item.track ? item : await item.resolve(this.aqua)
367
+ let resolvedItem = null
368
+ if (item?.track) {
369
+ resolvedItem = item
370
+ } else if (typeof item?.resolve === 'function') {
371
+ resolvedItem = await item.resolve(this.aqua)
372
+ }
373
+ this.current = resolvedItem
338
374
  if (this.destroyed) return this
339
375
  if (!this.current?.track) throw new Error('Failed to resolve track')
340
376
 
341
377
  this.playing = true
342
378
  this.paused = !!options.paused
343
379
  this.position = options.startTime || 0
380
+ this.aqua?._trace?.('player.play', {
381
+ guildId: this.guildId,
382
+ paused: this.paused,
383
+ startTime: this.position,
384
+ hasTrack: !!this.current?.track
385
+ })
344
386
 
345
387
  if (this.destroyed || !this._updateBatcher) return this
346
388
 
389
+ if (
390
+ this.aqua?.autoRegionMigrate &&
391
+ !this._resuming &&
392
+ !this.connection?.endpoint
393
+ ) {
394
+ this._deferredStart = true
395
+ this.aqua?._trace?.('player.play.deferred', {
396
+ guildId: this.guildId,
397
+ reason: 'awaiting_voice_server_update'
398
+ })
399
+ return this
400
+ }
401
+
347
402
  const updateData = {
348
- guildId: this.guildId,
349
403
  track: { encoded: this.current.track },
350
- paused: this.paused,
404
+ paused: this.paused
351
405
  }
352
406
  if (this.position > 0) updateData.position = this.position
353
407
 
408
+ this._deferredStart = false
354
409
  await this.batchUpdatePlayer(updateData, true)
355
410
  } catch (error) {
356
411
  if (!this.destroyed) this.aqua?.emit(AqualinkEvents.Error, error)
@@ -382,6 +437,13 @@ class Player extends EventEmitter {
382
437
  self_deaf: this.deaf,
383
438
  self_mute: this.mute
384
439
  })
440
+ this.aqua?._trace?.('player.connect.request', {
441
+ guildId: this.guildId,
442
+ txId: this.txId,
443
+ voiceChannel,
444
+ deaf: this.deaf,
445
+ mute: this.mute
446
+ })
385
447
  return this
386
448
  }
387
449
 
@@ -462,6 +524,12 @@ class Player extends EventEmitter {
462
524
 
463
525
  if (!this.destroyed) {
464
526
  this.destroyed = true
527
+ this.aqua?._trace?.('player.destroy', {
528
+ guildId: this.guildId,
529
+ skipRemote: !!skipRemote,
530
+ preserveTracks: !!preserveTracks,
531
+ preserveReconnecting: !!preserveReconnecting
532
+ })
465
533
  this.emit('destroy')
466
534
  }
467
535
 
@@ -480,6 +548,7 @@ class Player extends EventEmitter {
480
548
  }
481
549
 
482
550
  this.connected = this.playing = this.paused = this.isAutoplay = false
551
+ this._deferredStart = false
483
552
  this.state = PLAYER_STATE.DESTROYED
484
553
  this.autoplayRetries = this.reconnectionRetries = 0
485
554
  if (!preserveReconnecting) this._reconnecting = false
@@ -547,10 +616,7 @@ class Player extends EventEmitter {
547
616
  pause(paused) {
548
617
  if (this.destroyed || this.paused === !!paused) return this
549
618
  this.paused = !!paused
550
- this.batchUpdatePlayer(
551
- { guildId: this.guildId, paused: this.paused },
552
- true
553
- ).catch(() => {})
619
+ this.batchUpdatePlayer({ paused: this.paused }, true).catch(() => {})
554
620
  return this
555
621
  }
556
622
 
@@ -562,10 +628,7 @@ class Player extends EventEmitter {
562
628
  ? Math.min(Math.max(position, 0), len)
563
629
  : Math.max(position, 0)
564
630
  this.position = clamped
565
- this.batchUpdatePlayer(
566
- { guildId: this.guildId, position: clamped },
567
- true
568
- ).catch(() => {})
631
+ this.batchUpdatePlayer({ position: clamped }, true).catch(() => {})
569
632
  return this
570
633
  }
571
634
 
@@ -617,7 +680,7 @@ class Player extends EventEmitter {
617
680
  this.playing = this.paused = false
618
681
  this.position = 0
619
682
  this.batchUpdatePlayer(
620
- { guildId: this.guildId, track: { encoded: null, paused: this.paused } },
683
+ { track: { encoded: null }, paused: this.paused },
621
684
  true
622
685
  ).catch(() => {})
623
686
  return this
@@ -627,9 +690,7 @@ class Player extends EventEmitter {
627
690
  const vol = _functions.clamp(volume)
628
691
  if (this.destroyed || this.volume === vol) return this
629
692
  this.volume = vol
630
- this.batchUpdatePlayer({ guildId: this.guildId, volume: vol }).catch(
631
- () => {}
632
- )
693
+ this.batchUpdatePlayer({ volume: vol }).catch(() => {})
633
694
  return this
634
695
  }
635
696
 
@@ -646,9 +707,7 @@ class Player extends EventEmitter {
646
707
  const id = _functions.toId(channel)
647
708
  if (!id) throw new TypeError('Invalid text channel')
648
709
  this.textChannel = id
649
- this.batchUpdatePlayer({ guildId: this.guildId, text_channel: id }).catch(
650
- () => {}
651
- )
710
+ this.batchUpdatePlayer({ text_channel: id }).catch(() => {})
652
711
  return this
653
712
  }
654
713
 
@@ -734,7 +793,7 @@ class Player extends EventEmitter {
734
793
  this.destroyed ||
735
794
  !this.isAutoplayEnabled ||
736
795
  !this.previous ||
737
- (this.queue?.size)
796
+ this.queue?.size
738
797
  )
739
798
  return this
740
799
  const prev = this.previous
@@ -837,22 +896,26 @@ class Player extends EventEmitter {
837
896
  return null
838
897
  }
839
898
 
840
- trackStart(player, track, payload = {}) {
899
+ trackStart(_player, _track, payload = {}) {
841
900
  if (this.destroyed) return
901
+ const startedTrack = this.current || _track
902
+ if (!startedTrack) return
903
+ if (!this.current) this.current = startedTrack
842
904
  this.playing = true
843
905
  this.paused = false
844
- this.aqua.emit(AqualinkEvents.TrackStart, this, this.current, {
906
+ this.aqua.emit(AqualinkEvents.TrackStart, this, startedTrack, {
845
907
  ...payload,
846
908
  resumed: this._resuming
847
909
  })
848
910
  this._resuming = false
849
911
  }
850
912
 
851
- async trackEnd(player, track, payload) {
913
+ async trackEnd(_player, track, payload) {
852
914
  if (this.destroyed) return
853
915
 
854
916
  const reason = payload?.reason
855
- const isFailure = reason === 'loadFailed' || reason === 'cleanup'
917
+ const isFailure = reason === 'loadFailed'
918
+ const isCleanup = reason === 'cleanup'
856
919
  const isReplaced = reason === 'replaced'
857
920
 
858
921
  if (track) this.previousTracks.push(track)
@@ -860,8 +923,8 @@ class Player extends EventEmitter {
860
923
  _functions.safeDel(this.nowPlayingMessage)
861
924
  if (!isReplaced) this.current = null
862
925
 
863
- if (isFailure) {
864
- if (!this.queue.size) {
926
+ if (isFailure || isCleanup) {
927
+ if (!this.queue.size || isCleanup) {
865
928
  this.clearData({ preserveTracks: this._reconnecting || this._resuming })
866
929
  this.aqua.emit(AqualinkEvents.QueueEnd, this)
867
930
  } else {
@@ -879,8 +942,9 @@ class Player extends EventEmitter {
879
942
  this.queue.add(track)
880
943
  }
881
944
 
882
- if (this.queue.size && !isReplaced) {
883
- this.aqua.emit(AqualinkEvents.TrackEnd, this, track, reason)
945
+ if (this.queue.size) {
946
+ if (!isReplaced)
947
+ this.aqua.emit(AqualinkEvents.TrackEnd, this, track, reason)
884
948
  await this.play()
885
949
  } else if (this.isAutoplayEnabled && !isReplaced) {
886
950
  await this.autoplay()
@@ -894,55 +958,55 @@ class Player extends EventEmitter {
894
958
  }
895
959
  }
896
960
 
897
- trackError(player, track, payload) {
961
+ trackError(_player, track, payload) {
898
962
  if (this.destroyed) return
899
963
  this.aqua.emit(AqualinkEvents.TrackError, this, track, payload)
900
964
  this.stop()
901
965
  }
902
966
 
903
- trackStuck(player, track, payload) {
967
+ trackStuck(_player, track, payload) {
904
968
  if (this.destroyed) return
905
969
  this.aqua.emit(AqualinkEvents.TrackStuck, this, track, payload)
906
970
  this.stop()
907
971
  }
908
972
 
909
- trackChange(p, t, payload) {
973
+ trackChange(_p, t, payload) {
910
974
  _functions.emitIfActive(this, AqualinkEvents.TrackChange, t, payload)
911
975
  }
912
- lyricsLine(p, t, payload) {
976
+ lyricsLine(_p, t, payload) {
913
977
  _functions.emitIfActive(this, AqualinkEvents.LyricsLine, t, payload)
914
978
  }
915
- volumeChanged(p, t, payload) {
979
+ volumeChanged(_p, t, payload) {
916
980
  _functions.emitIfActive(this, AqualinkEvents.VolumeChanged, t, payload)
917
981
  }
918
- filtersChanged(p, t, payload) {
982
+ filtersChanged(_p, t, payload) {
919
983
  _functions.emitIfActive(this, AqualinkEvents.FiltersChanged, t, payload)
920
984
  }
921
- seekEvent(p, t, payload) {
985
+ seekEvent(_p, t, payload) {
922
986
  _functions.emitIfActive(this, AqualinkEvents.Seek, t, payload)
923
987
  }
924
- lyricsFound(p, t, payload) {
988
+ lyricsFound(_p, t, payload) {
925
989
  _functions.emitIfActive(this, AqualinkEvents.LyricsFound, t, payload)
926
990
  }
927
- lyricsNotFound(p, t, payload) {
991
+ lyricsNotFound(_p, t, payload) {
928
992
  _functions.emitIfActive(this, AqualinkEvents.LyricsNotFound, t, payload)
929
993
  }
930
- playerCreated(p, t, payload) {
994
+ playerCreated(_p, _t, payload) {
931
995
  _functions.emitIfActive(this, AqualinkEvents.PlayerCreated, payload)
932
996
  }
933
- playerConnected(p, t, payload) {
997
+ playerConnected(_p, _t, payload) {
934
998
  _functions.emitIfActive(this, AqualinkEvents.PlayerConnected, payload)
935
999
  }
936
- playerDestroyed(p, t, payload) {
1000
+ playerDestroyed(_p, _t, payload) {
937
1001
  _functions.emitIfActive(this, AqualinkEvents.PlayerDestroyed, payload)
938
1002
  }
939
- pauseEvent(p, t, payload) {
1003
+ pauseEvent(_p, _t, payload) {
940
1004
  _functions.emitIfActive(this, AqualinkEvents.PauseEvent, payload)
941
1005
  }
942
- mixStarted(p, t, payload) {
1006
+ mixStarted(_p, t, payload) {
943
1007
  _functions.emitIfActive(this, AqualinkEvents.MixStarted, t, payload)
944
1008
  }
945
- mixEnded(p, t, payload) {
1009
+ mixEnded(_p, t, payload) {
946
1010
  _functions.emitIfActive(this, AqualinkEvents.MixEnded, t, payload)
947
1011
  }
948
1012
 
@@ -952,10 +1016,23 @@ class Player extends EventEmitter {
952
1016
  throw new Error('Resume failed')
953
1017
  }
954
1018
 
955
- async socketClosed(player, track, payload) {
1019
+ async socketClosed(_player, _track, payload) {
956
1020
  if (this.destroyed || this._reconnecting) return
1021
+ this.aqua?._trace?.('player.socketClosed', {
1022
+ guildId: this.guildId,
1023
+ code: payload?.code
1024
+ })
957
1025
 
958
1026
  const code = payload?.code
1027
+ if (code === 4006 && this._resuming) {
1028
+ this.aqua?._trace?.('player.socketClosed.ignored', {
1029
+ guildId: this.guildId,
1030
+ code,
1031
+ reason: 'transient_while_resuming'
1032
+ })
1033
+ return
1034
+ }
1035
+
959
1036
  let isRecoverable = [4015, 4009, 4006, 4014, 4022].includes(code)
960
1037
  if (code === 4014 && this.connection?.isWaitingForDisconnect)
961
1038
  isRecoverable = false
@@ -1135,6 +1212,27 @@ class Player extends EventEmitter {
1135
1212
  return this.nodes.rest.updatePlayer({ guildId: this.guildId, data })
1136
1213
  }
1137
1214
 
1215
+ _flushDeferredPlay() {
1216
+ if (
1217
+ !this._deferredStart ||
1218
+ this.destroyed ||
1219
+ !this.current?.track ||
1220
+ !this._updateBatcher
1221
+ )
1222
+ return
1223
+ this._deferredStart = false
1224
+ const updateData = {
1225
+ track: { encoded: this.current.track },
1226
+ paused: this.paused
1227
+ }
1228
+ if (this.position > 0) updateData.position = this.position
1229
+ this.aqua?._trace?.('player.play.deferred.flush', {
1230
+ guildId: this.guildId,
1231
+ hasEndpoint: !!this.connection?.endpoint
1232
+ })
1233
+ this.batchUpdatePlayer(updateData, true).catch(() => {})
1234
+ }
1235
+
1138
1236
  cleanup() {
1139
1237
  if (!this.playing && !this.paused && !this.queue?.size) this.destroy()
1140
1238
  }
@@ -3,8 +3,8 @@ class Plugin {
3
3
  this.name = name
4
4
  }
5
5
 
6
- load(aqua) {}
7
- unload(aqua) {}
6
+ load(_aqua) {}
7
+ unload(_aqua) {}
8
8
  }
9
9
 
10
10
  module.exports = Plugin
@@ -38,17 +38,19 @@ class Queue {
38
38
  this._head = 0
39
39
  }
40
40
 
41
- shuffle() {
42
- if (this._head > 0) {
43
- if (this._head > 0) {
44
- const len = this._items.length - this._head
45
- for (let i = 0; i < len; i++) {
46
- this._items[i] = this._items[this._head + i]
47
- }
48
- this._items.length = len
49
- this._head = 0
50
- }
41
+ _compact(force = false) {
42
+ if (this._head <= 0) return
43
+ if (!force && this._head <= this._items.length / 2) return
44
+ const len = this._items.length - this._head
45
+ for (let i = 0; i < len; i++) {
46
+ this._items[i] = this._items[this._head + i]
51
47
  }
48
+ this._items.length = len
49
+ this._head = 0
50
+ }
51
+
52
+ shuffle() {
53
+ this._compact(true)
52
54
  for (let i = this._items.length - 1; i > 0; i--) {
53
55
  const j = Math.floor(Math.random() * (i + 1))
54
56
  const temp = this._items[i]
@@ -106,14 +108,7 @@ class Queue {
106
108
  const item = this._items[this._head]
107
109
  this._items[this._head] = undefined // Allow GC
108
110
  this._head++
109
- if (this._head > 0 && this._head > this._items.length / 2) {
110
- const len = this._items.length - this._head
111
- for (let i = 0; i < len; i++) {
112
- this._items[i] = this._items[this._head + i]
113
- }
114
- this._items.length = len
115
- this._head = 0
116
- }
111
+ this._compact(false)
117
112
  return item
118
113
  }
119
114