From a3104fda4b0d58b076d0c737111636a66e468acc Mon Sep 17 00:00:00 2001 From: JellyBrick Date: Thu, 30 Nov 2023 11:59:27 +0900 Subject: [PATCH] feat: run prettier --- src/config/defaults.ts | 4 +- src/config/plugins.ts | 17 +- src/config/store.ts | 36 +- src/custom-electron-prompt.d.ts | 50 +- src/error.html | 84 +- src/index.ts | 7 +- src/loader/main.ts | 40 +- src/loader/menu.ts | 13 +- src/loader/preload.ts | 34 +- src/loader/renderer.ts | 45 +- src/menu.ts | 6 +- src/navigation.d.ts | 7 +- src/plugins/adblocker/blocker.ts | 27 +- src/plugins/adblocker/index.ts | 15 +- src/plugins/adblocker/injectors/inject.js | 34 +- src/plugins/album-color-theme/index.ts | 142 ++- src/plugins/album-color-theme/style.css | 20 +- src/plugins/ambient-mode/index.ts | 48 +- src/plugins/audio-compressor.ts | 3 +- src/plugins/bypass-age-restrictions/index.ts | 2 +- .../templates/captions-settings-template.html | 27 +- src/plugins/compact-sidebar/index.ts | 7 +- src/plugins/crossfade/fader.ts | 60 +- src/plugins/crossfade/index.ts | 39 +- src/plugins/disable-autoplay/index.ts | 13 +- src/plugins/discord/index.ts | 3 +- src/plugins/discord/main.ts | 65 +- src/plugins/discord/menu.ts | 33 +- src/plugins/downloader/index.ts | 5 +- src/plugins/downloader/main/index.ts | 16 +- src/plugins/downloader/main/utils.ts | 3 +- src/plugins/downloader/menu.ts | 5 +- src/plugins/downloader/renderer.ts | 14 +- src/plugins/downloader/types.ts | 822 ++++++++++++++++-- src/plugins/exponential-volume/index.ts | 10 +- src/plugins/in-app-menu/index.ts | 12 +- src/plugins/in-app-menu/main.ts | 33 +- src/plugins/in-app-menu/menu.ts | 9 +- src/plugins/in-app-menu/menu/icons.ts | 12 +- src/plugins/in-app-menu/menu/panel.ts | 54 +- src/plugins/in-app-menu/renderer.ts | 51 +- src/plugins/in-app-menu/titlebar.css | 42 +- src/plugins/last-fm/index.ts | 10 +- src/plugins/last-fm/main.ts | 127 ++- src/plugins/lumiastream/index.ts | 26 +- src/plugins/lyrics-genius/index.ts | 2 +- src/plugins/lyrics-genius/main.ts | 26 +- src/plugins/lyrics-genius/renderer.ts | 13 +- src/plugins/navigation/index.ts | 3 +- src/plugins/navigation/style.css | 6 +- src/plugins/navigation/templates/back.html | 54 +- src/plugins/navigation/templates/forward.html | 2 +- src/plugins/no-google-login/index.ts | 6 +- src/plugins/no-google-login/style.css | 4 +- src/plugins/notifications/index.ts | 3 +- src/plugins/notifications/interactive.ts | 121 ++- src/plugins/notifications/main.ts | 12 +- src/plugins/notifications/menu.ts | 16 +- src/plugins/notifications/utils.ts | 15 +- src/plugins/picture-in-picture/index.ts | 18 +- src/plugins/picture-in-picture/main.ts | 20 +- src/plugins/picture-in-picture/menu.ts | 34 +- src/plugins/picture-in-picture/renderer.ts | 61 +- .../templates/picture-in-picture.html | 12 +- src/plugins/playback-speed/index.ts | 5 +- src/plugins/playback-speed/renderer.ts | 35 +- .../playback-speed/templates/slider.html | 16 +- src/plugins/precise-volume/index.ts | 104 ++- src/plugins/precise-volume/override.ts | 30 +- src/plugins/precise-volume/renderer.ts | 47 +- src/plugins/precise-volume/volume-hud.css | 2 +- src/plugins/quality-changer/index.ts | 62 +- .../templates/qualitySettingsTemplate.html | 26 +- src/plugins/shortcuts/index.ts | 5 +- src/plugins/shortcuts/main.ts | 39 +- src/plugins/shortcuts/menu.ts | 48 +- src/plugins/shortcuts/mpris-service.d.ts | 9 +- src/plugins/shortcuts/mpris.ts | 30 +- src/plugins/skip-silences/index.ts | 2 +- src/plugins/skip-silences/renderer.ts | 38 +- src/plugins/sponsorblock/index.ts | 40 +- src/plugins/sponsorblock/types.ts | 17 +- src/plugins/taskbar-mediacontrol/index.ts | 12 +- src/plugins/touchbar/index.ts | 6 +- src/plugins/tuna-obs/index.ts | 34 +- src/plugins/utils/common/types.ts | 12 +- src/plugins/utils/main/css.ts | 24 +- src/plugins/utils/main/fetch.ts | 29 +- src/plugins/utils/main/fs.ts | 2 +- src/plugins/utils/main/types.ts | 12 +- src/plugins/video-toggle/button-switcher.css | 16 +- src/plugins/video-toggle/index.ts | 88 +- .../templates/button_template.html | 2 +- src/plugins/visualizer/butterchurn.d.ts | 37 +- src/plugins/visualizer/index.ts | 22 +- .../visualizer/visualizers/butterchurn.ts | 12 +- src/plugins/visualizer/visualizers/vudio.ts | 3 +- src/plugins/visualizer/visualizers/wave.ts | 12 +- src/plugins/visualizer/vudio.d.ts | 10 +- src/providers/app-controls.ts | 18 +- src/providers/decorators.ts | 23 +- src/providers/protocol-handler.ts | 9 +- src/providers/song-controls.ts | 12 +- src/providers/song-info-front.ts | 48 +- src/providers/song-info.ts | 46 +- src/renderer.ts | 64 +- src/reset.d.ts | 6 +- src/tray.ts | 5 +- src/types/contexts.ts | 19 +- src/types/datahost-get-state.ts | 3 +- src/types/get-player-response.ts | 5 +- src/types/player-api-events.ts | 20 +- src/types/plugins.ts | 42 +- src/types/youtube-player.ts | 399 ++++++--- src/utils/index.ts | 14 +- src/youtube-music.d.ts | 1 - 116 files changed, 2928 insertions(+), 1254 deletions(-) diff --git a/src/config/defaults.ts b/src/config/defaults.ts index f6f0e646..c0cc051e 100644 --- a/src/config/defaults.ts +++ b/src/config/defaults.ts @@ -32,8 +32,8 @@ export interface DefaultConfig { startingPage: string; overrideUserAgent: boolean; themes: string[]; - }, - plugins: Record, + }; + plugins: Record; } const defaultConfig: DefaultConfig = { diff --git a/src/config/plugins.ts b/src/config/plugins.ts index 510a0ebb..52121a0b 100644 --- a/src/config/plugins.ts +++ b/src/config/plugins.ts @@ -12,7 +12,10 @@ export function getPlugins() { } export function isEnabled(plugin: string) { - const pluginConfig = deepmerge(allPlugins[plugin].config ?? { enabled: false }, (store.get('plugins') as Record)[plugin] ?? {}); + const pluginConfig = deepmerge( + allPlugins[plugin].config ?? { enabled: false }, + (store.get('plugins') as Record)[plugin] ?? {}, + ); return pluginConfig !== undefined && pluginConfig.enabled; } @@ -22,7 +25,11 @@ export function isEnabled(plugin: string) { * @param options Options to set * @param exclude Options to exclude from the options object */ -export function setOptions(plugin: string, options: T, exclude: string[] = ['enabled']) { +export function setOptions( + plugin: string, + options: T, + exclude: string[] = ['enabled'], +) { const plugins = store.get('plugins') as Record; // HACK: This is a workaround for preventing changed options from being overwritten exclude.forEach((key) => { @@ -39,7 +46,11 @@ export function setOptions(plugin: string, options: T, exclude: string[] = [' }); } -export function setMenuOptions(plugin: string, options: T, exclude: string[] = ['enabled']) { +export function setMenuOptions( + plugin: string, + options: T, + exclude: string[] = ['enabled'], +) { setOptions(plugin, options, exclude); if (store.get('options.restartOnConfigChanges')) { restart(); diff --git a/src/config/store.ts b/src/config/store.ts index 49e56f0d..68934241 100644 --- a/src/config/store.ts +++ b/src/config/store.ts @@ -7,10 +7,17 @@ import { DefaultPresetList, type Preset } from '@/plugins/downloader/types'; const migrations = { '>=3.0.0'(store: Conf>) { - const discordConfig = store.get('plugins.discord') as Record; + const discordConfig = store.get('plugins.discord') as Record< + string, + unknown + >; if (discordConfig) { - const oldActivityTimoutEnabled = store.get('plugins.discord.activityTimoutEnabled') as boolean | undefined; - const oldActivityTimoutTime = store.get('plugins.discord.activityTimoutTime') as number | undefined; + const oldActivityTimoutEnabled = store.get( + 'plugins.discord.activityTimoutEnabled', + ) as boolean | undefined; + const oldActivityTimoutTime = store.get( + 'plugins.discord.activityTimoutTime', + ) as number | undefined; if (oldActivityTimoutEnabled !== undefined) { discordConfig.activityTimeoutEnabled = oldActivityTimoutEnabled; store.set('plugins.discord', discordConfig); @@ -93,18 +100,23 @@ const migrations = { } }, '>=1.12.0'(store: Conf>) { - const options = store.get('plugins.shortcuts') as Record< - string, - | { - action: string; - shortcut: unknown; - }[] - | Record - > | undefined; + const options = store.get('plugins.shortcuts') as + | Record< + string, + | { + action: string; + shortcut: unknown; + }[] + | Record + > + | undefined; if (options) { let updated = false; for (const optionType of ['global', 'local']) { - if (Object.hasOwn(options, optionType) && Array.isArray(options[optionType])) { + if ( + Object.hasOwn(options, optionType) && + Array.isArray(options[optionType]) + ) { const optionsArray = options[optionType] as { action: string; shortcut: unknown; diff --git a/src/custom-electron-prompt.d.ts b/src/custom-electron-prompt.d.ts index 6f08d3d1..bdbdc87f 100644 --- a/src/custom-electron-prompt.d.ts +++ b/src/custom-electron-prompt.d.ts @@ -53,33 +53,45 @@ declare module 'custom-electron-prompt' { export interface CounterPromptOptions extends BasePromptOptions<'counter'> { counterOptions: CounterOptions; } - export interface MultiInputPromptOptions extends BasePromptOptions<'multiInput'> { + export interface MultiInputPromptOptions + extends BasePromptOptions<'multiInput'> { multiInputOptions: InputOptions[]; } export interface KeybindPromptOptions extends BasePromptOptions<'keybind'> { keybindOptions: KeybindOptions[]; } - export type PromptOptions = ( - T extends 'input' ? InputPromptOptions : - T extends 'select' ? SelectPromptOptions : - T extends 'counter' ? CounterPromptOptions : - T extends 'keybind' ? KeybindPromptOptions : - T extends 'multiInput' ? MultiInputPromptOptions : - never - ); + export type PromptOptions = T extends 'input' + ? InputPromptOptions + : T extends 'select' + ? SelectPromptOptions + : T extends 'counter' + ? CounterPromptOptions + : T extends 'keybind' + ? KeybindPromptOptions + : T extends 'multiInput' + ? MultiInputPromptOptions + : never; - type PromptResult = T extends 'input' ? string : - T extends 'select' ? string : - T extends 'counter' ? number : - T extends 'keybind' ? { - value: string; - accelerator: string - }[] : - T extends 'multiInput' ? string[] : - never; + type PromptResult = T extends 'input' + ? string + : T extends 'select' + ? string + : T extends 'counter' + ? number + : T extends 'keybind' + ? { + value: string; + accelerator: string; + }[] + : T extends 'multiInput' + ? string[] + : never; - const prompt: (options?: PromptOptions & { type: T }, parent?: BrowserWindow) => Promise | null>; + const prompt: ( + options?: PromptOptions & { type: T }, + parent?: BrowserWindow, + ) => Promise | null>; export default prompt; } diff --git a/src/error.html b/src/error.html index 5aa9d552..6336b1e9 100644 --- a/src/error.html +++ b/src/error.html @@ -1,50 +1,50 @@ - + - - + + Cannot load YouTube Music - + - -
-

Cannot load YouTube Music… Internet disconnected?

- Retry -
- + +
+

Cannot load YouTube Music… Internet disconnected?

+ Retry +
+ diff --git a/src/index.ts b/src/index.ts index 414c20f6..18de66f6 100644 --- a/src/index.ts +++ b/src/index.ts @@ -165,7 +165,10 @@ const initHook = (win: BrowserWindow) => { const mainPlugin = getAllLoadedMainPlugins()[id]; if (mainPlugin) { if (config.enabled && typeof mainPlugin.backend !== 'function') { - mainPlugin.backend?.onConfigChange?.call(mainPlugin.backend, config); + mainPlugin.backend?.onConfigChange?.call( + mainPlugin.backend, + config, + ); } } @@ -282,7 +285,6 @@ async function createMainWindow() { await loadAllMainPlugins(win); - if (windowPosition) { const { x: windowX, y: windowY } = windowPosition; const winSize = win.getSize(); @@ -317,7 +319,6 @@ async function createMainWindow() { } } - if (windowMaximized) { win.maximize(); } diff --git a/src/loader/main.ts b/src/loader/main.ts index 52c6cbb1..ed78300b 100644 --- a/src/loader/main.ts +++ b/src/loader/main.ts @@ -9,9 +9,15 @@ import { LoggerPrefix, startPlugin, stopPlugin } from '@/utils'; import type { PluginConfig, PluginDef } from '@/types/plugins'; import type { BackendContext } from '@/types/contexts'; -const loadedPluginMap: Record> = {}; +const loadedPluginMap: Record< + string, + PluginDef +> = {}; -const createContext = (id: string, win: BrowserWindow): BackendContext => ({ +const createContext = ( + id: string, + win: BrowserWindow, +): BackendContext => ({ getConfig: () => deepmerge( allPlugins[id].config ?? { enabled: false }, @@ -36,7 +42,7 @@ const createContext = (id: string, win: BrowserWindow): BackendContext { ipcMain.removeHandler(event); - } + }, }, window: win, @@ -56,19 +62,15 @@ export const forceUnloadMainPlugin = async ( }); if ( hasStopped || - ( - hasStopped === null && - typeof plugin.backend !== 'function' && plugin.backend - ) + (hasStopped === null && + typeof plugin.backend !== 'function' && + plugin.backend) ) { delete loadedPluginMap[id]; console.log(LoggerPrefix, `"${id}" plugin is unloaded`); return; } else { - console.log( - LoggerPrefix, - `Cannot unload "${id}" plugin`, - ); + console.log(LoggerPrefix, `Cannot unload "${id}" plugin`); return Promise.reject(); } } catch (err) { @@ -92,10 +94,9 @@ export const forceLoadMainPlugin = async ( }); if ( hasStarted || - ( - hasStarted === null && - typeof plugin.backend !== 'function' && plugin.backend - ) + (hasStarted === null && + typeof plugin.backend !== 'function' && + plugin.backend) ) { loadedPluginMap[id] = plugin; } else { @@ -103,10 +104,7 @@ export const forceLoadMainPlugin = async ( return Promise.reject(); } } catch (err) { - console.error( - LoggerPrefix, - `Cannot initialize "${id}" plugin: `, - ); + console.error(LoggerPrefix, `Cannot initialize "${id}" plugin: `); console.trace(err); return Promise.reject(err); } @@ -135,7 +133,9 @@ export const unloadAllMainPlugins = async (win: BrowserWindow) => { } }; -export const getLoadedMainPlugin = (id: string): PluginDef | undefined => { +export const getLoadedMainPlugin = ( + id: string, +): PluginDef | undefined => { return loadedPluginMap[id]; }; diff --git a/src/loader/menu.ts b/src/loader/menu.ts index f44d886e..cb01cecb 100644 --- a/src/loader/menu.ts +++ b/src/loader/menu.ts @@ -11,7 +11,10 @@ import type { BrowserWindow, MenuItemConstructorOptions } from 'electron'; import type { PluginConfig } from '@/types/plugins'; const menuTemplateMap: Record = {}; -const createContext = (id: string, win: BrowserWindow): MenuContext => ({ +const createContext = ( + id: string, + win: BrowserWindow, +): MenuContext => ({ getConfig: () => deepmerge( allPlugins[id].config ?? { enabled: false }, @@ -43,8 +46,7 @@ export const forceLoadMenuPlugin = async (id: string, win: BrowserWindow) => { } else { return; } - } - else return; + } else return; console.log(LoggerPrefix, `Successfully loaded '${id}::menu'`); } catch (err) { @@ -57,7 +59,10 @@ export const loadAllMenuPlugins = async (win: BrowserWindow) => { const pluginConfigs = config.plugins.getPlugins(); for (const [pluginId, pluginDef] of Object.entries(allPlugins)) { - const config = deepmerge(pluginDef.config ?? { enabled: false }, pluginConfigs[pluginId] ?? {}); + const config = deepmerge( + pluginDef.config ?? { enabled: false }, + pluginConfigs[pluginId] ?? {}, + ); if (config.enabled) { await forceLoadMenuPlugin(pluginId, win); diff --git a/src/loader/preload.ts b/src/loader/preload.ts index 74eb9bf7..2c08fe43 100644 --- a/src/loader/preload.ts +++ b/src/loader/preload.ts @@ -8,7 +8,10 @@ import config from '@/config'; import type { PreloadContext } from '@/types/contexts'; import type { PluginConfig, PluginDef } from '@/types/plugins'; -const loadedPluginMap: Record> = {}; +const loadedPluginMap: Record< + string, + PluginDef +> = {}; const createContext = (id: string): PreloadContext => ({ getConfig: () => deepmerge( @@ -27,13 +30,7 @@ export const forceUnloadPreloadPlugin = async (id: string) => { ctx: 'preload', context: createContext(id), }); - if ( - hasStopped || - ( - hasStopped === null && - loadedPluginMap[id].preload - ) - ) { + if (hasStopped || (hasStopped === null && loadedPluginMap[id].preload)) { console.log(LoggerPrefix, `"${id}" plugin is unloaded`); delete loadedPluginMap[id]; } else { @@ -53,20 +50,16 @@ export const forceLoadPreloadPlugin = async (id: string) => { if ( hasStarted || - ( - hasStarted === null && - typeof plugin.preload !== 'function' && plugin.preload - ) + (hasStarted === null && + typeof plugin.preload !== 'function' && + plugin.preload) ) { loadedPluginMap[id] = plugin; } console.log(LoggerPrefix, `"${id}" plugin is loaded`); } catch (err) { - console.error( - LoggerPrefix, - `Cannot initialize "${id}" plugin: `, - ); + console.error(LoggerPrefix, `Cannot initialize "${id}" plugin: `); console.trace(err); } }; @@ -75,7 +68,10 @@ export const loadAllPreloadPlugins = () => { const pluginConfigs = config.plugins.getPlugins(); for (const [pluginId, pluginDef] of Object.entries(preloadPlugins)) { - const config = deepmerge(pluginDef.config ?? { enable: false }, pluginConfigs[pluginId] ?? {}); + const config = deepmerge( + pluginDef.config ?? { enable: false }, + pluginConfigs[pluginId] ?? {}, + ); if (config.enabled) { forceLoadPreloadPlugin(pluginId); @@ -93,7 +89,9 @@ export const unloadAllPreloadPlugins = async () => { } }; -export const getLoadedPreloadPlugin = (id: string): PluginDef | undefined => { +export const getLoadedPreloadPlugin = ( + id: string, +): PluginDef | undefined => { return loadedPluginMap[id]; }; diff --git a/src/loader/renderer.ts b/src/loader/renderer.ts index 7c5bafef..cf8391f9 100644 --- a/src/loader/renderer.ts +++ b/src/loader/renderer.ts @@ -8,9 +8,14 @@ import type { RendererContext } from '@/types/contexts'; import type { PluginConfig, PluginDef } from '@/types/plugins'; const unregisterStyleMap: Record void)[]> = {}; -const loadedPluginMap: Record> = {}; +const loadedPluginMap: Record< + string, + PluginDef +> = {}; -export const createContext = (id: string): RendererContext => ({ +export const createContext = ( + id: string, +): RendererContext => ({ getConfig: async () => window.ipcRenderer.invoke('get-config', id), setConfig: async (newConfig) => { await window.ipcRenderer.invoke('set-config', id, newConfig); @@ -19,7 +24,8 @@ export const createContext = (id: string): Renderer send: (event: string, ...args: unknown[]) => { window.ipcRenderer.send(event, ...args); }, - invoke: (event: string, ...args: unknown[]) => window.ipcRenderer.invoke(event, ...args), + invoke: (event: string, ...args: unknown[]) => + window.ipcRenderer.invoke(event, ...args), on: (event: string, listener: CallableFunction) => { window.ipcRenderer.on(event, (_, ...args: unknown[]) => { listener(...args); @@ -27,7 +33,7 @@ export const createContext = (id: string): Renderer }, removeAllListeners: (event: string) => { window.ipcRenderer.removeAllListeners(event); - } + }, }, }); @@ -40,17 +46,14 @@ export const forceUnloadRendererPlugin = async (id: string) => { const plugin = rendererPlugins[id]; if (!plugin) return; - const hasStopped = await stopPlugin(id, plugin, { ctx: 'renderer', context: createContext(id) }); + const hasStopped = await stopPlugin(id, plugin, { + ctx: 'renderer', + context: createContext(id), + }); if (plugin?.stylesheets) { document.querySelector(`style#plugin-${id}`)?.remove(); } - if ( - hasStopped || - ( - hasStopped === null && - plugin?.renderer - ) - ) { + if (hasStopped || (hasStopped === null && plugin?.renderer)) { console.log(LoggerPrefix, `"${id}" plugin is unloaded`); } else { console.error(LoggerPrefix, `Cannot stop "${id}" plugin`); @@ -69,10 +72,9 @@ export const forceLoadRendererPlugin = async (id: string) => { if ( hasEvaled || plugin?.stylesheets || - ( - hasEvaled === null && - typeof plugin?.renderer !== 'function' && plugin?.renderer - ) + (hasEvaled === null && + typeof plugin?.renderer !== 'function' && + plugin?.renderer) ) { loadedPluginMap[id] = plugin; @@ -84,7 +86,10 @@ export const forceLoadRendererPlugin = async (id: string) => { return styleSheet; }); - document.adoptedStyleSheets = [...document.adoptedStyleSheets, ...styleSheetList]; + document.adoptedStyleSheets = [ + ...document.adoptedStyleSheets, + ...styleSheetList, + ]; } console.log(LoggerPrefix, `"${id}" plugin is loaded`); @@ -97,7 +102,7 @@ export const loadAllRendererPlugins = async () => { const pluginConfigs = window.mainConfig.plugins.getPlugins(); for (const [pluginId, pluginDef] of Object.entries(rendererPlugins)) { - const config = deepmerge(pluginDef.config, pluginConfigs[pluginId] ?? {}) ; + const config = deepmerge(pluginDef.config, pluginConfigs[pluginId] ?? {}); if (config.enabled) { await forceLoadRendererPlugin(pluginId); @@ -115,7 +120,9 @@ export const unloadAllRendererPlugins = async () => { } }; -export const getLoadedRendererPlugin = (id: string): PluginDef | undefined => { +export const getLoadedRendererPlugin = ( + id: string, +): PluginDef | undefined => { return loadedPluginMap[id]; }; diff --git a/src/menu.ts b/src/menu.ts index 1fa0506a..e3295b70 100644 --- a/src/menu.ts +++ b/src/menu.ts @@ -53,7 +53,9 @@ export const refreshMenu = async (win: BrowserWindow) => { } }; -export const mainMenuTemplate = async (win: BrowserWindow): Promise => { +export const mainMenuTemplate = async ( + win: BrowserWindow, +): Promise => { const innerRefreshMenu = () => refreshMenu(win); await loadAllMenuPlugins(win); @@ -453,7 +455,7 @@ export const mainMenuTemplate = async (win: BrowserWindow): Promise { - const menuTemplate: MenuTemplate = [...await mainMenuTemplate(win)]; + const menuTemplate: MenuTemplate = [...(await mainMenuTemplate(win))]; if (process.platform === 'darwin') { const { name } = app; menuTemplate.unshift({ diff --git a/src/navigation.d.ts b/src/navigation.d.ts index 7d80de52..c2912a2a 100644 --- a/src/navigation.d.ts +++ b/src/navigation.d.ts @@ -62,7 +62,10 @@ interface Navigation extends EventTarget { onnavigateerror: ((this: Navigation, ev: Event) => any) | null; oncurrententrychange: ((this: Navigation, ev: Event) => any) | null; - addEventListener(name: K, listener: (event: NavigationEventsMap[K]) => void); + addEventListener( + name: K, + listener: (event: NavigationEventsMap[K]) => void, + ); } declare class NavigateEvent extends Event { @@ -84,5 +87,5 @@ type NavigationHistoryBehavior = 'auto' | 'push' | 'replace'; declare const Navigation: { prototype: Navigation; - new(): Navigation; + new (): Navigation; }; diff --git a/src/plugins/adblocker/blocker.ts b/src/plugins/adblocker/blocker.ts index 2deddf3f..451b5083 100644 --- a/src/plugins/adblocker/blocker.ts +++ b/src/plugins/adblocker/blocker.ts @@ -30,19 +30,19 @@ export const loadAdBlockerEngine = async ( if (!fs.existsSync(cacheDirectory)) { fs.mkdirSync(cacheDirectory); } - const cachingOptions - = cache && additionalBlockLists.length === 0 - ? { - path: path.join(cacheDirectory, 'adblocker-engine.bin'), - read: promises.readFile, - write: promises.writeFile, - } - : undefined; + const cachingOptions = + cache && additionalBlockLists.length === 0 + ? { + path: path.join(cacheDirectory, 'adblocker-engine.bin'), + read: promises.readFile, + write: promises.writeFile, + } + : undefined; const lists = [ - ...( - (disableDefaultLists && !Array.isArray(disableDefaultLists)) || - (Array.isArray(disableDefaultLists) && disableDefaultLists.length > 0) ? [] : SOURCES - ), + ...((disableDefaultLists && !Array.isArray(disableDefaultLists)) || + (Array.isArray(disableDefaultLists) && disableDefaultLists.length > 0) + ? [] + : SOURCES), ...additionalBlockLists, ]; @@ -72,4 +72,5 @@ export const unloadAdBlockerEngine = (session: Electron.Session) => { } }; -export const isBlockerEnabled = (session: Electron.Session) => blocker !== undefined && blocker.isBlockingEnabled(session); +export const isBlockerEnabled = (session: Electron.Session) => + blocker !== undefined && blocker.isBlockingEnabled(session); diff --git a/src/plugins/adblocker/index.ts b/src/plugins/adblocker/index.ts index bae1a802..755405a5 100644 --- a/src/plugins/adblocker/index.ts +++ b/src/plugins/adblocker/index.ts @@ -1,6 +1,10 @@ import { blockers } from './types'; import { createPlugin } from '@/utils'; -import { isBlockerEnabled, loadAdBlockerEngine, unloadAdBlockerEngine } from './blocker'; +import { + isBlockerEnabled, + loadAdBlockerEngine, + unloadAdBlockerEngine, +} from './blocker'; import injectCliqzPreload from './injectors/inject-cliqz-preload'; import { inject, isInjected } from './injectors/inject'; @@ -22,7 +26,7 @@ interface AdblockerConfig { * Which adblocker to use. * @default blockers.InPlayer */ - blocker: typeof blockers[keyof typeof blockers]; + blocker: (typeof blockers)[keyof typeof blockers]; /** * Additional list of filters to use. * @example ["https://raw.githubusercontent.com/uBlockOrigin/uAssets/master/filters/filters.txt"] @@ -86,7 +90,10 @@ export default createPlugin({ }, async onConfigChange(newConfig) { if (this.mainWindow) { - if (newConfig.blocker === blockers.WithBlocklists && !isBlockerEnabled(this.mainWindow.webContents.session)) { + if ( + newConfig.blocker === blockers.WithBlocklists && + !isBlockerEnabled(this.mainWindow.webContents.session) + ) { await loadAdBlockerEngine( this.mainWindow.webContents.session, newConfig.cache, @@ -117,5 +124,5 @@ export default createPlugin({ } } }, - } + }, }); diff --git a/src/plugins/adblocker/injectors/inject.js b/src/plugins/adblocker/injectors/inject.js index 12ded09a..9cc15bae 100644 --- a/src/plugins/adblocker/injectors/inject.js +++ b/src/plugins/adblocker/injectors/inject.js @@ -73,8 +73,7 @@ export const inject = () => { } case 'noopFunc': { - cValue = function () { - }; + cValue = function () {}; break; } @@ -103,7 +102,7 @@ export const inject = () => { return; } - if (Math.abs(cValue) > 0x7F_FF) { + if (Math.abs(cValue) > 0x7f_ff) { return; } } else { @@ -119,12 +118,12 @@ export const inject = () => { return true; } - aborted - = v !== undefined - && v !== null - && cValue !== undefined - && cValue !== null - && typeof v !== typeof cValue; + aborted = + v !== undefined && + v !== null && + cValue !== undefined && + cValue !== null && + typeof v !== typeof cValue; return aborted; }; @@ -272,8 +271,7 @@ export const inject = () => { } case 'noopFunc': { - cValue = function () { - }; + cValue = function () {}; break; } @@ -302,7 +300,7 @@ export const inject = () => { return; } - if (Math.abs(cValue) > 0x7F_FF) { + if (Math.abs(cValue) > 0x7f_ff) { return; } } else { @@ -318,12 +316,12 @@ export const inject = () => { return true; } - aborted - = v !== undefined - && v !== null - && cValue !== undefined - && cValue !== null - && typeof v !== typeof cValue; + aborted = + v !== undefined && + v !== null && + cValue !== undefined && + cValue !== null && + typeof v !== typeof cValue; return aborted; }; diff --git a/src/plugins/album-color-theme/index.ts b/src/plugins/album-color-theme/index.ts index 96983031..c46d858a 100644 --- a/src/plugins/album-color-theme/index.ts +++ b/src/plugins/album-color-theme/index.ts @@ -8,7 +8,8 @@ import type { VideoDataChanged } from '@/types/video-data-changed'; export default createPlugin({ name: 'Album Color Theme', - description: 'Applies a dynamic theme and visual effects based on the album color palette', + description: + 'Applies a dynamic theme and visual effects based on the album color palette', restartNeeded: true, config: { enabled: false, @@ -62,13 +63,18 @@ export default createPlugin({ l = +(l * 100).toFixed(1); //return "hsl(" + h + "," + s + "%," + l + "%)"; - return [h,s,l]; + return [h, s, l]; }, hue: 0, saturation: 0, lightness: 0, - changeElementColor: (element: HTMLElement | null, hue: number, saturation: number, lightness: number) => { + changeElementColor: ( + element: HTMLElement | null, + hue: number, + saturation: number, + lightness: number, + ) => { if (element) { element.style.backgroundColor = `hsl(${hue}, ${saturation}%, ${lightness}%)`; } @@ -84,19 +90,32 @@ export default createPlugin({ start() { this.playerPage = document.querySelector('#player-page'); - this.navBarBackground = document.querySelector('#nav-bar-background'); - this.ytmusicPlayerBar = document.querySelector('ytmusic-player-bar'); - this.playerBarBackground = document.querySelector('#player-bar-background'); + this.navBarBackground = document.querySelector( + '#nav-bar-background', + ); + this.ytmusicPlayerBar = + document.querySelector('ytmusic-player-bar'); + this.playerBarBackground = document.querySelector( + '#player-bar-background', + ); this.sidebarBig = document.querySelector('#guide-wrapper'); - this.sidebarSmall = document.querySelector('#mini-guide-background'); + this.sidebarSmall = document.querySelector( + '#mini-guide-background', + ); this.ytmusicAppLayout = document.querySelector('#layout'); const observer = new MutationObserver((mutationsList) => { for (const mutation of mutationsList) { if (mutation.type === 'attributes') { - const isPageOpen = this.ytmusicAppLayout?.hasAttribute('player-page-open'); + const isPageOpen = + this.ytmusicAppLayout?.hasAttribute('player-page-open'); if (isPageOpen) { - this.changeElementColor(this.sidebarSmall, this.hue, this.saturation, this.lightness - 30); + this.changeElementColor( + this.sidebarSmall, + this.hue, + this.saturation, + this.lightness - 30, + ); } else { if (this.sidebarSmall) { this.sidebarSmall.style.backgroundColor = 'black'; @@ -113,35 +132,84 @@ export default createPlugin({ onPlayerApiReady(playerApi) { const fastAverageColor = new FastAverageColor(); - document.addEventListener('videodatachange', (event: CustomEvent) => { - if (event.detail.name === 'dataloaded') { - const playerResponse = playerApi.getPlayerResponse(); - const thumbnail = playerResponse?.videoDetails?.thumbnail?.thumbnails?.at(0); - if (thumbnail) { - fastAverageColor.getColorAsync(thumbnail.url) - .then((albumColor) => { - if (albumColor) { - const [hue, saturation, lightness] = [this.hue, this.saturation, this.lightness] = this.hexToHSL(albumColor.hex); - this.changeElementColor(this.playerPage, hue, saturation, lightness - 30); - this.changeElementColor(this.navBarBackground, hue, saturation, lightness - 15); - this.changeElementColor(this.ytmusicPlayerBar, hue, saturation, lightness - 15); - this.changeElementColor(this.playerBarBackground, hue, saturation, lightness - 15); - this.changeElementColor(this.sidebarBig, hue, saturation, lightness - 15); - if (this.ytmusicAppLayout?.hasAttribute('player-page-open')) { - this.changeElementColor(this.sidebarSmall, hue, saturation, lightness - 30); + document.addEventListener( + 'videodatachange', + (event: CustomEvent) => { + if (event.detail.name === 'dataloaded') { + const playerResponse = playerApi.getPlayerResponse(); + const thumbnail = + playerResponse?.videoDetails?.thumbnail?.thumbnails?.at(0); + if (thumbnail) { + fastAverageColor + .getColorAsync(thumbnail.url) + .then((albumColor) => { + if (albumColor) { + const [hue, saturation, lightness] = ([ + this.hue, + this.saturation, + this.lightness, + ] = this.hexToHSL(albumColor.hex)); + this.changeElementColor( + this.playerPage, + hue, + saturation, + lightness - 30, + ); + this.changeElementColor( + this.navBarBackground, + hue, + saturation, + lightness - 15, + ); + this.changeElementColor( + this.ytmusicPlayerBar, + hue, + saturation, + lightness - 15, + ); + this.changeElementColor( + this.playerBarBackground, + hue, + saturation, + lightness - 15, + ); + this.changeElementColor( + this.sidebarBig, + hue, + saturation, + lightness - 15, + ); + if ( + this.ytmusicAppLayout?.hasAttribute('player-page-open') + ) { + this.changeElementColor( + this.sidebarSmall, + hue, + saturation, + lightness - 30, + ); + } + const ytRightClickList = + document.querySelector( + 'tp-yt-paper-listbox', + ); + this.changeElementColor( + ytRightClickList, + hue, + saturation, + lightness - 15, + ); + } else { + if (this.playerPage) { + this.playerPage.style.backgroundColor = '#000000'; + } } - const ytRightClickList = document.querySelector('tp-yt-paper-listbox'); - this.changeElementColor(ytRightClickList, hue, saturation, lightness - 15); - } else { - if (this.playerPage) { - this.playerPage.style.backgroundColor = '#000000'; - } - } - }) - .catch((e) => console.error(e)); + }) + .catch((e) => console.error(e)); + } } - } - }); + }, + ); }, - } + }, }); diff --git a/src/plugins/album-color-theme/style.css b/src/plugins/album-color-theme/style.css index 83fe51bc..4577d7bb 100644 --- a/src/plugins/album-color-theme/style.css +++ b/src/plugins/album-color-theme/style.css @@ -4,23 +4,33 @@ yt-page-navigation-progress { } #player-page { - transition: transform 300ms,background-color 300ms cubic-bezier(0.2,0,0.6,1) !important; + transition: + transform 300ms, + background-color 300ms cubic-bezier(0.2, 0, 0.6, 1) !important; } #nav-bar-background { - transition: opacity 200ms,background-color 300ms cubic-bezier(0.2,0,0.6,1) !important; + transition: + opacity 200ms, + background-color 300ms cubic-bezier(0.2, 0, 0.6, 1) !important; } #mini-guide-background { - transition: opacity 200ms,background-color 300ms cubic-bezier(0.2,0,0.6,1) !important; + transition: + opacity 200ms, + background-color 300ms cubic-bezier(0.2, 0, 0.6, 1) !important; border-right: 0px !important; } #guide-wrapper { - transition: opacity 200ms,background-color 300ms cubic-bezier(0.2,0,0.6,1) !important; + transition: + opacity 200ms, + background-color 300ms cubic-bezier(0.2, 0, 0.6, 1) !important; } -#img, #player, .song-media-controls.style-scope.ytmusic-player { +#img, +#player, +.song-media-controls.style-scope.ytmusic-player { border-radius: 2% !important; } diff --git a/src/plugins/ambient-mode/index.ts b/src/plugins/ambient-mode/index.ts index b7f1cd1f..53eeb46f 100644 --- a/src/plugins/ambient-mode/index.ts +++ b/src/plugins/ambient-mode/index.ts @@ -25,7 +25,8 @@ const defaultConfig: AmbientModePluginConfig = { export default createPlugin({ name: 'Ambient Mode', - description: 'Applies a lighting effect by casting gentle colors from the video, into your screen’s background.', + description: + 'Applies a lighting effect by casting gentle colors from the video, into your screen’s background.', restartNeeded: false, config: defaultConfig, stylesheets: [style], @@ -133,7 +134,9 @@ export default createPlugin({ start() { const injectBlurVideo = (): (() => void) | null => { const songVideo = document.querySelector('#song-video'); - const video = document.querySelector('#song-video .html5-video-container > video'); + const video = document.querySelector( + '#song-video .html5-video-container > video', + ); const wrapper = document.querySelector('#song-video > .player-wrapper'); if (!songVideo) return null; @@ -143,27 +146,34 @@ export default createPlugin({ const blurCanvas = document.createElement('canvas'); blurCanvas.classList.add('html5-blur-canvas'); - const context = blurCanvas.getContext('2d', { willReadFrequently: true }); + const context = blurCanvas.getContext('2d', { + willReadFrequently: true, + }); /* effect */ let lastEffectWorkId: number | null = null; let lastImageData: ImageData | null = null; const onSync = () => { - if (typeof lastEffectWorkId === 'number') cancelAnimationFrame(lastEffectWorkId); + if (typeof lastEffectWorkId === 'number') + cancelAnimationFrame(lastEffectWorkId); lastEffectWorkId = requestAnimationFrame(() => { // console.log('context', context); if (!context) return; const width = this.qualityRatio; - let height = Math.max(Math.floor(blurCanvas.height / blurCanvas.width * width), 1); + let height = Math.max( + Math.floor((blurCanvas.height / blurCanvas.width) * width), + 1, + ); if (!Number.isFinite(height)) height = width; if (!height) return; context.globalAlpha = 1; if (lastImageData) { - const frameOffset = (1 / this.buffer) * (1000 / this.interpolationTime); + const frameOffset = + (1 / this.buffer) * (1000 / this.interpolationTime); context.globalAlpha = 1 - (frameOffset * 2); // because of alpha value must be < 1 context.putImageData(lastImageData, 0, 0); context.globalAlpha = frameOffset; @@ -185,15 +195,17 @@ export default createPlugin({ if (newWidth === 0 || newHeight === 0) return; blurCanvas.width = this.qualityRatio; - blurCanvas.height = Math.floor(newHeight / newWidth * this.qualityRatio); + blurCanvas.height = Math.floor( + (newHeight / newWidth) * this.qualityRatio, + ); blurCanvas.style.width = `${newWidth * this.sizeRatio}px`; blurCanvas.style.height = `${newHeight * this.sizeRatio}px`; if (this.isFullscreen) blurCanvas.classList.add('fullscreen'); else blurCanvas.classList.remove('fullscreen'); - const leftOffset = newWidth * (this.sizeRatio - 1) / 2; - const topOffset = newHeight * (this.sizeRatio - 1) / 2; + const leftOffset = (newWidth * (this.sizeRatio - 1)) / 2; + const topOffset = (newHeight * (this.sizeRatio - 1)) / 2; blurCanvas.style.setProperty('--left', `${-1 * leftOffset}px`); blurCanvas.style.setProperty('--top', `${-1 * topOffset}px`); blurCanvas.style.setProperty('--blur', `${this.blur}px`); @@ -214,7 +226,10 @@ export default createPlugin({ /* hooking */ let canvasInterval: NodeJS.Timeout | null = null; - canvasInterval = setInterval(onSync, Math.max(1, Math.ceil(1000 / this.buffer))); + canvasInterval = setInterval( + onSync, + Math.max(1, Math.ceil(1000 / this.buffer)), + ); applyVideoAttributes(); observer.observe(songVideo, { attributes: true }); resizeObserver.observe(songVideo); @@ -226,7 +241,10 @@ export default createPlugin({ }; const onPlay = () => { if (canvasInterval) clearInterval(canvasInterval); - canvasInterval = setInterval(onSync, Math.max(1, Math.ceil(1000 / this.buffer))); + canvasInterval = setInterval( + onSync, + Math.max(1, Math.ceil(1000 / this.buffer)), + ); }; songVideo.addEventListener('pause', onPause); songVideo.addEventListener('play', onPlay); @@ -249,7 +267,6 @@ export default createPlugin({ }; }; - const playerPage = document.querySelector('#player-page'); const ytmusicAppLayout = document.querySelector('#layout'); @@ -262,7 +279,8 @@ export default createPlugin({ const observer = new MutationObserver((mutationsList) => { for (const mutation of mutationsList) { if (mutation.type === 'attributes') { - const isPageOpen = ytmusicAppLayout?.hasAttribute('player-page-open'); + const isPageOpen = + ytmusicAppLayout?.hasAttribute('player-page-open'); if (isPageOpen) { this.unregister?.(); this.unregister = injectBlurVideo() ?? null; @@ -293,6 +311,6 @@ export default createPlugin({ this.observer?.disconnect(); this.update = null; this.unregister?.(); - } - } + }, + }, }); diff --git a/src/plugins/audio-compressor.ts b/src/plugins/audio-compressor.ts index 49a29192..e66d6a33 100644 --- a/src/plugins/audio-compressor.ts +++ b/src/plugins/audio-compressor.ts @@ -2,7 +2,8 @@ import { createPlugin } from '@/utils'; export default createPlugin({ name: 'Audio Compressor', - description: 'Apply compression to audio (lowers the volume of the loudest parts of the signal and raises the volume of the softest parts)', + description: + 'Apply compression to audio (lowers the volume of the loudest parts of the signal and raises the volume of the softest parts)', renderer() { document.addEventListener( diff --git a/src/plugins/bypass-age-restrictions/index.ts b/src/plugins/bypass-age-restrictions/index.ts index 5800fd95..7fa7f270 100644 --- a/src/plugins/bypass-age-restrictions/index.ts +++ b/src/plugins/bypass-age-restrictions/index.ts @@ -2,7 +2,7 @@ import { createPlugin } from '@/utils'; export default createPlugin({ name: 'Bypass Age Restrictions', - description: 'bypass YouTube\'s age verification', + description: "bypass YouTube's age verification", restartNeeded: true, // See https://github.com/zerodytrash/Simple-YouTube-Age-Restriction-Bypass#userscript diff --git a/src/plugins/captions-selector/templates/captions-settings-template.html b/src/plugins/captions-selector/templates/captions-settings-template.html index 682a6cbb..6d30cdb4 100644 --- a/src/plugins/captions-selector/templates/captions-settings-template.html +++ b/src/plugins/captions-selector/templates/captions-settings-template.html @@ -1,16 +1,25 @@ - + - + + d="M20 4H4c-1.103 0-2 .897-2 2v12c0 1.103.897 2 2 2h16c1.103 0 2-.897 2-2V6c0-1.103-.897-2-2-2zm-9 6H8v4h3v2H8c-1.103 0-2-.897-2-2v-4c0-1.103.897-2 2-2h3v2zm7 0h-3v4h3v2h-3c-1.103 0-2-.897-2-2v-4c0-1.103.897-2 2-2h3v2z" + > diff --git a/src/plugins/compact-sidebar/index.ts b/src/plugins/compact-sidebar/index.ts index 89387f76..9a4c5f97 100644 --- a/src/plugins/compact-sidebar/index.ts +++ b/src/plugins/compact-sidebar/index.ts @@ -18,7 +18,10 @@ export default createPlugin< getCompactSidebar: () => document.querySelector('#mini-guide'), isCompactSidebarDisabled() { const compactSidebar = this.getCompactSidebar(); - return compactSidebar === null || window.getComputedStyle(compactSidebar).display === 'none'; + return ( + compactSidebar === null || + window.getComputedStyle(compactSidebar).display === 'none' + ); }, start() { if (this.isCompactSidebarDisabled()) { @@ -34,6 +37,6 @@ export default createPlugin< if (this.isCompactSidebarDisabled()) { document.querySelector('#button')?.click(); } - } + }, }, }); diff --git a/src/plugins/crossfade/fader.ts b/src/plugins/crossfade/fader.ts index 0066f8c2..5e8630d1 100644 --- a/src/plugins/crossfade/fader.ts +++ b/src/plugins/crossfade/fader.ts @@ -20,14 +20,16 @@ const validateVolumeLevel = (value: number) => { // Number between 0 and 1? if (!Number.isNaN(value) && value >= 0 && value <= 1) { // Yup, that's fine - } else { // Abort and throw an exception throw new TypeError('Number between 0 and 1 expected as volume!'); } }; -type VolumeLogger = (message: string, ...args: Params) => void; +type VolumeLogger = ( + message: string, + ...args: Params +) => void; interface VolumeFaderOptions { /** * logging `function(stuff, …)` for execution information (default: no logging) @@ -71,7 +73,6 @@ export class VolumeFader { private active: boolean = false; private fade: VolumeFade | undefined; - /** * VolumeFader Constructor * @@ -119,17 +120,17 @@ export class VolumeFader { // Default dynamic range? if ( - options.fadeScaling === undefined - || options.fadeScaling === 'logarithmic' + options.fadeScaling === undefined || + options.fadeScaling === 'logarithmic' ) { // Set default of 60 dB dynamicRange = 3; } // Custom dynamic range? else if ( - typeof options.fadeScaling === 'number' - && !Number.isNaN(options.fadeScaling) - && options.fadeScaling > 0 + typeof options.fadeScaling === 'number' && + !Number.isNaN(options.fadeScaling) && + options.fadeScaling > 0 ) { // Turn amplitude dB into a multiple of 10 power dB dynamicRange = options.fadeScaling / 2 / 10; @@ -151,13 +152,13 @@ export class VolumeFader { }; // Log setting if not default - options.fadeScaling - && this.logger - && this.logger( - 'Using logarithmic fading with ' - + String(10 * dynamicRange) - + ' dB dynamic range.', - ); + options.fadeScaling && + this.logger && + this.logger( + 'Using logarithmic fading with ' + + String(10 * dynamicRange) + + ' dB dynamic range.', + ); } // Set initial volume? @@ -169,10 +170,8 @@ export class VolumeFader { this.media.volume = options.initialVolume; // Log setting - this.logger - && this.logger( - 'Set initial volume to ' + String(this.media.volume) + '.', - ); + this.logger && + this.logger('Set initial volume to ' + String(this.media.volume) + '.'); } // Fade duration given? @@ -237,8 +236,8 @@ export class VolumeFader { this.fadeDuration = fadeDuration; // Log setting - this.logger - && this.logger('Set fade duration to ' + String(fadeDuration) + ' ms.'); + this.logger && + this.logger('Set fade duration to ' + String(fadeDuration) + ' ms.'); } else { // Abort and throw an exception throw new TypeError('Positive number expected as fade duration!'); @@ -308,13 +307,14 @@ export class VolumeFader { // Time left for fading? if (now < this.fade.time.end) { // Compute current fade progress - const progress - = (now - this.fade.time.start) - / (this.fade.time.end - this.fade.time.start); + const progress = + (now - this.fade.time.start) / + (this.fade.time.end - this.fade.time.start); // Compute current level on internal scale - const level - = (progress * (this.fade.volume.end - this.fade.volume.start)) + this.fade.volume.start; + const level = + (progress * (this.fade.volume.end - this.fade.volume.start)) + + this.fade.volume.start; // Map fade level to volume level and apply it to media element this.media.volume = this.scale.internalToVolume(level); @@ -323,10 +323,8 @@ export class VolumeFader { window.requestAnimationFrame(this.updateVolume.bind(this)); } else { // Log end of fade - this.logger - && this.logger( - 'Fade to ' + String(this.fade.volume.end) + ' complete.', - ); + this.logger && + this.logger('Fade to ' + String(this.fade.volume.end) + ' complete.'); // Time is up, jump to target volume this.media.volume = this.scale.internalToVolume(this.fade.volume.end); @@ -389,5 +387,5 @@ export class VolumeFader { } export default { - VolumeFader + VolumeFader, }; diff --git a/src/plugins/crossfade/index.ts b/src/plugins/crossfade/index.ts index 8147285a..7f6b09d5 100644 --- a/src/plugins/crossfade/index.ts +++ b/src/plugins/crossfade/index.ts @@ -18,7 +18,7 @@ export type CrossfadePluginConfig = { fadeOutDuration: number; secondsBeforeEnd: number; fadeScaling: 'linear' | 'logarithmic' | number; -} +}; export default createPlugin< unknown, @@ -61,7 +61,10 @@ export default createPlugin< fadeScaling: 'linear', }, menu({ window, getConfig, setConfig }) { - const promptCrossfadeValues = async (win: BrowserWindow, options: CrossfadePluginConfig): Promise | undefined> => { + const promptCrossfadeValues = async ( + win: BrowserWindow, + options: CrossfadePluginConfig, + ): Promise | undefined> => { const res = await prompt( { title: 'Crossfade Options', @@ -89,8 +92,7 @@ export default createPlugin< }, { label: 'Crossfade x seconds before end', - value: - options.secondsBeforeEnd, + value: options.secondsBeforeEnd, inputAttrs: { type: 'number', required: true, @@ -135,7 +137,10 @@ export default createPlugin< { label: 'Advanced', async click() { - const newOptions = await promptCrossfadeValues(window, await getConfig()); + const newOptions = await promptCrossfadeValues( + window, + await getConfig(), + ); if (newOptions) { setConfig(newOptions); } @@ -170,11 +175,14 @@ export default createPlugin< let firstVideo = true; let waitForTransition: Promise; - const getStreamURL = async (videoID: string): Promise => this.ipc?.invoke('audio-url', videoID); + const getStreamURL = async (videoID: string): Promise => + this.ipc?.invoke('audio-url', videoID); - const getVideoIDFromURL = (url: string) => new URLSearchParams(url.split('?')?.at(-1)).get('v'); + const getVideoIDFromURL = (url: string) => + new URLSearchParams(url.split('?')?.at(-1)).get('v'); - const isReadyToCrossfade = () => transitionAudio && transitionAudio.state() === 'loaded'; + const isReadyToCrossfade = () => + transitionAudio && transitionAudio.state() === 'loaded'; const watchVideoIDChanges = (cb: (id: string) => void) => { window.navigation.addEventListener('navigate', (event) => { @@ -184,9 +192,9 @@ export default createPlugin< const nextVideoID = getVideoIDFromURL(event.destination.url ?? ''); if ( - nextVideoID - && currentVideoID - && (firstVideo || nextVideoID !== currentVideoID) + nextVideoID && + currentVideoID && + (firstVideo || nextVideoID !== currentVideoID) ) { if (isReadyToCrossfade()) { crossfade(() => { @@ -245,8 +253,9 @@ export default createPlugin< // Exit just before the end for the transition const transitionBeforeEnd = () => { if ( - video.currentTime >= video.duration - this.config!.secondsBeforeEnd - && isReadyToCrossfade() + video.currentTime >= + video.duration - this.config!.secondsBeforeEnd && + isReadyToCrossfade() ) { video.removeEventListener('timeupdate', transitionBeforeEnd); @@ -294,6 +303,6 @@ export default createPlugin< createAudioForCrossfade(url); }); - } - } + }, + }, }); diff --git a/src/plugins/disable-autoplay/index.ts b/src/plugins/disable-autoplay/index.ts index a85df599..84d1ce52 100644 --- a/src/plugins/disable-autoplay/index.ts +++ b/src/plugins/disable-autoplay/index.ts @@ -6,7 +6,7 @@ import type { YoutubePlayer } from '@/types/youtube-player'; export type DisableAutoPlayPluginConfig = { enabled: boolean; applyOnce: boolean; -} +}; export default createPlugin< unknown, @@ -53,7 +53,11 @@ export default createPlugin< if (event.detail.name === 'dataloaded') { this.api?.pauseVideo(); - document.querySelector('video')?.addEventListener('timeupdate', this.timeUpdateListener, { once: true }); + document + .querySelector('video') + ?.addEventListener('timeupdate', this.timeUpdateListener, { + once: true, + }); } }, timeUpdateListener(e: Event) { @@ -74,7 +78,6 @@ export default createPlugin< }, onConfigChange(newConfig) { this.config = newConfig; - } - } + }, + }, }); - diff --git a/src/plugins/discord/index.ts b/src/plugins/discord/index.ts index ca3cfc2b..ce86e0e3 100644 --- a/src/plugins/discord/index.ts +++ b/src/plugins/discord/index.ts @@ -32,7 +32,7 @@ export type DiscordPluginConfig = { * Hide the "duration left" in the rich presence */ hideDurationLeft: boolean; -} +}; export default createPlugin({ name: 'Discord Rich Presence', @@ -50,4 +50,3 @@ export default createPlugin({ menu: onMenu, backend, }); - diff --git a/src/plugins/discord/main.ts b/src/plugins/discord/main.ts index 6c82bb14..925dc7cb 100644 --- a/src/plugins/discord/main.ts +++ b/src/plugins/discord/main.ts @@ -10,7 +10,6 @@ import { createBackend } from '@/utils'; import type { DiscordPluginConfig } from './index'; - // Application ID registered by @th-ch/youtube-music dev team const clientId = '1177081335727267940'; @@ -47,13 +46,16 @@ const resetInfo = () => { } }; -const connectTimeout = () => new Promise((resolve, reject) => setTimeout(() => { - if (!info.autoReconnect || info.rpc.isConnected) { - return; - } +const connectTimeout = () => + new Promise((resolve, reject) => + setTimeout(() => { + if (!info.autoReconnect || info.rpc.isConnected) { + return; + } - info.rpc.login().then(resolve).catch(reject); -}, 5000)); + info.rpc.login().then(resolve).catch(reject); + }, 5000), + ); const connectRecursive = () => { if (!info.autoReconnect || info.rpc.isConnected) { return; @@ -106,10 +108,13 @@ export const clear = () => { export const registerRefresh = (cb: () => void) => refreshCallbacks.push(cb); export const isConnected = () => info.rpc !== null; -export const backend = createBackend<{ - config?: DiscordPluginConfig; - updateActivity: (songInfo: SongInfo, config: DiscordPluginConfig) => void; -}, DiscordPluginConfig>({ +export const backend = createBackend< + { + config?: DiscordPluginConfig; + updateActivity: (songInfo: SongInfo, config: DiscordPluginConfig) => void; + }, + DiscordPluginConfig +>({ /** * We get multiple events * Next song: PAUSE(n), PAUSE(n+1), PLAY(n+1) @@ -132,7 +137,11 @@ export const backend = createBackend<{ } // Clear directly if timeout is 0 - if (songInfo.isPaused && config.activityTimeoutEnabled && config.activityTimeoutTime === 0) { + if ( + songInfo.isPaused && + config.activityTimeoutEnabled && + config.activityTimeoutTime === 0 + ) { info.rpc.user?.clearActivity().catch(console.error); return; } @@ -142,10 +151,14 @@ export const backend = createBackend<{ // not all options are transfered through https://github.com/discordjs/RPC/blob/6f83d8d812c87cb7ae22064acd132600407d7d05/src/client.js#L518-530 const hangulFillerUnicodeCharacter = '\u3164'; // This is an empty character if (songInfo.title.length < 2) { - songInfo.title += hangulFillerUnicodeCharacter.repeat(2 - songInfo.title.length); + songInfo.title += hangulFillerUnicodeCharacter.repeat( + 2 - songInfo.title.length, + ); } if (songInfo.artist.length < 2) { - songInfo.artist += hangulFillerUnicodeCharacter.repeat(2 - songInfo.title.length); + songInfo.artist += hangulFillerUnicodeCharacter.repeat( + 2 - songInfo.title.length, + ); } const activityInfo: SetActivity = { @@ -154,11 +167,17 @@ export const backend = createBackend<{ largeImageKey: songInfo.imageSrc ?? '', largeImageText: songInfo.album ?? '', buttons: [ - ...(config.playOnYouTubeMusic ? [{ label: 'Play on YouTube Music', url: songInfo.url ?? '' }] : []), - ...(config.hideGitHubButton ? [] : [{ - label: 'View App On GitHub', - url: 'https://github.com/th-ch/youtube-music' - }]), + ...(config.playOnYouTubeMusic + ? [{ label: 'Play on YouTube Music', url: songInfo.url ?? '' }] + : []), + ...(config.hideGitHubButton + ? [] + : [ + { + label: 'View App On GitHub', + url: 'https://github.com/th-ch/youtube-music', + }, + ]), ], }; @@ -168,14 +187,16 @@ export const backend = createBackend<{ activityInfo.smallImageText = 'Paused'; // Set start the timer so the activity gets cleared after a while if enabled if (config.activityTimeoutEnabled) { - clearActivity = setTimeout(() => info.rpc.user?.clearActivity().catch(console.error), config.activityTimeoutTime ?? 10_000); + clearActivity = setTimeout( + () => info.rpc.user?.clearActivity().catch(console.error), + config.activityTimeoutTime ?? 10_000, + ); } } else if (!config.hideDurationLeft) { // Add the start and end time of the song const songStartTime = Date.now() - ((songInfo.elapsedSeconds ?? 0) * 1000); activityInfo.startTimestamp = songStartTime; - activityInfo.endTimestamp - = songStartTime + (songInfo.songDuration * 1000); + activityInfo.endTimestamp = songStartTime + (songInfo.songDuration * 1000); } info.rpc.user?.setActivity(activityInfo).catch(console.error); diff --git a/src/plugins/discord/menu.ts b/src/plugins/discord/menu.ts index 4fe35d80..99ad270e 100644 --- a/src/plugins/discord/menu.ts +++ b/src/plugins/discord/menu.ts @@ -15,7 +15,12 @@ const registerRefreshOnce = singleton((refreshMenu: () => void) => { registerRefresh(refreshMenu); }); -export const onMenu = async ({ window, getConfig, setConfig, refresh }: MenuContext): Promise => { +export const onMenu = async ({ + window, + getConfig, + setConfig, + refresh, +}: MenuContext): Promise => { const config = await getConfig(); registerRefreshOnce(refresh); @@ -86,16 +91,22 @@ export const onMenu = async ({ window, getConfig, setConfig, refresh }: MenuCont ]; }; -async function setInactivityTimeout(win: Electron.BrowserWindow, options: DiscordPluginConfig) { - const output = await prompt({ - title: 'Set Inactivity Timeout', - label: 'Enter inactivity timeout in seconds:', - value: String(Math.round((options.activityTimeoutTime ?? 0) / 1e3)), - type: 'counter', - counterOptions: { minimum: 0, multiFire: true }, - width: 450, - ...promptOptions(), - }, win); +async function setInactivityTimeout( + win: Electron.BrowserWindow, + options: DiscordPluginConfig, +) { + const output = await prompt( + { + title: 'Set Inactivity Timeout', + label: 'Enter inactivity timeout in seconds:', + value: String(Math.round((options.activityTimeoutTime ?? 0) / 1e3)), + type: 'counter', + counterOptions: { minimum: 0, multiFire: true }, + width: 450, + ...promptOptions(), + }, + win, + ); if (output) { options.activityTimeoutTime = Math.round(~~output * 1e3); diff --git a/src/plugins/downloader/index.ts b/src/plugins/downloader/index.ts index c4a4e328..2982f1c6 100644 --- a/src/plugins/downloader/index.ts +++ b/src/plugins/downloader/index.ts @@ -13,7 +13,7 @@ export type DownloaderPluginConfig = { customPresetSetting: Preset; skipExisting: boolean; playlistMaxItems?: number; -} +}; export const defaultConfig: DownloaderPluginConfig = { enabled: false, @@ -37,6 +37,5 @@ export default createPlugin({ renderer: { start: onRendererLoad, onPlayerApiReady, - } + }, }); - diff --git a/src/plugins/downloader/main/index.ts b/src/plugins/downloader/main/index.ts index fe2d4a27..9845e690 100644 --- a/src/plugins/downloader/main/index.ts +++ b/src/plugins/downloader/main/index.ts @@ -93,7 +93,11 @@ export const getCookieFromWindow = async (win: BrowserWindow) => { let config: DownloaderPluginConfig; -export const onMainLoad = async ({ window: _win, getConfig, ipc }: BackendContext) => { +export const onMainLoad = async ({ + window: _win, + getConfig, + ipc, +}: BackendContext) => { win = _win; config = await getConfig(); @@ -107,7 +111,9 @@ export const onMainLoad = async ({ window: _win, getConfig, ipc }: BackendContex ipc.on('video-src-changed', (data: GetPlayerResponse) => { playingUrl = data.microformat.microformatDataRenderer.urlCanonical; }); - ipc.handle('download-playlist-request', async (url: string) => downloadPlaylist(url)); + ipc.handle('download-playlist-request', async (url: string) => + downloadPlaylist(url), + ); }; export const onConfigChange = (newConfig: DownloaderPluginConfig) => { @@ -230,8 +236,7 @@ async function downloadSongUnsafe( const selectedPreset = config.selectedPreset ?? 'mp3 (256kbps)'; let presetSetting: Preset; if (selectedPreset === 'Custom') { - presetSetting = - config.customPresetSetting ?? DefaultPresetList['Custom']; + presetSetting = config.customPresetSetting ?? DefaultPresetList['Custom']; } else if (selectedPreset === 'Source') { presetSetting = DefaultPresetList['Source']; } else { @@ -444,8 +449,7 @@ export async function downloadPlaylist(givenUrl?: string | URL) { } const playlistId = - getPlaylistID(givenUrl) || - getPlaylistID(new URL(playingUrl)); + getPlaylistID(givenUrl) || getPlaylistID(new URL(playingUrl)); if (!playlistId) { sendError(new Error('No playlist ID found')); diff --git a/src/plugins/downloader/main/utils.ts b/src/plugins/downloader/main/utils.ts index feabc52a..6fced15a 100644 --- a/src/plugins/downloader/main/utils.ts +++ b/src/plugins/downloader/main/utils.ts @@ -1,7 +1,8 @@ import { app, BrowserWindow } from 'electron'; import is from 'electron-is'; -export const getFolder = (customFolder: string) => customFolder || app.getPath('downloads'); +export const getFolder = (customFolder: string) => + customFolder || app.getPath('downloads'); export const defaultMenuDownloadLabel = 'Download playlist'; export const sendFeedback = (win: BrowserWindow, message?: unknown) => { diff --git a/src/plugins/downloader/menu.ts b/src/plugins/downloader/menu.ts index 2b95dcb7..f0322491 100644 --- a/src/plugins/downloader/menu.ts +++ b/src/plugins/downloader/menu.ts @@ -9,7 +9,10 @@ import type { MenuTemplate } from '@/menu'; import type { DownloaderPluginConfig } from './index'; -export const onMenu = async ({ getConfig, setConfig }: MenuContext): Promise => { +export const onMenu = async ({ + getConfig, + setConfig, +}: MenuContext): Promise => { const config = await getConfig(); return [ diff --git a/src/plugins/downloader/renderer.ts b/src/plugins/downloader/renderer.ts index aa7a2d4c..b04f843d 100644 --- a/src/plugins/downloader/renderer.ts +++ b/src/plugins/downloader/renderer.ts @@ -28,7 +28,9 @@ const menuObserver = new MutationObserver(() => { return; } - const menuUrl = document.querySelector('tp-yt-paper-listbox [tabindex="-1"] #navigation-endpoint')?.href; + const menuUrl = document.querySelector( + 'tp-yt-paper-listbox [tabindex="-1"] #navigation-endpoint', + )?.href; if (!menuUrl?.includes('watch?') && doneFirstLoad) { return; } @@ -40,14 +42,18 @@ const menuObserver = new MutationObserver(() => { return; } - setTimeout(() => doneFirstLoad ||= true, 500); + setTimeout(() => (doneFirstLoad ||= true), 500); }); -export const onRendererLoad = ({ ipc }: RendererContext) => { +export const onRendererLoad = ({ + ipc, +}: RendererContext) => { window.download = () => { let videoUrl = getSongMenu() // Selector of first button which is always "Start Radio" - ?.querySelector('ytmusic-menu-navigation-item-renderer[tabindex="-1"] #navigation-endpoint') + ?.querySelector( + 'ytmusic-menu-navigation-item-renderer[tabindex="-1"] #navigation-endpoint', + ) ?.getAttribute('href'); if (videoUrl) { if (videoUrl.startsWith('watch?')) { diff --git a/src/plugins/downloader/types.ts b/src/plugins/downloader/types.ts index 175fc015..fcab5b83 100644 --- a/src/plugins/downloader/types.ts +++ b/src/plugins/downloader/types.ts @@ -16,7 +16,7 @@ export const DefaultPresetList: Record = { 'Custom': { extension: null, ffmpegArgs: [], - } + }, }; export interface YouTubeFormat { @@ -31,86 +31,742 @@ export interface YouTubeFormat { // converted from https://gist.github.com/sidneys/7095afe4da4ae58694d128b1034e01e2#file-youtube_format_code_itag_list-md export const YoutubeFormatList: YouTubeFormat[] = [ - { itag: 5, container: 'flv', content: 'audio/video', resolution: '240p', bitrate: '-', range: '-', vrOr3D: '-' }, - { itag: 6, container: 'flv', content: 'audio/video', resolution: '270p', bitrate: '-', range: '-', vrOr3D: '-' }, - { itag: 17, container: '3gp', content: 'audio/video', resolution: '144p', bitrate: '-', range: '-', vrOr3D: '-' }, - { itag: 18, container: 'mp4', content: 'audio/video', resolution: '360p', bitrate: '-', range: '-', vrOr3D: '-' }, - { itag: 22, container: 'mp4', content: 'audio/video', resolution: '720p', bitrate: '-', range: '-', vrOr3D: '-' }, - { itag: 34, container: 'flv', content: 'audio/video', resolution: '360p', bitrate: '-', range: '-', vrOr3D: '-' }, - { itag: 35, container: 'flv', content: 'audio/video', resolution: '480p', bitrate: '-', range: '-', vrOr3D: '-' }, - { itag: 36, container: '3gp', content: 'audio/video', resolution: '180p', bitrate: '-', range: '-', vrOr3D: '-' }, - { itag: 37, container: 'mp4', content: 'audio/video', resolution: '1080p', bitrate: '-', range: '-', vrOr3D: '-' }, - { itag: 38, container: 'mp4', content: 'audio/video', resolution: '3072p', bitrate: '-', range: '-', vrOr3D: '-' }, - { itag: 43, container: 'webm', content: 'audio/video', resolution: '360p', bitrate: '-', range: '-', vrOr3D: '-' }, - { itag: 44, container: 'webm', content: 'audio/video', resolution: '480p', bitrate: '-', range: '-', vrOr3D: '-' }, - { itag: 45, container: 'webm', content: 'audio/video', resolution: '720p', bitrate: '-', range: '-', vrOr3D: '-' }, - { itag: 46, container: 'webm', content: 'audio/video', resolution: '1080p', bitrate: '-', range: '-', vrOr3D: '-' }, - { itag: 82, container: 'mp4', content: 'audio/video', resolution: '360p', bitrate: '-', range: '-', vrOr3D: '3D' }, - { itag: 83, container: 'mp4', content: 'audio/video', resolution: '480p', bitrate: '-', range: '-', vrOr3D: '3D' }, - { itag: 84, container: 'mp4', content: 'audio/video', resolution: '720p', bitrate: '-', range: '-', vrOr3D: '3D' }, - { itag: 85, container: 'mp4', content: 'audio/video', resolution: '1080p', bitrate: '-', range: '-', vrOr3D: '3D' }, - { itag: 91, container: 'hls', content: 'audio/video', resolution: '144p', bitrate: '-', range: '-', vrOr3D: '3D' }, - { itag: 92, container: 'hls', content: 'audio/video', resolution: '240p', bitrate: '-', range: '-', vrOr3D: '3D' }, - { itag: 93, container: 'hls', content: 'audio/video', resolution: '360p', bitrate: '-', range: '-', vrOr3D: '3D' }, - { itag: 94, container: 'hls', content: 'audio/video', resolution: '480p', bitrate: '-', range: '-', vrOr3D: '3D' }, - { itag: 95, container: 'hls', content: 'audio/video', resolution: '720p', bitrate: '-', range: '-', vrOr3D: '3D' }, - { itag: 96, container: 'hls', content: 'audio/video', resolution: '1080p', bitrate: '-', range: '-', vrOr3D: '-' }, - { itag: 100, container: 'webm', content: 'audio/video', resolution: '360p', bitrate: '-', range: '-', vrOr3D: '3D' }, - { itag: 101, container: 'webm', content: 'audio/video', resolution: '480p', bitrate: '-', range: '-', vrOr3D: '3D' }, - { itag: 102, container: 'webm', content: 'audio/video', resolution: '720p', bitrate: '-', range: '-', vrOr3D: '3D' }, - { itag: 132, container: 'hls', content: 'audio/video', resolution: '240p', bitrate: '-', range: '-', vrOr3D: '' }, - { itag: 133, container: 'mp4', content: 'video', resolution: '240p', bitrate: '-', range: '-', vrOr3D: '' }, - { itag: 134, container: 'mp4', content: 'video', resolution: '360p', bitrate: '-', range: '-', vrOr3D: '' }, - { itag: 135, container: 'mp4', content: 'video', resolution: '480p', bitrate: '-', range: '-', vrOr3D: '' }, - { itag: 136, container: 'mp4', content: 'video', resolution: '720p', bitrate: '-', range: '-', vrOr3D: '' }, - { itag: 137, container: 'mp4', content: 'video', resolution: '1080p', bitrate: '-', range: '-', vrOr3D: '' }, - { itag: 138, container: 'mp4', content: 'video', resolution: '2160p60', bitrate: '-', range: '-', vrOr3D: '' }, - { itag: 139, container: 'm4a', content: 'audio', resolution: '-', bitrate: '48k', range: '-', vrOr3D: '' }, - { itag: 140, container: 'm4a', content: 'audio', resolution: '-', bitrate: '128k', range: '-', vrOr3D: '' }, - { itag: 141, container: 'm4a', content: 'audio', resolution: '-', bitrate: '256k', range: '-', vrOr3D: '' }, - { itag: 151, container: 'hls', content: 'audio/video', resolution: '72p', bitrate: '-', range: '-', vrOr3D: '' }, - { itag: 160, container: 'mp4', content: 'video', resolution: '144p', bitrate: '-', range: '-', vrOr3D: '' }, - { itag: 167, container: 'webm', content: 'video', resolution: '360p', bitrate: '-', range: '-', vrOr3D: '' }, - { itag: 168, container: 'webm', content: 'video', resolution: '480p', bitrate: '-', range: '-', vrOr3D: '' }, - { itag: 169, container: 'webm', content: 'video', resolution: '1080p', bitrate: '-', range: '-', vrOr3D: '' }, - { itag: 171, container: 'webm', content: 'audio', resolution: '-', bitrate: '128k', range: '-', vrOr3D: '' }, - { itag: 218, container: 'webm', content: 'video', resolution: '480p', bitrate: '-', range: '-', vrOr3D: '' }, - { itag: 219, container: 'webm', content: 'video', resolution: '144p', bitrate: '-', range: '-', vrOr3D: '' }, - { itag: 242, container: 'webm', content: 'video', resolution: '240p', bitrate: '-', range: '-', vrOr3D: '' }, - { itag: 243, container: 'webm', content: 'video', resolution: '360p', bitrate: '-', range: '-', vrOr3D: '' }, - { itag: 244, container: 'webm', content: 'video', resolution: '480p', bitrate: '-', range: '-', vrOr3D: '' }, - { itag: 245, container: 'webm', content: 'video', resolution: '480p', bitrate: '-', range: '-', vrOr3D: '' }, - { itag: 246, container: 'webm', content: 'video', resolution: '480p', bitrate: '-', range: '-', vrOr3D: '' }, - { itag: 247, container: 'webm', content: 'video', resolution: '720p', bitrate: '-', range: '-', vrOr3D: '' }, - { itag: 248, container: 'webm', content: 'video', resolution: '1080p', bitrate: '-', range: '-', vrOr3D: '' }, - { itag: 249, container: 'webm', content: 'audio', resolution: '-', bitrate: '50k', range: '-', vrOr3D: '' }, - { itag: 250, container: 'webm', content: 'audio', resolution: '-', bitrate: '70k', range: '-', vrOr3D: '' }, - { itag: 251, container: 'webm', content: 'audio', resolution: '-', bitrate: '160k', range: '-', vrOr3D: '' }, - { itag: 264, container: 'mp4', content: 'video', resolution: '1440p', bitrate: '-', range: '-', vrOr3D: '' }, - { itag: 266, container: 'mp4', content: 'video', resolution: '2160p60', bitrate: '-', range: '-', vrOr3D: '' }, - { itag: 271, container: 'webm', content: 'video', resolution: '1440p', bitrate: '-', range: '-', vrOr3D: '' }, - { itag: 272, container: 'webm', content: 'video', resolution: '4320p', bitrate: '-', range: '-', vrOr3D: '' }, - { itag: 278, container: 'webm', content: 'video', resolution: '144p', bitrate: '-', range: '-', vrOr3D: '' }, - { itag: 298, container: 'mp4', content: 'video', resolution: '720p60', bitrate: '-', range: '-', vrOr3D: '' }, - { itag: 299, container: 'mp4', content: 'video', resolution: '1080p60', bitrate: '-', range: '-', vrOr3D: '' }, - { itag: 302, container: 'webm', content: 'video', resolution: '720p60', bitrate: '-', range: '-', vrOr3D: '' }, - { itag: 303, container: 'webm', content: 'video', resolution: '1080p60', bitrate: '-', range: '-', vrOr3D: '' }, - { itag: 308, container: 'webm', content: 'video', resolution: '1440p60', bitrate: '-', range: '-', vrOr3D: '' }, - { itag: 313, container: 'webm', content: 'video', resolution: '2160p', bitrate: '-', range: '-', vrOr3D: '' }, - { itag: 315, container: 'webm', content: 'video', resolution: '2160p60', bitrate: '-', range: '-', vrOr3D: '' }, - { itag: 330, container: 'webm', content: 'video', resolution: '144p60', bitrate: '-', range: 'hdr', vrOr3D: '' }, - { itag: 331, container: 'webm', content: 'video', resolution: '240p60', bitrate: '-', range: 'hdr', vrOr3D: '' }, - { itag: 332, container: 'webm', content: 'video', resolution: '360p60', bitrate: '-', range: 'hdr', vrOr3D: '' }, - { itag: 333, container: 'webm', content: 'video', resolution: '480p60', bitrate: '-', range: 'hdr', vrOr3D: '' }, - { itag: 334, container: 'webm', content: 'video', resolution: '720p60', bitrate: '-', range: 'hdr', vrOr3D: '' }, - { itag: 335, container: 'webm', content: 'video', resolution: '1080p60', bitrate: '-', range: 'hdr', vrOr3D: '' }, - { itag: 336, container: 'webm', content: 'video', resolution: '1440p60', bitrate: '-', range: 'hdr', vrOr3D: '' }, - { itag: 337, container: 'webm', content: 'video', resolution: '2160p60', bitrate: '-', range: 'hdr', vrOr3D: '' }, - { itag: 272, container: 'webm', content: 'video', resolution: '2880p/4320p', bitrate: '-', range: '-', vrOr3D: '' }, - { itag: 399, container: 'mp4', content: 'video', resolution: '1080p', bitrate: '-', range: '-', vrOr3D: '' }, - { itag: 400, container: 'mp4', content: 'video', resolution: '1440p', bitrate: '-', range: '-', vrOr3D: '' }, - { itag: 401, container: 'mp4', content: 'video', resolution: '2160p', bitrate: '-', range: '-', vrOr3D: '' }, - { itag: 402, container: 'mp4', content: 'video', resolution: '2880p', bitrate: '-', range: '-', vrOr3D: '' }, - { itag: 571, container: 'mp4', content: 'video', resolution: '3840p', bitrate: '-', range: '-', vrOr3D: '' }, - { itag: 702, container: 'mp4', content: 'video', resolution: '3840p', bitrate: '-', range: '-', vrOr3D: '' }, + { + itag: 5, + container: 'flv', + content: 'audio/video', + resolution: '240p', + bitrate: '-', + range: '-', + vrOr3D: '-', + }, + { + itag: 6, + container: 'flv', + content: 'audio/video', + resolution: '270p', + bitrate: '-', + range: '-', + vrOr3D: '-', + }, + { + itag: 17, + container: '3gp', + content: 'audio/video', + resolution: '144p', + bitrate: '-', + range: '-', + vrOr3D: '-', + }, + { + itag: 18, + container: 'mp4', + content: 'audio/video', + resolution: '360p', + bitrate: '-', + range: '-', + vrOr3D: '-', + }, + { + itag: 22, + container: 'mp4', + content: 'audio/video', + resolution: '720p', + bitrate: '-', + range: '-', + vrOr3D: '-', + }, + { + itag: 34, + container: 'flv', + content: 'audio/video', + resolution: '360p', + bitrate: '-', + range: '-', + vrOr3D: '-', + }, + { + itag: 35, + container: 'flv', + content: 'audio/video', + resolution: '480p', + bitrate: '-', + range: '-', + vrOr3D: '-', + }, + { + itag: 36, + container: '3gp', + content: 'audio/video', + resolution: '180p', + bitrate: '-', + range: '-', + vrOr3D: '-', + }, + { + itag: 37, + container: 'mp4', + content: 'audio/video', + resolution: '1080p', + bitrate: '-', + range: '-', + vrOr3D: '-', + }, + { + itag: 38, + container: 'mp4', + content: 'audio/video', + resolution: '3072p', + bitrate: '-', + range: '-', + vrOr3D: '-', + }, + { + itag: 43, + container: 'webm', + content: 'audio/video', + resolution: '360p', + bitrate: '-', + range: '-', + vrOr3D: '-', + }, + { + itag: 44, + container: 'webm', + content: 'audio/video', + resolution: '480p', + bitrate: '-', + range: '-', + vrOr3D: '-', + }, + { + itag: 45, + container: 'webm', + content: 'audio/video', + resolution: '720p', + bitrate: '-', + range: '-', + vrOr3D: '-', + }, + { + itag: 46, + container: 'webm', + content: 'audio/video', + resolution: '1080p', + bitrate: '-', + range: '-', + vrOr3D: '-', + }, + { + itag: 82, + container: 'mp4', + content: 'audio/video', + resolution: '360p', + bitrate: '-', + range: '-', + vrOr3D: '3D', + }, + { + itag: 83, + container: 'mp4', + content: 'audio/video', + resolution: '480p', + bitrate: '-', + range: '-', + vrOr3D: '3D', + }, + { + itag: 84, + container: 'mp4', + content: 'audio/video', + resolution: '720p', + bitrate: '-', + range: '-', + vrOr3D: '3D', + }, + { + itag: 85, + container: 'mp4', + content: 'audio/video', + resolution: '1080p', + bitrate: '-', + range: '-', + vrOr3D: '3D', + }, + { + itag: 91, + container: 'hls', + content: 'audio/video', + resolution: '144p', + bitrate: '-', + range: '-', + vrOr3D: '3D', + }, + { + itag: 92, + container: 'hls', + content: 'audio/video', + resolution: '240p', + bitrate: '-', + range: '-', + vrOr3D: '3D', + }, + { + itag: 93, + container: 'hls', + content: 'audio/video', + resolution: '360p', + bitrate: '-', + range: '-', + vrOr3D: '3D', + }, + { + itag: 94, + container: 'hls', + content: 'audio/video', + resolution: '480p', + bitrate: '-', + range: '-', + vrOr3D: '3D', + }, + { + itag: 95, + container: 'hls', + content: 'audio/video', + resolution: '720p', + bitrate: '-', + range: '-', + vrOr3D: '3D', + }, + { + itag: 96, + container: 'hls', + content: 'audio/video', + resolution: '1080p', + bitrate: '-', + range: '-', + vrOr3D: '-', + }, + { + itag: 100, + container: 'webm', + content: 'audio/video', + resolution: '360p', + bitrate: '-', + range: '-', + vrOr3D: '3D', + }, + { + itag: 101, + container: 'webm', + content: 'audio/video', + resolution: '480p', + bitrate: '-', + range: '-', + vrOr3D: '3D', + }, + { + itag: 102, + container: 'webm', + content: 'audio/video', + resolution: '720p', + bitrate: '-', + range: '-', + vrOr3D: '3D', + }, + { + itag: 132, + container: 'hls', + content: 'audio/video', + resolution: '240p', + bitrate: '-', + range: '-', + vrOr3D: '', + }, + { + itag: 133, + container: 'mp4', + content: 'video', + resolution: '240p', + bitrate: '-', + range: '-', + vrOr3D: '', + }, + { + itag: 134, + container: 'mp4', + content: 'video', + resolution: '360p', + bitrate: '-', + range: '-', + vrOr3D: '', + }, + { + itag: 135, + container: 'mp4', + content: 'video', + resolution: '480p', + bitrate: '-', + range: '-', + vrOr3D: '', + }, + { + itag: 136, + container: 'mp4', + content: 'video', + resolution: '720p', + bitrate: '-', + range: '-', + vrOr3D: '', + }, + { + itag: 137, + container: 'mp4', + content: 'video', + resolution: '1080p', + bitrate: '-', + range: '-', + vrOr3D: '', + }, + { + itag: 138, + container: 'mp4', + content: 'video', + resolution: '2160p60', + bitrate: '-', + range: '-', + vrOr3D: '', + }, + { + itag: 139, + container: 'm4a', + content: 'audio', + resolution: '-', + bitrate: '48k', + range: '-', + vrOr3D: '', + }, + { + itag: 140, + container: 'm4a', + content: 'audio', + resolution: '-', + bitrate: '128k', + range: '-', + vrOr3D: '', + }, + { + itag: 141, + container: 'm4a', + content: 'audio', + resolution: '-', + bitrate: '256k', + range: '-', + vrOr3D: '', + }, + { + itag: 151, + container: 'hls', + content: 'audio/video', + resolution: '72p', + bitrate: '-', + range: '-', + vrOr3D: '', + }, + { + itag: 160, + container: 'mp4', + content: 'video', + resolution: '144p', + bitrate: '-', + range: '-', + vrOr3D: '', + }, + { + itag: 167, + container: 'webm', + content: 'video', + resolution: '360p', + bitrate: '-', + range: '-', + vrOr3D: '', + }, + { + itag: 168, + container: 'webm', + content: 'video', + resolution: '480p', + bitrate: '-', + range: '-', + vrOr3D: '', + }, + { + itag: 169, + container: 'webm', + content: 'video', + resolution: '1080p', + bitrate: '-', + range: '-', + vrOr3D: '', + }, + { + itag: 171, + container: 'webm', + content: 'audio', + resolution: '-', + bitrate: '128k', + range: '-', + vrOr3D: '', + }, + { + itag: 218, + container: 'webm', + content: 'video', + resolution: '480p', + bitrate: '-', + range: '-', + vrOr3D: '', + }, + { + itag: 219, + container: 'webm', + content: 'video', + resolution: '144p', + bitrate: '-', + range: '-', + vrOr3D: '', + }, + { + itag: 242, + container: 'webm', + content: 'video', + resolution: '240p', + bitrate: '-', + range: '-', + vrOr3D: '', + }, + { + itag: 243, + container: 'webm', + content: 'video', + resolution: '360p', + bitrate: '-', + range: '-', + vrOr3D: '', + }, + { + itag: 244, + container: 'webm', + content: 'video', + resolution: '480p', + bitrate: '-', + range: '-', + vrOr3D: '', + }, + { + itag: 245, + container: 'webm', + content: 'video', + resolution: '480p', + bitrate: '-', + range: '-', + vrOr3D: '', + }, + { + itag: 246, + container: 'webm', + content: 'video', + resolution: '480p', + bitrate: '-', + range: '-', + vrOr3D: '', + }, + { + itag: 247, + container: 'webm', + content: 'video', + resolution: '720p', + bitrate: '-', + range: '-', + vrOr3D: '', + }, + { + itag: 248, + container: 'webm', + content: 'video', + resolution: '1080p', + bitrate: '-', + range: '-', + vrOr3D: '', + }, + { + itag: 249, + container: 'webm', + content: 'audio', + resolution: '-', + bitrate: '50k', + range: '-', + vrOr3D: '', + }, + { + itag: 250, + container: 'webm', + content: 'audio', + resolution: '-', + bitrate: '70k', + range: '-', + vrOr3D: '', + }, + { + itag: 251, + container: 'webm', + content: 'audio', + resolution: '-', + bitrate: '160k', + range: '-', + vrOr3D: '', + }, + { + itag: 264, + container: 'mp4', + content: 'video', + resolution: '1440p', + bitrate: '-', + range: '-', + vrOr3D: '', + }, + { + itag: 266, + container: 'mp4', + content: 'video', + resolution: '2160p60', + bitrate: '-', + range: '-', + vrOr3D: '', + }, + { + itag: 271, + container: 'webm', + content: 'video', + resolution: '1440p', + bitrate: '-', + range: '-', + vrOr3D: '', + }, + { + itag: 272, + container: 'webm', + content: 'video', + resolution: '4320p', + bitrate: '-', + range: '-', + vrOr3D: '', + }, + { + itag: 278, + container: 'webm', + content: 'video', + resolution: '144p', + bitrate: '-', + range: '-', + vrOr3D: '', + }, + { + itag: 298, + container: 'mp4', + content: 'video', + resolution: '720p60', + bitrate: '-', + range: '-', + vrOr3D: '', + }, + { + itag: 299, + container: 'mp4', + content: 'video', + resolution: '1080p60', + bitrate: '-', + range: '-', + vrOr3D: '', + }, + { + itag: 302, + container: 'webm', + content: 'video', + resolution: '720p60', + bitrate: '-', + range: '-', + vrOr3D: '', + }, + { + itag: 303, + container: 'webm', + content: 'video', + resolution: '1080p60', + bitrate: '-', + range: '-', + vrOr3D: '', + }, + { + itag: 308, + container: 'webm', + content: 'video', + resolution: '1440p60', + bitrate: '-', + range: '-', + vrOr3D: '', + }, + { + itag: 313, + container: 'webm', + content: 'video', + resolution: '2160p', + bitrate: '-', + range: '-', + vrOr3D: '', + }, + { + itag: 315, + container: 'webm', + content: 'video', + resolution: '2160p60', + bitrate: '-', + range: '-', + vrOr3D: '', + }, + { + itag: 330, + container: 'webm', + content: 'video', + resolution: '144p60', + bitrate: '-', + range: 'hdr', + vrOr3D: '', + }, + { + itag: 331, + container: 'webm', + content: 'video', + resolution: '240p60', + bitrate: '-', + range: 'hdr', + vrOr3D: '', + }, + { + itag: 332, + container: 'webm', + content: 'video', + resolution: '360p60', + bitrate: '-', + range: 'hdr', + vrOr3D: '', + }, + { + itag: 333, + container: 'webm', + content: 'video', + resolution: '480p60', + bitrate: '-', + range: 'hdr', + vrOr3D: '', + }, + { + itag: 334, + container: 'webm', + content: 'video', + resolution: '720p60', + bitrate: '-', + range: 'hdr', + vrOr3D: '', + }, + { + itag: 335, + container: 'webm', + content: 'video', + resolution: '1080p60', + bitrate: '-', + range: 'hdr', + vrOr3D: '', + }, + { + itag: 336, + container: 'webm', + content: 'video', + resolution: '1440p60', + bitrate: '-', + range: 'hdr', + vrOr3D: '', + }, + { + itag: 337, + container: 'webm', + content: 'video', + resolution: '2160p60', + bitrate: '-', + range: 'hdr', + vrOr3D: '', + }, + { + itag: 272, + container: 'webm', + content: 'video', + resolution: '2880p/4320p', + bitrate: '-', + range: '-', + vrOr3D: '', + }, + { + itag: 399, + container: 'mp4', + content: 'video', + resolution: '1080p', + bitrate: '-', + range: '-', + vrOr3D: '', + }, + { + itag: 400, + container: 'mp4', + content: 'video', + resolution: '1440p', + bitrate: '-', + range: '-', + vrOr3D: '', + }, + { + itag: 401, + container: 'mp4', + content: 'video', + resolution: '2160p', + bitrate: '-', + range: '-', + vrOr3D: '', + }, + { + itag: 402, + container: 'mp4', + content: 'video', + resolution: '2880p', + bitrate: '-', + range: '-', + vrOr3D: '', + }, + { + itag: 571, + container: 'mp4', + content: 'video', + resolution: '3840p', + bitrate: '-', + range: '-', + vrOr3D: '', + }, + { + itag: 702, + container: 'mp4', + content: 'video', + resolution: '3840p', + bitrate: '-', + range: '-', + vrOr3D: '', + }, ]; diff --git a/src/plugins/exponential-volume/index.ts b/src/plugins/exponential-volume/index.ts index cf37c2ea..6f54abca 100644 --- a/src/plugins/exponential-volume/index.ts +++ b/src/plugins/exponential-volume/index.ts @@ -2,7 +2,8 @@ import { createPlugin } from '@/utils'; export default createPlugin({ name: 'Exponential Volume', - description: 'Makes the volume slider exponential so it\'s easier to select lower volumes.', + description: + "Makes the volume slider exponential so it's easier to select lower volumes.", restartNeeded: true, config: { enabled: false, @@ -24,7 +25,8 @@ export default createPlugin({ ); Object.defineProperty(HTMLMediaElement.prototype, 'volume', { get(this: HTMLMediaElement) { - const lowVolume = propertyDescriptor?.get?.call(this) as number ?? 0; + const lowVolume = + (propertyDescriptor?.get?.call(this) as number) ?? 0; const calculatedOriginalVolume = lowVolume ** (1 / EXPONENT); // The calculated value has some accuracy issues which can lead to problems for implementations that expect exact values. @@ -46,6 +48,6 @@ export default createPlugin({ propertyDescriptor?.set?.call(this, lowVolume); }, }); - } - } + }, + }, }); diff --git a/src/plugins/in-app-menu/index.ts b/src/plugins/in-app-menu/index.ts index c0fe8acb..974cf26d 100644 --- a/src/plugins/in-app-menu/index.ts +++ b/src/plugins/in-app-menu/index.ts @@ -13,13 +13,10 @@ export default createPlugin({ description: 'gives menu-bars a fancy, dark or album-color look', restartNeeded: true, config: { - enabled: ( - typeof window !== 'undefined' && - !window.navigator?.userAgent?.includes('mac') - ) || ( - typeof global !== 'undefined' && - global.process?.platform !== 'darwin' - ), + enabled: + (typeof window !== 'undefined' && + !window.navigator?.userAgent?.includes('mac')) || + (typeof global !== 'undefined' && global.process?.platform !== 'darwin'), hideDOMWindowControls: false, } as InAppMenuConfig, stylesheets: [titlebarStyle], @@ -31,4 +28,3 @@ export default createPlugin({ onPlayerApiReady, }, }); - diff --git a/src/plugins/in-app-menu/main.ts b/src/plugins/in-app-menu/main.ts index 6719e4d6..c5d1f10f 100644 --- a/src/plugins/in-app-menu/main.ts +++ b/src/plugins/in-app-menu/main.ts @@ -5,7 +5,10 @@ import { BrowserWindow, Menu, MenuItem, ipcMain, nativeImage } from 'electron'; import type { BackendContext } from '@/types/contexts'; import type { InAppMenuConfig } from './index'; -export const onMainLoad = ({ window: win, ipc: { handle, send } }: BackendContext) => { +export const onMainLoad = ({ + window: win, + ipc: { handle, send }, +}: BackendContext) => { win.on('close', () => { send('close-all-in-app-menu-panel'); }); @@ -16,11 +19,13 @@ export const onMainLoad = ({ window: win, ipc: { handle, send } }: BackendContex }); }); - handle( - 'get-menu', - () => JSON.parse(JSON.stringify( - Menu.getApplicationMenu(), - (key: string, value: unknown) => (key !== 'commandsMap' && key !== 'menu') ? value : undefined), + handle('get-menu', () => + JSON.parse( + JSON.stringify( + Menu.getApplicationMenu(), + (key: string, value: unknown) => + key !== 'commandsMap' && key !== 'menu' ? value : undefined, + ), ), ); @@ -28,7 +33,7 @@ export const onMainLoad = ({ window: win, ipc: { handle, send } }: BackendContex const menu = Menu.getApplicationMenu(); let target: MenuItem | null = null; - const stack = [...menu?.items ?? []]; + const stack = [...(menu?.items ?? [])]; while (stack.length > 0) { const now = stack.shift(); now?.submenu?.items.forEach((item) => stack.push(item)); @@ -44,15 +49,21 @@ export const onMainLoad = ({ window: win, ipc: { handle, send } }: BackendContex ipcMain.handle('menu-event', (event, commandId: number) => { const target = getMenuItemById(commandId); - 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) => { const result = getMenuItemById(commandId); - return JSON.parse(JSON.stringify( - result, - (key: string, value: unknown) => (key !== 'commandsMap' && key !== 'menu') ? value : undefined), + return JSON.parse( + JSON.stringify(result, (key: string, value: unknown) => + key !== 'commandsMap' && key !== 'menu' ? value : undefined, + ), ); }); diff --git a/src/plugins/in-app-menu/menu.ts b/src/plugins/in-app-menu/menu.ts index 3aa871a6..4c875679 100644 --- a/src/plugins/in-app-menu/menu.ts +++ b/src/plugins/in-app-menu/menu.ts @@ -4,7 +4,10 @@ import type { InAppMenuConfig } from './index'; import type { MenuContext } from '@/types/contexts'; import type { MenuTemplate } from '@/menu'; -export const onMenu = async ({ getConfig, setConfig }: MenuContext): Promise => { +export const onMenu = async ({ + getConfig, + setConfig, +}: MenuContext): Promise => { const config = await getConfig(); if (is.linux()) { @@ -16,8 +19,8 @@ export const onMenu = async ({ getConfig, setConfig }: MenuContext', - checkbox: '', + submenu: + '', + checkbox: + '', radio: { - checked: '', - unchecked: '', + checked: + '', + unchecked: + '', }, }; diff --git a/src/plugins/in-app-menu/menu/panel.ts b/src/plugins/in-app-menu/menu/panel.ts index 16bdeae7..0407e22f 100644 --- a/src/plugins/in-app-menu/menu/panel.ts +++ b/src/plugins/in-app-menu/menu/panel.ts @@ -27,8 +27,12 @@ export const createPanel = ( if (item.checked) iconWrapper.innerHTML = Icons.radio.checked; else iconWrapper.innerHTML = Icons.radio.unchecked; } else { - const iconURL = typeof item.icon === 'string' ? - await window.ipcRenderer.invoke('image-path-to-data-url') as string : item.icon?.toDataURL(); + const iconURL = + typeof item.icon === 'string' + ? ((await window.ipcRenderer.invoke( + 'image-path-to-data-url', + )) as string) + : item.icon?.toDataURL(); if (iconURL) iconWrapper.style.background = `url(${iconURL})`; } @@ -36,7 +40,8 @@ export const createPanel = ( const radioGroups: [MenuItem, HTMLElement][] = []; items.map((item) => { - if (item.type === 'separator') return panel.appendChild(document.createElement('menu-separator')); + if (item.type === 'separator') + return panel.appendChild(document.createElement('menu-separator')); const menu = document.createElement('menu-item'); const iconWrapper = document.createElement('menu-icon'); @@ -47,7 +52,10 @@ export const createPanel = ( menu.addEventListener('click', async () => { await window.ipcRenderer.invoke('menu-event', item.commandId); - const menuItem = await window.ipcRenderer.invoke('get-menu-by-id', item.commandId) as MenuItem | null; + const menuItem = (await window.ipcRenderer.invoke( + 'get-menu-by-id', + item.commandId, + )) as MenuItem | null; if (menuItem) { updateIconState(iconWrapper, menuItem); @@ -56,10 +64,13 @@ export const createPanel = ( await Promise.all( radioGroups.map(async ([item, iconWrapper]) => { if (item.commandId === menuItem.commandId) return; - const newItem = await window.ipcRenderer.invoke('get-menu-by-id', item.commandId) as MenuItem | null; + const newItem = (await window.ipcRenderer.invoke( + 'get-menu-by-id', + item.commandId, + )) as MenuItem | null; if (newItem) updateIconState(iconWrapper, newItem); - }) + }), ); } } @@ -74,10 +85,15 @@ export const createPanel = ( subMenuIcon.appendChild(ElementFromHtml(Icons.submenu)); menu.appendChild(subMenuIcon); - const [child, , children] = createPanel(parent, menu, item.submenu?.items ?? [], { - placement: 'right', - order: (options?.order ?? 0) + 1, - }); + const [child, , children] = createPanel( + parent, + menu, + item.submenu?.items ?? [], + { + placement: 'right', + order: (options?.order ?? 0) + 1, + }, + ); childPanels.push(child); children.push(...children); @@ -106,7 +122,10 @@ export const createPanel = ( // long lists to squeeze their children at the bottom of the screen // (This needs to be done *after* setAttribute) panel.classList.remove('position-by-bottom'); - if (options.placement === 'right' && panel.scrollHeight > panel.clientHeight ) { + if ( + options.placement === 'right' && + panel.scrollHeight > panel.clientHeight + ) { panel.style.setProperty('--y', `${rect.y + rect.height}px`); panel.classList.add('position-by-bottom'); } @@ -119,16 +138,17 @@ export const createPanel = ( document.body.addEventListener('click', (event) => { const path = event.composedPath(); - const isInside = path.some((it) => it === panel || it === anchor || childPanels.includes(it as HTMLElement)); + const isInside = path.some( + (it) => + it === panel || + it === anchor || + childPanels.includes(it as HTMLElement), + ); if (!isInside) close(); }); parent.appendChild(panel); - return [ - panel, - { isOpened, close, open }, - childPanels, - ] as const; + return [panel, { isOpened, close, open }, childPanels] as const; }; diff --git a/src/plugins/in-app-menu/renderer.ts b/src/plugins/in-app-menu/renderer.ts index 182d22db..a69c653e 100644 --- a/src/plugins/in-app-menu/renderer.ts +++ b/src/plugins/in-app-menu/renderer.ts @@ -12,9 +12,13 @@ import type { RendererContext } from '@/types/contexts'; import type { InAppMenuConfig } from '@/plugins/in-app-menu/index'; const isMacOS = navigator.userAgent.includes('Macintosh'); -const isNotWindowsOrMacOS = !navigator.userAgent.includes('Windows') && !isMacOS; +const isNotWindowsOrMacOS = + !navigator.userAgent.includes('Windows') && !isMacOS; -export const onRendererLoad = async ({ getConfig, ipc: { invoke, on } }: RendererContext) => { +export const onRendererLoad = async ({ + getConfig, + ipc: { invoke, on }, +}: RendererContext) => { const config = await getConfig(); const hideDOMWindowControls = config.hideDOMWindowControls; @@ -70,7 +74,6 @@ export const onRendererLoad = async ({ getConfig, ipc: { invoke, on } }: Rendere titleBar.appendChild(logo); const addWindowControls = async () => { - // Create window control buttons const minimizeButton = document.createElement('button'); minimizeButton.classList.add('window-control'); @@ -124,12 +127,20 @@ export const onRendererLoad = async ({ getConfig, ipc: { invoke, on } }: Rendere if (navBar) { const observer = new MutationObserver((mutations) => { mutations.forEach(() => { - titleBar.style.setProperty('--titlebar-background-color', navBar.style.backgroundColor); - document.querySelector('html')!.style.setProperty('--titlebar-background-color', navBar.style.backgroundColor); + titleBar.style.setProperty( + '--titlebar-background-color', + navBar.style.backgroundColor, + ); + document + .querySelector('html')! + .style.setProperty( + '--titlebar-background-color', + navBar.style.backgroundColor, + ); }); }); - observer.observe(navBar, { attributes : true, attributeFilter : ['style'] }); + observer.observe(navBar, { attributes: true, attributeFilter: ['style'] }); } const updateMenu = async () => { @@ -139,12 +150,16 @@ export const onRendererLoad = async ({ getConfig, ipc: { invoke, on } }: Rendere }); panelClosers = []; - const menu = await invoke('get-menu') as Menu | null; + const menu = (await invoke('get-menu')) as Menu | null; if (!menu) return; menu.items.forEach((menuItem) => { const menu = document.createElement('menu-button'); - const [, { close: closer }] = createPanel(titleBar, menu, menuItem.submenu?.items ?? []); + const [, { close: closer }] = createPanel( + titleBar, + menu, + menuItem.submenu?.items ?? [], + ); panelClosers.push(closer); menu.append(menuItem.label); @@ -153,7 +168,8 @@ export const onRendererLoad = async ({ getConfig, ipc: { invoke, on } }: Rendere menu.style.visibility = 'hidden'; } }); - if (isNotWindowsOrMacOS && !hideDOMWindowControls) await addWindowControls(); + if (isNotWindowsOrMacOS && !hideDOMWindowControls) + await addWindowControls(); }; await updateMenu(); @@ -164,13 +180,21 @@ export const onRendererLoad = async ({ getConfig, ipc: { invoke, on } }: Rendere }); on('refresh-in-app-menu', () => updateMenu()); on('window-maximize', () => { - if (isNotWindowsOrMacOS && !hideDOMWindowControls && maximizeButton.firstChild) { + if ( + isNotWindowsOrMacOS && + !hideDOMWindowControls && + maximizeButton.firstChild + ) { maximizeButton.removeChild(maximizeButton.firstChild); maximizeButton.appendChild(unmaximize); } }); on('window-unmaximize', () => { - if (isNotWindowsOrMacOS && !hideDOMWindowControls && maximizeButton.firstChild) { + if ( + isNotWindowsOrMacOS && + !hideDOMWindowControls && + maximizeButton.firstChild + ) { maximizeButton.removeChild(maximizeButton.firstChild); maximizeButton.appendChild(unmaximize); } @@ -187,6 +211,9 @@ export const onPlayerApiReady = () => { const htmlHeadStyle = document.querySelector('head > div > style'); if (htmlHeadStyle) { // HACK: This is a hack to remove the scrollbar width - htmlHeadStyle.innerHTML = htmlHeadStyle.innerHTML.replace('html::-webkit-scrollbar {width: var(--ytmusic-scrollbar-width);', 'html::-webkit-scrollbar {'); + htmlHeadStyle.innerHTML = htmlHeadStyle.innerHTML.replace( + 'html::-webkit-scrollbar {width: var(--ytmusic-scrollbar-width);', + 'html::-webkit-scrollbar {', + ); } }; diff --git a/src/plugins/in-app-menu/titlebar.css b/src/plugins/in-app-menu/titlebar.css index 9a368a54..1772397c 100644 --- a/src/plugins/in-app-menu/titlebar.css +++ b/src/plugins/in-app-menu/titlebar.css @@ -27,7 +27,9 @@ title-bar { background-color: var(--titlebar-background-color, #030303); user-select: none; - transition: opacity 200ms ease 0s, background-color 300ms cubic-bezier(0.2, 0, 0.6, 1) 0s; + transition: + opacity 200ms ease 0s, + background-color 300ms cubic-bezier(0.2, 0, 0.6, 1) 0s; } menu-button { @@ -64,18 +66,26 @@ menu-panel { padding: 4px; border-radius: 8px; pointer-events: none; - background-color: color-mix(in srgb, var(--titlebar-background-color, #030303) 50%, rgba(0, 0, 0, 0.1)); + background-color: color-mix( + in srgb, + var(--titlebar-background-color, #030303) 50%, + rgba(0, 0, 0, 0.1) + ); backdrop-filter: blur(8px); - box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.05), 0 2px 8px rgba(0, 0, 0, 0.2); + box-shadow: + 0 0 0 1px rgba(0, 0, 0, 0.05), + 0 2px 8px rgba(0, 0, 0, 0.2); z-index: 0; opacity: 0; transform: scale(0.8); transform-origin: top left; - transition: opacity 200ms ease 0s, transform 200ms ease 0s; + transition: + opacity 200ms ease 0s, + transform 200ms ease 0s; } -menu-panel[open="true"] { +menu-panel[open='true'] { pointer-events: all; opacity: 1; transform: scale(1); @@ -159,22 +169,32 @@ ytmusic-app-layout { margin-top: var(--menu-bar-height, 36px) !important; } -ytmusic-app-layout>[slot=nav-bar], #nav-bar-background.ytmusic-app-layout { +ytmusic-app-layout > [slot='nav-bar'], +#nav-bar-background.ytmusic-app-layout { top: var(--menu-bar-height, 36px) !important; } #nav-bar-divider.ytmusic-app-layout { - top: calc(var(--ytmusic-nav-bar-height) + var(--menu-bar-height, 36px)) !important; + top: calc( + var(--ytmusic-nav-bar-height) + var(--menu-bar-height, 36px) + ) !important; } ytmusic-app[is-bauhaus-sidenav-enabled] #guide-spacer.ytmusic-app, ytmusic-app[is-bauhaus-sidenav-enabled] #mini-guide-spacer.ytmusic-app { - margin-top: calc(var(--ytmusic-nav-bar-height) + var(--menu-bar-height, 36px)) !important; + margin-top: calc( + var(--ytmusic-nav-bar-height) + var(--menu-bar-height, 36px) + ) !important; } -ytmusic-app-layout>[slot=player-page] { +ytmusic-app-layout > [slot='player-page'] { margin-top: var(--menu-bar-height); - height: calc(100vh - var(--menu-bar-height) - var(--ytmusic-nav-bar-height) - var(--ytmusic-player-bar-height)) !important; + height: calc( + 100vh - var(--menu-bar-height) - var(--ytmusic-nav-bar-height) - + var(--ytmusic-player-bar-height) + ) !important; } ytmusic-guide-renderer { - height: calc(100vh - var(--menu-bar-height) - var(--ytmusic-nav-bar-height)) !important; + height: calc( + 100vh - var(--menu-bar-height) - var(--ytmusic-nav-bar-height) + ) !important; } diff --git a/src/plugins/last-fm/index.ts b/src/plugins/last-fm/index.ts index ca64311f..885db09c 100644 --- a/src/plugins/last-fm/index.ts +++ b/src/plugins/last-fm/index.ts @@ -63,13 +63,17 @@ export default createPlugin({ if (!songInfo.isPaused) { setNowPlaying(songInfo, config, setConfig); // Scrobble when the song is halfway through, or has passed the 4-minute mark - const scrobbleTime = Math.min(Math.ceil(songInfo.songDuration / 2), 4 * 60); + const scrobbleTime = Math.min( + Math.ceil(songInfo.songDuration / 2), + 4 * 60, + ); if (scrobbleTime > (songInfo.elapsedSeconds ?? 0)) { // Scrobble still needs to happen - const timeToWait = (scrobbleTime - (songInfo.elapsedSeconds ?? 0)) * 1000; + const timeToWait = + (scrobbleTime - (songInfo.elapsedSeconds ?? 0)) * 1000; scrobbleTimer = setTimeout(addScrobble, timeToWait, songInfo, config); } } }); - } + }, }); diff --git a/src/plugins/last-fm/main.ts b/src/plugins/last-fm/main.ts index 07e887c3..4f956b61 100644 --- a/src/plugins/last-fm/main.ts +++ b/src/plugins/last-fm/main.ts @@ -6,21 +6,21 @@ import type { LastFmPluginConfig } from './index'; import type { SongInfo } from '@/providers/song-info'; interface LastFmData { - method: string, - timestamp?: number, + method: string; + timestamp?: number; } interface LastFmSongData { - track?: string, - duration?: number, - artist?: string, - album?: string, - api_key: string, - sk?: string, - format: string, - method: string, - timestamp?: number, - api_sig?: string, + track?: string; + duration?: number; + artist?: string; + album?: string; + api_key: string; + sk?: string; + format: string; + method: string; + timestamp?: number; + api_sig?: string; } const createFormData = (parameters: LastFmSongData) => { @@ -33,12 +33,19 @@ const createFormData = (parameters: LastFmSongData) => { return formData; }; -const createQueryString = (parameters: Record, apiSignature: string) => { +const createQueryString = ( + parameters: Record, + apiSignature: string, +) => { // Creates a querystring const queryData = []; parameters.api_sig = apiSignature; for (const key in parameters) { - queryData.push(`${encodeURIComponent(key)}=${encodeURIComponent(String(parameters[key]))}`); + queryData.push( + `${encodeURIComponent(key)}=${encodeURIComponent( + String(parameters[key]), + )}`, + ); } return '?' + queryData.join('&'); @@ -63,7 +70,11 @@ const createApiSig = (parameters: LastFmSongData, secret: string) => { return sig; }; -const createToken = async ({ api_key: apiKey, api_root: apiRoot, secret }: LastFmPluginConfig) => { +const createToken = async ({ + api_key: apiKey, + api_root: apiRoot, + secret, +}: LastFmPluginConfig) => { // Creates and stores the auth token const data = { method: 'auth.gettoken', @@ -71,19 +82,28 @@ const createToken = async ({ api_key: apiKey, api_root: apiRoot, secret }: LastF format: 'json', }; const apiSigature = createApiSig(data, secret); - const response = await net.fetch(`${apiRoot}${createQueryString(data, apiSigature)}`); - const json = await response.json() as Record; + const response = await net.fetch( + `${apiRoot}${createQueryString(data, apiSigature)}`, + ); + const json = (await response.json()) as Record; return json?.token; }; const authenticate = async (config: LastFmPluginConfig) => { // Asks the user for authentication - await shell.openExternal(`https://www.last.fm/api/auth/?api_key=${config.api_key}&token=${config.token}`); + await shell.openExternal( + `https://www.last.fm/api/auth/?api_key=${config.api_key}&token=${config.token}`, + ); }; -type SetConfType = (conf: Partial>) => (void | Promise); +type SetConfType = ( + conf: Partial>, +) => void | Promise; -export const getAndSetSessionKey = async (config: LastFmPluginConfig, setConfig: SetConfType) => { +export const getAndSetSessionKey = async ( + config: LastFmPluginConfig, + setConfig: SetConfType, +) => { // Get and store the session key const data = { api_key: config.api_key, @@ -92,12 +112,14 @@ export const getAndSetSessionKey = async (config: LastFmPluginConfig, setConfig: token: config.token, }; const apiSignature = createApiSig(data, config.secret); - const response = await net.fetch(`${config.api_root}${createQueryString(data, apiSignature)}`); - const json = await response.json() as { - error?: string, + const response = await net.fetch( + `${config.api_root}${createQueryString(data, apiSignature)}`, + ); + const json = (await response.json()) as { + error?: string; session?: { - key: string, - } + key: string; + }; }; if (json.error) { config.token = await createToken(config); @@ -111,7 +133,12 @@ export const getAndSetSessionKey = async (config: LastFmPluginConfig, setConfig: return config; }; -const postSongDataToAPI = async (songInfo: SongInfo, config: LastFmPluginConfig, data: LastFmData, setConfig: SetConfType) => { +const postSongDataToAPI = async ( + songInfo: SongInfo, + config: LastFmPluginConfig, + data: LastFmData, + setConfig: SetConfType, +) => { // This sends a post request to the api, and adds the common data if (!config.session_key) { await getAndSetSessionKey(config, setConfig); @@ -130,25 +157,35 @@ const postSongDataToAPI = async (songInfo: SongInfo, config: LastFmPluginConfig, postData.api_sig = createApiSig(postData, config.secret); const formData = createFormData(postData); - net.fetch('https://ws.audioscrobbler.com/2.0/', { method: 'POST', body: formData }) - .catch(async (error: { - response?: { - data?: { - error: number, + net + .fetch('https://ws.audioscrobbler.com/2.0/', { + method: 'POST', + body: formData, + }) + .catch( + async (error: { + response?: { + data?: { + error: number; + }; + }; + }) => { + if (error?.response?.data?.error === 9) { + // Session key is invalid, so remove it from the config and reauthenticate + config.session_key = undefined; + config.token = await createToken(config); + await authenticate(config); + setConfig(config); } - } - }) => { - if (error?.response?.data?.error === 9) { - // Session key is invalid, so remove it from the config and reauthenticate - config.session_key = undefined; - config.token = await createToken(config); - await authenticate(config); - setConfig(config); - } - }); + }, + ); }; -export const addScrobble = (songInfo: SongInfo, config: LastFmPluginConfig, setConfig: SetConfType) => { +export const addScrobble = ( + songInfo: SongInfo, + config: LastFmPluginConfig, + setConfig: SetConfType, +) => { // This adds one scrobbled song to last.fm const data = { method: 'track.scrobble', @@ -157,7 +194,11 @@ export const addScrobble = (songInfo: SongInfo, config: LastFmPluginConfig, setC postSongDataToAPI(songInfo, config, data, setConfig); }; -export const setNowPlaying = (songInfo: SongInfo, config: LastFmPluginConfig, setConfig: SetConfType) => { +export const setNowPlaying = ( + songInfo: SongInfo, + config: LastFmPluginConfig, + setConfig: SetConfType, +) => { // This sets the now playing status in last.fm const data = { method: 'track.updateNowPlaying', diff --git a/src/plugins/lumiastream/index.ts b/src/plugins/lumiastream/index.ts index 996c6afb..9e1bf68d 100644 --- a/src/plugins/lumiastream/index.ts +++ b/src/plugins/lumiastream/index.ts @@ -9,18 +9,18 @@ type LumiaData = { url?: string; videoId?: string; playlistId?: string; - cover?: string|null; - cover_url?: string|null; + cover?: string | null; + cover_url?: string | null; title?: string; artists?: string[]; status?: string; progress?: number; duration?: number; - album_url?: string|null; - album?: string|null; + album_url?: string | null; + album?: string | null; views?: number; isPaused?: boolean; -} +}; export default createPlugin({ name: 'Lumia Stream [beta]', @@ -30,7 +30,8 @@ export default createPlugin({ enabled: false, }, backend() { - const secToMilisec = (t?: number) => t ? Math.round(Number(t) * 1e3) : undefined; + const secToMilisec = (t?: number) => + t ? Math.round(Number(t) * 1e3) : undefined; const previousStatePaused = null; const data: LumiaData = { @@ -48,12 +49,17 @@ export default createPlugin({ } as const; const url = `http://127.0.0.1:${port}/api/media`; - net.fetch(url, { method: 'POST', body: JSON.stringify({ token: 'lsmedia_ytmsI7812', data }), headers }) - .catch((error: { code: number, errno: number }) => { + net + .fetch(url, { + method: 'POST', + body: JSON.stringify({ token: 'lsmedia_ytmsI7812', data }), + headers, + }) + .catch((error: { code: number; errno: number }) => { console.log( `Error: '${ error.code || error.errno - }' - when trying to access lumiastream webserver at port ${port}` + }' - when trying to access lumiastream webserver at port ${port}`, ); }); }; @@ -85,5 +91,5 @@ export default createPlugin({ data.views = songInfo.views; post(data); }); - } + }, }); diff --git a/src/plugins/lyrics-genius/index.ts b/src/plugins/lyrics-genius/index.ts index 543817b8..fecbdc5d 100644 --- a/src/plugins/lyrics-genius/index.ts +++ b/src/plugins/lyrics-genius/index.ts @@ -6,7 +6,7 @@ import { onRendererLoad } from './renderer'; export type LyricsGeniusPluginConfig = { enabled: boolean; romanizedLyrics: boolean; -} +}; export default createPlugin({ name: 'Lyrics Genius', diff --git a/src/plugins/lyrics-genius/main.ts b/src/plugins/lyrics-genius/main.ts index bd7840dd..13c69ae9 100644 --- a/src/plugins/lyrics-genius/main.ts +++ b/src/plugins/lyrics-genius/main.ts @@ -9,10 +9,14 @@ import type { LyricsGeniusPluginConfig } from './index'; import type { BackendContext } from '@/types/contexts'; -const eastAsianChars = /\p{Script=Katakana}|\p{Script=Hiragana}|\p{Script=Hangul}|\p{Script=Han}/u; +const eastAsianChars = + /\p{Script=Katakana}|\p{Script=Hiragana}|\p{Script=Hangul}|\p{Script=Han}/u; let revRomanized = false; -export const onMainLoad = async ({ ipc, getConfig }: BackendContext) => { +export const onMainLoad = async ({ + ipc, + getConfig, +}: BackendContext) => { const config = await getConfig(); if (config.romanizedLyrics) { @@ -38,7 +42,10 @@ export const fetchFromGenius = async (metadata: SongInfo) => { Genius Lyrics behavior is observed. */ let hasAsianChars = false; - if (revRomanized && (eastAsianChars.test(songTitle) || eastAsianChars.test(songArtist))) { + if ( + revRomanized && + (eastAsianChars.test(songTitle) || eastAsianChars.test(songArtist)) + ) { lyrics = await getLyricsList(`${songArtist} ${songTitle} Romanized`); hasAsianChars = true; } else { @@ -62,7 +69,9 @@ export const fetchFromGenius = async (metadata: SongInfo) => { */ const getLyricsList = async (queryString: string): Promise => { const response = await net.fetch( - `https://genius.com/api/search/multi?per_page=5&q=${encodeURIComponent(queryString)}`, + `https://genius.com/api/search/multi?per_page=5&q=${encodeURIComponent( + queryString, + )}`, ); if (!response.ok) { return null; @@ -71,11 +80,10 @@ const getLyricsList = async (queryString: string): Promise => { /* Fetch the first URL with the api, giving a collection of song results. Pick the first song, parsing the json given by the API. */ - const info = await response.json() as GetGeniusLyric; - const url = info - ?.response - ?.sections - ?.find((section) => section.type === 'song')?.hits[0]?.result?.url; + const info = (await response.json()) as GetGeniusLyric; + const url = info?.response?.sections?.find( + (section) => section.type === 'song', + )?.hits[0]?.result?.url; if (url) { return await getLyrics(url); diff --git a/src/plugins/lyrics-genius/renderer.ts b/src/plugins/lyrics-genius/renderer.ts index 2f74eb41..e46ab2aa 100644 --- a/src/plugins/lyrics-genius/renderer.ts +++ b/src/plugins/lyrics-genius/renderer.ts @@ -2,11 +2,16 @@ import type { SongInfo } from '@/providers/song-info'; import type { RendererContext } from '@/types/contexts'; import type { LyricsGeniusPluginConfig } from '@/plugins/lyrics-genius/index'; -export const onRendererLoad = ({ ipc: { invoke, on } }: RendererContext) => { +export const onRendererLoad = ({ + ipc: { invoke, on }, +}: RendererContext) => { const setLyrics = (lyricsContainer: Element, lyrics: string | null) => { lyricsContainer.innerHTML = `
- ${lyrics?.replaceAll(/\r\n|\r|\n/g, '
') ?? 'Could not retrieve lyrics from genius'} + ${ + lyrics?.replaceAll(/\r\n|\r|\n/g, '
') ?? + 'Could not retrieve lyrics from genius' + }
@@ -37,10 +42,10 @@ export const onRendererLoad = ({ ipc: { invoke, on } }: RendererContext + diff --git a/src/plugins/navigation/templates/forward.html b/src/plugins/navigation/templates/forward.html index 0c8631c1..381754ba 100644 --- a/src/plugins/navigation/templates/forward.html +++ b/src/plugins/navigation/templates/forward.html @@ -19,7 +19,7 @@ class="style-scope iron-icon" focusable="false" preserveAspectRatio="xMidYMid meet" - style="pointer-events: none; display: block; width: 100%; height: 100%;" + style="pointer-events: none; display: block; width: 100%; height: 100%" viewBox="0 0 492 492" > diff --git a/src/plugins/no-google-login/index.ts b/src/plugins/no-google-login/index.ts index 1a895960..43f1ecbd 100644 --- a/src/plugins/no-google-login/index.ts +++ b/src/plugins/no-google-login/index.ts @@ -23,8 +23,8 @@ export default createPlugin({ } // Remove the library button - const libraryIconPath - = 'M16,6v2h-2v5c0,1.1-0.9,2-2,2s-2-0.9-2-2s0.9-2,2-2c0.37,0,0.7,0.11,1,0.28V6H16z M18,20H4V6H3v15h15V20z M21,3H6v15h15V3z M7,4h13v13H7V4z'; + const libraryIconPath = + 'M16,6v2h-2v5c0,1.1-0.9,2-2,2s-2-0.9-2-2s0.9-2,2-2c0.37,0,0.7,0.11,1,0.28V6H16z M18,20H4V6H3v15h15V20z M21,3H6v15h15V3z M7,4h13v13H7V4z'; const observer = new MutationObserver(() => { const menuEntries = document.querySelectorAll( '#items ytmusic-guide-entry-renderer', @@ -43,5 +43,5 @@ export default createPlugin({ childList: true, subtree: true, }); - } + }, }); diff --git a/src/plugins/no-google-login/style.css b/src/plugins/no-google-login/style.css index e9c80f14..874f22b5 100644 --- a/src/plugins/no-google-login/style.css +++ b/src/plugins/no-google-login/style.css @@ -1,6 +1,6 @@ -.ytmusic-pivot-bar-renderer[tab-id="FEmusic_liked"], +.ytmusic-pivot-bar-renderer[tab-id='FEmusic_liked'], ytmusic-guide-signin-promo-renderer, -a[href="/music_premium"], +a[href='/music_premium'], .sign-in-link { display: none !important; } diff --git a/src/plugins/notifications/index.ts b/src/plugins/notifications/index.ts index d927ef30..f3080435 100644 --- a/src/plugins/notifications/index.ts +++ b/src/plugins/notifications/index.ts @@ -36,7 +36,8 @@ export const defaultConfig: NotificationsPluginConfig = { export default createPlugin({ name: 'Notifications', - description: 'Display a notification when a song starts playing (interactive notifications are available on windows)', + description: + 'Display a notification when a song starts playing (interactive notifications are available on windows)', restartNeeded: true, config: defaultConfig, menu: onMenu, diff --git a/src/plugins/notifications/interactive.ts b/src/plugins/notifications/interactive.ts index 2814e95c..33a7d22e 100644 --- a/src/plugins/notifications/interactive.ts +++ b/src/plugins/notifications/interactive.ts @@ -108,13 +108,19 @@ export default ( } return `\ - content="${config().toastStyle ? '' : kind.charAt(0).toUpperCase() + kind.slice(1)}"\ + content="${ + config().toastStyle + ? '' + : kind.charAt(0).toUpperCase() + kind.slice(1) + }"\ imageUri="file:///${selectIcon(kind)}" `; }; const getButton = (kind: keyof typeof mediaIcons) => - ``; + ``; const getButtons = (isPaused: boolean) => `\ @@ -136,19 +142,32 @@ export default ( ${getButtons(isPaused)} `; - const xmlImage = ({ title, artist, isPaused }: SongInfo, imgSrc: string, placement: string) => toast(`\ + const xmlImage = ( + { title, artist, isPaused }: SongInfo, + imgSrc: string, + placement: string, + ) => + toast( + `\ ${title} ${artist}\ -`, isPaused ?? false); +`, + isPaused ?? false, + ); - const xmlLogo = (songInfo: SongInfo, imgSrc: string) => xmlImage(songInfo, imgSrc, 'placement="appLogoOverride"'); + const xmlLogo = (songInfo: SongInfo, imgSrc: string) => + xmlImage(songInfo, imgSrc, 'placement="appLogoOverride"'); - const xmlHero = (songInfo: SongInfo, imgSrc: string) => xmlImage(songInfo, imgSrc, 'placement="hero"'); + const xmlHero = (songInfo: SongInfo, imgSrc: string) => + xmlImage(songInfo, imgSrc, 'placement="hero"'); - const xmlBannerBottom = (songInfo: SongInfo, imgSrc: string) => xmlImage(songInfo, imgSrc, ''); + const xmlBannerBottom = (songInfo: SongInfo, imgSrc: string) => + xmlImage(songInfo, imgSrc, ''); - const xmlBannerTopCustom = (songInfo: SongInfo, imgSrc: string) => toast(`\ + const xmlBannerTopCustom = (songInfo: SongInfo, imgSrc: string) => + toast( + `\ @@ -158,37 +177,62 @@ export default ( ${xmlMoreData(songInfo)} \ -`, songInfo.isPaused ?? false); +`, + songInfo.isPaused ?? false, + ); const xmlMoreData = ({ album, elapsedSeconds, songDuration }: SongInfo) => `\ - ${album - ? `${album}` : ''} - ${secondsToMinutes(elapsedSeconds ?? 0)} / ${secondsToMinutes(songDuration)} + ${ + album + ? `${album}` + : '' + } + ${secondsToMinutes( + elapsedSeconds ?? 0, + )} / ${secondsToMinutes(songDuration)} \ `; - const xmlBannerCenteredBottom = ({ title, artist, isPaused }: SongInfo, imgSrc: string) => toast(`\ + const xmlBannerCenteredBottom = ( + { title, artist, isPaused }: SongInfo, + imgSrc: string, + ) => + toast( + `\ - ${title} + ${title} ${artist} \ -`, isPaused ?? false); +`, + isPaused ?? false, + ); - const xmlBannerCenteredTop = ({ title, artist, isPaused }: SongInfo, imgSrc: string) => toast(`\ + const xmlBannerCenteredTop = ( + { title, artist, isPaused }: SongInfo, + imgSrc: string, + ) => + toast( + `\ - ${title} + ${title} ${artist} \ -`, isPaused ?? false); +`, + isPaused ?? false, + ); const titleFontPicker = (title: string) => { if (title.length <= 13) { @@ -206,7 +250,6 @@ export default ( return 'Subtitle'; }; - songControls = getSongControls(win); let currentSeconds = 0; @@ -226,8 +269,9 @@ export default ( } savedSongInfo = { ...songInfo }; - if (!songInfo.isPaused - && (songInfo.url !== lastUrl || config().unpauseNotification) + if ( + !songInfo.isPaused && + (songInfo.url !== lastUrl || config().unpauseNotification) ) { lastUrl = songInfo.url; sendNotification(songInfo); @@ -260,24 +304,21 @@ export default ( savedNotification?.close(); }); - changeProtocolHandler( - (cmd) => { - if (Object.keys(songControls).includes(cmd)) { - songControls[cmd as keyof typeof songControls](); - if (config().refreshOnPlayPause && ( - cmd === 'pause' - || (cmd === 'play' && !config().unpauseNotification) - ) - ) { - setImmediate(() => - sendNotification({ - ...savedSongInfo, - isPaused: cmd === 'pause', - elapsedSeconds: currentSeconds, - }), - ); - } + changeProtocolHandler((cmd) => { + if (Object.keys(songControls).includes(cmd)) { + songControls[cmd as keyof typeof songControls](); + if ( + config().refreshOnPlayPause && + (cmd === 'pause' || (cmd === 'play' && !config().unpauseNotification)) + ) { + setImmediate(() => + sendNotification({ + ...savedSongInfo, + isPaused: cmd === 'pause', + elapsedSeconds: currentSeconds, + }), + ); } - }, - ); + } + }); }; diff --git a/src/plugins/notifications/main.ts b/src/plugins/notifications/main.ts index c4b4481b..0782c6a0 100644 --- a/src/plugins/notifications/main.ts +++ b/src/plugins/notifications/main.ts @@ -31,7 +31,10 @@ const setup = () => { let currentUrl: string | undefined; registerCallback((songInfo: SongInfo) => { - if (!songInfo.isPaused && (songInfo.url !== currentUrl || config.unpauseNotification)) { + if ( + !songInfo.isPaused && + (songInfo.url !== currentUrl || config.unpauseNotification) + ) { // Close the old notification oldNotification?.close(); currentUrl = songInfo.url; @@ -43,11 +46,14 @@ const setup = () => { }); }; -export const onMainLoad = async (context: BackendContext) => { +export const onMainLoad = async ( + context: BackendContext, +) => { config = await context.getConfig(); // Register the callback for new song information - if (is.windows() && config.interactive) interactive(context.window, () => config, context); + if (is.windows() && config.interactive) + interactive(context.window, () => config, context); else setup(); }; diff --git a/src/plugins/notifications/menu.ts b/src/plugins/notifications/menu.ts index 1b603049..ee1492d1 100644 --- a/src/plugins/notifications/menu.ts +++ b/src/plugins/notifications/menu.ts @@ -8,7 +8,10 @@ import type { NotificationsPluginConfig } from './index'; import type { MenuTemplate } from '@/menu'; import type { MenuContext } from '@/types/contexts'; -export const onMenu = async ({ getConfig, setConfig }: MenuContext): Promise => { +export const onMenu = async ({ + getConfig, + setConfig, +}: MenuContext): Promise => { const config = await getConfig(); const getToastStyleMenuItems = (options: NotificationsPluginConfig) => { @@ -38,7 +41,7 @@ export const onMenu = async ({ getConfig, setConfig }: MenuContext setConfig({ urgency: level.value }), })), - } + }, ]; } else if (is.windows()) { return [ @@ -57,19 +60,22 @@ export const onMenu = async ({ getConfig, setConfig }: MenuContext setConfig({ trayControls: item.checked }), + click: (item: MenuItem) => + setConfig({ trayControls: item.checked }), }, { label: 'Hide Button Text', type: 'checkbox', checked: config.hideButtonText, - click: (item: MenuItem) => setConfig({ hideButtonText: item.checked }), + click: (item: MenuItem) => + setConfig({ hideButtonText: item.checked }), }, { label: 'Refresh on Play/Pause', type: 'checkbox', checked: config.refreshOnPlayPause, - click: (item: MenuItem) => setConfig({ refreshOnPlayPause: item.checked }), + click: (item: MenuItem) => + setConfig({ refreshOnPlayPause: item.checked }), }, ], }, diff --git a/src/plugins/notifications/utils.ts b/src/plugins/notifications/utils.ts index 1c88fe63..2d3f8876 100644 --- a/src/plugins/notifications/utils.ts +++ b/src/plugins/notifications/utils.ts @@ -14,7 +14,6 @@ const userData = app.getPath('userData'); const temporaryIcon = path.join(userData, 'tempIcon.png'); const temporaryBanner = path.join(userData, 'tempBanner.png'); - export const ToastStyles = { logo: 1, banner_centered_top: 2, @@ -43,7 +42,10 @@ const nativeImageToLogo = cache((nativeImage: NativeImage) => { }); }); -export const notificationImage = (songInfo: SongInfo, config: NotificationsPluginConfig) => { +export const notificationImage = ( + songInfo: SongInfo, + config: NotificationsPluginConfig, +) => { if (!songInfo.image) { return youtubeMusicIcon; } @@ -76,11 +78,10 @@ export const saveImage = cache((img: NativeImage, savePath: string) => { return savePath; }); -export const snakeToCamel = (string_: string) => string_.replaceAll(/([-_][a-z]|^[a-z])/g, (group) => - group.toUpperCase() - .replace('-', ' ') - .replace('_', ' '), -); +export const snakeToCamel = (string_: string) => + string_.replaceAll(/([-_][a-z]|^[a-z])/g, (group) => + group.toUpperCase().replace('-', ' ').replace('_', ' '), + ); export const secondsToMinutes = (seconds: number) => { const minutes = Math.floor(seconds / 60); diff --git a/src/plugins/picture-in-picture/index.ts b/src/plugins/picture-in-picture/index.ts index 5d84b6c7..0955a430 100644 --- a/src/plugins/picture-in-picture/index.ts +++ b/src/plugins/picture-in-picture/index.ts @@ -6,16 +6,16 @@ import { onMenu } from './menu'; import { onPlayerApiReady, onRendererLoad } from './renderer'; export type PictureInPicturePluginConfig = { - 'enabled': boolean; - 'alwaysOnTop': boolean; - 'savePosition': boolean; - 'saveSize': boolean; - 'hotkey': 'P', + enabled: boolean; + alwaysOnTop: boolean; + savePosition: boolean; + saveSize: boolean; + hotkey: 'P'; 'pip-position': [number, number]; 'pip-size': [number, number]; - 'isInPiP': boolean; - 'useNativePiP': boolean; -} + isInPiP: boolean; + useNativePiP: boolean; +}; export default createPlugin({ name: 'Picture In Picture', @@ -42,5 +42,5 @@ export default createPlugin({ renderer: { start: onRendererLoad, onPlayerApiReady, - } + }, }); diff --git a/src/plugins/picture-in-picture/main.ts b/src/plugins/picture-in-picture/main.ts index abbea0d7..c857b560 100644 --- a/src/plugins/picture-in-picture/main.ts +++ b/src/plugins/picture-in-picture/main.ts @@ -6,14 +6,20 @@ import type { BackendContext } from '@/types/contexts'; let config: PictureInPicturePluginConfig; -export const onMainLoad = async ({ window, getConfig, setConfig, ipc: { send, handle, on } }: BackendContext) => { +export const onMainLoad = async ({ + window, + getConfig, + setConfig, + ipc: { send, handle, on }, +}: BackendContext) => { let isInPiP = false; let originalPosition: number[]; let originalSize: number[]; let originalFullScreen: boolean; let originalMaximized: boolean; - const pipPosition = () => (config.savePosition && config['pip-position']) || [10, 10]; + const pipPosition = () => + (config.savePosition && config['pip-position']) || [10, 10]; const pipSize = () => (config.saveSize && config['pip-size']) || [450, 275]; const togglePiP = () => { @@ -50,7 +56,10 @@ export const onMainLoad = async ({ window, getConfig, setConfig, ipc: { send, ha window.setAlwaysOnTop(true, 'screen-saver', 1); } } else { - window.webContents.removeListener('before-input-event', blockShortcutsInPiP); + window.webContents.removeListener( + 'before-input-event', + blockShortcutsInPiP, + ); window.setMaximizable(true); window.setFullScreenable(true); @@ -76,7 +85,10 @@ export const onMainLoad = async ({ window, getConfig, setConfig, ipc: { send, ha window.setWindowButtonVisibility?.(!isInPiP); }; - const blockShortcutsInPiP = (event: Electron.Event, input: Electron.Input) => { + const blockShortcutsInPiP = ( + event: Electron.Event, + input: Electron.Input, + ) => { const key = input.key.toLowerCase(); if (key === 'f') { diff --git a/src/plugins/picture-in-picture/menu.ts b/src/plugins/picture-in-picture/menu.ts index 35f55619..1f74dd73 100644 --- a/src/plugins/picture-in-picture/menu.ts +++ b/src/plugins/picture-in-picture/menu.ts @@ -7,8 +7,11 @@ import type { PictureInPicturePluginConfig } from './index'; import type { MenuContext } from '@/types/contexts'; import type { MenuTemplate } from '@/menu'; - -export const onMenu = async ({ window, getConfig, setConfig }: MenuContext): Promise => { +export const onMenu = async ({ + window, + getConfig, + setConfig, +}: MenuContext): Promise => { const config = await getConfig(); return [ @@ -42,17 +45,22 @@ export const onMenu = async ({ window, getConfig, setConfig }: MenuContext { if ( menu.contains(pipButton) || - !(menu.parentElement as (HTMLElement & { eventSink_: Element }) | null) - ?.eventSink_ - ?.matches('ytmusic-menu-renderer.ytmusic-player-bar') + !( + menu.parentElement as (HTMLElement & { eventSink_: Element }) | null + )?.eventSink_?.matches('ytmusic-menu-renderer.ytmusic-player-bar') ) { return; } - const menuUrl = $('tp-yt-paper-listbox [tabindex="0"] #navigation-endpoint')?.href; + const menuUrl = $( + 'tp-yt-paper-listbox [tabindex="0"] #navigation-endpoint', + )?.href; if (!menuUrl?.includes('watch?')) { return; } @@ -79,8 +81,7 @@ const togglePictureInPicture = async () => { await togglePiP(); $('#icon')?.click(); // Close the menu return true; - } catch { - } + } catch {} } window.ipcRenderer.send('picture-in-picture'); @@ -94,10 +95,16 @@ const listenForToggle = () => { const appLayout = $('ytmusic-app-layout'); const expandMenu = $('#expanding-menu'); const middleControls = $('.middle-controls'); - const playerPage = $('ytmusic-player-page'); - const togglePlayerPageButton = $('.toggle-player-page-button'); + const playerPage = $( + 'ytmusic-player-page', + ); + const togglePlayerPageButton = $( + '.toggle-player-page-button', + ); const fullScreenButton = $('.fullscreen-button'); - const player = $ void) | undefined }>('#player'); + const player = $< + HTMLVideoElement & { onDoubleClick_: (() => void) | undefined } + >('#player'); const onPlayerDblClick = player?.onDoubleClick_; const mouseLeaveEventListener = () => middleControls?.click(); @@ -106,9 +113,11 @@ const listenForToggle = () => { window.ipcRenderer.on('pip-toggle', (_, isPip: boolean) => { if (originalExitButton && player) { if (isPip) { - replaceButton('.exit-fullscreen-button', originalExitButton)?.addEventListener('click', () => togglePictureInPicture()); - player.onDoubleClick_ = () => { - }; + replaceButton( + '.exit-fullscreen-button', + originalExitButton, + )?.addEventListener('click', () => togglePictureInPicture()); + player.onDoubleClick_ = () => {}; expandMenu?.addEventListener('mouseleave', mouseLeaveEventListener); if (!playerPage?.playerPageOpen_) { @@ -134,7 +143,9 @@ const listenForToggle = () => { }); }; -export const onRendererLoad = async ({ getConfig }: RendererContext) => { +export const onRendererLoad = async ({ + getConfig, +}: RendererContext) => { const config = await getConfig(); useNativePiP = config.useNativePiP; @@ -143,8 +154,8 @@ export const onRendererLoad = async ({ getConfig }: RendererContext { if ( - keyEventAreEqual(event, hotkeyEvent) - && !$('ytmusic-search-box')?.opened + keyEventAreEqual(event, hotkeyEvent) && + !$('ytmusic-search-box')?.opened ) { togglePictureInPicture(); } @@ -155,17 +166,21 @@ export const onRendererLoad = async ({ getConfig }: RendererContext { listenForToggle(); - cloneButton('.player-minimize-button')?.addEventListener('click', async () => { - await togglePictureInPicture(); - setTimeout(() => $('#player')?.click()); - }); + cloneButton('.player-minimize-button')?.addEventListener( + 'click', + async () => { + await togglePictureInPicture(); + setTimeout(() => $('#player')?.click()); + }, + ); // Allows easily closing the menu by programmatically clicking outside of it $('#expanding-menu')?.removeAttribute('no-cancel-on-outside-click'); // TODO: think about wether an additional button in songMenu is needed const popupContainer = $('ytmusic-popup-container'); - if (popupContainer) observer.observe(popupContainer, { - childList: true, - subtree: true, - }); + if (popupContainer) + observer.observe(popupContainer, { + childList: true, + subtree: true, + }); }; diff --git a/src/plugins/picture-in-picture/templates/picture-in-picture.html b/src/plugins/picture-in-picture/templates/picture-in-picture.html index 733a29bb..0d5033b8 100644 --- a/src/plugins/picture-in-picture/templates/picture-in-picture.html +++ b/src/plugins/picture-in-picture/templates/picture-in-picture.html @@ -24,21 +24,21 @@ xmlns="http://www.w3.org/2000/svg" y="0px" > - + - - - + +
{ let menu: Element | null = null; const immediateValueChangedListener = (e: Event) => { - playbackSpeed = (e as CustomEvent<{ value: number; }>).detail.value || MIN_PLAYBACK_SPEED; + playbackSpeed = + (e as CustomEvent<{ value: number }>).detail.value || MIN_PLAYBACK_SPEED; if (isNaN(playbackSpeed)) { playbackSpeed = 1; } @@ -38,7 +39,12 @@ const immediateValueChangedListener = (e: Event) => { }; const setupSliderListener = singleton(() => { - document.querySelector('#playback-speed-slider')?.addEventListener('immediate-value-changed', immediateValueChangedListener); + document + .querySelector('#playback-speed-slider') + ?.addEventListener( + 'immediate-value-changed', + immediateValueChangedListener, + ); }); const observePopupContainer = () => { @@ -49,9 +55,10 @@ const observePopupContainer = () => { if ( menu && - (menu.parentElement as HTMLElement & { eventSink_: Element | null }) - ?.eventSink_ - ?.matches('ytmusic-menu-renderer.ytmusic-player-bar')&& !menu.contains(slider) + ( + menu.parentElement as HTMLElement & { eventSink_: Element | null } + )?.eventSink_?.matches('ytmusic-menu-renderer.ytmusic-player-bar') && + !menu.contains(slider) ) { menu.prepend(slider); setupSliderListener(); @@ -82,14 +89,17 @@ const wheelEventListener = (e: WheelEvent) => { } // E.deltaY < 0 means wheel-up - playbackSpeed = roundToTwo(e.deltaY < 0 - ? Math.min(playbackSpeed + 0.01, MAX_PLAYBACK_SPEED) - : Math.max(playbackSpeed - 0.01, MIN_PLAYBACK_SPEED), + playbackSpeed = roundToTwo( + e.deltaY < 0 + ? Math.min(playbackSpeed + 0.01, MAX_PLAYBACK_SPEED) + : Math.max(playbackSpeed - 0.01, MIN_PLAYBACK_SPEED), ); updatePlayBackSpeed(); // Update slider position - const playbackSpeedSilder = document.querySelector('#playback-speed-slider'); + const playbackSpeedSilder = document.querySelector< + HTMLElement & { value: number } + >('#playback-speed-slider'); if (playbackSpeedSilder) { playbackSpeedSilder.value = playbackSpeed; } @@ -122,5 +132,10 @@ export const onUnload = () => { } slider.removeEventListener('wheel', wheelEventListener); getSongMenu()?.removeChild(slider); - document.querySelector('#playback-speed-slider')?.removeEventListener('immediate-value-changed', immediateValueChangedListener); + document + .querySelector('#playback-speed-slider') + ?.removeEventListener( + 'immediate-value-changed', + immediateValueChangedListener, + ); }; diff --git a/src/plugins/playback-speed/templates/slider.html b/src/plugins/playback-speed/templates/slider.html index 38526289..0952d914 100644 --- a/src/plugins/playback-speed/templates/slider.html +++ b/src/plugins/playback-speed/templates/slider.html @@ -27,7 +27,7 @@ tabindex="0" title="Playback speed" value="1" - > + >
+ >
- - + +
- + - +
{ const config = await getConfig(); - function changeOptions(changedOptions: Partial, options: PreciseVolumePluginConfig) { + function changeOptions( + changedOptions: Partial, + options: PreciseVolumePluginConfig, + ) { for (const option in changedOptions) { // HACK: Weird TypeScript error - (options as Record)[option] = (changedOptions as Record)[option]; + (options as Record)[option] = ( + changedOptions as Record + )[option]; } setConfig(options); } // Helper function for globalShortcuts prompt - const kb = (label_: string, value_: string, default_: string): KeybindOptions => ({ 'value': value_, 'label': label_, 'default': default_ || undefined }); + const kb = ( + label_: string, + value_: string, + default_: string, + ): KeybindOptions => ({ + value: value_, + label: label_, + default: default_ || undefined, + }); async function promptVolumeSteps(options: PreciseVolumePluginConfig) { - const output = await prompt({ - title: 'Volume Steps', - label: 'Choose Volume Increase/Decrease Steps', - value: options.steps || 1, - type: 'counter', - counterOptions: { minimum: 0, maximum: 100, multiFire: true }, - width: 380, - ...promptOptions(), - }, window); + const output = await prompt( + { + title: 'Volume Steps', + label: 'Choose Volume Increase/Decrease Steps', + value: options.steps || 1, + type: 'counter', + counterOptions: { minimum: 0, maximum: 100, multiFire: true }, + width: 380, + ...promptOptions(), + }, + window, + ); - if (output || output === 0) { // 0 is somewhat valid + if (output || output === 0) { + // 0 is somewhat valid changeOptions({ steps: output }, options); } } - async function promptGlobalShortcuts(options: PreciseVolumePluginConfig, item: MenuItem) { - const output = await prompt({ - title: 'Global Volume Keybinds', - label: 'Choose Global Volume Keybinds:', - type: 'keybind', - keybindOptions: [ - kb('Increase Volume', 'volumeUp', options.globalShortcuts?.volumeUp), - kb('Decrease Volume', 'volumeDown', options.globalShortcuts?.volumeDown), - ], - ...promptOptions(), - }, window); + async function promptGlobalShortcuts( + options: PreciseVolumePluginConfig, + item: MenuItem, + ) { + const output = await prompt( + { + title: 'Global Volume Keybinds', + label: 'Choose Global Volume Keybinds:', + type: 'keybind', + keybindOptions: [ + kb( + 'Increase Volume', + 'volumeUp', + options.globalShortcuts?.volumeUp, + ), + kb( + 'Decrease Volume', + 'volumeDown', + options.globalShortcuts?.volumeDown, + ), + ], + ...promptOptions(), + }, + window, + ); if (output) { const newGlobalShortcuts: { @@ -83,12 +115,15 @@ export default createPlugin({ volumeDown: string; } = { volumeUp: '', volumeDown: '' }; for (const { value, accelerator } of output) { - newGlobalShortcuts[value as keyof typeof newGlobalShortcuts] = accelerator; + newGlobalShortcuts[value as keyof typeof newGlobalShortcuts] = + accelerator; } changeOptions({ globalShortcuts: newGlobalShortcuts }, options); - item.checked = Boolean(options.globalShortcuts.volumeUp) || Boolean(options.globalShortcuts.volumeDown); + item.checked = + Boolean(options.globalShortcuts.volumeUp) || + Boolean(options.globalShortcuts.volumeDown); } else { // Reset checkbox if prompt was canceled item.checked = !item.checked; @@ -107,7 +142,10 @@ export default createPlugin({ { label: 'Global Hotkeys', type: 'checkbox', - checked: Boolean(config.globalShortcuts?.volumeUp ?? config.globalShortcuts?.volumeDown), + checked: Boolean( + config.globalShortcuts?.volumeUp ?? + config.globalShortcuts?.volumeDown, + ), click: (item) => promptGlobalShortcuts(config, item), }, { @@ -121,11 +159,15 @@ export default createPlugin({ const config = await getConfig(); if (config.globalShortcuts?.volumeUp) { - globalShortcut.register(config.globalShortcuts.volumeUp, () => ipc.send('changeVolume', true)); + globalShortcut.register(config.globalShortcuts.volumeUp, () => + ipc.send('changeVolume', true), + ); } if (config.globalShortcuts?.volumeDown) { - globalShortcut.register(config.globalShortcuts.volumeDown, () => ipc.send('changeVolume', false)); + globalShortcut.register(config.globalShortcuts.volumeDown, () => + ipc.send('changeVolume', false), + ); } }, @@ -135,5 +177,5 @@ export default createPlugin({ }, onPlayerApiReady, onConfigChange, - } + }, }); diff --git a/src/plugins/precise-volume/override.ts b/src/plugins/precise-volume/override.ts index 0b001824..0f4656b2 100644 --- a/src/plugins/precise-volume/override.ts +++ b/src/plugins/precise-volume/override.ts @@ -13,11 +13,12 @@ function overrideAddEventListener() { // eslint-disable-next-line @typescript-eslint/unbound-method Element.prototype._addEventListener = Element.prototype.addEventListener; // Override addEventListener to Ignore specific events in volume-slider - Element.prototype.addEventListener = function(type: string, listener: (event: Event) => void, useCapture = false) { - if (!( - ignored.id.includes(this.id) - && ignored.types.includes(type) - )) { + Element.prototype.addEventListener = function ( + type: string, + listener: (event: Event) => void, + useCapture = false, + ) { + if (!(ignored.id.includes(this.id) && ignored.types.includes(type))) { // eslint-disable-next-line @typescript-eslint/no-explicit-any,@typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-member-access (this as any)._addEventListener(type, listener, useCapture); } else if (window.electronIs.dev()) { @@ -29,11 +30,16 @@ function overrideAddEventListener() { export const overrideListener = () => { overrideAddEventListener(); // Restore original function after finished loading to avoid keeping Element.prototype altered - window.addEventListener('load', () => { - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-explicit-any,@typescript-eslint/no-unsafe-member-access - Element.prototype.addEventListener = (Element.prototype as any)._addEventListener; - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-explicit-any,@typescript-eslint/no-unsafe-member-access - (Element.prototype as any)._addEventListener = undefined; - - }, { once: true }); + window.addEventListener( + 'load', + () => { + /* eslint-disable @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-explicit-any,@typescript-eslint/no-unsafe-member-access */ + Element.prototype.addEventListener = ( + Element.prototype as any + )._addEventListener; + (Element.prototype as any)._addEventListener = undefined; + /* eslint-enable @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-explicit-any,@typescript-eslint/no-unsafe-member-access */ + }, + { once: true }, + ); }; diff --git a/src/plugins/precise-volume/renderer.ts b/src/plugins/precise-volume/renderer.ts index 0c9bbd22..51f9dc24 100644 --- a/src/plugins/precise-volume/renderer.ts +++ b/src/plugins/precise-volume/renderer.ts @@ -24,7 +24,10 @@ export const moveVolumeHud = debounce((showVideo: boolean) => { let options: PreciseVolumePluginConfig; -export const onPlayerApiReady = async (playerApi: YoutubePlayer, context: RendererContext) => { +export const onPlayerApiReady = async ( + playerApi: YoutubePlayer, + context: RendererContext, +) => { options = await context.getConfig(); api = playerApi; @@ -57,14 +60,20 @@ export const onPlayerApiReady = async (playerApi: YoutubePlayer, context: Render setupLocalArrowShortcuts(); // Workaround: computedStyleMap().get(string) returns CSSKeywordValue instead of CSSStyleValue - const noVid = ($('#main-panel')?.computedStyleMap().get('display') as CSSKeywordValue)?.value === 'none'; + const noVid = + ($('#main-panel')?.computedStyleMap().get('display') as CSSKeywordValue) + ?.value === 'none'; injectVolumeHud(noVid); if (!noVid) { setupVideoPlayerOnwheel(); if (!window.mainConfig.plugins.isEnabled('video-toggle')) { // Video-toggle handles hud positioning on its own - const videoMode = () => api.getPlayerResponse().videoDetails?.musicVideoType !== 'MUSIC_VIDEO_TYPE_ATV'; - $('video')?.addEventListener('srcChanged', () => moveVolumeHud(videoMode())); + const videoMode = () => + api.getPlayerResponse().videoDetails?.musicVideoType !== + 'MUSIC_VIDEO_TYPE_ATV'; + $('video')?.addEventListener('srcChanged', () => + moveVolumeHud(videoMode()), + ); } } } @@ -80,7 +89,8 @@ export const onPlayerApiReady = async (playerApi: YoutubePlayer, context: Render ); } else { const position = 'top: 10px; left: 10px;'; - const mainStyle = 'font-size: xxx-large; webkit-text-stroke: 1px black; font-weight: 600;'; + const mainStyle = + 'font-size: xxx-large; webkit-text-stroke: 1px black; font-weight: 600;'; $('#song-video')?.insertAdjacentHTML( 'afterend', @@ -149,8 +159,11 @@ export const onPlayerApiReady = async (playerApi: YoutubePlayer, context: Render // This checks that volume-slider was manually set const target = mutation.target; const targetValueNumeric = Number(target.value); - if (mutation.oldValue !== target.value - && (typeof options.savedVolume !== 'number' || Math.abs(options.savedVolume - targetValueNumeric) > 4)) { + if ( + mutation.oldValue !== target.value && + (typeof options.savedVolume !== 'number' || + Math.abs(options.savedVolume - targetValueNumeric) > 4) + ) { // Diff>4 means it was manually set setTooltip(targetValueNumeric); saveVolume(targetValueNumeric); @@ -189,9 +202,11 @@ export const onPlayerApiReady = async (playerApi: YoutubePlayer, context: Render function changeVolume(toIncrease: boolean) { // Apply volume change if valid const steps = Number(options.steps || 1); - setVolume(toIncrease - ? Math.min(api.getVolume() + steps, 100) - : Math.max(api.getVolume() - steps, 0)); + setVolume( + toIncrease + ? Math.min(api.getVolume() + steps, 100) + : Math.max(api.getVolume() - steps, 0), + ); } function updateVolumeSlider() { @@ -200,7 +215,9 @@ export const onPlayerApiReady = async (playerApi: YoutubePlayer, context: Render for (const slider of ['#volume-slider', '#expand-volume-slider']) { const silderElement = $(slider); if (silderElement) { - silderElement.value = String(savedVolume > 0 && savedVolume < 5 ? 5 : savedVolume); + silderElement.value = String( + savedVolume > 0 && savedVolume < 5 ? 5 : savedVolume, + ); } } } @@ -235,7 +252,9 @@ export const onPlayerApiReady = async (playerApi: YoutubePlayer, context: Render function setupLocalArrowShortcuts() { if (options.arrowsShortcut) { window.addEventListener('keydown', (event) => { - if ($('ytmusic-search-box')?.opened) { + if ( + $('ytmusic-search-box')?.opened + ) { return; } @@ -256,7 +275,9 @@ export const onPlayerApiReady = async (playerApi: YoutubePlayer, context: Render } } - context.ipc.on('changeVolume', (toIncrease: boolean) => changeVolume(toIncrease)); + context.ipc.on('changeVolume', (toIncrease: boolean) => + changeVolume(toIncrease), + ); context.ipc.on('setVolume', (value: number) => setVolume(value)); firstRun(); diff --git a/src/plugins/precise-volume/volume-hud.css b/src/plugins/precise-volume/volume-hud.css index 6517eb94..6b6f1956 100644 --- a/src/plugins/precise-volume/volume-hud.css +++ b/src/plugins/precise-volume/volume-hud.css @@ -8,6 +8,6 @@ text-shadow: rgba(0, 0, 0, 0.5) 0px 0px 12px; } -ytmusic-player[player-ui-state_="MINIPLAYER"] #volumeHud { +ytmusic-player[player-ui-state_='MINIPLAYER'] #volumeHud { top: 0 !important; } diff --git a/src/plugins/quality-changer/index.ts b/src/plugins/quality-changer/index.ts index 6cfb9334..337e3f05 100644 --- a/src/plugins/quality-changer/index.ts +++ b/src/plugins/quality-changer/index.ts @@ -9,28 +9,34 @@ import type { YoutubePlayer } from '@/types/youtube-player'; export default createPlugin({ name: 'Video Quality Changer', - description: 'Allows changing the video quality with a button on the video overlay', + description: + 'Allows changing the video quality with a button on the video overlay', restartNeeded: false, config: { enabled: false, }, backend({ ipc, window }) { - ipc.handle('qualityChanger', async (qualityLabels: string[], currentIndex: number) => await dialog.showMessageBox(window, { - type: 'question', - buttons: qualityLabels, - defaultId: currentIndex, - title: 'Choose Video Quality', - message: 'Choose Video Quality:', - detail: `Current Quality: ${qualityLabels[currentIndex]}`, - cancelId: -1, - })); + ipc.handle( + 'qualityChanger', + async (qualityLabels: string[], currentIndex: number) => + await dialog.showMessageBox(window, { + type: 'question', + buttons: qualityLabels, + defaultId: currentIndex, + title: 'Choose Video Quality', + message: 'Choose Video Quality:', + detail: `Current Quality: ${qualityLabels[currentIndex]}`, + cancelId: -1, + }), + ); }, renderer: { qualitySettingsButton: ElementFromHtml(QualitySettingsTemplate), onPlayerApiReady(api: YoutubePlayer, context) { - const getPlayer = () => document.querySelector('#player'); + const getPlayer = () => + document.querySelector('#player'); const chooseQuality = () => { setTimeout(() => getPlayer()?.click()); @@ -38,20 +44,27 @@ export default createPlugin({ const currentIndex = qualityLevels.indexOf(api.getPlaybackQuality()); - (context.ipc.invoke('qualityChanger', api.getAvailableQualityLabels(), currentIndex) as Promise<{ response: number }>) - .then((promise) => { - if (promise.response === -1) { - return; - } + ( + context.ipc.invoke( + 'qualityChanger', + api.getAvailableQualityLabels(), + currentIndex, + ) as Promise<{ response: number }> + ).then((promise) => { + if (promise.response === -1) { + return; + } - const newQuality = qualityLevels[promise.response]; - api.setPlaybackQualityRange(newQuality); - api.setPlaybackQuality(newQuality); - }); + const newQuality = qualityLevels[promise.response]; + api.setPlaybackQualityRange(newQuality); + api.setPlaybackQuality(newQuality); + }); }; const setup = () => { - document.querySelector('.top-row-buttons.ytmusic-player')?.prepend(this.qualitySettingsButton); + document + .querySelector('.top-row-buttons.ytmusic-player') + ?.prepend(this.qualitySettingsButton); this.qualitySettingsButton.addEventListener('click', chooseQuality); }; @@ -59,8 +72,9 @@ export default createPlugin({ setup(); }, stop() { - document.querySelector('.top-row-buttons.ytmusic-player')?.removeChild(this.qualitySettingsButton); + document + .querySelector('.top-row-buttons.ytmusic-player') + ?.removeChild(this.qualitySettingsButton); }, - } + }, }); - diff --git a/src/plugins/quality-changer/templates/qualitySettingsTemplate.html b/src/plugins/quality-changer/templates/qualitySettingsTemplate.html index 1b7d5b1f..2a561eff 100644 --- a/src/plugins/quality-changer/templates/qualitySettingsTemplate.html +++ b/src/plugins/quality-changer/templates/qualitySettingsTemplate.html @@ -1,15 +1,25 @@ - + - + + d="M19.43 12.98c.04-.32.07-.64.07-.98s-.03-.66-.07-.98l2.1-1.65c.2-.15.25-.42.13-.64l-2-3.46c-.12-.22-.4-.3-.6-.22l-2.5 1c-.52-.4-1.08-.73-1.7-.98l-.37-2.65c-.06-.24-.27-.42-.5-.42h-4c-.27 0-.48.18-.5.42l-.4 2.65c-.6.25-1.17.6-1.7.98l-2.48-1c-.23-.1-.5 0-.6.22l-2 3.46c-.14.22-.08.5.1.64l2.12 1.65c-.04.32-.07.65-.07.98s.02.66.06.98l-2.1 1.65c-.2.15-.25.42-.13.64l2 3.46c.12.22.4.3.6.22l2.5-1c.52.4 1.08.73 1.7.98l.37 2.65c.04.24.25.42.5.42h4c.25 0 .46-.18.5-.42l.37-2.65c.6-.25 1.17-.6 1.7-.98l2.48 1c.23.1.5 0 .6-.22l2-3.46c.13-.22.08-.5-.1-.64l-2.12-1.65zM12 15.5c-1.93 0-3.5-1.57-3.5-3.5s1.57-3.5 3.5-3.5 3.5 1.57 3.5 3.5-1.57 3.5-3.5 3.5z" + > diff --git a/src/plugins/shortcuts/index.ts b/src/plugins/shortcuts/index.ts index a868f703..bc897981 100644 --- a/src/plugins/shortcuts/index.ts +++ b/src/plugins/shortcuts/index.ts @@ -12,11 +12,12 @@ export type ShortcutsPluginConfig = { overrideMediaKeys: boolean; global: ShortcutMappingType; local: ShortcutMappingType; -} +}; export default createPlugin({ name: 'Shortcuts (& MPRIS)', - description: 'Allows setting global hotkeys for playback (play/pause/next/previous) + disable media osd by overriding media keys + enable Ctrl/CMD + F to search + enable linux mpris support for mediakeys + custom hotkeys for advanced users', + description: + 'Allows setting global hotkeys for playback (play/pause/next/previous) + disable media osd by overriding media keys + enable Ctrl/CMD + F to search + enable linux mpris support for mediakeys + custom hotkeys for advanced users', restartNeeded: true, config: { enabled: false, diff --git a/src/plugins/shortcuts/main.ts b/src/plugins/shortcuts/main.ts index 18480062..89ce111e 100644 --- a/src/plugins/shortcuts/main.ts +++ b/src/plugins/shortcuts/main.ts @@ -9,20 +9,30 @@ import type { ShortcutMappingType, ShortcutsPluginConfig } from './index'; import type { BackendContext } from '@/types/contexts'; - -function _registerGlobalShortcut(webContents: Electron.WebContents, shortcut: string, action: (webContents: Electron.WebContents) => void) { +function _registerGlobalShortcut( + webContents: Electron.WebContents, + shortcut: string, + action: (webContents: Electron.WebContents) => void, +) { globalShortcut.register(shortcut, () => { action(webContents); }); } -function _registerLocalShortcut(win: BrowserWindow, shortcut: string, action: (webContents: Electron.WebContents) => void) { +function _registerLocalShortcut( + win: BrowserWindow, + shortcut: string, + action: (webContents: Electron.WebContents) => void, +) { registerElectronLocalShortcut(win, shortcut, () => { action(win.webContents); }); } -export const onMainLoad = async ({ getConfig, window }: BackendContext) => { +export const onMainLoad = async ({ + getConfig, + window, +}: BackendContext) => { const config = await getConfig(); const songControls = getSongControls(window); @@ -45,7 +55,10 @@ export const onMainLoad = async ({ getConfig, window }: BackendContext void = songControls[action]; if (typeof actionCallback !== 'function') { console.warn('Invalid action', action); @@ -65,8 +83,13 @@ export const onMainLoad = async ({ getConfig, window }: BackendContext): Promise => { +export const onMenu = async ({ + window, + getConfig, + setConfig, +}: MenuContext): Promise => { const config = await getConfig(); /** * Helper function for keybind prompt */ - const kb = (label_: string, value_: string, default_?: string): KeybindOptions => ({ value: value_, label: label_, default: default_ }); + const kb = ( + label_: string, + value_: string, + default_?: string, + ): KeybindOptions => ({ value: value_, label: label_, default: default_ }); - async function promptKeybind(config: ShortcutsPluginConfig, win: BrowserWindow) { - const output = await prompt({ - title: 'Global Keybinds', - label: 'Choose Global Keybinds for Songs Control:', - type: 'keybind', - keybindOptions: [ // If default=undefined then no default is used - kb('Previous', 'previous', config.global?.previous), - kb('Play / Pause', 'playPause', config.global?.playPause), - kb('Next', 'next', config.global?.next), - ], - height: 270, - ...promptOptions(), - }, win); + async function promptKeybind( + config: ShortcutsPluginConfig, + win: BrowserWindow, + ) { + const output = await prompt( + { + title: 'Global Keybinds', + label: 'Choose Global Keybinds for Songs Control:', + type: 'keybind', + keybindOptions: [ + // If default=undefined then no default is used + kb('Previous', 'previous', config.global?.previous), + kb('Play / Pause', 'playPause', config.global?.playPause), + kb('Next', 'next', config.global?.next), + ], + height: 270, + ...promptOptions(), + }, + win, + ); if (output) { const newConfig = { ...config }; for (const { value, accelerator } of output) { - newConfig.global[value as keyof ShortcutsPluginConfig['global']] = accelerator; + newConfig.global[value as keyof ShortcutsPluginConfig['global']] = + accelerator; } setConfig(config); diff --git a/src/plugins/shortcuts/mpris-service.d.ts b/src/plugins/shortcuts/mpris-service.d.ts index e5201050..8dceee23 100644 --- a/src/plugins/shortcuts/mpris-service.d.ts +++ b/src/plugins/shortcuts/mpris-service.d.ts @@ -3,7 +3,6 @@ declare module '@jellybrick/mpris-service' { import { interface as dbusInterface } from 'dbus-next'; - interface RootInterfaceOptions { identity: string; supportedUriSchemes: string[]; @@ -105,14 +104,11 @@ declare module '@jellybrick/mpris-service' { setProperty(property: string, valuePlain: unknown): void; } - interface RootInterface { - } + interface RootInterface {} - interface PlayerInterface { - } + interface PlayerInterface {} interface TracklistInterface { - TrackListReplaced(tracks: Track[]): void; TrackAdded(afterTrack: string): void; @@ -121,7 +117,6 @@ declare module '@jellybrick/mpris-service' { } interface PlaylistsInterface { - PlaylistChanged(playlist: unknown[]): void; setActivePlaylistId(playlistId: string): void; diff --git a/src/plugins/shortcuts/mpris.ts b/src/plugins/shortcuts/mpris.ts index 115d2d74..b2dcabe5 100644 --- a/src/plugins/shortcuts/mpris.ts +++ b/src/plugins/shortcuts/mpris.ts @@ -21,14 +21,17 @@ function setupMPRIS() { function registerMPRIS(win: BrowserWindow) { const songControls = getSongControls(win); - const { playPause, next, previous, volumeMinus10, volumePlus10, shuffle } = songControls; + const { playPause, next, previous, volumeMinus10, volumePlus10, shuffle } = + songControls; try { // TODO: "Typing" for this arguments const secToMicro = (n: unknown) => Math.round(Number(n) * 1e6); const microToSec = (n: unknown) => Math.round(Number(n) / 1e6); - const seekTo = (e: { position: unknown }) => win.webContents.send('seekTo', microToSec(e.position)); - const seekBy = (o: unknown) => win.webContents.send('seekBy', microToSec(o)); + const seekTo = (e: { position: unknown }) => + win.webContents.send('seekTo', microToSec(e.position)); + const seekBy = (o: unknown) => + win.webContents.send('seekBy', microToSec(o)); const player = setupMPRIS(); @@ -42,7 +45,7 @@ function registerMPRIS(win: BrowserWindow) { ipcMain.on('seeked', (_, t: number) => player.seeked(secToMicro(t))); let currentSeconds = 0; - ipcMain.on('timeChanged', (_, t: number) => currentSeconds = t); + ipcMain.on('timeChanged', (_, t: number) => (currentSeconds = t)); ipcMain.on('repeatChanged', (_, mode: string) => { switch (mode) { @@ -63,7 +66,11 @@ function registerMPRIS(win: BrowserWindow) { }); player.on('loopStatus', (status: string) => { // SwitchRepeat cycles between states in that order - const switches = [mpris.LOOP_STATUS_NONE, mpris.LOOP_STATUS_PLAYLIST, mpris.LOOP_STATUS_TRACK]; + const switches = [ + mpris.LOOP_STATUS_NONE, + mpris.LOOP_STATUS_PLAYLIST, + mpris.LOOP_STATUS_TRACK, + ]; const currentIndex = switches.indexOf(player.loopStatus); const targetIndex = switches.indexOf(status); @@ -91,7 +98,10 @@ function registerMPRIS(win: BrowserWindow) { } }); player.on('playpause', () => { - player.playbackStatus = player.playbackStatus === mpris.PLAYBACK_STATUS_PLAYING ? mpris.PLAYBACK_STATUS_PAUSED : mpris.PLAYBACK_STATUS_PLAYING; + player.playbackStatus = + player.playbackStatus === mpris.PLAYBACK_STATUS_PLAYING + ? mpris.PLAYBACK_STATUS_PAUSED + : mpris.PLAYBACK_STATUS_PLAYING; playPause(); }); @@ -106,7 +116,9 @@ function registerMPRIS(win: BrowserWindow) { shuffle(); } }); - player.on('open', (args: { uri: string }) => { win.loadURL(args.uri); }); + player.on('open', (args: { uri: string }) => { + win.loadURL(args.uri); + }); let mprisVolNewer = false; let autoUpdate = false; @@ -166,7 +178,9 @@ function registerMPRIS(win: BrowserWindow) { player.metadata = data; player.seeked(secToMicro(songInfo.elapsedSeconds)); - player.playbackStatus = songInfo.isPaused ? mpris.PLAYBACK_STATUS_PAUSED : mpris.PLAYBACK_STATUS_PLAYING; + player.playbackStatus = songInfo.isPaused + ? mpris.PLAYBACK_STATUS_PAUSED + : mpris.PLAYBACK_STATUS_PLAYING; } }); } catch (error) { diff --git a/src/plugins/skip-silences/index.ts b/src/plugins/skip-silences/index.ts index d6468082..9d8dbcbd 100644 --- a/src/plugins/skip-silences/index.ts +++ b/src/plugins/skip-silences/index.ts @@ -17,5 +17,5 @@ export default createPlugin({ renderer: { start: onRendererLoad, stop: onRendererUnload, - } + }, }); diff --git a/src/plugins/skip-silences/renderer.ts b/src/plugins/skip-silences/renderer.ts index c8c74061..477cd1d1 100644 --- a/src/plugins/skip-silences/renderer.ts +++ b/src/plugins/skip-silences/renderer.ts @@ -67,16 +67,15 @@ const audioCanPlayListener = (e: CustomEvent) => { history += element; } - if (history == 0 // Silent - - && !( - video && ( - video.paused - || video.seeking - || video.ended - || video.muted - || video.volume === 0 - ) + if ( + history == 0 && // Silent + !( + video && + (video.paused || + video.seeking || + video.ended || + video.muted || + video.volume === 0) ) ) { isSilent = true; @@ -112,23 +111,18 @@ const audioCanPlayListener = (e: CustomEvent) => { video?.addEventListener('seeked', playOrSeekHandler); }; -export const onRendererLoad = async ({ getConfig }: RendererContext) => { +export const onRendererLoad = async ({ + getConfig, +}: RendererContext) => { config = await getConfig(); - document.addEventListener( - 'audioCanPlay', - audioCanPlayListener, - { - passive: true, - }, - ); + document.addEventListener('audioCanPlay', audioCanPlayListener, { + passive: true, + }); }; export const onRendererUnload = () => { - document.removeEventListener( - 'audioCanPlay', - audioCanPlayListener, - ); + document.removeEventListener('audioCanPlay', audioCanPlayListener); if (playOrSeekHandler) { const video = document.querySelector('video'); diff --git a/src/plugins/sponsorblock/index.ts b/src/plugins/sponsorblock/index.ts index 151fdd92..a5486c40 100644 --- a/src/plugins/sponsorblock/index.ts +++ b/src/plugins/sponsorblock/index.ts @@ -10,14 +10,22 @@ import type { Segment, SkipSegment } from './types'; export type SponsorBlockPluginConfig = { enabled: boolean; apiURL: string; - categories: ('sponsor' | 'intro' | 'outro' | 'interaction' | 'selfpromo' | 'music_offtopic')[]; + categories: ( + | 'sponsor' + | 'intro' + | 'outro' + | 'interaction' + | 'selfpromo' + | 'music_offtopic' + )[]; }; let currentSegments: Segment[] = []; export default createPlugin({ name: 'SponsorBlock', - description: 'Automatically Skips non-music parts like intro/outro or parts of music videos where the song isn\'t playing', + description: + "Automatically Skips non-music parts like intro/outro or parts of music videos where the song isn't playing", restartNeeded: true, config: { enabled: false, @@ -32,7 +40,11 @@ export default createPlugin({ ], } as SponsorBlockPluginConfig, async backend({ getConfig, ipc }) { - const fetchSegments = async (apiURL: string, categories: string[], videoId: string) => { + const fetchSegments = async ( + apiURL: string, + categories: string[], + videoId: string, + ) => { const sponsorBlockURL = `${apiURL}/api/skipSegments?videoID=${videoId}&categories=${JSON.stringify( categories, )}`; @@ -48,10 +60,8 @@ export default createPlugin({ return []; } - const segments = await resp.json() as SkipSegment[]; - return sortSegments( - segments.map((submission) => submission.segment), - ); + const segments = (await resp.json()) as SkipSegment[]; + return sortSegments(segments.map((submission) => submission.segment)); } catch (error) { if (is.dev()) { console.log('error on sponsorblock request:', error); @@ -66,7 +76,11 @@ export default createPlugin({ const { apiURL, categories } = config; ipc.on('video-src-changed', async (data: GetPlayerResponse) => { - const segments = await fetchSegments(apiURL, categories, data?.videoDetails?.videoId); + const segments = await fetchSegments( + apiURL, + categories, + data?.videoDetails?.videoId, + ); ipc.send('sponsorblock-skip', segments); }); }, @@ -77,8 +91,8 @@ export default createPlugin({ for (const segment of currentSegments) { if ( - target.currentTime >= segment[0] - && target.currentTime < segment[1] + target.currentTime >= segment[0] && + target.currentTime < segment[1] ) { target.currentTime = segment[1]; if (window.electronIs.dev()) { @@ -88,7 +102,7 @@ export default createPlugin({ } } }, - resetSegments: () => currentSegments = [], + resetSegments: () => (currentSegments = []), start({ ipc }) { ipc.on('sponsorblock-skip', (segments: Segment[]) => { currentSegments = segments; @@ -108,6 +122,6 @@ export default createPlugin({ video.removeEventListener('timeupdate', this.timeUpdateListener); video.removeEventListener('emptied', this.resetSegments); - } - } + }, + }, }); diff --git a/src/plugins/sponsorblock/types.ts b/src/plugins/sponsorblock/types.ts index 4bfe7e36..5c18f3d8 100644 --- a/src/plugins/sponsorblock/types.ts +++ b/src/plugins/sponsorblock/types.ts @@ -1,12 +1,13 @@ export type Segment = [number, number]; -export interface SkipSegment { // Array of this object +export interface SkipSegment { + // Array of this object segment: Segment; //[0, 15.23] start and end time in seconds - UUID: string, - category: string, // [1] - videoDuration: number // Duration of video when submission occurred (to be used to determine when a submission is out of date). 0 when unknown. +- 1 second - actionType: string, // [3] - locked: number, // if submission is locked - votes: number, // Votes on segment - description: string, // title for chapters, empty string for other segments + UUID: string; + category: string; // [1] + videoDuration: number; // Duration of video when submission occurred (to be used to determine when a submission is out of date). 0 when unknown. +- 1 second + actionType: string; // [3] + locked: number; // if submission is locked + votes: number; // Votes on segment + description: string; // title for chapters, empty string for other segments } diff --git a/src/plugins/taskbar-mediacontrol/index.ts b/src/plugins/taskbar-mediacontrol/index.ts index a90b9439..c4fad613 100644 --- a/src/plugins/taskbar-mediacontrol/index.ts +++ b/src/plugins/taskbar-mediacontrol/index.ts @@ -37,14 +37,18 @@ export default createPlugin({ click() { previous(); }, - }, { + }, + { tooltip: 'Play/Pause', // Update icon based on play state - icon: nativeImage.createFromPath(songInfo.isPaused ? get('play') : get('pause')), + icon: nativeImage.createFromPath( + songInfo.isPaused ? get('play') : get('pause'), + ), click() { playPause(); }, - }, { + }, + { tooltip: 'Next', icon: nativeImage.createFromPath(get('next')), click() { @@ -81,5 +85,5 @@ export default createPlugin({ window.on('show', () => { setThumbar(currentSongInfo); }); - } + }, }); diff --git a/src/plugins/touchbar/index.ts b/src/plugins/touchbar/index.ts index 9fc80ce9..49d09cea 100644 --- a/src/plugins/touchbar/index.ts +++ b/src/plugins/touchbar/index.ts @@ -70,8 +70,8 @@ export default createPlugin({ ], }); - - const { playPause, next, previous, dislike, like } = getSongControls(window); + const { playPause, next, previous, dislike, like } = + getSongControls(window); // If the page is ready, register the callback window.once('ready-to-show', () => { @@ -95,5 +95,5 @@ export default createPlugin({ window.setTouchBar(touchBar); }); }); - } + }, }); diff --git a/src/plugins/tuna-obs/index.ts b/src/plugins/tuna-obs/index.ts index abcad12d..da79acdb 100644 --- a/src/plugins/tuna-obs/index.ts +++ b/src/plugins/tuna-obs/index.ts @@ -19,7 +19,7 @@ interface Data { export default createPlugin({ name: 'Tuna OBS', - description: 'Integration with OBS\'s plugin Tuna', + description: "Integration with OBS's plugin Tuna", restartNeeded: true, config: { enabled: false, @@ -48,18 +48,26 @@ export default createPlugin({ 'Access-Control-Allow-Origin': '*', }; const url = `http://127.0.0.1:${port}/`; - net.fetch(url, { - method: 'POST', - headers, - body: JSON.stringify({ data }), - }).catch((error: { code: number, errno: number }) => { - if (is.dev()) { - console.debug(`Error: '${error.code || error.errno}' - when trying to access obs-tuna webserver at port ${port}`); - } - }); + net + .fetch(url, { + method: 'POST', + headers, + body: JSON.stringify({ data }), + }) + .catch((error: { code: number; errno: number }) => { + if (is.dev()) { + console.debug( + `Error: '${ + error.code || error.errno + }' - when trying to access obs-tuna webserver at port ${port}`, + ); + } + }); }; - ipc.on('ytmd:player-api-loaded', () => ipc.send('setupTimeChangedListener')); + ipc.on('ytmd:player-api-loaded', () => + ipc.send('setupTimeChangedListener'), + ); ipc.on('timeChanged', (t: number) => { if (!this.data.title) { return; @@ -85,6 +93,6 @@ export default createPlugin({ this.data.album = songInfo.album; post(this.data); }); - } - } + }, + }, }); diff --git a/src/plugins/utils/common/types.ts b/src/plugins/utils/common/types.ts index 1a0ef5a9..e69b89fb 100644 --- a/src/plugins/utils/common/types.ts +++ b/src/plugins/utils/common/types.ts @@ -10,19 +10,23 @@ export interface Plugin { config: ConfigType; } -export interface RendererPlugin extends Plugin { +export interface RendererPlugin + extends Plugin { onEnable: (config: ConfigType) => void; } -export interface MainPlugin extends Plugin { +export interface MainPlugin + extends Plugin { onEnable: (window: BrowserWindow, config: ConfigType) => string; } -export interface PreloadPlugin extends Plugin { +export interface PreloadPlugin + extends Plugin { onEnable: (config: ConfigType) => void; } -export interface MenuPlugin extends Plugin { +export interface MenuPlugin + extends Plugin { onEnable: (config: ConfigType) => void; } diff --git a/src/plugins/utils/main/css.ts b/src/plugins/utils/main/css.ts index a69f5f85..7a2da6e6 100644 --- a/src/plugins/utils/main/css.ts +++ b/src/plugins/utils/main/css.ts @@ -4,9 +4,18 @@ type Unregister = () => void; let isLoaded = false; -const cssToInject = new Map void) | undefined>(); -const cssToInjectFile = new Map void) | undefined>(); -export const injectCSS = async (webContents: Electron.WebContents, css: string): Promise => { +const cssToInject = new Map< + string, + ((unregister: Unregister) => void) | undefined +>(); +const cssToInjectFile = new Map< + string, + ((unregister: Unregister) => void) | undefined +>(); +export const injectCSS = async ( + webContents: Electron.WebContents, + css: string, +): Promise => { if (isLoaded) { const key = await webContents.insertCSS(css); return async () => await webContents.removeInsertedCSS(key); @@ -20,7 +29,10 @@ export const injectCSS = async (webContents: Electron.WebContents, css: string): }); }; -export const injectCSSAsFile = async (webContents: Electron.WebContents, filepath: string): Promise => { +export const injectCSSAsFile = async ( + webContents: Electron.WebContents, + filepath: string, +): Promise => { if (isLoaded) { const key = await webContents.insertCSS(fs.readFileSync(filepath, 'utf-8')); return async () => await webContents.removeInsertedCSS(key); @@ -47,7 +59,9 @@ const setupCssInjection = (webContents: Electron.WebContents) => { }); cssToInjectFile.forEach(async (callback, filepath) => { - const key = await webContents.insertCSS(fs.readFileSync(filepath, 'utf-8')); + const key = await webContents.insertCSS( + fs.readFileSync(filepath, 'utf-8'), + ); const remove = async () => await webContents.removeInsertedCSS(key); callback?.(remove); diff --git a/src/plugins/utils/main/fetch.ts b/src/plugins/utils/main/fetch.ts index 9c71c09f..3db7618c 100644 --- a/src/plugins/utils/main/fetch.ts +++ b/src/plugins/utils/main/fetch.ts @@ -1,21 +1,22 @@ import { net } from 'electron'; -export const getNetFetchAsFetch = () => (async (input: RequestInfo | URL, init?: RequestInit) => { - const url = - typeof input === 'string' - ? new URL(input) - : input instanceof URL +export const getNetFetchAsFetch = () => + (async (input: RequestInfo | URL, init?: RequestInit) => { + const url = + typeof input === 'string' + ? new URL(input) + : input instanceof URL ? input : new URL(input.url); - if (init?.body && !init.method) { - init.method = 'POST'; - } + if (init?.body && !init.method) { + init.method = 'POST'; + } - const request = new Request( - url, - input instanceof Request ? input : undefined, - ); + const request = new Request( + url, + input instanceof Request ? input : undefined, + ); - return net.fetch(request, init); -}) as typeof fetch; + return net.fetch(request, init); + }) as typeof fetch; diff --git a/src/plugins/utils/main/fs.ts b/src/plugins/utils/main/fs.ts index 1e478796..4430ca83 100644 --- a/src/plugins/utils/main/fs.ts +++ b/src/plugins/utils/main/fs.ts @@ -2,7 +2,7 @@ import fs from 'node:fs'; export const fileExists = ( path: fs.PathLike, - callbackIfExists: { (): void; (): void; (): void; }, + callbackIfExists: { (): void; (): void; (): void }, callbackIfError: (() => void) | undefined = undefined, ) => { fs.access(path, fs.constants.F_OK, (error) => { diff --git a/src/plugins/utils/main/types.ts b/src/plugins/utils/main/types.ts index 18e66989..988b9a72 100644 --- a/src/plugins/utils/main/types.ts +++ b/src/plugins/utils/main/types.ts @@ -1,7 +1,13 @@ import type { Config, MainPlugin, MenuPlugin, PreloadPlugin } from '../common'; -export const defineMainPlugin = (plugin: MainPlugin) => plugin; +export const defineMainPlugin = ( + plugin: MainPlugin, +) => plugin; -export const definePreloadPlugin = (plugin: PreloadPlugin) => plugin; +export const definePreloadPlugin = ( + plugin: PreloadPlugin, +) => plugin; -export const defineMenuPlugin = (plugin: MenuPlugin) => plugin; +export const defineMenuPlugin = ( + plugin: MenuPlugin, +) => plugin; diff --git a/src/plugins/video-toggle/button-switcher.css b/src/plugins/video-toggle/button-switcher.css index 7273e820..3fed5c7f 100644 --- a/src/plugins/video-toggle/button-switcher.css +++ b/src/plugins/video-toggle/button-switcher.css @@ -25,7 +25,7 @@ } .video-toggle-custom-mode .video-switch-button:before { - content: "Video"; + content: 'Video'; position: absolute; top: 0; bottom: 0; @@ -54,12 +54,16 @@ position: relative; } -.video-toggle-custom-mode .video-switch-button-checkbox:checked + .video-switch-button-label:before { +.video-toggle-custom-mode + .video-switch-button-checkbox:checked + + .video-switch-button-label:before { transform: translateX(10rem); transition: transform 300ms linear; } -.video-toggle-custom-mode .video-switch-button-checkbox + .video-switch-button-label { +.video-toggle-custom-mode + .video-switch-button-checkbox + + .video-switch-button-label { position: relative; padding: 15px 0; display: block; @@ -67,8 +71,10 @@ pointer-events: none; } -.video-toggle-custom-mode .video-switch-button-checkbox + .video-switch-button-label:before { - content: ""; +.video-toggle-custom-mode + .video-switch-button-checkbox + + .video-switch-button-label:before { + content: ''; background: rgba(60, 60, 60, 0.4); height: 100%; width: 100%; diff --git a/src/plugins/video-toggle/index.ts b/src/plugins/video-toggle/index.ts index 3514994d..ec625def 100644 --- a/src/plugins/video-toggle/index.ts +++ b/src/plugins/video-toggle/index.ts @@ -13,11 +13,12 @@ export type VideoTogglePluginConfig = { mode: 'custom' | 'native' | 'disabled'; forceHide: boolean; align: 'left' | 'middle' | 'right'; -} +}; export default createPlugin({ name: 'Video Toggle', - description: 'Adds a button to switch between Video/Song mode. can also optionally remove the whole video tab', + description: + 'Adds a button to switch between Video/Song mode. can also optionally remove the whole video tab', restartNeeded: true, config: { enabled: false, @@ -26,10 +27,7 @@ export default createPlugin({ forceHide: false, align: 'left', } as VideoTogglePluginConfig, - stylesheets: [ - buttonSwitcherStyle, - forceHideStyle, - ], + stylesheets: [buttonSwitcherStyle, forceHideStyle], menu: async ({ getConfig, setConfig }) => { const config = await getConfig(); @@ -124,14 +122,22 @@ export default createPlugin({ switch (config.mode) { case 'native': { - document.querySelector('ytmusic-player-page')?.setAttribute('has-av-switcher', ''); - document.querySelector('ytmusic-player')?.setAttribute('has-av-switcher', ''); + document + .querySelector('ytmusic-player-page') + ?.setAttribute('has-av-switcher', ''); + document + .querySelector('ytmusic-player') + ?.setAttribute('has-av-switcher', ''); return; } case 'disabled': { - document.querySelector('ytmusic-player-page')?.removeAttribute('has-av-switcher'); - document.querySelector('ytmusic-player')?.removeAttribute('has-av-switcher'); + document + .querySelector('ytmusic-player-page') + ?.removeAttribute('has-av-switcher'); + document + .querySelector('ytmusic-player') + ?.removeAttribute('has-av-switcher'); return; } } @@ -140,17 +146,22 @@ export default createPlugin({ const config = await getConfig(); this.config = config; - const moveVolumeHud = window.mainConfig.plugins.isEnabled('precise-volume') ? - preciseVolumeMoveVolumeHud as (_: boolean) => void - : (() => {}); + const moveVolumeHud = window.mainConfig.plugins.isEnabled( + 'precise-volume', + ) + ? (preciseVolumeMoveVolumeHud as (_: boolean) => void) + : () => {}; - const player = document.querySelector<(HTMLElement & { videoMode_: boolean; })>('ytmusic-player'); + const player = document.querySelector< + HTMLElement & { videoMode_: boolean } + >('ytmusic-player'); const video = document.querySelector('video'); const switchButtonDiv = ElementFromHtml(buttonTemplate); const forceThumbnail = (img: HTMLImageElement) => { - const thumbnails: ThumbnailElement[] = api?.getPlayerResponse()?.videoDetails?.thumbnail?.thumbnails ?? []; + const thumbnails: ThumbnailElement[] = + api?.getPlayerResponse()?.videoDetails?.thumbnail?.thumbnails ?? []; if (thumbnails && thumbnails.length > 0) { const thumbnail = thumbnails.at(-1)?.url.split('?')[0]; if (thumbnail) img.src = thumbnail; @@ -163,18 +174,28 @@ export default createPlugin({ } window.mainConfig.plugins.setOptions('video-toggle', config); - const checkbox = document.querySelector('.video-switch-button-checkbox'); // custom mode + const checkbox = document.querySelector( + '.video-switch-button-checkbox', + ); // custom mode if (checkbox) checkbox.checked = !config.hideVideo; if (player) { player.style.margin = showVideo ? '' : 'auto 0px'; - player.setAttribute('playback-mode', showVideo ? 'OMV_PREFERRED' : 'ATV_PREFERRED'); + player.setAttribute( + 'playback-mode', + showVideo ? 'OMV_PREFERRED' : 'ATV_PREFERRED', + ); - document.querySelector('#song-video.ytmusic-player')!.style.display = showVideo ? 'block' : 'none'; - document.querySelector('#song-image')!.style.display = showVideo ? 'none' : 'block'; + document.querySelector( + '#song-video.ytmusic-player', + )!.style.display = showVideo ? 'block' : 'none'; + document.querySelector('#song-image')!.style.display = + showVideo ? 'none' : 'block'; if (showVideo && video && !video.style.top) { - video.style.top = `${(player.clientHeight - video.clientHeight) / 2}px`; + video.style.top = `${ + (player.clientHeight - video.clientHeight) / 2 + }px`; } moveVolumeHud(showVideo); @@ -182,13 +203,17 @@ export default createPlugin({ }; const videoStarted = () => { - if (api.getPlayerResponse().videoDetails.musicVideoType === 'MUSIC_VIDEO_TYPE_ATV') { + if ( + api.getPlayerResponse().videoDetails.musicVideoType === + 'MUSIC_VIDEO_TYPE_ATV' + ) { // Video doesn't exist -> switch to song mode setVideoState(false); // Hide toggle button switchButtonDiv.style.display = 'none'; } else { - const songImage = document.querySelector('#song-image img'); + const songImage = + document.querySelector('#song-image img'); if (!songImage) { return; } @@ -197,7 +222,11 @@ export default createPlugin({ // Show toggle button switchButtonDiv.style.display = 'initial'; // Change display to video mode if video exist & video is hidden & option.hideVideo = false - if (!this.config?.hideVideo && document.querySelector('#song-video.ytmusic-player')?.style.display === 'none') { + if ( + !this.config?.hideVideo && + document.querySelector('#song-video.ytmusic-player') + ?.style.display === 'none' + ) { setVideoState(true); } else { moveVolumeHud(!this.config?.hideVideo); @@ -222,7 +251,9 @@ export default createPlugin({ } } }); - playbackModeObserver.observe(player, { attributeFilter: ['playback-mode'] }); + playbackModeObserver.observe(player, { + attributeFilter: ['playback-mode'], + }); } }; @@ -243,11 +274,16 @@ export default createPlugin({ } } }); - playbackModeObserver.observe(document.querySelector('#song-image img')!, { attributeFilter: ['src'] }); + playbackModeObserver.observe( + document.querySelector('#song-image img')!, + { attributeFilter: ['src'] }, + ); }; if (config.mode !== 'native' && config.mode != 'disabled') { - document.querySelector('#player')?.prepend(switchButtonDiv); + document + .querySelector('#player') + ?.prepend(switchButtonDiv); setVideoState(!config.hideVideo); forcePlaybackMode(); diff --git a/src/plugins/video-toggle/templates/button_template.html b/src/plugins/video-toggle/templates/button_template.html index 5b86cd75..76d81de3 100644 --- a/src/plugins/video-toggle/templates/button_template.html +++ b/src/plugins/video-toggle/templates/button_template.html @@ -1,4 +1,4 @@
- +
diff --git a/src/plugins/visualizer/butterchurn.d.ts b/src/plugins/visualizer/butterchurn.d.ts index 2803e6cd..dfdf6517 100644 --- a/src/plugins/visualizer/butterchurn.d.ts +++ b/src/plugins/visualizer/butterchurn.d.ts @@ -10,24 +10,43 @@ declare module 'butterchurn' { } class Visualizer { - constructor(audioContext: AudioContext, canvas: HTMLCanvasElement, opts: ButterchurnOptions); + constructor( + audioContext: AudioContext, + canvas: HTMLCanvasElement, + opts: ButterchurnOptions, + ); loseGLContext(): void; connectAudio(audioNode: AudioNode): void; disconnectAudio(audioNode: AudioNode): void; - static overrideDefaultVars(baseValsDefaults: unknown, baseVals: unknown): unknown; + static overrideDefaultVars( + baseValsDefaults: unknown, + baseVals: unknown, + ): unknown; createQVars(): Record; createTVars(): Record; createPerFramePool(baseVals: unknown): Record; createPerPixelPool(baseVals: unknown): Record; - createCustomShapePerFramePool(baseVals: unknown): Record; - createCustomWavePerFramePool(baseVals: unknown): Record; - static makeShapeResetPool(pool: Record, variables: string[], idx: number): Record; + createCustomShapePerFramePool( + baseVals: unknown, + ): Record; + createCustomWavePerFramePool( + baseVals: unknown, + ): Record; + static makeShapeResetPool( + pool: Record, + variables: string[], + idx: number, + ): Record; static base64ToArrayBuffer(base64: string): ArrayBuffer; loadPreset(presetMap: unknown, blendTime?: number): Promise; async loadWASMPreset(preset: unknown, blendTime: number): Promise; loadJSPreset(preset: unknown, blendTime: number): void; loadExtraImages(imageData: unknown): void; - setRendererSize(width: number, height: number, opts?: VisualizerOptions): void; + setRendererSize( + width: number, + height: number, + opts?: VisualizerOptions, + ): void; setInternalMeshSize(width: number, height: number): void; setOutputAA(useAA: boolean): void; setCanvas(canvas: HTMLCanvasElement): void; @@ -44,7 +63,11 @@ declare module 'butterchurn' { } export default class Butterchurn { - static createVisualizer(audioContext: AudioContext, canvas: HTMLCanvasElement, options?: ButterchurnOptions): Visualizer; + static createVisualizer( + audioContext: AudioContext, + canvas: HTMLCanvasElement, + options?: ButterchurnOptions, + ): Visualizer; } } diff --git a/src/plugins/visualizer/index.ts b/src/plugins/visualizer/index.ts index 294312b5..1edecedd 100644 --- a/src/plugins/visualizer/index.ts +++ b/src/plugins/visualizer/index.ts @@ -4,7 +4,7 @@ import { Visualizer } from './visualizers/visualizer'; import { ButterchurnVisualizer as butterchurn, VudioVisualizer as vudio, - WaveVisualizer as wave + WaveVisualizer as wave, } from './visualizers'; type WaveColor = { @@ -19,7 +19,7 @@ export type VisualizerPluginConfig = { preset: string; renderingFrequencyInMs: number; blendTimeInSeconds: number; - }, + }; vudio: { effect: string; accuracy: number; @@ -35,7 +35,7 @@ export type VisualizerPluginConfig = { horizontalAlign: string; verticalAlign: string; dottify: boolean; - } + }; }; wave: { animations: { @@ -51,7 +51,7 @@ export type VisualizerPluginConfig = { lineColor?: string | WaveColor; radius?: number; frequencyBand?: string; - } + }; }[]; }; }; @@ -151,7 +151,7 @@ export default createPlugin({ const config = await getConfig(); // eslint-disable-next-line @typescript-eslint/no-explicit-any - let visualizerType: { new(...args: any[]): Visualizer } = vudio; + let visualizerType: { new (...args: any[]): Visualizer } = vudio; if (config.type === 'wave') { visualizerType = wave; @@ -162,12 +162,15 @@ export default createPlugin({ document.addEventListener( 'audioCanPlay', (e) => { - const video = document.querySelector('video'); + const video = document.querySelector< + HTMLVideoElement & { captureStream(): MediaStream } + >('video'); if (!video) { return; } - const visualizerContainer = document.querySelector('#player'); + const visualizerContainer = + document.querySelector('#player'); if (!visualizerContainer) { return; } @@ -210,7 +213,10 @@ export default createPlugin({ resizeVisualizer(canvas.width, canvas.height); const visualizerContainerObserver = new ResizeObserver((entries) => { for (const entry of entries) { - resizeVisualizer(entry.contentRect.width, entry.contentRect.height); + resizeVisualizer( + entry.contentRect.width, + entry.contentRect.height, + ); } }); visualizerContainerObserver.observe(visualizerContainer); diff --git a/src/plugins/visualizer/visualizers/butterchurn.ts b/src/plugins/visualizer/visualizers/butterchurn.ts index 12c8fd31..1a2f8865 100644 --- a/src/plugins/visualizer/visualizers/butterchurn.ts +++ b/src/plugins/visualizer/visualizers/butterchurn.ts @@ -30,14 +30,10 @@ class ButterchurnVisualizer extends Visualizer { options, ); - this.visualizer = Butterchurn.createVisualizer( - audioContext, - canvas, - { - width: canvas.width, - height: canvas.height, - } - ); + this.visualizer = Butterchurn.createVisualizer(audioContext, canvas, { + width: canvas.width, + height: canvas.height, + }); const preset = ButterchurnPresets[options.butterchurn.preset]; this.visualizer.loadPreset(preset, options.butterchurn.blendTimeInSeconds); diff --git a/src/plugins/visualizer/visualizers/vudio.ts b/src/plugins/visualizer/visualizers/vudio.ts index 64167813..16f803fb 100644 --- a/src/plugins/visualizer/visualizers/vudio.ts +++ b/src/plugins/visualizer/visualizers/vudio.ts @@ -45,8 +45,7 @@ class VudioVisualizer extends Visualizer { }); } - render() { - } + render() {} } export default VudioVisualizer; diff --git a/src/plugins/visualizer/visualizers/wave.ts b/src/plugins/visualizer/visualizers/wave.ts index c1f1c7fd..3456e90c 100644 --- a/src/plugins/visualizer/visualizers/wave.ts +++ b/src/plugins/visualizer/visualizers/wave.ts @@ -3,6 +3,7 @@ import { Wave } from '@foobar404/wave'; import { Visualizer } from './visualizer'; import type { VisualizerPluginConfig } from '../index'; + class WaveVisualizer extends Visualizer { name = 'wave'; @@ -32,7 +33,10 @@ class WaveVisualizer extends Visualizer { canvas, ); for (const animation of options.wave.animations) { - const TargetVisualizer = this.visualizer.animations[animation.type as keyof typeof this.visualizer.animations]; + const TargetVisualizer = + this.visualizer.animations[ + animation.type as keyof typeof this.visualizer.animations + ]; this.visualizer.addAnimation( new TargetVisualizer(animation.config as never), // Magic of Typescript @@ -40,11 +44,9 @@ class WaveVisualizer extends Visualizer { } } - resize(_: number, __: number) { - } + resize(_: number, __: number) {} - render() { - } + render() {} } export default WaveVisualizer; diff --git a/src/plugins/visualizer/vudio.d.ts b/src/plugins/visualizer/vudio.d.ts index 4964951b..090a8833 100644 --- a/src/plugins/visualizer/vudio.d.ts +++ b/src/plugins/visualizer/vudio.d.ts @@ -9,7 +9,7 @@ declare module 'vudio/umd/vudio' { fadeSide?: boolean; } - interface WaveformOptions extends NoneWaveformOptions{ + interface WaveformOptions extends NoneWaveformOptions { horizontalAlign: 'left' | 'center' | 'right'; verticalAlign: 'top' | 'middle' | 'bottom'; } @@ -19,11 +19,15 @@ declare module 'vudio/umd/vudio' { accuracy?: number; width?: number; height?: number; - waveform?: WaveformOptions + waveform?: WaveformOptions; } class Vudio { - constructor(audio: HTMLAudioElement | MediaStream, canvas: HTMLCanvasElement, options: VudioOptions = {}); + constructor( + audio: HTMLAudioElement | MediaStream, + canvas: HTMLCanvasElement, + options: VudioOptions = {}, + ); dance(): void; pause(): void; diff --git a/src/providers/app-controls.ts b/src/providers/app-controls.ts index 93f08f56..ff3d574a 100644 --- a/src/providers/app-controls.ts +++ b/src/providers/app-controls.ts @@ -9,7 +9,11 @@ export const restart = () => restartInternal(); export const setupAppControls = () => { ipcMain.on('restart', restart); ipcMain.handle('getDownloadsFolder', () => app.getPath('downloads')); - ipcMain.on('reload', () => BrowserWindow.getFocusedWindow()?.webContents.loadURL(config.get('url'))); + ipcMain.on( + 'reload', + () => + BrowserWindow.getFocusedWindow()?.webContents.loadURL(config.get('url')), + ); ipcMain.handle('getPath', (_, ...args: string[]) => path.join(...args)); }; @@ -25,9 +29,9 @@ function sendToFrontInternal(channel: string, ...args: unknown[]) { } } -export const sendToFront - = process.type === 'browser' - ? sendToFrontInternal - : () => { - console.error('sendToFront called from renderer'); - }; +export const sendToFront = + process.type === 'browser' + ? sendToFrontInternal + : () => { + console.error('sendToFront called from renderer'); + }; diff --git a/src/providers/decorators.ts b/src/providers/decorators.ts index 3b59e8a8..d2e1f56c 100644 --- a/src/providers/decorators.ts +++ b/src/providers/decorators.ts @@ -11,7 +11,10 @@ export function singleton unknown>(fn: T): T { }) as T; } -export function debounce unknown>(fn: T, delay: number): T { +export function debounce unknown>( + fn: T, + delay: number, +): T { let timeout: NodeJS.Timeout; return ((...args) => { clearTimeout(timeout); @@ -19,13 +22,15 @@ export function debounce unknown>(fn: T, delay }) as T; } -export function cache R, P extends never[], R>(fn: T): T { +export function cache R, P extends never[], R>( + fn: T, +): T { let lastArgs: P; let lastResult: R; return ((...args: P) => { if ( - args.length !== lastArgs?.length - || args.some((arg, i) => arg !== lastArgs[i]) + args.length !== lastArgs?.length || + args.some((arg, i) => arg !== lastArgs[i]) ) { lastArgs = args; lastResult = fn(...args); @@ -39,7 +44,10 @@ export function cache R, P extends never[], R>(fn: T The following are currently unused, but potentially useful in the future */ -export function throttle unknown>(fn: T, delay: number): T { +export function throttle unknown>( + fn: T, + delay: number, +): T { let timeout: NodeJS.Timeout | undefined; return ((...args) => { if (timeout) { @@ -66,7 +74,10 @@ function memoize unknown>(fn: T): T { }) as T; } -function retry unknown>(fn: T, { retries = 3, delay = 1000 } = {}): T { +function retry unknown>( + fn: T, + { retries = 3, delay = 1000 } = {}, +): T { return ((...args) => { try { return fn(...args); diff --git a/src/providers/protocol-handler.ts b/src/providers/protocol-handler.ts index 49dd88b2..1ad52328 100644 --- a/src/providers/protocol-handler.ts +++ b/src/providers/protocol-handler.ts @@ -10,11 +10,9 @@ let protocolHandler: ((cmd: string) => void) | undefined; export function setupProtocolHandler(win: BrowserWindow) { if (process.defaultApp && process.argv.length >= 2) { - app.setAsDefaultProtocolClient( - APP_PROTOCOL, - process.execPath, - [path.resolve(process.argv[1])], - ); + app.setAsDefaultProtocolClient(APP_PROTOCOL, process.execPath, [ + path.resolve(process.argv[1]), + ]); } else { app.setAsDefaultProtocolClient(APP_PROTOCOL); } @@ -42,4 +40,3 @@ export default { handleProtocol, changeProtocolHandler, }; - diff --git a/src/providers/song-controls.ts b/src/providers/song-controls.ts index eae0fd60..75af545f 100644 --- a/src/providers/song-controls.ts +++ b/src/providers/song-controls.ts @@ -1,8 +1,16 @@ // This is used for to control the songs import { BrowserWindow } from 'electron'; -type Modifiers = (Electron.MouseInputEvent | Electron.MouseWheelInputEvent | Electron.KeyboardInputEvent)['modifiers']; -export const pressKey = (window: BrowserWindow, key: string, modifiers: Modifiers = []) => { +type Modifiers = ( + | Electron.MouseInputEvent + | Electron.MouseWheelInputEvent + | Electron.KeyboardInputEvent +)['modifiers']; +export const pressKey = ( + window: BrowserWindow, + key: string, + modifiers: Modifiers = [], +) => { window.webContents.sendInputEvent({ type: 'keyDown', modifiers, diff --git a/src/providers/song-info-front.ts b/src/providers/song-info-front.ts index b203a556..462e3f26 100644 --- a/src/providers/song-info-front.ts +++ b/src/providers/song-info-front.ts @@ -10,7 +10,8 @@ import type { VideoDataChanged } from '@/types/video-data-changed'; let songInfo: SongInfo = {} as SongInfo; export const getSongInfo = () => songInfo; -const $ = (s: string): E | null => document.querySelector(s); +const $ = (s: string): E | null => + document.querySelector(s); window.ipcRenderer.on('update-song-info', (_, extractedSongInfo: SongInfo) => { songInfo = extractedSongInfo; @@ -43,26 +44,31 @@ export const setupTimeChangedListener = singleton(() => { export const setupRepeatChangedListener = singleton(() => { const repeatObserver = new MutationObserver((mutations) => { - // provided by YouTube Music window.ipcRenderer.send( 'repeatChanged', - (mutations[0].target as Node & { - __dataHost: { - getState: () => GetState; + ( + mutations[0].target as Node & { + __dataHost: { + getState: () => GetState; + }; } - }).__dataHost.getState().queue.repeatMode, + ).__dataHost.getState().queue.repeatMode, ); }); - repeatObserver.observe($('#right-controls .repeat')!, { attributeFilter: ['title'] }); + repeatObserver.observe($('#right-controls .repeat')!, { + attributeFilter: ['title'], + }); // Emit the initial value as well; as it's persistent between launches. // provided by YouTube Music window.ipcRenderer.send( 'repeatChanged', - $ GetState; - }>('ytmusic-player-bar')?.getState().queue.repeatMode, + $< + HTMLElement & { + getState: () => GetState; + } + >('ytmusic-player-bar')?.getState().queue.repeatMode, ); }); @@ -92,7 +98,10 @@ export default (api: YoutubePlayer) => { }); const playPausedHandler = (e: Event, status: string) => { - if (e.target instanceof HTMLVideoElement && Math.round(e.target.currentTime) > 0) { + if ( + e.target instanceof HTMLVideoElement && + Math.round(e.target.currentTime) > 0 + ) { window.ipcRenderer.send('playPaused', { isPaused: status === 'pause', elapsedSeconds: Math.floor(e.target.currentTime), @@ -108,7 +117,11 @@ export default (api: YoutubePlayer) => { const waitingEvent = new Set(); // Name = "dataloaded" and abit later "dataupdated" api.addEventListener('videodatachange', (name: string, videoData) => { - document.dispatchEvent(new CustomEvent('videodatachange', { detail: { name, videoData } })); + document.dispatchEvent( + new CustomEvent('videodatachange', { + detail: { name, videoData }, + }), + ); if (name === 'dataupdated' && waitingEvent.has(videoData.videoId)) { waitingEvent.delete(videoData.videoId); @@ -117,7 +130,8 @@ export default (api: YoutubePlayer) => { const video = $('video'); video?.dispatchEvent(srcChangedEvent); - for (const status of ['playing', 'pause'] as const) { // for fix issue that pause event not fired + for (const status of ['playing', 'pause'] as const) { + // for fix issue that pause event not fired video?.addEventListener(status, playPausedHandlers[status]); } @@ -133,13 +147,17 @@ export default (api: YoutubePlayer) => { function sendSongInfo(videoData: VideoDataChangeValue) { const data = api.getPlayerResponse(); - data.videoDetails.album = videoData?.Hd?.playerOverlays?.playerOverlayRenderer?.browserMediaSession?.browserMediaSessionRenderer?.album.runs?.at(0)?.text; + data.videoDetails.album = + videoData?.Hd?.playerOverlays?.playerOverlayRenderer?.browserMediaSession?.browserMediaSessionRenderer?.album.runs?.at( + 0, + )?.text; data.videoDetails.elapsedSeconds = 0; data.videoDetails.isPaused = false; // HACK: This is a workaround for "podcast" type video. GREAT JOB GOOGLE. if (data.playabilityStatus.transportControlsConfig) { - data.videoDetails.author = data.microformat.microformatDataRenderer.pageOwnerDetails.name; + data.videoDetails.author = + data.microformat.microformatDataRenderer.pageOwnerDetails.name; } window.ipcRenderer.send('video-src-changed', data); diff --git a/src/providers/song-info.ts b/src/providers/song-info.ts index 51db27dc..777db821 100644 --- a/src/providers/song-info.ts +++ b/src/providers/song-info.ts @@ -42,11 +42,11 @@ export const songInfo: SongInfo = { // Grab the native image using the src export const getImage = cache( async (src: string): Promise => { - const result = await net.fetch(src); const buffer = await result.arrayBuffer(); const output = nativeImage.createFromBuffer(Buffer.from(buffer)); - if (output.isEmpty() && !src.endsWith('.jpg') && src.includes('.jpg')) { // Fix hidden webp files (https://github.com/th-ch/youtube-music/issues/315) + if (output.isEmpty() && !src.endsWith('.jpg') && src.includes('.jpg')) { + // Fix hidden webp files (https://github.com/th-ch/youtube-music/issues/315) return getImage(src.slice(0, src.lastIndexOf('.jpg') + 4)); } @@ -54,7 +54,10 @@ export const getImage = cache( }, ); -const handleData = async (data: GetPlayerResponse, win: Electron.BrowserWindow) => { +const handleData = async ( + data: GetPlayerResponse, + win: Electron.BrowserWindow, +) => { if (!data) { return; } @@ -63,7 +66,8 @@ const handleData = async (data: GetPlayerResponse, win: Electron.BrowserWindow) if (microformat) { songInfo.uploadDate = microformat.uploadDate; songInfo.url = microformat.urlCanonical?.split('&')[0]; - songInfo.playlistId = new URL(microformat.urlCanonical).searchParams.get('list') ?? ''; + songInfo.playlistId = + new URL(microformat.urlCanonical).searchParams.get('list') ?? ''; // Used for options.resumeOnStart config.set('url', microformat.urlCanonical); } @@ -108,17 +112,26 @@ const registerProvider = (win: BrowserWindow) => { c(songInfo, 'video-src-changed'); } }); - ipcMain.on('playPaused', (_, { isPaused, elapsedSeconds }: { isPaused: boolean, elapsedSeconds: number }) => { - songInfo.isPaused = isPaused; - songInfo.elapsedSeconds = elapsedSeconds; - if (handlingData) { - return; - } + ipcMain.on( + 'playPaused', + ( + _, + { + isPaused, + elapsedSeconds, + }: { isPaused: boolean; elapsedSeconds: number }, + ) => { + songInfo.isPaused = isPaused; + songInfo.elapsedSeconds = elapsedSeconds; + if (handlingData) { + return; + } - for (const c of callbacks) { - c(songInfo, 'playPaused'); - } - }); + for (const c of callbacks) { + c(songInfo, 'playPaused'); + } + }, + ); }; const suffixesToRemove = [ @@ -133,7 +146,10 @@ export function cleanupName(name: string): string { return name; } - name = name.replace(/\((?:official)? ?(?:music)? ?(?:lyrics?)? ?(?:video)?\)$/i, ''); + name = name.replace( + /\((?:official)? ?(?:music)? ?(?:lyrics?)? ?(?:video)?\)$/i, + '', + ); const lowCaseName = name.toLowerCase(); for (const suffix of suffixesToRemove) { if (lowCaseName.endsWith(suffix)) { diff --git a/src/renderer.ts b/src/renderer.ts index 6d29118a..be081875 100644 --- a/src/renderer.ts +++ b/src/renderer.ts @@ -18,7 +18,9 @@ let isApiLoaded = false; let firstDataLoaded = false; const observer = new MutationObserver(() => { - const playerApi = document.querySelector('#movie_player'); + const playerApi = document.querySelector( + '#movie_player', + ); if (playerApi) { observer.disconnect(); @@ -70,14 +72,22 @@ async function onApiLoaded() { const audioSource = audioContext.createMediaElementSource(video); audioSource.connect(audioContext.destination); - for await (const [id, plugin] of Object.entries(getAllLoadedRendererPlugins())) { + for await (const [id, plugin] of Object.entries( + getAllLoadedRendererPlugins(), + )) { if (typeof plugin.renderer !== 'function') { - await plugin.renderer?.onPlayerApiReady?.call(plugin.renderer, api!, createContext(id)); + await plugin.renderer?.onPlayerApiReady?.call( + plugin.renderer, + api!, + createContext(id), + ); } } if (firstDataLoaded) { - document.dispatchEvent(new CustomEvent('videodatachange', { detail: { name: 'dataloaded' } })); + document.dispatchEvent( + new CustomEvent('videodatachange', { detail: { name: 'dataloaded' } }), + ); } const audioCanPlayEventDispatcher = () => { @@ -93,22 +103,16 @@ async function onApiLoaded() { const loadstartListener = () => { // Emit "audioCanPlay" for each video - video.addEventListener( - 'canplaythrough', - audioCanPlayEventDispatcher, - { once: true }, - ); + video.addEventListener('canplaythrough', audioCanPlayEventDispatcher, { + once: true, + }); }; if (video.readyState === 4 /* HAVE_ENOUGH_DATA (loaded) */) { audioCanPlayEventDispatcher(); } - video.addEventListener( - 'loadstart', - loadstartListener, - { passive: true }, - ); + video.addEventListener('loadstart', loadstartListener, { passive: true }); window.ipcRenderer.send('ytmd:player-api-loaded'); @@ -151,24 +155,22 @@ async function onApiLoaded() { await loadAllRendererPlugins(); isPluginLoaded = true; - window.ipcRenderer.on( - 'plugin:unload', - async (_event, id: string) => { - await forceUnloadRendererPlugin(id); - }, - ); - window.ipcRenderer.on( - 'plugin:enable', - async (_event, id: string) => { - await forceLoadRendererPlugin(id); - if (api) { - const plugin = getLoadedRendererPlugin(id); - if (plugin && typeof plugin.renderer !== 'function') { - await plugin.renderer?.onPlayerApiReady?.call(plugin.renderer, api, createContext(id)); - } + window.ipcRenderer.on('plugin:unload', async (_event, id: string) => { + await forceUnloadRendererPlugin(id); + }); + window.ipcRenderer.on('plugin:enable', async (_event, id: string) => { + await forceLoadRendererPlugin(id); + if (api) { + const plugin = getLoadedRendererPlugin(id); + if (plugin && typeof plugin.renderer !== 'function') { + await plugin.renderer?.onPlayerApiReady?.call( + plugin.renderer, + api, + createContext(id), + ); } - }, - ); + } + }); window.ipcRenderer.on( 'config-changed', diff --git a/src/reset.d.ts b/src/reset.d.ts index b0e369b3..7c75d820 100644 --- a/src/reset.d.ts +++ b/src/reset.d.ts @@ -13,8 +13,8 @@ declare global { } interface DocumentEventMap { - 'audioCanPlay': CustomEvent; - 'videodatachange': CustomEvent; + audioCanPlay: CustomEvent; + videodatachange: CustomEvent; } interface Window { @@ -33,7 +33,7 @@ declare global { } } - // import { Howl as _Howl } from 'howler'; +// import { Howl as _Howl } from 'howler'; declare module 'howler' { interface Howl { _sounds: { diff --git a/src/tray.ts b/src/tray.ts index ced00e7d..a6d44b41 100644 --- a/src/tray.ts +++ b/src/tray.ts @@ -12,7 +12,10 @@ import type { MenuTemplate } from './menu'; // Prevent tray being garbage collected let tray: Electron.Tray | undefined; -type TrayEvent = (event: Electron.KeyboardEvent, bounds: Electron.Rectangle) => void; +type TrayEvent = ( + event: Electron.KeyboardEvent, + bounds: Electron.Rectangle, +) => void; export const setTrayOnClick = (fn: TrayEvent) => { if (!tray) { diff --git a/src/types/contexts.ts b/src/types/contexts.ts index e9b48f7a..60e15fda 100644 --- a/src/types/contexts.ts +++ b/src/types/contexts.ts @@ -1,4 +1,9 @@ -import type { IpcMain, IpcRenderer, WebContents, BrowserWindow } from 'electron'; +import type { + IpcMain, + IpcRenderer, + WebContents, + BrowserWindow, +} from 'electron'; import type { PluginConfig } from '@/types/plugins'; export interface BaseContext { @@ -6,7 +11,8 @@ export interface BaseContext { setConfig(conf: Partial>): Promise | void; } -export interface BackendContext extends BaseContext { +export interface BackendContext + extends BaseContext { ipc: { send: WebContents['send']; handle: (event: string, listener: CallableFunction) => void; @@ -17,14 +23,17 @@ export interface BackendContext extends BaseContext window: BrowserWindow; } -export interface MenuContext extends BaseContext { +export interface MenuContext + extends BaseContext { window: BrowserWindow; refresh: () => Promise | void; } -export interface PreloadContext extends BaseContext {} +export interface PreloadContext + extends BaseContext {} -export interface RendererContext extends BaseContext { +export interface RendererContext + extends BaseContext { ipc: { send: IpcRenderer['send']; invoke: IpcRenderer['invoke']; diff --git a/src/types/datahost-get-state.ts b/src/types/datahost-get-state.ts index 3f796f05..4729dc0b 100644 --- a/src/types/datahost-get-state.ts +++ b/src/types/datahost-get-state.ts @@ -29,8 +29,7 @@ export interface Download { isLeaderTab: boolean; } -export interface Entities { -} +export interface Entities {} export interface LikeStatus { videos: Videos; diff --git a/src/types/get-player-response.ts b/src/types/get-player-response.ts index 54bd7d5a..b9250f3f 100644 --- a/src/types/get-player-response.ts +++ b/src/types/get-player-response.ts @@ -108,8 +108,7 @@ export interface Endpoint { watchEndpoint: WatchEndpoint; } -export interface CommandMetadata { -} +export interface CommandMetadata {} export interface WatchEndpoint { videoId: string; @@ -221,7 +220,7 @@ export interface PlayabilityStatus { } type ReplaceDefaultType = { - replaceDefault: boolean, + replaceDefault: boolean; }; export interface TransportControlsConfig { diff --git a/src/types/player-api-events.ts b/src/types/player-api-events.ts index afca99ae..533a552a 100644 --- a/src/types/player-api-events.ts +++ b/src/types/player-api-events.ts @@ -242,21 +242,21 @@ export interface FlagEndpoint { } export type VideoDataChangeValue = Record & { - videoId: string - title: string - author: string + videoId: string; + title: string; + author: string; - Hd?: AlbumDetails + Hd?: AlbumDetails; - playlistId: string - isUpcoming: boolean - loading: boolean + playlistId: string; + isUpcoming: boolean; + loading: boolean; - lengthSeconds: number + lengthSeconds: number; }; export interface PlayerAPIEvents { videodatachange: { - value: VideoDataChangeValue - } & ({ name: 'dataloaded' } | { name: 'dataupdated ' }) + value: VideoDataChangeValue; + } & ({ name: 'dataloaded' } | { name: 'dataupdated ' }); } diff --git a/src/types/plugins.ts b/src/types/plugins.ts index ba8e600b..56331728 100644 --- a/src/types/plugins.ts +++ b/src/types/plugins.ts @@ -13,18 +13,30 @@ export type PluginConfig = { enabled: boolean; }; -export type PluginLifecycleSimple = (this: This, ctx: Context) => void | Promise; +export type PluginLifecycleSimple = ( + this: This, + ctx: Context, +) => void | Promise; export type PluginLifecycleExtra = This & { start?: PluginLifecycleSimple; stop?: PluginLifecycleSimple; onConfigChange?: (this: This, newConfig: Config) => void | Promise; }; -export type RendererPluginLifecycleExtra = This & PluginLifecycleExtra & { - onPlayerApiReady?: (this: This, playerApi: YoutubePlayer, context: Context) => void | Promise; -} +export type RendererPluginLifecycleExtra = This & + PluginLifecycleExtra & { + onPlayerApiReady?: ( + this: This, + playerApi: YoutubePlayer, + context: Context, + ) => void | Promise; + }; -export type PluginLifecycle = PluginLifecycleSimple | PluginLifecycleExtra; -export type RendererPluginLifecycle = PluginLifecycleSimple | RendererPluginLifecycleExtra; +export type PluginLifecycle = + | PluginLifecycleSimple + | PluginLifecycleExtra; +export type RendererPluginLifecycle = + | PluginLifecycleSimple + | RendererPluginLifecycleExtra; export interface PluginDef< BackendProperties, @@ -37,17 +49,25 @@ export interface PluginDef< description?: string; config?: Config; - menu?: (ctx: MenuContext) => Promise | Electron.MenuItemConstructorOptions[]; + menu?: ( + ctx: MenuContext, + ) => + | Promise + | Electron.MenuItemConstructorOptions[]; stylesheets?: string[]; restartNeeded?: boolean; backend?: { - [Key in keyof BackendProperties]: BackendProperties[Key] + [Key in keyof BackendProperties]: BackendProperties[Key]; } & PluginLifecycle, BackendProperties>; preload?: { - [Key in keyof PreloadProperties]: PreloadProperties[Key] + [Key in keyof PreloadProperties]: PreloadProperties[Key]; } & PluginLifecycle, PreloadProperties>; renderer?: { - [Key in keyof RendererProperties]: RendererProperties[Key] - } & RendererPluginLifecycle, RendererProperties>; + [Key in keyof RendererProperties]: RendererProperties[Key]; + } & RendererPluginLifecycle< + Config, + RendererContext, + RendererProperties + >; } diff --git a/src/types/youtube-player.ts b/src/types/youtube-player.ts index c5ee1173..47585fdd 100644 --- a/src/types/youtube-player.ts +++ b/src/types/youtube-player.ts @@ -5,97 +5,263 @@ import { GetPlayerResponse } from './get-player-response'; import { PlayerAPIEvents } from './player-api-events'; export interface YoutubePlayer { - getInternalApiInterface: (...params: Parameters) => Return; - getApiInterface: (...params: Parameters) => Return; + getInternalApiInterface: ( + ...params: Parameters + ) => Return; + getApiInterface: ( + ...params: Parameters + ) => Return; cueVideoByPlayerVars: () => void; loadVideoByPlayerVars: () => void; - preloadVideoByPlayerVars: (...params: Parameters) => Return; - getAdState: (...params: Parameters) => Return; - sendAbandonmentPing: (...params: Parameters) => Return; - setLoopRange: (...params: Parameters) => Return; - getLoopRange: (...params: Parameters) => Return; - setAutonavState: (...params: Parameters) => Return; - seekToLiveHead: (...params: Parameters) => Return; - requestSeekToWallTimeSeconds: (...params: Parameters) => Return; - seekToStreamTime: (...params: Parameters) => Return; - startSeekCsiAction: (...params: Parameters) => Return; - getStreamTimeOffset: (...params: Parameters) => Return; + preloadVideoByPlayerVars: ( + ...params: Parameters + ) => Return; + getAdState: ( + ...params: Parameters + ) => Return; + sendAbandonmentPing: ( + ...params: Parameters + ) => Return; + setLoopRange: ( + ...params: Parameters + ) => Return; + getLoopRange: ( + ...params: Parameters + ) => Return; + setAutonavState: ( + ...params: Parameters + ) => Return; + seekToLiveHead: ( + ...params: Parameters + ) => Return; + requestSeekToWallTimeSeconds: ( + ...params: Parameters + ) => Return; + seekToStreamTime: ( + ...params: Parameters + ) => Return; + startSeekCsiAction: ( + ...params: Parameters + ) => Return; + getStreamTimeOffset: ( + ...params: Parameters + ) => Return; getVideoData: () => VideoDetails; - setInlinePreview: (...params: Parameters) => Return; - updateDownloadState: (...params: Parameters) => Return; - queueOfflineAction: (...params: Parameters) => Return; - pauseVideoDownload: (...params: Parameters) => Return; - resumeVideoDownload: (...params: Parameters) => Return; - refreshAllStaleEntities: (...params: Parameters) => Return; - isOrchestrationLeader: (...params: Parameters) => Return; - getAppState: (...params: Parameters) => Return; - updateLastActiveTime: (...params: Parameters) => Return; - setBlackout: (...params: Parameters) => Return; - setUserEngagement: (...params: Parameters) => Return; - updateSubtitlesUserSettings: (...params: Parameters) => Return; - getPresentingPlayerType: (...params: Parameters) => Return; - canPlayType: (...params: Parameters) => Return; - updatePlaylist: (...params: Parameters) => Return; - updateVideoData: (...params: Parameters) => Return; - updateEnvironmentData: (...params: Parameters) => Return; - sendVideoStatsEngageEvent: (...params: Parameters) => Return; - productsInVideoVisibilityUpdated: (...params: Parameters) => Return; - setSafetyMode: (...params: Parameters) => Return; - isAtLiveHead: (...params: Parameters) => Return; - getVideoAspectRatio: (...params: Parameters) => Return; - getPreferredQuality: (...params: Parameters) => Return; - getPlaybackQualityLabel: (...params: Parameters) => Return; + setInlinePreview: ( + ...params: Parameters + ) => Return; + updateDownloadState: ( + ...params: Parameters + ) => Return; + queueOfflineAction: ( + ...params: Parameters + ) => Return; + pauseVideoDownload: ( + ...params: Parameters + ) => Return; + resumeVideoDownload: ( + ...params: Parameters + ) => Return; + refreshAllStaleEntities: ( + ...params: Parameters + ) => Return; + isOrchestrationLeader: ( + ...params: Parameters + ) => Return; + getAppState: ( + ...params: Parameters + ) => Return; + updateLastActiveTime: ( + ...params: Parameters + ) => Return; + setBlackout: ( + ...params: Parameters + ) => Return; + setUserEngagement: ( + ...params: Parameters + ) => Return; + updateSubtitlesUserSettings: ( + ...params: Parameters + ) => Return; + getPresentingPlayerType: ( + ...params: Parameters + ) => Return; + canPlayType: ( + ...params: Parameters + ) => Return; + updatePlaylist: ( + ...params: Parameters + ) => Return; + updateVideoData: ( + ...params: Parameters + ) => Return; + updateEnvironmentData: ( + ...params: Parameters + ) => Return; + sendVideoStatsEngageEvent: ( + ...params: Parameters + ) => Return; + productsInVideoVisibilityUpdated: ( + ...params: Parameters + ) => Return; + setSafetyMode: ( + ...params: Parameters + ) => Return; + isAtLiveHead: ( + ...params: Parameters + ) => Return; + getVideoAspectRatio: ( + ...params: Parameters + ) => Return; + getPreferredQuality: ( + ...params: Parameters + ) => Return; + getPlaybackQualityLabel: ( + ...params: Parameters + ) => Return; setPlaybackQualityRange: (quality: string) => void; - onAdUxClicked: (...params: Parameters) => Return; - getFeedbackProductData: (...params: Parameters) => Return; - getStoryboardFrame: (...params: Parameters) => Return; - getStoryboardFrameIndex: (...params: Parameters) => Return; - getStoryboardLevel: (...params: Parameters) => Return; - getNumberOfStoryboardLevels: (...params: Parameters) => Return; - getCaptionWindowContainerId: (...params: Parameters) => Return; + onAdUxClicked: ( + ...params: Parameters + ) => Return; + getFeedbackProductData: ( + ...params: Parameters + ) => Return; + getStoryboardFrame: ( + ...params: Parameters + ) => Return; + getStoryboardFrameIndex: ( + ...params: Parameters + ) => Return; + getStoryboardLevel: ( + ...params: Parameters + ) => Return; + getNumberOfStoryboardLevels: ( + ...params: Parameters + ) => Return; + getCaptionWindowContainerId: ( + ...params: Parameters + ) => Return; getAvailableQualityLabels: () => string[]; - addUtcCueRange: (...params: Parameters) => Return; - showAirplayPicker: (...params: Parameters) => Return; - dispatchReduxAction: (...params: Parameters) => Return; + addUtcCueRange: ( + ...params: Parameters + ) => Return; + showAirplayPicker: ( + ...params: Parameters + ) => Return; + dispatchReduxAction: ( + ...params: Parameters + ) => Return; getPlayerResponse: () => GetPlayerResponse; - getHeartbeatResponse: (...params: Parameters) => Return; - changeMarkerVisibility: (...params: Parameters) => Return; - setAutonav: (...params: Parameters) => Return; - isNotServable: (...params: Parameters) => Return; - channelSubscribed: (...params: Parameters) => Return; - channelUnsubscribed: (...params: Parameters) => Return; - togglePictureInPicture: (...params: Parameters) => Return; + getHeartbeatResponse: ( + ...params: Parameters + ) => Return; + changeMarkerVisibility: ( + ...params: Parameters + ) => Return; + setAutonav: ( + ...params: Parameters + ) => Return; + isNotServable: ( + ...params: Parameters + ) => Return; + channelSubscribed: ( + ...params: Parameters + ) => Return; + channelUnsubscribed: ( + ...params: Parameters + ) => Return; + togglePictureInPicture: ( + ...params: Parameters + ) => Return; supportsGaplessAudio: () => boolean; supportsGaplessShorts: () => boolean; - enqueueVideoByPlayerVars: (...params: Parameters) => Return; - clearQueue: (...params: Parameters) => Return; - getAudioTrack: (...params: Parameters) => Return; - setAudioTrack: (...params: Parameters) => Return; - getAvailableAudioTracks: (...params: Parameters) => Return; - getMaxPlaybackQuality: (...params: Parameters) => Return; - getUserPlaybackQualityPreference: (...params: Parameters) => Return; - getSubtitlesUserSettings: (...params: Parameters) => Return; - resetSubtitlesUserSettings: (...params: Parameters) => Return; - setMinimized: (...params: Parameters) => Return; - setOverlayVisibility: (...params: Parameters) => Return; - confirmYpcRental: (...params: Parameters) => Return; - toggleSubtitlesOn: (...params: Parameters) => Return; - isSubtitlesOn: (...params: Parameters) => Return; - queueNextVideo: (...params: Parameters) => Return; - handleExternalCall: (...params: Parameters) => Return; - logApiCall: (...params: Parameters) => Return; - isExternalMethodAvailable: (...params: Parameters) => Return; - setScreenLayer: (...params: Parameters) => Return; - getCurrentPlaylistSequence: (...params: Parameters) => Return; - getPlaylistSequenceForTime: (...params: Parameters) => Return; - shouldSendVisibilityState: (...params: Parameters) => Return; - syncVolume: (...params: Parameters) => Return; - highlightSettingsMenuItem: (...params: Parameters) => Return; - openSettingsMenuItem: (...params: Parameters) => Return; - getVisibilityState: (...params: Parameters) => Return; - isMutedByMutedAutoplay: (...params: Parameters) => Return; - setGlobalCrop: (...params: Parameters) => Return; - setInternalSize: (...params: Parameters) => Return; + enqueueVideoByPlayerVars: ( + ...params: Parameters + ) => Return; + clearQueue: ( + ...params: Parameters + ) => Return; + getAudioTrack: ( + ...params: Parameters + ) => Return; + setAudioTrack: ( + ...params: Parameters + ) => Return; + getAvailableAudioTracks: ( + ...params: Parameters + ) => Return; + getMaxPlaybackQuality: ( + ...params: Parameters + ) => Return; + getUserPlaybackQualityPreference: ( + ...params: Parameters + ) => Return; + getSubtitlesUserSettings: ( + ...params: Parameters + ) => Return; + resetSubtitlesUserSettings: ( + ...params: Parameters + ) => Return; + setMinimized: ( + ...params: Parameters + ) => Return; + setOverlayVisibility: ( + ...params: Parameters + ) => Return; + confirmYpcRental: ( + ...params: Parameters + ) => Return; + toggleSubtitlesOn: ( + ...params: Parameters + ) => Return; + isSubtitlesOn: ( + ...params: Parameters + ) => Return; + queueNextVideo: ( + ...params: Parameters + ) => Return; + handleExternalCall: ( + ...params: Parameters + ) => Return; + logApiCall: ( + ...params: Parameters + ) => Return; + isExternalMethodAvailable: ( + ...params: Parameters + ) => Return; + setScreenLayer: ( + ...params: Parameters + ) => Return; + getCurrentPlaylistSequence: ( + ...params: Parameters + ) => Return; + getPlaylistSequenceForTime: ( + ...params: Parameters + ) => Return; + shouldSendVisibilityState: ( + ...params: Parameters + ) => Return; + syncVolume: ( + ...params: Parameters + ) => Return; + highlightSettingsMenuItem: ( + ...params: Parameters + ) => Return; + openSettingsMenuItem: ( + ...params: Parameters + ) => Return; + getVisibilityState: ( + ...params: Parameters + ) => Return; + isMutedByMutedAutoplay: ( + ...params: Parameters + ) => Return; + setGlobalCrop: ( + ...params: Parameters + ) => Return; + setInternalSize: ( + ...params: Parameters + ) => Return; seekBy: (seconds: number) => void; showControls: () => void; hideControls: () => void; @@ -120,8 +286,12 @@ export interface YoutubePlayer { wakeUpControls: () => void; cueVideoById: (videoId: string) => void; loadVideoById: (videoId: string) => void; - cueVideoByUrl: (...params: Parameters) => Return; - loadVideoByUrl: (...params: Parameters) => Return; + cueVideoByUrl: ( + ...params: Parameters + ) => Return; + loadVideoByUrl: ( + ...params: Parameters + ) => Return; /** * Note: This doesn't resume playback, it plays from the start. */ @@ -176,7 +346,7 @@ export interface YoutubePlayer { listener: ( this: Document, name: PlayerAPIEvents[K]['name'], - data: PlayerAPIEvents[K]['value'] + data: PlayerAPIEvents[K]['value'], ) => void, options?: boolean | AddEventListenerOptions | undefined, ) => void; @@ -185,15 +355,21 @@ export interface YoutubePlayer { listener: ( this: Document, name: PlayerAPIEvents[K]['name'], - data: PlayerAPIEvents[K]['value'] + data: PlayerAPIEvents[K]['value'], ) => void, options?: boolean | EventListenerOptions | undefined, ) => void; getDebugText: () => string; - addCueRange: (...params: Parameters) => Return; - removeCueRange: (...params: Parameters) => Return; + addCueRange: ( + ...params: Parameters + ) => Return; + removeCueRange: ( + ...params: Parameters + ) => Return; setSize: (size: { width: number; height: number }) => void; - destroy: (...params: Parameters) => Return; + destroy: ( + ...params: Parameters + ) => Return; getSphericalProperties: () => Return; setSphericalProperties: (param: Parameter) => void; mutedAutoplay: () => void; @@ -207,19 +383,42 @@ export interface YoutubePlayer { getVideoUrl: () => string; getMediaReferenceTime: () => number; getSize: () => { width: number; height: number }; - logImaAdEvent: (...params: Parameters) => Return; - preloadVideoById: (...params: Parameters) => Return; - setAccountLinkState: (...params: Parameters) => Return; - updateAccountLinkingConfig: (...params: Parameters) => Return; - getAvailableQualityData: (...params: Parameters) => Return; - setCompositeParam: (...params: Parameters) => Return; - getStatsForNerds: (...params: Parameters) => Return; + logImaAdEvent: ( + ...params: Parameters + ) => Return; + preloadVideoById: ( + ...params: Parameters + ) => Return; + setAccountLinkState: ( + ...params: Parameters + ) => Return; + updateAccountLinkingConfig: ( + ...params: Parameters + ) => Return; + getAvailableQualityData: ( + ...params: Parameters + ) => Return; + setCompositeParam: ( + ...params: Parameters + ) => Return; + getStatsForNerds: ( + ...params: Parameters + ) => Return; showVideoInfo: () => void; hideVideoInfo: () => void; isVideoInfoVisible: () => boolean; getPlaybackRate: () => number; setPlaybackRate: (playbackRate: number) => void; - updateFullerscreenEduButtonSubtleModeState: (...params: Parameters) => Return; - updateFullerscreenEduButtonVisibility: (...params: Parameters) => Return; - addEmbedsConversionTrackingParams: (...params: Parameters) => Return; + updateFullerscreenEduButtonSubtleModeState: < + Parameters extends unknown[], + Return, + >( + ...params: Parameters + ) => Return; + updateFullerscreenEduButtonVisibility: ( + ...params: Parameters + ) => Return; + addEmbedsConversionTrackingParams: ( + ...params: Parameters + ) => Return; } diff --git a/src/utils/index.ts b/src/utils/index.ts index e0ab6be5..1db92323 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -105,9 +105,8 @@ export const startPlugin = async ( ); console.log( - LoggerPrefix, `Executed ${id}::${options.ctx} in ${ - performance.now() - start - } ms`, + LoggerPrefix, + `Executed ${id}::${options.ctx} in ${performance.now() - start} ms`, ); return lifecycle ? true : null; @@ -126,7 +125,9 @@ export const stopPlugin = async ( if (!def || !def[options.ctx]) return false; if (typeof def[options.ctx] === 'function') return false; - const defCtx = def[options.ctx] as { stop: PluginLifecycleSimple } | undefined; + const defCtx = def[options.ctx] as + | { stop: PluginLifecycleSimple } + | undefined; if (!defCtx?.stop) return null; try { @@ -138,9 +139,8 @@ export const stopPlugin = async ( ); console.log( - LoggerPrefix, `Executed ${id}::${options.ctx} in ${ - performance.now() - start - } ms`, + LoggerPrefix, + `Executed ${id}::${options.ctx} in ${performance.now() - start} ms`, ); return true; diff --git a/src/youtube-music.d.ts b/src/youtube-music.d.ts index d0622920..8ee11c56 100644 --- a/src/youtube-music.d.ts +++ b/src/youtube-music.d.ts @@ -34,4 +34,3 @@ declare module '*.css?inline' { export default css; } -