import { VideoItem } from '../../viewmodel/VideoItem'; import { PlayerState, PlayMode } from './PlayerStateModel'; import { PreferencesUtil } from '@pura/harmony-utils'; import { common } from '@kit.AbilityKit'; /** * 存储键名接口 */ interface StorageKeys { PLAYLIST: string; PLAYER_STATE: string; PROGRESS_PREFIX: string; COMPLETED_SONGS: string; } /** * 播放进度数据接口 */ export interface PlaybackProgress { songId: string; filePath: string; position: number; duration: number; timestamp: number; completed: boolean; } /** * 播放列表数据接口 */ export interface PlaylistData { songs: VideoItem[]; currentIndex: number; playMode: PlayMode; timestamp: number; } /** * 播放状态数据接口 */ export interface PlayerStateData { isPlaying: boolean; isPaused: boolean; currentIndex: number; playMode: PlayMode; volume: number; speed: number; timestamp: number; } /** * 数据统计信息接口 */ export interface DataStats { playlistSize: number; progressRecords: number; completedSongs: number; lastUpdate: number; } /** * 同步状态接口 */ export interface SyncStatus { autoSyncEnabled: boolean; lastSyncTimestamp: number; syncInterval: number; } /** * 数据持久化服务接口 */ export interface IDataPersistenceService { // 播放进度相关 savePlaybackProgress(songId: string, filePath: string, position: number, duration: number): Promise; loadPlaybackProgress(songId: string): Promise; clearPlaybackProgress(songId: string): Promise; clearCompletedProgress(): Promise; // 播放列表相关 savePlaylist(playlist: VideoItem[], currentIndex: number, playMode: PlayMode): Promise; loadPlaylist(): Promise; // 播放状态相关 savePlayerState(state: PlayerState): Promise; loadPlayerState(): Promise; // 初始化和清理 initialize(context: common.UIAbilityContext): Promise; reinitialize(context: common.UIAbilityContext): Promise; isContextValid(): boolean; clearAllData(): Promise; } /** * 数据持久化服务实现 * 处理播放列表、播放状态和播放进度的持久化存储 */ export class DataPersistenceService implements IDataPersistenceService { private static instance: DataPersistenceService | null = null; private context: common.UIAbilityContext | null = null; private isInitialized: boolean = false; // 存储键名常量 private static readonly KEYS: StorageKeys = { PLAYLIST: 'player_playlist', PLAYER_STATE: 'player_state', PROGRESS_PREFIX: 'playback_progress_', COMPLETED_SONGS: 'completed_songs' }; // 播放完成阈值(5秒) private static readonly COMPLETION_THRESHOLD = 5000; private constructor() { } public static getInstance(): DataPersistenceService { if (!DataPersistenceService.instance) { DataPersistenceService.instance = new DataPersistenceService(); } return DataPersistenceService.instance; } async initialize(context: common.UIAbilityContext): Promise { if (this.isInitialized && this.context) { return; } try { // 验证context是否有效 if (!context) { throw new Error('Context is null'); } this.context = context; this.isInitialized = true; } catch (error) { throw new Error; } } /** * 重新初始化context(用于context失效后的恢复) */ async reinitialize(context: common.UIAbilityContext): Promise { try { if (!context) { throw new Error('Context is null'); } this.context = context; this.isInitialized = true; } catch (error) { throw new Error; } } /** * 检查context是否有效 */ isContextValid(): boolean { return this.isInitialized && this.context !== null; } // ==================== 播放进度记忆功能实现 ==================== /** * 保存播放进度 * 实现播放位置的自动保存机制 */ async savePlaybackProgress(songId: string, filePath: string, position: number, duration: number): Promise { try { if (!this.isInitialized) { throw new Error('DataPersistenceService not initialized'); } // 检查是否接近播放完成 const isCompleted = duration > 0 && (duration - position) < DataPersistenceService.COMPLETION_THRESHOLD; const progressData: PlaybackProgress = { songId, filePath, position: isCompleted ? 0 : position, // 播放完成时保存为0 duration, timestamp: Date.now(), completed: isCompleted }; const key = DataPersistenceService.KEYS.PROGRESS_PREFIX + songId; // 首先保存到AppStorage以便快速访问 AppStorage.setOrCreate(key, progressData); // 如果context有效,尝试保存到PreferencesUtil if (this.context) { try { PreferencesUtil.putSync(key, JSON.stringify(progressData)); } catch (error) { // 即使PreferencesUtil失败,AppStorage已保存,不抛出异常 } } // 如果播放完成,添加到已完成列表 if (isCompleted) { await this.addToCompletedSongs(songId); } } catch (error) { throw new Error; } } /** * 加载播放进度 * 实现应用重启后的播放位置恢复 */ async loadPlaybackProgress(songId: string): Promise { try { if (!this.isInitialized) { throw new Error('DataPersistenceService not initialized'); } const key = DataPersistenceService.KEYS.PROGRESS_PREFIX + songId; // 优先从AppStorage获取(内存中的数据) let progressData = AppStorage.get(key); if (!progressData && this.context) { // 从PreferencesUtil获取持久化数据,增加重试机制 const maxRetries = 2; let retryCount = 0; while (retryCount < maxRetries) { try { const progressStr = PreferencesUtil.getStringSync(key, ''); if (progressStr) { try { const parsedData = JSON.parse(progressStr) as PlaybackProgress; progressData = parsedData; // 同步到AppStorage AppStorage.setOrCreate(key, progressData); break; // 成功,跳出循环 } catch (parseError) { return null; } } else { break; // 没有数据 } } catch (preferencesError) { if (preferencesError.toString().includes('context is invalid') && retryCount < maxRetries - 1) { await new Promise(resolve => setTimeout(resolve, 50)); } else { break; } retryCount++; } } } if (progressData) { return progressData; } return null; } catch (error) { return null; } } /** * 清除播放进度 * 添加播放完成时的进度清理逻辑 */ async clearPlaybackProgress(songId: string): Promise { try { if (!this.isInitialized) { throw new Error('DataPersistenceService not initialized'); } const key = DataPersistenceService.KEYS.PROGRESS_PREFIX + songId; // 从PreferencesUtil删除 PreferencesUtil.deleteSync(key); // 从AppStorage删除 AppStorage.delete(key); } catch (error) { throw new Error; } } /** * 清理已完成播放的进度记录 */ async clearCompletedProgress(): Promise { try { if (!this.isInitialized) { throw new Error('DataPersistenceService not initialized'); } // 获取已完成的歌曲列表 const completedSongs = await this.getCompletedSongs(); // 清理每个已完成歌曲的进度记录 for (const songId of completedSongs) { await this.clearPlaybackProgress(songId); } // 清空已完成列表 PreferencesUtil.deleteSync(DataPersistenceService.KEYS.COMPLETED_SONGS); AppStorage.delete(DataPersistenceService.KEYS.COMPLETED_SONGS); } catch (error) { throw new Error; } } /** * 添加歌曲到已完成列表 */ private async addToCompletedSongs(songId: string): Promise { try { const completedSongs = await this.getCompletedSongs(); if (!completedSongs.includes(songId)) { completedSongs.push(songId); PreferencesUtil.putSync(DataPersistenceService.KEYS.COMPLETED_SONGS, JSON.stringify(completedSongs)); AppStorage.setOrCreate(DataPersistenceService.KEYS.COMPLETED_SONGS, completedSongs); } } catch (error) { } } /** * 获取已完成播放的歌曲列表 */ private async getCompletedSongs(): Promise { try { // 优先从AppStorage获取 let completedSongs = AppStorage.get(DataPersistenceService.KEYS.COMPLETED_SONGS); if (!completedSongs) { // 从PreferencesUtil获取 const completedStr = PreferencesUtil.getStringSync(DataPersistenceService.KEYS.COMPLETED_SONGS, '[]'); try { const parsedData = JSON.parse(completedStr) as string[]; completedSongs = parsedData; AppStorage.setOrCreate(DataPersistenceService.KEYS.COMPLETED_SONGS, completedSongs); } catch (parseError) { completedSongs = []; } } return completedSongs || []; } catch (error) { return []; } } // ==================== 播放列表持久化功能实现 ==================== /** * 保存播放列表 */ async savePlaylist(playlist: VideoItem[], currentIndex: number, playMode: PlayMode): Promise { try { if (!this.isInitialized) { throw new Error('DataPersistenceService not initialized'); } const playlistData: PlaylistData = { songs: playlist, currentIndex, playMode, timestamp: Date.now() }; // 首先保存到AppStorage(确保内存中有数据) AppStorage.setOrCreate(DataPersistenceService.KEYS.PLAYLIST, playlistData); // 如果context有效,尝试保存到PreferencesUtil if (this.context) { const maxRetries = 3; let retryCount = 0; let lastError: Error | null = null; while (retryCount < maxRetries) { try { PreferencesUtil.putSync(DataPersistenceService.KEYS.PLAYLIST, JSON.stringify(playlistData)); break; // 成功,跳出循环 } catch (error) { lastError = error; if (error.toString().includes('context is invalid')) { await new Promise(resolve => setTimeout(resolve, 100 * (retryCount + 1))); } else { // 其他错误,不重试 break; } retryCount++; } } if (retryCount >= maxRetries && lastError) { } } else { } } catch (error) { throw new Error; } } /** * 加载播放列表 */ async loadPlaylist(): Promise { try { if (!this.isInitialized) { throw new Error('DataPersistenceService not initialized'); } // 优先从AppStorage获取 let playlistData = AppStorage.get(DataPersistenceService.KEYS.PLAYLIST); if (!playlistData) { // 检查context是否有效 if (!this.context) { return null; } // 从PreferencesUtil获取,增加重试机制 const maxRetries = 3; let retryCount = 0; let lastError: Error | null = null; while (retryCount < maxRetries) { try { const playlistStr = PreferencesUtil.getStringSync(DataPersistenceService.KEYS.PLAYLIST, ''); if (playlistStr) { try { const parsedData = JSON.parse(playlistStr) as PlaylistData; playlistData = parsedData; // 同步到AppStorage AppStorage.setOrCreate(DataPersistenceService.KEYS.PLAYLIST, playlistData); break; // 成功,跳出循环 } catch (parseError) { return null; } } else { // 空字符串,没有数据 break; } } catch (preferencesError) { lastError = preferencesError; // 如果是context无效错误,增加等待时间 if (preferencesError.toString().includes('context is invalid')) { await new Promise(resolve => setTimeout(resolve, 100 * (retryCount + 1))); } else { // 其他错误,不重试 break; } retryCount++; } } // 如果所有重试都失败了 if (retryCount >= maxRetries && lastError) { return null; } } if (playlistData) { return playlistData; } return null; } catch (error) { return null; } } // ==================== 播放状态持久化功能实现 ==================== /** * 保存播放状态 */ async savePlayerState(state: PlayerState): Promise { try { if (!this.isInitialized) { throw new Error('DataPersistenceService not initialized'); } const stateData: PlayerStateData = { isPlaying: state.isPlaying, isPaused: state.isPaused, currentIndex: state.currentIndex, playMode: state.playMode, volume: state.volume, speed: state.speed, timestamp: Date.now() }; // 首先保存到AppStorage(确保内存中有数据) AppStorage.setOrCreate(DataPersistenceService.KEYS.PLAYER_STATE, stateData); // 如果context有效,尝试保存到PreferencesUtil if (this.context) { const maxRetries = 3; let retryCount = 0; let lastError: Error | null = null; while (retryCount < maxRetries) { try { PreferencesUtil.putSync(DataPersistenceService.KEYS.PLAYER_STATE, JSON.stringify(stateData)); break; // 成功,跳出循环 } catch (error) { lastError = error; if (error.toString().includes('context is invalid')) { await new Promise(resolve => setTimeout(resolve, 100 * (retryCount + 1))); } else { // 其他错误,不重试 break; } retryCount++; } } if (retryCount >= maxRetries && lastError) { } } else { } } catch (error) { throw new Error; } } /** * 加载播放状态 */ async loadPlayerState(): Promise { try { if (!this.isInitialized) { throw new Error('DataPersistenceService not initialized'); } // 优先从AppStorage获取 let stateData = AppStorage.get(DataPersistenceService.KEYS.PLAYER_STATE); if (!stateData) { // 检查context是否有效 if (!this.context) { return null; } // 从PreferencesUtil获取,增加重试机制 const maxRetries = 3; let retryCount = 0; let lastError: Error | null = null; while (retryCount < maxRetries) { try { const stateStr = PreferencesUtil.getStringSync(DataPersistenceService.KEYS.PLAYER_STATE, ''); if (stateStr) { try { const parsedData = JSON.parse(stateStr) as PlayerStateData; stateData = parsedData; // 同步到AppStorage AppStorage.setOrCreate(DataPersistenceService.KEYS.PLAYER_STATE, stateData); break; // 成功,跳出循环 } catch (parseError) { return null; } } else { // 空字符串,没有数据 break; } } catch (preferencesError) { lastError = preferencesError; // 如果是context无效错误,增加等待时间 if (preferencesError.toString().includes('context is invalid')) { await new Promise(resolve => setTimeout(resolve, 100 * (retryCount + 1))); } else { // 其他错误,不重试 break; } retryCount++; } } // 如果所有重试都失败了 if (retryCount >= maxRetries && lastError) { return null; } } if (stateData) { return stateData; } return null; } catch (error) { return null; } } // ==================== 数据清理和维护功能 ==================== /** * 清除所有持久化数据 */ async clearAllData(): Promise { try { if (!this.isInitialized) { throw new Error('DataPersistenceService not initialized'); } // 清除播放列表 PreferencesUtil.deleteSync(DataPersistenceService.KEYS.PLAYLIST); AppStorage.delete(DataPersistenceService.KEYS.PLAYLIST); // 清除播放状态 PreferencesUtil.deleteSync(DataPersistenceService.KEYS.PLAYER_STATE); AppStorage.delete(DataPersistenceService.KEYS.PLAYER_STATE); // 清除所有播放进度 await this.clearCompletedProgress(); } catch (error) { throw new Error; } } /** * 数据同步检查 * 确保AppStorage和PreferencesUtil数据一致性 */ async syncData(): Promise { try { if (!this.isInitialized) { return; } // 同步播放列表数据 const playlistData = await this.loadPlaylist(); if (playlistData) { AppStorage.setOrCreate(DataPersistenceService.KEYS.PLAYLIST, playlistData); } // 同步播放状态数据 const stateData = await this.loadPlayerState(); if (stateData) { AppStorage.setOrCreate(DataPersistenceService.KEYS.PLAYER_STATE, stateData); } } catch (error) { } } /** * 获取数据统计信息 */ async getDataStats(): Promise { try { const playlistData = await this.loadPlaylist(); const completedSongs = await this.getCompletedSongs(); const stats: DataStats = { playlistSize: playlistData?.songs.length || 0, progressRecords: 0, // 这里可以扩展统计所有进度记录 completedSongs: completedSongs.length, lastUpdate: Math.max( playlistData?.timestamp || 0, (await this.loadPlayerState())?.timestamp || 0 ) }; return stats; } catch (error) { const errorStats: DataStats = { playlistSize: 0, progressRecords: 0, completedSongs: 0, lastUpdate: 0 }; return errorStats; } } } /** * 播放列表同步服务 * 实现与PreferencesUtil的数据同步和数据变化监听 */ export class PlaylistSyncService { private static instance: PlaylistSyncService | null = null; private dataPersistence: DataPersistenceService; private syncListeners: Set = new Set(); private autoSyncEnabled: boolean = true; private syncTimer: number = -1; private lastSyncTimestamp: number = 0; // 同步间隔(毫秒) private static readonly SYNC_INTERVAL = 5000; // 5秒 private constructor() { this.dataPersistence = DataPersistenceService.getInstance(); } public static getInstance(): PlaylistSyncService { if (!PlaylistSyncService.instance) { PlaylistSyncService.instance = new PlaylistSyncService(); } return PlaylistSyncService.instance; } /** * 初始化同步服务 */ async initialize(context: common.UIAbilityContext): Promise { try { await this.dataPersistence.initialize(context); if (this.autoSyncEnabled) { this.startAutoSync(); } } catch (error) { throw new Error; } } /** * 同步播放列表到持久化存储 * 确保与LocalMusic的播放列表数据一致性 */ async syncPlaylistToStorage(playlist: VideoItem[], currentIndex: number, playMode: PlayMode): Promise { try { // 保存到持久化存储 await this.dataPersistence.savePlaylist(playlist, currentIndex, playMode); // 更新同步时间戳 this.lastSyncTimestamp = Date.now(); // 通知监听器 this.notifyPlaylistSynced(playlist, currentIndex, playMode); } catch (error) { throw new Error; } } /** * 从持久化存储同步播放列表 */ async syncPlaylistFromStorage(): Promise { try { const playlistData = await this.dataPersistence.loadPlaylist(); if (playlistData) { // 通知监听器 this.notifyPlaylistLoaded(playlistData); } return playlistData; } catch (error) { return null; } } /** * 双向同步播放列表 * 比较本地和存储中的数据,选择最新的版本 */ async bidirectionalSync(localPlaylist: VideoItem[], localIndex: number, localPlayMode: PlayMode): Promise { try { const storedData = await this.dataPersistence.loadPlaylist(); // 如果没有存储的数据,使用本地数据 if (!storedData) { await this.syncPlaylistToStorage(localPlaylist, localIndex, localPlayMode); return { songs: localPlaylist, currentIndex: localIndex, playMode: localPlayMode, timestamp: Date.now() }; } // 比较时间戳,使用更新的数据 const localTimestamp = this.lastSyncTimestamp || Date.now(); if (storedData.timestamp > localTimestamp) { // 存储的数据更新,使用存储的数据 return storedData; } else { // 本地数据更新,同步到存储 await this.syncPlaylistToStorage(localPlaylist, localIndex, localPlayMode); return { songs: localPlaylist, currentIndex: localIndex, playMode: localPlayMode, timestamp: Date.now() }; } } catch (error) { // 出错时返回本地数据 return { songs: localPlaylist, currentIndex: localIndex, playMode: localPlayMode, timestamp: Date.now() }; } } /** * 启动自动同步 */ startAutoSync(): void { if (this.syncTimer !== -1) { return; // 已经启动 } this.syncTimer = setInterval(async () => { try { await this.performAutoSync(); } catch (error) { } }, PlaylistSyncService.SYNC_INTERVAL); } /** * 停止自动同步 */ stopAutoSync(): void { if (this.syncTimer !== -1) { clearInterval(this.syncTimer); this.syncTimer = -1; } } /** * 执行自动同步 */ private async performAutoSync(): Promise { try { // 检查数据一致性 await this.dataPersistence.syncData(); // 通知监听器执行同步检查 this.notifyAutoSyncPerformed(); } catch (error) { } } /** * 设置自动同步开关 */ setAutoSyncEnabled(enabled: boolean): void { this.autoSyncEnabled = enabled; if (enabled) { this.startAutoSync(); } else { this.stopAutoSync(); } } /** * 添加同步监听器 */ addSyncListener(listener: PlaylistSyncListener): void { this.syncListeners.add(listener); } /** * 移除同步监听器 */ removeSyncListener(listener: PlaylistSyncListener): void { this.syncListeners.delete(listener); } /** * 通知播放列表已同步到存储 */ private notifyPlaylistSynced(playlist: VideoItem[], currentIndex: number, playMode: PlayMode): void { this.syncListeners.forEach(listener => { try { listener.onPlaylistSynced?.(playlist, currentIndex, playMode); } catch (error) { } }); } /** * 通知播放列表已从存储加载 */ private notifyPlaylistLoaded(playlistData: PlaylistData): void { this.syncListeners.forEach(listener => { try { listener.onPlaylistLoaded?.(playlistData); } catch (error) { } }); } /** * 通知自动同步已执行 */ private notifyAutoSyncPerformed(): void { this.syncListeners.forEach(listener => { try { listener.onAutoSyncPerformed?.(); } catch (error) { } }); } /** * 获取同步状态 */ getSyncStatus(): SyncStatus { const status: SyncStatus = { autoSyncEnabled: this.autoSyncEnabled, lastSyncTimestamp: this.lastSyncTimestamp, syncInterval: PlaylistSyncService.SYNC_INTERVAL }; return status; } /** * 清理资源 */ release(): void { this.stopAutoSync(); this.syncListeners.clear(); } } /** * 播放列表同步监听器接口 */ export interface PlaylistSyncListener { onPlaylistSynced?(playlist: VideoItem[], currentIndex: number, playMode: PlayMode): void; onPlaylistLoaded?(playlistData: PlaylistData): void; onAutoSyncPerformed?(): void; onSyncError?(error: Error): void; }