ziplayer 0.2.7-dev.1 → 0.2.7-dev.2

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.
@@ -94,6 +94,7 @@ 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();
97
98
  private manager: PlayerManager;
98
99
  private leaveTimeout: NodeJS.Timeout | null = null;
99
100
  private currentResource: AudioResource | null = null;
@@ -1495,6 +1496,11 @@ export class Player extends EventEmitter {
1495
1496
  */
1496
1497
  destroy(): void {
1497
1498
  this.debug(`[Player] destroy called`);
1499
+
1500
+ if (this.manager.getPersistence()) {
1501
+ this.manager.getPersistence()?.markPlayerDestroyed(this.guildId, "player_destroy_called");
1502
+ }
1503
+
1498
1504
  if (this.leaveTimeout) {
1499
1505
  clearTimeout(this.leaveTimeout);
1500
1506
  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
- player.on("willPlay", (track, tracks) => this.emit("willPlay", player, track as Track, tracks as Track[]));
371
- player.on("trackStart", (track) => {
372
- (player as any)._lastActivity = Date.now();
373
- this.emit("trackStart", player, track as Track);
374
- });
375
- player.on("trackEnd", (track) => this.emit("trackEnd", player, track as Track));
376
- player.on("queueEnd", () => this.emit("queueEnd", player));
377
- player.on("playerError", (error, track) => this.emit("playerError", player, error, track as Track));
378
- player.on("connectionError", (error) => this.emit("connectionError", player, error));
379
- player.on("volumeChange", (oldVol, newVol) => this.emit("volumeChange", player, oldVol as number, newVol as number));
380
- player.on("queueAdd", (track) => this.emit("queueAdd", player, track as Track));
381
- player.on("queueAddList", (tracks) => this.emit("queueAddList", player, tracks as Track[]));
382
- player.on("queueRemove", (track, index) => this.emit("queueRemove", player, track as Track, index as number));
383
- player.on("playerPause", (track) => this.emit("playerPause", player, track as Track));
384
- player.on("playerResume", (track) => this.emit("playerResume", player, track as Track));
385
- player.on("playerStop", () => this.emit("playerStop", player));
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
- player.on("ttsStart", (payload) => this.emit("ttsStart", player, payload));
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
- // Forward persistence events
719
- this.persistenceManager.on("playerSaved", (guildId) => {
720
- this.emit("playerSaved", guildId);
721
- });
734
+ const forwardEvents = {
735
+ playerSaved: "playerSaved",
736
+ playerLoaded: "playerLoaded",
722
737
 
723
- this.persistenceManager.on("playerLoaded", (guildId, data) => {
724
- this.emit("playerLoaded", guildId, data);
725
- });
738
+ playerSkipped: "RTSkipped",
739
+ playerMarkedDestroyed: "RTMarkedDestroyed",
740
+ playerDestroyedCleared: "RTDestroyedCleared",
726
741
 
727
- this.persistenceManager.on("savedAll", (results) => {
728
- this.emit("savedAll", results);
729
- });
742
+ savedAll: "savedAll",
743
+ loadedAll: "loadedAll",
730
744
 
731
- this.persistenceManager.on("loadedAll", (results) => {
732
- this.emit("loadedAll", results);
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
  */
@@ -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[]];
@@ -1,4 +1,4 @@
1
- import type { Track, LoopMode, PlayerOptions } from ".";
1
+ import type { Track, LoopMode, PlayerOptions } 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; // For additional metadata
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; // Current playback position in ms
23
+ position?: number;
24
24
  }
25
25
 
26
26
  export interface SerializedPlayer {
@@ -32,34 +32,52 @@ export interface SerializedPlayer {
32
32
  options: PlayerOptions;
33
33
  filters?: string[];
34
34
  lastUpdate: number;
35
- version: string; // For backward compatibility
35
+ version: string;
36
+ wasDestroyed?: boolean; // Track if player was destroyed
37
+ destroyedAt?: number; // When it was destroyed
36
38
  }
37
39
 
38
40
  export interface PersistenceOptions {
39
41
  enabled: boolean;
40
42
  provider?: "file" | "redis" | "database";
41
- saveInterval?: number; // Auto-save interval (ms)
42
- autoLoad?: boolean; // Auto-load on start
43
- maxBackups?: number; // Number of backups to keep
44
- compress?: boolean; // Compress saved data
45
-
46
- // File provider options
43
+ saveInterval?: number;
44
+ autoLoad?: boolean;
45
+ autoRestoreOnRestart?: boolean; // Auto restore after restart
46
+ restoreDelay?: number; // Delay before restoring (ms)
47
+ maxBackups?: number; // Max backups per player (default: 5)
48
+ maxTotalBackups?: number; // Max total backups across all players
49
+ autoCleanupBackupsOnStart?: boolean; // Auto delete old backups on restart
50
+ backupRetentionDays?: number; // Delete backups older than N days
51
+ compress?: boolean;
47
52
  filePath?: string;
48
-
49
- // Redis provider options
50
53
  redisUrl?: string;
51
54
  redisPrefix?: string;
52
55
 
53
- // Database options (for custom provider)
54
- save?: (data: Map<string, SerializedPlayer>) => Promise<void>;
55
- load?: () => Promise<Map<string, SerializedPlayer>>;
56
- delete?: (guildId: string) => Promise<void>;
56
+ // Database provider functions (different signatures)
57
+ save?: ((key: string, data: any) => Promise<void>) | ((data: any) => Promise<void>);
58
+ load?: ((key: string) => Promise<any>) | (() => Promise<any>);
59
+ delete?: (key: string) => Promise<void>;
57
60
  list?: () => Promise<string[]>;
58
61
  }
59
62
 
60
63
  export interface PersistenceProvider {
61
- save(key: string, data: any, compress?: boolean): Promise<void>;
64
+ save(key: string, data: any): Promise<void>;
62
65
  load(key: string): Promise<any>;
63
66
  delete(key: string): Promise<void>;
64
67
  list(): Promise<string[]>;
65
68
  }
69
+
70
+ // Track destroyed players for restart detection
71
+ export interface DestroyedRecord {
72
+ guildId: string;
73
+ destroyedAt: number;
74
+ reason?: string;
75
+ }
76
+
77
+ export interface BackupInfo {
78
+ key: string;
79
+ path: string;
80
+ timestamp: number;
81
+ size: number;
82
+ isCompressed: boolean;
83
+ }