config.service.ts 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. import Store from 'electron-store'
  2. import * as fs from 'fs'
  3. import * as path from 'path'
  4. import { app } from 'electron'
  5. import { WE_APP_ID } from '@shared/constants'
  6. interface AppConfig {
  7. workshopPath: string | null
  8. defaultFps: number | null
  9. lweRepoUrl: string | null
  10. lweRepoBranch: string | null
  11. lweCmakeArgs: string | null
  12. backupPath: string | null
  13. autoUnsubscribeAfterBackup: boolean
  14. trayEnabled: boolean
  15. autostartEnabled: boolean
  16. autostartMinimized: boolean
  17. autostartPlaylistId: string | null
  18. killLweOnQuit: boolean
  19. recommendedFpsEnabled: boolean
  20. recommendedWebFpsEnabled: boolean
  21. audioScreen: string | null
  22. ambientVolume: number | null
  23. /** Default --audio-sensitivity multiplier for audio-reactive objects with no per-wallpaper override */
  24. defaultAudioSensitivity: number
  25. }
  26. const store = new Store<AppConfig>({
  27. name: 'config',
  28. defaults: {
  29. workshopPath: null,
  30. defaultFps: null,
  31. recommendedFpsEnabled: false,
  32. recommendedWebFpsEnabled: false,
  33. lweRepoUrl: null,
  34. lweRepoBranch: null,
  35. lweCmakeArgs: null,
  36. backupPath: null,
  37. autoUnsubscribeAfterBackup: false,
  38. trayEnabled: false,
  39. autostartEnabled: false,
  40. autostartMinimized: false,
  41. autostartPlaylistId: null,
  42. killLweOnQuit: false,
  43. audioScreen: null,
  44. ambientVolume: null,
  45. defaultAudioSensitivity: 1
  46. }
  47. })
  48. export function getConfiguredWorkshopPath(): string | null {
  49. return store.get('workshopPath')
  50. }
  51. export function setConfiguredWorkshopPath(p: string): void {
  52. store.set('workshopPath', p)
  53. }
  54. export function isWorkshopPathConfigured(): boolean {
  55. return store.get('workshopPath') !== null
  56. }
  57. export function getConfiguredBackupPath(): string | null {
  58. return store.get('backupPath')
  59. }
  60. export function setConfiguredBackupPath(p: string): void {
  61. store.set('backupPath', p)
  62. }
  63. export function isBackupPathConfigured(): boolean {
  64. return store.get('backupPath') !== null
  65. }
  66. export function getAutoUnsubscribeAfterBackup(): boolean {
  67. return store.get('autoUnsubscribeAfterBackup')
  68. }
  69. export function setAutoUnsubscribeAfterBackup(enabled: boolean): void {
  70. store.set('autoUnsubscribeAfterBackup', enabled)
  71. }
  72. export function getTrayEnabled(): boolean {
  73. return store.get('trayEnabled')
  74. }
  75. export function setTrayEnabled(enabled: boolean): void {
  76. store.set('trayEnabled', enabled)
  77. }
  78. export function getAutostartEnabled(): boolean {
  79. return store.get('autostartEnabled')
  80. }
  81. export function getAutostartMinimized(): boolean {
  82. return store.get('autostartMinimized')
  83. }
  84. export function setAutostart(enabled: boolean, minimized: boolean): void {
  85. store.set('autostartEnabled', enabled)
  86. store.set('autostartMinimized', minimized)
  87. }
  88. export function getAutostartPlaylistId(): string | null {
  89. return store.get('autostartPlaylistId')
  90. }
  91. export function setAutostartPlaylistId(id: string | null): void {
  92. store.set('autostartPlaylistId', id)
  93. }
  94. export function getKillLweOnQuit(): boolean {
  95. return store.get('killLweOnQuit')
  96. }
  97. export function setKillLweOnQuit(enabled: boolean): void {
  98. store.set('killLweOnQuit', enabled)
  99. }
  100. export function getDefaultFps(): number | null {
  101. return store.get('defaultFps')
  102. }
  103. export function setDefaultFps(fps: number | null): void {
  104. store.set('defaultFps', fps)
  105. }
  106. /** When enabled, video wallpapers without a manual FPS override are launched at their own native frame rate. */
  107. export function getRecommendedFpsEnabled(): boolean {
  108. return store.get('recommendedFpsEnabled')
  109. }
  110. export function setRecommendedFpsEnabled(enabled: boolean): void {
  111. store.set('recommendedFpsEnabled', enabled)
  112. }
  113. /** When enabled, web wallpapers without a manual FPS override launch at RECOMMENDED_WEB_FPS
  114. * instead of the default limit - CEF paints internally at that rate regardless, so a lower
  115. * default just throttles how often the engine displays what CEF already rendered. */
  116. export function getRecommendedWebFpsEnabled(): boolean {
  117. return store.get('recommendedWebFpsEnabled')
  118. }
  119. export function setRecommendedWebFpsEnabled(enabled: boolean): void {
  120. store.set('recommendedWebFpsEnabled', enabled)
  121. }
  122. /** Screen name (matches --screen-root/--audio-screen) that alone produces audio; null = every screen can. */
  123. export function getAudioScreen(): string | null {
  124. return store.get('audioScreen')
  125. }
  126. export function setAudioScreen(screen: string | null): void {
  127. store.set('audioScreen', screen)
  128. }
  129. /** 0-128, applied to non-video (scene sound + web) backgrounds instead of the per-wallpaper volume; null = same volume. */
  130. export function getAmbientVolume(): number | null {
  131. return store.get('ambientVolume')
  132. }
  133. export function setAmbientVolume(volume: number | null): void {
  134. store.set('ambientVolume', volume)
  135. }
  136. /** Default --audio-sensitivity multiplier for audio-reactive objects with no per-wallpaper override. */
  137. export function getDefaultAudioSensitivity(): number {
  138. return store.get('defaultAudioSensitivity')
  139. }
  140. export function setDefaultAudioSensitivity(multiplier: number): void {
  141. store.set('defaultAudioSensitivity', multiplier)
  142. }
  143. /** Custom linux-wallpaperengine repo (git URL or local path); null = official repo. */
  144. export function getLweRepoUrl(): string | null {
  145. return store.get('lweRepoUrl')
  146. }
  147. /** Branch/tag to build from the custom repo; null = default branch. */
  148. export function getLweRepoBranch(): string | null {
  149. return store.get('lweRepoBranch')
  150. }
  151. export function setLweRepo(url: string | null, branch: string | null): void {
  152. store.set('lweRepoUrl', url?.trim() || null)
  153. store.set('lweRepoBranch', branch?.trim() || null)
  154. }
  155. /** Extra cmake arguments (e.g. "-DENABLE_KDE_EXPERIMENTAL_FEATURES=ON"), appended to the build. */
  156. export function getLweCmakeArgs(): string | null {
  157. return store.get('lweCmakeArgs')
  158. }
  159. export function setLweCmakeArgs(args: string | null): void {
  160. store.set('lweCmakeArgs', args?.trim() || null)
  161. }
  162. /** Path where we store our copy of config.json */
  163. export function getWeConfigPath(): string {
  164. return path.join(app.getPath('userData'), 'we-config.json')
  165. }
  166. /**
  167. * Import a Wallpaper Engine config.json:
  168. * 1. Copies the file to our data dir
  169. * 2. Replaces all old workshop content paths with the configured workshop path
  170. * 3. Returns the path to the fixed copy
  171. */
  172. export function importWEConfigFile(sourcePath: string): string {
  173. const workshopPath = getConfiguredWorkshopPath()
  174. if (!workshopPath) throw new Error('Workshop path must be configured before importing')
  175. let content = fs.readFileSync(sourcePath, 'utf8')
  176. // The WE config uses paths like:
  177. // D:/Games/SteamLibrary/steamapps/workshop/content/431960/...
  178. // C:/Program Files (x86)/Steam/steamapps/workshop/content/431960/...
  179. // We need to replace everything up to and including "431960/" with the configured workshop path + "/"
  180. const appId = String(WE_APP_ID)
  181. const pattern = new RegExp(
  182. `[A-Za-z]:[^"]*?/steamapps/workshop/content/${appId}/|` +
  183. `/[^"]*?/steamapps/workshop/content/${appId}/`,
  184. 'g'
  185. )
  186. const normalizedTarget = workshopPath.endsWith('/') ? workshopPath : workshopPath + '/'
  187. content = content.replace(pattern, normalizedTarget)
  188. const destPath = getWeConfigPath()
  189. fs.writeFileSync(destPath, content, 'utf8')
  190. console.log(`[Config] Imported WE config from ${sourcePath} to ${destPath}`)
  191. return destPath
  192. }
  193. /** Create a fresh empty WE-style config.json */
  194. export function createFreshConfig(): string {
  195. const destPath = getWeConfigPath()
  196. const skeleton = {
  197. '~': {
  198. general: {
  199. browser: { folders: [] },
  200. playlists: []
  201. }
  202. }
  203. }
  204. fs.writeFileSync(destPath, JSON.stringify(skeleton, null, '\t'), 'utf8')
  205. console.log(`[Config] Created fresh config at ${destPath}`)
  206. return destPath
  207. }