DataPersistenceService.ets 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071
  1. import { VideoItem } from '../../viewmodel/VideoItem';
  2. import { PlayerState, PlayMode } from './PlayerStateModel';
  3. import { PreferencesUtil } from '@pura/harmony-utils';
  4. import { common } from '@kit.AbilityKit';
  5. /**
  6. * 存储键名接口
  7. */
  8. interface StorageKeys {
  9. PLAYLIST: string;
  10. PLAYER_STATE: string;
  11. PROGRESS_PREFIX: string;
  12. COMPLETED_SONGS: string;
  13. }
  14. /**
  15. * 播放进度数据接口
  16. */
  17. export interface PlaybackProgress {
  18. songId: string;
  19. filePath: string;
  20. position: number;
  21. duration: number;
  22. timestamp: number;
  23. completed: boolean;
  24. }
  25. /**
  26. * 播放列表数据接口
  27. */
  28. export interface PlaylistData {
  29. songs: VideoItem[];
  30. currentIndex: number;
  31. playMode: PlayMode;
  32. timestamp: number;
  33. }
  34. /**
  35. * 播放状态数据接口
  36. */
  37. export interface PlayerStateData {
  38. isPlaying: boolean;
  39. isPaused: boolean;
  40. currentIndex: number;
  41. playMode: PlayMode;
  42. volume: number;
  43. speed: number;
  44. timestamp: number;
  45. }
  46. /**
  47. * 数据统计信息接口
  48. */
  49. export interface DataStats {
  50. playlistSize: number;
  51. progressRecords: number;
  52. completedSongs: number;
  53. lastUpdate: number;
  54. }
  55. /**
  56. * 同步状态接口
  57. */
  58. export interface SyncStatus {
  59. autoSyncEnabled: boolean;
  60. lastSyncTimestamp: number;
  61. syncInterval: number;
  62. }
  63. /**
  64. * 数据持久化服务接口
  65. */
  66. export interface IDataPersistenceService {
  67. // 播放进度相关
  68. savePlaybackProgress(songId: string, filePath: string, position: number, duration: number): Promise<void>;
  69. loadPlaybackProgress(songId: string): Promise<PlaybackProgress | null>;
  70. clearPlaybackProgress(songId: string): Promise<void>;
  71. clearCompletedProgress(): Promise<void>;
  72. // 播放列表相关
  73. savePlaylist(playlist: VideoItem[], currentIndex: number, playMode: PlayMode): Promise<void>;
  74. loadPlaylist(): Promise<PlaylistData | null>;
  75. // 播放状态相关
  76. savePlayerState(state: PlayerState): Promise<void>;
  77. loadPlayerState(): Promise<PlayerStateData | null>;
  78. // 初始化和清理
  79. initialize(context: common.UIAbilityContext): Promise<void>;
  80. reinitialize(context: common.UIAbilityContext): Promise<void>;
  81. isContextValid(): boolean;
  82. clearAllData(): Promise<void>;
  83. }
  84. /**
  85. * 数据持久化服务实现
  86. * 处理播放列表、播放状态和播放进度的持久化存储
  87. */
  88. export class DataPersistenceService implements IDataPersistenceService {
  89. private static instance: DataPersistenceService | null = null;
  90. private context: common.UIAbilityContext | null = null;
  91. private isInitialized: boolean = false;
  92. // 存储键名常量
  93. private static readonly KEYS: StorageKeys = {
  94. PLAYLIST: 'player_playlist',
  95. PLAYER_STATE: 'player_state',
  96. PROGRESS_PREFIX: 'playback_progress_',
  97. COMPLETED_SONGS: 'completed_songs'
  98. };
  99. // 播放完成阈值(5秒)
  100. private static readonly COMPLETION_THRESHOLD = 5000;
  101. private constructor() {
  102. }
  103. public static getInstance(): DataPersistenceService {
  104. if (!DataPersistenceService.instance) {
  105. DataPersistenceService.instance = new DataPersistenceService();
  106. }
  107. return DataPersistenceService.instance;
  108. }
  109. async initialize(context: common.UIAbilityContext): Promise<void> {
  110. if (this.isInitialized && this.context) {
  111. return;
  112. }
  113. try {
  114. // 验证context是否有效
  115. if (!context) {
  116. throw new Error('Context is null');
  117. }
  118. this.context = context;
  119. this.isInitialized = true;
  120. } catch (error) {
  121. throw new Error;
  122. }
  123. }
  124. /**
  125. * 重新初始化context(用于context失效后的恢复)
  126. */
  127. async reinitialize(context: common.UIAbilityContext): Promise<void> {
  128. try {
  129. if (!context) {
  130. throw new Error('Context is null');
  131. }
  132. this.context = context;
  133. this.isInitialized = true;
  134. } catch (error) {
  135. throw new Error;
  136. }
  137. }
  138. /**
  139. * 检查context是否有效
  140. */
  141. isContextValid(): boolean {
  142. return this.isInitialized && this.context !== null;
  143. }
  144. // ==================== 播放进度记忆功能实现 ====================
  145. /**
  146. * 保存播放进度
  147. * 实现播放位置的自动保存机制
  148. */
  149. async savePlaybackProgress(songId: string, filePath: string, position: number, duration: number): Promise<void> {
  150. try {
  151. if (!this.isInitialized) {
  152. throw new Error('DataPersistenceService not initialized');
  153. }
  154. // 检查是否接近播放完成
  155. const isCompleted = duration > 0 && (duration - position) < DataPersistenceService.COMPLETION_THRESHOLD;
  156. const progressData: PlaybackProgress = {
  157. songId,
  158. filePath,
  159. position: isCompleted ? 0 : position, // 播放完成时保存为0
  160. duration,
  161. timestamp: Date.now(),
  162. completed: isCompleted
  163. };
  164. const key = DataPersistenceService.KEYS.PROGRESS_PREFIX + songId;
  165. // 首先保存到AppStorage以便快速访问
  166. AppStorage.setOrCreate(key, progressData);
  167. // 如果context有效,尝试保存到PreferencesUtil
  168. if (this.context) {
  169. try {
  170. PreferencesUtil.putSync(key, JSON.stringify(progressData));
  171. } catch (error) {
  172. // 即使PreferencesUtil失败,AppStorage已保存,不抛出异常
  173. }
  174. }
  175. // 如果播放完成,添加到已完成列表
  176. if (isCompleted) {
  177. await this.addToCompletedSongs(songId);
  178. }
  179. } catch (error) {
  180. throw new Error;
  181. }
  182. }
  183. /**
  184. * 加载播放进度
  185. * 实现应用重启后的播放位置恢复
  186. */
  187. async loadPlaybackProgress(songId: string): Promise<PlaybackProgress | null> {
  188. try {
  189. if (!this.isInitialized) {
  190. throw new Error('DataPersistenceService not initialized');
  191. }
  192. const key = DataPersistenceService.KEYS.PROGRESS_PREFIX + songId;
  193. // 优先从AppStorage获取(内存中的数据)
  194. let progressData = AppStorage.get<PlaybackProgress>(key);
  195. if (!progressData && this.context) {
  196. // 从PreferencesUtil获取持久化数据,增加重试机制
  197. const maxRetries = 2;
  198. let retryCount = 0;
  199. while (retryCount < maxRetries) {
  200. try {
  201. const progressStr = PreferencesUtil.getStringSync(key, '');
  202. if (progressStr) {
  203. try {
  204. const parsedData = JSON.parse(progressStr) as PlaybackProgress;
  205. progressData = parsedData;
  206. // 同步到AppStorage
  207. AppStorage.setOrCreate(key, progressData);
  208. break; // 成功,跳出循环
  209. } catch (parseError) {
  210. return null;
  211. }
  212. } else {
  213. break; // 没有数据
  214. }
  215. } catch (preferencesError) {
  216. if (preferencesError.toString().includes('context is invalid') && retryCount < maxRetries - 1) {
  217. await new Promise<void>(resolve => setTimeout(resolve, 50));
  218. } else {
  219. break;
  220. }
  221. retryCount++;
  222. }
  223. }
  224. }
  225. if (progressData) {
  226. return progressData;
  227. }
  228. return null;
  229. } catch (error) {
  230. return null;
  231. }
  232. }
  233. /**
  234. * 清除播放进度
  235. * 添加播放完成时的进度清理逻辑
  236. */
  237. async clearPlaybackProgress(songId: string): Promise<void> {
  238. try {
  239. if (!this.isInitialized) {
  240. throw new Error('DataPersistenceService not initialized');
  241. }
  242. const key = DataPersistenceService.KEYS.PROGRESS_PREFIX + songId;
  243. // 从PreferencesUtil删除
  244. PreferencesUtil.deleteSync(key);
  245. // 从AppStorage删除
  246. AppStorage.delete(key);
  247. } catch (error) {
  248. throw new Error;
  249. }
  250. }
  251. /**
  252. * 清理已完成播放的进度记录
  253. */
  254. async clearCompletedProgress(): Promise<void> {
  255. try {
  256. if (!this.isInitialized) {
  257. throw new Error('DataPersistenceService not initialized');
  258. }
  259. // 获取已完成的歌曲列表
  260. const completedSongs = await this.getCompletedSongs();
  261. // 清理每个已完成歌曲的进度记录
  262. for (const songId of completedSongs) {
  263. await this.clearPlaybackProgress(songId);
  264. }
  265. // 清空已完成列表
  266. PreferencesUtil.deleteSync(DataPersistenceService.KEYS.COMPLETED_SONGS);
  267. AppStorage.delete(DataPersistenceService.KEYS.COMPLETED_SONGS);
  268. } catch (error) {
  269. throw new Error;
  270. }
  271. }
  272. /**
  273. * 添加歌曲到已完成列表
  274. */
  275. private async addToCompletedSongs(songId: string): Promise<void> {
  276. try {
  277. const completedSongs = await this.getCompletedSongs();
  278. if (!completedSongs.includes(songId)) {
  279. completedSongs.push(songId);
  280. PreferencesUtil.putSync(DataPersistenceService.KEYS.COMPLETED_SONGS, JSON.stringify(completedSongs));
  281. AppStorage.setOrCreate(DataPersistenceService.KEYS.COMPLETED_SONGS, completedSongs);
  282. }
  283. } catch (error) {
  284. }
  285. }
  286. /**
  287. * 获取已完成播放的歌曲列表
  288. */
  289. private async getCompletedSongs(): Promise<string[]> {
  290. try {
  291. // 优先从AppStorage获取
  292. let completedSongs = AppStorage.get<string[]>(DataPersistenceService.KEYS.COMPLETED_SONGS);
  293. if (!completedSongs) {
  294. // 从PreferencesUtil获取
  295. const completedStr = PreferencesUtil.getStringSync(DataPersistenceService.KEYS.COMPLETED_SONGS, '[]');
  296. try {
  297. const parsedData = JSON.parse(completedStr) as string[];
  298. completedSongs = parsedData;
  299. AppStorage.setOrCreate(DataPersistenceService.KEYS.COMPLETED_SONGS, completedSongs);
  300. } catch (parseError) {
  301. completedSongs = [];
  302. }
  303. }
  304. return completedSongs || [];
  305. } catch (error) {
  306. return [];
  307. }
  308. }
  309. // ==================== 播放列表持久化功能实现 ====================
  310. /**
  311. * 保存播放列表
  312. */
  313. async savePlaylist(playlist: VideoItem[], currentIndex: number, playMode: PlayMode): Promise<void> {
  314. try {
  315. if (!this.isInitialized) {
  316. throw new Error('DataPersistenceService not initialized');
  317. }
  318. const playlistData: PlaylistData = {
  319. songs: playlist,
  320. currentIndex,
  321. playMode,
  322. timestamp: Date.now()
  323. };
  324. // 首先保存到AppStorage(确保内存中有数据)
  325. AppStorage.setOrCreate(DataPersistenceService.KEYS.PLAYLIST, playlistData);
  326. // 如果context有效,尝试保存到PreferencesUtil
  327. if (this.context) {
  328. const maxRetries = 3;
  329. let retryCount = 0;
  330. let lastError: Error | null = null;
  331. while (retryCount < maxRetries) {
  332. try {
  333. PreferencesUtil.putSync(DataPersistenceService.KEYS.PLAYLIST, JSON.stringify(playlistData));
  334. break; // 成功,跳出循环
  335. } catch (error) {
  336. lastError = error;
  337. if (error.toString().includes('context is invalid')) {
  338. await new Promise<void>(resolve => setTimeout(resolve, 100 * (retryCount + 1)));
  339. } else {
  340. // 其他错误,不重试
  341. break;
  342. }
  343. retryCount++;
  344. }
  345. }
  346. if (retryCount >= maxRetries && lastError) {
  347. }
  348. } else {
  349. }
  350. } catch (error) {
  351. throw new Error;
  352. }
  353. }
  354. /**
  355. * 加载播放列表
  356. */
  357. async loadPlaylist(): Promise<PlaylistData | null> {
  358. try {
  359. if (!this.isInitialized) {
  360. throw new Error('DataPersistenceService not initialized');
  361. }
  362. // 优先从AppStorage获取
  363. let playlistData = AppStorage.get<PlaylistData>(DataPersistenceService.KEYS.PLAYLIST);
  364. if (!playlistData) {
  365. // 检查context是否有效
  366. if (!this.context) {
  367. return null;
  368. }
  369. // 从PreferencesUtil获取,增加重试机制
  370. const maxRetries = 3;
  371. let retryCount = 0;
  372. let lastError: Error | null = null;
  373. while (retryCount < maxRetries) {
  374. try {
  375. const playlistStr = PreferencesUtil.getStringSync(DataPersistenceService.KEYS.PLAYLIST, '');
  376. if (playlistStr) {
  377. try {
  378. const parsedData = JSON.parse(playlistStr) as PlaylistData;
  379. playlistData = parsedData;
  380. // 同步到AppStorage
  381. AppStorage.setOrCreate(DataPersistenceService.KEYS.PLAYLIST, playlistData);
  382. break; // 成功,跳出循环
  383. } catch (parseError) {
  384. return null;
  385. }
  386. } else {
  387. // 空字符串,没有数据
  388. break;
  389. }
  390. } catch (preferencesError) {
  391. lastError = preferencesError;
  392. // 如果是context无效错误,增加等待时间
  393. if (preferencesError.toString().includes('context is invalid')) {
  394. await new Promise<void>(resolve => setTimeout(resolve, 100 * (retryCount + 1)));
  395. } else {
  396. // 其他错误,不重试
  397. break;
  398. }
  399. retryCount++;
  400. }
  401. }
  402. // 如果所有重试都失败了
  403. if (retryCount >= maxRetries && lastError) {
  404. return null;
  405. }
  406. }
  407. if (playlistData) {
  408. return playlistData;
  409. }
  410. return null;
  411. } catch (error) {
  412. return null;
  413. }
  414. }
  415. // ==================== 播放状态持久化功能实现 ====================
  416. /**
  417. * 保存播放状态
  418. */
  419. async savePlayerState(state: PlayerState): Promise<void> {
  420. try {
  421. if (!this.isInitialized) {
  422. throw new Error('DataPersistenceService not initialized');
  423. }
  424. const stateData: PlayerStateData = {
  425. isPlaying: state.isPlaying,
  426. isPaused: state.isPaused,
  427. currentIndex: state.currentIndex,
  428. playMode: state.playMode,
  429. volume: state.volume,
  430. speed: state.speed,
  431. timestamp: Date.now()
  432. };
  433. // 首先保存到AppStorage(确保内存中有数据)
  434. AppStorage.setOrCreate(DataPersistenceService.KEYS.PLAYER_STATE, stateData);
  435. // 如果context有效,尝试保存到PreferencesUtil
  436. if (this.context) {
  437. const maxRetries = 3;
  438. let retryCount = 0;
  439. let lastError: Error | null = null;
  440. while (retryCount < maxRetries) {
  441. try {
  442. PreferencesUtil.putSync(DataPersistenceService.KEYS.PLAYER_STATE, JSON.stringify(stateData));
  443. break; // 成功,跳出循环
  444. } catch (error) {
  445. lastError = error;
  446. if (error.toString().includes('context is invalid')) {
  447. await new Promise<void>(resolve => setTimeout(resolve, 100 * (retryCount + 1)));
  448. } else {
  449. // 其他错误,不重试
  450. break;
  451. }
  452. retryCount++;
  453. }
  454. }
  455. if (retryCount >= maxRetries && lastError) {
  456. }
  457. } else {
  458. }
  459. } catch (error) {
  460. throw new Error;
  461. }
  462. }
  463. /**
  464. * 加载播放状态
  465. */
  466. async loadPlayerState(): Promise<PlayerStateData | null> {
  467. try {
  468. if (!this.isInitialized) {
  469. throw new Error('DataPersistenceService not initialized');
  470. }
  471. // 优先从AppStorage获取
  472. let stateData = AppStorage.get<PlayerStateData>(DataPersistenceService.KEYS.PLAYER_STATE);
  473. if (!stateData) {
  474. // 检查context是否有效
  475. if (!this.context) {
  476. return null;
  477. }
  478. // 从PreferencesUtil获取,增加重试机制
  479. const maxRetries = 3;
  480. let retryCount = 0;
  481. let lastError: Error | null = null;
  482. while (retryCount < maxRetries) {
  483. try {
  484. const stateStr = PreferencesUtil.getStringSync(DataPersistenceService.KEYS.PLAYER_STATE, '');
  485. if (stateStr) {
  486. try {
  487. const parsedData = JSON.parse(stateStr) as PlayerStateData;
  488. stateData = parsedData;
  489. // 同步到AppStorage
  490. AppStorage.setOrCreate(DataPersistenceService.KEYS.PLAYER_STATE, stateData);
  491. break; // 成功,跳出循环
  492. } catch (parseError) {
  493. return null;
  494. }
  495. } else {
  496. // 空字符串,没有数据
  497. break;
  498. }
  499. } catch (preferencesError) {
  500. lastError = preferencesError;
  501. // 如果是context无效错误,增加等待时间
  502. if (preferencesError.toString().includes('context is invalid')) {
  503. await new Promise<void>(resolve => setTimeout(resolve, 100 * (retryCount + 1)));
  504. } else {
  505. // 其他错误,不重试
  506. break;
  507. }
  508. retryCount++;
  509. }
  510. }
  511. // 如果所有重试都失败了
  512. if (retryCount >= maxRetries && lastError) {
  513. return null;
  514. }
  515. }
  516. if (stateData) {
  517. return stateData;
  518. }
  519. return null;
  520. } catch (error) {
  521. return null;
  522. }
  523. }
  524. // ==================== 数据清理和维护功能 ====================
  525. /**
  526. * 清除所有持久化数据
  527. */
  528. async clearAllData(): Promise<void> {
  529. try {
  530. if (!this.isInitialized) {
  531. throw new Error('DataPersistenceService not initialized');
  532. }
  533. // 清除播放列表
  534. PreferencesUtil.deleteSync(DataPersistenceService.KEYS.PLAYLIST);
  535. AppStorage.delete(DataPersistenceService.KEYS.PLAYLIST);
  536. // 清除播放状态
  537. PreferencesUtil.deleteSync(DataPersistenceService.KEYS.PLAYER_STATE);
  538. AppStorage.delete(DataPersistenceService.KEYS.PLAYER_STATE);
  539. // 清除所有播放进度
  540. await this.clearCompletedProgress();
  541. } catch (error) {
  542. throw new Error;
  543. }
  544. }
  545. /**
  546. * 数据同步检查
  547. * 确保AppStorage和PreferencesUtil数据一致性
  548. */
  549. async syncData(): Promise<void> {
  550. try {
  551. if (!this.isInitialized) {
  552. return;
  553. }
  554. // 同步播放列表数据
  555. const playlistData = await this.loadPlaylist();
  556. if (playlistData) {
  557. AppStorage.setOrCreate(DataPersistenceService.KEYS.PLAYLIST, playlistData);
  558. }
  559. // 同步播放状态数据
  560. const stateData = await this.loadPlayerState();
  561. if (stateData) {
  562. AppStorage.setOrCreate(DataPersistenceService.KEYS.PLAYER_STATE, stateData);
  563. }
  564. } catch (error) {
  565. }
  566. }
  567. /**
  568. * 获取数据统计信息
  569. */
  570. async getDataStats(): Promise<DataStats> {
  571. try {
  572. const playlistData = await this.loadPlaylist();
  573. const completedSongs = await this.getCompletedSongs();
  574. const stats: DataStats = {
  575. playlistSize: playlistData?.songs.length || 0,
  576. progressRecords: 0, // 这里可以扩展统计所有进度记录
  577. completedSongs: completedSongs.length,
  578. lastUpdate: Math.max(
  579. playlistData?.timestamp || 0,
  580. (await this.loadPlayerState())?.timestamp || 0
  581. )
  582. };
  583. return stats;
  584. } catch (error) {
  585. const errorStats: DataStats = {
  586. playlistSize: 0,
  587. progressRecords: 0,
  588. completedSongs: 0,
  589. lastUpdate: 0
  590. };
  591. return errorStats;
  592. }
  593. }
  594. }
  595. /**
  596. * 播放列表同步服务
  597. * 实现与PreferencesUtil的数据同步和数据变化监听
  598. */
  599. export class PlaylistSyncService {
  600. private static instance: PlaylistSyncService | null = null;
  601. private dataPersistence: DataPersistenceService;
  602. private syncListeners: Set<PlaylistSyncListener> = new Set();
  603. private autoSyncEnabled: boolean = true;
  604. private syncTimer: number = -1;
  605. private lastSyncTimestamp: number = 0;
  606. // 同步间隔(毫秒)
  607. private static readonly SYNC_INTERVAL = 5000; // 5秒
  608. private constructor() {
  609. this.dataPersistence = DataPersistenceService.getInstance();
  610. }
  611. public static getInstance(): PlaylistSyncService {
  612. if (!PlaylistSyncService.instance) {
  613. PlaylistSyncService.instance = new PlaylistSyncService();
  614. }
  615. return PlaylistSyncService.instance;
  616. }
  617. /**
  618. * 初始化同步服务
  619. */
  620. async initialize(context: common.UIAbilityContext): Promise<void> {
  621. try {
  622. await this.dataPersistence.initialize(context);
  623. if (this.autoSyncEnabled) {
  624. this.startAutoSync();
  625. }
  626. } catch (error) {
  627. throw new Error;
  628. }
  629. }
  630. /**
  631. * 同步播放列表到持久化存储
  632. * 确保与LocalMusic的播放列表数据一致性
  633. */
  634. async syncPlaylistToStorage(playlist: VideoItem[], currentIndex: number, playMode: PlayMode): Promise<void> {
  635. try {
  636. // 保存到持久化存储
  637. await this.dataPersistence.savePlaylist(playlist, currentIndex, playMode);
  638. // 更新同步时间戳
  639. this.lastSyncTimestamp = Date.now();
  640. // 通知监听器
  641. this.notifyPlaylistSynced(playlist, currentIndex, playMode);
  642. } catch (error) {
  643. throw new Error;
  644. }
  645. }
  646. /**
  647. * 从持久化存储同步播放列表
  648. */
  649. async syncPlaylistFromStorage(): Promise<PlaylistData | null> {
  650. try {
  651. const playlistData = await this.dataPersistence.loadPlaylist();
  652. if (playlistData) {
  653. // 通知监听器
  654. this.notifyPlaylistLoaded(playlistData);
  655. }
  656. return playlistData;
  657. } catch (error) {
  658. return null;
  659. }
  660. }
  661. /**
  662. * 双向同步播放列表
  663. * 比较本地和存储中的数据,选择最新的版本
  664. */
  665. async bidirectionalSync(localPlaylist: VideoItem[], localIndex: number, localPlayMode: PlayMode): Promise<PlaylistData> {
  666. try {
  667. const storedData = await this.dataPersistence.loadPlaylist();
  668. // 如果没有存储的数据,使用本地数据
  669. if (!storedData) {
  670. await this.syncPlaylistToStorage(localPlaylist, localIndex, localPlayMode);
  671. return {
  672. songs: localPlaylist,
  673. currentIndex: localIndex,
  674. playMode: localPlayMode,
  675. timestamp: Date.now()
  676. };
  677. }
  678. // 比较时间戳,使用更新的数据
  679. const localTimestamp = this.lastSyncTimestamp || Date.now();
  680. if (storedData.timestamp > localTimestamp) {
  681. // 存储的数据更新,使用存储的数据
  682. return storedData;
  683. } else {
  684. // 本地数据更新,同步到存储
  685. await this.syncPlaylistToStorage(localPlaylist, localIndex, localPlayMode);
  686. return {
  687. songs: localPlaylist,
  688. currentIndex: localIndex,
  689. playMode: localPlayMode,
  690. timestamp: Date.now()
  691. };
  692. }
  693. } catch (error) {
  694. // 出错时返回本地数据
  695. return {
  696. songs: localPlaylist,
  697. currentIndex: localIndex,
  698. playMode: localPlayMode,
  699. timestamp: Date.now()
  700. };
  701. }
  702. }
  703. /**
  704. * 启动自动同步
  705. */
  706. startAutoSync(): void {
  707. if (this.syncTimer !== -1) {
  708. return; // 已经启动
  709. }
  710. this.syncTimer = setInterval(async () => {
  711. try {
  712. await this.performAutoSync();
  713. } catch (error) {
  714. }
  715. }, PlaylistSyncService.SYNC_INTERVAL);
  716. }
  717. /**
  718. * 停止自动同步
  719. */
  720. stopAutoSync(): void {
  721. if (this.syncTimer !== -1) {
  722. clearInterval(this.syncTimer);
  723. this.syncTimer = -1;
  724. }
  725. }
  726. /**
  727. * 执行自动同步
  728. */
  729. private async performAutoSync(): Promise<void> {
  730. try {
  731. // 检查数据一致性
  732. await this.dataPersistence.syncData();
  733. // 通知监听器执行同步检查
  734. this.notifyAutoSyncPerformed();
  735. } catch (error) {
  736. }
  737. }
  738. /**
  739. * 设置自动同步开关
  740. */
  741. setAutoSyncEnabled(enabled: boolean): void {
  742. this.autoSyncEnabled = enabled;
  743. if (enabled) {
  744. this.startAutoSync();
  745. } else {
  746. this.stopAutoSync();
  747. }
  748. }
  749. /**
  750. * 添加同步监听器
  751. */
  752. addSyncListener(listener: PlaylistSyncListener): void {
  753. this.syncListeners.add(listener);
  754. }
  755. /**
  756. * 移除同步监听器
  757. */
  758. removeSyncListener(listener: PlaylistSyncListener): void {
  759. this.syncListeners.delete(listener);
  760. }
  761. /**
  762. * 通知播放列表已同步到存储
  763. */
  764. private notifyPlaylistSynced(playlist: VideoItem[], currentIndex: number, playMode: PlayMode): void {
  765. this.syncListeners.forEach(listener => {
  766. try {
  767. listener.onPlaylistSynced?.(playlist, currentIndex, playMode);
  768. } catch (error) {
  769. }
  770. });
  771. }
  772. /**
  773. * 通知播放列表已从存储加载
  774. */
  775. private notifyPlaylistLoaded(playlistData: PlaylistData): void {
  776. this.syncListeners.forEach(listener => {
  777. try {
  778. listener.onPlaylistLoaded?.(playlistData);
  779. } catch (error) {
  780. }
  781. });
  782. }
  783. /**
  784. * 通知自动同步已执行
  785. */
  786. private notifyAutoSyncPerformed(): void {
  787. this.syncListeners.forEach(listener => {
  788. try {
  789. listener.onAutoSyncPerformed?.();
  790. } catch (error) {
  791. }
  792. });
  793. }
  794. /**
  795. * 获取同步状态
  796. */
  797. getSyncStatus(): SyncStatus {
  798. const status: SyncStatus = {
  799. autoSyncEnabled: this.autoSyncEnabled,
  800. lastSyncTimestamp: this.lastSyncTimestamp,
  801. syncInterval: PlaylistSyncService.SYNC_INTERVAL
  802. };
  803. return status;
  804. }
  805. /**
  806. * 清理资源
  807. */
  808. release(): void {
  809. this.stopAutoSync();
  810. this.syncListeners.clear();
  811. }
  812. }
  813. /**
  814. * 播放列表同步监听器接口
  815. */
  816. export interface PlaylistSyncListener {
  817. onPlaylistSynced?(playlist: VideoItem[], currentIndex: number, playMode: PlayMode): void;
  818. onPlaylistLoaded?(playlistData: PlaylistData): void;
  819. onAutoSyncPerformed?(): void;
  820. onSyncError?(error: Error): void;
  821. }