Improve mpv error logging

This commit is contained in:
jeffvli 2024-02-12 20:11:55 -08:00
parent ff4ce89bc9
commit 60105103f3
4 changed files with 178 additions and 131 deletions

View file

@ -4,7 +4,7 @@ import uniq from 'lodash/uniq';
import MpvAPI from 'node-mpv'; import MpvAPI from 'node-mpv';
import { getMainWindow, sendToastToRenderer } from '../../../main'; import { getMainWindow, sendToastToRenderer } from '../../../main';
import { PlayerData } from '/@/renderer/store'; import { PlayerData } from '/@/renderer/store';
import { isWindows } from '../../../utils'; import { createLog, isWindows } from '../../../utils';
import { store } from '../settings'; import { store } from '../settings';
declare module 'node-mpv'; declare module 'node-mpv';
@ -19,6 +19,40 @@ declare module 'node-mpv';
let mpvInstance: MpvAPI | null = null; 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 MPV_BINARY_PATH = store.get('mpv_path') as string | undefined;
const isDevelopment = process.env.NODE_ENV === 'development' || process.env.DEBUG_PROD === 'true'; 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 { extraParameters, properties } = data;
const params = uniq([...DEFAULT_MPV_PARAMETERS(extraParameters), ...(extraParameters || [])]); const params = uniq([...DEFAULT_MPV_PARAMETERS(extraParameters), ...(extraParameters || [])]);
console.log('Setting mpv params: ', params);
const extra = isDevelopment ? '-dev' : ''; const extra = isDevelopment ? '-dev' : '';
@ -62,15 +95,13 @@ const createMpv = async (data: {
try { try {
await mpv.start(); await mpv.start();
} catch (error: { message: string; stack: any } | any) { } catch (error: any) {
console.log('MPV failed to start', error); console.log('mpv failed to start', error);
} finally { } finally {
console.log('Setting MPV properties: ', properties);
await mpv.setMultipleProperties(properties || {}); await mpv.setMultipleProperties(properties || {});
} }
mpv.on('status', (status, ...rest) => { mpv.on('status', (status) => {
console.log('MPV Event: status', status.property, status.value, rest);
if (status.property === 'playlist-pos') { if (status.property === 'playlist-pos') {
if (status.value === -1) { if (status.value === -1) {
mpv?.stop(); mpv?.stop();
@ -84,19 +115,16 @@ const createMpv = async (data: {
// Automatically updates the play button when the player is playing // Automatically updates the play button when the player is playing
mpv.on('resumed', () => { mpv.on('resumed', () => {
console.log('MPV Event: resumed');
getMainWindow()?.webContents.send('renderer-player-play'); getMainWindow()?.webContents.send('renderer-player-play');
}); });
// Automatically updates the play button when the player is stopped // Automatically updates the play button when the player is stopped
mpv.on('stopped', () => { mpv.on('stopped', () => {
console.log('MPV Event: stopped');
getMainWindow()?.webContents.send('renderer-player-stop'); getMainWindow()?.webContents.send('renderer-player-stop');
}); });
// Automatically updates the play button when the player is paused // Automatically updates the play button when the player is paused
mpv.on('paused', () => { mpv.on('paused', () => {
console.log('MPV Event: paused');
getMainWindow()?.webContents.send('renderer-player-pause'); getMainWindow()?.webContents.send('renderer-player-pause');
}); });
@ -105,10 +133,6 @@ const createMpv = async (data: {
getMainWindow()?.webContents.send('renderer-player-current-time', time); getMainWindow()?.webContents.send('renderer-player-current-time', time);
}); });
mpv.on('quit', () => {
console.log('MPV Event: quit');
});
return mpv; return mpv;
}; };
@ -117,27 +141,31 @@ export const getMpvInstance = () => {
}; };
ipcMain.on('player-set-properties', async (_event, data: Record<string, any>) => { ipcMain.on('player-set-properties', async (_event, data: Record<string, any>) => {
mpvLog(`Setting properties: ${JSON.stringify(data)}`);
if (data.length === 0) { if (data.length === 0) {
return; return;
} }
if (data.length === 1) { try {
getMpvInstance()?.setProperty(Object.keys(data)[0], Object.values(data)[0]); if (data.length === 1) {
} else { getMpvInstance()?.setProperty(Object.keys(data)[0], Object.values(data)[0]);
getMpvInstance()?.setMultipleProperties(data); } else {
getMpvInstance()?.setMultipleProperties(data);
}
} catch (err: NodeMpvError | any) {
mpvLog(`Failed to set properties: ${JSON.stringify(data)}`, err);
} }
}); });
ipcMain.on( ipcMain.on(
'player-restart', 'player-restart',
async (_event, data: { extraParameters?: string[]; properties?: Record<string, any> }) => { async (_event, data: { extraParameters?: string[]; properties?: Record<string, any> }) => {
console.log('Initializing MPV with data: ', data);
mpvInstance?.quit(); mpvInstance?.quit();
try { try {
mpvLog(`Attempting to initialize mpv with parameters: ${JSON.stringify(data)}`);
mpvInstance = await createMpv(data); mpvInstance = await createMpv(data);
} catch (err) { } catch (err: NodeMpvError | any) {
console.log('init error', err); mpvLog('Failed to initialize mpv', err);
sendToastToRenderer({ message: 'Initialization error', type: 'error' });
} }
}, },
); );
@ -145,20 +173,23 @@ ipcMain.on(
ipcMain.handle( ipcMain.handle(
'player-initialize', 'player-initialize',
async (_event, data: { extraParameters?: string[]; properties?: Record<string, any> }) => { async (_event, data: { extraParameters?: string[]; properties?: Record<string, any> }) => {
console.log('Initializing MPV with data: ', data);
try { try {
mpvLog(`Attempting to initialize mpv with parameters: ${JSON.stringify(data)}`);
mpvInstance = await createMpv(data); mpvInstance = await createMpv(data);
} catch (err) { } catch (err: NodeMpvError | any) {
console.log('init error', err); mpvLog('Failed to initialize mpv', err);
sendToastToRenderer({ message: 'Initialization error', type: 'error' });
} }
}, },
); );
ipcMain.on('player-quit', async () => { ipcMain.on('player-quit', async () => {
mpvInstance?.stop(); try {
mpvInstance?.quit(); mpvInstance?.stop();
mpvInstance = null; mpvInstance?.quit();
mpvInstance = null;
} catch (err: NodeMpvError | any) {
mpvLog('Failed to quit mpv', err);
}
}); });
ipcMain.handle('player-is-running', async () => { ipcMain.handle('player-is-running', async () => {
@ -171,99 +202,93 @@ ipcMain.handle('player-clean-up', async () => {
}); });
ipcMain.on('player-start', async () => { ipcMain.on('player-start', async () => {
await getMpvInstance() try {
?.play() await getMpvInstance()?.play();
.catch((err) => { } catch (err: NodeMpvError | any) {
console.log('MPV failed to play', err); mpvLog('Failed to start mpv playback', err);
}); }
}); });
// Starts the player // Starts the player
ipcMain.on('player-play', async () => { ipcMain.on('player-play', async () => {
await getMpvInstance() try {
?.play() await getMpvInstance()?.play();
.catch((err) => { } catch (err: NodeMpvError | any) {
console.log('MPV failed to play', err); mpvLog('Failed to start mpv playback', err);
}); }
}); });
// Pauses the player // Pauses the player
ipcMain.on('player-pause', async () => { ipcMain.on('player-pause', async () => {
await getMpvInstance() try {
?.pause() await getMpvInstance()?.pause();
.catch((err) => { } catch (err: NodeMpvError | any) {
console.log('MPV failed to pause', err); mpvLog('Failed to pause mpv playback', err);
}); }
}); });
// Stops the player // Stops the player
ipcMain.on('player-stop', async () => { ipcMain.on('player-stop', async () => {
await getMpvInstance() try {
?.stop() await getMpvInstance()?.stop();
.catch((err) => { } catch (err: NodeMpvError | any) {
console.log('MPV failed to stop', err); mpvLog('Failed to stop mpv playback', err);
}); }
}); });
// Goes to the next track in the playlist // Goes to the next track in the playlist
ipcMain.on('player-next', async () => { ipcMain.on('player-next', async () => {
await getMpvInstance() try {
?.next() await getMpvInstance()?.next();
.catch((err) => { } catch (err: NodeMpvError | any) {
console.log('MPV failed to go to next', err); mpvLog('Failed to go to next track', err);
}); }
}); });
// Goes to the previous track in the playlist // Goes to the previous track in the playlist
ipcMain.on('player-previous', async () => { ipcMain.on('player-previous', async () => {
await getMpvInstance() try {
?.prev() await getMpvInstance()?.prev();
.catch((err) => { } catch (err: NodeMpvError | any) {
console.log('MPV failed to go to previous', err); mpvLog('Failed to go to previous track', err);
}); }
}); });
// Seeks forward or backward by the given amount of seconds // Seeks forward or backward by the given amount of seconds
ipcMain.on('player-seek', async (_event, time: number) => { ipcMain.on('player-seek', async (_event, time: number) => {
await getMpvInstance() try {
?.seek(time) await getMpvInstance()?.seek(time);
.catch((err) => { } catch (err: NodeMpvError | any) {
console.log('MPV failed to seek', err); mpvLog(`Failed to seek by ${time} seconds`, err);
}); }
}); });
// Seeks to the given time in seconds // Seeks to the given time in seconds
ipcMain.on('player-seek-to', async (_event, time: number) => { ipcMain.on('player-seek-to', async (_event, time: number) => {
await getMpvInstance() try {
?.goToPosition(time) await getMpvInstance()?.goToPosition(time);
.catch((err) => { } catch (err: NodeMpvError | any) {
console.log(`MPV failed to seek to ${time}`, err); 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 // 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) => { ipcMain.on('player-set-queue', async (_event, data: PlayerData, pause?: boolean) => {
if (!data.queue.current && !data.queue.next) { if (!data.queue.current && !data.queue.next) {
await getMpvInstance() try {
?.clearPlaylist() await getMpvInstance()?.clearPlaylist();
.catch((err) => { await getMpvInstance()?.pause();
console.log('MPV failed to clear playlist', err); return;
}); } catch (err: NodeMpvError | any) {
mpvLog(`Failed to clear play queue`, err);
await getMpvInstance() }
?.pause()
.catch((err) => {
console.log('MPV failed to pause', err);
});
return;
} }
try { try {
if (data.queue.current) { if (data.queue.current) {
await getMpvInstance() await getMpvInstance()
?.load(data.queue.current.streamUrl, 'replace') ?.load(data.queue.current.streamUrl, 'replace')
.catch((err) => { .catch(() => {
console.log('MPV failed to load song', err);
getMpvInstance()?.play(); 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'); await getMpvInstance()?.load(data.queue.next.streamUrl, 'append');
} }
} }
} catch (err) { } catch (err: NodeMpvError | any) {
console.error(err); mpvLog(`Failed to set play queue`, err);
} }
if (pause) { 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 // Replaces the queue in position 1 to the given data
ipcMain.on('player-set-queue-next', async (_event, data: PlayerData) => { ipcMain.on('player-set-queue-next', async (_event, data: PlayerData) => {
const size = await getMpvInstance() try {
?.getPlaylistSize() const size = await getMpvInstance()?.getPlaylistSize();
.catch((err) => {
console.log('MPV failed to get playlist size', err);
});
if (!size) { if (!size) {
return; return;
} }
if (size > 1) { if (size > 1) {
await getMpvInstance() await getMpvInstance()?.playlistRemove(1);
?.playlistRemove(1) }
.catch((err) => {
console.log('MPV failed to remove song from playlist', err);
});
}
if (data.queue.next) { if (data.queue.next) {
await getMpvInstance() await getMpvInstance()?.load(data.queue.next.streamUrl, 'append');
?.load(data.queue.next.streamUrl, 'append') }
.catch((err) => { } catch (err: NodeMpvError | any) {
console.log('MPV failed to load next song', err); 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 // 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 // This allows us to easily set update the next song in the queue without
// disturbing the currently playing song // disturbing the currently playing song
await getMpvInstance() try {
?.playlistRemove(0)
.catch((err) => {
console.log('MPV failed to remove song from playlist', err);
getMpvInstance()?.pause();
});
if (data.queue.next) {
await getMpvInstance() await getMpvInstance()
?.load(data.queue.next.streamUrl, 'append') ?.playlistRemove(0)
.catch((err) => { .catch(() => {
console.log('MPV failed to load next song', err); 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) // Sets the volume to the given value (0-100)
ipcMain.on('player-volume', async (_event, value: number) => { ipcMain.on('player-volume', async (_event, value: number) => {
await getMpvInstance() try {
?.volume(value) if (!value || value < 0 || value > 100) {
.catch((err) => { return;
console.log('MPV failed to set volume', err); }
});
await getMpvInstance()?.volume(value);
} catch (err: NodeMpvError | any) {
mpvLog(`Failed to set volume to ${value}`, err);
}
}); });
// Toggles the mute status // Toggles the mute status
ipcMain.on('player-mute', async (_event, mute: boolean) => { ipcMain.on('player-mute', async (_event, mute: boolean) => {
await getMpvInstance() try {
?.mute(mute) await getMpvInstance()?.mute(mute);
.catch((err) => { } catch (err: NodeMpvError | any) {
console.log('MPV failed to toggle mute', err); mpvLog(`Failed to set mute status`, err);
}); }
}); });
ipcMain.handle('player-get-time', async (): Promise<number | undefined> => { ipcMain.handle('player-get-time', async (): Promise<number | undefined> => {

View file

@ -38,6 +38,7 @@ import {
isWindows, isWindows,
resolveHtmlPath, resolveHtmlPath,
createLog, createLog,
autoUpdaterLogInterface,
} from './utils'; } from './utils';
import './features'; import './features';
import type { TitleTheme } from '/@/renderer/types'; import type { TitleTheme } from '/@/renderer/types';
@ -47,7 +48,7 @@ declare module 'node-mpv';
export default class AppUpdater { export default class AppUpdater {
constructor() { constructor() {
log.transports.file.level = 'info'; log.transports.file.level = 'info';
autoUpdater.logger = log; autoUpdater.logger = autoUpdaterLogInterface;
autoUpdater.checkForUpdatesAndNotify(); autoUpdater.checkForUpdatesAndNotify();
} }
} }

View file

@ -18,7 +18,6 @@ const cleanup = () => {
}; };
const setProperties = (data: Record<string, any>) => { const setProperties = (data: Record<string, any>) => {
console.log('Setting property :>>', data);
ipcRenderer.send('player-set-properties', data); ipcRenderer.send('player-set-properties', data);
}; };

View file

@ -52,7 +52,7 @@ export const hotkeyToElectronAccelerator = (hotkey: string) => {
return accelerator; return accelerator;
}; };
const logInstance = { const logMethod = {
debug: log.debug, debug: log.debug,
error: log.error, error: log.error,
info: log.info, info: log.info,
@ -61,9 +61,36 @@ const logInstance = {
warning: log.warn, warning: log.warn,
}; };
const logColor = {
debug: 'blue',
error: 'red',
info: 'blue',
success: 'green',
verbose: 'blue',
warning: 'yellow',
};
export const createLog = (data: { export const createLog = (data: {
message: string; message: string;
type: 'debug' | 'verbose' | 'success' | 'error' | 'warning' | 'info'; 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' });
},
}; };