diff --git a/src/main/features/core/player/index.ts b/src/main/features/core/player/index.ts index b1843acc..1e218fdf 100644 --- a/src/main/features/core/player/index.ts +++ b/src/main/features/core/player/index.ts @@ -4,7 +4,7 @@ import uniq from 'lodash/uniq'; import MpvAPI from 'node-mpv'; import { getMainWindow, sendToastToRenderer } from '../../../main'; import { PlayerData } from '/@/renderer/store'; -import { isWindows } from '../../../utils'; +import { createLog, isWindows } from '../../../utils'; import { store } from '../settings'; declare module 'node-mpv'; @@ -19,6 +19,40 @@ declare module 'node-mpv'; let mpvInstance: MpvAPI | null = null; +const NodeMpvErrorCode = { + 0: 'Unable to load file or stream', + 1: 'Invalid argument', + 2: 'Binary not found', + 3: 'IPC command invalid', + 4: 'Unable to bind IPC socket', + 5: 'Connection timeout', + 6: 'MPV is already running', + 7: 'Could not send IPC message', + 8: 'MPV is not running', + 9: 'Unsupported protocol', +}; + +type NodeMpvError = { + errcode: number; + method: string; + stackTrace: string; + verbose: string; +}; + +const mpvLog = (action: string, err?: NodeMpvError) => { + if (err) { + const message = `[AUDIO PLAYER] ${action} - mpv errorcode ${err.errcode} - ${ + NodeMpvErrorCode[err.errcode as keyof typeof NodeMpvErrorCode] + }`; + + sendToastToRenderer({ message, type: 'error' }); + createLog({ message, type: 'error' }); + } + + const message = `[AUDIO PLAYER] ${action}`; + createLog({ message, type: 'error' }); +}; + const MPV_BINARY_PATH = store.get('mpv_path') as string | undefined; const isDevelopment = process.env.NODE_ENV === 'development' || process.env.DEBUG_PROD === 'true'; @@ -45,7 +79,6 @@ const createMpv = async (data: { const { extraParameters, properties } = data; const params = uniq([...DEFAULT_MPV_PARAMETERS(extraParameters), ...(extraParameters || [])]); - console.log('Setting mpv params: ', params); const extra = isDevelopment ? '-dev' : ''; @@ -62,15 +95,13 @@ const createMpv = async (data: { try { await mpv.start(); - } catch (error: { message: string; stack: any } | any) { - console.log('MPV failed to start', error); + } catch (error: any) { + console.log('mpv failed to start', error); } finally { - console.log('Setting MPV properties: ', properties); await mpv.setMultipleProperties(properties || {}); } - mpv.on('status', (status, ...rest) => { - console.log('MPV Event: status', status.property, status.value, rest); + mpv.on('status', (status) => { if (status.property === 'playlist-pos') { if (status.value === -1) { mpv?.stop(); @@ -84,19 +115,16 @@ const createMpv = async (data: { // Automatically updates the play button when the player is playing mpv.on('resumed', () => { - console.log('MPV Event: resumed'); getMainWindow()?.webContents.send('renderer-player-play'); }); // Automatically updates the play button when the player is stopped mpv.on('stopped', () => { - console.log('MPV Event: stopped'); getMainWindow()?.webContents.send('renderer-player-stop'); }); // Automatically updates the play button when the player is paused mpv.on('paused', () => { - console.log('MPV Event: paused'); getMainWindow()?.webContents.send('renderer-player-pause'); }); @@ -105,10 +133,6 @@ const createMpv = async (data: { getMainWindow()?.webContents.send('renderer-player-current-time', time); }); - mpv.on('quit', () => { - console.log('MPV Event: quit'); - }); - return mpv; }; @@ -117,27 +141,31 @@ export const getMpvInstance = () => { }; ipcMain.on('player-set-properties', async (_event, data: Record) => { + mpvLog(`Setting properties: ${JSON.stringify(data)}`); if (data.length === 0) { return; } - if (data.length === 1) { - getMpvInstance()?.setProperty(Object.keys(data)[0], Object.values(data)[0]); - } else { - getMpvInstance()?.setMultipleProperties(data); + try { + if (data.length === 1) { + getMpvInstance()?.setProperty(Object.keys(data)[0], Object.values(data)[0]); + } else { + getMpvInstance()?.setMultipleProperties(data); + } + } catch (err: NodeMpvError | any) { + mpvLog(`Failed to set properties: ${JSON.stringify(data)}`, err); } }); ipcMain.on( 'player-restart', async (_event, data: { extraParameters?: string[]; properties?: Record }) => { - console.log('Initializing MPV with data: ', data); mpvInstance?.quit(); try { + mpvLog(`Attempting to initialize mpv with parameters: ${JSON.stringify(data)}`); mpvInstance = await createMpv(data); - } catch (err) { - console.log('init error', err); - sendToastToRenderer({ message: 'Initialization error', type: 'error' }); + } catch (err: NodeMpvError | any) { + mpvLog('Failed to initialize mpv', err); } }, ); @@ -145,20 +173,23 @@ ipcMain.on( ipcMain.handle( 'player-initialize', async (_event, data: { extraParameters?: string[]; properties?: Record }) => { - console.log('Initializing MPV with data: ', data); try { + mpvLog(`Attempting to initialize mpv with parameters: ${JSON.stringify(data)}`); mpvInstance = await createMpv(data); - } catch (err) { - console.log('init error', err); - sendToastToRenderer({ message: 'Initialization error', type: 'error' }); + } catch (err: NodeMpvError | any) { + mpvLog('Failed to initialize mpv', err); } }, ); ipcMain.on('player-quit', async () => { - mpvInstance?.stop(); - mpvInstance?.quit(); - mpvInstance = null; + try { + mpvInstance?.stop(); + mpvInstance?.quit(); + mpvInstance = null; + } catch (err: NodeMpvError | any) { + mpvLog('Failed to quit mpv', err); + } }); ipcMain.handle('player-is-running', async () => { @@ -171,99 +202,93 @@ ipcMain.handle('player-clean-up', async () => { }); ipcMain.on('player-start', async () => { - await getMpvInstance() - ?.play() - .catch((err) => { - console.log('MPV failed to play', err); - }); + try { + await getMpvInstance()?.play(); + } catch (err: NodeMpvError | any) { + mpvLog('Failed to start mpv playback', err); + } }); // Starts the player ipcMain.on('player-play', async () => { - await getMpvInstance() - ?.play() - .catch((err) => { - console.log('MPV failed to play', err); - }); + try { + await getMpvInstance()?.play(); + } catch (err: NodeMpvError | any) { + mpvLog('Failed to start mpv playback', err); + } }); // Pauses the player ipcMain.on('player-pause', async () => { - await getMpvInstance() - ?.pause() - .catch((err) => { - console.log('MPV failed to pause', err); - }); + try { + await getMpvInstance()?.pause(); + } catch (err: NodeMpvError | any) { + mpvLog('Failed to pause mpv playback', err); + } }); // Stops the player ipcMain.on('player-stop', async () => { - await getMpvInstance() - ?.stop() - .catch((err) => { - console.log('MPV failed to stop', err); - }); + try { + await getMpvInstance()?.stop(); + } catch (err: NodeMpvError | any) { + mpvLog('Failed to stop mpv playback', err); + } }); // Goes to the next track in the playlist ipcMain.on('player-next', async () => { - await getMpvInstance() - ?.next() - .catch((err) => { - console.log('MPV failed to go to next', err); - }); + try { + await getMpvInstance()?.next(); + } catch (err: NodeMpvError | any) { + mpvLog('Failed to go to next track', err); + } }); // Goes to the previous track in the playlist ipcMain.on('player-previous', async () => { - await getMpvInstance() - ?.prev() - .catch((err) => { - console.log('MPV failed to go to previous', err); - }); + try { + await getMpvInstance()?.prev(); + } catch (err: NodeMpvError | any) { + mpvLog('Failed to go to previous track', err); + } }); // Seeks forward or backward by the given amount of seconds ipcMain.on('player-seek', async (_event, time: number) => { - await getMpvInstance() - ?.seek(time) - .catch((err) => { - console.log('MPV failed to seek', err); - }); + try { + await getMpvInstance()?.seek(time); + } catch (err: NodeMpvError | any) { + mpvLog(`Failed to seek by ${time} seconds`, err); + } }); // Seeks to the given time in seconds ipcMain.on('player-seek-to', async (_event, time: number) => { - await getMpvInstance() - ?.goToPosition(time) - .catch((err) => { - console.log(`MPV failed to seek to ${time}`, err); - }); + try { + await getMpvInstance()?.goToPosition(time); + } catch (err: NodeMpvError | any) { + mpvLog(`Failed to seek to ${time} seconds`, err); + } }); // Sets the queue in position 0 and 1 to the given data. Used when manually starting a song or using the next/prev buttons ipcMain.on('player-set-queue', async (_event, data: PlayerData, pause?: boolean) => { if (!data.queue.current && !data.queue.next) { - await getMpvInstance() - ?.clearPlaylist() - .catch((err) => { - console.log('MPV failed to clear playlist', err); - }); - - await getMpvInstance() - ?.pause() - .catch((err) => { - console.log('MPV failed to pause', err); - }); - return; + try { + await getMpvInstance()?.clearPlaylist(); + await getMpvInstance()?.pause(); + return; + } catch (err: NodeMpvError | any) { + mpvLog(`Failed to clear play queue`, err); + } } try { if (data.queue.current) { await getMpvInstance() ?.load(data.queue.current.streamUrl, 'replace') - .catch((err) => { - console.log('MPV failed to load song', err); + .catch(() => { getMpvInstance()?.play(); }); @@ -271,8 +296,8 @@ ipcMain.on('player-set-queue', async (_event, data: PlayerData, pause?: boolean) await getMpvInstance()?.load(data.queue.next.streamUrl, 'append'); } } - } catch (err) { - console.error(err); + } catch (err: NodeMpvError | any) { + mpvLog(`Failed to set play queue`, err); } if (pause) { @@ -282,30 +307,22 @@ ipcMain.on('player-set-queue', async (_event, data: PlayerData, pause?: boolean) // Replaces the queue in position 1 to the given data ipcMain.on('player-set-queue-next', async (_event, data: PlayerData) => { - const size = await getMpvInstance() - ?.getPlaylistSize() - .catch((err) => { - console.log('MPV failed to get playlist size', err); - }); + try { + const size = await getMpvInstance()?.getPlaylistSize(); - if (!size) { - return; - } + if (!size) { + return; + } - if (size > 1) { - await getMpvInstance() - ?.playlistRemove(1) - .catch((err) => { - console.log('MPV failed to remove song from playlist', err); - }); - } + if (size > 1) { + await getMpvInstance()?.playlistRemove(1); + } - if (data.queue.next) { - await getMpvInstance() - ?.load(data.queue.next.streamUrl, 'append') - .catch((err) => { - console.log('MPV failed to load next song', err); - }); + if (data.queue.next) { + await getMpvInstance()?.load(data.queue.next.streamUrl, 'append'); + } + } catch (err: NodeMpvError | any) { + mpvLog(`Failed to set play queue`, err); } }); @@ -314,38 +331,41 @@ ipcMain.on('player-auto-next', async (_event, data: PlayerData) => { // Always keep the current song as position 0 in the mpv queue // This allows us to easily set update the next song in the queue without // disturbing the currently playing song - await getMpvInstance() - ?.playlistRemove(0) - .catch((err) => { - console.log('MPV failed to remove song from playlist', err); - getMpvInstance()?.pause(); - }); - - if (data.queue.next) { + try { await getMpvInstance() - ?.load(data.queue.next.streamUrl, 'append') - .catch((err) => { - console.log('MPV failed to load next song', err); + ?.playlistRemove(0) + .catch(() => { + getMpvInstance()?.pause(); }); + + if (data.queue.next) { + await getMpvInstance()?.load(data.queue.next.streamUrl, 'append'); + } + } catch (err: NodeMpvError | any) { + mpvLog(`Failed to load next song`, err); } }); // Sets the volume to the given value (0-100) ipcMain.on('player-volume', async (_event, value: number) => { - await getMpvInstance() - ?.volume(value) - .catch((err) => { - console.log('MPV failed to set volume', err); - }); + try { + if (!value || value < 0 || value > 100) { + return; + } + + await getMpvInstance()?.volume(value); + } catch (err: NodeMpvError | any) { + mpvLog(`Failed to set volume to ${value}`, err); + } }); // Toggles the mute status ipcMain.on('player-mute', async (_event, mute: boolean) => { - await getMpvInstance() - ?.mute(mute) - .catch((err) => { - console.log('MPV failed to toggle mute', err); - }); + try { + await getMpvInstance()?.mute(mute); + } catch (err: NodeMpvError | any) { + mpvLog(`Failed to set mute status`, err); + } }); ipcMain.handle('player-get-time', async (): Promise => { diff --git a/src/main/main.ts b/src/main/main.ts index e02e430c..848fc3a8 100644 --- a/src/main/main.ts +++ b/src/main/main.ts @@ -38,6 +38,7 @@ import { isWindows, resolveHtmlPath, createLog, + autoUpdaterLogInterface, } from './utils'; import './features'; import type { TitleTheme } from '/@/renderer/types'; @@ -47,7 +48,7 @@ declare module 'node-mpv'; export default class AppUpdater { constructor() { log.transports.file.level = 'info'; - autoUpdater.logger = log; + autoUpdater.logger = autoUpdaterLogInterface; autoUpdater.checkForUpdatesAndNotify(); } } diff --git a/src/main/preload/mpv-player.ts b/src/main/preload/mpv-player.ts index 79d68561..d7d84f8d 100644 --- a/src/main/preload/mpv-player.ts +++ b/src/main/preload/mpv-player.ts @@ -18,7 +18,6 @@ const cleanup = () => { }; const setProperties = (data: Record) => { - console.log('Setting property :>>', data); ipcRenderer.send('player-set-properties', data); }; diff --git a/src/main/utils.ts b/src/main/utils.ts index ab49f2a1..8a29f6ef 100644 --- a/src/main/utils.ts +++ b/src/main/utils.ts @@ -52,7 +52,7 @@ export const hotkeyToElectronAccelerator = (hotkey: string) => { return accelerator; }; -const logInstance = { +const logMethod = { debug: log.debug, error: log.error, info: log.info, @@ -61,9 +61,36 @@ const logInstance = { warning: log.warn, }; +const logColor = { + debug: 'blue', + error: 'red', + info: 'blue', + success: 'green', + verbose: 'blue', + warning: 'yellow', +}; + export const createLog = (data: { message: string; type: 'debug' | 'verbose' | 'success' | 'error' | 'warning' | 'info'; }) => { - logInstance[data.type](data.message); + logMethod[data.type](`%c${data.message}`, `color: ${logColor[data.type]}`); +}; + +export const autoUpdaterLogInterface = { + debug: (message: string) => { + createLog({ message: `[SYSTEM] ${message}`, type: 'debug' }); + }, + + error: (message: string) => { + createLog({ message: `[SYSTEM] ${message}`, type: 'error' }); + }, + + info: (message: string) => { + createLog({ message: `[SYSTEM] ${message}`, type: 'info' }); + }, + + warn: (message: string) => { + createLog({ message: `[SYSTEM] ${message}`, type: 'warning' }); + }, };