index.ts 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365
  1. import { contextBridge, ipcRenderer } from 'electron'
  2. import { IpcChannels } from '../shared/ipc-channels'
  3. import type {
  4. WorkshopQueryParams,
  5. WorkshopQueryResult,
  6. WorkshopItem,
  7. CreatorWorkshopQueryParams,
  8. WallpaperMeta,
  9. WallpaperFolder,
  10. LibraryFilters,
  11. WorkshopAuthorInfo,
  12. ApplyWallpaperOptions,
  13. DownloadProgressEvent,
  14. WallpaperEnvironment,
  15. LweStatus,
  16. LweInstallProgress,
  17. LinuxDistro,
  18. LweSceneObject,
  19. LweProperty,
  20. LweAudioObject,
  21. LweSceneEffect,
  22. BackupProgressEvent,
  23. Playlist,
  24. PlaylistSettings,
  25. PlaylistPlaybackState,
  26. SteamIdentity
  27. } from '../shared/types'
  28. const api = {
  29. workshop: {
  30. query: (params: WorkshopQueryParams): Promise<WorkshopQueryResult> =>
  31. ipcRenderer.invoke(IpcChannels.WORKSHOP_QUERY, params),
  32. getItem: (publishedFileId: string): Promise<WorkshopItem | null> =>
  33. ipcRenderer.invoke(IpcChannels.WORKSHOP_GET_ITEM, publishedFileId),
  34. queryByCreator: (
  35. creatorSteamId: string,
  36. params?: CreatorWorkshopQueryParams
  37. ): Promise<WorkshopQueryResult> =>
  38. ipcRenderer.invoke(IpcChannels.WORKSHOP_QUERY_BY_CREATOR, creatorSteamId, params)
  39. },
  40. steam: {
  41. isRunning: (): Promise<boolean> => ipcRenderer.invoke(IpcChannels.STEAM_IS_RUNNING),
  42. subscribe: (itemId: string): Promise<{ ok: boolean }> =>
  43. ipcRenderer.invoke(IpcChannels.STEAM_SUBSCRIBE, itemId),
  44. unsubscribe: (itemId: string): Promise<{ ok: boolean }> =>
  45. ipcRenderer.invoke(IpcChannels.STEAM_UNSUBSCRIBE, itemId),
  46. redownload: (itemId: string): Promise<{ ok: boolean }> =>
  47. ipcRenderer.invoke(IpcChannels.STEAM_REDOWNLOAD, itemId),
  48. getSubscribedItems: (): Promise<string[]> =>
  49. ipcRenderer.invoke(IpcChannels.STEAM_GET_SUBSCRIBED),
  50. downloadInfo: (itemId: string): Promise<DownloadProgressEvent | null> =>
  51. ipcRenderer.invoke(IpcChannels.STEAM_DOWNLOAD_INFO, itemId),
  52. itemState: (itemId: string): Promise<number> =>
  53. ipcRenderer.invoke(IpcChannels.STEAM_ITEM_STATE, itemId),
  54. vote: (itemId: string, voteUp: boolean): Promise<{ ok: boolean; confirmed: boolean }> =>
  55. ipcRenderer.invoke(IpcChannels.STEAM_VOTE, itemId, voteUp),
  56. openWorkshopItem: (itemId: string): Promise<{ ok: boolean }> =>
  57. ipcRenderer.invoke(IpcChannels.STEAM_OPEN_WORKSHOP, itemId),
  58. getVotedIds: (): Promise<string[]> =>
  59. ipcRenderer.invoke(IpcChannels.STEAM_GET_VOTED_IDS),
  60. getAuthorInfo: (steamId: string): Promise<WorkshopAuthorInfo | null> =>
  61. ipcRenderer.invoke(IpcChannels.STEAM_GET_AUTHOR_INFO, steamId)
  62. },
  63. library: {
  64. getAll: (filters?: LibraryFilters): Promise<WallpaperMeta[]> =>
  65. ipcRenderer.invoke(IpcChannels.LIBRARY_GET_ALL, filters),
  66. getOne: (id: string): Promise<WallpaperMeta | null> =>
  67. ipcRenderer.invoke(IpcChannels.LIBRARY_GET_ONE, id),
  68. update: (id: string, patch: Partial<WallpaperMeta>): Promise<WallpaperMeta | null> =>
  69. ipcRenderer.invoke(IpcChannels.LIBRARY_UPDATE, id, patch),
  70. delete: (id: string): Promise<{ ok: boolean }> =>
  71. ipcRenderer.invoke(IpcChannels.LIBRARY_DELETE, id),
  72. addTag: (id: string, tag: string): Promise<WallpaperMeta | null> =>
  73. ipcRenderer.invoke(IpcChannels.LIBRARY_ADD_TAG, id, tag),
  74. removeTag: (id: string, tag: string): Promise<WallpaperMeta | null> =>
  75. ipcRenderer.invoke(IpcChannels.LIBRARY_REMOVE_TAG, id, tag),
  76. getTags: (): Promise<string[]> => ipcRenderer.invoke(IpcChannels.LIBRARY_GET_TAGS),
  77. search: (query: string): Promise<WallpaperMeta[]> =>
  78. ipcRenderer.invoke(IpcChannels.LIBRARY_SEARCH, query),
  79. scan: (): Promise<{ imported: number; skipped: number; removed: number }> =>
  80. ipcRenderer.invoke(IpcChannels.LIBRARY_SCAN),
  81. distinctTags: (): Promise<string[]> =>
  82. ipcRenderer.invoke(IpcChannels.LIBRARY_DISTINCT_TAGS),
  83. resetFpsOverrides: (): Promise<{ count: number }> =>
  84. ipcRenderer.invoke(IpcChannels.LIBRARY_RESET_FPS_OVERRIDES),
  85. checkUnavailable: (): Promise<{ checked: number; unavailable: number; changed: boolean }> =>
  86. ipcRenderer.invoke(IpcChannels.LIBRARY_CHECK_UNAVAILABLE)
  87. },
  88. config: {
  89. get: (): Promise<{
  90. workshopPath: string | null
  91. defaultWorkshopPath: string
  92. isConfigured: boolean
  93. defaultFps: number | null
  94. recommendedFpsEnabled: boolean
  95. recommendedWebFpsEnabled: boolean
  96. lweRepoUrl: string | null
  97. lweRepoBranch: string | null
  98. lweCmakeArgs: string | null
  99. defaultLweRepoUrl: string
  100. backupPath: string | null
  101. isBackupConfigured: boolean
  102. autoUnsubscribeAfterBackup: boolean
  103. trayEnabled: boolean
  104. autostartSupported: boolean
  105. autostartEnabled: boolean
  106. autostartMinimized: boolean
  107. autostartPlaylistId: string | null
  108. killLweOnQuit: boolean
  109. audioScreen: string | null
  110. ambientVolume: number | null
  111. defaultAudioSensitivity: number
  112. disablePuppetAnimation: boolean
  113. disableAnimations: boolean
  114. steamIdentity: SteamIdentity
  115. ignoredCreators: string[]
  116. }> => ipcRenderer.invoke(IpcChannels.CONFIG_GET),
  117. setWorkshopPath: (p: string): Promise<{ ok: boolean }> =>
  118. ipcRenderer.invoke(IpcChannels.CONFIG_SET_WORKSHOP_PATH, p),
  119. setDefaultFps: (fps: number | null): Promise<{ ok: boolean }> =>
  120. ipcRenderer.invoke(IpcChannels.CONFIG_SET_DEFAULT_FPS, fps),
  121. setRecommendedFpsEnabled: (enabled: boolean): Promise<{ ok: boolean }> =>
  122. ipcRenderer.invoke(IpcChannels.CONFIG_SET_RECOMMENDED_FPS, enabled),
  123. setRecommendedWebFpsEnabled: (enabled: boolean): Promise<{ ok: boolean }> =>
  124. ipcRenderer.invoke(IpcChannels.CONFIG_SET_RECOMMENDED_WEB_FPS, enabled),
  125. setLweRepo: (url: string | null, branch: string | null): Promise<{ ok: boolean }> =>
  126. ipcRenderer.invoke(IpcChannels.CONFIG_SET_LWE_REPO, url, branch),
  127. setLweCmakeArgs: (args: string | null): Promise<{ ok: boolean }> =>
  128. ipcRenderer.invoke(IpcChannels.CONFIG_SET_LWE_CMAKE_ARGS, args),
  129. setBackupPath: (p: string): Promise<{ ok: boolean }> =>
  130. ipcRenderer.invoke(IpcChannels.CONFIG_SET_BACKUP_PATH, p),
  131. setAutoUnsubscribeAfterBackup: (enabled: boolean): Promise<{ ok: boolean }> =>
  132. ipcRenderer.invoke(IpcChannels.CONFIG_SET_AUTO_UNSUBSCRIBE, enabled),
  133. pickFolder: (): Promise<string | null> =>
  134. ipcRenderer.invoke(IpcChannels.CONFIG_PICK_FOLDER),
  135. pickFile: (): Promise<string | null> =>
  136. ipcRenderer.invoke(IpcChannels.CONFIG_PICK_FILE),
  137. importWE: (sourcePath: string): Promise<{ folders: number; playlists: number; configPath: string }> =>
  138. ipcRenderer.invoke(IpcChannels.CONFIG_IMPORT_WE, sourcePath),
  139. createFresh: (): Promise<{ configPath: string }> =>
  140. ipcRenderer.invoke(IpcChannels.CONFIG_CREATE_FRESH),
  141. setTrayEnabled: (enabled: boolean): Promise<{ ok: boolean }> =>
  142. ipcRenderer.invoke(IpcChannels.CONFIG_SET_TRAY_ENABLED, enabled),
  143. setKillLweOnQuit: (enabled: boolean): Promise<{ ok: boolean }> =>
  144. ipcRenderer.invoke(IpcChannels.CONFIG_SET_KILL_LWE_ON_QUIT, enabled),
  145. setAutostart: (
  146. enabled: boolean,
  147. minimized: boolean,
  148. playlistId: string | null
  149. ): Promise<{ ok: boolean }> =>
  150. ipcRenderer.invoke(IpcChannels.CONFIG_SET_AUTOSTART, enabled, minimized, playlistId),
  151. getAutostartSupported: (): Promise<boolean> =>
  152. ipcRenderer.invoke(IpcChannels.CONFIG_GET_AUTOSTART_SUPPORTED),
  153. setAudioScreen: (screen: string | null): Promise<{ ok: boolean }> =>
  154. ipcRenderer.invoke(IpcChannels.CONFIG_SET_AUDIO_SCREEN, screen),
  155. setAmbientVolume: (volume: number | null): Promise<{ ok: boolean }> =>
  156. ipcRenderer.invoke(IpcChannels.CONFIG_SET_AMBIENT_VOLUME, volume),
  157. setDefaultAudioSensitivity: (multiplier: number): Promise<{ ok: boolean }> =>
  158. ipcRenderer.invoke(IpcChannels.CONFIG_SET_DEFAULT_AUDIO_SENSITIVITY, multiplier),
  159. setDisablePuppetAnimation: (disabled: boolean): Promise<{ ok: boolean }> =>
  160. ipcRenderer.invoke(IpcChannels.CONFIG_SET_DISABLE_PUPPET_ANIMATION, disabled),
  161. setDisableAnimations: (disabled: boolean): Promise<{ ok: boolean }> =>
  162. ipcRenderer.invoke(IpcChannels.CONFIG_SET_DISABLE_ANIMATIONS, disabled),
  163. setSteamIdentity: (identity: SteamIdentity): Promise<{ ok: boolean }> =>
  164. ipcRenderer.invoke(IpcChannels.CONFIG_SET_STEAM_IDENTITY, identity),
  165. ignoreCreator: (steamId: string): Promise<{ ok: boolean }> =>
  166. ipcRenderer.invoke(IpcChannels.CONFIG_IGNORE_CREATOR, steamId),
  167. unignoreCreator: (steamId: string): Promise<{ ok: boolean }> =>
  168. ipcRenderer.invoke(IpcChannels.CONFIG_UNIGNORE_CREATOR, steamId)
  169. },
  170. playlist: {
  171. getAll: (): Promise<Playlist[]> => ipcRenderer.invoke(IpcChannels.PLAYLIST_GET_ALL),
  172. getOne: (id: string): Promise<Playlist | null> =>
  173. ipcRenderer.invoke(IpcChannels.PLAYLIST_GET_ONE, id),
  174. create: (title: string): Promise<Playlist> =>
  175. ipcRenderer.invoke(IpcChannels.PLAYLIST_CREATE, title),
  176. rename: (id: string, title: string): Promise<Playlist | null> =>
  177. ipcRenderer.invoke(IpcChannels.PLAYLIST_RENAME, id, title),
  178. delete: (id: string): Promise<{ ok: boolean }> =>
  179. ipcRenderer.invoke(IpcChannels.PLAYLIST_DELETE, id),
  180. updateSettings: (id: string, patch: Partial<PlaylistSettings>): Promise<Playlist | null> =>
  181. ipcRenderer.invoke(IpcChannels.PLAYLIST_UPDATE_SETTINGS, id, patch),
  182. addItems: (id: string, wallpaperIds: string[]): Promise<Playlist | null> =>
  183. ipcRenderer.invoke(IpcChannels.PLAYLIST_ADD_ITEMS, id, wallpaperIds),
  184. removeItems: (id: string, wallpaperIds: string[]): Promise<Playlist | null> =>
  185. ipcRenderer.invoke(IpcChannels.PLAYLIST_REMOVE_ITEMS, id, wallpaperIds),
  186. reorderItems: (id: string, orderedWallpaperIds: string[]): Promise<Playlist | null> =>
  187. ipcRenderer.invoke(IpcChannels.PLAYLIST_REORDER_ITEMS, id, orderedWallpaperIds),
  188. updateItem: (
  189. id: string,
  190. wallpaperId: string,
  191. patch: { volume?: number; durationSec?: number }
  192. ): Promise<Playlist | null> =>
  193. ipcRenderer.invoke(IpcChannels.PLAYLIST_UPDATE_ITEM, id, wallpaperId, patch),
  194. start: (id: string): Promise<PlaylistPlaybackState> =>
  195. ipcRenderer.invoke(IpcChannels.PLAYLIST_START, id),
  196. playItem: (id: string, wallpaperId: string): Promise<PlaylistPlaybackState> =>
  197. ipcRenderer.invoke(IpcChannels.PLAYLIST_PLAY_ITEM, id, wallpaperId),
  198. stop: (): Promise<PlaylistPlaybackState> => ipcRenderer.invoke(IpcChannels.PLAYLIST_STOP),
  199. pause: (): Promise<PlaylistPlaybackState> => ipcRenderer.invoke(IpcChannels.PLAYLIST_PAUSE),
  200. resume: (): Promise<PlaylistPlaybackState> => ipcRenderer.invoke(IpcChannels.PLAYLIST_RESUME),
  201. next: (): Promise<PlaylistPlaybackState> => ipcRenderer.invoke(IpcChannels.PLAYLIST_NEXT),
  202. previous: (): Promise<PlaylistPlaybackState> =>
  203. ipcRenderer.invoke(IpcChannels.PLAYLIST_PREVIOUS),
  204. getState: (): Promise<PlaylistPlaybackState> =>
  205. ipcRenderer.invoke(IpcChannels.PLAYLIST_GET_STATE)
  206. },
  207. folders: {
  208. getAll: (): Promise<WallpaperFolder[]> =>
  209. ipcRenderer.invoke(IpcChannels.FOLDERS_GET_ALL),
  210. create: (title: string): Promise<WallpaperFolder> =>
  211. ipcRenderer.invoke(IpcChannels.FOLDERS_CREATE, title),
  212. rename: (id: string, title: string): Promise<WallpaperFolder | null> =>
  213. ipcRenderer.invoke(IpcChannels.FOLDERS_RENAME, id, title),
  214. delete: (id: string): Promise<{ ok: boolean }> =>
  215. ipcRenderer.invoke(IpcChannels.FOLDERS_DELETE, id),
  216. addItems: (folderId: string, itemIds: string[]): Promise<WallpaperFolder | null> =>
  217. ipcRenderer.invoke(IpcChannels.FOLDERS_ADD_ITEMS, folderId, itemIds),
  218. removeItems: (folderId: string, itemIds: string[]): Promise<WallpaperFolder | null> =>
  219. ipcRenderer.invoke(IpcChannels.FOLDERS_REMOVE_ITEMS, folderId, itemIds),
  220. importWEConfig: (configPath: string): Promise<{ folders: number; playlists: number }> =>
  221. ipcRenderer.invoke(IpcChannels.FOLDERS_IMPORT_WE_CONFIG, configPath),
  222. cleanup: (): Promise<{ removed: number }> =>
  223. ipcRenderer.invoke(IpcChannels.FOLDERS_CLEANUP)
  224. },
  225. wallpaper: {
  226. apply: (options: ApplyWallpaperOptions): Promise<{ ok: boolean; appliedPath: string }> =>
  227. ipcRenderer.invoke(IpcChannels.WALLPAPER_APPLY, options),
  228. getActive: (): Promise<WallpaperMeta | null> =>
  229. ipcRenderer.invoke(IpcChannels.WALLPAPER_GET_ACTIVE),
  230. detectEnvironment: (): Promise<WallpaperEnvironment> =>
  231. ipcRenderer.invoke(IpcChannels.WALLPAPER_DETECT_ENV)
  232. },
  233. lwe: {
  234. status: (): Promise<LweStatus> =>
  235. ipcRenderer.invoke(IpcChannels.LWE_STATUS),
  236. detectDistro: (): Promise<LinuxDistro> =>
  237. ipcRenderer.invoke(IpcChannels.LWE_DETECT_DISTRO),
  238. installDeps: (): Promise<{ ok: boolean }> =>
  239. ipcRenderer.invoke(IpcChannels.LWE_INSTALL_DEPS),
  240. install: (): Promise<LweStatus> =>
  241. ipcRenderer.invoke(IpcChannels.LWE_INSTALL),
  242. uninstall: (): Promise<{ ok: boolean; message: string }> =>
  243. ipcRenderer.invoke(IpcChannels.LWE_UNINSTALL),
  244. launch: (wallpaperPath: string, options?: { screenRoot?: string; fps?: number }): Promise<{ ok: boolean; running: boolean }> =>
  245. ipcRenderer.invoke(IpcChannels.LWE_LAUNCH, wallpaperPath, options),
  246. stop: (): Promise<{ ok: boolean }> =>
  247. ipcRenderer.invoke(IpcChannels.LWE_STOP),
  248. killAll: (): Promise<{ ok: boolean; message: string }> =>
  249. ipcRenderer.invoke(IpcChannels.LWE_KILL_ALL),
  250. listObjects: (wallpaperPath: string): Promise<LweSceneObject[]> =>
  251. ipcRenderer.invoke(IpcChannels.LWE_LIST_OBJECTS, wallpaperPath),
  252. hotswapSettings: (options: {
  253. disabledObjects?: string[]
  254. enabledObjects?: string[]
  255. volume?: number
  256. xray?: boolean
  257. scaling?: string
  258. zoom?: number
  259. offsetX?: number
  260. offsetY?: number
  261. disableParallax?: boolean
  262. expandCanvas?: boolean
  263. cornerColor?: string
  264. speed?: number
  265. propertyOverrides?: Record<string, string>
  266. audioScreen?: string
  267. ambientVolume?: number
  268. audioSensitivity?: Record<string, number>
  269. soundVolume?: Record<string, number>
  270. }): Promise<{ ok: boolean }> =>
  271. ipcRenderer.invoke(IpcChannels.LWE_HOTSWAP_SETTINGS, options),
  272. listProperties: (wallpaperPath: string): Promise<LweProperty[]> =>
  273. ipcRenderer.invoke(IpcChannels.LWE_LIST_PROPERTIES, wallpaperPath),
  274. listScreens: (): Promise<string[]> =>
  275. ipcRenderer.invoke(IpcChannels.LWE_LIST_SCREENS),
  276. listAudioObjects: (wallpaperPath: string): Promise<LweAudioObject[]> =>
  277. ipcRenderer.invoke(IpcChannels.LWE_LIST_AUDIO_OBJECTS, wallpaperPath),
  278. listEffects: (wallpaperPath: string): Promise<LweSceneEffect[]> =>
  279. ipcRenderer.invoke(IpcChannels.LWE_LIST_EFFECTS, wallpaperPath)
  280. },
  281. backup: {
  282. item: (itemId: string): Promise<{ ok: boolean; unsubscribed: boolean }> =>
  283. ipcRenderer.invoke(IpcChannels.BACKUP_ITEM, itemId),
  284. selection: (itemIds: string[]): Promise<{ backedUp: number; failed: number; unsubscribed: number }> =>
  285. ipcRenderer.invoke(IpcChannels.BACKUP_SELECTION, itemIds),
  286. scan: (): Promise<{ imported: number; skipped: number; linked: number }> =>
  287. ipcRenderer.invoke(IpcChannels.BACKUP_SCAN)
  288. },
  289. desktopIcons: {
  290. setEnabled: (enabled: boolean): Promise<{ ok: boolean; enabled: boolean }> =>
  291. ipcRenderer.invoke(IpcChannels.DESKTOP_ICONS_SET_ENABLED, enabled),
  292. getEnabled: (): Promise<boolean> =>
  293. ipcRenderer.invoke(IpcChannels.DESKTOP_ICONS_GET_ENABLED)
  294. },
  295. shell: {
  296. openInFileManager: (folderPath: string): Promise<{ ok: boolean; error?: string }> =>
  297. ipcRenderer.invoke(IpcChannels.SHELL_OPEN_IN_FILE_MANAGER, folderPath),
  298. openWithDefault: (filePath: string): Promise<{ ok: boolean; error?: string }> =>
  299. ipcRenderer.invoke(IpcChannels.SHELL_OPEN_WITH_DEFAULT, filePath),
  300. openPath: (targetPath: string): Promise<{ ok: boolean; error?: string }> =>
  301. ipcRenderer.invoke(IpcChannels.SHELL_OPEN_PATH, targetPath),
  302. openPaths: (targetPaths: string[]): Promise<{ ok: boolean; error?: string }> =>
  303. ipcRenderer.invoke(IpcChannels.SHELL_OPEN_PATHS, targetPaths),
  304. openExternal: (url: string): Promise<{ ok: boolean; error?: string }> =>
  305. ipcRenderer.invoke(IpcChannels.SHELL_OPEN_EXTERNAL, url)
  306. },
  307. on: {
  308. downloadProgress: (cb: (info: DownloadProgressEvent) => void): (() => void) => {
  309. const listener = (_: Electron.IpcRendererEvent, info: DownloadProgressEvent) => cb(info)
  310. ipcRenderer.on(IpcChannels.EVENT_DOWNLOAD_PROGRESS, listener)
  311. return () => ipcRenderer.off(IpcChannels.EVENT_DOWNLOAD_PROGRESS, listener)
  312. },
  313. wallpaperImported: (cb: (meta: WallpaperMeta) => void): (() => void) => {
  314. const listener = (_: Electron.IpcRendererEvent, meta: WallpaperMeta) => cb(meta)
  315. ipcRenderer.on(IpcChannels.EVENT_WALLPAPER_IMPORTED, listener)
  316. return () => ipcRenderer.off(IpcChannels.EVENT_WALLPAPER_IMPORTED, listener)
  317. },
  318. steamStatus: (cb: (running: boolean) => void): (() => void) => {
  319. const listener = (_: Electron.IpcRendererEvent, running: boolean) => cb(running)
  320. ipcRenderer.on(IpcChannels.EVENT_STEAM_STATUS, listener)
  321. return () => ipcRenderer.off(IpcChannels.EVENT_STEAM_STATUS, listener)
  322. },
  323. lweInstallProgress: (cb: (progress: LweInstallProgress) => void): (() => void) => {
  324. const listener = (_: Electron.IpcRendererEvent, progress: LweInstallProgress) => cb(progress)
  325. ipcRenderer.on(IpcChannels.EVENT_LWE_INSTALL_PROGRESS, listener)
  326. return () => ipcRenderer.off(IpcChannels.EVENT_LWE_INSTALL_PROGRESS, listener)
  327. },
  328. backupProgress: (cb: (progress: BackupProgressEvent) => void): (() => void) => {
  329. const listener = (_: Electron.IpcRendererEvent, progress: BackupProgressEvent) => cb(progress)
  330. ipcRenderer.on(IpcChannels.EVENT_BACKUP_PROGRESS, listener)
  331. return () => ipcRenderer.off(IpcChannels.EVENT_BACKUP_PROGRESS, listener)
  332. },
  333. playlistStateChanged: (cb: (state: PlaylistPlaybackState) => void): (() => void) => {
  334. const listener = (_: Electron.IpcRendererEvent, state: PlaylistPlaybackState) => cb(state)
  335. ipcRenderer.on(IpcChannels.EVENT_PLAYLIST_STATE_CHANGED, listener)
  336. return () => ipcRenderer.off(IpcChannels.EVENT_PLAYLIST_STATE_CHANGED, listener)
  337. },
  338. votedIdsChanged: (cb: (ids: string[]) => void): (() => void) => {
  339. const listener = (_: Electron.IpcRendererEvent, ids: string[]) => cb(ids)
  340. ipcRenderer.on(IpcChannels.EVENT_VOTED_IDS_CHANGED, listener)
  341. return () => ipcRenderer.off(IpcChannels.EVENT_VOTED_IDS_CHANGED, listener)
  342. },
  343. libraryChanged: (cb: () => void): (() => void) => {
  344. const listener = () => cb()
  345. ipcRenderer.on(IpcChannels.EVENT_LIBRARY_CHANGED, listener)
  346. return () => ipcRenderer.off(IpcChannels.EVENT_LIBRARY_CHANGED, listener)
  347. }
  348. }
  349. }
  350. contextBridge.exposeInMainWorld('electronAPI', api)
  351. export type ElectronAPI = typeof api