ziplayer 0.3.11 → 0.3.12
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/index.d.ts +1 -0
- package/dist/extensions/index.d.ts.map +1 -1
- package/dist/extensions/index.js +9 -1
- package/dist/extensions/index.js.map +1 -1
- package/dist/structures/FilterManager.d.ts.map +1 -1
- package/dist/structures/FilterManager.js +1 -2
- package/dist/structures/FilterManager.js.map +1 -1
- package/dist/structures/Player.d.ts.map +1 -1
- package/dist/structures/Player.js +24 -14
- package/dist/structures/Player.js.map +1 -1
- package/dist/structures/PlayerManager.d.ts +11 -7
- package/dist/structures/PlayerManager.d.ts.map +1 -1
- package/dist/structures/PlayerManager.js +39 -20
- package/dist/structures/PlayerManager.js.map +1 -1
- package/dist/structures/PreloadManager.d.ts.map +1 -1
- package/dist/structures/PreloadManager.js +10 -7
- package/dist/structures/PreloadManager.js.map +1 -1
- package/dist/types/index.d.ts +1 -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/extensions/index.ts +9 -1
- package/src/plugins/index.ts +975 -975
- package/src/structures/FilterManager.ts +1 -2
- package/src/structures/Player.ts +31 -20
- package/src/structures/PlayerManager.ts +46 -21
- package/src/structures/PreloadManager.ts +11 -7
- package/src/types/index.ts +2 -0
|
@@ -244,8 +244,7 @@ export class FilterManager {
|
|
|
244
244
|
wasRecreated = true;
|
|
245
245
|
|
|
246
246
|
position = -1;
|
|
247
|
-
streamInfo
|
|
248
|
-
if (!filterString) return { ...streamInfo, stream: sourceStream, wasRecreated };
|
|
247
|
+
if (!filterString) return { ...streamInfo, type: "arbitrary", stream: sourceStream, wasRecreated };
|
|
249
248
|
}
|
|
250
249
|
|
|
251
250
|
this.debug(`Applying filters and seek — filters: ${filterString || "none"}, seek: ${position}ms`);
|
package/src/structures/Player.ts
CHANGED
|
@@ -125,6 +125,7 @@ export class Player extends EventEmitter {
|
|
|
125
125
|
resource: null,
|
|
126
126
|
track: null,
|
|
127
127
|
streamId: null,
|
|
128
|
+
processedStreamId: null,
|
|
128
129
|
abortController: null,
|
|
129
130
|
isValid: false,
|
|
130
131
|
isLoading: false,
|
|
@@ -331,7 +332,11 @@ export class Player extends EventEmitter {
|
|
|
331
332
|
const stream = (this.currentResource as any)?.metadata?.stream ?? (this.currentResource as any)?.stream;
|
|
332
333
|
|
|
333
334
|
if (stream && typeof stream.destroy === "function") {
|
|
334
|
-
|
|
335
|
+
try {
|
|
336
|
+
stream.destroy();
|
|
337
|
+
} catch (e: any) {
|
|
338
|
+
this.debug("Stream destroy error:", e);
|
|
339
|
+
}
|
|
335
340
|
}
|
|
336
341
|
|
|
337
342
|
this.currentResource = null;
|
|
@@ -1128,17 +1133,20 @@ export class Player extends EventEmitter {
|
|
|
1128
1133
|
return await this.playRemote(track, streamInfo);
|
|
1129
1134
|
}
|
|
1130
1135
|
|
|
1131
|
-
if (!streamInfo?.stream) {
|
|
1136
|
+
if (!streamInfo?.stream && !streamInfo?.url) {
|
|
1132
1137
|
throw new Error(`No stream available`);
|
|
1133
1138
|
}
|
|
1134
1139
|
|
|
1135
1140
|
// Register the RAW source stream — this is what we can reuse on seek
|
|
1136
|
-
const rawStreamId =
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1141
|
+
const rawStreamId =
|
|
1142
|
+
streamInfo.stream ?
|
|
1143
|
+
this.streamManager.registerStream(streamInfo.stream, track, {
|
|
1144
|
+
source: track.source || "stream",
|
|
1145
|
+
isPreload: false,
|
|
1146
|
+
isRemote: !!streamInfo?.remote,
|
|
1147
|
+
priority: 10,
|
|
1148
|
+
})
|
|
1149
|
+
: null;
|
|
1142
1150
|
|
|
1143
1151
|
// createResource now returns both the AudioResource
|
|
1144
1152
|
// AND the processedStream (ffmpeg stdout) when filters/seek are involved.
|
|
@@ -1159,14 +1167,14 @@ export class Player extends EventEmitter {
|
|
|
1159
1167
|
if (this.currentSlot.streamId && this.currentSlot.streamId !== rawStreamId) {
|
|
1160
1168
|
this.streamManager.unregisterStream(this.currentSlot.streamId, true);
|
|
1161
1169
|
}
|
|
1162
|
-
if (
|
|
1163
|
-
this.streamManager.unregisterStream(
|
|
1170
|
+
if (this.currentSlot.processedStreamId && this.currentSlot.processedStreamId !== playStreamId) {
|
|
1171
|
+
this.streamManager.unregisterStream(this.currentSlot.processedStreamId, true);
|
|
1164
1172
|
}
|
|
1165
1173
|
|
|
1166
1174
|
this.currentSlot.resource = resource;
|
|
1167
1175
|
this.currentSlot.track = track;
|
|
1168
1176
|
this.currentSlot.streamId = rawStreamId;
|
|
1169
|
-
|
|
1177
|
+
this.currentSlot.processedStreamId = processedStream ? playStreamId : null;
|
|
1170
1178
|
this.currentSlot.isValid = true;
|
|
1171
1179
|
this.currentResource = resource;
|
|
1172
1180
|
this.seekOffset = 0;
|
|
@@ -2508,10 +2516,10 @@ export class Player extends EventEmitter {
|
|
|
2508
2516
|
}
|
|
2509
2517
|
|
|
2510
2518
|
// Clean up processedStream first (it's what AudioResource reads)
|
|
2511
|
-
const processedStreamId =
|
|
2519
|
+
const processedStreamId = this.currentSlot.processedStreamId;
|
|
2512
2520
|
if (processedStreamId && processedStreamId !== currentStreamId) {
|
|
2513
2521
|
this.streamManager.unregisterStream(processedStreamId, true);
|
|
2514
|
-
|
|
2522
|
+
this.currentSlot.processedStreamId = null;
|
|
2515
2523
|
}
|
|
2516
2524
|
|
|
2517
2525
|
if (currentStreamId) {
|
|
@@ -2544,7 +2552,7 @@ export class Player extends EventEmitter {
|
|
|
2544
2552
|
streaminfo = await this.getStream(track);
|
|
2545
2553
|
}
|
|
2546
2554
|
|
|
2547
|
-
if (!streaminfo?.stream) {
|
|
2555
|
+
if (!streaminfo?.stream && !streaminfo?.url) {
|
|
2548
2556
|
this.debug(`[Player] No stream available for refresh`);
|
|
2549
2557
|
return false;
|
|
2550
2558
|
}
|
|
@@ -2554,11 +2562,14 @@ export class Player extends EventEmitter {
|
|
|
2554
2562
|
const { resource, processedStream } = await this.createResource(streaminfo, track, createPosition);
|
|
2555
2563
|
|
|
2556
2564
|
// Register raw source stream
|
|
2557
|
-
const newStreamId =
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2565
|
+
const newStreamId =
|
|
2566
|
+
streaminfo.stream ?
|
|
2567
|
+
this.streamManager.registerStream(streaminfo.stream, track, {
|
|
2568
|
+
source: track.source || "stream",
|
|
2569
|
+
isPreload: false,
|
|
2570
|
+
priority: 10,
|
|
2571
|
+
})
|
|
2572
|
+
: null;
|
|
2562
2573
|
|
|
2563
2574
|
let newProcessedStreamId: string | null = null;
|
|
2564
2575
|
if (processedStream && processedStream !== streaminfo.stream) {
|
|
@@ -2572,7 +2583,7 @@ export class Player extends EventEmitter {
|
|
|
2572
2583
|
this.currentSlot.resource = resource;
|
|
2573
2584
|
this.currentSlot.track = track;
|
|
2574
2585
|
this.currentSlot.streamId = newStreamId;
|
|
2575
|
-
|
|
2586
|
+
this.currentSlot.processedStreamId = newProcessedStreamId;
|
|
2576
2587
|
this.currentSlot.isValid = true;
|
|
2577
2588
|
this.currentResource = resource;
|
|
2578
2589
|
|
|
@@ -15,9 +15,10 @@ import {
|
|
|
15
15
|
} from "../types";
|
|
16
16
|
import type { BaseExtension } from "../extensions";
|
|
17
17
|
import { withTimeout } from "../utils/timeout";
|
|
18
|
-
import { PluginManager } from "../plugins";
|
|
19
18
|
|
|
20
19
|
const GLOBAL_MANAGER_KEY: symbol = Symbol.for("ziplayer.PlayerManager.instance");
|
|
20
|
+
/** Guild id for the internal search-only player (never stored in {@link PlayerManager.players}). */
|
|
21
|
+
const SEARCH_PLAYER_GUILD_ID = "__ziplayer_search__";
|
|
21
22
|
|
|
22
23
|
export const getGlobalManager = (): PlayerManager | null => {
|
|
23
24
|
try {
|
|
@@ -105,7 +106,8 @@ export class PlayerManager extends EventEmitter {
|
|
|
105
106
|
}
|
|
106
107
|
|
|
107
108
|
private plugins: SourcePlugin[];
|
|
108
|
-
|
|
109
|
+
/** Reused player for {@link search}; not registered in {@link players}. */
|
|
110
|
+
private searchPlayer: Player | null = null;
|
|
109
111
|
private extensions: any[];
|
|
110
112
|
private B_debug: boolean = false;
|
|
111
113
|
private extractorTimeout: number = 10000;
|
|
@@ -126,9 +128,6 @@ export class PlayerManager extends EventEmitter {
|
|
|
126
128
|
constructor(options: PlayerManagerOptions = {}) {
|
|
127
129
|
super();
|
|
128
130
|
this.plugins = [];
|
|
129
|
-
this.pluginManager = new PluginManager(null as any, this, {
|
|
130
|
-
extractorTimeout: this.extractorTimeout,
|
|
131
|
-
});
|
|
132
131
|
this.searchCache = new Map();
|
|
133
132
|
|
|
134
133
|
// Initialize plugins
|
|
@@ -145,7 +144,6 @@ export class PlayerManager extends EventEmitter {
|
|
|
145
144
|
|
|
146
145
|
if (instance) {
|
|
147
146
|
this.plugins.push(instance);
|
|
148
|
-
this.pluginManager.register(instance);
|
|
149
147
|
}
|
|
150
148
|
this.debug(`Registered plugin: ${p.name || "unnamed"}`);
|
|
151
149
|
} catch (e) {
|
|
@@ -249,6 +247,25 @@ export class PlayerManager extends EventEmitter {
|
|
|
249
247
|
this.debug(`Auto-cleanup started with interval: ${this.cleanupTimeout}ms`);
|
|
250
248
|
}
|
|
251
249
|
|
|
250
|
+
/**
|
|
251
|
+
* Lazy internal player used only for {@link search}.
|
|
252
|
+
* Not added to {@link players} and does not forward manager events.
|
|
253
|
+
*/
|
|
254
|
+
private getSearchPlayer(): Player {
|
|
255
|
+
if (this.searchPlayer && !this.searchPlayer.destroyed) {
|
|
256
|
+
return this.searchPlayer;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const player = new Player(SEARCH_PLAYER_GUILD_ID, { extractorTimeout: this.extractorTimeout }, this);
|
|
260
|
+
for (const plugin of this.plugins) {
|
|
261
|
+
player.addPlugin(plugin);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
this.searchPlayer = player;
|
|
265
|
+
this.debug(`Created internal search player (not stored in players map)`);
|
|
266
|
+
return player;
|
|
267
|
+
}
|
|
268
|
+
|
|
252
269
|
private startStatsCollection(): void {
|
|
253
270
|
if (this.statsInterval) {
|
|
254
271
|
clearInterval(this.statsInterval);
|
|
@@ -291,6 +308,10 @@ export class PlayerManager extends EventEmitter {
|
|
|
291
308
|
async create(guildOrId: string | { id: string }, options?: PlayerOptions): Promise<Player> {
|
|
292
309
|
const guildId = this.resolveGuildId(guildOrId);
|
|
293
310
|
|
|
311
|
+
if (guildId === SEARCH_PLAYER_GUILD_ID) {
|
|
312
|
+
throw new Error(`Guild id "${SEARCH_PLAYER_GUILD_ID}" is reserved for internal search.`);
|
|
313
|
+
}
|
|
314
|
+
|
|
294
315
|
if (this.players.has(guildId)) {
|
|
295
316
|
this.debug(`Player already exists for guildId: ${guildId}, returning existing`);
|
|
296
317
|
return this.players.get(guildId)!;
|
|
@@ -770,6 +791,11 @@ export class PlayerManager extends EventEmitter {
|
|
|
770
791
|
player.destroy();
|
|
771
792
|
}
|
|
772
793
|
|
|
794
|
+
if (this.searchPlayer && !this.searchPlayer.destroyed) {
|
|
795
|
+
this.searchPlayer.destroy();
|
|
796
|
+
}
|
|
797
|
+
this.searchPlayer = null;
|
|
798
|
+
|
|
773
799
|
this.players.clear();
|
|
774
800
|
this.searchCache.clear();
|
|
775
801
|
this.removeAllListeners();
|
|
@@ -777,13 +803,11 @@ export class PlayerManager extends EventEmitter {
|
|
|
777
803
|
}
|
|
778
804
|
|
|
779
805
|
/**
|
|
780
|
-
* Search
|
|
806
|
+
* Search via an internal Player instance (all registered plugins) without
|
|
807
|
+
* storing it in {@link players}.
|
|
781
808
|
*
|
|
782
|
-
* Uses the same search pipeline as Player.search
|
|
783
|
-
*
|
|
784
|
-
* - plugin deduplication
|
|
785
|
-
* - plugin scoring/evaluation
|
|
786
|
-
* - fallback handling
|
|
809
|
+
* Uses the same search pipeline as {@link Player.search}:
|
|
810
|
+
* extension hooks, plugin deduplication, scoring, and fallback handling.
|
|
787
811
|
*
|
|
788
812
|
* @param {string} query
|
|
789
813
|
* @param {string} requestedBy
|
|
@@ -792,20 +816,15 @@ export class PlayerManager extends EventEmitter {
|
|
|
792
816
|
async search(query: string, requestedBy: string): Promise<SearchResult> {
|
|
793
817
|
this.debug(`Search called with query: ${query}, requestedBy: ${requestedBy}`);
|
|
794
818
|
|
|
795
|
-
// Cache
|
|
796
819
|
const cached = this.getCachedSearch(query);
|
|
797
820
|
if (cached) {
|
|
798
821
|
return cached;
|
|
799
822
|
}
|
|
800
823
|
|
|
801
824
|
try {
|
|
802
|
-
const result = await this.
|
|
825
|
+
const result = await this.getSearchPlayer().search(query, requestedBy);
|
|
803
826
|
|
|
804
|
-
|
|
805
|
-
throw new Error(`No results found for: ${query}`);
|
|
806
|
-
}
|
|
807
|
-
|
|
808
|
-
this.debug(`Plugin search returned ${result.tracks.length} tracks (score: ${result.score?.score ?? "unknown"}%)`);
|
|
827
|
+
this.debug(`Search returned ${result.tracks.length} tracks (score: ${result.score?.score ?? "unknown"}%)`);
|
|
809
828
|
|
|
810
829
|
if (result.score) {
|
|
811
830
|
this.debug(`Search evaluation - ${result.score.reason}`);
|
|
@@ -836,10 +855,13 @@ export class PlayerManager extends EventEmitter {
|
|
|
836
855
|
*/
|
|
837
856
|
registerPlugin(plugin: SourcePlugin): void {
|
|
838
857
|
this.plugins.push(plugin);
|
|
839
|
-
this.pluginManager.register(plugin);
|
|
840
858
|
|
|
841
859
|
this.debug(`Registered plugin: ${plugin.name}`);
|
|
842
860
|
|
|
861
|
+
if (this.searchPlayer && !this.searchPlayer.destroyed) {
|
|
862
|
+
this.searchPlayer.addPlugin(plugin);
|
|
863
|
+
}
|
|
864
|
+
|
|
843
865
|
for (const player of this.players.values()) {
|
|
844
866
|
player.addPlugin(plugin);
|
|
845
867
|
}
|
|
@@ -856,7 +878,10 @@ export class PlayerManager extends EventEmitter {
|
|
|
856
878
|
if (index === -1) return false;
|
|
857
879
|
|
|
858
880
|
this.plugins.splice(index, 1);
|
|
859
|
-
|
|
881
|
+
|
|
882
|
+
if (this.searchPlayer && !this.searchPlayer.destroyed) {
|
|
883
|
+
this.searchPlayer.removePlugin(name);
|
|
884
|
+
}
|
|
860
885
|
|
|
861
886
|
this.debug(`Unregistered plugin: ${name}`);
|
|
862
887
|
|
|
@@ -24,6 +24,7 @@ export class PreloadManager {
|
|
|
24
24
|
resource: null,
|
|
25
25
|
track: null,
|
|
26
26
|
streamId: null,
|
|
27
|
+
processedStreamId: null,
|
|
27
28
|
abortController: null,
|
|
28
29
|
isValid: false,
|
|
29
30
|
isLoading: false,
|
|
@@ -239,17 +240,20 @@ export class PreloadManager {
|
|
|
239
240
|
this.debugLog(`[Preload] Track changed after stream fetch`);
|
|
240
241
|
throw new Error("PRELOAD_CANCELLED");
|
|
241
242
|
}
|
|
242
|
-
if (!streamInfo?.stream) {
|
|
243
|
+
if (!streamInfo?.stream && !streamInfo?.url) {
|
|
243
244
|
throw new Error(`No stream available`);
|
|
244
245
|
}
|
|
245
246
|
|
|
246
|
-
const streamId =
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
247
|
+
const streamId =
|
|
248
|
+
streamInfo.stream ?
|
|
249
|
+
this.streamManager.registerStream(streamInfo.stream, track, {
|
|
250
|
+
source: track.source || "preload",
|
|
251
|
+
isPreload: true,
|
|
252
|
+
priority: 5,
|
|
253
|
+
})
|
|
254
|
+
: null;
|
|
251
255
|
|
|
252
|
-
const resource = createAudioResource(streamInfo.stream
|
|
256
|
+
const resource = createAudioResource(streamInfo.stream || streamInfo.url!, {
|
|
253
257
|
inlineVolume: true,
|
|
254
258
|
metadata: { ...track, preloaded: true },
|
|
255
259
|
});
|
package/src/types/index.ts
CHANGED
|
@@ -420,6 +420,7 @@ export interface SaveOptions {
|
|
|
420
420
|
/** Seek position in milliseconds to start saving from */
|
|
421
421
|
seek?: number;
|
|
422
422
|
}
|
|
423
|
+
|
|
423
424
|
export interface PlayerSession {
|
|
424
425
|
guildId: string;
|
|
425
426
|
queue: Track[];
|
|
@@ -471,6 +472,7 @@ export interface StreamSlot {
|
|
|
471
472
|
resource: AudioResource | null;
|
|
472
473
|
track: Track | null;
|
|
473
474
|
streamId: string | null;
|
|
475
|
+
processedStreamId: string | null;
|
|
474
476
|
abortController: AbortController | null;
|
|
475
477
|
isValid: boolean;
|
|
476
478
|
isLoading: boolean;
|