@kattowo/distube 6.0.2 → 6.1.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/dist/index.d.mts +186 -4
- package/dist/index.d.ts +186 -4
- package/dist/index.js +256 -26
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +255 -26
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -2,7 +2,7 @@ var __defProp = Object.defineProperty;
|
|
|
2
2
|
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
3
3
|
|
|
4
4
|
// src/constant.ts
|
|
5
|
-
var version = "6.0
|
|
5
|
+
var version = "6.1.0";
|
|
6
6
|
var AUDIO_SAMPLE_RATE = 48e3;
|
|
7
7
|
var AUDIO_CHANNELS = 2;
|
|
8
8
|
var DEFAULT_VOLUME = 50;
|
|
@@ -29,6 +29,7 @@ var defaultFilters = {
|
|
|
29
29
|
surround: "surround",
|
|
30
30
|
earwax: "earwax"
|
|
31
31
|
};
|
|
32
|
+
var DEFAULT_PRELOAD_THRESHOLD_SECS = 15;
|
|
32
33
|
var defaultOptions = {
|
|
33
34
|
plugins: [],
|
|
34
35
|
emitNewSongOnly: false,
|
|
@@ -38,7 +39,8 @@ var defaultOptions = {
|
|
|
38
39
|
emitAddListWhenCreatingQueue: true,
|
|
39
40
|
joinNewVoiceChannel: true,
|
|
40
41
|
unhandledError: "log",
|
|
41
|
-
stuckThreshold: STUCK_THRESHOLD_MS
|
|
42
|
+
stuckThreshold: STUCK_THRESHOLD_MS,
|
|
43
|
+
preload: false
|
|
42
44
|
};
|
|
43
45
|
|
|
44
46
|
// src/core/DisTubeBase.ts
|
|
@@ -615,7 +617,14 @@ var DisTubeVoice = class extends TypedEmitter {
|
|
|
615
617
|
if (newState.reason === VoiceConnectionDisconnectReason.Manual) {
|
|
616
618
|
this.leave();
|
|
617
619
|
} else if (newState.reason === VoiceConnectionDisconnectReason.WebSocketClose && newState.closeCode === 4014) {
|
|
618
|
-
entersState(this.connection, VoiceConnectionStatus.Connecting, RECONNECT_TIMEOUT_MS).
|
|
620
|
+
entersState(this.connection, VoiceConnectionStatus.Connecting, RECONNECT_TIMEOUT_MS).then(() => entersState(this.connection, VoiceConnectionStatus.Ready, RECONNECT_TIMEOUT_MS)).then(() => {
|
|
621
|
+
const oldChannel = this.#channel;
|
|
622
|
+
const currentChannel = this.channel;
|
|
623
|
+
if (oldChannel && currentChannel && oldChannel.id !== currentChannel.id) {
|
|
624
|
+
this.#channel = currentChannel;
|
|
625
|
+
this.emit("channelChange", oldChannel, currentChannel);
|
|
626
|
+
}
|
|
627
|
+
}).catch(() => {
|
|
619
628
|
if (![VoiceConnectionStatus.Ready, VoiceConnectionStatus.Connecting].includes(this.connection.state.status)) {
|
|
620
629
|
this.leave();
|
|
621
630
|
}
|
|
@@ -676,8 +685,12 @@ var DisTubeVoice = class extends TypedEmitter {
|
|
|
676
685
|
if (channel.full) throw new DisTubeError("VOICE_FULL");
|
|
677
686
|
else throw new DisTubeError("VOICE_MISSING_PERMS");
|
|
678
687
|
}
|
|
688
|
+
const oldChannel = this.#channel;
|
|
679
689
|
this.connection = this.#join(channel);
|
|
680
690
|
this.#channel = channel;
|
|
691
|
+
if (oldChannel && oldChannel.id !== channel.id) {
|
|
692
|
+
this.emit("channelChange", oldChannel, channel);
|
|
693
|
+
}
|
|
681
694
|
}
|
|
682
695
|
#join(channel) {
|
|
683
696
|
return joinVoiceChannel({
|
|
@@ -965,7 +978,13 @@ var FilterManager = class extends BaseManager {
|
|
|
965
978
|
}
|
|
966
979
|
throw new DisTubeError("INVALID_TYPE", "FilterResolvable", filter, "filter");
|
|
967
980
|
}
|
|
968
|
-
#apply() {
|
|
981
|
+
#apply(oldFilters) {
|
|
982
|
+
this.queue._clearPreload?.();
|
|
983
|
+
const newFilters = this.names;
|
|
984
|
+
const changed = oldFilters.length !== newFilters.length || !oldFilters.every((f) => newFilters.includes(f));
|
|
985
|
+
if (changed) {
|
|
986
|
+
this.emit("changeFilter" /* CHANGE_FILTER */, this.queue, newFilters, oldFilters);
|
|
987
|
+
}
|
|
969
988
|
void this.queue._restartStream().catch((e) => {
|
|
970
989
|
const errorMessage = e instanceof Error ? e.message : String(e);
|
|
971
990
|
this.queue.debug(`[${this.queue.id}] Failed to apply the filters: ${errorMessage}`);
|
|
@@ -983,6 +1002,7 @@ var FilterManager = class extends BaseManager {
|
|
|
983
1002
|
* ```
|
|
984
1003
|
*/
|
|
985
1004
|
add(filterOrFilters, override = false) {
|
|
1005
|
+
const oldFilters = this.names;
|
|
986
1006
|
if (Array.isArray(filterOrFilters)) {
|
|
987
1007
|
for (const filter of filterOrFilters) {
|
|
988
1008
|
const ft = this.#resolve(filter);
|
|
@@ -992,7 +1012,7 @@ var FilterManager = class extends BaseManager {
|
|
|
992
1012
|
const ft = this.#resolve(filterOrFilters);
|
|
993
1013
|
if (override || !this.has(ft)) this.collection.set(ft.name, ft);
|
|
994
1014
|
}
|
|
995
|
-
this.#apply();
|
|
1015
|
+
this.#apply(oldFilters);
|
|
996
1016
|
return this;
|
|
997
1017
|
}
|
|
998
1018
|
/**
|
|
@@ -1011,12 +1031,13 @@ var FilterManager = class extends BaseManager {
|
|
|
1011
1031
|
*/
|
|
1012
1032
|
set(filters) {
|
|
1013
1033
|
if (!Array.isArray(filters)) throw new DisTubeError("INVALID_TYPE", "Array<FilterResolvable>", filters, "filters");
|
|
1034
|
+
const oldFilters = this.names;
|
|
1014
1035
|
this.collection.clear();
|
|
1015
1036
|
for (const f of filters) {
|
|
1016
1037
|
const filter = this.#resolve(f);
|
|
1017
1038
|
this.collection.set(filter.name, filter);
|
|
1018
1039
|
}
|
|
1019
|
-
this.#apply();
|
|
1040
|
+
this.#apply(oldFilters);
|
|
1020
1041
|
return this;
|
|
1021
1042
|
}
|
|
1022
1043
|
#removeFn(f) {
|
|
@@ -1029,9 +1050,10 @@ var FilterManager = class extends BaseManager {
|
|
|
1029
1050
|
* @throws {@link DisTubeError} When a filter is not a valid {@link FilterResolvable}
|
|
1030
1051
|
*/
|
|
1031
1052
|
remove(filterOrFilters) {
|
|
1053
|
+
const oldFilters = this.names;
|
|
1032
1054
|
if (Array.isArray(filterOrFilters)) filterOrFilters.forEach((f) => this.#removeFn(f));
|
|
1033
1055
|
else this.#removeFn(filterOrFilters);
|
|
1034
|
-
this.#apply();
|
|
1056
|
+
this.#apply(oldFilters);
|
|
1035
1057
|
return this;
|
|
1036
1058
|
}
|
|
1037
1059
|
/**
|
|
@@ -1087,6 +1109,17 @@ var Events = /* @__PURE__ */ ((Events2) => {
|
|
|
1087
1109
|
Events2["DELETE_QUEUE"] = "deleteQueue";
|
|
1088
1110
|
Events2["FFMPEG_DEBUG"] = "ffmpegDebug";
|
|
1089
1111
|
Events2["DEBUG"] = "debug";
|
|
1112
|
+
Events2["PAUSE_SONG"] = "pauseSong";
|
|
1113
|
+
Events2["RESUME_SONG"] = "resumeSong";
|
|
1114
|
+
Events2["SEEK_SONG"] = "seekSong";
|
|
1115
|
+
Events2["SET_VOLUME"] = "setVolume";
|
|
1116
|
+
Events2["SHUFFLE_QUEUE"] = "shuffleQueue";
|
|
1117
|
+
Events2["SKIP_SONG"] = "skipSong";
|
|
1118
|
+
Events2["CHANGE_FILTER"] = "changeFilter";
|
|
1119
|
+
Events2["VOICE_CHANNEL_CHANGE"] = "voiceChannelChange";
|
|
1120
|
+
Events2["PRELOAD_SONG"] = "preloadSong";
|
|
1121
|
+
Events2["PRELOADED_SONG"] = "preloadedSong";
|
|
1122
|
+
Events2["PRELOAD_ERROR"] = "preloadError";
|
|
1090
1123
|
return Events2;
|
|
1091
1124
|
})(Events || {});
|
|
1092
1125
|
var SearchResultType = /* @__PURE__ */ ((SearchResultType2) => {
|
|
@@ -1220,6 +1253,7 @@ var Queue = class extends DisTubeBase {
|
|
|
1220
1253
|
*/
|
|
1221
1254
|
_beginTime;
|
|
1222
1255
|
#filters;
|
|
1256
|
+
#initialized = false;
|
|
1223
1257
|
/**
|
|
1224
1258
|
* Whether or not the queue is being updated manually (skip, jump, previous)
|
|
1225
1259
|
* @internal
|
|
@@ -1235,6 +1269,48 @@ var Queue = class extends DisTubeBase {
|
|
|
1235
1269
|
* @internal
|
|
1236
1270
|
*/
|
|
1237
1271
|
_listeners;
|
|
1272
|
+
/**
|
|
1273
|
+
* Preload timer
|
|
1274
|
+
* @internal
|
|
1275
|
+
*/
|
|
1276
|
+
_preloadTimeout;
|
|
1277
|
+
/**
|
|
1278
|
+
* Preloaded song
|
|
1279
|
+
* @internal
|
|
1280
|
+
*/
|
|
1281
|
+
_preloadedSong;
|
|
1282
|
+
/**
|
|
1283
|
+
* Preloaded stream
|
|
1284
|
+
* @internal
|
|
1285
|
+
*/
|
|
1286
|
+
_preloadedStream;
|
|
1287
|
+
/**
|
|
1288
|
+
* In-flight preload task
|
|
1289
|
+
* @internal
|
|
1290
|
+
*/
|
|
1291
|
+
_preloading;
|
|
1292
|
+
/**
|
|
1293
|
+
* Bumped whenever the preloaded state is invalidated, so an in-flight
|
|
1294
|
+
* preload task can detect that its result is no longer needed.
|
|
1295
|
+
* @internal
|
|
1296
|
+
*/
|
|
1297
|
+
_preloadGeneration = 0;
|
|
1298
|
+
/**
|
|
1299
|
+
* Clear any preloaded song and cancel pending preload timer.
|
|
1300
|
+
* @internal
|
|
1301
|
+
*/
|
|
1302
|
+
_clearPreload() {
|
|
1303
|
+
this._preloadGeneration++;
|
|
1304
|
+
if (this._preloadTimeout) {
|
|
1305
|
+
clearTimeout(this._preloadTimeout);
|
|
1306
|
+
this._preloadTimeout = void 0;
|
|
1307
|
+
}
|
|
1308
|
+
if (this._preloadedStream) {
|
|
1309
|
+
this._preloadedStream.kill();
|
|
1310
|
+
this._preloadedStream = void 0;
|
|
1311
|
+
}
|
|
1312
|
+
this._preloadedSong = void 0;
|
|
1313
|
+
}
|
|
1238
1314
|
/**
|
|
1239
1315
|
* Create a queue for the guild
|
|
1240
1316
|
* @param distube - DisTube
|
|
@@ -1263,6 +1339,7 @@ var Queue = class extends DisTubeBase {
|
|
|
1263
1339
|
input: { ...this.options.ffmpeg.args.input },
|
|
1264
1340
|
output: { ...this.options.ffmpeg.args.output }
|
|
1265
1341
|
};
|
|
1342
|
+
this.#initialized = true;
|
|
1266
1343
|
}
|
|
1267
1344
|
#addToPreviousSongs(songs) {
|
|
1268
1345
|
if (Array.isArray(songs)) {
|
|
@@ -1345,7 +1422,11 @@ var Queue = class extends DisTubeBase {
|
|
|
1345
1422
|
* negative
|
|
1346
1423
|
*/
|
|
1347
1424
|
set volume(value) {
|
|
1425
|
+
const oldVolume = this.voice.volume;
|
|
1348
1426
|
this.voice.volume = value;
|
|
1427
|
+
if (this.#initialized && oldVolume !== value) {
|
|
1428
|
+
this.emit("setVolume" /* SET_VOLUME */, this, value, oldVolume);
|
|
1429
|
+
}
|
|
1349
1430
|
}
|
|
1350
1431
|
/**
|
|
1351
1432
|
* Add a song or a list of songs to the queue.
|
|
@@ -1374,6 +1455,7 @@ var Queue = class extends DisTubeBase {
|
|
|
1374
1455
|
} else {
|
|
1375
1456
|
this.songs.splice(position, 0, song);
|
|
1376
1457
|
}
|
|
1458
|
+
this.queues?.schedulePreload?.(this);
|
|
1377
1459
|
return this;
|
|
1378
1460
|
}
|
|
1379
1461
|
/**
|
|
@@ -1391,7 +1473,9 @@ var Queue = class extends DisTubeBase {
|
|
|
1391
1473
|
try {
|
|
1392
1474
|
if (this.paused) throw new DisTubeError("PAUSED");
|
|
1393
1475
|
this.paused = true;
|
|
1476
|
+
this._clearPreload();
|
|
1394
1477
|
this.voice.pause();
|
|
1478
|
+
this.emit("pauseSong" /* PAUSE_SONG */, this, this.songs[0]);
|
|
1395
1479
|
return this;
|
|
1396
1480
|
} finally {
|
|
1397
1481
|
this._taskQueue.resolve();
|
|
@@ -1412,6 +1496,8 @@ var Queue = class extends DisTubeBase {
|
|
|
1412
1496
|
if (!this.paused) throw new DisTubeError("RESUMED");
|
|
1413
1497
|
this.paused = false;
|
|
1414
1498
|
this.voice.unpause();
|
|
1499
|
+
this.emit("resumeSong" /* RESUME_SONG */, this, this.songs[0]);
|
|
1500
|
+
this.queues?.schedulePreload?.(this);
|
|
1415
1501
|
return this;
|
|
1416
1502
|
} finally {
|
|
1417
1503
|
this._taskQueue.resolve();
|
|
@@ -1476,11 +1562,14 @@ var Queue = class extends DisTubeBase {
|
|
|
1476
1562
|
try {
|
|
1477
1563
|
const playing = this.songs.shift();
|
|
1478
1564
|
if (playing === void 0) return this;
|
|
1565
|
+
this._clearPreload();
|
|
1479
1566
|
for (let i = this.songs.length - 1; i > 0; i--) {
|
|
1480
1567
|
const j = Math.floor(Math.random() * (i + 1));
|
|
1481
1568
|
[this.songs[i], this.songs[j]] = [this.songs[j], this.songs[i]];
|
|
1482
1569
|
}
|
|
1483
1570
|
this.songs.unshift(playing);
|
|
1571
|
+
this.emit("shuffleQueue" /* SHUFFLE_QUEUE */, this, this.songs);
|
|
1572
|
+
this.queues?.schedulePreload?.(this);
|
|
1484
1573
|
return this;
|
|
1485
1574
|
} finally {
|
|
1486
1575
|
this._taskQueue.resolve();
|
|
@@ -1518,12 +1607,14 @@ var Queue = class extends DisTubeBase {
|
|
|
1518
1607
|
} else {
|
|
1519
1608
|
this.#addToPreviousSongs(skipped);
|
|
1520
1609
|
}
|
|
1610
|
+
this.emit("skipSong" /* SKIP_SONG */, this, skipped[0]);
|
|
1521
1611
|
} else if (!this.options.savePreviousSongs) {
|
|
1522
1612
|
throw new DisTubeError("DISABLED_OPTION", "savePreviousSongs");
|
|
1523
1613
|
} else {
|
|
1524
1614
|
const skipped = this.previousSongs.splice(position);
|
|
1525
1615
|
this.songs.unshift(...skipped);
|
|
1526
1616
|
}
|
|
1617
|
+
this._clearPreload();
|
|
1527
1618
|
this.#stop();
|
|
1528
1619
|
return this.songs[0];
|
|
1529
1620
|
} finally {
|
|
@@ -1562,8 +1653,10 @@ var Queue = class extends DisTubeBase {
|
|
|
1562
1653
|
try {
|
|
1563
1654
|
if (typeof time !== "number") throw new DisTubeError("INVALID_TYPE", "number", time, "time");
|
|
1564
1655
|
if (Number.isNaN(time) || time < 0) throw new DisTubeError("NUMBER_COMPARE", "time", "bigger or equal to", 0);
|
|
1656
|
+
this._clearPreload();
|
|
1565
1657
|
this._beginTime = time;
|
|
1566
1658
|
await this.play(false);
|
|
1659
|
+
this.emit("seekSong" /* SEEK_SONG */, this, this.songs[0], time);
|
|
1567
1660
|
return this;
|
|
1568
1661
|
} finally {
|
|
1569
1662
|
this._taskQueue.resolve();
|
|
@@ -1634,6 +1727,7 @@ var Queue = class extends DisTubeBase {
|
|
|
1634
1727
|
* @returns Nothing
|
|
1635
1728
|
*/
|
|
1636
1729
|
remove() {
|
|
1730
|
+
this._clearPreload();
|
|
1637
1731
|
this.paused = true;
|
|
1638
1732
|
this.stopped = true;
|
|
1639
1733
|
this.songs = [];
|
|
@@ -2307,6 +2401,11 @@ var Options = class {
|
|
|
2307
2401
|
* @default 10000
|
|
2308
2402
|
*/
|
|
2309
2403
|
stuckThreshold;
|
|
2404
|
+
/**
|
|
2405
|
+
* The preload options of the DisTube instance.
|
|
2406
|
+
* @default { enabled: false, threshold: 15 }
|
|
2407
|
+
*/
|
|
2408
|
+
preload;
|
|
2310
2409
|
/**
|
|
2311
2410
|
* The FFmpeg options of the DisTube instance.
|
|
2312
2411
|
*/
|
|
@@ -2332,10 +2431,34 @@ var Options = class {
|
|
|
2332
2431
|
this.joinNewVoiceChannel = opts.joinNewVoiceChannel;
|
|
2333
2432
|
this.unhandledError = opts.unhandledError;
|
|
2334
2433
|
this.stuckThreshold = opts.stuckThreshold;
|
|
2434
|
+
this.preload = this.#preloadOption(opts.preload);
|
|
2335
2435
|
this.ffmpeg = this.#ffmpegOption(options);
|
|
2336
2436
|
checkInvalidKey(opts, this, "DisTubeOptions");
|
|
2337
2437
|
this.#validateOptions();
|
|
2338
2438
|
}
|
|
2439
|
+
#preloadOption(value) {
|
|
2440
|
+
if (value === void 0 || value === false) {
|
|
2441
|
+
return { enabled: false, threshold: DEFAULT_PRELOAD_THRESHOLD_SECS };
|
|
2442
|
+
}
|
|
2443
|
+
if (value === true) {
|
|
2444
|
+
return { enabled: true, threshold: DEFAULT_PRELOAD_THRESHOLD_SECS };
|
|
2445
|
+
}
|
|
2446
|
+
if (typeof value !== "object" || Array.isArray(value)) {
|
|
2447
|
+
throw new DisTubeError("INVALID_TYPE", ["boolean", "object"], value, "DisTubeOptions.preload");
|
|
2448
|
+
}
|
|
2449
|
+
checkInvalidKey(value, ["enabled", "threshold"], "DisTubeOptions.preload");
|
|
2450
|
+
const { enabled, threshold } = value;
|
|
2451
|
+
if (enabled !== void 0 && typeof enabled !== "boolean") {
|
|
2452
|
+
throw new DisTubeError("INVALID_TYPE", "boolean", enabled, "DisTubeOptions.preload.enabled");
|
|
2453
|
+
}
|
|
2454
|
+
if (threshold !== void 0 && (typeof threshold !== "number" || Number.isNaN(threshold) || threshold < 0)) {
|
|
2455
|
+
throw new DisTubeError("INVALID_TYPE", "number", threshold, "DisTubeOptions.preload.threshold");
|
|
2456
|
+
}
|
|
2457
|
+
return {
|
|
2458
|
+
enabled: enabled ?? false,
|
|
2459
|
+
threshold: threshold ?? DEFAULT_PRELOAD_THRESHOLD_SECS
|
|
2460
|
+
};
|
|
2461
|
+
}
|
|
2339
2462
|
#validateOptions(options = this) {
|
|
2340
2463
|
const booleanOptions = /* @__PURE__ */ new Set([
|
|
2341
2464
|
"emitNewSongOnly",
|
|
@@ -2347,7 +2470,7 @@ var Options = class {
|
|
|
2347
2470
|
]);
|
|
2348
2471
|
const numberOptions = /* @__PURE__ */ new Set(["stuckThreshold"]);
|
|
2349
2472
|
const stringOptions = /* @__PURE__ */ new Set(["unhandledError"]);
|
|
2350
|
-
const objectOptions = /* @__PURE__ */ new Set(["customFilters", "ffmpeg"]);
|
|
2473
|
+
const objectOptions = /* @__PURE__ */ new Set(["customFilters", "ffmpeg", "preload"]);
|
|
2351
2474
|
const optionalOptions = /* @__PURE__ */ new Set(["customFilters"]);
|
|
2352
2475
|
for (const [key, value] of Object.entries(options)) {
|
|
2353
2476
|
if (value === void 0 && optionalOptions.has(key)) continue;
|
|
@@ -2735,7 +2858,10 @@ var QueueManager = class extends GuildIdManager {
|
|
|
2735
2858
|
if (error) this.emitError(error, queue, queue.songs?.[0]);
|
|
2736
2859
|
}, "disconnect"),
|
|
2737
2860
|
error: /* @__PURE__ */ __name((error) => void this.#handlePlayingError(queue, error), "error"),
|
|
2738
|
-
finish: /* @__PURE__ */ __name(() => void this.handleSongFinish(queue), "finish")
|
|
2861
|
+
finish: /* @__PURE__ */ __name(() => void this.handleSongFinish(queue), "finish"),
|
|
2862
|
+
channelChange: /* @__PURE__ */ __name((oldChannel, newChannel) => {
|
|
2863
|
+
this.emit("voiceChannelChange" /* VOICE_CHANNEL_CHANGE */, queue, oldChannel, newChannel);
|
|
2864
|
+
}, "channelChange")
|
|
2739
2865
|
};
|
|
2740
2866
|
for (const event of objectKeys(queue._listeners)) {
|
|
2741
2867
|
queue.voice.on(event, queue._listeners[event]);
|
|
@@ -2833,12 +2959,109 @@ var QueueManager = class extends GuildIdManager {
|
|
|
2833
2959
|
}
|
|
2834
2960
|
try {
|
|
2835
2961
|
const song = queue.songs[0];
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2962
|
+
let dtStream;
|
|
2963
|
+
if (queue._preloadedSong === song && queue._preloadedStream) {
|
|
2964
|
+
this.debug(`[${queue.id}] Using preloaded stream for: ${song}`);
|
|
2965
|
+
dtStream = queue._preloadedStream;
|
|
2966
|
+
queue._preloadedSong = void 0;
|
|
2967
|
+
queue._preloadedStream = void 0;
|
|
2968
|
+
} else {
|
|
2969
|
+
queue._clearPreload();
|
|
2970
|
+
this.debug(`[${queue.id}] Getting stream from: ${song}`);
|
|
2971
|
+
await this.handler.attachStreamInfo(song);
|
|
2972
|
+
const willPlaySong = song.stream.playFromSource ? song : song.stream.song;
|
|
2973
|
+
const stream = willPlaySong?.stream;
|
|
2974
|
+
if (!willPlaySong || !stream?.playFromSource || !stream.url) throw new DisTubeError("NO_STREAM_URL", `${song}`);
|
|
2975
|
+
this.debug(`[${queue.id}] Creating DisTubeStream for: ${willPlaySong}`);
|
|
2976
|
+
const streamOptions = {
|
|
2977
|
+
ffmpeg: {
|
|
2978
|
+
path: this.options.ffmpeg.path,
|
|
2979
|
+
args: {
|
|
2980
|
+
global: { ...queue.ffmpegArgs.global },
|
|
2981
|
+
input: { ...queue.ffmpegArgs.input },
|
|
2982
|
+
output: { ...queue.ffmpegArgs.output, ...queue.filters.ffmpegArgs }
|
|
2983
|
+
}
|
|
2984
|
+
},
|
|
2985
|
+
seek: willPlaySong.duration ? queue._beginTime : void 0
|
|
2986
|
+
};
|
|
2987
|
+
queue._beginTime = 0;
|
|
2988
|
+
dtStream = new DisTubeStream(stream.url, streamOptions);
|
|
2989
|
+
dtStream.on("debug", (data) => this.emit("ffmpegDebug" /* FFMPEG_DEBUG */, `[${queue.id}] ${data}`));
|
|
2990
|
+
}
|
|
2991
|
+
queue._beginTime = 0;
|
|
2992
|
+
this.debug(`[${queue.id}] Started playing: ${song}`);
|
|
2993
|
+
await queue.voice.play(dtStream);
|
|
2994
|
+
if (emitPlaySong) this.emit("playSong" /* PLAY_SONG */, queue, song);
|
|
2995
|
+
this.schedulePreload(queue);
|
|
2996
|
+
} catch (e) {
|
|
2997
|
+
const error = e instanceof Error ? e : new Error(String(e));
|
|
2998
|
+
if (options?.keepSongOnError) {
|
|
2999
|
+
this.debug(`[${queue.id}] Error while restarting the stream: ${error.stack || error.message}`);
|
|
3000
|
+
this.emitError(error, queue, queue.songs[0]);
|
|
3001
|
+
return;
|
|
3002
|
+
}
|
|
3003
|
+
await this.#handlePlayingError(queue, error);
|
|
3004
|
+
}
|
|
3005
|
+
}
|
|
3006
|
+
/**
|
|
3007
|
+
* Schedule preloading the next song in the queue before the current song finishes.
|
|
3008
|
+
* @param queue - The guild queue
|
|
3009
|
+
*/
|
|
3010
|
+
schedulePreload(queue) {
|
|
3011
|
+
if (!this.options.preload.enabled || queue.stopped || queue.paused) return;
|
|
3012
|
+
if (queue._preloadTimeout) {
|
|
3013
|
+
clearTimeout(queue._preloadTimeout);
|
|
3014
|
+
queue._preloadTimeout = void 0;
|
|
3015
|
+
}
|
|
3016
|
+
const currentSong = queue.songs[0];
|
|
3017
|
+
const nextSong = queue.songs[1];
|
|
3018
|
+
if (!currentSong || !nextSong) return;
|
|
3019
|
+
if (queue._preloadedSong === nextSong && queue._preloadedStream) return;
|
|
3020
|
+
const threshold = this.options.preload.threshold;
|
|
3021
|
+
const duration = currentSong.duration ?? 0;
|
|
3022
|
+
const currentTime = queue.currentTime;
|
|
3023
|
+
const remaining = duration > 0 && Number.isFinite(currentTime) ? duration - currentTime : 0;
|
|
3024
|
+
if (duration === 0 || remaining <= threshold) {
|
|
3025
|
+
void this.preloadNextSong(queue);
|
|
3026
|
+
} else {
|
|
3027
|
+
const delayMs = Math.max(0, (remaining - threshold) * 1e3);
|
|
3028
|
+
queue._preloadTimeout = setTimeout(() => {
|
|
3029
|
+
queue._preloadTimeout = void 0;
|
|
3030
|
+
void this.preloadNextSong(queue);
|
|
3031
|
+
}, delayMs);
|
|
3032
|
+
queue._preloadTimeout.unref?.();
|
|
3033
|
+
}
|
|
3034
|
+
}
|
|
3035
|
+
/**
|
|
3036
|
+
* Preload the next song in the queue.
|
|
3037
|
+
* @param queue - The guild queue
|
|
3038
|
+
*/
|
|
3039
|
+
async preloadNextSong(queue) {
|
|
3040
|
+
if (queue._preloading) return queue._preloading;
|
|
3041
|
+
const task = this.#preloadTask(queue);
|
|
3042
|
+
queue._preloading = task;
|
|
3043
|
+
try {
|
|
3044
|
+
await task;
|
|
3045
|
+
} finally {
|
|
3046
|
+
if (queue._preloading === task) queue._preloading = void 0;
|
|
3047
|
+
}
|
|
3048
|
+
}
|
|
3049
|
+
async #preloadTask(queue) {
|
|
3050
|
+
if (!this.options.preload.enabled || queue.stopped || queue.paused) return;
|
|
3051
|
+
const nextSong = queue.songs[1];
|
|
3052
|
+
if (!nextSong) return;
|
|
3053
|
+
if (queue._preloadedSong === nextSong && queue._preloadedStream) return;
|
|
3054
|
+
const generation = queue._preloadGeneration;
|
|
3055
|
+
this.debug(`[${queue.id}] Preloading next song: ${nextSong}`);
|
|
3056
|
+
this.emit("preloadSong" /* PRELOAD_SONG */, queue, nextSong);
|
|
3057
|
+
try {
|
|
3058
|
+
await this.handler.attachStreamInfo(nextSong);
|
|
3059
|
+
if (queue.stopped || queue.songs[1] !== nextSong || generation !== queue._preloadGeneration) return;
|
|
3060
|
+
const willPlaySong = nextSong.stream.playFromSource ? nextSong : nextSong.stream.song;
|
|
2839
3061
|
const stream = willPlaySong?.stream;
|
|
2840
|
-
if (!willPlaySong || !stream?.playFromSource || !stream.url)
|
|
2841
|
-
|
|
3062
|
+
if (!willPlaySong || !stream?.playFromSource || !stream.url) {
|
|
3063
|
+
throw new DisTubeError("NO_STREAM_URL", `${nextSong}`);
|
|
3064
|
+
}
|
|
2842
3065
|
const streamOptions = {
|
|
2843
3066
|
ffmpeg: {
|
|
2844
3067
|
path: this.options.ffmpeg.path,
|
|
@@ -2848,22 +3071,27 @@ var QueueManager = class extends GuildIdManager {
|
|
|
2848
3071
|
output: { ...queue.ffmpegArgs.output, ...queue.filters.ffmpegArgs }
|
|
2849
3072
|
}
|
|
2850
3073
|
},
|
|
2851
|
-
seek:
|
|
3074
|
+
seek: void 0
|
|
2852
3075
|
};
|
|
2853
|
-
queue._beginTime = 0;
|
|
2854
3076
|
const dtStream = new DisTubeStream(stream.url, streamOptions);
|
|
2855
|
-
dtStream.on("debug", (data) => this.emit("ffmpegDebug" /* FFMPEG_DEBUG */, `[${queue.id}] ${data}`));
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
if (emitPlaySong) this.emit("playSong" /* PLAY_SONG */, queue, song);
|
|
2859
|
-
} catch (e) {
|
|
2860
|
-
const error = e instanceof Error ? e : new Error(String(e));
|
|
2861
|
-
if (options?.keepSongOnError) {
|
|
2862
|
-
this.debug(`[${queue.id}] Error while restarting the stream: ${error.stack || error.message}`);
|
|
2863
|
-
this.emitError(error, queue, queue.songs[0]);
|
|
3077
|
+
dtStream.on("debug", (data) => this.emit("ffmpegDebug" /* FFMPEG_DEBUG */, `[${queue.id}] [preload] ${data}`));
|
|
3078
|
+
if (queue.stopped || queue.songs[1] !== nextSong || generation !== queue._preloadGeneration) {
|
|
3079
|
+
dtStream.kill();
|
|
2864
3080
|
return;
|
|
2865
3081
|
}
|
|
2866
|
-
|
|
3082
|
+
queue._preloadedSong = nextSong;
|
|
3083
|
+
queue._preloadedStream = dtStream;
|
|
3084
|
+
this.debug(`[${queue.id}] Successfully preloaded next song: ${nextSong}`);
|
|
3085
|
+
this.emit("preloadedSong" /* PRELOADED_SONG */, queue, nextSong);
|
|
3086
|
+
} catch (e) {
|
|
3087
|
+
if (queue._preloadedSong === nextSong) {
|
|
3088
|
+
queue._preloadedSong = void 0;
|
|
3089
|
+
queue._preloadedStream?.kill();
|
|
3090
|
+
queue._preloadedStream = void 0;
|
|
3091
|
+
}
|
|
3092
|
+
const error = e instanceof Error ? e : new Error(String(e));
|
|
3093
|
+
this.debug(`[${queue.id}] Preload failed for ${nextSong}: ${error.message}`);
|
|
3094
|
+
this.emit("preloadError" /* PRELOAD_ERROR */, queue, nextSong, error);
|
|
2867
3095
|
}
|
|
2868
3096
|
}
|
|
2869
3097
|
};
|
|
@@ -3879,6 +4107,7 @@ export {
|
|
|
3879
4107
|
AUDIO_CHANNELS,
|
|
3880
4108
|
AUDIO_SAMPLE_RATE,
|
|
3881
4109
|
BaseManager,
|
|
4110
|
+
DEFAULT_PRELOAD_THRESHOLD_SECS,
|
|
3882
4111
|
DEFAULT_VOLUME,
|
|
3883
4112
|
DisTube,
|
|
3884
4113
|
DisTubeBase,
|