From 7befd70e21421de9817166b82a79312ca0585f2b Mon Sep 17 00:00:00 2001 From: Kendall Garner <17521368+kgarner7@users.noreply.github.com> Date: Sat, 23 May 2026 05:09:22 +0000 Subject: [PATCH] Apply additional security recommendations (#2050) * enable sandbox * enable CSP (umami tentatively works?) and reduce amount of ipc APIs exposed * remove csp from index --- src/main/env.ts | 15 ++++ src/main/features/core/player/index.ts | 3 +- src/main/features/core/player/media-keys.ts | 2 +- src/main/features/core/remote/index.ts | 2 +- src/main/index.ts | 42 ++++++++--- src/main/utils.ts | 16 ---- src/preload/ipc.ts | 10 --- src/preload/local-settings.ts | 6 +- src/preload/mpris.ts | 26 +++---- src/preload/mpv-player.ts | 74 +++++++++---------- src/preload/remote.ts | 27 +++---- src/preload/utils.ts | 65 ++++++++-------- .../hooks/use-main-player-listener.tsx | 2 +- .../features/player/hooks/use-mpris.ts | 6 +- .../player/hooks/use-power-save-blocker.ts | 10 +-- .../features/remote/hooks/use-remote.tsx | 54 ++++++-------- .../general/application-settings.tsx | 4 +- src/renderer/index.html | 1 - src/renderer/update-available-dialog.tsx | 4 +- 19 files changed, 179 insertions(+), 190 deletions(-) create mode 100644 src/main/env.ts diff --git a/src/main/env.ts b/src/main/env.ts new file mode 100644 index 000000000..1a65e61a2 --- /dev/null +++ b/src/main/env.ts @@ -0,0 +1,15 @@ +export const disableAutoUpdates = () => { + return process.env['DISABLE_AUTO_UPDATES']; +}; + +export const isMacOS = () => { + return process.platform === 'darwin'; +}; + +export const isWindows = () => { + return process.platform === 'win32'; +}; + +export const isLinux = () => { + return process.platform === 'linux'; +}; diff --git a/src/main/features/core/player/index.ts b/src/main/features/core/player/index.ts index e8c8cd8e0..95e75d51a 100644 --- a/src/main/features/core/player/index.ts +++ b/src/main/features/core/player/index.ts @@ -7,9 +7,10 @@ import { pid } from 'node:process'; import process from 'process'; import { getMainWindow, sendToastToRenderer } from '../../../index'; -import { createLog, isMacOS, isWindows } from '../../../utils'; +import { createLog } from '../../../utils'; import { store } from '../settings'; +import { isMacOS, isWindows } from '/@/main/env'; import { PlayerData } from '/@/shared/types/domain-types'; declare module 'node-mpv'; diff --git a/src/main/features/core/player/media-keys.ts b/src/main/features/core/player/media-keys.ts index d56749401..ad1b82ebd 100644 --- a/src/main/features/core/player/media-keys.ts +++ b/src/main/features/core/player/media-keys.ts @@ -1,8 +1,8 @@ import { BrowserWindow, globalShortcut, systemPreferences } from 'electron'; -import { isLinux, isMacOS } from '../../../utils'; import { store } from '../settings'; +import { isLinux, isMacOS } from '/@/main/env'; import { PlayerType } from '/@/shared/types/types'; export const enableMediaKeys = (window: BrowserWindow | null) => { diff --git a/src/main/features/core/remote/index.ts b/src/main/features/core/remote/index.ts index 6e5a7aff1..124931297 100644 --- a/src/main/features/core/remote/index.ts +++ b/src/main/features/core/remote/index.ts @@ -9,8 +9,8 @@ import { deflate, gzip } from 'zlib'; import manifest from './manifest.json'; +import { isLinux } from '/@/main/env'; import { getMainWindow } from '/@/main/index'; -import { isLinux } from '/@/main/utils'; import { QueueSong } from '/@/shared/types/domain-types'; import { ClientEvent, ServerEvent } from '/@/shared/types/remote-types'; import { PlayerRepeat, PlayerStatus, SongState } from '/@/shared/types/types'; diff --git a/src/main/index.ts b/src/main/index.ts index faffc31a0..85d4c81eb 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -16,6 +16,7 @@ import { protocol, Rectangle, screen, + session, shell, Tray, } from 'electron'; @@ -33,16 +34,9 @@ import { store } from './features/core/settings'; import { canHandleVisualizerDisplayMedia } from './features/core/visualizer'; import MenuBuilder, { MenuPlaybackState } from './menu'; import './features'; -import { - autoUpdaterLogInterface, - createLog, - disableAutoUpdates, - hotkeyToElectronAccelerator, - isLinux, - isMacOS, - isWindows, -} from './utils'; +import { autoUpdaterLogInterface, createLog, hotkeyToElectronAccelerator } from './utils'; +import { disableAutoUpdates, isLinux, isMacOS, isWindows } from '/@/main/env'; import { PlayerRepeat, PlayerStatus, PlayerType, TitleTheme } from '/@/shared/types/types'; const ALPHA_UPDATER_CONFIG: { @@ -477,6 +471,15 @@ const createTray = () => { tray.setContextMenu(contextMenu); }; +const validateUrl = (url: string): boolean => { + // Minor security, really. Enforce only loading websites (http/https). file:// + // URLs and the like should've already been blocked, but this is another check. + // Note that arbitrary web URLs are still allowed under this scheme, although + // that should really only be hit by Subsonic share url (or if artist homepage + // is allowed for ND extensions) + return url.startsWith('http://') || url.startsWith('https://'); +}; + async function createWindow(first = true): Promise { if (isDevelopment) { await installExtensions().catch(console.log); @@ -518,7 +521,7 @@ async function createWindow(first = true): Promise { devTools: true, nodeIntegration: false, preload: join(__dirname, '../preload/index.js'), - sandbox: false, + sandbox: true, webSecurity: !store.get('ignore_cors'), }, width: 1440, @@ -730,7 +733,9 @@ async function createWindow(first = true): Promise { // Open URLs in the user's browser mainWindow.webContents.setWindowOpenHandler((edata) => { - shell.openExternal(edata.url); + if (validateUrl(edata.url)) { + shell.openExternal(edata.url); + } return { action: 'deny' }; }); @@ -770,7 +775,9 @@ async function createWindow(first = true): Promise { nativeTheme.themeSource = theme || 'dark'; mainWindow.webContents.setWindowOpenHandler((details) => { - shell.openExternal(details.url); + if (validateUrl(details.url)) { + shell.openExternal(details.url); + } return { action: 'deny' }; }); @@ -1017,6 +1024,17 @@ if (!singleInstance) { return response; }); + session.defaultSession.webRequest.onHeadersReceived((details, callback) => { + callback({ + responseHeaders: { + ...details.responseHeaders, + 'Content-Security-Policy': [ + "script-src 'self' 'unsafe-inline' https://umami.jeffvli.org; style-src 'self' 'unsafe-inline'; media-src 'self' http: https: data: blob:; img-src 'self' http: https: data: blob:; connect-src 'self' http: https: ws: wss:; default-src 'self';", + ], + }, + }); + }); + createWindow(); if (store.get('window_enable_tray', true)) { createTray(); diff --git a/src/main/utils.ts b/src/main/utils.ts index 3a189324c..a6e5ed909 100644 --- a/src/main/utils.ts +++ b/src/main/utils.ts @@ -18,22 +18,6 @@ if (process.env.NODE_ENV === 'development') { }; } -export const disableAutoUpdates = () => { - return process.env['DISABLE_AUTO_UPDATES']; -}; - -export const isMacOS = () => { - return process.platform === 'darwin'; -}; - -export const isWindows = () => { - return process.platform === 'win32'; -}; - -export const isLinux = () => { - return process.platform === 'linux'; -}; - export const hotkeyToElectronAccelerator = (hotkey: string) => { let accelerator = hotkey; diff --git a/src/preload/ipc.ts b/src/preload/ipc.ts index 56e2b2c55..55588df86 100644 --- a/src/preload/ipc.ts +++ b/src/preload/ipc.ts @@ -8,21 +8,11 @@ const send = (channel: string, ...args: any[]) => { ipcRenderer.send(channel, ...args); }; -const invoke = (channel: string, ...args: any[]) => { - return ipcRenderer.invoke(channel, ...args); -}; - -const on = (channel: string, listener: (event: any, ...args: any[]) => void) => { - ipcRenderer.on(channel, listener); -}; - const removeListener = (channel: string, listener: (event: any, ...args: any[]) => void) => { ipcRenderer.removeListener(channel, listener); }; export const ipc = { - invoke, - on, removeAllListeners, removeListener, send, diff --git a/src/preload/local-settings.ts b/src/preload/local-settings.ts index 6729d9a68..92aaca5eb 100644 --- a/src/preload/local-settings.ts +++ b/src/preload/local-settings.ts @@ -1,4 +1,4 @@ -import { ipcRenderer, IpcRendererEvent, OpenDialogOptions, webFrame } from 'electron'; +import { ipcRenderer, OpenDialogOptions, webFrame } from 'electron'; import { TitleTheme } from '/@/shared/types/types'; @@ -41,8 +41,8 @@ const setZoomFactor = (zoomFactor: number) => { webFrame.setZoomFactor(zoomFactor / 100); }; -const fontError = (cb: (event: IpcRendererEvent, file: string) => void) => { - ipcRenderer.on('custom-font-error', cb); +const fontError = (cb: (file: string) => void) => { + ipcRenderer.on('custom-font-error', (_, file) => cb(file)); }; const themeSet = (theme: TitleTheme): void => { diff --git a/src/preload/mpris.ts b/src/preload/mpris.ts index e1252888b..8e77bb9da 100644 --- a/src/preload/mpris.ts +++ b/src/preload/mpris.ts @@ -1,4 +1,4 @@ -import { ipcRenderer, IpcRendererEvent } from 'electron'; +import { ipcRenderer } from 'electron'; import { QueueSong } from '/@/shared/types/domain-types'; import { PlayerRepeat, PlayerStatus } from '/@/shared/types/types'; @@ -31,28 +31,24 @@ const updateSong = (song: QueueSong | undefined, imageUrl?: null | string) => { ipcRenderer.send('update-song', song, imageUrl); }; -const requestSeek = (cb: (event: IpcRendererEvent, data: { offset: number }) => void) => { - ipcRenderer.on('request-seek', cb); +const requestSeek = (cb: (data: { offset: number }) => void) => { + ipcRenderer.on('request-seek', (_, data) => cb(data)); }; -const requestPosition = (cb: (event: IpcRendererEvent, data: { position: number }) => void) => { - ipcRenderer.on('request-position', cb); +const requestPosition = (cb: (data: { position: number }) => void) => { + ipcRenderer.on('request-position', (_, data) => cb(data)); }; -const requestToggleRepeat = ( - cb: (event: IpcRendererEvent, data: { repeat: PlayerRepeat }) => void, -) => { - ipcRenderer.on('mpris-request-toggle-repeat', cb); +const requestToggleRepeat = (cb: (data: { repeat: PlayerRepeat }) => void) => { + ipcRenderer.on('mpris-request-toggle-repeat', (_, data) => cb(data)); }; -const requestToggleShuffle = ( - cb: (event: IpcRendererEvent, data: { shuffle: boolean }) => void, -) => { - ipcRenderer.on('mpris-request-toggle-shuffle', cb); +const requestToggleShuffle = (cb: (data: { shuffle: boolean }) => void) => { + ipcRenderer.on('mpris-request-toggle-shuffle', (_, data) => cb(data)); }; -const requestVolume = (cb: (event: IpcRendererEvent, data: { volume: number }) => void) => { - ipcRenderer.on('request-volume', cb); +const requestVolume = (cb: (data: { volume: number }) => void) => { + ipcRenderer.on('request-volume', (_, data) => cb(data)); }; export const mpris = { diff --git a/src/preload/mpv-player.ts b/src/preload/mpv-player.ts index daa450b02..f5a4f52b0 100644 --- a/src/preload/mpv-player.ts +++ b/src/preload/mpv-player.ts @@ -1,4 +1,4 @@ -import { ipcRenderer, IpcRendererEvent } from 'electron'; +import { ipcRenderer } from 'electron'; import { PlayerData } from '/@/shared/types/domain-types'; @@ -102,76 +102,76 @@ const getAudioDevices = async () => { return ipcRenderer.invoke('player-get-audio-devices'); }; -const rendererAutoNext = (cb: (event: IpcRendererEvent, data: PlayerData) => void) => { - ipcRenderer.on('renderer-player-auto-next', cb); +const rendererAutoNext = (cb: (data: PlayerData) => void) => { + ipcRenderer.on('renderer-player-auto-next', (_, data) => cb(data)); }; -const rendererCurrentTime = (cb: (event: IpcRendererEvent, data: number) => void) => { - ipcRenderer.on('renderer-player-current-time', cb); +const rendererCurrentTime = (cb: (data: number) => void) => { + ipcRenderer.on('renderer-player-current-time', (_, data) => cb(data)); }; -const rendererNext = (cb: (event: IpcRendererEvent, data: PlayerData) => void) => { - ipcRenderer.on('renderer-player-next', cb); +const rendererNext = (cb: (data: PlayerData) => void) => { + ipcRenderer.on('renderer-player-next', (_, data) => cb(data)); }; -const rendererPause = (cb: (event: IpcRendererEvent, data: PlayerData) => void) => { - ipcRenderer.on('renderer-player-pause', cb); +const rendererPause = (cb: (data: PlayerData) => void) => { + ipcRenderer.on('renderer-player-pause', (_, data) => cb(data)); }; -const rendererPlay = (cb: (event: IpcRendererEvent, data: PlayerData) => void) => { - ipcRenderer.on('renderer-player-play', cb); +const rendererPlay = (cb: (data: PlayerData) => void) => { + ipcRenderer.on('renderer-player-play', (_, data) => cb(data)); }; -const rendererPlayPause = (cb: (event: IpcRendererEvent, data: PlayerData) => void) => { - ipcRenderer.on('renderer-player-play-pause', cb); +const rendererPlayPause = (cb: (data: PlayerData) => void) => { + ipcRenderer.on('renderer-player-play-pause', (_, data) => cb(data)); }; -const rendererPrevious = (cb: (event: IpcRendererEvent, data: PlayerData) => void) => { - ipcRenderer.on('renderer-player-previous', cb); +const rendererPrevious = (cb: (data: PlayerData) => void) => { + ipcRenderer.on('renderer-player-previous', (_, data) => cb(data)); }; -const rendererStop = (cb: (event: IpcRendererEvent, data: PlayerData) => void) => { - ipcRenderer.on('renderer-player-stop', cb); +const rendererStop = (cb: (data: PlayerData) => void) => { + ipcRenderer.on('renderer-player-stop', (_, data) => cb(data)); }; -const rendererSkipForward = (cb: (event: IpcRendererEvent, data: PlayerData) => void) => { - ipcRenderer.on('renderer-player-skip-forward', cb); +const rendererSkipForward = (cb: (data: PlayerData) => void) => { + ipcRenderer.on('renderer-player-skip-forward', (_, data) => cb(data)); }; -const rendererSkipBackward = (cb: (event: IpcRendererEvent, data: PlayerData) => void) => { - ipcRenderer.on('renderer-player-skip-backward', cb); +const rendererSkipBackward = (cb: (data: PlayerData) => void) => { + ipcRenderer.on('renderer-player-skip-backward', (_, data) => cb(data)); }; -const rendererVolumeUp = (cb: (event: IpcRendererEvent, data: PlayerData) => void) => { - ipcRenderer.on('renderer-player-volume-up', cb); +const rendererVolumeUp = (cb: (data: PlayerData) => void) => { + ipcRenderer.on('renderer-player-volume-up', (_, data) => cb(data)); }; -const rendererVolumeDown = (cb: (event: IpcRendererEvent, data: PlayerData) => void) => { - ipcRenderer.on('renderer-player-volume-down', cb); +const rendererVolumeDown = (cb: (data: PlayerData) => void) => { + ipcRenderer.on('renderer-player-volume-down', (_, data) => cb(data)); }; -const rendererVolumeMute = (cb: (event: IpcRendererEvent, data: PlayerData) => void) => { - ipcRenderer.on('renderer-player-volume-mute', cb); +const rendererVolumeMute = (cb: (data: PlayerData) => void) => { + ipcRenderer.on('renderer-player-volume-mute', (_, data) => cb(data)); }; -const rendererToggleRepeat = (cb: (event: IpcRendererEvent, data: PlayerData) => void) => { - ipcRenderer.on('renderer-player-toggle-repeat', cb); +const rendererToggleRepeat = (cb: (data: PlayerData) => void) => { + ipcRenderer.on('renderer-player-toggle-repeat', (_, data) => cb(data)); }; -const rendererToggleShuffle = (cb: (event: IpcRendererEvent, data: PlayerData) => void) => { - ipcRenderer.on('renderer-player-toggle-shuffle', cb); +const rendererToggleShuffle = (cb: (data: PlayerData) => void) => { + ipcRenderer.on('renderer-player-toggle-shuffle', (_, data) => cb(data)); }; -const rendererQuit = (cb: (event: IpcRendererEvent) => void) => { - ipcRenderer.on('renderer-player-quit', cb); +const rendererQuit = (cb: () => void) => { + ipcRenderer.on('renderer-player-quit', () => cb()); }; -const rendererError = (cb: (event: IpcRendererEvent, data: string) => void) => { - ipcRenderer.on('renderer-player-error', cb); +const rendererError = (cb: (data: string) => void) => { + ipcRenderer.on('renderer-player-error', (_, data) => cb(data)); }; -const rendererPlayerFallback = (cb: (event: IpcRendererEvent, data: boolean) => void) => { - ipcRenderer.on('renderer-player-fallback', cb); +const rendererPlayerFallback = (cb: (data: boolean) => void) => { + ipcRenderer.on('renderer-player-fallback', (_, data) => cb(data)); }; export const mpvPlayer = { diff --git a/src/preload/remote.ts b/src/preload/remote.ts index ebc6d8c6e..42dc7f521 100644 --- a/src/preload/remote.ts +++ b/src/preload/remote.ts @@ -1,33 +1,28 @@ -import { ipcRenderer, IpcRendererEvent } from 'electron'; +import { ipcRenderer } from 'electron'; import { QueueSong } from '/@/shared/types/domain-types'; import { PlayerStatus } from '/@/shared/types/types'; const requestFavorite = ( - cb: ( - event: IpcRendererEvent, - data: { favorite: boolean; id: string; serverId: string }, - ) => void, + cb: (data: { favorite: boolean; id: string; serverId: string }) => void, ) => { - ipcRenderer.on('request-favorite', cb); + ipcRenderer.on('request-favorite', (_, data) => cb(data)); }; -const requestPosition = (cb: (event: IpcRendererEvent, data: { position: number }) => void) => { - ipcRenderer.on('request-position', cb); +const requestPosition = (cb: (data: { position: number }) => void) => { + ipcRenderer.on('request-position', (_, data) => cb(data)); }; -const requestRating = ( - cb: (event: IpcRendererEvent, data: { id: string; rating: number; serverId: string }) => void, -) => { - ipcRenderer.on('request-rating', cb); +const requestRating = (cb: (data: { id: string; rating: number; serverId: string }) => void) => { + ipcRenderer.on('request-rating', (_, data) => cb(data)); }; -const requestSeek = (cb: (event: IpcRendererEvent, data: { offset: number }) => void) => { - ipcRenderer.on('request-seek', cb); +const requestSeek = (cb: (data: { offset: number }) => void) => { + ipcRenderer.on('request-seek', (_, data) => cb(data)); }; -const requestVolume = (cb: (event: IpcRendererEvent, data: { volume: number }) => void) => { - ipcRenderer.on('request-volume', cb); +const requestVolume = (cb: (data: { volume: number }) => void) => { + ipcRenderer.on('request-volume', (_, data) => cb(data)); }; const setRemoteEnabled = (enabled: boolean): Promise => { diff --git a/src/preload/utils.ts b/src/preload/utils.ts index 4ca9970af..3593dd58f 100644 --- a/src/preload/utils.ts +++ b/src/preload/utils.ts @@ -1,6 +1,6 @@ -import { ipcRenderer, IpcRendererEvent, webFrame } from 'electron'; +import { ipcRenderer, webFrame } from 'electron'; -import { disableAutoUpdates, isLinux, isMacOS, isWindows } from '../main/utils'; +import { disableAutoUpdates, isLinux, isMacOS, isWindows } from '../main/env'; const openItem = async (path: string) => { return ipcRenderer.invoke('open-item', path); @@ -10,29 +10,14 @@ const openApplicationDirectory = async () => { return ipcRenderer.invoke('open-application-directory'); }; -const playerErrorListener = (cb: (event: IpcRendererEvent, data: { code: number }) => void) => { - ipcRenderer.on('player-error-listener', cb); +const playerErrorListener = (cb: (data: { code: number }) => void) => { + ipcRenderer.on('player-error-listener', (_, data) => cb(data)); }; const mainMessageListener = ( - cb: ( - event: IpcRendererEvent, - data: { message: string; type: 'error' | 'info' | 'success' | 'warning' }, - ) => void, + cb: (data: { message: string; type: 'error' | 'info' | 'success' | 'warning' }) => void, ) => { - ipcRenderer.on('toast-from-main', cb); -}; - -const logger = ( - cb: ( - event: IpcRendererEvent, - data: { - message: string; - type: 'debug' | 'error' | 'info' | 'verbose' | 'warning'; - }, - ) => void, -) => { - ipcRenderer.send('logger', cb); + ipcRenderer.on('toast-from-main', (_, data) => cb(data)); }; const download = (url: string) => { @@ -43,6 +28,14 @@ const checkForUpdates = (): Promise<{ updateAvailable: boolean; version?: string return ipcRenderer.invoke('app-check-for-updates'); }; +const startPowerSaveBlocker = (full: boolean) => { + return ipcRenderer.invoke('power-save-blocker-start', { full }); +}; + +const stopPowerSaveBlocker = () => { + return ipcRenderer.invoke('power-save-blocker-stop'); +}; + const forceGarbageCollection = (): boolean => { try { if (typeof global.gc === 'function') { @@ -61,28 +54,32 @@ const forceGarbageCollection = (): boolean => { } }; -const rendererOpenSettings = (cb: (event: IpcRendererEvent) => void) => { - ipcRenderer.on('renderer-open-settings', cb); +const rendererOpenSettings = (cb: () => void) => { + ipcRenderer.on('renderer-open-settings', () => cb()); }; -const rendererOpenCommandPalette = (cb: (event: IpcRendererEvent) => void) => { - ipcRenderer.on('renderer-open-command-palette', cb); +const rendererOpenCommandPalette = (cb: () => void) => { + ipcRenderer.on('renderer-open-command-palette', () => cb()); }; -const rendererOpenManageServers = (cb: (event: IpcRendererEvent) => void) => { - ipcRenderer.on('renderer-open-manage-servers', cb); +const rendererOpenManageServers = (cb: () => void) => { + ipcRenderer.on('renderer-open-manage-servers', () => cb()); }; -const rendererTogglePrivateMode = (cb: (event: IpcRendererEvent) => void) => { +const rendererTogglePrivateMode = (cb: () => void) => { ipcRenderer.on('renderer-toggle-private-mode', cb); }; -const rendererToggleSidebar = (cb: (event: IpcRendererEvent) => void) => { - ipcRenderer.on('renderer-toggle-sidebar', cb); +const rendererToggleSidebar = (cb: () => void) => { + ipcRenderer.on('renderer-toggle-sidebar', () => cb()); }; -const rendererOpenReleaseNotes = (cb: (event: IpcRendererEvent) => void) => { - ipcRenderer.on('renderer-open-release-notes', cb); +const rendererOpenReleaseNotes = (cb: () => void) => { + ipcRenderer.on('renderer-open-release-notes', () => cb()); +}; + +const rendererUpdateAvailable = (cb: (version: string) => void) => { + ipcRenderer.on('update-available', (_, version) => cb(version)); }; export const utils = { @@ -93,7 +90,6 @@ export const utils = { isLinux, isMacOS, isWindows, - logger, mainMessageListener, openApplicationDirectory, openItem, @@ -104,6 +100,9 @@ export const utils = { rendererOpenSettings, rendererTogglePrivateMode, rendererToggleSidebar, + rendererUpdateAvailable, + startPowerSaveBlocker, + stopPowerSaveBlocker, }; export type Utils = typeof utils; diff --git a/src/renderer/features/player/audio-player/hooks/use-main-player-listener.tsx b/src/renderer/features/player/audio-player/hooks/use-main-player-listener.tsx index cd4ccfb9d..089ce238c 100644 --- a/src/renderer/features/player/audio-player/hooks/use-main-player-listener.tsx +++ b/src/renderer/features/player/audio-player/hooks/use-main-player-listener.tsx @@ -112,7 +112,7 @@ export const useMainPlayerListener = () => { decreaseVolume(volumeWheelStep); }); - mpvPlayerListener.rendererError((_event: any, message: string) => { + mpvPlayerListener.rendererError((message: string) => { handleMpvError(message); }); diff --git a/src/renderer/features/player/hooks/use-mpris.ts b/src/renderer/features/player/hooks/use-mpris.ts index af165da84..bdec1c386 100644 --- a/src/renderer/features/player/hooks/use-mpris.ts +++ b/src/renderer/features/player/hooks/use-mpris.ts @@ -117,11 +117,11 @@ export const useMPRIS = () => { return; } - mpris?.requestPosition((_e: unknown, data: { position: number }) => { + mpris?.requestPosition((data: { position: number }) => { player.mediaSeekToTimestamp(data.position); }); - mpris?.requestSeek((_e: unknown, data: { offset: number }) => { + mpris?.requestSeek((data: { offset: number }) => { player.mediaSkipForward(data.offset); }); @@ -133,7 +133,7 @@ export const useMPRIS = () => { player.toggleShuffle(); }); - mpris?.requestVolume((_e: unknown, data: { volume: number }) => { + mpris?.requestVolume((data: { volume: number }) => { player.setVolume(data.volume); }); diff --git a/src/renderer/features/player/hooks/use-power-save-blocker.ts b/src/renderer/features/player/hooks/use-power-save-blocker.ts index a7fa0a6fc..34c0e2184 100644 --- a/src/renderer/features/player/hooks/use-power-save-blocker.ts +++ b/src/renderer/features/player/hooks/use-power-save-blocker.ts @@ -4,27 +4,27 @@ import React, { useCallback, useEffect } from 'react'; import { usePlayerStatus, useSettingsStore, useWindowSettings } from '/@/renderer/store'; import { PlayerStatus } from '/@/shared/types/types'; -const ipc = isElectron() ? window.api.ipc : null; +const utils = isElectron() ? window.api.utils : null; export const usePowerSaveBlocker = () => { const status = usePlayerStatus(); const { preventSleepOnPlayback, preventSuspendOnPlayback } = useWindowSettings(); const startPowerSaveBlocker = useCallback(async () => { - if (!ipc) return; + if (!utils) return; try { - await ipc.invoke('power-save-blocker-start', { full: preventSleepOnPlayback }); + await utils.startPowerSaveBlocker(preventSleepOnPlayback); } catch (error) { console.error('Failed to start power save blocker:', error); } }, [preventSleepOnPlayback]); const stopPowerSaveBlocker = useCallback(async () => { - if (!ipc) return; + if (!utils) return; try { - await ipc.invoke('power-save-blocker-stop'); + await utils.stopPowerSaveBlocker(); } catch (error) { console.error('Failed to stop power save blocker:', error); } diff --git a/src/renderer/features/remote/hooks/use-remote.tsx b/src/renderer/features/remote/hooks/use-remote.tsx index 8e216f510..af8b1e459 100644 --- a/src/renderer/features/remote/hooks/use-remote.tsx +++ b/src/renderer/features/remote/hooks/use-remote.tsx @@ -64,7 +64,7 @@ export const useRemote = () => { return; } - remote.requestPosition((_e: unknown, data: { position: number }) => { + remote.requestPosition((data: { position: number }) => { logFn.debug(logMsg[LogCategory.REMOTE].requestPositionReceived, { category: LogCategory.REMOTE, meta: { position: data.position }, @@ -73,7 +73,7 @@ export const useRemote = () => { player.mediaSeekToTimestamp(newTime); }); - remote.requestSeek((_e: unknown, data: { offset: number }) => { + remote.requestSeek((data: { offset: number }) => { logFn.debug(logMsg[LogCategory.REMOTE].requestSeekReceived, { category: LogCategory.REMOTE, meta: { offset: data.offset }, @@ -81,17 +81,15 @@ export const useRemote = () => { mediaSkipForward(data.offset); }); - remote.requestRating( - (_e: unknown, data: { id: string; rating: number; serverId: string }) => { - logFn.debug(logMsg[LogCategory.REMOTE].requestRatingReceived, { - category: LogCategory.REMOTE, - meta: { id: data.id, rating: data.rating, serverId: data.serverId }, - }); - setRating(data.serverId, [data.id], LibraryItem.SONG, data.rating); - }, - ); + remote.requestRating((data: { id: string; rating: number; serverId: string }) => { + logFn.debug(logMsg[LogCategory.REMOTE].requestRatingReceived, { + category: LogCategory.REMOTE, + meta: { id: data.id, rating: data.rating, serverId: data.serverId }, + }); + setRating(data.serverId, [data.id], LibraryItem.SONG, data.rating); + }); - remote.requestVolume((_e: unknown, data: { volume: number }) => { + remote.requestVolume((data: { volume: number }) => { logFn.debug(logMsg[LogCategory.REMOTE].requestVolumeReceived, { category: LogCategory.REMOTE, meta: { volume: data.volume }, @@ -99,24 +97,20 @@ export const useRemote = () => { setVolume(data.volume); }); - remote.requestFavorite( - (_e: unknown, data: { favorite: boolean; id: string; serverId: string }) => { - logFn.debug(logMsg[LogCategory.REMOTE].requestFavoriteReceived, { - category: LogCategory.REMOTE, - meta: { favorite: data.favorite, id: data.id, serverId: data.serverId }, - }); - const mutator = data.favorite - ? addToFavoritesMutation - : removeFromFavoritesMutation; - mutator.mutate({ - apiClientProps: { serverId: data.serverId }, - query: { - id: [data.id], - type: LibraryItem.SONG, - }, - }); - }, - ); + remote.requestFavorite((data: { favorite: boolean; id: string; serverId: string }) => { + logFn.debug(logMsg[LogCategory.REMOTE].requestFavoriteReceived, { + category: LogCategory.REMOTE, + meta: { favorite: data.favorite, id: data.id, serverId: data.serverId }, + }); + const mutator = data.favorite ? addToFavoritesMutation : removeFromFavoritesMutation; + mutator.mutate({ + apiClientProps: { serverId: data.serverId }, + query: { + id: [data.id], + type: LibraryItem.SONG, + }, + }); + }); return () => { ipc?.removeAllListeners('request-position'); diff --git a/src/renderer/features/settings/components/general/application-settings.tsx b/src/renderer/features/settings/components/general/application-settings.tsx index 6ce39ea96..941b3690b 100644 --- a/src/renderer/features/settings/components/general/application-settings.tsx +++ b/src/renderer/features/settings/components/general/application-settings.tsx @@ -1,5 +1,3 @@ -import type { IpcRendererEvent } from 'electron'; - import { t } from 'i18next'; import isElectron from 'is-electron'; import { memo, useCallback, useEffect, useState } from 'react'; @@ -124,7 +122,7 @@ export const ApplicationSettings = memo(() => { // }, [fontSettings.custom]); const onFontError = useCallback( - (_: IpcRendererEvent, file: string) => { + (file: string) => { toast.error({ message: `${file} is not a valid font file`, }); diff --git a/src/renderer/index.html b/src/renderer/index.html index 590374018..f082b0663 100644 --- a/src/renderer/index.html +++ b/src/renderer/index.html @@ -2,7 +2,6 @@ - Feishin <% if (web) { %> diff --git a/src/renderer/update-available-dialog.tsx b/src/renderer/update-available-dialog.tsx index 7d8d3e3e3..1b4f0f2b4 100644 --- a/src/renderer/update-available-dialog.tsx +++ b/src/renderer/update-available-dialog.tsx @@ -21,14 +21,14 @@ export const UpdateAvailableDialog = () => { useEffect(() => { if (!isElectron()) return; - const handleUpdateAvailable = (_event: any, newVersion: string) => { + const handleUpdateAvailable = (newVersion: string) => { if (versionDismissed !== newVersion) { setVersion(newVersion); setOpened(true); } }; - window.api.ipc.on('update-available', handleUpdateAvailable); + window.api.utils.rendererUpdateAvailable(handleUpdateAvailable); return () => { window.api.ipc.removeListener?.('update-available', handleUpdateAvailable);