index.ts 17 KB

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