ziplayer 0.2.7-dev.1 → 0.2.7-dev.3
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 +17 -6
- package/dist/persistence/PersistenceManager.d.ts +50 -16
- package/dist/persistence/PersistenceManager.d.ts.map +1 -1
- package/dist/persistence/PersistenceManager.js +484 -60
- package/dist/persistence/PersistenceManager.js.map +1 -1
- package/dist/structures/Player.d.ts +2 -0
- package/dist/structures/Player.d.ts.map +1 -1
- package/dist/structures/Player.js +5 -0
- package/dist/structures/Player.js.map +1 -1
- package/dist/structures/PlayerManager.d.ts +3 -1
- package/dist/structures/PlayerManager.d.ts.map +1 -1
- package/dist/structures/PlayerManager.js +49 -31
- package/dist/structures/PlayerManager.js.map +1 -1
- package/dist/types/index.d.ts +7 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js.map +1 -1
- package/dist/types/persistence.d.ts +26 -5
- package/dist/types/persistence.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/persistence/PersistenceManager.ts +570 -65
- package/src/structures/Player.ts +8 -0
- package/src/structures/PlayerManager.ts +66 -32
- package/src/types/index.ts +9 -0
- package/src/types/persistence.ts +37 -17
package/src/structures/Player.ts
CHANGED
|
@@ -94,6 +94,8 @@ export class Player extends EventEmitter {
|
|
|
94
94
|
public pluginManager: PluginManager;
|
|
95
95
|
public extensionManager: ExtensionManager;
|
|
96
96
|
public userdata?: Record<string, any>;
|
|
97
|
+
public _lastActivity: number = Date.now();
|
|
98
|
+
public channelConnection?: VoiceChannel;
|
|
97
99
|
private manager: PlayerManager;
|
|
98
100
|
private leaveTimeout: NodeJS.Timeout | null = null;
|
|
99
101
|
private currentResource: AudioResource | null = null;
|
|
@@ -858,6 +860,7 @@ export class Player extends EventEmitter {
|
|
|
858
860
|
async connect(channel: VoiceChannel): Promise<VoiceConnection> {
|
|
859
861
|
try {
|
|
860
862
|
this.debug(`[Player] Connecting to voice channel: ${channel.id}`);
|
|
863
|
+
this.channelConnection = channel;
|
|
861
864
|
const connection = joinVoiceChannel({
|
|
862
865
|
channelId: channel.id,
|
|
863
866
|
guildId: channel.guildId,
|
|
@@ -1495,6 +1498,11 @@ export class Player extends EventEmitter {
|
|
|
1495
1498
|
*/
|
|
1496
1499
|
destroy(): void {
|
|
1497
1500
|
this.debug(`[Player] destroy called`);
|
|
1501
|
+
|
|
1502
|
+
if (this.manager.getPersistence()) {
|
|
1503
|
+
this.manager.getPersistence()?.markPlayerDestroyed(this.guildId, "player_destroy_called");
|
|
1504
|
+
}
|
|
1505
|
+
|
|
1498
1506
|
if (this.leaveTimeout) {
|
|
1499
1507
|
clearTimeout(this.leaveTimeout);
|
|
1500
1508
|
this.leaveTimeout = null;
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
ManagerEvents,
|
|
10
10
|
PlayerStats,
|
|
11
11
|
PersistenceOptions,
|
|
12
|
+
DestroyedRecord,
|
|
12
13
|
} from "../types";
|
|
13
14
|
import type { BaseExtension } from "../extensions";
|
|
14
15
|
import { withTimeout } from "../utils/timeout";
|
|
@@ -367,36 +368,51 @@ export class PlayerManager extends EventEmitter {
|
|
|
367
368
|
}
|
|
368
369
|
|
|
369
370
|
private setupEventForwarding(player: Player, guildId: string): void {
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
371
|
+
const forwardEvents = {
|
|
372
|
+
willPlay: "willPlay",
|
|
373
|
+
trackStart: "trackStart",
|
|
374
|
+
trackEnd: "trackEnd",
|
|
375
|
+
queueEnd: "queueEnd",
|
|
376
|
+
playerError: "playerError",
|
|
377
|
+
connectionError: "connectionError",
|
|
378
|
+
volumeChange: "volumeChange",
|
|
379
|
+
queueAdd: "queueAdd",
|
|
380
|
+
queueAddList: "queueAddList",
|
|
381
|
+
queueRemove: "queueRemove",
|
|
382
|
+
playerPause: "playerPause",
|
|
383
|
+
playerResume: "playerResume",
|
|
384
|
+
playerStop: "playerStop",
|
|
385
|
+
ttsStart: "ttsStart",
|
|
386
|
+
ttsEnd: "ttsEnd",
|
|
387
|
+
} as const satisfies Record<string, keyof ManagerEvents>;
|
|
388
|
+
|
|
389
|
+
for (const [sourceEvent, targetEvent] of Object.entries(forwardEvents) as [
|
|
390
|
+
keyof typeof forwardEvents,
|
|
391
|
+
keyof ManagerEvents,
|
|
392
|
+
][]) {
|
|
393
|
+
player.on(sourceEvent, (...args: any[]) => {
|
|
394
|
+
if (sourceEvent === "trackStart") {
|
|
395
|
+
player._lastActivity = Date.now();
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
(this.emit as any)(targetEvent, player, ...args);
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
|
|
386
402
|
player.on("playerDestroy", () => {
|
|
387
403
|
this.emit("playerDestroy", player);
|
|
404
|
+
|
|
388
405
|
this.players.delete(guildId);
|
|
406
|
+
|
|
389
407
|
this.debug(`Player destroyed for guildId: ${guildId}`);
|
|
390
408
|
});
|
|
391
|
-
|
|
392
|
-
player.on("ttsEnd", () => this.emit("ttsEnd", player));
|
|
409
|
+
|
|
393
410
|
player.on("debug", (...args) => {
|
|
394
411
|
if (this.listenerCount("debug") > 0) {
|
|
395
412
|
this.emit("debug", ...args);
|
|
396
413
|
}
|
|
397
414
|
});
|
|
398
415
|
}
|
|
399
|
-
|
|
400
416
|
/**
|
|
401
417
|
* Get an existing player for a guild
|
|
402
418
|
*
|
|
@@ -715,26 +731,44 @@ export class PlayerManager extends EventEmitter {
|
|
|
715
731
|
private initPersistence(persistenceOptions: PersistenceOptions): void {
|
|
716
732
|
this.persistenceManager = new PersistenceManager(this, persistenceOptions);
|
|
717
733
|
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
});
|
|
734
|
+
const forwardEvents = {
|
|
735
|
+
playerSaved: "playerSaved",
|
|
736
|
+
playerLoaded: "playerLoaded",
|
|
722
737
|
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
738
|
+
playerSkipped: "RTSkipped",
|
|
739
|
+
playerMarkedDestroyed: "RTMarkedDestroyed",
|
|
740
|
+
playerDestroyedCleared: "RTDestroyedCleared",
|
|
726
741
|
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
});
|
|
742
|
+
savedAll: "savedAll",
|
|
743
|
+
loadedAll: "loadedAll",
|
|
730
744
|
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
745
|
+
backupsCleaned: "backupsCleaned",
|
|
746
|
+
allBackupsCleaned: "allBackupsCleaned",
|
|
747
|
+
|
|
748
|
+
backupStats: "backupStats",
|
|
749
|
+
backupCleanupDone: "backupCleanupDone",
|
|
750
|
+
} as const satisfies Record<string, keyof ManagerEvents>;
|
|
751
|
+
|
|
752
|
+
for (const [sourceEvent, targetEvent] of Object.entries(forwardEvents) as [
|
|
753
|
+
keyof typeof forwardEvents,
|
|
754
|
+
keyof ManagerEvents,
|
|
755
|
+
][]) {
|
|
756
|
+
this.persistenceManager.on(sourceEvent, (...args: any[]) => {
|
|
757
|
+
this.emit(targetEvent, ...(args as ManagerEvents[typeof targetEvent]));
|
|
758
|
+
});
|
|
759
|
+
}
|
|
734
760
|
|
|
735
761
|
this.debug("Persistence manager initialized");
|
|
736
762
|
}
|
|
737
763
|
|
|
764
|
+
isAutoRestoreEnabled(): boolean {
|
|
765
|
+
return this.persistenceManager?.isAutoRestoreEnabled() ?? false;
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
getDestroyedPlayers(): DestroyedRecord[] {
|
|
769
|
+
return this.persistenceManager?.getDestroyedPlayers() ?? [];
|
|
770
|
+
}
|
|
771
|
+
|
|
738
772
|
/**
|
|
739
773
|
* Get persistence manager
|
|
740
774
|
*/
|
package/src/types/index.ts
CHANGED
|
@@ -363,11 +363,20 @@ export interface ManagerEvents {
|
|
|
363
363
|
lyricsCreate: [player: Player, track: Track, lyrics: any];
|
|
364
364
|
lyricsChange: [player: Player, track: Track, lyrics: any];
|
|
365
365
|
voiceCreate: [player: Player, evt: any];
|
|
366
|
+
|
|
367
|
+
// Persistence events
|
|
366
368
|
stats: [stats: PlayerStats];
|
|
367
369
|
playerSaved: [guildId: string];
|
|
368
370
|
playerLoaded: [guildId: string, data: any];
|
|
369
371
|
savedAll: [results: Map<string, boolean>];
|
|
370
372
|
loadedAll: [results: Map<string, boolean>];
|
|
373
|
+
RTSkipped: [guildId: string, reason: string];
|
|
374
|
+
RTMarkedDestroyed: [guildId: string];
|
|
375
|
+
RTDestroyedCleared: [guildId: string];
|
|
376
|
+
backupsCleaned: [guildId: string, count: number];
|
|
377
|
+
allBackupsCleaned: [count: number];
|
|
378
|
+
backupStats: [stats: any];
|
|
379
|
+
backupCleanupDone: [];
|
|
371
380
|
}
|
|
372
381
|
export interface PlayerEvents {
|
|
373
382
|
debug: [message: string, ...args: any[]];
|
package/src/types/persistence.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Track, LoopMode, PlayerOptions } from "
|
|
1
|
+
import type { Track, LoopMode, PlayerOptions, VoiceChannel } from "../types";
|
|
2
2
|
|
|
3
3
|
export interface SerializedTrack {
|
|
4
4
|
id: string;
|
|
@@ -11,7 +11,7 @@ export interface SerializedTrack {
|
|
|
11
11
|
requestedBy?: string;
|
|
12
12
|
isLive?: boolean;
|
|
13
13
|
artwork?: string;
|
|
14
|
-
[key: string]: any;
|
|
14
|
+
[key: string]: any;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
export interface SerializedQueue {
|
|
@@ -20,7 +20,7 @@ export interface SerializedQueue {
|
|
|
20
20
|
history: SerializedTrack[];
|
|
21
21
|
loopMode: LoopMode;
|
|
22
22
|
autoPlay: boolean;
|
|
23
|
-
position?: number;
|
|
23
|
+
position?: number;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
export interface SerializedPlayer {
|
|
@@ -32,34 +32,54 @@ export interface SerializedPlayer {
|
|
|
32
32
|
options: PlayerOptions;
|
|
33
33
|
filters?: string[];
|
|
34
34
|
lastUpdate: number;
|
|
35
|
-
version: string;
|
|
35
|
+
version: string;
|
|
36
|
+
wasDestroyed?: boolean; // Track if player was destroyed
|
|
37
|
+
destroyedAt?: number; // When it was destroyed
|
|
38
|
+
channelConnection?: VoiceChannel; // Store channel connection data for auto-reconnect
|
|
36
39
|
}
|
|
37
40
|
|
|
38
41
|
export interface PersistenceOptions {
|
|
39
42
|
enabled: boolean;
|
|
40
43
|
provider?: "file" | "redis" | "database";
|
|
41
|
-
saveInterval?: number;
|
|
42
|
-
autoLoad?: boolean;
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
//
|
|
44
|
+
saveInterval?: number;
|
|
45
|
+
autoLoad?: boolean;
|
|
46
|
+
autoRestoreOnRestart?: boolean; // Auto restore after restart
|
|
47
|
+
autoConnect?: boolean; // Auto connect to saved voice channel on restore
|
|
48
|
+
restoreDelay?: number; // Delay before restoring (ms)
|
|
49
|
+
maxBackups?: number; // Max backups per player (default: 5)
|
|
50
|
+
maxTotalBackups?: number; // Max total backups across all players
|
|
51
|
+
autoCleanupBackupsOnStart?: boolean; // Auto delete old backups on restart
|
|
52
|
+
backupRetentionDays?: number; // Delete backups older than N days
|
|
53
|
+
compress?: boolean;
|
|
47
54
|
filePath?: string;
|
|
48
|
-
|
|
49
|
-
// Redis provider options
|
|
50
55
|
redisUrl?: string;
|
|
51
56
|
redisPrefix?: string;
|
|
52
57
|
|
|
53
|
-
// Database
|
|
54
|
-
save?: (
|
|
55
|
-
load?: () => Promise<
|
|
56
|
-
delete?: (
|
|
58
|
+
// Database provider functions (different signatures)
|
|
59
|
+
save?: ((key: string, data: any) => Promise<void>) | ((data: any) => Promise<void>);
|
|
60
|
+
load?: ((key: string) => Promise<any>) | (() => Promise<any>);
|
|
61
|
+
delete?: (key: string) => Promise<void>;
|
|
57
62
|
list?: () => Promise<string[]>;
|
|
58
63
|
}
|
|
59
64
|
|
|
60
65
|
export interface PersistenceProvider {
|
|
61
|
-
save(key: string, data: any
|
|
66
|
+
save(key: string, data: any): Promise<void>;
|
|
62
67
|
load(key: string): Promise<any>;
|
|
63
68
|
delete(key: string): Promise<void>;
|
|
64
69
|
list(): Promise<string[]>;
|
|
65
70
|
}
|
|
71
|
+
|
|
72
|
+
// Track destroyed players for restart detection
|
|
73
|
+
export interface DestroyedRecord {
|
|
74
|
+
guildId: string;
|
|
75
|
+
destroyedAt: number;
|
|
76
|
+
reason?: string;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export interface BackupInfo {
|
|
80
|
+
key: string;
|
|
81
|
+
path: string;
|
|
82
|
+
timestamp: number;
|
|
83
|
+
size: number;
|
|
84
|
+
isCompressed: boolean;
|
|
85
|
+
}
|