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.
@@ -1,9 +1,7 @@
1
- 'use strict'
2
-
3
- const { Buffer } = require('buffer')
4
- const { Agent: HttpsAgent, request: httpsRequest } = require('https')
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 {
@@ -147,7 +150,7 @@ class Rest {
147
150
  decodetracks: `${this._apiBase}/decodetracks`,
148
151
  stats: `${this._apiBase}/stats`,
149
152
  info: `${this._apiBase}/info`,
150
- version: `${this._apiBase}/version`,
153
+ version: `/version`,
151
154
  routeplanner: Object.freeze({
152
155
  status: `${this._apiBase}/routeplanner/status`,
153
156
  freeAddress: `${this._apiBase}/routeplanner/free/address`,
@@ -173,6 +176,7 @@ class Rest {
173
176
  this.useHttp2 = !!aqua?.options?.useHttp2
174
177
  this._h2 = null
175
178
  this._h2Timer = null
179
+ this.calls = 0
176
180
  }
177
181
 
178
182
  _setupAgent(node) {
@@ -203,6 +207,10 @@ class Rest {
203
207
  this.agent = new (node.ssl ? HttpsAgent : HttpAgent)(opts)
204
208
  this.request = node.ssl ? httpsRequest : httpRequest
205
209
 
210
+ if (node.ssl && autoplayModule?.setSharedAgent) {
211
+ autoplayModule.setSharedAgent(this.agent)
212
+ }
213
+
206
214
  const origCreate = this.agent.createConnection.bind(this.agent)
207
215
  this.agent.createConnection = (options, cb) => {
208
216
  const socket = origCreate(options, cb)
@@ -249,6 +257,88 @@ class Rest {
249
257
  }
250
258
  }
251
259
 
260
+ _collectBody(stream, preallocSize = 0) {
261
+ return new Promise((resolve, reject) => {
262
+ let done = false
263
+ let size = 0
264
+ let prealloc =
265
+ preallocSize > 0 && preallocSize <= MAX_RESPONSE_SIZE
266
+ ? Buffer.allocUnsafe(preallocSize)
267
+ : null
268
+ let chunks = prealloc ? null : []
269
+
270
+ const finish = (ok, val) => {
271
+ if (done) return
272
+ done = true
273
+ prealloc = null
274
+ chunks = null
275
+ ok ? resolve(val) : reject(val)
276
+ }
277
+
278
+ stream.on('data', (chunk) => {
279
+ if (done) return
280
+ if (prealloc) {
281
+ if (size + chunk.length <= prealloc.length) {
282
+ chunk.copy(prealloc, size)
283
+ size += chunk.length
284
+ } else {
285
+ chunks = [prealloc.slice(0, size), chunk]
286
+ size += chunk.length
287
+ prealloc = null
288
+ }
289
+ } else {
290
+ size += chunk.length
291
+ chunks.push(chunk)
292
+ }
293
+ if (size > MAX_RESPONSE_SIZE) finish(false, ERRORS.RESPONSE_TOO_LARGE)
294
+ })
295
+
296
+ stream.once('error', (e) => finish(false, e))
297
+ stream.once('end', () => {
298
+ if (done) return
299
+ if (size === 0) return finish(true, null)
300
+ finish(
301
+ true,
302
+ prealloc
303
+ ? prealloc.slice(0, size)
304
+ : chunks.length === 1
305
+ ? chunks[0]
306
+ : Buffer.concat(chunks, size)
307
+ )
308
+ })
309
+ })
310
+ }
311
+
312
+ _parseResponseBuffer(
313
+ buffer,
314
+ status,
315
+ method,
316
+ url,
317
+ headers,
318
+ contentType,
319
+ statusMessage
320
+ ) {
321
+ if (!buffer) return null
322
+ if (buffer.length > MAX_RESPONSE_SIZE) throw ERRORS.RESPONSE_TOO_LARGE
323
+ let result
324
+ try {
325
+ result = _functions.parseBody(buffer, contentType, false)
326
+ } catch (e) {
327
+ throw new Error(`JSON parse error: ${e.message}`)
328
+ }
329
+ if (status >= 400) {
330
+ throw _functions.createHttpError(
331
+ status,
332
+ method,
333
+ url,
334
+ headers,
335
+ result,
336
+ statusMessage
337
+ )
338
+ }
339
+ return result
340
+ }
341
+
252
342
  async makeRequest(method, endpoint, body) {
253
343
  const url = `${this.baseUrl}${endpoint}`
254
344
  const payload =
@@ -259,6 +349,7 @@ class Rest {
259
349
  : JSON.stringify(body)
260
350
  const payloadLen = payload ? Buffer.byteLength(payload, UTF8) : 0
261
351
  const headers = this._buildHeaders(!!payload, payloadLen)
352
+ this.calls++
262
353
 
263
354
  try {
264
355
  const resp =
@@ -267,6 +358,7 @@ class Rest {
267
358
  : await this._h1Request(method, url, headers, payload)
268
359
  return resp
269
360
  } finally {
361
+ if (this.calls > 0) this.calls--
270
362
  this._returnHeaders(headers)
271
363
  }
272
364
  }
@@ -276,9 +368,6 @@ class Rest {
276
368
  let req,
277
369
  timer,
278
370
  done = false
279
- let chunks = null,
280
- size = 0,
281
- prealloc = null
282
371
 
283
372
  const finish = (ok, val) => {
284
373
  if (done) return
@@ -287,7 +376,6 @@ class Rest {
287
376
  clearTimeout(timer)
288
377
  timer = null
289
378
  }
290
- chunks = prealloc = null
291
379
  if (req && !ok) req.destroy()
292
380
  ok ? resolve(val) : reject(val)
293
381
  }
@@ -319,104 +407,63 @@ class Rest {
319
407
  const encoding = _functions.getEncodingType(
320
408
  res.headers['content-encoding']
321
409
  )
322
-
323
- const handleResponse = (buffer) => {
324
- if (buffer.length > MAX_RESPONSE_SIZE)
325
- return finish(false, ERRORS.RESPONSE_TOO_LARGE)
326
-
410
+ const finalize = (buffer) => {
327
411
  try {
328
- const result = _functions.parseBody(buffer, contentType, false)
329
- if (status >= 400) {
330
- finish(
331
- false,
332
- _functions.createHttpError(
333
- status,
334
- method,
335
- url,
336
- res.headers,
337
- result,
338
- res.statusMessage
339
- )
340
- )
341
- } else {
342
- finish(true, result)
343
- }
412
+ const result = this._parseResponseBuffer(
413
+ buffer,
414
+ status,
415
+ method,
416
+ url,
417
+ res.headers,
418
+ contentType,
419
+ res.statusMessage
420
+ )
421
+ finish(true, result)
344
422
  } catch (e) {
345
- finish(false, new Error(`JSON parse error: ${e.message}`))
423
+ finish(false, e)
346
424
  }
347
425
  }
348
426
 
427
+ res.once('aborted', () => finish(false, ERRORS.RESPONSE_ABORTED))
428
+ res.once('error', (e) => finish(false, e))
429
+
349
430
  if (
350
431
  encoding !== ENCODING_NONE &&
351
432
  clInt > 0 &&
352
433
  clInt < COMPRESSION_MIN_SIZE
353
434
  ) {
354
- const compressed = []
355
- let csize = 0
356
-
357
- res.on('data', (c) => {
358
- csize += c.length
359
- if (csize > MAX_RESPONSE_SIZE)
360
- return finish(false, ERRORS.RESPONSE_TOO_LARGE)
361
- compressed.push(c)
362
- })
363
- res.once('end', () => {
364
- try {
365
- handleResponse(
366
- _functions.decompressSync(Buffer.concat(compressed), encoding)
435
+ this._collectBody(res)
436
+ .then((compressed) => {
437
+ if (!compressed) return finish(true, null)
438
+ const decompressed = _functions.decompressSync(
439
+ compressed,
440
+ encoding
367
441
  )
368
- } catch (e) {
442
+ finalize(decompressed)
443
+ })
444
+ .catch((e) => {
369
445
  finish(false, e)
370
- }
371
- })
372
- res.once('aborted', () => finish(false, ERRORS.RESPONSE_ABORTED))
373
- res.once('error', (e) => finish(false, e))
446
+ })
374
447
  return
375
448
  }
376
449
 
377
- if (
378
- encoding === ENCODING_NONE &&
379
- clInt > 0 &&
380
- clInt <= MAX_RESPONSE_SIZE
381
- ) {
382
- prealloc = Buffer.allocUnsafe(clInt)
383
- } else {
384
- chunks = []
385
- }
386
-
387
450
  let stream = res
451
+ let preallocSize = 0
388
452
  if (encoding !== ENCODING_NONE) {
389
453
  const decomp = _functions.createDecompressor(encoding)
390
454
  decomp.once('error', (e) => finish(false, e))
391
455
  res.pipe(decomp)
392
456
  stream = decomp
457
+ } else if (clInt > 0 && clInt <= MAX_RESPONSE_SIZE) {
458
+ preallocSize = clInt
393
459
  }
394
460
 
395
- res.once('aborted', () => finish(false, ERRORS.RESPONSE_ABORTED))
396
- res.once('error', (e) => finish(false, e))
397
-
398
- stream.on('data', (chunk) => {
399
- if (prealloc) {
400
- chunk.copy(prealloc, size)
401
- size += chunk.length
402
- } else {
403
- size += chunk.length
404
- if (size > MAX_RESPONSE_SIZE)
405
- return finish(false, ERRORS.RESPONSE_TOO_LARGE)
406
- chunks.push(chunk)
407
- }
408
- })
409
-
410
- stream.once('end', () => {
411
- if (size === 0) return finish(true, null)
412
- handleResponse(
413
- prealloc
414
- ? prealloc.slice(0, size)
415
- : chunks.length === 1
416
- ? chunks[0]
417
- : Buffer.concat(chunks, size)
418
- )
419
- })
461
+ this._collectBody(stream, preallocSize)
462
+ .then((buffer) => {
463
+ if (!buffer) return finish(true, null)
464
+ finalize(buffer)
465
+ })
466
+ .catch((e) => finish(false, e))
420
467
  }
421
468
  )
422
469
 
@@ -483,9 +530,6 @@ class Rest {
483
530
  let req,
484
531
  timer,
485
532
  done = false
486
- let chunks = null,
487
- size = 0,
488
- prealloc = null
489
533
 
490
534
  const finish = (ok, val) => {
491
535
  if (done) return
@@ -494,7 +538,6 @@ class Rest {
494
538
  clearTimeout(timer)
495
539
  timer = null
496
540
  }
497
- chunks = prealloc = null
498
541
  if (req && !ok) req.close(http2.constants.NGHTTP2_CANCEL)
499
542
  ok ? resolve(val) : reject(val)
500
543
  }
@@ -536,15 +579,20 @@ class Rest {
536
579
  }
537
580
 
538
581
  const encoding = _functions.getEncodingType(rh['content-encoding'])
539
-
540
- if (
541
- encoding === ENCODING_NONE &&
542
- clInt > 0 &&
543
- clInt <= MAX_RESPONSE_SIZE
544
- ) {
545
- prealloc = Buffer.allocUnsafe(clInt)
546
- } else {
547
- chunks = []
582
+ const finalize = (buffer) => {
583
+ try {
584
+ const result = this._parseResponseBuffer(
585
+ buffer,
586
+ status,
587
+ method,
588
+ this.baseUrl + path,
589
+ rh,
590
+ contentType
591
+ )
592
+ finish(true, result)
593
+ } catch (e) {
594
+ finish(false, e)
595
+ }
548
596
  }
549
597
 
550
598
  const decomp =
@@ -552,51 +600,19 @@ class Rest {
552
600
  ? _functions.createDecompressor(encoding)
553
601
  : null
554
602
  const stream = decomp ? req.pipe(decomp) : req
603
+ const preallocSize =
604
+ encoding === ENCODING_NONE && clInt > 0 && clInt <= MAX_RESPONSE_SIZE
605
+ ? clInt
606
+ : 0
555
607
 
556
608
  if (decomp) decomp.once('error', (e) => finish(false, e))
557
609
  req.once('error', (e) => finish(false, e))
558
-
559
- stream.on('data', (chunk) => {
560
- if (prealloc) {
561
- chunk.copy(prealloc, size)
562
- size += chunk.length
563
- } else {
564
- size += chunk.length
565
- if (size > MAX_RESPONSE_SIZE)
566
- return finish(false, ERRORS.RESPONSE_TOO_LARGE)
567
- chunks.push(chunk)
568
- }
569
- })
570
-
571
- stream.once('end', () => {
572
- if (size === 0) return finish(true, null)
573
-
574
- const buffer = prealloc
575
- ? prealloc.slice(0, size)
576
- : chunks.length === 1
577
- ? chunks[0]
578
- : Buffer.concat(chunks, size)
579
-
580
- try {
581
- const result = _functions.parseBody(buffer, contentType, false)
582
- if (status >= 400) {
583
- finish(
584
- false,
585
- _functions.createHttpError(
586
- status,
587
- method,
588
- this.baseUrl + path,
589
- rh,
590
- result
591
- )
592
- )
593
- } else {
594
- finish(true, result)
595
- }
596
- } catch (e) {
597
- finish(false, new Error(`JSON parse error: ${e.message}`))
598
- }
599
- })
610
+ this._collectBody(stream, preallocSize)
611
+ .then((buffer) => {
612
+ if (!buffer) return finish(true, null)
613
+ finalize(buffer)
614
+ })
615
+ .catch((e) => finish(false, e))
600
616
  })
601
617
 
602
618
  timer = setTimeout(
@@ -844,6 +860,7 @@ class Rest {
844
860
  this.defaultHeaders =
845
861
  this._endpoints =
846
862
  null
863
+ this.calls = 0
847
864
  }
848
865
  }
849
866
 
@@ -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
- return (this._infoCache ||= Object.freeze({
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
- return this.artworkUrl || this._computeArtwork()
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
- return `https://i.ytimg.com/vi/${id}/hqdefault.jpg`
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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aqualink",
3
- "version": "2.19.0",
3
+ "version": "2.20.0",
4
4
  "description": "An Lavalink/Nodelink client, focused in pure performance and features",
5
5
  "main": "./build/index.js",
6
6
  "types": "./build/index.d.ts",
@@ -45,8 +45,7 @@
45
45
  },
46
46
  "homepage": "https://aqualink-6006388d.mintlify.app/",
47
47
  "dependencies": {
48
- "ws": "^8.19.0",
49
- "tseep": "^1.3.1"
48
+ "ws": "^8.19.0"
50
49
  },
51
50
  "contributors": [
52
51
  {
@@ -68,4 +67,4 @@
68
67
  "url": "https://github.com/ToddyTheNoobDud"
69
68
  }
70
69
  ]
71
- }
70
+ }