PlaybackSnapshotStore.ets 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. import { PreferencesUtil } from '@pura/harmony-utils'
  2. import { VideoItem } from '../viewmodel/VideoItem'
  3. import { PlaybackSnapshot, PlaybackSnapshotSong } from './model/PlaybackSnapshot'
  4. export const PLAYBACK_SNAPSHOT_PREFERENCES_KEY = 'playback.host.snapshot'
  5. export interface RecoveredPlaybackSnapshotState {
  6. queue: VideoItem[]
  7. currentIndex: number
  8. currentSong: VideoItem | undefined
  9. }
  10. export class PlaybackSnapshotStore {
  11. public write(snapshot: PlaybackSnapshot): void {
  12. PreferencesUtil.putSync(PLAYBACK_SNAPSHOT_PREFERENCES_KEY, JSON.stringify(snapshot))
  13. }
  14. public read(): PlaybackSnapshot | undefined {
  15. const text = PreferencesUtil.getStringSync(PLAYBACK_SNAPSHOT_PREFERENCES_KEY, '')
  16. if (text.length === 0) {
  17. return undefined
  18. }
  19. try {
  20. const parsedSnapshot = JSON.parse(text) as Object | null
  21. if (!this.isValidSnapshot(parsedSnapshot)) {
  22. this.clear()
  23. return undefined
  24. }
  25. return parsedSnapshot
  26. } catch (_error) {
  27. this.clear()
  28. return undefined
  29. }
  30. }
  31. public clear(): void {
  32. PreferencesUtil.putSync(PLAYBACK_SNAPSHOT_PREFERENCES_KEY, '')
  33. }
  34. public restoreQueue(snapshot: PlaybackSnapshot): VideoItem[] {
  35. return this.recoverQueueState(snapshot).queue
  36. }
  37. public restoreCurrentSong(snapshot: PlaybackSnapshot): VideoItem | undefined {
  38. return this.recoverQueueState(snapshot).currentSong
  39. }
  40. public recoverQueueState(snapshot: PlaybackSnapshot): RecoveredPlaybackSnapshotState {
  41. const snapshotQueue = Array.isArray(snapshot.queue) ? snapshot.queue : []
  42. const queue: VideoItem[] = []
  43. for (let index = 0; index < snapshotQueue.length; index++) {
  44. const snapshotSong = snapshotQueue[index] as Object | null
  45. if (!this.isValidSnapshotSong(snapshotSong)) {
  46. continue
  47. }
  48. queue.push(this.buildVideoItem(snapshotSong))
  49. }
  50. const currentIndex = this.resolveCurrentIndex(snapshot, queue)
  51. const currentSong = currentIndex >= 0 ? queue[currentIndex] : undefined
  52. return {
  53. queue,
  54. currentIndex,
  55. currentSong
  56. }
  57. }
  58. private resolveCurrentIndex(snapshot: PlaybackSnapshot, queue: VideoItem[]): number {
  59. if (queue.length <= 0) {
  60. return -1
  61. }
  62. if (snapshot.currentSongKey && snapshot.currentSongKey !== '') {
  63. const matchedIndex = queue.findIndex((item: VideoItem): boolean => item.filePath === snapshot.currentSongKey)
  64. if (matchedIndex >= 0) {
  65. return matchedIndex
  66. }
  67. }
  68. const requestedIndex = typeof snapshot.currentIndex === 'number' ? snapshot.currentIndex : 0
  69. return Math.max(0, Math.min(requestedIndex, queue.length - 1))
  70. }
  71. private isValidSnapshot(snapshot: Object | null): snapshot is PlaybackSnapshot {
  72. if (!snapshot || Array.isArray(snapshot)) {
  73. return false
  74. }
  75. const candidate = snapshot as PlaybackSnapshot
  76. return Array.isArray(candidate.queue) &&
  77. typeof candidate.currentIndex === 'number' &&
  78. typeof candidate.currentSongKey === 'string' &&
  79. typeof candidate.currentSongName === 'string' &&
  80. typeof candidate.isPlaying === 'boolean' &&
  81. typeof candidate.positionMs === 'number' &&
  82. typeof candidate.durationMs === 'number' &&
  83. typeof candidate.playType === 'number' &&
  84. typeof candidate.playlistContext === 'string' &&
  85. typeof candidate.updatedAt === 'number' &&
  86. typeof candidate.shouldResumeWhenActivated === 'boolean'
  87. }
  88. private isValidSnapshotSong(snapshotSong: Object | null): snapshotSong is PlaybackSnapshotSong {
  89. if (!snapshotSong || Array.isArray(snapshotSong)) {
  90. return false
  91. }
  92. const candidate = snapshotSong as PlaybackSnapshotSong
  93. return typeof candidate.filePath === 'string' &&
  94. typeof candidate.name === 'string' &&
  95. typeof candidate.type === 'number'
  96. }
  97. private buildVideoItem(song: PlaybackSnapshotSong): VideoItem {
  98. const item = new VideoItem(song.name, song.id ?? '', song.filePath, song.type, 0, '')
  99. item.artist = song.artist
  100. item.album = song.album
  101. item.duration = song.duration
  102. item.pixelMapPath = song.pixelMapPath
  103. item.lyricContent = song.lyricContent
  104. item.remote_rel_path = song.remote_rel_path
  105. item.webdav_account_id = song.webdav_account_id
  106. return item
  107. }
  108. }