@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.js
CHANGED
|
@@ -34,6 +34,7 @@ __export(index_exports, {
|
|
|
34
34
|
AUDIO_CHANNELS: () => AUDIO_CHANNELS,
|
|
35
35
|
AUDIO_SAMPLE_RATE: () => AUDIO_SAMPLE_RATE,
|
|
36
36
|
BaseManager: () => BaseManager,
|
|
37
|
+
DEFAULT_PRELOAD_THRESHOLD_SECS: () => DEFAULT_PRELOAD_THRESHOLD_SECS,
|
|
37
38
|
DEFAULT_VOLUME: () => DEFAULT_VOLUME,
|
|
38
39
|
DisTube: () => DisTube,
|
|
39
40
|
DisTubeBase: () => DisTubeBase,
|
|
@@ -96,7 +97,7 @@ __export(index_exports, {
|
|
|
96
97
|
module.exports = __toCommonJS(index_exports);
|
|
97
98
|
|
|
98
99
|
// src/constant.ts
|
|
99
|
-
var version = "6.0
|
|
100
|
+
var version = "6.1.0";
|
|
100
101
|
var AUDIO_SAMPLE_RATE = 48e3;
|
|
101
102
|
var AUDIO_CHANNELS = 2;
|
|
102
103
|
var DEFAULT_VOLUME = 50;
|
|
@@ -123,6 +124,7 @@ var defaultFilters = {
|
|
|
123
124
|
surround: "surround",
|
|
124
125
|
earwax: "earwax"
|
|
125
126
|
};
|
|
127
|
+
var DEFAULT_PRELOAD_THRESHOLD_SECS = 15;
|
|
126
128
|
var defaultOptions = {
|
|
127
129
|
plugins: [],
|
|
128
130
|
emitNewSongOnly: false,
|
|
@@ -132,7 +134,8 @@ var defaultOptions = {
|
|
|
132
134
|
emitAddListWhenCreatingQueue: true,
|
|
133
135
|
joinNewVoiceChannel: true,
|
|
134
136
|
unhandledError: "log",
|
|
135
|
-
stuckThreshold: STUCK_THRESHOLD_MS
|
|
137
|
+
stuckThreshold: STUCK_THRESHOLD_MS,
|
|
138
|
+
preload: false
|
|
136
139
|
};
|
|
137
140
|
|
|
138
141
|
// src/core/DisTubeBase.ts
|
|
@@ -702,7 +705,14 @@ var DisTubeVoice = class extends import_tiny_typed_emitter.TypedEmitter {
|
|
|
702
705
|
if (newState.reason === import_voice.VoiceConnectionDisconnectReason.Manual) {
|
|
703
706
|
this.leave();
|
|
704
707
|
} else if (newState.reason === import_voice.VoiceConnectionDisconnectReason.WebSocketClose && newState.closeCode === 4014) {
|
|
705
|
-
(0, import_voice.entersState)(this.connection, import_voice.VoiceConnectionStatus.Connecting, RECONNECT_TIMEOUT_MS).
|
|
708
|
+
(0, import_voice.entersState)(this.connection, import_voice.VoiceConnectionStatus.Connecting, RECONNECT_TIMEOUT_MS).then(() => (0, import_voice.entersState)(this.connection, import_voice.VoiceConnectionStatus.Ready, RECONNECT_TIMEOUT_MS)).then(() => {
|
|
709
|
+
const oldChannel = this.#channel;
|
|
710
|
+
const currentChannel = this.channel;
|
|
711
|
+
if (oldChannel && currentChannel && oldChannel.id !== currentChannel.id) {
|
|
712
|
+
this.#channel = currentChannel;
|
|
713
|
+
this.emit("channelChange", oldChannel, currentChannel);
|
|
714
|
+
}
|
|
715
|
+
}).catch(() => {
|
|
706
716
|
if (![import_voice.VoiceConnectionStatus.Ready, import_voice.VoiceConnectionStatus.Connecting].includes(this.connection.state.status)) {
|
|
707
717
|
this.leave();
|
|
708
718
|
}
|
|
@@ -763,8 +773,12 @@ var DisTubeVoice = class extends import_tiny_typed_emitter.TypedEmitter {
|
|
|
763
773
|
if (channel.full) throw new DisTubeError("VOICE_FULL");
|
|
764
774
|
else throw new DisTubeError("VOICE_MISSING_PERMS");
|
|
765
775
|
}
|
|
776
|
+
const oldChannel = this.#channel;
|
|
766
777
|
this.connection = this.#join(channel);
|
|
767
778
|
this.#channel = channel;
|
|
779
|
+
if (oldChannel && oldChannel.id !== channel.id) {
|
|
780
|
+
this.emit("channelChange", oldChannel, channel);
|
|
781
|
+
}
|
|
768
782
|
}
|
|
769
783
|
#join(channel) {
|
|
770
784
|
return (0, import_voice.joinVoiceChannel)({
|
|
@@ -1052,7 +1066,13 @@ var FilterManager = class extends BaseManager {
|
|
|
1052
1066
|
}
|
|
1053
1067
|
throw new DisTubeError("INVALID_TYPE", "FilterResolvable", filter, "filter");
|
|
1054
1068
|
}
|
|
1055
|
-
#apply() {
|
|
1069
|
+
#apply(oldFilters) {
|
|
1070
|
+
this.queue._clearPreload?.();
|
|
1071
|
+
const newFilters = this.names;
|
|
1072
|
+
const changed = oldFilters.length !== newFilters.length || !oldFilters.every((f) => newFilters.includes(f));
|
|
1073
|
+
if (changed) {
|
|
1074
|
+
this.emit("changeFilter" /* CHANGE_FILTER */, this.queue, newFilters, oldFilters);
|
|
1075
|
+
}
|
|
1056
1076
|
void this.queue._restartStream().catch((e) => {
|
|
1057
1077
|
const errorMessage = e instanceof Error ? e.message : String(e);
|
|
1058
1078
|
this.queue.debug(`[${this.queue.id}] Failed to apply the filters: ${errorMessage}`);
|
|
@@ -1070,6 +1090,7 @@ var FilterManager = class extends BaseManager {
|
|
|
1070
1090
|
* ```
|
|
1071
1091
|
*/
|
|
1072
1092
|
add(filterOrFilters, override = false) {
|
|
1093
|
+
const oldFilters = this.names;
|
|
1073
1094
|
if (Array.isArray(filterOrFilters)) {
|
|
1074
1095
|
for (const filter of filterOrFilters) {
|
|
1075
1096
|
const ft = this.#resolve(filter);
|
|
@@ -1079,7 +1100,7 @@ var FilterManager = class extends BaseManager {
|
|
|
1079
1100
|
const ft = this.#resolve(filterOrFilters);
|
|
1080
1101
|
if (override || !this.has(ft)) this.collection.set(ft.name, ft);
|
|
1081
1102
|
}
|
|
1082
|
-
this.#apply();
|
|
1103
|
+
this.#apply(oldFilters);
|
|
1083
1104
|
return this;
|
|
1084
1105
|
}
|
|
1085
1106
|
/**
|
|
@@ -1098,12 +1119,13 @@ var FilterManager = class extends BaseManager {
|
|
|
1098
1119
|
*/
|
|
1099
1120
|
set(filters) {
|
|
1100
1121
|
if (!Array.isArray(filters)) throw new DisTubeError("INVALID_TYPE", "Array<FilterResolvable>", filters, "filters");
|
|
1122
|
+
const oldFilters = this.names;
|
|
1101
1123
|
this.collection.clear();
|
|
1102
1124
|
for (const f of filters) {
|
|
1103
1125
|
const filter = this.#resolve(f);
|
|
1104
1126
|
this.collection.set(filter.name, filter);
|
|
1105
1127
|
}
|
|
1106
|
-
this.#apply();
|
|
1128
|
+
this.#apply(oldFilters);
|
|
1107
1129
|
return this;
|
|
1108
1130
|
}
|
|
1109
1131
|
#removeFn(f) {
|
|
@@ -1116,9 +1138,10 @@ var FilterManager = class extends BaseManager {
|
|
|
1116
1138
|
* @throws {@link DisTubeError} When a filter is not a valid {@link FilterResolvable}
|
|
1117
1139
|
*/
|
|
1118
1140
|
remove(filterOrFilters) {
|
|
1141
|
+
const oldFilters = this.names;
|
|
1119
1142
|
if (Array.isArray(filterOrFilters)) filterOrFilters.forEach((f) => this.#removeFn(f));
|
|
1120
1143
|
else this.#removeFn(filterOrFilters);
|
|
1121
|
-
this.#apply();
|
|
1144
|
+
this.#apply(oldFilters);
|
|
1122
1145
|
return this;
|
|
1123
1146
|
}
|
|
1124
1147
|
/**
|
|
@@ -1174,6 +1197,17 @@ var Events = /* @__PURE__ */ ((Events2) => {
|
|
|
1174
1197
|
Events2["DELETE_QUEUE"] = "deleteQueue";
|
|
1175
1198
|
Events2["FFMPEG_DEBUG"] = "ffmpegDebug";
|
|
1176
1199
|
Events2["DEBUG"] = "debug";
|
|
1200
|
+
Events2["PAUSE_SONG"] = "pauseSong";
|
|
1201
|
+
Events2["RESUME_SONG"] = "resumeSong";
|
|
1202
|
+
Events2["SEEK_SONG"] = "seekSong";
|
|
1203
|
+
Events2["SET_VOLUME"] = "setVolume";
|
|
1204
|
+
Events2["SHUFFLE_QUEUE"] = "shuffleQueue";
|
|
1205
|
+
Events2["SKIP_SONG"] = "skipSong";
|
|
1206
|
+
Events2["CHANGE_FILTER"] = "changeFilter";
|
|
1207
|
+
Events2["VOICE_CHANNEL_CHANGE"] = "voiceChannelChange";
|
|
1208
|
+
Events2["PRELOAD_SONG"] = "preloadSong";
|
|
1209
|
+
Events2["PRELOADED_SONG"] = "preloadedSong";
|
|
1210
|
+
Events2["PRELOAD_ERROR"] = "preloadError";
|
|
1177
1211
|
return Events2;
|
|
1178
1212
|
})(Events || {});
|
|
1179
1213
|
var SearchResultType = /* @__PURE__ */ ((SearchResultType2) => {
|
|
@@ -1307,6 +1341,7 @@ var Queue = class extends DisTubeBase {
|
|
|
1307
1341
|
*/
|
|
1308
1342
|
_beginTime;
|
|
1309
1343
|
#filters;
|
|
1344
|
+
#initialized = false;
|
|
1310
1345
|
/**
|
|
1311
1346
|
* Whether or not the queue is being updated manually (skip, jump, previous)
|
|
1312
1347
|
* @internal
|
|
@@ -1322,6 +1357,48 @@ var Queue = class extends DisTubeBase {
|
|
|
1322
1357
|
* @internal
|
|
1323
1358
|
*/
|
|
1324
1359
|
_listeners;
|
|
1360
|
+
/**
|
|
1361
|
+
* Preload timer
|
|
1362
|
+
* @internal
|
|
1363
|
+
*/
|
|
1364
|
+
_preloadTimeout;
|
|
1365
|
+
/**
|
|
1366
|
+
* Preloaded song
|
|
1367
|
+
* @internal
|
|
1368
|
+
*/
|
|
1369
|
+
_preloadedSong;
|
|
1370
|
+
/**
|
|
1371
|
+
* Preloaded stream
|
|
1372
|
+
* @internal
|
|
1373
|
+
*/
|
|
1374
|
+
_preloadedStream;
|
|
1375
|
+
/**
|
|
1376
|
+
* In-flight preload task
|
|
1377
|
+
* @internal
|
|
1378
|
+
*/
|
|
1379
|
+
_preloading;
|
|
1380
|
+
/**
|
|
1381
|
+
* Bumped whenever the preloaded state is invalidated, so an in-flight
|
|
1382
|
+
* preload task can detect that its result is no longer needed.
|
|
1383
|
+
* @internal
|
|
1384
|
+
*/
|
|
1385
|
+
_preloadGeneration = 0;
|
|
1386
|
+
/**
|
|
1387
|
+
* Clear any preloaded song and cancel pending preload timer.
|
|
1388
|
+
* @internal
|
|
1389
|
+
*/
|
|
1390
|
+
_clearPreload() {
|
|
1391
|
+
this._preloadGeneration++;
|
|
1392
|
+
if (this._preloadTimeout) {
|
|
1393
|
+
clearTimeout(this._preloadTimeout);
|
|
1394
|
+
this._preloadTimeout = void 0;
|
|
1395
|
+
}
|
|
1396
|
+
if (this._preloadedStream) {
|
|
1397
|
+
this._preloadedStream.kill();
|
|
1398
|
+
this._preloadedStream = void 0;
|
|
1399
|
+
}
|
|
1400
|
+
this._preloadedSong = void 0;
|
|
1401
|
+
}
|
|
1325
1402
|
/**
|
|
1326
1403
|
* Create a queue for the guild
|
|
1327
1404
|
* @param distube - DisTube
|
|
@@ -1350,6 +1427,7 @@ var Queue = class extends DisTubeBase {
|
|
|
1350
1427
|
input: { ...this.options.ffmpeg.args.input },
|
|
1351
1428
|
output: { ...this.options.ffmpeg.args.output }
|
|
1352
1429
|
};
|
|
1430
|
+
this.#initialized = true;
|
|
1353
1431
|
}
|
|
1354
1432
|
#addToPreviousSongs(songs) {
|
|
1355
1433
|
if (Array.isArray(songs)) {
|
|
@@ -1432,7 +1510,11 @@ var Queue = class extends DisTubeBase {
|
|
|
1432
1510
|
* negative
|
|
1433
1511
|
*/
|
|
1434
1512
|
set volume(value) {
|
|
1513
|
+
const oldVolume = this.voice.volume;
|
|
1435
1514
|
this.voice.volume = value;
|
|
1515
|
+
if (this.#initialized && oldVolume !== value) {
|
|
1516
|
+
this.emit("setVolume" /* SET_VOLUME */, this, value, oldVolume);
|
|
1517
|
+
}
|
|
1436
1518
|
}
|
|
1437
1519
|
/**
|
|
1438
1520
|
* Add a song or a list of songs to the queue.
|
|
@@ -1461,6 +1543,7 @@ var Queue = class extends DisTubeBase {
|
|
|
1461
1543
|
} else {
|
|
1462
1544
|
this.songs.splice(position, 0, song);
|
|
1463
1545
|
}
|
|
1546
|
+
this.queues?.schedulePreload?.(this);
|
|
1464
1547
|
return this;
|
|
1465
1548
|
}
|
|
1466
1549
|
/**
|
|
@@ -1478,7 +1561,9 @@ var Queue = class extends DisTubeBase {
|
|
|
1478
1561
|
try {
|
|
1479
1562
|
if (this.paused) throw new DisTubeError("PAUSED");
|
|
1480
1563
|
this.paused = true;
|
|
1564
|
+
this._clearPreload();
|
|
1481
1565
|
this.voice.pause();
|
|
1566
|
+
this.emit("pauseSong" /* PAUSE_SONG */, this, this.songs[0]);
|
|
1482
1567
|
return this;
|
|
1483
1568
|
} finally {
|
|
1484
1569
|
this._taskQueue.resolve();
|
|
@@ -1499,6 +1584,8 @@ var Queue = class extends DisTubeBase {
|
|
|
1499
1584
|
if (!this.paused) throw new DisTubeError("RESUMED");
|
|
1500
1585
|
this.paused = false;
|
|
1501
1586
|
this.voice.unpause();
|
|
1587
|
+
this.emit("resumeSong" /* RESUME_SONG */, this, this.songs[0]);
|
|
1588
|
+
this.queues?.schedulePreload?.(this);
|
|
1502
1589
|
return this;
|
|
1503
1590
|
} finally {
|
|
1504
1591
|
this._taskQueue.resolve();
|
|
@@ -1563,11 +1650,14 @@ var Queue = class extends DisTubeBase {
|
|
|
1563
1650
|
try {
|
|
1564
1651
|
const playing = this.songs.shift();
|
|
1565
1652
|
if (playing === void 0) return this;
|
|
1653
|
+
this._clearPreload();
|
|
1566
1654
|
for (let i = this.songs.length - 1; i > 0; i--) {
|
|
1567
1655
|
const j = Math.floor(Math.random() * (i + 1));
|
|
1568
1656
|
[this.songs[i], this.songs[j]] = [this.songs[j], this.songs[i]];
|
|
1569
1657
|
}
|
|
1570
1658
|
this.songs.unshift(playing);
|
|
1659
|
+
this.emit("shuffleQueue" /* SHUFFLE_QUEUE */, this, this.songs);
|
|
1660
|
+
this.queues?.schedulePreload?.(this);
|
|
1571
1661
|
return this;
|
|
1572
1662
|
} finally {
|
|
1573
1663
|
this._taskQueue.resolve();
|
|
@@ -1605,12 +1695,14 @@ var Queue = class extends DisTubeBase {
|
|
|
1605
1695
|
} else {
|
|
1606
1696
|
this.#addToPreviousSongs(skipped);
|
|
1607
1697
|
}
|
|
1698
|
+
this.emit("skipSong" /* SKIP_SONG */, this, skipped[0]);
|
|
1608
1699
|
} else if (!this.options.savePreviousSongs) {
|
|
1609
1700
|
throw new DisTubeError("DISABLED_OPTION", "savePreviousSongs");
|
|
1610
1701
|
} else {
|
|
1611
1702
|
const skipped = this.previousSongs.splice(position);
|
|
1612
1703
|
this.songs.unshift(...skipped);
|
|
1613
1704
|
}
|
|
1705
|
+
this._clearPreload();
|
|
1614
1706
|
this.#stop();
|
|
1615
1707
|
return this.songs[0];
|
|
1616
1708
|
} finally {
|
|
@@ -1649,8 +1741,10 @@ var Queue = class extends DisTubeBase {
|
|
|
1649
1741
|
try {
|
|
1650
1742
|
if (typeof time !== "number") throw new DisTubeError("INVALID_TYPE", "number", time, "time");
|
|
1651
1743
|
if (Number.isNaN(time) || time < 0) throw new DisTubeError("NUMBER_COMPARE", "time", "bigger or equal to", 0);
|
|
1744
|
+
this._clearPreload();
|
|
1652
1745
|
this._beginTime = time;
|
|
1653
1746
|
await this.play(false);
|
|
1747
|
+
this.emit("seekSong" /* SEEK_SONG */, this, this.songs[0], time);
|
|
1654
1748
|
return this;
|
|
1655
1749
|
} finally {
|
|
1656
1750
|
this._taskQueue.resolve();
|
|
@@ -1721,6 +1815,7 @@ var Queue = class extends DisTubeBase {
|
|
|
1721
1815
|
* @returns Nothing
|
|
1722
1816
|
*/
|
|
1723
1817
|
remove() {
|
|
1818
|
+
this._clearPreload();
|
|
1724
1819
|
this.paused = true;
|
|
1725
1820
|
this.stopped = true;
|
|
1726
1821
|
this.songs = [];
|
|
@@ -2394,6 +2489,11 @@ var Options = class {
|
|
|
2394
2489
|
* @default 10000
|
|
2395
2490
|
*/
|
|
2396
2491
|
stuckThreshold;
|
|
2492
|
+
/**
|
|
2493
|
+
* The preload options of the DisTube instance.
|
|
2494
|
+
* @default { enabled: false, threshold: 15 }
|
|
2495
|
+
*/
|
|
2496
|
+
preload;
|
|
2397
2497
|
/**
|
|
2398
2498
|
* The FFmpeg options of the DisTube instance.
|
|
2399
2499
|
*/
|
|
@@ -2419,10 +2519,34 @@ var Options = class {
|
|
|
2419
2519
|
this.joinNewVoiceChannel = opts.joinNewVoiceChannel;
|
|
2420
2520
|
this.unhandledError = opts.unhandledError;
|
|
2421
2521
|
this.stuckThreshold = opts.stuckThreshold;
|
|
2522
|
+
this.preload = this.#preloadOption(opts.preload);
|
|
2422
2523
|
this.ffmpeg = this.#ffmpegOption(options);
|
|
2423
2524
|
checkInvalidKey(opts, this, "DisTubeOptions");
|
|
2424
2525
|
this.#validateOptions();
|
|
2425
2526
|
}
|
|
2527
|
+
#preloadOption(value) {
|
|
2528
|
+
if (value === void 0 || value === false) {
|
|
2529
|
+
return { enabled: false, threshold: DEFAULT_PRELOAD_THRESHOLD_SECS };
|
|
2530
|
+
}
|
|
2531
|
+
if (value === true) {
|
|
2532
|
+
return { enabled: true, threshold: DEFAULT_PRELOAD_THRESHOLD_SECS };
|
|
2533
|
+
}
|
|
2534
|
+
if (typeof value !== "object" || Array.isArray(value)) {
|
|
2535
|
+
throw new DisTubeError("INVALID_TYPE", ["boolean", "object"], value, "DisTubeOptions.preload");
|
|
2536
|
+
}
|
|
2537
|
+
checkInvalidKey(value, ["enabled", "threshold"], "DisTubeOptions.preload");
|
|
2538
|
+
const { enabled, threshold } = value;
|
|
2539
|
+
if (enabled !== void 0 && typeof enabled !== "boolean") {
|
|
2540
|
+
throw new DisTubeError("INVALID_TYPE", "boolean", enabled, "DisTubeOptions.preload.enabled");
|
|
2541
|
+
}
|
|
2542
|
+
if (threshold !== void 0 && (typeof threshold !== "number" || Number.isNaN(threshold) || threshold < 0)) {
|
|
2543
|
+
throw new DisTubeError("INVALID_TYPE", "number", threshold, "DisTubeOptions.preload.threshold");
|
|
2544
|
+
}
|
|
2545
|
+
return {
|
|
2546
|
+
enabled: enabled ?? false,
|
|
2547
|
+
threshold: threshold ?? DEFAULT_PRELOAD_THRESHOLD_SECS
|
|
2548
|
+
};
|
|
2549
|
+
}
|
|
2426
2550
|
#validateOptions(options = this) {
|
|
2427
2551
|
const booleanOptions = /* @__PURE__ */ new Set([
|
|
2428
2552
|
"emitNewSongOnly",
|
|
@@ -2434,7 +2558,7 @@ var Options = class {
|
|
|
2434
2558
|
]);
|
|
2435
2559
|
const numberOptions = /* @__PURE__ */ new Set(["stuckThreshold"]);
|
|
2436
2560
|
const stringOptions = /* @__PURE__ */ new Set(["unhandledError"]);
|
|
2437
|
-
const objectOptions = /* @__PURE__ */ new Set(["customFilters", "ffmpeg"]);
|
|
2561
|
+
const objectOptions = /* @__PURE__ */ new Set(["customFilters", "ffmpeg", "preload"]);
|
|
2438
2562
|
const optionalOptions = /* @__PURE__ */ new Set(["customFilters"]);
|
|
2439
2563
|
for (const [key, value] of Object.entries(options)) {
|
|
2440
2564
|
if (value === void 0 && optionalOptions.has(key)) continue;
|
|
@@ -2822,7 +2946,10 @@ var QueueManager = class extends GuildIdManager {
|
|
|
2822
2946
|
if (error) this.emitError(error, queue, queue.songs?.[0]);
|
|
2823
2947
|
}, "disconnect"),
|
|
2824
2948
|
error: /* @__PURE__ */ __name((error) => void this.#handlePlayingError(queue, error), "error"),
|
|
2825
|
-
finish: /* @__PURE__ */ __name(() => void this.handleSongFinish(queue), "finish")
|
|
2949
|
+
finish: /* @__PURE__ */ __name(() => void this.handleSongFinish(queue), "finish"),
|
|
2950
|
+
channelChange: /* @__PURE__ */ __name((oldChannel, newChannel) => {
|
|
2951
|
+
this.emit("voiceChannelChange" /* VOICE_CHANNEL_CHANGE */, queue, oldChannel, newChannel);
|
|
2952
|
+
}, "channelChange")
|
|
2826
2953
|
};
|
|
2827
2954
|
for (const event of objectKeys(queue._listeners)) {
|
|
2828
2955
|
queue.voice.on(event, queue._listeners[event]);
|
|
@@ -2920,12 +3047,109 @@ var QueueManager = class extends GuildIdManager {
|
|
|
2920
3047
|
}
|
|
2921
3048
|
try {
|
|
2922
3049
|
const song = queue.songs[0];
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
3050
|
+
let dtStream;
|
|
3051
|
+
if (queue._preloadedSong === song && queue._preloadedStream) {
|
|
3052
|
+
this.debug(`[${queue.id}] Using preloaded stream for: ${song}`);
|
|
3053
|
+
dtStream = queue._preloadedStream;
|
|
3054
|
+
queue._preloadedSong = void 0;
|
|
3055
|
+
queue._preloadedStream = void 0;
|
|
3056
|
+
} else {
|
|
3057
|
+
queue._clearPreload();
|
|
3058
|
+
this.debug(`[${queue.id}] Getting stream from: ${song}`);
|
|
3059
|
+
await this.handler.attachStreamInfo(song);
|
|
3060
|
+
const willPlaySong = song.stream.playFromSource ? song : song.stream.song;
|
|
3061
|
+
const stream = willPlaySong?.stream;
|
|
3062
|
+
if (!willPlaySong || !stream?.playFromSource || !stream.url) throw new DisTubeError("NO_STREAM_URL", `${song}`);
|
|
3063
|
+
this.debug(`[${queue.id}] Creating DisTubeStream for: ${willPlaySong}`);
|
|
3064
|
+
const streamOptions = {
|
|
3065
|
+
ffmpeg: {
|
|
3066
|
+
path: this.options.ffmpeg.path,
|
|
3067
|
+
args: {
|
|
3068
|
+
global: { ...queue.ffmpegArgs.global },
|
|
3069
|
+
input: { ...queue.ffmpegArgs.input },
|
|
3070
|
+
output: { ...queue.ffmpegArgs.output, ...queue.filters.ffmpegArgs }
|
|
3071
|
+
}
|
|
3072
|
+
},
|
|
3073
|
+
seek: willPlaySong.duration ? queue._beginTime : void 0
|
|
3074
|
+
};
|
|
3075
|
+
queue._beginTime = 0;
|
|
3076
|
+
dtStream = new DisTubeStream(stream.url, streamOptions);
|
|
3077
|
+
dtStream.on("debug", (data) => this.emit("ffmpegDebug" /* FFMPEG_DEBUG */, `[${queue.id}] ${data}`));
|
|
3078
|
+
}
|
|
3079
|
+
queue._beginTime = 0;
|
|
3080
|
+
this.debug(`[${queue.id}] Started playing: ${song}`);
|
|
3081
|
+
await queue.voice.play(dtStream);
|
|
3082
|
+
if (emitPlaySong) this.emit("playSong" /* PLAY_SONG */, queue, song);
|
|
3083
|
+
this.schedulePreload(queue);
|
|
3084
|
+
} catch (e) {
|
|
3085
|
+
const error = e instanceof Error ? e : new Error(String(e));
|
|
3086
|
+
if (options?.keepSongOnError) {
|
|
3087
|
+
this.debug(`[${queue.id}] Error while restarting the stream: ${error.stack || error.message}`);
|
|
3088
|
+
this.emitError(error, queue, queue.songs[0]);
|
|
3089
|
+
return;
|
|
3090
|
+
}
|
|
3091
|
+
await this.#handlePlayingError(queue, error);
|
|
3092
|
+
}
|
|
3093
|
+
}
|
|
3094
|
+
/**
|
|
3095
|
+
* Schedule preloading the next song in the queue before the current song finishes.
|
|
3096
|
+
* @param queue - The guild queue
|
|
3097
|
+
*/
|
|
3098
|
+
schedulePreload(queue) {
|
|
3099
|
+
if (!this.options.preload.enabled || queue.stopped || queue.paused) return;
|
|
3100
|
+
if (queue._preloadTimeout) {
|
|
3101
|
+
clearTimeout(queue._preloadTimeout);
|
|
3102
|
+
queue._preloadTimeout = void 0;
|
|
3103
|
+
}
|
|
3104
|
+
const currentSong = queue.songs[0];
|
|
3105
|
+
const nextSong = queue.songs[1];
|
|
3106
|
+
if (!currentSong || !nextSong) return;
|
|
3107
|
+
if (queue._preloadedSong === nextSong && queue._preloadedStream) return;
|
|
3108
|
+
const threshold = this.options.preload.threshold;
|
|
3109
|
+
const duration = currentSong.duration ?? 0;
|
|
3110
|
+
const currentTime = queue.currentTime;
|
|
3111
|
+
const remaining = duration > 0 && Number.isFinite(currentTime) ? duration - currentTime : 0;
|
|
3112
|
+
if (duration === 0 || remaining <= threshold) {
|
|
3113
|
+
void this.preloadNextSong(queue);
|
|
3114
|
+
} else {
|
|
3115
|
+
const delayMs = Math.max(0, (remaining - threshold) * 1e3);
|
|
3116
|
+
queue._preloadTimeout = setTimeout(() => {
|
|
3117
|
+
queue._preloadTimeout = void 0;
|
|
3118
|
+
void this.preloadNextSong(queue);
|
|
3119
|
+
}, delayMs);
|
|
3120
|
+
queue._preloadTimeout.unref?.();
|
|
3121
|
+
}
|
|
3122
|
+
}
|
|
3123
|
+
/**
|
|
3124
|
+
* Preload the next song in the queue.
|
|
3125
|
+
* @param queue - The guild queue
|
|
3126
|
+
*/
|
|
3127
|
+
async preloadNextSong(queue) {
|
|
3128
|
+
if (queue._preloading) return queue._preloading;
|
|
3129
|
+
const task = this.#preloadTask(queue);
|
|
3130
|
+
queue._preloading = task;
|
|
3131
|
+
try {
|
|
3132
|
+
await task;
|
|
3133
|
+
} finally {
|
|
3134
|
+
if (queue._preloading === task) queue._preloading = void 0;
|
|
3135
|
+
}
|
|
3136
|
+
}
|
|
3137
|
+
async #preloadTask(queue) {
|
|
3138
|
+
if (!this.options.preload.enabled || queue.stopped || queue.paused) return;
|
|
3139
|
+
const nextSong = queue.songs[1];
|
|
3140
|
+
if (!nextSong) return;
|
|
3141
|
+
if (queue._preloadedSong === nextSong && queue._preloadedStream) return;
|
|
3142
|
+
const generation = queue._preloadGeneration;
|
|
3143
|
+
this.debug(`[${queue.id}] Preloading next song: ${nextSong}`);
|
|
3144
|
+
this.emit("preloadSong" /* PRELOAD_SONG */, queue, nextSong);
|
|
3145
|
+
try {
|
|
3146
|
+
await this.handler.attachStreamInfo(nextSong);
|
|
3147
|
+
if (queue.stopped || queue.songs[1] !== nextSong || generation !== queue._preloadGeneration) return;
|
|
3148
|
+
const willPlaySong = nextSong.stream.playFromSource ? nextSong : nextSong.stream.song;
|
|
2926
3149
|
const stream = willPlaySong?.stream;
|
|
2927
|
-
if (!willPlaySong || !stream?.playFromSource || !stream.url)
|
|
2928
|
-
|
|
3150
|
+
if (!willPlaySong || !stream?.playFromSource || !stream.url) {
|
|
3151
|
+
throw new DisTubeError("NO_STREAM_URL", `${nextSong}`);
|
|
3152
|
+
}
|
|
2929
3153
|
const streamOptions = {
|
|
2930
3154
|
ffmpeg: {
|
|
2931
3155
|
path: this.options.ffmpeg.path,
|
|
@@ -2935,22 +3159,27 @@ var QueueManager = class extends GuildIdManager {
|
|
|
2935
3159
|
output: { ...queue.ffmpegArgs.output, ...queue.filters.ffmpegArgs }
|
|
2936
3160
|
}
|
|
2937
3161
|
},
|
|
2938
|
-
seek:
|
|
3162
|
+
seek: void 0
|
|
2939
3163
|
};
|
|
2940
|
-
queue._beginTime = 0;
|
|
2941
3164
|
const dtStream = new DisTubeStream(stream.url, streamOptions);
|
|
2942
|
-
dtStream.on("debug", (data) => this.emit("ffmpegDebug" /* FFMPEG_DEBUG */, `[${queue.id}] ${data}`));
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
if (emitPlaySong) this.emit("playSong" /* PLAY_SONG */, queue, song);
|
|
2946
|
-
} catch (e) {
|
|
2947
|
-
const error = e instanceof Error ? e : new Error(String(e));
|
|
2948
|
-
if (options?.keepSongOnError) {
|
|
2949
|
-
this.debug(`[${queue.id}] Error while restarting the stream: ${error.stack || error.message}`);
|
|
2950
|
-
this.emitError(error, queue, queue.songs[0]);
|
|
3165
|
+
dtStream.on("debug", (data) => this.emit("ffmpegDebug" /* FFMPEG_DEBUG */, `[${queue.id}] [preload] ${data}`));
|
|
3166
|
+
if (queue.stopped || queue.songs[1] !== nextSong || generation !== queue._preloadGeneration) {
|
|
3167
|
+
dtStream.kill();
|
|
2951
3168
|
return;
|
|
2952
3169
|
}
|
|
2953
|
-
|
|
3170
|
+
queue._preloadedSong = nextSong;
|
|
3171
|
+
queue._preloadedStream = dtStream;
|
|
3172
|
+
this.debug(`[${queue.id}] Successfully preloaded next song: ${nextSong}`);
|
|
3173
|
+
this.emit("preloadedSong" /* PRELOADED_SONG */, queue, nextSong);
|
|
3174
|
+
} catch (e) {
|
|
3175
|
+
if (queue._preloadedSong === nextSong) {
|
|
3176
|
+
queue._preloadedSong = void 0;
|
|
3177
|
+
queue._preloadedStream?.kill();
|
|
3178
|
+
queue._preloadedStream = void 0;
|
|
3179
|
+
}
|
|
3180
|
+
const error = e instanceof Error ? e : new Error(String(e));
|
|
3181
|
+
this.debug(`[${queue.id}] Preload failed for ${nextSong}: ${error.message}`);
|
|
3182
|
+
this.emit("preloadError" /* PRELOAD_ERROR */, queue, nextSong, error);
|
|
2954
3183
|
}
|
|
2955
3184
|
}
|
|
2956
3185
|
};
|
|
@@ -3967,6 +4196,7 @@ var PlayableExtractorPlugin = class extends ExtractorPlugin {
|
|
|
3967
4196
|
AUDIO_CHANNELS,
|
|
3968
4197
|
AUDIO_SAMPLE_RATE,
|
|
3969
4198
|
BaseManager,
|
|
4199
|
+
DEFAULT_PRELOAD_THRESHOLD_SECS,
|
|
3970
4200
|
DEFAULT_VOLUME,
|
|
3971
4201
|
DisTube,
|
|
3972
4202
|
DisTubeBase,
|