ziplayer 0.2.7 → 0.3.1
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/AI-Guide.md +624 -956
- package/README.md +277 -10
- package/dist/extensions/BaseExtension.d.ts +1 -0
- package/dist/extensions/BaseExtension.d.ts.map +1 -1
- package/dist/extensions/BaseExtension.js.map +1 -1
- package/dist/extensions/index.d.ts +38 -3
- package/dist/extensions/index.d.ts.map +1 -1
- package/dist/extensions/index.js +259 -41
- package/dist/extensions/index.js.map +1 -1
- package/dist/persistence/PersistenceManager.d.ts +95 -0
- package/dist/persistence/PersistenceManager.d.ts.map +1 -0
- package/dist/persistence/PersistenceManager.js +975 -0
- package/dist/persistence/PersistenceManager.js.map +1 -0
- package/dist/plugins/BasePlugin.js +1 -1
- package/dist/plugins/BasePlugin.js.map +1 -1
- package/dist/plugins/index.d.ts +74 -8
- package/dist/plugins/index.d.ts.map +1 -1
- package/dist/plugins/index.js +657 -116
- package/dist/plugins/index.js.map +1 -1
- package/dist/structures/FilterManager.js +3 -3
- package/dist/structures/FilterManager.js.map +1 -1
- package/dist/structures/PersistenceManager.d.ts +96 -0
- package/dist/structures/PersistenceManager.d.ts.map +1 -0
- package/dist/structures/PersistenceManager.js +1008 -0
- package/dist/structures/PersistenceManager.js.map +1 -0
- package/dist/structures/Player.d.ts +158 -14
- package/dist/structures/Player.d.ts.map +1 -1
- package/dist/structures/Player.js +1175 -188
- package/dist/structures/Player.js.map +1 -1
- package/dist/structures/PlayerManager.d.ts +106 -91
- package/dist/structures/PlayerManager.d.ts.map +1 -1
- package/dist/structures/PlayerManager.js +365 -124
- package/dist/structures/PlayerManager.js.map +1 -1
- package/dist/structures/Queue.d.ts +136 -31
- package/dist/structures/Queue.d.ts.map +1 -1
- package/dist/structures/Queue.js +265 -46
- package/dist/structures/Queue.js.map +1 -1
- package/dist/structures/StreamManager.d.ts +137 -0
- package/dist/structures/StreamManager.d.ts.map +1 -0
- package/dist/structures/StreamManager.js +420 -0
- package/dist/structures/StreamManager.js.map +1 -0
- package/dist/types/index.d.ts +181 -8
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js.map +1 -1
- package/dist/types/persistence.d.ts +77 -0
- package/dist/types/persistence.d.ts.map +1 -0
- package/dist/types/persistence.js +3 -0
- package/dist/types/persistence.js.map +1 -0
- package/package.json +3 -2
- package/src/extensions/BaseExtension.ts +1 -0
- package/src/extensions/index.ts +320 -37
- package/src/plugins/BasePlugin.ts +1 -1
- package/src/plugins/index.ts +809 -139
- package/src/structures/FilterManager.ts +3 -3
- package/src/structures/Player.ts +2810 -1693
- package/src/structures/PlayerManager.ts +438 -129
- package/src/structures/Queue.ts +300 -55
- package/src/structures/StreamManager.ts +524 -0
- package/src/types/extension.ts +129 -129
- package/src/types/fillter.ts +264 -264
- package/src/types/index.ts +187 -12
- package/src/types/plugin.ts +59 -59
- package/tsconfig.json +0 -1
|
@@ -46,7 +46,9 @@ const setGlobalManager = (instance) => {
|
|
|
46
46
|
* nodes: [{ host: "localhost", port: 2333, password: "youshallnotpass" }]
|
|
47
47
|
* })
|
|
48
48
|
* ],
|
|
49
|
-
* extractorTimeout: 10000
|
|
49
|
+
* extractorTimeout: 10000,
|
|
50
|
+
* autoCleanup: true,
|
|
51
|
+
* cleanupInterval: 60000
|
|
50
52
|
* });
|
|
51
53
|
*
|
|
52
54
|
* // Create a player for a guild
|
|
@@ -72,7 +74,7 @@ class PlayerManager extends events_1.EventEmitter {
|
|
|
72
74
|
}
|
|
73
75
|
debug(message, ...optionalParams) {
|
|
74
76
|
if (this.listenerCount("debug") > 0) {
|
|
75
|
-
this.emit("debug", message
|
|
77
|
+
this.emit("debug", `[PlayerManager] ${message}`, ...optionalParams);
|
|
76
78
|
if (!this.B_debug) {
|
|
77
79
|
this.B_debug = true;
|
|
78
80
|
}
|
|
@@ -81,9 +83,18 @@ class PlayerManager extends events_1.EventEmitter {
|
|
|
81
83
|
constructor(options = {}) {
|
|
82
84
|
super();
|
|
83
85
|
this.players = new Map();
|
|
86
|
+
this.SEARCH_CACHE_TTL = 60 * 1000; // 1 minute
|
|
87
|
+
this.MAX_CACHE_SIZE = 100;
|
|
88
|
+
this.cleanupInterval = null;
|
|
89
|
+
this.statsInterval = null;
|
|
84
90
|
this.B_debug = false;
|
|
85
91
|
this.extractorTimeout = 10000;
|
|
92
|
+
this.autoCleanup = true;
|
|
93
|
+
this.cleanupTimeout = 60000; // 1 minute
|
|
94
|
+
this.enableSearchCache = true;
|
|
86
95
|
this.plugins = [];
|
|
96
|
+
this.searchCache = new Map();
|
|
97
|
+
// Initialize plugins
|
|
87
98
|
const provided = options.plugins || [];
|
|
88
99
|
for (const p of provided) {
|
|
89
100
|
try {
|
|
@@ -94,13 +105,27 @@ class PlayerManager extends events_1.EventEmitter {
|
|
|
94
105
|
const instance = new p();
|
|
95
106
|
this.plugins.push(instance);
|
|
96
107
|
}
|
|
108
|
+
this.debug(`Registered plugin: ${p.name || "unnamed"}`);
|
|
97
109
|
}
|
|
98
110
|
catch (e) {
|
|
99
|
-
this.debug(`
|
|
111
|
+
this.debug(`Failed to init plugin:`, e);
|
|
100
112
|
}
|
|
101
113
|
}
|
|
102
114
|
this.extensions = options.extensions || [];
|
|
115
|
+
this.extractorTimeout = options.extractorTimeout ?? 10000;
|
|
116
|
+
this.autoCleanup = options.autoCleanup ?? true;
|
|
117
|
+
this.cleanupTimeout = options.cleanupInterval ?? 60000;
|
|
118
|
+
this.enableSearchCache = options.enableSearchCache ?? true;
|
|
119
|
+
// Setup auto cleanup
|
|
120
|
+
if (this.autoCleanup) {
|
|
121
|
+
this.startAutoCleanup();
|
|
122
|
+
}
|
|
123
|
+
// Setup stats collection (optional)
|
|
124
|
+
if (options.enableStatsCollection) {
|
|
125
|
+
this.startStatsCollection();
|
|
126
|
+
}
|
|
103
127
|
setGlobalManager(this);
|
|
128
|
+
this.debug(`Initialized with ${this.plugins.length} plugins, ${this.extensions.length} extensions`);
|
|
104
129
|
}
|
|
105
130
|
withTimeout(promise, message) {
|
|
106
131
|
const timeout = this.extractorTimeout;
|
|
@@ -113,48 +138,108 @@ class PlayerManager extends events_1.EventEmitter {
|
|
|
113
138
|
return guildOrId.id;
|
|
114
139
|
throw new Error("Invalid guild or guildId provided.");
|
|
115
140
|
}
|
|
141
|
+
getSearchCacheKey(query) {
|
|
142
|
+
return query.toLowerCase().trim();
|
|
143
|
+
}
|
|
144
|
+
getCachedSearch(query) {
|
|
145
|
+
if (!this.enableSearchCache)
|
|
146
|
+
return null;
|
|
147
|
+
const key = this.getSearchCacheKey(query);
|
|
148
|
+
const cached = this.searchCache.get(key);
|
|
149
|
+
if (cached && Date.now() < cached.expiresAt) {
|
|
150
|
+
this.debug(`[Cache] Search hit for: ${query}`);
|
|
151
|
+
return cached.data;
|
|
152
|
+
}
|
|
153
|
+
if (cached) {
|
|
154
|
+
this.searchCache.delete(key);
|
|
155
|
+
}
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
setCachedSearch(query, result) {
|
|
159
|
+
if (!this.enableSearchCache)
|
|
160
|
+
return;
|
|
161
|
+
// Clean up old entries if cache is too large
|
|
162
|
+
if (this.searchCache.size >= this.MAX_CACHE_SIZE) {
|
|
163
|
+
const oldest = Array.from(this.searchCache.entries()).sort((a, b) => a[1].timestamp - b[1].timestamp)[0];
|
|
164
|
+
if (oldest)
|
|
165
|
+
this.searchCache.delete(oldest[0]);
|
|
166
|
+
}
|
|
167
|
+
const key = this.getSearchCacheKey(query);
|
|
168
|
+
this.searchCache.set(key, {
|
|
169
|
+
data: result,
|
|
170
|
+
timestamp: Date.now(),
|
|
171
|
+
expiresAt: Date.now() + this.SEARCH_CACHE_TTL,
|
|
172
|
+
});
|
|
173
|
+
this.debug(`[Cache] Search stored for: ${query}`);
|
|
174
|
+
}
|
|
175
|
+
clearExpiredCache() {
|
|
176
|
+
const now = Date.now();
|
|
177
|
+
let expiredCount = 0;
|
|
178
|
+
for (const [key, entry] of this.searchCache) {
|
|
179
|
+
if (now >= entry.expiresAt) {
|
|
180
|
+
this.searchCache.delete(key);
|
|
181
|
+
expiredCount++;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
if (expiredCount > 0) {
|
|
185
|
+
this.debug(`[Cache] Cleared ${expiredCount} expired search entries`);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
startAutoCleanup() {
|
|
189
|
+
if (this.cleanupInterval) {
|
|
190
|
+
clearInterval(this.cleanupInterval);
|
|
191
|
+
}
|
|
192
|
+
this.cleanupInterval = setInterval(() => {
|
|
193
|
+
this.cleanupInactivePlayers();
|
|
194
|
+
this.clearExpiredCache();
|
|
195
|
+
}, this.cleanupTimeout);
|
|
196
|
+
this.debug(`Auto-cleanup started with interval: ${this.cleanupTimeout}ms`);
|
|
197
|
+
}
|
|
198
|
+
startStatsCollection() {
|
|
199
|
+
if (this.statsInterval) {
|
|
200
|
+
clearInterval(this.statsInterval);
|
|
201
|
+
}
|
|
202
|
+
this.statsInterval = setInterval(() => {
|
|
203
|
+
const stats = this.getStats();
|
|
204
|
+
this.emit("stats", stats);
|
|
205
|
+
}, 30000); // Every 30 seconds
|
|
206
|
+
}
|
|
207
|
+
cleanupInactivePlayers() {
|
|
208
|
+
let cleanedCount = 0;
|
|
209
|
+
for (const [guildId, player] of this.players) {
|
|
210
|
+
// Clean up players that are not playing and not connected
|
|
211
|
+
if (!player.isPlaying && !player.connection && player.queue.isEmpty) {
|
|
212
|
+
const idleTime = Date.now() - player._lastActivity || Date.now();
|
|
213
|
+
if (idleTime > this.cleanupTimeout) {
|
|
214
|
+
this.debug(`Cleaning up inactive player for guild: ${guildId}`);
|
|
215
|
+
player.destroy();
|
|
216
|
+
this.players.delete(guildId);
|
|
217
|
+
cleanedCount++;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
if (cleanedCount > 0) {
|
|
222
|
+
this.debug(`Cleaned up ${cleanedCount} inactive players`);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
116
225
|
/**
|
|
117
226
|
* Create a new player for a guild
|
|
118
227
|
*
|
|
119
228
|
* @param {string | {id: string}} guildOrId - Guild ID or guild object
|
|
120
229
|
* @param {PlayerOptions} options - Player configuration options
|
|
121
230
|
* @returns {Promise<Player>} The created player instance
|
|
122
|
-
*
|
|
123
|
-
* @example
|
|
124
|
-
* // Create player with basic options
|
|
125
|
-
* const player = await manager.create(guildId, {
|
|
126
|
-
* tts: { interrupt: true, volume: 1 },
|
|
127
|
-
* leaveOnEnd: true,
|
|
128
|
-
* leaveTimeout: 30000
|
|
129
|
-
* });
|
|
130
|
-
*
|
|
131
|
-
* // Create player with advanced options
|
|
132
|
-
* const advancedPlayer = await manager.create(guild, {
|
|
133
|
-
* volume: 0.8,
|
|
134
|
-
* quality: "high",
|
|
135
|
-
* selfDeaf: false,
|
|
136
|
-
* selfMute: false,
|
|
137
|
-
* tts: {
|
|
138
|
-
* createPlayer: true,
|
|
139
|
-
* interrupt: true,
|
|
140
|
-
* volume: 1.0,
|
|
141
|
-
* Max_Time_TTS: 30000
|
|
142
|
-
* },
|
|
143
|
-
* userdata: { customData: "example" }
|
|
144
|
-
* });
|
|
145
|
-
*
|
|
146
|
-
* // Connect and play immediately
|
|
147
|
-
* await player.connect(voiceChannel);
|
|
148
|
-
* await player.play("Never Gonna Give You Up", userId);
|
|
149
231
|
*/
|
|
150
232
|
async create(guildOrId, options) {
|
|
151
233
|
const guildId = this.resolveGuildId(guildOrId);
|
|
152
234
|
if (this.players.has(guildId)) {
|
|
235
|
+
this.debug(`Player already exists for guildId: ${guildId}, returning existing`);
|
|
153
236
|
return this.players.get(guildId);
|
|
154
237
|
}
|
|
155
|
-
this.debug(`
|
|
238
|
+
this.debug(`Creating player for guildId: ${guildId}`);
|
|
156
239
|
const player = new Player_1.Player(guildId, options, this);
|
|
240
|
+
// Add all registered plugins
|
|
157
241
|
this.plugins.forEach((plugin) => player.addPlugin(plugin));
|
|
242
|
+
// Activate extensions
|
|
158
243
|
let extsToActivate = [];
|
|
159
244
|
const optExts = options?.extensions;
|
|
160
245
|
if (Array.isArray(optExts)) {
|
|
@@ -172,6 +257,10 @@ class PlayerManager extends events_1.EventEmitter {
|
|
|
172
257
|
extsToActivate = optExts;
|
|
173
258
|
}
|
|
174
259
|
}
|
|
260
|
+
else {
|
|
261
|
+
// Use all extensions by default
|
|
262
|
+
extsToActivate = this.extensions;
|
|
263
|
+
}
|
|
175
264
|
for (const ext of extsToActivate) {
|
|
176
265
|
let instance = ext;
|
|
177
266
|
if (typeof ext === "function") {
|
|
@@ -179,7 +268,7 @@ class PlayerManager extends events_1.EventEmitter {
|
|
|
179
268
|
instance = new ext(player);
|
|
180
269
|
}
|
|
181
270
|
catch (e) {
|
|
182
|
-
this.debug(`
|
|
271
|
+
this.debug(`Extension constructor error for ${ext.name}:`, e);
|
|
183
272
|
continue;
|
|
184
273
|
}
|
|
185
274
|
}
|
|
@@ -192,11 +281,11 @@ class PlayerManager extends events_1.EventEmitter {
|
|
|
192
281
|
let activated = true;
|
|
193
282
|
try {
|
|
194
283
|
activated = await (0, timeout_1.withTimeout)(Promise.resolve(extInstance.active({ manager: this, player })), player.options.extractorTimeout ?? 15000, `Extension ${extInstance?.name} activation timed out`);
|
|
195
|
-
this.debug(`
|
|
284
|
+
this.debug(`Extension ${extInstance?.name} active check returned: ${activated}`);
|
|
196
285
|
}
|
|
197
286
|
catch (e) {
|
|
198
287
|
activated = false;
|
|
199
|
-
this.debug(`
|
|
288
|
+
this.debug(`Extension activation error for ${extInstance?.name}:`, e);
|
|
200
289
|
}
|
|
201
290
|
if (activated === false) {
|
|
202
291
|
player.detachExtension(extInstance);
|
|
@@ -205,158 +294,232 @@ class PlayerManager extends events_1.EventEmitter {
|
|
|
205
294
|
}
|
|
206
295
|
}
|
|
207
296
|
}
|
|
208
|
-
// Forward all player events
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
player.
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
297
|
+
// Forward all player events to manager
|
|
298
|
+
this.setupEventForwarding(player, guildId);
|
|
299
|
+
// Mark last activity
|
|
300
|
+
player._lastActivity = Date.now();
|
|
301
|
+
this.players.set(guildId, player);
|
|
302
|
+
this.debug(`Player created for guildId: ${guildId}`);
|
|
303
|
+
return player;
|
|
304
|
+
}
|
|
305
|
+
setupEventForwarding(player, guildId) {
|
|
306
|
+
const forwardEvents = {
|
|
307
|
+
willPlay: "willPlay",
|
|
308
|
+
trackStart: "trackStart",
|
|
309
|
+
trackEnd: "trackEnd",
|
|
310
|
+
queueEnd: "queueEnd",
|
|
311
|
+
playerError: "playerError",
|
|
312
|
+
connectionError: "connectionError",
|
|
313
|
+
volumeChange: "volumeChange",
|
|
314
|
+
queueAdd: "queueAdd",
|
|
315
|
+
queueAddList: "queueAddList",
|
|
316
|
+
queueRemove: "queueRemove",
|
|
317
|
+
playerPause: "playerPause",
|
|
318
|
+
playerResume: "playerResume",
|
|
319
|
+
playerStop: "playerStop",
|
|
320
|
+
ttsStart: "ttsStart",
|
|
321
|
+
ttsEnd: "ttsEnd",
|
|
322
|
+
streamError: "streamError",
|
|
323
|
+
};
|
|
324
|
+
for (const [sourceEvent, targetEvent] of Object.entries(forwardEvents)) {
|
|
325
|
+
player.on(sourceEvent, (...args) => {
|
|
326
|
+
if (sourceEvent === "trackStart") {
|
|
327
|
+
player._lastActivity = Date.now();
|
|
328
|
+
}
|
|
329
|
+
this.emit(targetEvent, player, ...args);
|
|
330
|
+
});
|
|
331
|
+
}
|
|
222
332
|
player.on("playerDestroy", () => {
|
|
223
333
|
this.emit("playerDestroy", player);
|
|
224
334
|
this.players.delete(guildId);
|
|
335
|
+
this.debug(`Player destroyed for guildId: ${guildId}`);
|
|
225
336
|
});
|
|
226
|
-
player.on("ttsStart", (payload) => this.emit("ttsStart", player, payload));
|
|
227
|
-
player.on("ttsEnd", () => this.emit("ttsEnd", player));
|
|
228
337
|
player.on("debug", (...args) => {
|
|
229
338
|
if (this.listenerCount("debug") > 0) {
|
|
230
339
|
this.emit("debug", ...args);
|
|
231
340
|
}
|
|
232
341
|
});
|
|
233
|
-
this.players.set(guildId, player);
|
|
234
|
-
return player;
|
|
235
342
|
}
|
|
236
343
|
/**
|
|
237
344
|
* Get an existing player for a guild
|
|
238
345
|
*
|
|
239
346
|
* @param {string | {id: string}} guildOrId - Guild ID or guild object
|
|
240
347
|
* @returns {Player | undefined} The player instance or undefined if not found
|
|
241
|
-
* @example
|
|
242
|
-
* // Get player by guild ID
|
|
243
|
-
* const player = manager.get(guildId);
|
|
244
|
-
* if (player) {
|
|
245
|
-
* await player.play("Never Gonna Give You Up", userId);
|
|
246
|
-
* } else {
|
|
247
|
-
* console.log("No player found for this guild");
|
|
248
|
-
* }
|
|
249
|
-
*
|
|
250
|
-
* // Get player by guild object
|
|
251
|
-
* const playerFromGuild = manager.get(guild);
|
|
252
|
-
* if (playerFromGuild) {
|
|
253
|
-
* playerFromGuild.setVolume(0.5);
|
|
254
|
-
* }
|
|
255
|
-
*
|
|
256
|
-
* // Check if player exists before using
|
|
257
|
-
* const existingPlayer = manager.get(guildId);
|
|
258
|
-
* if (existingPlayer && existingPlayer.playing) {
|
|
259
|
-
* existingPlayer.pause();
|
|
260
|
-
* }
|
|
261
348
|
*/
|
|
262
349
|
get(guildOrId) {
|
|
263
350
|
const guildId = this.resolveGuildId(guildOrId);
|
|
264
|
-
|
|
351
|
+
const player = this.players.get(guildId);
|
|
352
|
+
if (player) {
|
|
353
|
+
player._lastActivity = Date.now();
|
|
354
|
+
}
|
|
355
|
+
return player;
|
|
265
356
|
}
|
|
266
357
|
/**
|
|
267
|
-
* Get an existing player for a guild
|
|
268
|
-
*
|
|
269
|
-
* @param {string | {id: string}} guildOrId - Guild ID or guild object
|
|
270
|
-
* @returns {Player | undefined} The player instance or undefined
|
|
271
|
-
* @example
|
|
272
|
-
* const player = manager.get(guildId);
|
|
273
|
-
* if (player) {
|
|
274
|
-
* await player.play("song name", userId);
|
|
275
|
-
* }
|
|
358
|
+
* Get an existing player for a guild (alias for get)
|
|
276
359
|
*/
|
|
277
360
|
getPlayer(guildOrId) {
|
|
278
|
-
|
|
279
|
-
return this.players.get(guildId);
|
|
361
|
+
return this.get(guildOrId);
|
|
280
362
|
}
|
|
281
363
|
/**
|
|
282
364
|
* Get all players
|
|
283
365
|
*
|
|
284
366
|
* @returns {Player[]} All player instances
|
|
285
|
-
* @example
|
|
286
|
-
* const players = manager.getall();
|
|
287
|
-
* console.log(`Players: ${players.length}`);
|
|
288
367
|
*/
|
|
289
|
-
|
|
368
|
+
getAll() {
|
|
290
369
|
return Array.from(this.players.values());
|
|
291
370
|
}
|
|
371
|
+
/**
|
|
372
|
+
* Alias for getAll
|
|
373
|
+
*/
|
|
374
|
+
getall() {
|
|
375
|
+
return this.getAll();
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Get players by filter
|
|
379
|
+
*
|
|
380
|
+
* @param {(player: Player) => boolean} filter - Filter function
|
|
381
|
+
* @returns {Player[]} Filtered player instances
|
|
382
|
+
*/
|
|
383
|
+
getPlayersByFilter(filter) {
|
|
384
|
+
return this.getAll().filter(filter);
|
|
385
|
+
}
|
|
386
|
+
/**
|
|
387
|
+
* Get players in a voice channel
|
|
388
|
+
*
|
|
389
|
+
* @param {string} channelId - Voice channel ID
|
|
390
|
+
* @returns {Player[]} Players in the channel
|
|
391
|
+
*/
|
|
392
|
+
getPlayersInChannel(channelId) {
|
|
393
|
+
return this.getAll().filter((p) => p.connection?.joinConfig.channelId === channelId);
|
|
394
|
+
}
|
|
292
395
|
/**
|
|
293
396
|
* Destroy a player and clean up resources
|
|
294
397
|
*
|
|
295
398
|
* @param {string | {id: string}} guildOrId - Guild ID or guild object
|
|
296
399
|
* @returns {boolean} True if player was destroyed, false if not found
|
|
297
|
-
* @example
|
|
298
|
-
* // Destroy player by guild ID
|
|
299
|
-
* const destroyed = manager.delete(guildId);
|
|
300
|
-
* if (destroyed) {
|
|
301
|
-
* console.log("Player destroyed successfully");
|
|
302
|
-
* } else {
|
|
303
|
-
* console.log("No player found to destroy");
|
|
304
|
-
* }
|
|
305
|
-
*
|
|
306
|
-
* // Destroy player by guild object
|
|
307
|
-
* const destroyedFromGuild = manager.delete(guild);
|
|
308
|
-
* console.log(`Player destroyed: ${destroyedFromGuild}`);
|
|
309
|
-
*
|
|
310
|
-
* // Clean up all players
|
|
311
|
-
* for (const [guildId, player] of manager.players) {
|
|
312
|
-
* const destroyed = manager.delete(guildId);
|
|
313
|
-
* console.log(`Destroyed player for ${guildId}: ${destroyed}`);
|
|
314
|
-
* }
|
|
315
400
|
*/
|
|
316
401
|
delete(guildOrId) {
|
|
317
402
|
const guildId = this.resolveGuildId(guildOrId);
|
|
318
403
|
const player = this.players.get(guildId);
|
|
319
404
|
if (player) {
|
|
320
|
-
this.debug(`
|
|
405
|
+
this.debug(`Deleting player for guildId: ${guildId}`);
|
|
321
406
|
player.destroy();
|
|
322
|
-
return
|
|
407
|
+
return true;
|
|
323
408
|
}
|
|
324
409
|
return false;
|
|
325
410
|
}
|
|
411
|
+
/**
|
|
412
|
+
* Destroy multiple players by filter
|
|
413
|
+
*
|
|
414
|
+
* @param {(player: Player) => boolean} filter - Filter function
|
|
415
|
+
* @returns {number} Number of players destroyed
|
|
416
|
+
*/
|
|
417
|
+
deleteWhere(filter) {
|
|
418
|
+
const toDelete = this.getPlayersByFilter(filter);
|
|
419
|
+
let count = 0;
|
|
420
|
+
for (const player of toDelete) {
|
|
421
|
+
const guildId = player.guildId;
|
|
422
|
+
player.destroy();
|
|
423
|
+
this.players.delete(guildId);
|
|
424
|
+
count++;
|
|
425
|
+
}
|
|
426
|
+
if (count > 0) {
|
|
427
|
+
this.debug(`Deleted ${count} players by filter`);
|
|
428
|
+
}
|
|
429
|
+
return count;
|
|
430
|
+
}
|
|
326
431
|
/**
|
|
327
432
|
* Check if a player exists for a guild
|
|
328
433
|
*
|
|
329
434
|
* @param {string | {id: string}} guildOrId - Guild ID or guild object
|
|
330
|
-
* @returns {boolean} True if player exists
|
|
331
|
-
* @example
|
|
332
|
-
* const exists = manager.has(guildId);
|
|
333
|
-
* console.log(`Player exists: ${exists}`);
|
|
435
|
+
* @returns {boolean} True if player exists
|
|
334
436
|
*/
|
|
335
437
|
has(guildOrId) {
|
|
336
438
|
const guildId = this.resolveGuildId(guildOrId);
|
|
337
439
|
return this.players.has(guildId);
|
|
338
440
|
}
|
|
441
|
+
/**
|
|
442
|
+
* Get number of players
|
|
443
|
+
*/
|
|
339
444
|
get size() {
|
|
340
445
|
return this.players.size;
|
|
341
446
|
}
|
|
447
|
+
/**
|
|
448
|
+
* Check if debug is enabled
|
|
449
|
+
*/
|
|
342
450
|
get debugEnabled() {
|
|
343
451
|
return this.B_debug;
|
|
344
452
|
}
|
|
345
453
|
/**
|
|
346
|
-
*
|
|
454
|
+
* Get manager statistics
|
|
347
455
|
*
|
|
348
|
-
* @returns {
|
|
456
|
+
* @returns {PlayerStats} Statistics about players
|
|
457
|
+
*/
|
|
458
|
+
getStats() {
|
|
459
|
+
let activePlayers = 0;
|
|
460
|
+
let pausedPlayers = 0;
|
|
461
|
+
let connectedPlayers = 0;
|
|
462
|
+
let totalTracksInQueue = 0;
|
|
463
|
+
for (const player of this.players.values()) {
|
|
464
|
+
if (player.isPlaying)
|
|
465
|
+
activePlayers++;
|
|
466
|
+
if (player.isPaused)
|
|
467
|
+
pausedPlayers++;
|
|
468
|
+
if (player.connection)
|
|
469
|
+
connectedPlayers++;
|
|
470
|
+
totalTracksInQueue += player.queueSize;
|
|
471
|
+
}
|
|
472
|
+
return {
|
|
473
|
+
totalPlayers: this.players.size,
|
|
474
|
+
activePlayers,
|
|
475
|
+
pausedPlayers,
|
|
476
|
+
connectedPlayers,
|
|
477
|
+
totalTracksInQueue,
|
|
478
|
+
};
|
|
479
|
+
}
|
|
480
|
+
/**
|
|
481
|
+
* Broadcast an action to all players
|
|
482
|
+
*
|
|
483
|
+
* @param {string} action - Action to perform
|
|
484
|
+
* @param {...any[]} args - Arguments for the action
|
|
349
485
|
* @example
|
|
350
|
-
* manager.
|
|
351
|
-
*
|
|
486
|
+
* manager.broadcast("setVolume", 50);
|
|
487
|
+
* manager.broadcast("pause");
|
|
488
|
+
*/
|
|
489
|
+
broadcast(action, ...args) {
|
|
490
|
+
for (const player of this.players.values()) {
|
|
491
|
+
if (typeof player[action] === "function") {
|
|
492
|
+
try {
|
|
493
|
+
player[action](...args);
|
|
494
|
+
}
|
|
495
|
+
catch (error) {
|
|
496
|
+
this.debug(`Error broadcasting ${action} to ${player.guildId}:`, error);
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
/**
|
|
502
|
+
* Destroy all players and clean up
|
|
352
503
|
*/
|
|
353
504
|
destroy() {
|
|
354
|
-
this.debug(`
|
|
505
|
+
this.debug(`Destroying all players`);
|
|
506
|
+
// Stop cleanup intervals
|
|
507
|
+
if (this.cleanupInterval) {
|
|
508
|
+
clearInterval(this.cleanupInterval);
|
|
509
|
+
this.cleanupInterval = null;
|
|
510
|
+
}
|
|
511
|
+
if (this.statsInterval) {
|
|
512
|
+
clearInterval(this.statsInterval);
|
|
513
|
+
this.statsInterval = null;
|
|
514
|
+
}
|
|
515
|
+
// Destroy all players
|
|
355
516
|
for (const player of this.players.values()) {
|
|
356
517
|
player.destroy();
|
|
357
518
|
}
|
|
358
519
|
this.players.clear();
|
|
520
|
+
this.searchCache.clear();
|
|
359
521
|
this.removeAllListeners();
|
|
522
|
+
this.debug(`PlayerManager destroyed`);
|
|
360
523
|
}
|
|
361
524
|
/**
|
|
362
525
|
* Search using registered plugins without creating a Player.
|
|
@@ -364,28 +527,106 @@ class PlayerManager extends events_1.EventEmitter {
|
|
|
364
527
|
* @param {string} query - The query to search for
|
|
365
528
|
* @param {string} requestedBy - The user ID who requested the search
|
|
366
529
|
* @returns {Promise<SearchResult>} The search result
|
|
367
|
-
* @example
|
|
368
|
-
* const result = await manager.search("Never Gonna Give You Up", userId);
|
|
369
|
-
* console.log(`Search result: ${result.tracks.length} tracks`);
|
|
370
530
|
*/
|
|
371
531
|
async search(query, requestedBy) {
|
|
372
|
-
this.debug(`
|
|
532
|
+
this.debug(`Search called with query: ${query}, requestedBy: ${requestedBy}`);
|
|
533
|
+
// Check cache first
|
|
534
|
+
const cached = this.getCachedSearch(query);
|
|
535
|
+
if (cached) {
|
|
536
|
+
return cached;
|
|
537
|
+
}
|
|
373
538
|
const plugin = this.plugins.find((p) => p.canHandle(query));
|
|
374
539
|
if (!plugin) {
|
|
375
|
-
this.debug(`
|
|
540
|
+
this.debug(`No plugin found to handle: ${query}`);
|
|
376
541
|
throw new Error(`No plugin found to handle: ${query}`);
|
|
377
542
|
}
|
|
378
543
|
try {
|
|
379
|
-
|
|
544
|
+
const result = await this.withTimeout(plugin.search(query, requestedBy), "Search operation timed out");
|
|
545
|
+
this.setCachedSearch(query, result);
|
|
546
|
+
return result;
|
|
380
547
|
}
|
|
381
548
|
catch (error) {
|
|
382
|
-
this.debug(`
|
|
549
|
+
this.debug(`Search error:`, error);
|
|
383
550
|
throw error;
|
|
384
551
|
}
|
|
385
552
|
}
|
|
553
|
+
/**
|
|
554
|
+
* Clear search cache
|
|
555
|
+
*/
|
|
556
|
+
clearSearchCache() {
|
|
557
|
+
const size = this.searchCache.size;
|
|
558
|
+
this.searchCache.clear();
|
|
559
|
+
this.debug(`Cleared ${size} search cache entries`);
|
|
560
|
+
}
|
|
561
|
+
/**
|
|
562
|
+
* Register a plugin after initialization
|
|
563
|
+
*
|
|
564
|
+
* @param {SourcePlugin} plugin - Plugin to register
|
|
565
|
+
*/
|
|
566
|
+
registerPlugin(plugin) {
|
|
567
|
+
this.plugins.push(plugin);
|
|
568
|
+
this.debug(`Registered plugin: ${plugin.name}`);
|
|
569
|
+
// Register plugin with all existing players
|
|
570
|
+
for (const player of this.players.values()) {
|
|
571
|
+
player.addPlugin(plugin);
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
/**
|
|
575
|
+
* Unregister a plugin
|
|
576
|
+
*
|
|
577
|
+
* @param {string} name - Plugin name to unregister
|
|
578
|
+
* @returns {boolean} True if plugin was unregistered
|
|
579
|
+
*/
|
|
580
|
+
unregisterPlugin(name) {
|
|
581
|
+
const index = this.plugins.findIndex((p) => p.name === name);
|
|
582
|
+
if (index === -1)
|
|
583
|
+
return false;
|
|
584
|
+
this.plugins.splice(index, 1);
|
|
585
|
+
this.debug(`Unregistered plugin: ${name}`);
|
|
586
|
+
// Note: Cannot easily remove plugins from existing players
|
|
587
|
+
return true;
|
|
588
|
+
}
|
|
589
|
+
/**
|
|
590
|
+
* Get all registered plugins
|
|
591
|
+
*/
|
|
592
|
+
getPlugins() {
|
|
593
|
+
return [...this.plugins];
|
|
594
|
+
}
|
|
595
|
+
/**
|
|
596
|
+
* Register an extension after initialization
|
|
597
|
+
*
|
|
598
|
+
* @param {BaseExtension} extension - Extension to register
|
|
599
|
+
*/
|
|
600
|
+
registerExtension(extension) {
|
|
601
|
+
this.extensions.push(extension);
|
|
602
|
+
this.debug(`Registered extension: ${extension.name}`);
|
|
603
|
+
// Register extension with all existing players
|
|
604
|
+
for (const player of this.players.values()) {
|
|
605
|
+
player.attachExtension(extension);
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
/**
|
|
609
|
+
* Get manager configuration
|
|
610
|
+
*/
|
|
611
|
+
getConfig() {
|
|
612
|
+
return {
|
|
613
|
+
extractorTimeout: this.extractorTimeout,
|
|
614
|
+
autoCleanup: this.autoCleanup,
|
|
615
|
+
cleanupTimeout: this.cleanupTimeout,
|
|
616
|
+
enableSearchCache: this.enableSearchCache,
|
|
617
|
+
pluginsCount: this.plugins.length,
|
|
618
|
+
extensionsCount: this.extensions.length,
|
|
619
|
+
playersCount: this.players.size,
|
|
620
|
+
};
|
|
621
|
+
}
|
|
386
622
|
}
|
|
387
623
|
exports.PlayerManager = PlayerManager;
|
|
388
624
|
PlayerManager.instance = null;
|
|
625
|
+
/**
|
|
626
|
+
* Get the global PlayerManager instance
|
|
627
|
+
*
|
|
628
|
+
* @returns {PlayerManager | null} Global instance or null
|
|
629
|
+
*/
|
|
389
630
|
function getInstance() {
|
|
390
631
|
const globalInst = (0, exports.getGlobalManager)();
|
|
391
632
|
if (!globalInst) {
|