ziplayer 0.0.4 → 0.0.6
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/README.md +181 -156
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -1
- package/dist/index.js.map +1 -1
- package/dist/structures/Player.d.ts +21 -0
- package/dist/structures/Player.d.ts.map +1 -1
- package/dist/structures/Player.js +234 -14
- package/dist/structures/Player.js.map +1 -1
- package/dist/structures/PlayerManager.d.ts +3 -0
- package/dist/structures/PlayerManager.d.ts.map +1 -1
- package/dist/structures/PlayerManager.js +72 -17
- package/dist/structures/PlayerManager.js.map +1 -1
- package/dist/structures/Queue.d.ts +4 -0
- package/dist/structures/Queue.d.ts.map +1 -1
- package/dist/structures/Queue.js +36 -0
- package/dist/structures/Queue.js.map +1 -1
- package/dist/types/index.d.ts +30 -0
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/index.ts +6 -1
- package/src/structures/Player.ts +808 -576
- package/src/structures/PlayerManager.ts +196 -143
- package/src/structures/Queue.ts +37 -0
- package/src/types/index.ts +27 -0
|
@@ -1,143 +1,196 @@
|
|
|
1
|
-
import { EventEmitter } from "events";
|
|
2
|
-
import { Player } from "./Player";
|
|
3
|
-
import { PlayerManagerOptions, PlayerOptions, Track, SourcePlugin } from "../types";
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
this.
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
}
|
|
1
|
+
import { EventEmitter } from "events";
|
|
2
|
+
import { Player } from "./Player";
|
|
3
|
+
import { PlayerManagerOptions, PlayerOptions, Track, SourcePlugin } from "../types";
|
|
4
|
+
|
|
5
|
+
const GLOBAL_MANAGER_KEY: symbol = Symbol.for("ziplayer.PlayerManager.instance");
|
|
6
|
+
export const getGlobalManager = (): PlayerManager | null => {
|
|
7
|
+
try {
|
|
8
|
+
const instance = (globalThis as any)[GLOBAL_MANAGER_KEY];
|
|
9
|
+
if (!instance) {
|
|
10
|
+
console.debug("[PlayerManager] No global instance found");
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
return instance as PlayerManager;
|
|
14
|
+
} catch (error) {
|
|
15
|
+
console.error("[PlayerManager] Error getting global instance:", error);
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
const setGlobalManager = (instance: PlayerManager): void => {
|
|
20
|
+
try {
|
|
21
|
+
(globalThis as any)[GLOBAL_MANAGER_KEY] = instance;
|
|
22
|
+
console.debug("[PlayerManager] Global instance set successfully");
|
|
23
|
+
} catch (error) {
|
|
24
|
+
console.error("[PlayerManager] Error setting global instance:", error);
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export class PlayerManager extends EventEmitter {
|
|
29
|
+
private static instance: PlayerManager | null = null;
|
|
30
|
+
private players: Map<string, Player> = new Map();
|
|
31
|
+
private plugins: SourcePlugin[];
|
|
32
|
+
private extensions: any[];
|
|
33
|
+
private B_debug: boolean = false;
|
|
34
|
+
|
|
35
|
+
private debug(message?: any, ...optionalParams: any[]): void {
|
|
36
|
+
if (this.listenerCount("debug") > 0) {
|
|
37
|
+
this.emit("debug", message, ...optionalParams);
|
|
38
|
+
if (!this.B_debug) {
|
|
39
|
+
this.B_debug = true;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
constructor(options: PlayerManagerOptions = {}) {
|
|
45
|
+
super();
|
|
46
|
+
this.plugins = [];
|
|
47
|
+
const provided = options.plugins || [];
|
|
48
|
+
for (const p of provided as any[]) {
|
|
49
|
+
try {
|
|
50
|
+
if (p && typeof p === "object") {
|
|
51
|
+
this.plugins.push(p as SourcePlugin);
|
|
52
|
+
} else if (typeof p === "function") {
|
|
53
|
+
const instance = new (p as any)();
|
|
54
|
+
this.plugins.push(instance as SourcePlugin);
|
|
55
|
+
}
|
|
56
|
+
} catch (e) {
|
|
57
|
+
this.debug(`[PlayerManager] Failed to init plugin:`, e);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
this.extensions = options.extensions || [];
|
|
61
|
+
|
|
62
|
+
setGlobalManager(this);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
private resolveGuildId(guildOrId: string | { id: string }): string {
|
|
66
|
+
if (typeof guildOrId === "string") return guildOrId;
|
|
67
|
+
if (guildOrId && typeof guildOrId === "object" && "id" in guildOrId) return guildOrId.id;
|
|
68
|
+
throw new Error("Invalid guild or guildId provided.");
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
create(guildOrId: string | { id: string }, options?: PlayerOptions): Player {
|
|
72
|
+
const guildId = this.resolveGuildId(guildOrId);
|
|
73
|
+
if (this.players.has(guildId)) {
|
|
74
|
+
return this.players.get(guildId)!;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
this.debug(`[PlayerManager] Creating player for guildId: ${guildId}`);
|
|
78
|
+
const player = new Player(guildId, options, this);
|
|
79
|
+
this.plugins.forEach((plugin) => player.addPlugin(plugin));
|
|
80
|
+
|
|
81
|
+
let extsToActivate: any[] = [];
|
|
82
|
+
const optExts = (options as any)?.extensions as any[] | string[] | undefined;
|
|
83
|
+
if (Array.isArray(optExts)) {
|
|
84
|
+
if (optExts.length === 0) {
|
|
85
|
+
extsToActivate = [];
|
|
86
|
+
} else if (typeof optExts[0] === "string") {
|
|
87
|
+
const wanted = new Set(optExts as string[]);
|
|
88
|
+
extsToActivate = this.extensions.filter((ext) => {
|
|
89
|
+
const name = typeof ext === "function" ? ext.name : ext?.name;
|
|
90
|
+
return !!name && wanted.has(name);
|
|
91
|
+
});
|
|
92
|
+
} else {
|
|
93
|
+
extsToActivate = optExts;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
for (const ext of extsToActivate) {
|
|
98
|
+
let instance = ext;
|
|
99
|
+
if (typeof ext === "function") {
|
|
100
|
+
try {
|
|
101
|
+
instance = new ext(player);
|
|
102
|
+
} catch (e) {
|
|
103
|
+
this.debug(`[PlayerManager] Extension constructor error:`, e);
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
if (instance && typeof instance === "object") {
|
|
108
|
+
if ("player" in instance && !instance.player) instance.player = player;
|
|
109
|
+
if (typeof instance.active === "function") {
|
|
110
|
+
try {
|
|
111
|
+
instance.active({ manager: this, player });
|
|
112
|
+
this.debug(`[PlayerManager] Extension ${instance?.name} active`);
|
|
113
|
+
} catch (e) {
|
|
114
|
+
this.debug(`[PlayerManager] Extension activation error:`, e);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Forward all player events
|
|
121
|
+
player.on("willPlay", (track, tracks) => this.emit("willPlay", player, track, tracks));
|
|
122
|
+
player.on("trackStart", (track) => this.emit("trackStart", player, track));
|
|
123
|
+
player.on("trackEnd", (track) => this.emit("trackEnd", player, track));
|
|
124
|
+
player.on("queueEnd", () => this.emit("queueEnd", player));
|
|
125
|
+
player.on("playerError", (error, track) => this.emit("playerError", player, error, track));
|
|
126
|
+
player.on("connectionError", (error) => this.emit("connectionError", player, error));
|
|
127
|
+
player.on("volumeChange", (old, volume) => this.emit("volumeChange", player, old, volume));
|
|
128
|
+
player.on("queueAdd", (track) => this.emit("queueAdd", player, track));
|
|
129
|
+
player.on("queueAddList", (tracks) => this.emit("queueAddList", player, tracks));
|
|
130
|
+
player.on("queueRemove", (track, index) => this.emit("queueRemove", player, track, index));
|
|
131
|
+
player.on("playerPause", (track) => this.emit("playerPause", player, track));
|
|
132
|
+
player.on("playerResume", (track) => this.emit("playerResume", player, track));
|
|
133
|
+
player.on("playerStop", () => this.emit("playerStop", player));
|
|
134
|
+
player.on("playerDestroy", () => {
|
|
135
|
+
this.emit("playerDestroy", player);
|
|
136
|
+
this.players.delete(guildId);
|
|
137
|
+
});
|
|
138
|
+
player.on("ttsStart", (payload) => this.emit("ttsStart", player, payload));
|
|
139
|
+
player.on("ttsEnd", () => this.emit("ttsEnd", player));
|
|
140
|
+
player.on("debug", (...args) => {
|
|
141
|
+
if (this.listenerCount("debug") > 0) {
|
|
142
|
+
this.emit("debug", ...args);
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
this.players.set(guildId, player);
|
|
147
|
+
return player;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
get(guildOrId: string | { id: string }): Player | undefined {
|
|
151
|
+
const guildId = this.resolveGuildId(guildOrId);
|
|
152
|
+
return this.players.get(guildId);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
delete(guildOrId: string | { id: string }): boolean {
|
|
156
|
+
const guildId = this.resolveGuildId(guildOrId);
|
|
157
|
+
const player = this.players.get(guildId);
|
|
158
|
+
if (player) {
|
|
159
|
+
this.debug(`[PlayerManager] Deleting player for guildId: ${guildId}`);
|
|
160
|
+
player.destroy();
|
|
161
|
+
return this.players.delete(guildId);
|
|
162
|
+
}
|
|
163
|
+
return false;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
has(guildOrId: string | { id: string }): boolean {
|
|
167
|
+
const guildId = this.resolveGuildId(guildOrId);
|
|
168
|
+
return this.players.has(guildId);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
get size(): number {
|
|
172
|
+
return this.players.size;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
get debugEnabled(): boolean {
|
|
176
|
+
return this.B_debug;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
destroy(): void {
|
|
180
|
+
this.debug(`[PlayerManager] Destroying all players`);
|
|
181
|
+
for (const player of this.players.values()) {
|
|
182
|
+
player.destroy();
|
|
183
|
+
}
|
|
184
|
+
this.players.clear();
|
|
185
|
+
this.removeAllListeners();
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export function getInstance(): PlayerManager | null {
|
|
190
|
+
const globalInst = getGlobalManager();
|
|
191
|
+
if (!globalInst) {
|
|
192
|
+
console.debug("[PlayerManager] Global instance not found, make sure to initialize with new PlayerManager(options)");
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
195
|
+
return globalInst;
|
|
196
|
+
}
|
package/src/structures/Queue.ts
CHANGED
|
@@ -16,6 +16,43 @@ export class Queue {
|
|
|
16
16
|
this.tracks.push(...tracks);
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
+
/** Insert a track at a specific upcoming position (0 = next) */
|
|
20
|
+
insert(track: Track, index: number): void {
|
|
21
|
+
if (!Number.isFinite(index)) {
|
|
22
|
+
this.tracks.push(track);
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
const i = Math.max(0, Math.min(Math.floor(index), this.tracks.length));
|
|
26
|
+
if (i === this.tracks.length) {
|
|
27
|
+
this.tracks.push(track);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
if (i <= 0) {
|
|
31
|
+
this.tracks.unshift(track);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
this.tracks.splice(i, 0, track);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Insert multiple tracks at a specific upcoming position, preserving order */
|
|
38
|
+
insertMultiple(tracks: Track[], index: number): void {
|
|
39
|
+
if (!Array.isArray(tracks) || tracks.length === 0) return;
|
|
40
|
+
if (!Number.isFinite(index)) {
|
|
41
|
+
this.tracks.push(...tracks);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const i = Math.max(0, Math.min(Math.floor(index), this.tracks.length));
|
|
45
|
+
if (i === 0) {
|
|
46
|
+
this.tracks = [...tracks, ...this.tracks];
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
if (i === this.tracks.length) {
|
|
50
|
+
this.tracks.push(...tracks);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
this.tracks.splice(i, 0, ...tracks);
|
|
54
|
+
}
|
|
55
|
+
|
|
19
56
|
remove(index: number): Track | null {
|
|
20
57
|
if (index < 0 || index >= this.tracks.length) return null;
|
|
21
58
|
return this.tracks.splice(index, 1)[0];
|
package/src/types/index.ts
CHANGED
|
@@ -42,6 +42,28 @@ export interface PlayerOptions {
|
|
|
42
42
|
*/
|
|
43
43
|
extractorTimeout?: number;
|
|
44
44
|
userdata?: Record<string, any>;
|
|
45
|
+
/**
|
|
46
|
+
* Text-to-Speech settings. When enabled, the player can create a
|
|
47
|
+
* dedicated AudioPlayer to play TTS while pausing the music player
|
|
48
|
+
* then resume the music after TTS finishes.
|
|
49
|
+
*/
|
|
50
|
+
tts?: {
|
|
51
|
+
/** Create a dedicated tts AudioPlayer at construction time */
|
|
52
|
+
createPlayer?: boolean;
|
|
53
|
+
/** Pause music and swap subscription to play TTS */
|
|
54
|
+
interrupt?: boolean;
|
|
55
|
+
/** Default TTS volume multiplier 1 => 100% */
|
|
56
|
+
volume?: number;
|
|
57
|
+
/** Max time tts playback Duration */
|
|
58
|
+
Max_Time_TTS?: number;
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* Optional per-player extension selection. When provided, only these
|
|
62
|
+
* extensions will be activated for the created player.
|
|
63
|
+
* - Provide instances or constructors to use them explicitly
|
|
64
|
+
* - Or provide names (string) to select from manager-registered extensions
|
|
65
|
+
*/
|
|
66
|
+
extensions?: any[] | string[];
|
|
45
67
|
}
|
|
46
68
|
|
|
47
69
|
export type SourcePluginCtor<T extends SourcePlugin = SourcePlugin> = new (...args: any[]) => T;
|
|
@@ -70,11 +92,16 @@ export interface PlayerEvents {
|
|
|
70
92
|
connectionError: [error: Error];
|
|
71
93
|
volumeChange: [oldVolume: number, newVolume: number];
|
|
72
94
|
queueAdd: [track: Track];
|
|
95
|
+
queueAddList: [tracks: Track[]];
|
|
73
96
|
queueRemove: [track: Track, index: number];
|
|
74
97
|
playerPause: [track: Track];
|
|
75
98
|
playerResume: [track: Track];
|
|
76
99
|
playerStop: [];
|
|
77
100
|
playerDestroy: [];
|
|
101
|
+
/** Emitted when TTS starts playing (interruption mode) */
|
|
102
|
+
ttsStart: [payload: { text?: string; track?: Track }];
|
|
103
|
+
/** Emitted when TTS finished (interruption mode) */
|
|
104
|
+
ttsEnd: [];
|
|
78
105
|
}
|
|
79
106
|
|
|
80
107
|
// Plugin interfaces
|