mirror of
https://github.com/th-ch/youtube-music.git
synced 2026-01-15 04:11:47 +00:00
fix: plugin load
This commit is contained in:
@ -50,6 +50,7 @@ export default defineConfig({
|
|||||||
const commonConfig: UserConfig = {
|
const commonConfig: UserConfig = {
|
||||||
plugins: [
|
plugins: [
|
||||||
viteResolve({
|
viteResolve({
|
||||||
|
'virtual:PluginBuilders': pluginVirtualModuleGenerator('index'),
|
||||||
'virtual:PreloadPlugins': pluginVirtualModuleGenerator('preload'),
|
'virtual:PreloadPlugins': pluginVirtualModuleGenerator('preload'),
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
@ -86,6 +87,7 @@ export default defineConfig({
|
|||||||
const commonConfig: UserConfig = {
|
const commonConfig: UserConfig = {
|
||||||
plugins: [
|
plugins: [
|
||||||
viteResolve({
|
viteResolve({
|
||||||
|
'virtual:PluginBuilders': pluginVirtualModuleGenerator('index'),
|
||||||
'virtual:RendererPlugins': pluginVirtualModuleGenerator('renderer'),
|
'virtual:RendererPlugins': pluginVirtualModuleGenerator('renderer'),
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
|
|||||||
@ -1,27 +1,16 @@
|
|||||||
import { deepmerge } from '@fastify/deepmerge';
|
|
||||||
|
|
||||||
import store from './store';
|
import store from './store';
|
||||||
import defaultConfig from './defaults';
|
|
||||||
|
|
||||||
import { restart } from '../providers/app-controls';
|
import { restart } from '../providers/app-controls';
|
||||||
import { Entries } from '../utils/type-utils';
|
|
||||||
|
|
||||||
interface Plugin {
|
import type { PluginBaseConfig } from '../plugins/utils/builder';
|
||||||
enabled: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
type DefaultPluginsConfig = typeof defaultConfig.plugins;
|
export function getPlugins() {
|
||||||
const deepmergeFn = deepmerge();
|
return store.get('plugins') as Record<string, PluginBaseConfig>;
|
||||||
|
|
||||||
export function getEnabled() {
|
|
||||||
const plugins = deepmergeFn(defaultConfig.plugins, (store.get('plugins') as DefaultPluginsConfig));
|
|
||||||
return (Object.entries(plugins) as Entries<DefaultPluginsConfig>).filter(([, options]) =>
|
|
||||||
(options as Plugin).enabled,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isEnabled(plugin: string) {
|
export function isEnabled(plugin: string) {
|
||||||
const pluginConfig = (store.get('plugins') as Record<string, Plugin>)[plugin];
|
const pluginConfig = (store.get('plugins') as Record<string, PluginBaseConfig>)[plugin];
|
||||||
return pluginConfig !== undefined && pluginConfig.enabled;
|
return pluginConfig !== undefined && pluginConfig.enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,7 +58,7 @@ export function disable(plugin: string) {
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
isEnabled,
|
isEnabled,
|
||||||
getEnabled,
|
getPlugins,
|
||||||
enable,
|
enable,
|
||||||
disable,
|
disable,
|
||||||
setOptions,
|
setOptions,
|
||||||
|
|||||||
59
src/index.ts
59
src/index.ts
@ -10,6 +10,8 @@ import { autoUpdater } from 'electron-updater';
|
|||||||
import electronDebug from 'electron-debug';
|
import electronDebug from 'electron-debug';
|
||||||
import { parse } from 'node-html-parser';
|
import { parse } from 'node-html-parser';
|
||||||
|
|
||||||
|
import { deepmerge as createDeepmerge } from '@fastify/deepmerge';
|
||||||
|
|
||||||
import config from './config';
|
import config from './config';
|
||||||
|
|
||||||
import { refreshMenu, setApplicationMenu } from './menu';
|
import { refreshMenu, setApplicationMenu } from './menu';
|
||||||
@ -26,7 +28,10 @@ import { pluginBuilders } from 'virtual:PluginBuilders';
|
|||||||
/* eslint-enable import/order */
|
/* eslint-enable import/order */
|
||||||
|
|
||||||
import youtubeMusicCSS from './youtube-music.css?inline';
|
import youtubeMusicCSS from './youtube-music.css?inline';
|
||||||
import { MainPlugin, PluginBaseConfig, MainPluginContext, MainPluginFactory } from './plugins/utils/builder';
|
|
||||||
|
import type { MainPlugin, PluginBaseConfig, MainPluginContext, MainPluginFactory } from './plugins/utils/builder';
|
||||||
|
|
||||||
|
const deepmerge = createDeepmerge();
|
||||||
|
|
||||||
// Catch errors and log them
|
// Catch errors and log them
|
||||||
unhandled({
|
unhandled({
|
||||||
@ -159,42 +164,30 @@ 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]);
|
||||||
|
|
||||||
for (const [pluginId, options] of config.plugins.getEnabled()) {
|
if (config?.enabled) {
|
||||||
const builder = pluginBuilders[pluginId as keyof PluginBuilderList];
|
builder.styles?.forEach((style) => {
|
||||||
const factory = (mainPlugins as Record<string, MainPluginFactory<PluginBaseConfig>>)[pluginId];
|
injectCSS(win.webContents, style);
|
||||||
|
console.log('[YTMusic]', `"${pluginId}" plugin meta data is loaded`);
|
||||||
|
});
|
||||||
|
|
||||||
if (builder) {
|
try {
|
||||||
builder.styles?.forEach((style) => {
|
const context = createContext(pluginId as keyof PluginBuilderList);
|
||||||
injectCSS(win.webContents, style);
|
const plugin = await (factory as MainPluginFactory<PluginBaseConfig>)(context);
|
||||||
console.log('[YTMusic]', `"${pluginId}" plugin meta data is loaded`);
|
loadedPluginList.push([pluginId, plugin]);
|
||||||
});
|
plugin.onLoad?.(win);
|
||||||
}
|
console.log('[YTMusic]', `"${pluginId}" plugin is loaded`);
|
||||||
|
} catch (error) {
|
||||||
if (factory) {
|
console.error('[YTMusic]', `Cannot load plugin "${pluginId}"`);
|
||||||
try {
|
console.trace(error);
|
||||||
const context = createContext(pluginId as keyof PluginBuilderList);
|
}
|
||||||
const plugin = await factory(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 {
|
|
||||||
// if (Object.hasOwn(mainPlugins, plugin)) {
|
|
||||||
// console.log('Loaded plugin - ' + plugin);
|
|
||||||
// const handler = mainPlugins[plugin as keyof typeof mainPlugins];
|
|
||||||
// if (handler) {
|
|
||||||
// await handler(win, options as never);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// } catch (e) {
|
|
||||||
// console.error(`Failed to load plugin "${plugin}"`, e);
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -26,7 +26,6 @@ export default builder.createRenderer(async ({ getConfig }) => {
|
|||||||
if (!video) return null;
|
if (!video) return null;
|
||||||
if (!wrapper) return null;
|
if (!wrapper) return null;
|
||||||
|
|
||||||
console.log('injectBlurVideo', songVideo, video, wrapper);
|
|
||||||
const blurCanvas = document.createElement('canvas');
|
const blurCanvas = document.createElement('canvas');
|
||||||
blurCanvas.classList.add('html5-blur-canvas');
|
blurCanvas.classList.add('html5-blur-canvas');
|
||||||
|
|
||||||
|
|||||||
@ -49,7 +49,7 @@ export default builder.createMain(({ handle, send }) => {
|
|||||||
if (target) target.click(undefined, BrowserWindow.fromWebContents(event.sender), event.sender);
|
if (target) target.click(undefined, BrowserWindow.fromWebContents(event.sender), event.sender);
|
||||||
});
|
});
|
||||||
|
|
||||||
handle('get-menu-by-id', (_, commandId: number) => {
|
handle('get-menu-by-id', (commandId: number) => {
|
||||||
const result = getMenuItemById(commandId);
|
const result = getMenuItemById(commandId);
|
||||||
|
|
||||||
return JSON.parse(JSON.stringify(
|
return JSON.parse(JSON.stringify(
|
||||||
@ -67,7 +67,7 @@ export default builder.createMain(({ handle, send }) => {
|
|||||||
handle('window-unmaximize', () => win.unmaximize());
|
handle('window-unmaximize', () => win.unmaximize());
|
||||||
win.on('unmaximize', () => send('window-unmaximize'));
|
win.on('unmaximize', () => send('window-unmaximize'));
|
||||||
|
|
||||||
handle('image-path-to-data-url', (_, imagePath: string) => {
|
handle('image-path-to-data-url', (imagePath: string) => {
|
||||||
const nativeImageIcon = nativeImage.createFromPath(imagePath);
|
const nativeImageIcon = nativeImage.createFromPath(imagePath);
|
||||||
return nativeImageIcon?.toDataURL();
|
return nativeImageIcon?.toDataURL();
|
||||||
});
|
});
|
||||||
|
|||||||
@ -20,7 +20,7 @@ export default builder.createMain(({ handle, getConfig }) =>{
|
|||||||
revRomanized = true;
|
revRomanized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
handle('search-genius-lyrics', async (_, extractedSongInfo: SongInfo) => {
|
handle('search-genius-lyrics', async (extractedSongInfo: SongInfo) => {
|
||||||
const metadata = extractedSongInfo;
|
const metadata = extractedSongInfo;
|
||||||
return await fetchFromGenius(metadata);
|
return await fetchFromGenius(metadata);
|
||||||
});
|
});
|
||||||
|
|||||||
@ -24,7 +24,7 @@ export default builder.createRenderer(({ on, invoke }) => ({
|
|||||||
|
|
||||||
let unregister: (() => void) | null = null;
|
let unregister: (() => void) | null = null;
|
||||||
|
|
||||||
on('update-song-info', (_, extractedSongInfo: SongInfo) => {
|
on('update-song-info', (extractedSongInfo: SongInfo) => {
|
||||||
unregister?.();
|
unregister?.();
|
||||||
|
|
||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
|
|||||||
@ -261,8 +261,8 @@ export default builder.createRenderer(async ({ on, getConfig, setConfig }) => {
|
|||||||
|
|
||||||
document.addEventListener('apiLoaded', (e) => {
|
document.addEventListener('apiLoaded', (e) => {
|
||||||
api = e.detail;
|
api = e.detail;
|
||||||
on('changeVolume', (_, toIncrease: boolean) => changeVolume(toIncrease));
|
on('changeVolume', (toIncrease: boolean) => changeVolume(toIncrease));
|
||||||
on('setVolume', (_, value: number) => setVolume(value));
|
on('setVolume', (value: number) => setVolume(value));
|
||||||
firstRun();
|
firstRun();
|
||||||
}, { once: true, passive: true });
|
}, { once: true, passive: true });
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,13 +1,17 @@
|
|||||||
import { contextBridge, ipcRenderer, IpcRendererEvent } from 'electron';
|
import { contextBridge, ipcRenderer, IpcRendererEvent } from 'electron';
|
||||||
import is from 'electron-is';
|
import is from 'electron-is';
|
||||||
|
|
||||||
|
import { pluginBuilders } from 'virtual:PluginBuilders';
|
||||||
|
|
||||||
|
import { deepmerge as createDeepmerge } from '@fastify/deepmerge';
|
||||||
|
|
||||||
import config from './config';
|
import config from './config';
|
||||||
|
|
||||||
// eslint-disable-next-line import/order
|
// eslint-disable-next-line import/order
|
||||||
import { preloadPlugins } from 'virtual:PreloadPlugins';
|
import { preloadPlugins } from 'virtual:PreloadPlugins';
|
||||||
import { PluginBaseConfig, PluginContext, PreloadPluginFactory } from './plugins/utils/builder';
|
import { PluginBaseConfig, PluginContext, PreloadPluginFactory } from './plugins/utils/builder';
|
||||||
|
|
||||||
const enabledPluginNameAndOptions = config.plugins.getEnabled();
|
const deepmerge = createDeepmerge();
|
||||||
|
|
||||||
const createContext = <
|
const createContext = <
|
||||||
Key extends keyof PluginBuilderList,
|
Key extends keyof PluginBuilderList,
|
||||||
@ -22,12 +26,15 @@ const createContext = <
|
|||||||
|
|
||||||
const preloadedPluginList = [];
|
const preloadedPluginList = [];
|
||||||
|
|
||||||
enabledPluginNameAndOptions.forEach(async ([id]) => {
|
const pluginConfig = config.plugins.getPlugins();
|
||||||
|
Object.entries(preloadPlugins)
|
||||||
|
.filter(([id]) => deepmerge(pluginBuilders[id as keyof PluginBuilderList].config, pluginConfig[id as keyof PluginBuilderList])?.enabled)
|
||||||
|
.forEach(async ([id]) => {
|
||||||
if (Object.hasOwn(preloadPlugins, id)) {
|
if (Object.hasOwn(preloadPlugins, id)) {
|
||||||
const factory = (preloadPlugins as Record<string, PreloadPluginFactory<PluginBaseConfig>>)[id];
|
const factory = (preloadPlugins as Record<string, PreloadPluginFactory<PluginBaseConfig>>)[id];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const context = createContext(id);
|
const context = createContext(id as keyof PluginBuilderList);
|
||||||
const plugin = await factory(context);
|
const plugin = await factory(context);
|
||||||
plugin.onLoad?.();
|
plugin.onLoad?.();
|
||||||
preloadedPluginList.push(plugin);
|
preloadedPluginList.push(plugin);
|
||||||
|
|||||||
@ -2,13 +2,17 @@
|
|||||||
// eslint-disable-next-line import/order
|
// eslint-disable-next-line import/order
|
||||||
import { rendererPlugins } from 'virtual:RendererPlugins';
|
import { rendererPlugins } from 'virtual:RendererPlugins';
|
||||||
|
|
||||||
|
import { deepmerge as createDeepmerge } from '@fastify/deepmerge';
|
||||||
|
|
||||||
import { PluginBaseConfig, RendererPluginContext, RendererPluginFactory } from './plugins/utils/builder';
|
import { PluginBaseConfig, RendererPluginContext, RendererPluginFactory } from './plugins/utils/builder';
|
||||||
|
|
||||||
import { startingPages } from './providers/extracted-data';
|
import { startingPages } from './providers/extracted-data';
|
||||||
import { setupSongControls } from './providers/song-controls-front';
|
import { setupSongControls } from './providers/song-controls-front';
|
||||||
import setupSongInfo from './providers/song-info-front';
|
import setupSongInfo from './providers/song-info-front';
|
||||||
|
import {mainPlugins} from "virtual:MainPlugins";
|
||||||
|
import {pluginBuilders} from "virtual:PluginBuilders";
|
||||||
|
|
||||||
const enabledPluginNameAndOptions = window.mainConfig.plugins.getEnabled();
|
const deepmerge = createDeepmerge();
|
||||||
|
|
||||||
let api: Element | null = null;
|
let api: Element | null = null;
|
||||||
|
|
||||||
@ -116,55 +120,31 @@ const createContext = <
|
|||||||
});
|
});
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
// enabledPluginNameAndOptions.forEach(async ([pluginName, options]) => {
|
const pluginConfig = window.mainConfig.plugins.getPlugins();
|
||||||
// if (pluginName === 'ambient-mode') {
|
|
||||||
// const builder = rendererPlugins[pluginName];
|
|
||||||
|
|
||||||
// try {
|
|
||||||
// const context = createContext(pluginName);
|
|
||||||
// const plugin = await builder?.(context);
|
|
||||||
// console.log(plugin);
|
|
||||||
// plugin.onLoad?.();
|
|
||||||
// } catch (error) {
|
|
||||||
// console.error(`Error in plugin "${pluginName}"`);
|
|
||||||
// console.trace(error);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (Object.hasOwn(rendererPlugins, pluginName)) {
|
|
||||||
// const handler = rendererPlugins[pluginName];
|
|
||||||
// try {
|
|
||||||
// await handler?.(options as never);
|
|
||||||
// } catch (error) {
|
|
||||||
// console.error(`Error in plugin "${pluginName}"`);
|
|
||||||
// console.trace(error);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
|
|
||||||
|
const rendererPluginList = Object.entries(rendererPlugins);
|
||||||
const rendererPluginResult = await Promise.allSettled(
|
const rendererPluginResult = await Promise.allSettled(
|
||||||
enabledPluginNameAndOptions.map(async ([id]) => {
|
rendererPluginList
|
||||||
// HACK: eslint has a bug detects the type of rendererPlugins as "any"
|
.filter(([id]) => deepmerge(pluginBuilders[id as keyof PluginBuilderList].config, pluginConfig[id as keyof PluginBuilderList])?.enabled)
|
||||||
const builder = (rendererPlugins as Record<string, RendererPluginFactory<PluginBaseConfig>>)[id];
|
.map(async ([id, builder]) => {
|
||||||
|
const context = createContext(id as keyof PluginBuilderList);
|
||||||
const context = createContext(id as keyof PluginBuilderList);
|
return [id, await (builder as RendererPluginFactory<PluginBaseConfig>)(context)] as const;
|
||||||
return [id, await builder(context)] as const;
|
}),
|
||||||
}),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const rendererPluginList = rendererPluginResult
|
|
||||||
.map((it) => it.status === 'fulfilled' ? it.value : null)
|
|
||||||
.filter(Boolean);
|
|
||||||
|
|
||||||
rendererPluginResult.forEach((it, index) => {
|
rendererPluginResult.forEach((it, index) => {
|
||||||
if (it.status === 'rejected') {
|
if (it.status === 'rejected') {
|
||||||
const id = enabledPluginNameAndOptions[index][0];
|
const id = rendererPluginList[index][0];
|
||||||
console.error('[YTMusic]', `Cannot load plugin "${id}"`);
|
console.error('[YTMusic]', `Cannot load plugin "${id}"`);
|
||||||
console.trace(it.reason);
|
console.trace(it.reason);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
rendererPluginList.forEach(([id, plugin]) => {
|
const loadedRendererPluginList = rendererPluginResult
|
||||||
|
.map((it) => it.status === 'fulfilled' ? it.value : null)
|
||||||
|
.filter(Boolean);
|
||||||
|
|
||||||
|
loadedRendererPluginList.forEach(([id, plugin]) => {
|
||||||
try {
|
try {
|
||||||
plugin.onLoad?.();
|
plugin.onLoad?.();
|
||||||
console.log('[YTMusic]', `"${id}" plugin is loaded`);
|
console.log('[YTMusic]', `"${id}" plugin is loaded`);
|
||||||
@ -175,7 +155,7 @@ const createContext = <
|
|||||||
});
|
});
|
||||||
|
|
||||||
window.ipcRenderer.on('config-changed', (_event, id: string, newConfig: PluginBaseConfig) => {
|
window.ipcRenderer.on('config-changed', (_event, id: string, newConfig: PluginBaseConfig) => {
|
||||||
const plugin = rendererPluginList.find(([pluginId]) => pluginId === id);
|
const plugin = loadedRendererPluginList.find(([pluginId]) => pluginId === id);
|
||||||
|
|
||||||
if (plugin) {
|
if (plugin) {
|
||||||
plugin[1].onConfigChange?.(newConfig);
|
plugin[1].onConfigChange?.(newConfig);
|
||||||
|
|||||||
Reference in New Issue
Block a user