diff --git a/package.json b/package.json index b794b196..05dcd5d9 100644 --- a/package.json +++ b/package.json @@ -149,6 +149,7 @@ "conf": "10.2.0", "custom-electron-prompt": "1.5.7", "dbus-next": "0.10.2", + "deepmerge-ts": "5.1.0", "electron-debug": "3.2.0", "electron-is": "3.0.0", "electron-localshortcut": "3.2.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 35f385e1..abc95b1e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -58,6 +58,9 @@ dependencies: dbus-next: specifier: 0.10.2 version: 0.10.2 + deepmerge-ts: + specifier: 5.1.0 + version: 5.1.0 electron-debug: specifier: 3.2.0 version: 3.2.0 @@ -2265,6 +2268,11 @@ packages: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} dev: true + /deepmerge-ts@5.1.0: + resolution: {integrity: sha512-eS8dRJOckyo9maw9Tu5O5RUi/4inFLrnoLkBe3cPfDMx3WZioXtmOew4TXQaxq7Rhl4xjDtR7c6x8nNTxOvbFw==} + engines: {node: '>=16.0.0'} + dev: false + /deepmerge@4.3.1: resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} engines: {node: '>=0.10.0'} diff --git a/src/config/index.ts b/src/config/index.ts index fb081f2b..d88c23d8 100644 --- a/src/config/index.ts +++ b/src/config/index.ts @@ -14,7 +14,7 @@ const set = (key: string, value: unknown) => { store.set(key, value); }; const setPartial = (key: string, value: object) => { - deepmerge(store.get(key), value); + deepmerge(store.get(key) ?? {}, value); store.set(value); }; diff --git a/src/index.ts b/src/index.ts index 03eac02b..0cb2e6ce 100644 --- a/src/index.ts +++ b/src/index.ts @@ -103,7 +103,7 @@ if (is.windows()) { ipcMain.handle('get-main-plugin-names', () => Object.keys(mainPlugins)); const initHook = (win: BrowserWindow) => { - ipcMain.handle('get-config', (_, id: keyof PluginBuilderList) => deepmerge(pluginBuilders[id].config, config.get(`plugins.${id}`)) as PluginBuilderList[typeof id]['config']); + ipcMain.handle('get-config', (_, id: keyof PluginBuilderList) => deepmerge(pluginBuilders[id].config, config.get(`plugins.${id}`) ?? {}) as PluginBuilderList[typeof id]['config']); ipcMain.handle('set-config', (_, name: string, obj: object) => config.setPartial(`plugins.${name}`, obj)); config.watch((newValue) => { @@ -148,7 +148,7 @@ async function loadPlugins(win: BrowserWindow) { Key extends keyof PluginBuilderList, Config extends PluginBaseConfig = PluginBuilderList[Key]['config'], >(name: Key): MainPluginContext => ({ - getConfig: () => deepmerge(pluginBuilders[name].config, config.get(`plugins.${name}`)) as Config, + getConfig: () => deepmerge(pluginBuilders[name].config, config.get(`plugins.${name}`) ?? {}) as Config, setConfig: (newConfig) => { config.setPartial(`plugins.${name}`, newConfig); }, @@ -167,25 +167,29 @@ async function loadPlugins(win: BrowserWindow) { const pluginConfigs = config.plugins.getPlugins(); for (const [pluginId, factory] of Object.entries(mainPlugins)) { if (Object.hasOwn(pluginBuilders, pluginId)) { - const builder = pluginBuilders[pluginId as keyof PluginBuilderList]; - const config = deepmerge(builder.config, pluginConfigs[pluginId as keyof PluginBuilderList]); + try { + const builder = pluginBuilders[pluginId as keyof PluginBuilderList]; + const config = deepmerge(builder.config, pluginConfigs[pluginId as keyof PluginBuilderList] ?? {}); - if (config?.enabled) { - builder.styles?.forEach((style) => { - injectCSS(win.webContents, style); - console.log('[YTMusic]', `"${pluginId}" plugin meta data is loaded`); - }); + if (config.enabled) { + builder.styles?.forEach((style) => { + injectCSS(win.webContents, style); + }); + console.log('[YTMusic]', `"${pluginId}" plugin data is loaded`); - try { - const context = createContext(pluginId as keyof PluginBuilderList); - const plugin = await (factory as MainPluginFactory)(context); - loadedPluginList.push([pluginId, plugin]); - plugin.onLoad?.(win); - console.log('[YTMusic]', `"${pluginId}" plugin is loaded`); - } catch (error) { - console.error('[YTMusic]', `Cannot load plugin "${pluginId}"`); - console.trace(error); + try { + const context = createContext(pluginId as keyof PluginBuilderList); + const plugin = await (factory as MainPluginFactory)(context); + loadedPluginList.push([pluginId, plugin]); + plugin.onLoad?.(win); + console.log('[YTMusic]', `"${pluginId}" plugin is loaded`); + } catch (error) { + console.error('[YTMusic]', `Cannot load plugin "${pluginId}"`); + console.trace(error); + } } + } catch(err) { + console.log('[YTMusic]', `Cannot initialize "${pluginId}" plugin: ${String(err)}`); } } } diff --git a/src/menu.ts b/src/menu.ts index 82c0b2f8..314ca8e0 100644 --- a/src/menu.ts +++ b/src/menu.ts @@ -56,7 +56,7 @@ export const mainMenuTemplate = async (win: BrowserWindow): Promise(name: Key): MenuPluginContext => ({ - getConfig: () => deepmerge(pluginBuilders[name].config, config.get(`plugins.${name}`)) as Config, + getConfig: () => deepmerge(pluginBuilders[name].config, config.get(`plugins.${name}`) ?? {}) as Config, setConfig: (newConfig) => { config.setPartial(`plugins.${name}`, newConfig); }, diff --git a/src/plugins/picture-in-picture/main.ts b/src/plugins/picture-in-picture/main.ts index 4b3cb59b..4300ceac 100644 --- a/src/plugins/picture-in-picture/main.ts +++ b/src/plugins/picture-in-picture/main.ts @@ -6,7 +6,7 @@ import builder, { PictureInPicturePluginConfig } from './index'; import { injectCSS } from '../utils/main'; -export default builder.createMain(({ getConfig, setConfig, send, handle }) => { +export default builder.createMain(({ getConfig, setConfig, send, handle, on }) => { let isInPiP = false; let originalPosition: number[]; let originalSize: number[]; @@ -96,8 +96,7 @@ export default builder.createMain(({ getConfig, setConfig, send, handle }) => { config ??= await getConfig(); win ??= window; setConfig({ isInPiP }); - injectCSS(win.webContents, style); - ipcMain.on('picture-in-picture', () => { + on('picture-in-picture', () => { togglePiP(); }); diff --git a/src/preload.ts b/src/preload.ts index 60c800d0..733d697a 100644 --- a/src/preload.ts +++ b/src/preload.ts @@ -17,7 +17,7 @@ const createContext = < Key extends keyof PluginBuilderList, Config extends PluginBaseConfig = PluginBuilderList[Key]['config'], >(name: Key): PluginContext => ({ - getConfig: () => deepmerge(pluginBuilders[name].config, config.get(`plugins.${name}`)) as Config, + getConfig: () => deepmerge(pluginBuilders[name].config, config.get(`plugins.${name}`) ?? {}) as Config, setConfig: (newConfig) => { config.setPartial(`plugins.${name}`, newConfig); }, @@ -28,7 +28,7 @@ const preloadedPluginList = []; const pluginConfig = config.plugins.getPlugins(); Object.entries(preloadPlugins) - .filter(([id]) => deepmerge(pluginBuilders[id as keyof PluginBuilderList].config, pluginConfig[id as keyof PluginBuilderList])?.enabled) + .filter(([id]) => deepmerge(pluginBuilders[id as keyof PluginBuilderList].config, pluginConfig[id as keyof PluginBuilderList] ?? {}).enabled) .forEach(async ([id]) => { if (Object.hasOwn(preloadPlugins, id)) { const factory = (preloadPlugins as Record>)[id]; diff --git a/src/renderer.ts b/src/renderer.ts index 64b22f9a..5a9d122a 100644 --- a/src/renderer.ts +++ b/src/renderer.ts @@ -4,13 +4,13 @@ import { rendererPlugins } from 'virtual:RendererPlugins'; import { deepmerge as createDeepmerge } from '@fastify/deepmerge'; +import { pluginBuilders } from 'virtual:PluginBuilders'; + import { PluginBaseConfig, RendererPluginContext, RendererPluginFactory } from './plugins/utils/builder'; import { startingPages } from './providers/extracted-data'; import { setupSongControls } from './providers/song-controls-front'; import setupSongInfo from './providers/song-info-front'; -import {mainPlugins} from "virtual:MainPlugins"; -import {pluginBuilders} from "virtual:PluginBuilders"; const deepmerge = createDeepmerge(); @@ -125,7 +125,7 @@ const createContext = < const rendererPluginList = Object.entries(rendererPlugins); const rendererPluginResult = await Promise.allSettled( rendererPluginList - .filter(([id]) => deepmerge(pluginBuilders[id as keyof PluginBuilderList].config, pluginConfig[id as keyof PluginBuilderList])?.enabled) + .filter(([id]) => deepmerge(pluginBuilders[id as keyof PluginBuilderList].config, pluginConfig[id as keyof PluginBuilderList] ?? {}).enabled) .map(async ([id, builder]) => { const context = createContext(id as keyof PluginBuilderList); return [id, await (builder as RendererPluginFactory)(context)] as const;