playlist.service.ts 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. import Store from 'electron-store'
  2. import { randomUUID } from 'crypto'
  3. import type { Playlist, PlaylistItem, PlaylistSettings } from '@shared/types'
  4. import { findAndMutate } from '../utils/collections'
  5. interface PlaylistStore {
  6. playlists: Playlist[]
  7. }
  8. const DEFAULT_SETTINGS: PlaylistSettings = {
  9. randomize: false,
  10. sortBy: 'manual',
  11. defaultDurationSec: 600,
  12. defaultVolume: 100
  13. }
  14. const store = new Store<PlaylistStore>({
  15. name: 'playlists',
  16. defaults: { playlists: [] }
  17. })
  18. function updatePlaylist(id: string, mutate: (playlist: Playlist) => void | false): Playlist | null {
  19. const playlists = store.get('playlists')
  20. const playlist = findAndMutate(playlists, id, (p) => {
  21. if (mutate(p) === false) return false
  22. p.updatedAt = Date.now()
  23. })
  24. if (playlist) store.set('playlists', playlists)
  25. return playlist
  26. }
  27. export function getAllPlaylists(): Playlist[] {
  28. return store.get('playlists')
  29. }
  30. export function getPlaylist(id: string): Playlist | null {
  31. return store.get('playlists').find((p) => p.id === id) ?? null
  32. }
  33. export function createPlaylist(title: string): Playlist {
  34. const now = Date.now()
  35. const playlist: Playlist = {
  36. id: randomUUID(),
  37. title,
  38. items: [],
  39. settings: { ...DEFAULT_SETTINGS },
  40. createdAt: now,
  41. updatedAt: now
  42. }
  43. const playlists = store.get('playlists')
  44. playlists.push(playlist)
  45. store.set('playlists', playlists)
  46. return playlist
  47. }
  48. export function renamePlaylist(id: string, title: string): Playlist | null {
  49. return updatePlaylist(id, (playlist) => {
  50. playlist.title = title
  51. })
  52. }
  53. export function deletePlaylist(id: string): void {
  54. const playlists = store.get('playlists').filter((p) => p.id !== id)
  55. store.set('playlists', playlists)
  56. }
  57. export function updatePlaylistSettings(
  58. id: string,
  59. patch: Partial<PlaylistSettings>
  60. ): Playlist | null {
  61. return updatePlaylist(id, (playlist) => {
  62. playlist.settings = { ...playlist.settings, ...patch }
  63. })
  64. }
  65. export function addItemsToPlaylist(id: string, wallpaperIds: string[]): Playlist | null {
  66. return updatePlaylist(id, (playlist) => {
  67. const existing = new Set(playlist.items.map((i) => i.wallpaperId))
  68. for (const wallpaperId of wallpaperIds) {
  69. if (!existing.has(wallpaperId)) {
  70. playlist.items.push({ wallpaperId })
  71. existing.add(wallpaperId)
  72. }
  73. }
  74. })
  75. }
  76. export function removeItemsFromPlaylist(id: string, wallpaperIds: string[]): Playlist | null {
  77. return updatePlaylist(id, (playlist) => {
  78. const remove = new Set(wallpaperIds)
  79. playlist.items = playlist.items.filter((i) => !remove.has(i.wallpaperId))
  80. })
  81. }
  82. /** Reorders items to match the given wallpaperId sequence (used for manual drag-reorder). */
  83. export function reorderPlaylistItems(id: string, orderedWallpaperIds: string[]): Playlist | null {
  84. return updatePlaylist(id, (playlist) => {
  85. const byId = new Map(playlist.items.map((i) => [i.wallpaperId, i]))
  86. const reordered: PlaylistItem[] = []
  87. for (const wallpaperId of orderedWallpaperIds) {
  88. const item = byId.get(wallpaperId)
  89. if (item) {
  90. reordered.push(item)
  91. byId.delete(wallpaperId)
  92. }
  93. }
  94. // Append anything not mentioned in orderedWallpaperIds (defensive, shouldn't normally happen)
  95. reordered.push(...byId.values())
  96. playlist.items = reordered
  97. })
  98. }
  99. export function updatePlaylistItem(
  100. id: string,
  101. wallpaperId: string,
  102. patch: { volume?: number; durationSec?: number }
  103. ): Playlist | null {
  104. return updatePlaylist(id, (playlist) => {
  105. const item = playlist.items.find((i) => i.wallpaperId === wallpaperId)
  106. if (!item) return false
  107. if ('volume' in patch) item.volume = patch.volume
  108. if ('durationSec' in patch) item.durationSec = patch.durationSec
  109. })
  110. }
  111. /**
  112. * Remove playlist item references that don't exist in the library anymore.
  113. * Returns the number of stale references removed.
  114. */
  115. export function cleanupPlaylists(validWallpaperIds: Set<string>): number {
  116. const playlists = store.get('playlists')
  117. let removed = 0
  118. for (const playlist of playlists) {
  119. const before = playlist.items.length
  120. playlist.items = playlist.items.filter((i) => validWallpaperIds.has(i.wallpaperId))
  121. removed += before - playlist.items.length
  122. }
  123. if (removed > 0) {
  124. store.set('playlists', playlists)
  125. console.log(`[Playlist] Cleanup: removed ${removed} stale item(s)`)
  126. }
  127. return removed
  128. }