ziplayer 0.3.8 → 0.3.10
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/extensions/BaseExtension.js +1 -0
- package/dist/extensions/BaseExtension.js.map +1 -1
- package/dist/extensions/index.js +15 -4
- package/dist/extensions/index.js.map +1 -1
- package/dist/plugins/BasePlugin.js +1 -3
- package/dist/plugins/BasePlugin.js.map +1 -1
- package/dist/plugins/index.js +10 -6
- package/dist/plugins/index.js.map +1 -1
- package/dist/structures/FilterManager.d.ts +4 -3
- package/dist/structures/FilterManager.d.ts.map +1 -1
- package/dist/structures/FilterManager.js +54 -64
- package/dist/structures/FilterManager.js.map +1 -1
- package/dist/structures/Player.d.ts +2 -1
- package/dist/structures/Player.d.ts.map +1 -1
- package/dist/structures/Player.js +131 -92
- package/dist/structures/Player.js.map +1 -1
- package/dist/structures/PlayerManager.js +16 -12
- package/dist/structures/PlayerManager.js.map +1 -1
- package/dist/structures/PreloadManager.js +16 -10
- package/dist/structures/PreloadManager.js.map +1 -1
- package/dist/structures/Queue.js +10 -12
- package/dist/structures/Queue.js.map +1 -1
- package/dist/structures/StreamManager.js +11 -10
- package/dist/structures/StreamManager.js.map +1 -1
- package/dist/types/index.d.ts +3 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js.map +1 -1
- package/package.json +1 -1
- package/src/structures/FilterManager.ts +50 -77
- package/src/structures/Player.ts +57 -31
- package/src/types/index.ts +630 -628
- package/tsconfig.json +22 -22
package/src/structures/Player.ts
CHANGED
|
@@ -173,6 +173,7 @@ export class Player extends EventEmitter {
|
|
|
173
173
|
private ttsPlayer: DiscordAudioPlayer | null = null;
|
|
174
174
|
private lastDuration: number = 0;
|
|
175
175
|
private seekOffset: number = 0;
|
|
176
|
+
private recoveryInProgress = false;
|
|
176
177
|
|
|
177
178
|
constructor(guildId: string, options: PlayerOptions = {}, manager: PlayerManager) {
|
|
178
179
|
super();
|
|
@@ -269,7 +270,7 @@ export class Player extends EventEmitter {
|
|
|
269
270
|
extractorTimeout: this.options.extractorTimeout,
|
|
270
271
|
});
|
|
271
272
|
this.streamManager = new StreamManager({
|
|
272
|
-
maxConcurrentStreams: 4,
|
|
273
|
+
maxConcurrentStreams: this.options?.maxStreamStore ?? 4,
|
|
273
274
|
streamTimeout: 5 * 60 * 1000,
|
|
274
275
|
maxListenersPerStream: 15,
|
|
275
276
|
enableMetrics: true,
|
|
@@ -787,6 +788,7 @@ export class Player extends EventEmitter {
|
|
|
787
788
|
|
|
788
789
|
private async attemptTrackRecovery(track: Track, reason: unknown): Promise<boolean> {
|
|
789
790
|
if (!this.antiStuckEnabled) return false;
|
|
791
|
+
this.recoveryInProgress = true;
|
|
790
792
|
this.debug(`[AntiStuck] Recovery started for: ${track.title}`, reason);
|
|
791
793
|
|
|
792
794
|
const originalQuality = this.options.quality;
|
|
@@ -808,6 +810,7 @@ export class Player extends EventEmitter {
|
|
|
808
810
|
if (startedFromPreload) {
|
|
809
811
|
this.antiStuckConsecutiveFailures = 0;
|
|
810
812
|
this.options.quality = originalQuality;
|
|
813
|
+
this.recoveryInProgress = false;
|
|
811
814
|
return true;
|
|
812
815
|
}
|
|
813
816
|
}
|
|
@@ -816,6 +819,7 @@ export class Player extends EventEmitter {
|
|
|
816
819
|
if (started) {
|
|
817
820
|
this.antiStuckConsecutiveFailures = 0;
|
|
818
821
|
this.options.quality = originalQuality;
|
|
822
|
+
this.recoveryInProgress = false;
|
|
819
823
|
return true;
|
|
820
824
|
}
|
|
821
825
|
} catch (error) {
|
|
@@ -827,9 +831,10 @@ export class Player extends EventEmitter {
|
|
|
827
831
|
this.antiStuckConsecutiveFailures++;
|
|
828
832
|
if (this.antiStuckConsecutiveFailures >= this.antiStuckControlledSkipThreshold) {
|
|
829
833
|
this.debug(`[AntiStuck] Controlled skip threshold reached for ${track.title}`);
|
|
834
|
+
this.recoveryInProgress = false;
|
|
830
835
|
return false;
|
|
831
836
|
}
|
|
832
|
-
|
|
837
|
+
this.recoveryInProgress = false;
|
|
833
838
|
// Avoid hard skip storm by leaving track for next natural retry window.
|
|
834
839
|
this.debug(`[AntiStuck] Keeping track for controlled retry window: ${track.title}`);
|
|
835
840
|
return false;
|
|
@@ -900,16 +905,18 @@ export class Player extends EventEmitter {
|
|
|
900
905
|
const seekArg = position > 0 ? position : -1;
|
|
901
906
|
|
|
902
907
|
if (filterString || position > 0) {
|
|
903
|
-
const processedStream = await this.filter.applyFiltersAndSeek(streamInfo
|
|
908
|
+
const processedStream = await this.filter.applyFiltersAndSeek(streamInfo, seekArg);
|
|
904
909
|
|
|
905
|
-
|
|
906
|
-
const resource = createAudioResource(processedStream, {
|
|
910
|
+
const resource = createAudioResource(processedStream.stream, {
|
|
907
911
|
metadata: track,
|
|
908
|
-
inputType:
|
|
912
|
+
inputType:
|
|
913
|
+
processedStream.wasRecreated && !filterString ? StreamType.Arbitrary
|
|
914
|
+
: position > 0 ? StreamType.Raw
|
|
915
|
+
: StreamType.Arbitrary,
|
|
909
916
|
inlineVolume: true,
|
|
910
917
|
});
|
|
911
918
|
|
|
912
|
-
return { resource, processedStream };
|
|
919
|
+
return { resource, processedStream: processedStream.stream };
|
|
913
920
|
}
|
|
914
921
|
|
|
915
922
|
const resource = createAudioResource(streamInfo.stream, {
|
|
@@ -1077,6 +1084,7 @@ export class Player extends EventEmitter {
|
|
|
1077
1084
|
const currentResource = this.currentSlot.resource;
|
|
1078
1085
|
if (!currentResource) return false;
|
|
1079
1086
|
|
|
1087
|
+
// Ensure seekOffset is always an integer (milliseconds)
|
|
1080
1088
|
this.seekOffset = 0;
|
|
1081
1089
|
const targetVolume = this.getTrackTargetVolume(track);
|
|
1082
1090
|
|
|
@@ -1740,6 +1748,7 @@ export class Player extends EventEmitter {
|
|
|
1740
1748
|
this.debug("[Player] Cannot stop while subscribed to another player");
|
|
1741
1749
|
return false;
|
|
1742
1750
|
}
|
|
1751
|
+
this.recoveryInProgress = false;
|
|
1743
1752
|
this.debug(`[Player] stop called`);
|
|
1744
1753
|
if (this.playbackMode === PlaybackMode.REMOTE) {
|
|
1745
1754
|
this.cancelPreload();
|
|
@@ -1831,7 +1840,7 @@ export class Player extends EventEmitter {
|
|
|
1831
1840
|
return false;
|
|
1832
1841
|
}
|
|
1833
1842
|
this.debug(`[Player] skip called with index: ${index}`);
|
|
1834
|
-
|
|
1843
|
+
this.recoveryInProgress = false;
|
|
1835
1844
|
if (this.playbackMode === PlaybackMode.REMOTE) {
|
|
1836
1845
|
if (typeof index === "number" && index >= 0) {
|
|
1837
1846
|
for (let i = 0; i < index; i++) this.queue.remove(0);
|
|
@@ -1953,7 +1962,7 @@ export class Player extends EventEmitter {
|
|
|
1953
1962
|
}
|
|
1954
1963
|
|
|
1955
1964
|
// Apply filters if any are active
|
|
1956
|
-
let finalStream = streamInfo
|
|
1965
|
+
let finalStream = streamInfo;
|
|
1957
1966
|
|
|
1958
1967
|
if (saveOptions.filter || saveOptions.seek) {
|
|
1959
1968
|
try {
|
|
@@ -1964,14 +1973,14 @@ export class Player extends EventEmitter {
|
|
|
1964
1973
|
}
|
|
1965
1974
|
|
|
1966
1975
|
this.debug(`[Player] Applying filters to save stream: ${this.filter.getFilterString() || "none"}`);
|
|
1967
|
-
finalStream = await this.filter.applyFiltersAndSeek(streamInfo
|
|
1976
|
+
finalStream = await this.filter.applyFiltersAndSeek(streamInfo, saveOptions.seek || 0).catch((err) => {
|
|
1968
1977
|
this.debug(`[Player] Error applying filters to save stream:`, err);
|
|
1969
|
-
return streamInfo
|
|
1978
|
+
return streamInfo; // Fallback to original stream
|
|
1970
1979
|
});
|
|
1971
1980
|
}
|
|
1972
1981
|
|
|
1973
1982
|
// Return the stream directly - caller can pipe it to fs.createWriteStream()
|
|
1974
|
-
return finalStream;
|
|
1983
|
+
return finalStream.stream;
|
|
1975
1984
|
} catch (error) {
|
|
1976
1985
|
this.debug(`[Player] save error:`, error);
|
|
1977
1986
|
this.emit("playerError", error as Error, track);
|
|
@@ -2272,27 +2281,36 @@ export class Player extends EventEmitter {
|
|
|
2272
2281
|
* @returns Formatted time string with leading zeros
|
|
2273
2282
|
*/
|
|
2274
2283
|
formatTime(ms: number): string {
|
|
2275
|
-
|
|
2276
|
-
const
|
|
2277
|
-
const
|
|
2278
|
-
const
|
|
2284
|
+
// Ensure ms is an integer and convert to seconds
|
|
2285
|
+
const totalSeconds = Math.floor(ms / 1000) | 0;
|
|
2286
|
+
const hours = Math.floor(totalSeconds / 3600) | 0;
|
|
2287
|
+
const minutes = Math.floor((totalSeconds % 3600) / 60) | 0;
|
|
2288
|
+
const seconds = (totalSeconds % 60) | 0;
|
|
2289
|
+
|
|
2279
2290
|
const parts: string[] = [];
|
|
2280
|
-
|
|
2281
|
-
|
|
2291
|
+
|
|
2292
|
+
if (hours > 0) {
|
|
2293
|
+
parts.push(String(hours)); // Giờ không padStart (ví dụ: 1:05:00)
|
|
2294
|
+
parts.push(String(minutes).padStart(2, "0"));
|
|
2295
|
+
} else {
|
|
2296
|
+
parts.push(String(minutes)); // Phút không padStart nếu là số đầu tiên (ví dụ: 0:30 thay vì 00:30)
|
|
2297
|
+
}
|
|
2298
|
+
|
|
2282
2299
|
parts.push(String(seconds).padStart(2, "0"));
|
|
2283
2300
|
return parts.join(":");
|
|
2284
2301
|
}
|
|
2285
2302
|
|
|
2286
2303
|
/**
|
|
2287
2304
|
* Format time without leading zeros for hours (1:22:12 or 3:45)
|
|
2288
|
-
* @param ms - Time in milliseconds
|
|
2305
|
+
* @param ms - Time in milliseconds (must be integer)
|
|
2289
2306
|
* @returns Compact formatted time string
|
|
2290
2307
|
*/
|
|
2291
2308
|
formatTimeCompact(ms: number): string {
|
|
2292
|
-
|
|
2293
|
-
const
|
|
2294
|
-
const
|
|
2295
|
-
const
|
|
2309
|
+
// Ensure ms is an integer and convert to seconds
|
|
2310
|
+
const totalSeconds = Math.floor(ms / 1000) | 0;
|
|
2311
|
+
const hours = Math.floor(totalSeconds / 3600) | 0;
|
|
2312
|
+
const minutes = Math.floor((totalSeconds % 3600) / 60) | 0;
|
|
2313
|
+
const seconds = (totalSeconds % 60) | 0;
|
|
2296
2314
|
|
|
2297
2315
|
if (hours > 0) {
|
|
2298
2316
|
return `${hours}:${String(minutes).padStart(2, "0")}:${String(seconds).padStart(2, "0")}`;
|
|
@@ -2335,8 +2353,9 @@ export class Player extends EventEmitter {
|
|
|
2335
2353
|
};
|
|
2336
2354
|
}
|
|
2337
2355
|
|
|
2338
|
-
|
|
2339
|
-
const
|
|
2356
|
+
// Ensure all time values are integers (milliseconds)
|
|
2357
|
+
const total = Math.floor(track.duration > 1000 ? track.duration : track.duration * 1000) | 0;
|
|
2358
|
+
const current = Math.floor(resource.playbackDuration + this.seekOffset) | 0;
|
|
2340
2359
|
|
|
2341
2360
|
return {
|
|
2342
2361
|
current: current,
|
|
@@ -2465,20 +2484,22 @@ export class Player extends EventEmitter {
|
|
|
2465
2484
|
const track = this.queue.currentTrack;
|
|
2466
2485
|
this.debug(`[Player] Refreshing player resource for track: ${track.title}`);
|
|
2467
2486
|
|
|
2468
|
-
|
|
2469
|
-
this.seekOffset
|
|
2487
|
+
// Ensure all time values are integers (milliseconds)
|
|
2488
|
+
const currentPosition = (position >= 0 ? position : (this.currentResource?.playbackDuration ?? 0) + this.seekOffset) | 0;
|
|
2489
|
+
|
|
2490
|
+
this.seekOffset = currentPosition | 0;
|
|
2470
2491
|
const wasPaused = this.isPaused;
|
|
2471
|
-
const playbackDuration = this.currentResource?.playbackDuration ?? 0;
|
|
2492
|
+
const playbackDuration = (this.currentResource?.playbackDuration ?? 0) | 0;
|
|
2472
2493
|
|
|
2473
2494
|
const isForwardSeek = position < 0 || position >= playbackDuration;
|
|
2474
2495
|
const currentStreamId = this.currentSlot.streamId;
|
|
2475
2496
|
|
|
2476
|
-
// Try to grab the raw source stream for reuse (
|
|
2497
|
+
// Try to grab the raw source stream for reuse (only when not seeking, i.e. just applying filters)
|
|
2477
2498
|
let reuseStream: import("stream").Readable | null = null;
|
|
2478
|
-
if (isForwardSeek && currentStreamId) {
|
|
2499
|
+
if (position < 0 && isForwardSeek && currentStreamId) {
|
|
2479
2500
|
reuseStream = this.streamManager.getRawStream(currentStreamId);
|
|
2480
2501
|
if (reuseStream) {
|
|
2481
|
-
this.debug(`[Player] Will reuse source stream for
|
|
2502
|
+
this.debug(`[Player] Will reuse source stream for filter application`);
|
|
2482
2503
|
}
|
|
2483
2504
|
}
|
|
2484
2505
|
|
|
@@ -2636,6 +2657,10 @@ export class Player extends EventEmitter {
|
|
|
2636
2657
|
this.debug(`[Player] AudioPlayer went idle during resource refresh — skipping trackEnd/playNext`);
|
|
2637
2658
|
return;
|
|
2638
2659
|
}
|
|
2660
|
+
if (this.recoveryInProgress) {
|
|
2661
|
+
this.debug(`[Player] AudioPlayer went idle during recovery — skipping playNext`);
|
|
2662
|
+
return;
|
|
2663
|
+
}
|
|
2639
2664
|
// Track ended
|
|
2640
2665
|
const track = this.queue.currentTrack;
|
|
2641
2666
|
if (track) {
|
|
@@ -2731,6 +2756,7 @@ export class Player extends EventEmitter {
|
|
|
2731
2756
|
});
|
|
2732
2757
|
this.audioPlayer.on("error", (error) => {
|
|
2733
2758
|
if (this.destroyed) return;
|
|
2759
|
+
if (this.recoveryInProgress) return;
|
|
2734
2760
|
this.debug(`[Player] AudioPlayer error:`, error);
|
|
2735
2761
|
this.emit("playerError", error, this.queue.currentTrack || undefined);
|
|
2736
2762
|
const track = this.queue.currentTrack;
|