config.service.ts 7.0 KB

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