aqualink 3.0.0 → 3.2.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 +67 -66
- package/build/index.d.ts +171 -134
- package/build/index.js +3 -3
- package/build/structures/Aqua.js +76 -44
- package/build/structures/AquaRecovery.js +48 -13
- package/build/structures/AqualinkEvents.js +2 -0
- package/build/structures/Connection.js +21 -2
- package/build/structures/ConnectionRecovery.js +36 -44
- package/build/structures/Filters.js +3 -1
- package/build/structures/Node.js +266 -124
- package/build/structures/Player.js +146 -66
- package/build/structures/PlayerLifecycle.js +24 -6
- package/build/structures/Queue.js +5 -1
- package/build/structures/Reporting.js +12 -0
- package/build/structures/Rest.js +74 -51
- package/build/structures/Track.js +10 -3
- package/package.json +70 -70
|
@@ -35,6 +35,7 @@ const EVENT_HANDLERS = Object.freeze({
|
|
|
35
35
|
PlayerDestroyedEvent: 'playerDestroyed',
|
|
36
36
|
LyricsNotFoundEvent: 'lyricsNotFound',
|
|
37
37
|
MixStartedEvent: 'mixStarted',
|
|
38
|
+
PlayerReconnectingEvent: 'PlayerReconnectingEvent',
|
|
38
39
|
MixEndedEvent: 'mixEnded'
|
|
39
40
|
})
|
|
40
41
|
|
|
@@ -122,7 +123,8 @@ class MicrotaskUpdateBatcher {
|
|
|
122
123
|
const { player: p, updates: u } = this
|
|
123
124
|
this.updates = null
|
|
124
125
|
this.scheduled = false
|
|
125
|
-
if (!u || !p
|
|
126
|
+
if (!u || !p || p.destroyed || p.state === PLAYER_STATE.DISCONNECTING)
|
|
127
|
+
return Promise.resolve()
|
|
126
128
|
return p.updatePlayer(u).catch((err) => {
|
|
127
129
|
_functions.emitAquaError(
|
|
128
130
|
p.aqua,
|
|
@@ -296,7 +298,9 @@ class Player extends EventEmitter {
|
|
|
296
298
|
}
|
|
297
299
|
|
|
298
300
|
_isVoiceRecoveryActive(token) {
|
|
299
|
-
return
|
|
301
|
+
return (
|
|
302
|
+
!!token && !this.destroyed && this._activeVoiceRecoveryToken === token
|
|
303
|
+
)
|
|
300
304
|
}
|
|
301
305
|
|
|
302
306
|
_clearVoiceRecovery(token = this._activeVoiceRecoveryToken, reason = null) {
|
|
@@ -352,6 +356,11 @@ class Player extends EventEmitter {
|
|
|
352
356
|
|
|
353
357
|
async play(track, options = {}) {
|
|
354
358
|
if (this.destroyed || !this.queue) return this
|
|
359
|
+
if (options != null && typeof options !== 'object') {
|
|
360
|
+
throw new TypeError(
|
|
361
|
+
`Player.play(): options must be an object, got ${typeof options}`
|
|
362
|
+
)
|
|
363
|
+
}
|
|
355
364
|
|
|
356
365
|
let item = track
|
|
357
366
|
if (!item) {
|
|
@@ -484,7 +493,8 @@ class Player extends EventEmitter {
|
|
|
484
493
|
}
|
|
485
494
|
|
|
486
495
|
connect(options = {}) {
|
|
487
|
-
if (this.destroyed)
|
|
496
|
+
if (this.destroyed)
|
|
497
|
+
throw new Error(`Cannot connect destroyed player (guild=${this.guildId})`)
|
|
488
498
|
|
|
489
499
|
const voiceChannel = _functions.toId(
|
|
490
500
|
options.voiceChannel || this.voiceChannel
|
|
@@ -542,38 +552,29 @@ class Player extends EventEmitter {
|
|
|
542
552
|
}
|
|
543
553
|
|
|
544
554
|
destroy(options = {}) {
|
|
555
|
+
if (this.destroyed) return this
|
|
556
|
+
|
|
545
557
|
const {
|
|
546
558
|
preserveClient = true,
|
|
547
559
|
skipRemote = false,
|
|
548
560
|
preserveMessage = false,
|
|
549
561
|
preserveReconnecting = false,
|
|
550
|
-
preserveTracks = false
|
|
562
|
+
preserveTracks = false,
|
|
563
|
+
abortSignal = null
|
|
551
564
|
} = options
|
|
552
|
-
if (this.destroyed && !this.queue) {
|
|
553
|
-
this._reconnectNonce++
|
|
554
|
-
if (this._reconnectTimers) {
|
|
555
|
-
_functions.clearTimers(this._reconnectTimers)
|
|
556
|
-
this._reconnectTimers = null
|
|
557
|
-
}
|
|
558
|
-
this._reconnecting = false
|
|
559
|
-
this._isActivelyReconnecting = false
|
|
560
|
-
return this
|
|
561
|
-
}
|
|
562
565
|
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
this.
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
})
|
|
574
|
-
}
|
|
575
|
-
this.emit('destroy')
|
|
566
|
+
this._reconnectNonce++
|
|
567
|
+
this.destroyed = true
|
|
568
|
+
this._clearVoiceRecovery(undefined, 'destroyed')
|
|
569
|
+
if (this.aqua?.debugTrace) {
|
|
570
|
+
this.aqua._trace('player.destroy', {
|
|
571
|
+
guildId: this.guildId,
|
|
572
|
+
skipRemote: !!skipRemote,
|
|
573
|
+
preserveTracks: !!preserveTracks,
|
|
574
|
+
preserveReconnecting: !!preserveReconnecting
|
|
575
|
+
})
|
|
576
576
|
}
|
|
577
|
+
this.emit('destroy')
|
|
577
578
|
|
|
578
579
|
if (this._voiceWatchdogTimer) {
|
|
579
580
|
clearInterval(this._voiceWatchdogTimer)
|
|
@@ -583,7 +584,6 @@ class Player extends EventEmitter {
|
|
|
583
584
|
_functions.clearTimers(this._pendingTimers)
|
|
584
585
|
this._pendingTimers = null
|
|
585
586
|
|
|
586
|
-
// Clear reconnection timers to prevent memory leaks when destroyed externally
|
|
587
587
|
if (this._reconnectTimers) {
|
|
588
588
|
_functions.clearTimers(this._reconnectTimers)
|
|
589
589
|
this._reconnectTimers = null
|
|
@@ -633,8 +633,23 @@ class Player extends EventEmitter {
|
|
|
633
633
|
this.previousTracks = null
|
|
634
634
|
this.previousIdentifiers?.clear()
|
|
635
635
|
this.previousIdentifiers = null
|
|
636
|
-
this.queue
|
|
636
|
+
if (this.queue) {
|
|
637
|
+
for (
|
|
638
|
+
let i = this.queue._head || 0;
|
|
639
|
+
i < (this.queue._items?.length || 0);
|
|
640
|
+
i++
|
|
641
|
+
) {
|
|
642
|
+
const t = this.queue._items[i]
|
|
643
|
+
if (t && typeof t.dispose === 'function') {
|
|
644
|
+
try {
|
|
645
|
+
t.dispose()
|
|
646
|
+
} catch {}
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
this.queue.clear()
|
|
650
|
+
}
|
|
637
651
|
this.queue = null
|
|
652
|
+
// ML-1: Clear _dataStore to prevent unbounded growth
|
|
638
653
|
this._dataStore?.clear()
|
|
639
654
|
this._dataStore = null
|
|
640
655
|
|
|
@@ -662,14 +677,25 @@ class Player extends EventEmitter {
|
|
|
662
677
|
|
|
663
678
|
if (!skipRemote) {
|
|
664
679
|
try {
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
guildId: this.guildId
|
|
680
|
+
if (abortSignal?.aborted) {
|
|
681
|
+
if (this.aqua?.debugTrace) {
|
|
682
|
+
this.aqua._trace('player.destroy.aborted', {
|
|
683
|
+
guildId: this.guildId,
|
|
684
|
+
reason: 'abort_signal_already_set'
|
|
671
685
|
})
|
|
672
|
-
|
|
686
|
+
}
|
|
687
|
+
} else {
|
|
688
|
+
this.send({ guild_id: this.guildId, channel_id: null })
|
|
689
|
+
this.aqua?.destroyPlayer?.(this.guildId)
|
|
690
|
+
if (this.nodes?.connected)
|
|
691
|
+
this.nodes.rest
|
|
692
|
+
?.destroyPlayer(this.guildId, abortSignal)
|
|
693
|
+
.catch((error) => {
|
|
694
|
+
reportSuppressedError(this, 'player.destroy.remote', error, {
|
|
695
|
+
guildId: this.guildId
|
|
696
|
+
})
|
|
697
|
+
})
|
|
698
|
+
}
|
|
673
699
|
} catch (error) {
|
|
674
700
|
reportSuppressedError(this, 'player.destroy.gateway', error, {
|
|
675
701
|
guildId: this.guildId
|
|
@@ -677,12 +703,23 @@ class Player extends EventEmitter {
|
|
|
677
703
|
}
|
|
678
704
|
}
|
|
679
705
|
|
|
680
|
-
if (!preserveClient)
|
|
706
|
+
if (!preserveClient) {
|
|
707
|
+
try {
|
|
708
|
+
this.aqua?.removeListener?.('playerUpdate', this._boundPlayerUpdate)
|
|
709
|
+
} catch {}
|
|
710
|
+
this.aqua = this.nodes = null
|
|
711
|
+
}
|
|
681
712
|
return this
|
|
682
713
|
}
|
|
683
714
|
|
|
684
715
|
pause(paused) {
|
|
685
|
-
if (this.destroyed
|
|
716
|
+
if (this.destroyed) return this
|
|
717
|
+
if (paused != null && typeof paused !== 'boolean') {
|
|
718
|
+
throw new TypeError(
|
|
719
|
+
`Player.pause(): paused must be a boolean, got ${typeof paused}`
|
|
720
|
+
)
|
|
721
|
+
}
|
|
722
|
+
if (this.paused === !!paused) return this
|
|
686
723
|
this.paused = !!paused
|
|
687
724
|
this.batchUpdatePlayer({ paused: this.paused }, true).catch((error) =>
|
|
688
725
|
reportSuppressedError(this, 'player.pause', error, {
|
|
@@ -694,8 +731,12 @@ class Player extends EventEmitter {
|
|
|
694
731
|
}
|
|
695
732
|
|
|
696
733
|
seek(position) {
|
|
697
|
-
if (
|
|
698
|
-
|
|
734
|
+
if (position == null || !_functions.isNum(position)) {
|
|
735
|
+
throw new TypeError(
|
|
736
|
+
`Player.seek(): position must be a non-negative number, got ${typeof position}`
|
|
737
|
+
)
|
|
738
|
+
}
|
|
739
|
+
if (this.destroyed || !this.playing) return this
|
|
699
740
|
const len = this.current?.info?.length || 0
|
|
700
741
|
const clamped = len
|
|
701
742
|
? Math.min(Math.max(position, 0), len)
|
|
@@ -769,6 +810,14 @@ class Player extends EventEmitter {
|
|
|
769
810
|
}
|
|
770
811
|
|
|
771
812
|
setVolume(volume) {
|
|
813
|
+
if (
|
|
814
|
+
volume == null ||
|
|
815
|
+
(typeof volume !== 'number' && typeof volume !== 'string')
|
|
816
|
+
) {
|
|
817
|
+
throw new TypeError(
|
|
818
|
+
`Player.setVolume(): volume must be a number, got ${typeof volume}`
|
|
819
|
+
)
|
|
820
|
+
}
|
|
772
821
|
const vol = _functions.clamp(volume)
|
|
773
822
|
if (this.destroyed || this.volume === vol) return this
|
|
774
823
|
this.volume = vol
|
|
@@ -909,22 +958,24 @@ class Player extends EventEmitter {
|
|
|
909
958
|
const prev = this.previous
|
|
910
959
|
const info = prev?.info
|
|
911
960
|
if (!info?.sourceName || !info.identifier) return this
|
|
912
|
-
const { sourceName, identifier, uri, author } = info
|
|
961
|
+
const { sourceName, identifier, uri, author, title } = info
|
|
913
962
|
this.isAutoplay = true
|
|
914
963
|
|
|
915
|
-
if (sourceName === 'spotify'
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
this.previousIdentifiers.
|
|
919
|
-
this.previousIdentifiers.
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
if (!this.autoplaySeed) {
|
|
923
|
-
this.autoplaySeed = {
|
|
924
|
-
trackId: identifier,
|
|
925
|
-
artistIds: Array.isArray(author) ? author.join(',') : author
|
|
964
|
+
if (sourceName === 'spotify') {
|
|
965
|
+
if (info.identifier && info.identifier !== 'local') {
|
|
966
|
+
this.previousIdentifiers.add(info.identifier)
|
|
967
|
+
if (this.previousIdentifiers.size > PREVIOUS_IDS_MAX) {
|
|
968
|
+
this.previousIdentifiers.delete(
|
|
969
|
+
this.previousIdentifiers.values().next().value
|
|
970
|
+
)
|
|
926
971
|
}
|
|
927
972
|
}
|
|
973
|
+
this.autoplaySeed = {
|
|
974
|
+
trackId: identifier,
|
|
975
|
+
isrc: prev.isrc || null,
|
|
976
|
+
title: title || null,
|
|
977
|
+
author: author || null
|
|
978
|
+
}
|
|
928
979
|
}
|
|
929
980
|
|
|
930
981
|
for (
|
|
@@ -972,27 +1023,45 @@ class Player extends EventEmitter {
|
|
|
972
1023
|
}
|
|
973
1024
|
|
|
974
1025
|
async _getAutoplayTrack(sourceName, identifier, uri, requester) {
|
|
1026
|
+
const seen = new Set(this.previousIdentifiers)
|
|
1027
|
+
const prevId = this.current?.identifier
|
|
1028
|
+
if (prevId) seen.add(prevId)
|
|
1029
|
+
|
|
975
1030
|
if (sourceName === 'youtube' || sourceName === 'ytmusic') {
|
|
976
1031
|
const res = await this.aqua.resolve({
|
|
977
1032
|
query: `https://www.youtube.com/watch?v=${identifier}&list=RD${identifier}`,
|
|
978
1033
|
source: 'ytmsearch',
|
|
979
1034
|
requester
|
|
980
1035
|
})
|
|
981
|
-
|
|
982
|
-
|
|
1036
|
+
if (_functions.isInvalidLoad(res) || !res.tracks?.length) return null
|
|
1037
|
+
const candidates = res.tracks.filter(
|
|
1038
|
+
(t) => t.identifier && !seen.has(t.identifier)
|
|
1039
|
+
)
|
|
1040
|
+
return candidates.length
|
|
1041
|
+
? candidates[_functions.randIdx(candidates.length)]
|
|
983
1042
|
: res.tracks[_functions.randIdx(res.tracks.length)]
|
|
984
1043
|
}
|
|
985
1044
|
if (sourceName === 'soundcloud') {
|
|
986
|
-
const scRes = await scAutoPlay(uri)
|
|
1045
|
+
const scRes = await scAutoPlay(uri, this.nodes?.rest?._autoplayAgent)
|
|
987
1046
|
if (!scRes?.length) return null
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
1047
|
+
|
|
1048
|
+
let anyValid = null
|
|
1049
|
+
for (const link of scRes) {
|
|
1050
|
+
const res = await this.aqua.resolve({
|
|
1051
|
+
query: link,
|
|
1052
|
+
source: 'scsearch',
|
|
1053
|
+
requester
|
|
1054
|
+
})
|
|
1055
|
+
if (_functions.isInvalidLoad(res) || !res.tracks?.length) continue
|
|
1056
|
+
if (!anyValid)
|
|
1057
|
+
anyValid = res.tracks[_functions.randIdx(res.tracks.length)]
|
|
1058
|
+
const candidates = res.tracks.filter(
|
|
1059
|
+
(t) => t.identifier && !seen.has(t.identifier)
|
|
1060
|
+
)
|
|
1061
|
+
if (candidates.length)
|
|
1062
|
+
return candidates[_functions.randIdx(candidates.length)]
|
|
1063
|
+
}
|
|
1064
|
+
return anyValid
|
|
996
1065
|
}
|
|
997
1066
|
if (sourceName === 'spotify') {
|
|
998
1067
|
const res = await spAutoPlay(
|
|
@@ -1029,6 +1098,7 @@ class Player extends EventEmitter {
|
|
|
1029
1098
|
const isReplaced = reason === 'replaced'
|
|
1030
1099
|
|
|
1031
1100
|
if (track) this.previousTracks.push(track)
|
|
1101
|
+
if (isReplaced) return
|
|
1032
1102
|
if (this.shouldDeleteMessage && !this._reconnecting && !this._resuming)
|
|
1033
1103
|
_functions.safeDel(this.nowPlayingMessage)
|
|
1034
1104
|
if (!isReplaced) this.current = null
|
|
@@ -1122,9 +1192,19 @@ class Player extends EventEmitter {
|
|
|
1122
1192
|
mixEnded(_p, t, payload) {
|
|
1123
1193
|
_functions.emitIfActive(this, AqualinkEvents.MixEnded, t, payload)
|
|
1124
1194
|
}
|
|
1195
|
+
PlayerReconnectingEvent(_p, _t, payload) {
|
|
1196
|
+
this._resuming = true
|
|
1197
|
+
_functions.emitIfActive(
|
|
1198
|
+
this,
|
|
1199
|
+
AqualinkEvents.PlayerReconnectingEvent,
|
|
1200
|
+
payload
|
|
1201
|
+
)
|
|
1202
|
+
this.connection?.resendVoiceUpdate?.(true)
|
|
1203
|
+
this.aqua.emit(AqualinkEvents.PlayerReconnect, this, { resuming: true })
|
|
1204
|
+
}
|
|
1125
1205
|
|
|
1126
|
-
async _attemptVoiceResume() {
|
|
1127
|
-
return this._lifecycleController.attemptVoiceResume()
|
|
1206
|
+
async _attemptVoiceResume(abortSignal) {
|
|
1207
|
+
return this._lifecycleController.attemptVoiceResume(abortSignal)
|
|
1128
1208
|
}
|
|
1129
1209
|
|
|
1130
1210
|
async socketClosed(_player, _track, payload) {
|
|
@@ -1142,7 +1222,7 @@ class Player extends EventEmitter {
|
|
|
1142
1222
|
} catch (err) {
|
|
1143
1223
|
_functions.emitAquaError(
|
|
1144
1224
|
this.aqua,
|
|
1145
|
-
new Error(`Send fail: ${err.message}`)
|
|
1225
|
+
new Error(`Send fail (guild=${this.guildId}): ${err.message}`)
|
|
1146
1226
|
)
|
|
1147
1227
|
return false
|
|
1148
1228
|
}
|
|
@@ -30,6 +30,8 @@ class PlayerLifecycle {
|
|
|
30
30
|
player.ping = this._functions.isNum(s.ping) ? s.ping : 0
|
|
31
31
|
player.timestamp = this._functions.isNum(s.time) ? s.time : Date.now()
|
|
32
32
|
|
|
33
|
+
if (player.destroyed) return
|
|
34
|
+
|
|
33
35
|
if (!player.connected) {
|
|
34
36
|
if (wasConnected || !player._voiceDownSince) {
|
|
35
37
|
if (player.aqua?.debugTrace) {
|
|
@@ -148,7 +150,9 @@ class PlayerLifecycle {
|
|
|
148
150
|
if (!hasVoiceData) {
|
|
149
151
|
const downFor = Date.now() - player._voiceDownSince
|
|
150
152
|
if (downFor > this.VOICE_DOWN_THRESHOLD * this.VOICE_ABANDON_MULTIPLIER) {
|
|
151
|
-
const recoveryToken = player._claimVoiceRecovery(
|
|
153
|
+
const recoveryToken = player._claimVoiceRecovery(
|
|
154
|
+
'watchdog_voice_refresh'
|
|
155
|
+
)
|
|
152
156
|
if (player._isVoiceRecoveryActive(recoveryToken))
|
|
153
157
|
player.connection?._requestVoiceState?.()
|
|
154
158
|
if (player._isVoiceRecoveryActive(recoveryToken))
|
|
@@ -215,11 +219,15 @@ class PlayerLifecycle {
|
|
|
215
219
|
}
|
|
216
220
|
}
|
|
217
221
|
|
|
218
|
-
async attemptVoiceResume() {
|
|
222
|
+
async attemptVoiceResume(abortSignal) {
|
|
219
223
|
const player = this.player
|
|
220
|
-
if (!player.connection?.sessionId)
|
|
224
|
+
if (!player.connection?.sessionId)
|
|
225
|
+
throw new Error(`No session (guild=${player.guildId})`)
|
|
226
|
+
if (abortSignal?.aborted) throw new Error('Resume aborted by signal')
|
|
221
227
|
if (!(await player.connection.attemptResume()))
|
|
222
|
-
throw new Error(
|
|
228
|
+
throw new Error(
|
|
229
|
+
`Resume failed (guild=${player.guildId}, endpoint=${player.connection.endpoint || 'none'})`
|
|
230
|
+
)
|
|
223
231
|
}
|
|
224
232
|
|
|
225
233
|
async socketClosed(_player, _track, payload) {
|
|
@@ -233,6 +241,11 @@ class PlayerLifecycle {
|
|
|
233
241
|
}
|
|
234
242
|
|
|
235
243
|
const code = payload?.code
|
|
244
|
+
if (code === 4014) {
|
|
245
|
+
await player._delay(150)
|
|
246
|
+
if (player.destroyed) return
|
|
247
|
+
}
|
|
248
|
+
|
|
236
249
|
if (code === 4006 && player._resuming) {
|
|
237
250
|
if (player.aqua?.debugTrace) {
|
|
238
251
|
player.aqua._trace('player.socketClosed.ignored', {
|
|
@@ -245,7 +258,10 @@ class PlayerLifecycle {
|
|
|
245
258
|
}
|
|
246
259
|
|
|
247
260
|
let isRecoverable = [4015, 4009, 4006, 4014, 4022].includes(code)
|
|
248
|
-
if (
|
|
261
|
+
if (
|
|
262
|
+
code === 4014 &&
|
|
263
|
+
(player.connection?.isWaitingForDisconnect || !player.voiceChannel)
|
|
264
|
+
)
|
|
249
265
|
isRecoverable = false
|
|
250
266
|
|
|
251
267
|
if (code === 4015 && !player.nodes?.info?.isNodelink) {
|
|
@@ -342,7 +358,8 @@ class PlayerLifecycle {
|
|
|
342
358
|
return false
|
|
343
359
|
})
|
|
344
360
|
}
|
|
345
|
-
if (resumed)
|
|
361
|
+
if (resumed)
|
|
362
|
+
player._clearVoiceRecovery(recoveryToken, 'socket_soft_resumed')
|
|
346
363
|
if (
|
|
347
364
|
resumed ||
|
|
348
365
|
player.connected ||
|
|
@@ -443,6 +460,7 @@ class PlayerLifecycle {
|
|
|
443
460
|
if (
|
|
444
461
|
latestActivePlayer &&
|
|
445
462
|
latestActivePlayer !== player &&
|
|
463
|
+
latestActivePlayer !== np &&
|
|
446
464
|
!latestActivePlayer.destroyed
|
|
447
465
|
) {
|
|
448
466
|
try {
|
|
@@ -2,6 +2,7 @@ class Queue {
|
|
|
2
2
|
constructor() {
|
|
3
3
|
this._items = []
|
|
4
4
|
this._head = 0
|
|
5
|
+
this._compactThreshold = 64
|
|
5
6
|
}
|
|
6
7
|
|
|
7
8
|
get size() {
|
|
@@ -40,13 +41,16 @@ class Queue {
|
|
|
40
41
|
|
|
41
42
|
_compact(force = false) {
|
|
42
43
|
if (this._head <= 0) return
|
|
43
|
-
if (!force && this._head
|
|
44
|
+
if (!force && this._head < this._compactThreshold) return
|
|
44
45
|
const len = this._items.length - this._head
|
|
45
46
|
for (let i = 0; i < len; i++) {
|
|
46
47
|
this._items[i] = this._items[this._head + i]
|
|
47
48
|
}
|
|
48
49
|
this._items.length = len
|
|
49
50
|
this._head = 0
|
|
51
|
+
if (this._compactThreshold < len) {
|
|
52
|
+
this._compactThreshold = Math.min(len * 2, 1024)
|
|
53
|
+
}
|
|
50
54
|
}
|
|
51
55
|
|
|
52
56
|
shuffle() {
|
|
@@ -26,7 +26,19 @@ function reportSuppressedError(target, scope, error, data = null) {
|
|
|
26
26
|
return err
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
function emitOperationalError(target, source, error) {
|
|
30
|
+
const aqua = getAqua(target)
|
|
31
|
+
const err = normalizeError(error)
|
|
32
|
+
if (aqua?.listenerCount?.(AqualinkEvents.Error) > 0) {
|
|
33
|
+
aqua.emit(AqualinkEvents.Error, source, err)
|
|
34
|
+
} else if (aqua?.debugTrace) {
|
|
35
|
+
aqua._trace('aqua.error.unhandled', { error: err.message })
|
|
36
|
+
}
|
|
37
|
+
return err
|
|
38
|
+
}
|
|
39
|
+
|
|
29
40
|
module.exports = {
|
|
41
|
+
emitOperationalError,
|
|
30
42
|
normalizeError,
|
|
31
43
|
reportSuppressedError
|
|
32
44
|
}
|