-
Notifications
You must be signed in to change notification settings - Fork 1.8k
refactor: decouple lyrics providers from synced-lyrics #4267
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -132,14 +132,52 @@ export const forceLoadMainPlugin = async ( | |||||||||||
| } | ||||||||||||
| }; | ||||||||||||
|
|
||||||||||||
| const topologicalSort = (plugins: Record<string, PluginDef<unknown, unknown, unknown>>) => { | ||||||||||||
| const visited = new Set<string>(); | ||||||||||||
| const visiting = new Set<string>(); | ||||||||||||
| const order: string[] = []; | ||||||||||||
|
|
||||||||||||
| const visit = (id: string) => { | ||||||||||||
| if (visited.has(id)) return; | ||||||||||||
| if (visiting.has(id)) { | ||||||||||||
| console.warn(`Circular dependency detected involving plugin: ${id}`); | ||||||||||||
| return; | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| visiting.add(id); | ||||||||||||
| const plugin = plugins[id]; | ||||||||||||
| if (plugin?.dependencies) { | ||||||||||||
| for (const dep of plugin.dependencies) { | ||||||||||||
| if (plugins[dep]) { | ||||||||||||
| visit(dep); | ||||||||||||
| } else { | ||||||||||||
| console.warn(`Plugin ${id} depends on ${dep} which is not found`); | ||||||||||||
| } | ||||||||||||
| } | ||||||||||||
| } | ||||||||||||
| visiting.delete(id); | ||||||||||||
| visited.add(id); | ||||||||||||
| order.push(id); | ||||||||||||
| }; | ||||||||||||
|
|
||||||||||||
| for (const id of Object.keys(plugins)) { | ||||||||||||
| visit(id); | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| return order; | ||||||||||||
| }; | ||||||||||||
|
|
||||||||||||
| export const loadAllMainPlugins = async (win: BrowserWindow) => { | ||||||||||||
| console.log(LoggerPrefix, t('common.console.plugins.load-all')); | ||||||||||||
| const pluginConfigs = config.plugins.getPlugins(); | ||||||||||||
| const allPluginsMap = await mainPlugins(); | ||||||||||||
| const sortedPluginIds = topologicalSort(allPluginsMap); | ||||||||||||
| const queue: Promise<void>[] = []; | ||||||||||||
|
|
||||||||||||
| for (const [plugin, pluginDef] of Object.entries(await mainPlugins())) { | ||||||||||||
| const config = deepmerge(pluginDef.config, pluginConfigs[plugin] ?? {}); | ||||||||||||
| if (config.enabled) { | ||||||||||||
| for (const plugin of sortedPluginIds) { | ||||||||||||
| const pluginDef = allPluginsMap[plugin]; | ||||||||||||
| const pluginConfig = deepmerge(pluginDef.config, pluginConfigs[plugin] ?? {}); | ||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🚫 [eslint] <prettier/prettier> reported by reviewdog 🐶
Suggested change
|
||||||||||||
| if (pluginConfig.enabled) { | ||||||||||||
| queue.push(forceLoadMainPlugin(plugin, win)); | ||||||||||||
| } else if (loadedPluginMap[plugin]) { | ||||||||||||
| queue.push(forceUnloadMainPlugin(plugin, win)); | ||||||||||||
|
|
||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -82,16 +82,54 @@ export const forceLoadPreloadPlugin = async (id: string) => { | |||||||||
| } | ||||||||||
| }; | ||||||||||
|
|
||||||||||
| const topologicalSort = (plugins: Record<string, PluginDef<unknown, unknown, unknown>>) => { | ||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🚫 [eslint] <prettier/prettier> reported by reviewdog 🐶
Suggested change
|
||||||||||
| const visited = new Set<string>(); | ||||||||||
| const visiting = new Set<string>(); | ||||||||||
| const order: string[] = []; | ||||||||||
|
|
||||||||||
| const visit = (id: string) => { | ||||||||||
| if (visited.has(id)) return; | ||||||||||
| if (visiting.has(id)) { | ||||||||||
| console.warn(`Circular dependency detected involving plugin: ${id}`); | ||||||||||
| return; | ||||||||||
| } | ||||||||||
|
|
||||||||||
| visiting.add(id); | ||||||||||
| const plugin = plugins[id]; | ||||||||||
| if (plugin?.dependencies) { | ||||||||||
| for (const dep of plugin.dependencies) { | ||||||||||
| if (plugins[dep]) { | ||||||||||
| visit(dep); | ||||||||||
| } else { | ||||||||||
| console.warn(`Plugin ${id} depends on ${dep} which is not found`); | ||||||||||
| } | ||||||||||
| } | ||||||||||
| } | ||||||||||
| visiting.delete(id); | ||||||||||
| visited.add(id); | ||||||||||
| order.push(id); | ||||||||||
| }; | ||||||||||
|
|
||||||||||
| for (const id of Object.keys(plugins)) { | ||||||||||
| visit(id); | ||||||||||
| } | ||||||||||
|
|
||||||||||
| return order; | ||||||||||
| }; | ||||||||||
|
|
||||||||||
| export const loadAllPreloadPlugins = async () => { | ||||||||||
| const pluginConfigs = config.plugins.getPlugins(); | ||||||||||
| const allPluginsMap = await preloadPlugins(); | ||||||||||
| const sortedPluginIds = topologicalSort(allPluginsMap); | ||||||||||
|
|
||||||||||
| for (const [pluginId, pluginDef] of Object.entries(await preloadPlugins())) { | ||||||||||
| const config = deepmerge( | ||||||||||
| for (const pluginId of sortedPluginIds) { | ||||||||||
| const pluginDef = allPluginsMap[pluginId]; | ||||||||||
| const pluginConfig = deepmerge( | ||||||||||
| pluginDef.config ?? { enable: false }, | ||||||||||
| pluginConfigs[pluginId] ?? {}, | ||||||||||
| ); | ||||||||||
|
|
||||||||||
| if (config.enabled) { | ||||||||||
| if (pluginConfig.enabled) { | ||||||||||
| forceLoadPreloadPlugin(pluginId); | ||||||||||
| } else { | ||||||||||
| if (loadedPluginMap[pluginId]) { | ||||||||||
|
|
||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -114,10 +114,48 @@ export const forceLoadRendererPlugin = async (id: string) => { | |||||||||
| } | ||||||||||
| }; | ||||||||||
|
|
||||||||||
| const topologicalSort = (plugins: Record<string, PluginDef<unknown, unknown, unknown>>) => { | ||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🚫 [eslint] <prettier/prettier> reported by reviewdog 🐶
Suggested change
|
||||||||||
| const visited = new Set<string>(); | ||||||||||
| const visiting = new Set<string>(); | ||||||||||
| const order: string[] = []; | ||||||||||
|
|
||||||||||
| const visit = (id: string) => { | ||||||||||
| if (visited.has(id)) return; | ||||||||||
| if (visiting.has(id)) { | ||||||||||
| console.warn(`Circular dependency detected involving plugin: ${id}`); | ||||||||||
| return; | ||||||||||
| } | ||||||||||
|
|
||||||||||
| visiting.add(id); | ||||||||||
| const plugin = plugins[id]; | ||||||||||
| if (plugin?.dependencies) { | ||||||||||
| for (const dep of plugin.dependencies) { | ||||||||||
| if (plugins[dep]) { | ||||||||||
| visit(dep); | ||||||||||
| } else { | ||||||||||
| console.warn(`Plugin ${id} depends on ${dep} which is not found`); | ||||||||||
| } | ||||||||||
| } | ||||||||||
| } | ||||||||||
| visiting.delete(id); | ||||||||||
| visited.add(id); | ||||||||||
| order.push(id); | ||||||||||
| }; | ||||||||||
|
|
||||||||||
| for (const id of Object.keys(plugins)) { | ||||||||||
| visit(id); | ||||||||||
| } | ||||||||||
|
|
||||||||||
| return order; | ||||||||||
| }; | ||||||||||
|
|
||||||||||
| export const loadAllRendererPlugins = async () => { | ||||||||||
| const pluginConfigs = window.mainConfig.plugins.getPlugins(); | ||||||||||
| const allPluginsMap = await rendererPlugins(); | ||||||||||
| const sortedPluginIds = topologicalSort(allPluginsMap); | ||||||||||
|
|
||||||||||
| for (const [pluginId, pluginDef] of Object.entries(await rendererPlugins())) { | ||||||||||
| for (const pluginId of sortedPluginIds) { | ||||||||||
| const pluginDef = allPluginsMap[pluginId]; | ||||||||||
| const config = deepmerge(pluginDef.config, pluginConfigs[pluginId] ?? {}); | ||||||||||
|
|
||||||||||
| if (config.enabled) { | ||||||||||
|
|
||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| import { net } from 'electron'; | ||
|
|
||
| import { createBackend } from '@/utils'; | ||
|
|
||
| const handlers = { | ||
| // Note: This will only be used for Forbidden headers, e.g. User-Agent, Authority, Cookie, etc. | ||
| // See: https://developer.mozilla.org/en-US/docs/Glossary/Forbidden_request_header | ||
| async fetch( | ||
| url: string, | ||
| init: RequestInit, | ||
| ): Promise<[number, string, Record<string, string>]> { | ||
| const res = await net.fetch(url, init); | ||
| return [ | ||
| res.status, | ||
| await res.text(), | ||
| Object.fromEntries(res.headers.entries()), | ||
| ]; | ||
| }, | ||
| }; | ||
|
|
||
| export const backend = createBackend({ | ||
| start(ctx) { | ||
| ctx.ipc.handle('lyrics-provider:fetch', (url: string, init: RequestInit) => | ||
| handlers.fetch(url, init), | ||
| ); | ||
| }, | ||
| stop(ctx) { | ||
| ctx.ipc.removeHandler('lyrics-provider:fetch'); | ||
| }, | ||
| }); |
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,20 @@ | ||||||||
| import { createPlugin } from '@/utils'; | ||||||||
| import { t } from '@/i18n'; | ||||||||
|
|
||||||||
| import { backend } from './backend'; | ||||||||
| import { renderer } from './renderer'; | ||||||||
| import { menu } from './menu'; | ||||||||
|
|
||||||||
| export default createPlugin({ | ||||||||
| name: () => t('plugins.lyrics-provider.name'), | ||||||||
| description: () => t('plugins.lyrics-provider.description'), | ||||||||
| restartNeeded: false, | ||||||||
| config: { | ||||||||
| enabled: true, | ||||||||
| preferredProvider: null as string | null, | ||||||||
| }, | ||||||||
|
|
||||||||
| backend, | ||||||||
| renderer, | ||||||||
| menu, | ||||||||
| }); | ||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🚫 [eslint] <prettier/prettier> reported by reviewdog 🐶
Suggested change
|
||||||||
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,44 @@ | ||||||||||
| import { t } from '@/i18n'; | ||||||||||
|
|
||||||||||
| import { providerNames } from './providers'; | ||||||||||
|
|
||||||||||
| import type { MenuItemConstructorOptions } from 'electron'; | ||||||||||
| import type { MenuContext } from '@/types/contexts'; | ||||||||||
|
|
||||||||||
| export const menu = async ( | ||||||||||
| ctx: MenuContext, | ||||||||||
| ): Promise<MenuItemConstructorOptions[]> => { | ||||||||||
| const config = await ctx.getConfig(); | ||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🚫 [eslint] <@typescript-eslint/no-unsafe-assignment> reported by reviewdog 🐶
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🚫 [eslint] <@typescript-eslint/no-unsafe-call> reported by reviewdog 🐶
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🚫 [eslint] <@typescript-eslint/no-unsafe-member-access> reported by reviewdog 🐶 |
||||||||||
|
|
||||||||||
| return [ | ||||||||||
| { | ||||||||||
| label: t('plugins.lyrics-provider.menu.preferred-provider.label'), | ||||||||||
| toolTip: t('plugins.lyrics-provider.menu.preferred-provider.tooltip'), | ||||||||||
| type: 'submenu', | ||||||||||
| submenu: [ | ||||||||||
| { | ||||||||||
| label: t('plugins.lyrics-provider.menu.preferred-provider.none.label'), | ||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🚫 [eslint] <prettier/prettier> reported by reviewdog 🐶
Suggested change
|
||||||||||
| toolTip: t( | ||||||||||
| 'plugins.lyrics-provider.menu.preferred-provider.none.tooltip', | ||||||||||
| ), | ||||||||||
| type: 'radio', | ||||||||||
| checked: config.preferredProvider === null, | ||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🚫 [eslint] <@typescript-eslint/no-unsafe-member-access> reported by reviewdog 🐶 |
||||||||||
| click() { | ||||||||||
| ctx.setConfig({ preferredProvider: null }); | ||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🚫 [eslint] <@typescript-eslint/no-unsafe-call> reported by reviewdog 🐶
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🚫 [eslint] <@typescript-eslint/no-unsafe-member-access> reported by reviewdog 🐶 |
||||||||||
| }, | ||||||||||
| }, | ||||||||||
| ...providerNames.map( | ||||||||||
| (provider) => | ||||||||||
| ({ | ||||||||||
| label: provider, | ||||||||||
| type: 'radio', | ||||||||||
| checked: config.preferredProvider === provider, | ||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🚫 [eslint] <@typescript-eslint/no-unsafe-member-access> reported by reviewdog 🐶 |
||||||||||
| click() { | ||||||||||
| ctx.setConfig({ preferredProvider: provider }); | ||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🚫 [eslint] <@typescript-eslint/no-unsafe-call> reported by reviewdog 🐶
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🚫 [eslint] <@typescript-eslint/no-unsafe-member-access> reported by reviewdog 🐶 |
||||||||||
| }, | ||||||||||
| }) as const, | ||||||||||
| ), | ||||||||||
| ], | ||||||||||
| }, | ||||||||||
| ]; | ||||||||||
| }; | ||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🚫 [eslint] <prettier/prettier> reported by reviewdog 🐶
Suggested change
|
||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🚫 [eslint] <prettier/prettier> reported by reviewdog 🐶
Replace
plugins:·Record<string,·PluginDef<unknown,·unknown,·unknown>>with⏎··plugins:·Record<string,·PluginDef<unknown,·unknown,·unknown>>,⏎