aqualink 2.19.0 → 2.19.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/build/handlers/autoplay.js +17 -5
- package/build/index.d.ts +239 -2
- package/build/structures/Aqua.js +21 -17
- package/build/structures/AqualinkEvents.js +0 -2
- package/build/structures/Connection.js +9 -16
- package/build/structures/Filters.js +53 -5
- package/build/structures/Node.js +2 -5
- package/build/structures/Player.js +23 -13
- package/build/structures/Queue.js +13 -7
- package/build/structures/Rest.js +15 -8
- package/build/structures/Track.js +13 -6
- package/package.json +1 -1
|
@@ -1,14 +1,25 @@
|
|
|
1
1
|
const https = require('https')
|
|
2
2
|
|
|
3
|
+
// Default agent config (used only if shared agent not provided)
|
|
3
4
|
const AGENT_CONFIG = {
|
|
4
5
|
keepAlive: true,
|
|
5
|
-
maxSockets:
|
|
6
|
-
maxFreeSockets:
|
|
6
|
+
maxSockets: 64,
|
|
7
|
+
maxFreeSockets: 32,
|
|
7
8
|
timeout: 8000,
|
|
8
9
|
freeSocketTimeout: 4000
|
|
9
10
|
}
|
|
10
11
|
|
|
11
|
-
|
|
12
|
+
// Shared agent reference - can be set from Rest module
|
|
13
|
+
let sharedAgent = null
|
|
14
|
+
const getAgent = () => sharedAgent || (sharedAgent = new https.Agent(AGENT_CONFIG))
|
|
15
|
+
|
|
16
|
+
// Allow Rest module to inject its agent
|
|
17
|
+
const setSharedAgent = (agent) => {
|
|
18
|
+
if (agent && typeof agent.request === 'function') {
|
|
19
|
+
sharedAgent = agent
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
12
23
|
|
|
13
24
|
const SC_LINK_RE = /<a\s+itemprop="url"\s+href="(\/[^"]+)"/g
|
|
14
25
|
const MAX_REDIRECTS = 3
|
|
@@ -23,7 +34,7 @@ const fastFetch = (url, depth = 0) =>
|
|
|
23
34
|
|
|
24
35
|
const req = https.get(
|
|
25
36
|
url,
|
|
26
|
-
{ agent, timeout: DEFAULT_TIMEOUT_MS },
|
|
37
|
+
{ agent: getAgent(), timeout: DEFAULT_TIMEOUT_MS },
|
|
27
38
|
(res) => {
|
|
28
39
|
const { statusCode, headers } = res
|
|
29
40
|
|
|
@@ -132,5 +143,6 @@ const spAutoPlay = async (seed, player, requester, excludedIds = []) => {
|
|
|
132
143
|
|
|
133
144
|
module.exports = {
|
|
134
145
|
scAutoPlay,
|
|
135
|
-
spAutoPlay
|
|
146
|
+
spAutoPlay,
|
|
147
|
+
setSharedAgent
|
|
136
148
|
}
|
package/build/index.d.ts
CHANGED
|
@@ -50,16 +50,81 @@ declare module 'aqualink' {
|
|
|
50
50
|
get leastUsedNodes(): Node[]
|
|
51
51
|
|
|
52
52
|
// Core Methods
|
|
53
|
+
/**
|
|
54
|
+
* Initializes the specific client id and connects to all nodes
|
|
55
|
+
* @param clientId Client ID
|
|
56
|
+
* @example
|
|
57
|
+
* ```ts
|
|
58
|
+
* await aqua.init(client.user.id);
|
|
59
|
+
* ```
|
|
60
|
+
*/
|
|
53
61
|
init(clientId: string): Promise<Aqua>
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Creates a new node connection
|
|
65
|
+
* @param options Modified node options
|
|
66
|
+
*/
|
|
54
67
|
createNode(options: NodeOptions): Promise<Node>
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Destroys a node by identifier
|
|
71
|
+
* @param identifier Node identifier (name or host)
|
|
72
|
+
*/
|
|
55
73
|
destroyNode(identifier: string): void
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Updates the voice state of a player
|
|
77
|
+
* @param data Voice state update packet
|
|
78
|
+
*/
|
|
56
79
|
updateVoiceState(data: VoiceStateUpdate | VoiceServerUpdate): void
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Fetches nodes in a specific region
|
|
83
|
+
* @param region Region name
|
|
84
|
+
*/
|
|
57
85
|
fetchRegion(region: string): Node[]
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Creates a connection for a player
|
|
89
|
+
* @param options Connection options
|
|
90
|
+
*/
|
|
58
91
|
createConnection(options: ConnectionOptions): Player
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Creates a player on a specific node
|
|
95
|
+
* @param node Node to create player on
|
|
96
|
+
* @param options Player options
|
|
97
|
+
*/
|
|
59
98
|
createPlayer(node: Node, options: PlayerOptions): Player
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Destroys a player
|
|
102
|
+
* @param guildId Guild ID
|
|
103
|
+
*/
|
|
60
104
|
destroyPlayer(guildId: string): Promise<void>
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Resolves a track or playlist
|
|
108
|
+
* @param options Resolution options
|
|
109
|
+
* @example
|
|
110
|
+
* ```ts
|
|
111
|
+
* const result = await aqua.resolve({ query: 'https://...', requester: user });
|
|
112
|
+
* ```
|
|
113
|
+
*/
|
|
61
114
|
resolve(options: ResolveOptions): Promise<ResolveResponse>
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Gets an existing player
|
|
118
|
+
* @param guildId Guild ID
|
|
119
|
+
*/
|
|
62
120
|
get(guildId: string): Player
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Searches for tracks
|
|
124
|
+
* @param query Search query
|
|
125
|
+
* @param requester Requester object
|
|
126
|
+
* @param source Search source (ytsearch, scsearch, etc)
|
|
127
|
+
*/
|
|
63
128
|
search(
|
|
64
129
|
query: string,
|
|
65
130
|
requester: any,
|
|
@@ -68,12 +133,16 @@ declare module 'aqualink' {
|
|
|
68
133
|
|
|
69
134
|
// Save/Load Methods
|
|
70
135
|
savePlayer(filePath?: string): Promise<void>
|
|
136
|
+
savePlayerSync(filePath?: string): void
|
|
71
137
|
loadPlayers(filePath?: string): Promise<void>
|
|
72
138
|
|
|
73
139
|
// Failover and Migration Methods
|
|
74
140
|
handleNodeFailover(failedNode: Node): Promise<void>
|
|
75
141
|
|
|
76
142
|
// Utility Methods
|
|
143
|
+
/**
|
|
144
|
+
* Destroys the Aqua instance and all players
|
|
145
|
+
*/
|
|
77
146
|
destroy(): Promise<void>
|
|
78
147
|
|
|
79
148
|
// Internal Methods
|
|
@@ -168,7 +237,7 @@ declare module 'aqualink' {
|
|
|
168
237
|
_isConnecting: boolean
|
|
169
238
|
_debugEnabled: boolean
|
|
170
239
|
_headers: Record<string, string>
|
|
171
|
-
_boundHandlers: Record<string,
|
|
240
|
+
_boundHandlers: Record<string, ReturnType<typeof this._boundHandlers>>
|
|
172
241
|
|
|
173
242
|
// Methods
|
|
174
243
|
connect(): Promise<void>
|
|
@@ -244,29 +313,119 @@ declare module 'aqualink' {
|
|
|
244
313
|
_boundPlayerUpdate: (packet: any) => void
|
|
245
314
|
_boundEvent: (payload: any) => void
|
|
246
315
|
_boundAquaPlayerMove: (oldChannel: string, newChannel: string) => void
|
|
316
|
+
_lastVoiceChannel: string | null
|
|
317
|
+
_lastTextChannel: string | null
|
|
247
318
|
|
|
248
319
|
// Getters
|
|
249
320
|
get previous(): Track | null
|
|
250
321
|
get currenttrack(): Track | null
|
|
251
322
|
|
|
252
323
|
// Core Methods
|
|
253
|
-
|
|
324
|
+
/**
|
|
325
|
+
* Plays a track. If no track is provided, plays the next track in the queue.
|
|
326
|
+
* @param track The track to play
|
|
327
|
+
* @param options Options for playback
|
|
328
|
+
* @example
|
|
329
|
+
* ```ts
|
|
330
|
+
* // Play the next track in the queue
|
|
331
|
+
* await player.play();
|
|
332
|
+
*
|
|
333
|
+
* // Play a specific track
|
|
334
|
+
* await player.play(track);
|
|
335
|
+
* ```
|
|
336
|
+
*/
|
|
337
|
+
play(track?: Track | null, options?: { paused?: boolean; startTime?: number; noReplace?: boolean }): Promise<Player>
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Connects the player to a voice channel
|
|
341
|
+
* @param options Connection options
|
|
342
|
+
* @example
|
|
343
|
+
* ```ts
|
|
344
|
+
* player.connect({
|
|
345
|
+
* guildId: '...',
|
|
346
|
+
* voiceChannel: '...',
|
|
347
|
+
* deaf: true
|
|
348
|
+
* });
|
|
349
|
+
* ```
|
|
350
|
+
*/
|
|
254
351
|
connect(options?: ConnectionOptions): Player
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* Destroys the player and optionally cleans up resources
|
|
355
|
+
* @param options Destruction options
|
|
356
|
+
*/
|
|
255
357
|
destroy(options?: {
|
|
256
358
|
preserveClient?: boolean
|
|
257
359
|
skipRemote?: boolean
|
|
360
|
+
preserveMessage?: boolean
|
|
361
|
+
preserveReconnecting?: boolean
|
|
362
|
+
preserveTracks?: boolean
|
|
258
363
|
}): Player
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* Pauses or resumes the player
|
|
367
|
+
* @param paused Whether to pause
|
|
368
|
+
*/
|
|
259
369
|
pause(paused: boolean): Player
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* Seeks to a position in the current track
|
|
373
|
+
* @param position Position in milliseconds
|
|
374
|
+
*/
|
|
260
375
|
seek(position: number): Player
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* Stops the playback
|
|
379
|
+
*/
|
|
261
380
|
stop(): Player
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Sets the player volume
|
|
384
|
+
* @param volume Volume (0-1000)
|
|
385
|
+
*/
|
|
262
386
|
setVolume(volume: number): Player
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* Sets the loop mode
|
|
390
|
+
* @param mode Loop mode (off, track, queue)
|
|
391
|
+
*/
|
|
263
392
|
setLoop(mode: LoopMode | LoopModeName): Player
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* Sets the text channel for the player
|
|
396
|
+
* @param channel Channel ID
|
|
397
|
+
*/
|
|
264
398
|
setTextChannel(channel: string): Player
|
|
399
|
+
|
|
400
|
+
/**
|
|
401
|
+
* Sets the voice channel and moves the player
|
|
402
|
+
* @param channel Channel ID
|
|
403
|
+
*/
|
|
265
404
|
setVoiceChannel(channel: string): Player
|
|
405
|
+
|
|
406
|
+
/**
|
|
407
|
+
* Disconnects the player from voice
|
|
408
|
+
*/
|
|
266
409
|
disconnect(): Player
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* Shuffles the queue
|
|
413
|
+
*/
|
|
267
414
|
shuffle(): Player
|
|
415
|
+
|
|
416
|
+
/**
|
|
417
|
+
* Gets the player queue
|
|
418
|
+
*/
|
|
268
419
|
getQueue(): Queue
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* Replays the current track from the beginning
|
|
423
|
+
*/
|
|
269
424
|
replay(): Player
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
* Skips the current track
|
|
428
|
+
*/
|
|
270
429
|
skip(): Player
|
|
271
430
|
|
|
272
431
|
// Advanced Methods
|
|
@@ -355,9 +514,24 @@ declare module 'aqualink' {
|
|
|
355
514
|
get thumbnail(): string
|
|
356
515
|
|
|
357
516
|
// Methods
|
|
517
|
+
/**
|
|
518
|
+
* Resolves local artwork/thumbnail
|
|
519
|
+
*/
|
|
358
520
|
resolveThumbnail(url?: string): string | null
|
|
521
|
+
|
|
522
|
+
/**
|
|
523
|
+
* Resolves the track if it needs (re)resolution
|
|
524
|
+
*/
|
|
359
525
|
resolve(aqua: Aqua, opts?: TrackResolutionOptions): Promise<Track | null>
|
|
526
|
+
|
|
527
|
+
/**
|
|
528
|
+
* Checks if the track is valid
|
|
529
|
+
*/
|
|
360
530
|
isValid(): boolean
|
|
531
|
+
|
|
532
|
+
/**
|
|
533
|
+
* Disposes the track and frees resources
|
|
534
|
+
*/
|
|
361
535
|
dispose(): void
|
|
362
536
|
|
|
363
537
|
// Internal Methods
|
|
@@ -380,13 +554,54 @@ declare module 'aqualink' {
|
|
|
380
554
|
useHttp2: boolean
|
|
381
555
|
|
|
382
556
|
// Core Methods
|
|
557
|
+
/**
|
|
558
|
+
* Sets the session ID for the REST connection
|
|
559
|
+
* @param sessionId The session ID
|
|
560
|
+
*/
|
|
383
561
|
setSessionId(sessionId: string): void
|
|
562
|
+
|
|
563
|
+
/**
|
|
564
|
+
* Makes a generic request to the Lavalink REST API
|
|
565
|
+
* @param method HTTP method
|
|
566
|
+
* @param endpoint API endpoint
|
|
567
|
+
* @param body Request body
|
|
568
|
+
*/
|
|
384
569
|
makeRequest(method: HttpMethod, endpoint: string, body?: any): Promise<any>
|
|
570
|
+
|
|
571
|
+
/**
|
|
572
|
+
* Updates a player via REST
|
|
573
|
+
* @param options Update options
|
|
574
|
+
*/
|
|
385
575
|
updatePlayer(options: UpdatePlayerOptions): Promise<any>
|
|
576
|
+
|
|
577
|
+
/**
|
|
578
|
+
* Destroys a player via REST
|
|
579
|
+
* @param guildId Guild ID
|
|
580
|
+
*/
|
|
386
581
|
destroyPlayer(guildId: string): Promise<any>
|
|
582
|
+
|
|
583
|
+
/**
|
|
584
|
+
* Gets lyrics for a track
|
|
585
|
+
* @param options Lyrics options
|
|
586
|
+
*/
|
|
387
587
|
getLyrics(options: GetLyricsOptions): Promise<LyricsResponse>
|
|
588
|
+
|
|
589
|
+
/**
|
|
590
|
+
* Subscribes to live lyrics events
|
|
591
|
+
* @param guildId Guild ID
|
|
592
|
+
* @param sync Whether to sync with playback
|
|
593
|
+
*/
|
|
388
594
|
subscribeLiveLyrics(guildId: string, sync?: boolean): Promise<any>
|
|
595
|
+
|
|
596
|
+
/**
|
|
597
|
+
* Unsubscribes from live lyrics
|
|
598
|
+
* @param guildId Guild ID
|
|
599
|
+
*/
|
|
389
600
|
unsubscribeLiveLyrics(guildId: string): Promise<any>
|
|
601
|
+
|
|
602
|
+
/**
|
|
603
|
+
* Gets node statistics
|
|
604
|
+
*/
|
|
390
605
|
getStats(): Promise<NodeStats>
|
|
391
606
|
|
|
392
607
|
// Additional REST Methods
|
|
@@ -421,14 +636,33 @@ declare module 'aqualink' {
|
|
|
421
636
|
readonly last: Track | null
|
|
422
637
|
|
|
423
638
|
// Methods
|
|
639
|
+
/**
|
|
640
|
+
* Adds a track to the queue
|
|
641
|
+
* @param track Track to add
|
|
642
|
+
*/
|
|
424
643
|
add(track: Track): Queue
|
|
644
|
+
|
|
645
|
+
/**
|
|
646
|
+
* Adds multiple tracks to the queue
|
|
647
|
+
* @param tracks Tracks to add
|
|
648
|
+
*/
|
|
425
649
|
add(...tracks: Track[]): Queue
|
|
650
|
+
|
|
426
651
|
push(track: Track): number
|
|
427
652
|
unshift(track: Track): number
|
|
428
653
|
shift(): Track | undefined
|
|
429
654
|
remove(track: Track): boolean
|
|
655
|
+
|
|
656
|
+
/**
|
|
657
|
+
* Clears the queue
|
|
658
|
+
*/
|
|
430
659
|
clear(): void
|
|
660
|
+
|
|
661
|
+
/**
|
|
662
|
+
* Shuffles the queue
|
|
663
|
+
*/
|
|
431
664
|
shuffle(): Queue
|
|
665
|
+
|
|
432
666
|
peek(): Track | null
|
|
433
667
|
isEmpty(): boolean
|
|
434
668
|
toArray(): Track[]
|
|
@@ -509,6 +743,9 @@ declare module 'aqualink' {
|
|
|
509
743
|
_updateTimer: NodeJS.Timeout | null
|
|
510
744
|
_hasDebugListeners: boolean
|
|
511
745
|
_hasMoveListeners: boolean
|
|
746
|
+
_lastSentVoiceKey: string
|
|
747
|
+
_lastVoiceDataUpdate: number
|
|
748
|
+
_stateFlags: number
|
|
512
749
|
|
|
513
750
|
// Methods
|
|
514
751
|
setServerUpdate(data: VoiceServerUpdate['d']): void
|
package/build/structures/Aqua.js
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
1
|
const fs = require('node:fs')
|
|
4
2
|
const readline = require('node:readline')
|
|
5
3
|
const { EventEmitter } = require('tseep')
|
|
@@ -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,6 @@ 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 MAX_QUEUE_SAVE = 10
|
|
35
|
-
const MAX_TRACKS_RESTORE = 20
|
|
36
31
|
|
|
37
32
|
const DEFAULT_OPTIONS = Object.freeze({
|
|
38
33
|
shouldDeleteMessage: false,
|
|
@@ -181,7 +176,9 @@ class Aqua extends EventEmitter {
|
|
|
181
176
|
}
|
|
182
177
|
if (batch.length)
|
|
183
178
|
queueMicrotask(() =>
|
|
184
|
-
batch.forEach((p) =>
|
|
179
|
+
batch.forEach((p) => {
|
|
180
|
+
p.connection.resendVoiceUpdate()
|
|
181
|
+
})
|
|
185
182
|
)
|
|
186
183
|
}
|
|
187
184
|
}
|
|
@@ -257,7 +254,11 @@ class Aqua extends EventEmitter {
|
|
|
257
254
|
const id = node.name || node.host
|
|
258
255
|
const now = Date.now()
|
|
259
256
|
const cached = this._nodeLoadCache.get(id)
|
|
260
|
-
if (cached && now - cached.time < 5000)
|
|
257
|
+
if (cached && now - cached.time < 5000) {
|
|
258
|
+
this._nodeLoadCache.delete(id)
|
|
259
|
+
this._nodeLoadCache.set(id, cached)
|
|
260
|
+
return cached.load
|
|
261
|
+
}
|
|
261
262
|
const stats = node?.stats
|
|
262
263
|
if (!stats) return 0
|
|
263
264
|
const cores = Math.max(1, stats.cpu?.cores || 1)
|
|
@@ -267,11 +268,15 @@ class Aqua extends EventEmitter {
|
|
|
267
268
|
(stats.playingPlayers || 0) * 0.75 +
|
|
268
269
|
(stats.memory ? stats.memory.used / reservable : 0) * 40 +
|
|
269
270
|
(node.rest?.calls || 0) * 0.001
|
|
270
|
-
this._nodeLoadCache.
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
271
|
+
if (this._nodeLoadCache.size >= MAX_CACHE_SIZE) {
|
|
272
|
+
const iterator = this._nodeLoadCache.keys()
|
|
273
|
+
while (this._nodeLoadCache.size >= MAX_CACHE_SIZE) {
|
|
274
|
+
const oldest = iterator.next().value
|
|
275
|
+
if (!oldest) break
|
|
276
|
+
this._nodeLoadCache.delete(oldest)
|
|
277
|
+
}
|
|
274
278
|
}
|
|
279
|
+
this._nodeLoadCache.set(id, { load, time: now })
|
|
275
280
|
return load
|
|
276
281
|
}
|
|
277
282
|
|
|
@@ -528,7 +533,7 @@ class Aqua extends EventEmitter {
|
|
|
528
533
|
return newPlayer
|
|
529
534
|
} catch (error) {
|
|
530
535
|
if (retry === maxRetries - 1) throw error
|
|
531
|
-
await _functions.delay(retryDelay *
|
|
536
|
+
await _functions.delay(retryDelay * 1.5 ** retry)
|
|
532
537
|
}
|
|
533
538
|
}
|
|
534
539
|
}
|
|
@@ -858,7 +863,7 @@ class Aqua extends EventEmitter {
|
|
|
858
863
|
buffer.push(JSON.stringify(data))
|
|
859
864
|
|
|
860
865
|
if (buffer.length >= WRITE_BUFFER_SIZE) {
|
|
861
|
-
const chunk = buffer.join('\n')
|
|
866
|
+
const chunk = `${buffer.join('\n')}\n`
|
|
862
867
|
buffer.length = 0
|
|
863
868
|
if (!ws.write(chunk)) {
|
|
864
869
|
drainPromise = drainPromise.then(
|
|
@@ -868,7 +873,7 @@ class Aqua extends EventEmitter {
|
|
|
868
873
|
}
|
|
869
874
|
}
|
|
870
875
|
|
|
871
|
-
if (buffer.length) ws.write(buffer.join('\n')
|
|
876
|
+
if (buffer.length) ws.write(`${buffer.join('\n')}\n`)
|
|
872
877
|
await drainPromise
|
|
873
878
|
await new Promise((resolve, reject) =>
|
|
874
879
|
ws.end((err) => (err ? reject(err) : resolve()))
|
|
@@ -935,7 +940,7 @@ class Aqua extends EventEmitter {
|
|
|
935
940
|
try {
|
|
936
941
|
const gId = String(p.g)
|
|
937
942
|
const existing = this.players.get(gId)
|
|
938
|
-
if (existing
|
|
943
|
+
if (existing?.playing) return
|
|
939
944
|
|
|
940
945
|
const player =
|
|
941
946
|
existing ||
|
|
@@ -1068,7 +1073,6 @@ class Aqua extends EventEmitter {
|
|
|
1068
1073
|
break
|
|
1069
1074
|
}
|
|
1070
1075
|
} catch {
|
|
1071
|
-
continue
|
|
1072
1076
|
}
|
|
1073
1077
|
}
|
|
1074
1078
|
} catch {
|
|
@@ -1079,4 +1083,4 @@ class Aqua extends EventEmitter {
|
|
|
1079
1083
|
}
|
|
1080
1084
|
}
|
|
1081
1085
|
|
|
1082
|
-
module.exports = Aqua
|
|
1086
|
+
module.exports = Aqua
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
1
|
const { AqualinkEvents } = require('./AqualinkEvents')
|
|
4
2
|
|
|
5
3
|
const POOL_SIZE = 12
|
|
@@ -216,8 +214,9 @@ class Connection {
|
|
|
216
214
|
this._scheduleVoiceUpdate()
|
|
217
215
|
}
|
|
218
216
|
|
|
219
|
-
resendVoiceUpdate() {
|
|
217
|
+
resendVoiceUpdate(force = false) {
|
|
220
218
|
if (this._destroyed || !this._hasValidVoiceData()) return false
|
|
219
|
+
if (force) this._lastSentVoiceKey = ''
|
|
221
220
|
this._scheduleVoiceUpdate()
|
|
222
221
|
return true
|
|
223
222
|
}
|
|
@@ -456,17 +455,7 @@ class Connection {
|
|
|
456
455
|
_makeVoiceKey() {
|
|
457
456
|
const p = this._player
|
|
458
457
|
const vol = p?.volume ?? 100
|
|
459
|
-
return
|
|
460
|
-
(this.sessionId || '') +
|
|
461
|
-
'|' +
|
|
462
|
-
(this.token || '') +
|
|
463
|
-
'|' +
|
|
464
|
-
(this.endpoint || '') +
|
|
465
|
-
'|' +
|
|
466
|
-
(p?.voiceChannel || '') +
|
|
467
|
-
'|' +
|
|
468
|
-
vol
|
|
469
|
-
)
|
|
458
|
+
return `${this.sessionId || ''}|${this.token || ''}|${this.endpoint || ''}|${p?.voiceChannel || ''}|${vol}`
|
|
470
459
|
}
|
|
471
460
|
|
|
472
461
|
_scheduleVoiceUpdate() {
|
|
@@ -538,11 +527,15 @@ class Connection {
|
|
|
538
527
|
await this._rest.updatePlayer(payload)
|
|
539
528
|
} catch (e) {
|
|
540
529
|
if (e.statusCode === 404 || e.response?.statusCode === 404) {
|
|
530
|
+
const isSessionError = e.body?.message?.includes('sessionId') || false
|
|
541
531
|
if (this._aqua) {
|
|
542
532
|
this._aqua.emit(
|
|
543
533
|
AqualinkEvents.Debug,
|
|
544
|
-
`Player ${this._guildId} not found (404). Destroying.`
|
|
534
|
+
`[Aqua/Connection] Player ${this._guildId} not found (404)${isSessionError ? ' - Session invalid' : ''}. Destroying.`
|
|
545
535
|
)
|
|
536
|
+
if (isSessionError && this._player?.nodes?._clearSession) {
|
|
537
|
+
this._player.nodes._clearSession()
|
|
538
|
+
}
|
|
546
539
|
await this._aqua.destroyPlayer(this._guildId)
|
|
547
540
|
}
|
|
548
541
|
throw e
|
|
@@ -581,4 +574,4 @@ class Connection {
|
|
|
581
574
|
}
|
|
582
575
|
}
|
|
583
576
|
|
|
584
|
-
module.exports = Connection
|
|
577
|
+
module.exports = Connection
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
1
|
const FILTER_DEFAULTS = Object.freeze({
|
|
4
2
|
karaoke: Object.freeze({
|
|
5
3
|
level: 1,
|
|
@@ -41,6 +39,27 @@ const FILTER_KEYS = Object.freeze(
|
|
|
41
39
|
|
|
42
40
|
const EMPTY_ARRAY = Object.freeze([])
|
|
43
41
|
|
|
42
|
+
const FILTER_POOL_SIZE = 16
|
|
43
|
+
const filterPool = {
|
|
44
|
+
pools: Object.fromEntries(Object.keys(FILTER_DEFAULTS).map(k => [k, []])),
|
|
45
|
+
|
|
46
|
+
acquire(type) {
|
|
47
|
+
const pool = this.pools[type]
|
|
48
|
+
if (pool && pool.length > 0) {
|
|
49
|
+
return pool.pop()
|
|
50
|
+
}
|
|
51
|
+
return { ...FILTER_DEFAULTS[type] }
|
|
52
|
+
},
|
|
53
|
+
|
|
54
|
+
release(type, obj) {
|
|
55
|
+
if (!obj || !this.pools[type]) return
|
|
56
|
+
const pool = this.pools[type]
|
|
57
|
+
if (pool.length < FILTER_POOL_SIZE) {
|
|
58
|
+
pool.push(obj)
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
44
63
|
const _utils = Object.freeze({
|
|
45
64
|
shallowEqual(current, defaults, override, keys) {
|
|
46
65
|
if (!current) return false
|
|
@@ -73,6 +92,19 @@ const _utils = Object.freeze({
|
|
|
73
92
|
const out = new Array(len)
|
|
74
93
|
for (let i = 0; i < len; i++) out[i] = { band: i, gain }
|
|
75
94
|
return out
|
|
95
|
+
},
|
|
96
|
+
|
|
97
|
+
mutateFilter(target, defaults, options, keys) {
|
|
98
|
+
let changed = false
|
|
99
|
+
for (let i = 0; i < keys.length; i++) {
|
|
100
|
+
const k = keys[i]
|
|
101
|
+
const newVal = k in options ? options[k] : defaults[k]
|
|
102
|
+
if (target[k] !== newVal) {
|
|
103
|
+
target[k] = newVal
|
|
104
|
+
changed = true
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return changed
|
|
76
108
|
}
|
|
77
109
|
})
|
|
78
110
|
|
|
@@ -106,6 +138,11 @@ class Filters {
|
|
|
106
138
|
}
|
|
107
139
|
|
|
108
140
|
destroy() {
|
|
141
|
+
for (const [key, value] of Object.entries(this.filters)) {
|
|
142
|
+
if (value && typeof value === 'object' && key !== 'equalizer') {
|
|
143
|
+
filterPool.release(key, value)
|
|
144
|
+
}
|
|
145
|
+
}
|
|
109
146
|
this._pendingUpdate = false
|
|
110
147
|
this.player = null
|
|
111
148
|
}
|
|
@@ -114,16 +151,27 @@ class Filters {
|
|
|
114
151
|
const current = this.filters[filterName]
|
|
115
152
|
if (!enabled) {
|
|
116
153
|
if (current === null) return this
|
|
154
|
+
filterPool.release(filterName, current)
|
|
117
155
|
this.filters[filterName] = null
|
|
156
|
+
this._dirty.add(filterName)
|
|
118
157
|
return this._scheduleUpdate()
|
|
119
158
|
}
|
|
120
159
|
|
|
121
160
|
const defaults = FILTER_DEFAULTS[filterName]
|
|
122
161
|
const keys = FILTER_KEYS[filterName]
|
|
123
|
-
if (current && _utils.shallowEqual(current, defaults, options, keys))
|
|
124
|
-
return this
|
|
125
162
|
|
|
126
|
-
|
|
163
|
+
if (current) {
|
|
164
|
+
if (_utils.shallowEqual(current, defaults, options, keys)) {
|
|
165
|
+
return this
|
|
166
|
+
}
|
|
167
|
+
_utils.mutateFilter(current, defaults, options, keys)
|
|
168
|
+
this._dirty.add(filterName)
|
|
169
|
+
return this._scheduleUpdate()
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const newFilter = filterPool.acquire(filterName)
|
|
173
|
+
_utils.mutateFilter(newFilter, defaults, options, keys)
|
|
174
|
+
this.filters[filterName] = newFilter
|
|
127
175
|
this._dirty.add(filterName)
|
|
128
176
|
return this._scheduleUpdate()
|
|
129
177
|
}
|
package/build/structures/Node.js
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
1
|
const IS_BUN = !!(process?.isBun || process?.versions?.bun || globalThis.Bun)
|
|
4
2
|
if (process && typeof process.isBun !== 'boolean') process.isBun = IS_BUN
|
|
5
3
|
|
|
@@ -263,7 +261,6 @@ class Node {
|
|
|
263
261
|
|
|
264
262
|
_handleClose(code, reason) {
|
|
265
263
|
this.connected = false
|
|
266
|
-
const wasReady = this.state === NODE_STATE.READY
|
|
267
264
|
this.state = this.isDestroyed ? NODE_STATE.IDLE : NODE_STATE.RECONNECTING
|
|
268
265
|
this._isConnecting = false
|
|
269
266
|
|
|
@@ -343,7 +340,7 @@ class Node {
|
|
|
343
340
|
_calcBackoff(attempt) {
|
|
344
341
|
const baseBackoff =
|
|
345
342
|
this.reconnectTimeout *
|
|
346
|
-
|
|
343
|
+
Node.BACKOFF_MULTIPLIER ** Math.min(attempt, 10)
|
|
347
344
|
const maxJitter = Math.min(
|
|
348
345
|
Node.JITTER_MAX,
|
|
349
346
|
baseBackoff * Node.JITTER_FACTOR
|
|
@@ -667,4 +664,4 @@ class Node {
|
|
|
667
664
|
}
|
|
668
665
|
}
|
|
669
666
|
|
|
670
|
-
module.exports = Node
|
|
667
|
+
module.exports = Node
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
1
|
const { EventEmitter } = require('tseep')
|
|
4
2
|
const { AqualinkEvents } = require('./AqualinkEvents')
|
|
5
3
|
const Connection = require('./Connection')
|
|
@@ -41,7 +39,6 @@ const WATCHDOG_INTERVAL = 15000
|
|
|
41
39
|
const VOICE_DOWN_THRESHOLD = 10000
|
|
42
40
|
const VOICE_ABANDON_MULTIPLIER = 12
|
|
43
41
|
const RECONNECT_MAX = 15
|
|
44
|
-
const RESUME_TIMEOUT = 5000
|
|
45
42
|
const MUTE_TOGGLE_DELAY = 300
|
|
46
43
|
const SEEK_DELAY = 800
|
|
47
44
|
const PAUSE_DELAY = 1200
|
|
@@ -263,10 +260,16 @@ class Player extends EventEmitter {
|
|
|
263
260
|
this.timestamp = _functions.isNum(s.time) ? s.time : Date.now()
|
|
264
261
|
|
|
265
262
|
if (!this.connected) {
|
|
266
|
-
if (!this._voiceDownSince) {
|
|
263
|
+
if (!this._voiceDownSince && !this._reconnecting && !this._voiceRecovering) {
|
|
267
264
|
this._voiceDownSince = Date.now()
|
|
268
265
|
this._createTimer(() => {
|
|
269
|
-
if (
|
|
266
|
+
if (
|
|
267
|
+
this.connected ||
|
|
268
|
+
this.destroyed ||
|
|
269
|
+
this._reconnecting ||
|
|
270
|
+
this._voiceRecovering ||
|
|
271
|
+
this.nodes?.info?.isNodelink
|
|
272
|
+
)
|
|
270
273
|
return
|
|
271
274
|
this.connection.attemptResume()
|
|
272
275
|
}, 1000)
|
|
@@ -387,7 +390,9 @@ class Player extends EventEmitter {
|
|
|
387
390
|
this.nodes?.info?.isNodelink ||
|
|
388
391
|
this.destroyed ||
|
|
389
392
|
!this.voiceChannel ||
|
|
390
|
-
this.connected
|
|
393
|
+
this.connected ||
|
|
394
|
+
this._reconnecting ||
|
|
395
|
+
this._voiceRecovering
|
|
391
396
|
)
|
|
392
397
|
return false
|
|
393
398
|
if (
|
|
@@ -395,7 +400,7 @@ class Player extends EventEmitter {
|
|
|
395
400
|
Date.now() - this._voiceDownSince < VOICE_DOWN_THRESHOLD
|
|
396
401
|
)
|
|
397
402
|
return false
|
|
398
|
-
return
|
|
403
|
+
return this.reconnectionRetries < RECONNECT_MAX
|
|
399
404
|
}
|
|
400
405
|
|
|
401
406
|
async _voiceWatchdog() {
|
|
@@ -479,6 +484,7 @@ class Player extends EventEmitter {
|
|
|
479
484
|
this.autoplayRetries = this.reconnectionRetries = 0
|
|
480
485
|
if (!preserveReconnecting) this._reconnecting = false
|
|
481
486
|
this._lastVoiceChannel = this.voiceChannel
|
|
487
|
+
this._lastTextChannel = this.textChannel
|
|
482
488
|
this.voiceChannel = null
|
|
483
489
|
|
|
484
490
|
if (
|
|
@@ -728,7 +734,7 @@ class Player extends EventEmitter {
|
|
|
728
734
|
this.destroyed ||
|
|
729
735
|
!this.isAutoplayEnabled ||
|
|
730
736
|
!this.previous ||
|
|
731
|
-
(this.queue
|
|
737
|
+
(this.queue?.size)
|
|
732
738
|
)
|
|
733
739
|
return this
|
|
734
740
|
const prev = this.previous
|
|
@@ -947,18 +953,21 @@ class Player extends EventEmitter {
|
|
|
947
953
|
}
|
|
948
954
|
|
|
949
955
|
async socketClosed(player, track, payload) {
|
|
950
|
-
if (this.destroyed) return
|
|
956
|
+
if (this.destroyed || this._reconnecting) return
|
|
957
|
+
|
|
951
958
|
const code = payload?.code
|
|
952
959
|
let isRecoverable = [4015, 4009, 4006, 4014, 4022].includes(code)
|
|
953
960
|
if (code === 4014 && this.connection?.isWaitingForDisconnect)
|
|
954
961
|
isRecoverable = false
|
|
955
962
|
|
|
956
963
|
if (code === 4015 && !this.nodes?.info?.isNodelink) {
|
|
964
|
+
this._reconnecting = true
|
|
957
965
|
try {
|
|
958
966
|
await this._attemptVoiceResume()
|
|
967
|
+
this._reconnecting = false
|
|
959
968
|
return
|
|
960
969
|
} catch {
|
|
961
|
-
|
|
970
|
+
this._reconnecting = false
|
|
962
971
|
}
|
|
963
972
|
}
|
|
964
973
|
|
|
@@ -974,8 +983,6 @@ class Player extends EventEmitter {
|
|
|
974
983
|
if (code === 4022) this._suppressResumeUntil = Date.now() + 3000
|
|
975
984
|
}
|
|
976
985
|
|
|
977
|
-
if (this._reconnecting) return
|
|
978
|
-
|
|
979
986
|
const aqua = this.aqua
|
|
980
987
|
const vcId = _functions.toId(this.voiceChannel)
|
|
981
988
|
const tcId = _functions.toId(this.textChannel)
|
|
@@ -1102,7 +1109,10 @@ class Player extends EventEmitter {
|
|
|
1102
1109
|
|
|
1103
1110
|
set(key, value) {
|
|
1104
1111
|
if (this.destroyed) return
|
|
1105
|
-
|
|
1112
|
+
if (!this._dataStore) {
|
|
1113
|
+
this._dataStore = new Map()
|
|
1114
|
+
}
|
|
1115
|
+
this._dataStore.set(key, value)
|
|
1106
1116
|
}
|
|
1107
1117
|
|
|
1108
1118
|
get(key) {
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
1
|
class Queue {
|
|
4
2
|
constructor() {
|
|
5
3
|
this._items = []
|
|
@@ -41,10 +39,15 @@ class Queue {
|
|
|
41
39
|
}
|
|
42
40
|
|
|
43
41
|
shuffle() {
|
|
44
|
-
// Compact first if needed
|
|
45
42
|
if (this._head > 0) {
|
|
46
|
-
|
|
47
|
-
|
|
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
|
+
}
|
|
48
51
|
}
|
|
49
52
|
for (let i = this._items.length - 1; i > 0; i--) {
|
|
50
53
|
const j = Math.floor(Math.random() * (i + 1))
|
|
@@ -103,9 +106,12 @@ class Queue {
|
|
|
103
106
|
const item = this._items[this._head]
|
|
104
107
|
this._items[this._head] = undefined // Allow GC
|
|
105
108
|
this._head++
|
|
106
|
-
// Compact when head is > 50% of array to prevent unbounded growth
|
|
107
109
|
if (this._head > 0 && this._head > this._items.length / 2) {
|
|
108
|
-
|
|
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
|
|
109
115
|
this._head = 0
|
|
110
116
|
}
|
|
111
117
|
return item
|
package/build/structures/Rest.js
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
const {
|
|
4
|
-
const
|
|
5
|
-
const { Agent: HttpAgent, request: httpRequest } = require('http')
|
|
6
|
-
const http2 = require('http2')
|
|
1
|
+
const { Buffer } = require('node:buffer')
|
|
2
|
+
const { Agent: HttpsAgent, request: httpsRequest } = require('node:https')
|
|
3
|
+
const { Agent: HttpAgent, request: httpRequest } = require('node:http')
|
|
4
|
+
const http2 = require('node:http2')
|
|
7
5
|
const {
|
|
8
6
|
createBrotliDecompress,
|
|
9
7
|
createUnzip,
|
|
@@ -11,7 +9,12 @@ const {
|
|
|
11
9
|
unzipSync,
|
|
12
10
|
createZstdDecompress,
|
|
13
11
|
zstdDecompressSync
|
|
14
|
-
} = require('zlib')
|
|
12
|
+
} = require('node:zlib')
|
|
13
|
+
|
|
14
|
+
let autoplayModule = null
|
|
15
|
+
try {
|
|
16
|
+
autoplayModule = require('../handlers/autoplay')
|
|
17
|
+
} catch {}
|
|
15
18
|
|
|
16
19
|
const unrefTimer = (t) => {
|
|
17
20
|
try {
|
|
@@ -203,6 +206,10 @@ class Rest {
|
|
|
203
206
|
this.agent = new (node.ssl ? HttpsAgent : HttpAgent)(opts)
|
|
204
207
|
this.request = node.ssl ? httpsRequest : httpRequest
|
|
205
208
|
|
|
209
|
+
if (node.ssl && autoplayModule?.setSharedAgent) {
|
|
210
|
+
autoplayModule.setSharedAgent(this.agent)
|
|
211
|
+
}
|
|
212
|
+
|
|
206
213
|
const origCreate = this.agent.createConnection.bind(this.agent)
|
|
207
214
|
this.agent.createConnection = (options, cb) => {
|
|
208
215
|
const socket = origCreate(options, cb)
|
|
@@ -847,4 +854,4 @@ class Rest {
|
|
|
847
854
|
}
|
|
848
855
|
}
|
|
849
856
|
|
|
850
|
-
module.exports = Rest
|
|
857
|
+
module.exports = Rest
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
1
|
const YT_ID_REGEX =
|
|
4
2
|
/(?:[?&]v=|youtu\.be\/|\/embed\/|\/shorts\/)([A-Za-z0-9_-]{11})/
|
|
5
3
|
|
|
@@ -30,10 +28,12 @@ class Track {
|
|
|
30
28
|
this.nodes = data.nodes || null
|
|
31
29
|
this.requester = requester || null
|
|
32
30
|
this._infoCache = null
|
|
31
|
+
this._artworkCache = undefined // undefined = not computed, null = computed but no artwork
|
|
33
32
|
}
|
|
34
33
|
|
|
35
34
|
get info() {
|
|
36
|
-
|
|
35
|
+
if (this._infoCache) return this._infoCache
|
|
36
|
+
this._infoCache = Object.freeze({
|
|
37
37
|
identifier: this.identifier,
|
|
38
38
|
isSeekable: this.isSeekable,
|
|
39
39
|
position: this.position,
|
|
@@ -44,7 +44,8 @@ class Track {
|
|
|
44
44
|
uri: this.uri,
|
|
45
45
|
sourceName: this.sourceName,
|
|
46
46
|
artworkUrl: this.artworkUrl || this._computeArtwork()
|
|
47
|
-
})
|
|
47
|
+
})
|
|
48
|
+
return this._infoCache
|
|
48
49
|
}
|
|
49
50
|
|
|
50
51
|
get length() {
|
|
@@ -52,7 +53,10 @@ class Track {
|
|
|
52
53
|
}
|
|
53
54
|
|
|
54
55
|
get thumbnail() {
|
|
55
|
-
|
|
56
|
+
if (this.artworkUrl) return this.artworkUrl
|
|
57
|
+
if (this._artworkCache !== undefined) return this._artworkCache
|
|
58
|
+
this._artworkCache = this._computeArtwork()
|
|
59
|
+
return this._artworkCache
|
|
56
60
|
}
|
|
57
61
|
|
|
58
62
|
async resolve(aqua, opts = {}) {
|
|
@@ -140,10 +144,13 @@ class Track {
|
|
|
140
144
|
|
|
141
145
|
_computeArtwork() {
|
|
142
146
|
if (this.artworkUrl) return this.artworkUrl
|
|
147
|
+
if (this._artworkCache !== undefined) return this._artworkCache
|
|
143
148
|
const id = this.identifier || (this.uri && YT_ID_REGEX.exec(this.uri)?.[1])
|
|
144
149
|
if (id && this.sourceName?.includes('youtube')) {
|
|
145
|
-
|
|
150
|
+
this._artworkCache = `https://i.ytimg.com/vi/${id}/hqdefault.jpg`
|
|
151
|
+
return this._artworkCache
|
|
146
152
|
}
|
|
153
|
+
this._artworkCache = null
|
|
147
154
|
return null
|
|
148
155
|
}
|
|
149
156
|
}
|