Compare commits

...

3 Commits

Author SHA1 Message Date
jeffvli 31bcc70498 lint 2026-02-13 11:22:32 -08:00
jeffvli 41c21b94c1 add additional logging to controller and mutations 2026-02-13 11:05:40 -08:00
jeffvli bca14176fb remove logMsg and refactor messages inline 2026-02-13 02:34:22 -08:00
48 changed files with 550 additions and 360 deletions
+3 -2
View File
@@ -1,6 +1,7 @@
import { createSocket } from 'dgram'; import { createSocket } from 'dgram';
import { ipcMain } from 'electron'; import { ipcMain } from 'electron';
import { mainLogger } from '/@/main/logger';
import { DiscoveredServerItem, ServerType } from '/@/shared/types/types'; import { DiscoveredServerItem, ServerType } from '/@/shared/types/types';
type JellyfinResponse = { type JellyfinResponse = {
@@ -26,7 +27,7 @@ function discoverJellyfin(reply: (server: DiscoveredServerItem) => void) {
}); });
} catch (e) { } catch (e) {
// Got a spurious response, ignore? // Got a spurious response, ignore?
console.error(e); mainLogger.error('Autodiscover Jellyfin parse error', e);
} }
}); });
@@ -51,5 +52,5 @@ ipcMain.on('autodiscover-ping', (ev) => {
discoverAll((result) => port.postMessage(result)) discoverAll((result) => port.postMessage(result))
.then(() => port.close()) .then(() => port.close())
.catch((err) => console.error(err)); .catch((err) => mainLogger.error('Autodiscover failed', err));
}); });
+4 -3
View File
@@ -7,6 +7,7 @@ import {
LyricSearchQuery, LyricSearchQuery,
LyricSource, LyricSource,
} from '.'; } from '.';
import { mainLogger } from '../../../logger';
import { orderSearchResults } from './shared'; import { orderSearchResults } from './shared';
const SEARCH_URL = 'https://genius.com/api/search/song'; const SEARCH_URL = 'https://genius.com/api/search/song';
@@ -100,7 +101,7 @@ export async function getLyricsBySongId(url: string): Promise<null | string> {
try { try {
result = await axios.get<string>(url, { responseType: 'text' }); result = await axios.get<string>(url, { responseType: 'text' });
} catch (e) { } catch (e) {
console.error('Genius lyrics request got an error!', (e as Error)?.message); mainLogger.error('Genius lyrics request failed', (e as Error)?.message);
return null; return null;
} }
@@ -138,7 +139,7 @@ export async function getSearchResults(
}, },
}); });
} catch (e) { } catch (e) {
console.error('Genius search request got an error!', (e as Error)?.message); mainLogger.error('Genius search request failed', (e as Error)?.message);
return null; return null;
} }
@@ -193,7 +194,7 @@ async function getSongId(
}, },
}); });
} catch (e) { } catch (e) {
console.error('Genius search request got an error!', (e as Error)?.message); mainLogger.error('Genius search request failed', (e as Error)?.message);
return null; return null;
} }
+3 -2
View File
@@ -1,5 +1,6 @@
import { ipcMain } from 'electron'; import { ipcMain } from 'electron';
import { mainLogger } from '../../../logger';
import { store } from '../settings'; import { store } from '../settings';
import { getLyricsBySongId as getGenius, getSearchResults as searchGenius } from './genius'; import { getLyricsBySongId as getGenius, getSearchResults as searchGenius } from './genius';
import { getLyricsBySongId as getLrcLib, getSearchResults as searchLrcLib } from './lrclib'; import { getLyricsBySongId as getLrcLib, getSearchResults as searchLrcLib } from './lrclib';
@@ -96,7 +97,7 @@ const searchAllSources = async (
allSearchResults.push(...result.value.searchResults); allSearchResults.push(...result.value.searchResults);
} else if (result.status === 'rejected') { } else if (result.status === 'rejected') {
const index = settled.indexOf(result); const index = settled.indexOf(result);
console.error(`Error searching ${sources[index]} for lyrics:`, result.reason); mainLogger.error(`Error searching ${sources[index]} for lyrics`, result.reason);
} }
} }
return allSearchResults; return allSearchResults;
@@ -160,7 +161,7 @@ const getRemoteLyrics = async (song: Song) => {
}; };
} }
} catch (error) { } catch (error) {
console.error(`Error fetching lyrics from ${bestMatch.source}:`, error); mainLogger.error(`Error fetching lyrics from ${bestMatch.source}`, error);
} }
if (lyricsFromSource) { if (lyricsFromSource) {
+4 -3
View File
@@ -7,6 +7,7 @@ import {
LyricSearchQuery, LyricSearchQuery,
LyricSource, LyricSource,
} from '.'; } from '.';
import { mainLogger } from '../../../logger';
import { orderSearchResults } from './shared'; import { orderSearchResults } from './shared';
const FETCH_URL = 'https://lrclib.net/api/get'; const FETCH_URL = 'https://lrclib.net/api/get';
@@ -46,7 +47,7 @@ export async function getLyricsBySongId(songId: string): Promise<null | string>
try { try {
result = await axios.get<LrcLibTrackResponse>(`${FETCH_URL}/${songId}`); result = await axios.get<LrcLibTrackResponse>(`${FETCH_URL}/${songId}`);
} catch (e) { } catch (e) {
console.error('LrcLib lyrics request got an error!', (e as Error)?.message); mainLogger.error('LrcLib lyrics request failed', (e as Error)?.message);
return null; return null;
} }
@@ -69,7 +70,7 @@ export async function getSearchResults(
}, },
}); });
} catch (e) { } catch (e) {
console.error('LrcLib search request got an error!', (e as Error)?.message); mainLogger.error('LrcLib search request failed', (e as Error)?.message);
return null; return null;
} }
@@ -107,7 +108,7 @@ export async function query(
timeout: TIMEOUT_MS, timeout: TIMEOUT_MS,
}); });
} catch (e) { } catch (e) {
console.error('LrcLib search request got an error!', (e as Error).message); mainLogger.error('LrcLib search request failed', (e as Error).message);
return null; return null;
} }
+3 -2
View File
@@ -6,6 +6,7 @@ import {
LyricSearchQuery, LyricSearchQuery,
LyricSource, LyricSource,
} from '.'; } from '.';
import { mainLogger } from '../../../logger';
import { store } from '../settings'; import { store } from '../settings';
import { orderSearchResults } from './shared'; import { orderSearchResults } from './shared';
@@ -81,7 +82,7 @@ export async function getLyricsBySongId(songId: string): Promise<null | string>
}, },
}); });
} catch (e) { } catch (e) {
console.error('NetEase lyrics request got an error!', e); mainLogger.error('NetEase lyrics request failed', e);
return null; return null;
} }
const enableTranslation = store.get('enableNeteaseTranslation', false) as boolean; const enableTranslation = store.get('enableNeteaseTranslation', false) as boolean;
@@ -114,7 +115,7 @@ export async function getSearchResults(
}, },
}); });
} catch (e) { } catch (e) {
console.error('NetEase search request got an error!', e); mainLogger.error('NetEase search request failed', e);
return null; return null;
} }
+4 -4
View File
@@ -1,4 +1,3 @@
import console from 'console';
import { app, ipcMain } from 'electron'; import { app, ipcMain } from 'electron';
import { rm } from 'fs/promises'; import { rm } from 'fs/promises';
import uniq from 'lodash/uniq'; import uniq from 'lodash/uniq';
@@ -7,6 +6,7 @@ import { pid } from 'node:process';
import process from 'process'; import process from 'process';
import { getMainWindow, sendToastToRenderer } from '../../../index'; import { getMainWindow, sendToastToRenderer } from '../../../index';
import { mainLogger } from '../../../logger';
import { createLog, isWindows } from '../../../utils'; import { createLog, isWindows } from '../../../utils';
import { store } from '../settings'; import { store } from '../settings';
@@ -109,7 +109,7 @@ const createMpv = async (data: {
try { try {
await mpv.start(); await mpv.start();
} catch (error: any) { } catch (error: any) {
console.error('mpv failed to start', error); mainLogger.error('mpv failed to start', error);
} finally { } finally {
await mpv.setMultipleProperties(properties || {}); await mpv.setMultipleProperties(properties || {});
} }
@@ -672,7 +672,7 @@ process.on('SIGTERM', async () => {
// Handle uncaught exceptions - cleanup mpv before crashing // Handle uncaught exceptions - cleanup mpv before crashing
process.on('uncaughtException', async (error) => { process.on('uncaughtException', async (error) => {
console.error('Uncaught exception:', error); mainLogger.error('Uncaught exception', error);
await cleanupMpv(true).catch(() => { await cleanupMpv(true).catch(() => {
// Ignore cleanup errors during crash // Ignore cleanup errors during crash
}); });
@@ -680,7 +680,7 @@ process.on('uncaughtException', async (error) => {
// Handle unhandled rejections - cleanup mpv // Handle unhandled rejections - cleanup mpv
process.on('unhandledRejection', async (reason) => { process.on('unhandledRejection', async (reason) => {
console.error('Unhandled rejection:', reason); mainLogger.error('Unhandled rejection', reason);
await cleanupMpv(true).catch(() => { await cleanupMpv(true).catch(() => {
// Ignore cleanup errors // Ignore cleanup errors
}); });
+3 -2
View File
@@ -10,6 +10,7 @@ import { deflate, gzip } from 'zlib';
import manifest from './manifest.json'; import manifest from './manifest.json';
import { getMainWindow } from '/@/main/index'; import { getMainWindow } from '/@/main/index';
import { mainLogger } from '/@/main/logger';
import { isLinux } from '/@/main/utils'; import { isLinux } from '/@/main/utils';
import { QueueSong } from '/@/shared/types/domain-types'; import { QueueSong } from '/@/shared/types/domain-types';
import { ClientEvent, ServerEvent } from '/@/shared/types/remote-types'; import { ClientEvent, ServerEvent } from '/@/shared/types/remote-types';
@@ -349,7 +350,7 @@ const enableServer = (config: RemoteConfig): Promise<void> => {
}, 10000) as unknown as number; }, 10000) as unknown as number;
} }
ws.on('error', console.error); ws.on('error', (err) => mainLogger.error('Remote WebSocket error', err));
ws.on('message', (data) => { ws.on('message', (data) => {
try { try {
@@ -488,7 +489,7 @@ const enableServer = (config: RemoteConfig): Promise<void> => {
} }
} }
} catch (error) { } catch (error) {
console.error(error); mainLogger.error('Remote message handler error', error);
} }
}); });
+15 -14
View File
@@ -29,6 +29,7 @@ import packageJson from '../../package.json';
import { disableMediaKeys, enableMediaKeys } from './features/core/player/media-keys'; import { disableMediaKeys, enableMediaKeys } from './features/core/player/media-keys';
import { shutdownServer } from './features/core/remote'; import { shutdownServer } from './features/core/remote';
import { store } from './features/core/settings'; import { store } from './features/core/settings';
import { mainLogger } from './logger';
import MenuBuilder from './menu'; import MenuBuilder from './menu';
import { import {
autoUpdaterLogInterface, autoUpdaterLogInterface,
@@ -66,7 +67,7 @@ type UpdaterInstance = AppImageUpdater | MacUpdater | NsisUpdater | typeof autoU
class AppUpdater { class AppUpdater {
constructor() { constructor() {
const effectiveChannel = store.get('release_channel') as string; const effectiveChannel = store.get('release_channel') as string;
console.log('Effective update channel:', effectiveChannel); mainLogger.info('Effective update channel:', effectiveChannel);
if (effectiveChannel === 'alpha') { if (effectiveChannel === 'alpha') {
checkAllChannelsAndGetBest().then(({ updater: updaterInstance }) => { checkAllChannelsAndGetBest().then(({ updater: updaterInstance }) => {
updaterInstance.autoInstallOnAppQuit = true; updaterInstance.autoInstallOnAppQuit = true;
@@ -103,7 +104,7 @@ async function checkAllChannelsAndGetBest(): Promise<{
alphaUpdater.allowDowngrade = true; alphaUpdater.allowDowngrade = true;
try { try {
console.log('Checking for updates on alpha channel'); mainLogger.info('Checking for updates on alpha channel');
const alphaResult = await alphaUpdater.checkForUpdates(); const alphaResult = await alphaUpdater.checkForUpdates();
if ( if (
alphaResult?.updateInfo?.version && alphaResult?.updateInfo?.version &&
@@ -120,7 +121,7 @@ async function checkAllChannelsAndGetBest(): Promise<{
try { try {
autoUpdater.setFeedURL(GITHUB_UPDATER_CONFIG); autoUpdater.setFeedURL(GITHUB_UPDATER_CONFIG);
configureAutoUpdaterForChannel('latest'); configureAutoUpdaterForChannel('latest');
console.log('Checking for updates on latest channel (GitHub)'); mainLogger.info('Checking for updates on latest channel (GitHub)');
const latestResult = await autoUpdater.checkForUpdates(); const latestResult = await autoUpdater.checkForUpdates();
if ( if (
latestResult?.updateInfo?.version && latestResult?.updateInfo?.version &&
@@ -155,13 +156,13 @@ function configureAndGetUpdater(): UpdaterInstance {
let releaseChannel = store.get('release_channel'); let releaseChannel = store.get('release_channel');
const isNotConfigured = !releaseChannel; const isNotConfigured = !releaseChannel;
console.log('Release channel:', releaseChannel); mainLogger.info('Release channel:', releaseChannel);
console.log('Is beta version:', isBetaVersion); mainLogger.info('Is beta version:', isBetaVersion);
console.log('Is alpha version:', isAlphaVersion); mainLogger.info('Is alpha version:', isAlphaVersion);
console.log('Is not configured:', isNotConfigured); mainLogger.info('Is not configured:', isNotConfigured);
if (isNotConfigured) { if (isNotConfigured) {
console.log('Release channel not configured, setting default channel'); mainLogger.info('Release channel not configured, setting default channel');
const defaultChannel = isAlphaVersion ? 'alpha' : isBetaVersion ? 'beta' : 'latest'; const defaultChannel = isAlphaVersion ? 'alpha' : isBetaVersion ? 'beta' : 'latest';
store.set('release_channel', defaultChannel); store.set('release_channel', defaultChannel);
releaseChannel = defaultChannel; releaseChannel = defaultChannel;
@@ -235,7 +236,7 @@ function createAlphaUpdaterInstance(): AppImageUpdater | MacUpdater | NsisUpdate
protocol.registerSchemesAsPrivileged([{ privileges: { bypassCSP: true }, scheme: 'feishin' }]); protocol.registerSchemesAsPrivileged([{ privileges: { bypassCSP: true }, scheme: 'feishin' }]);
process.on('uncaughtException', (error: any) => { process.on('uncaughtException', (error: any) => {
console.error('Error in main process', error); mainLogger.error('Uncaught exception in main process', error);
}); });
if (store.get('ignore_ssl')) { if (store.get('ignore_ssl')) {
@@ -521,12 +522,12 @@ async function createWindow(first = true): Promise<void> {
'app-check-for-updates', 'app-check-for-updates',
async (): Promise<{ updateAvailable: boolean; version?: string }> => { async (): Promise<{ updateAvailable: boolean; version?: string }> => {
if (disableAutoUpdates()) { if (disableAutoUpdates()) {
console.log('Auto updates are disabled'); mainLogger.info('Auto updates are disabled');
return { updateAvailable: false }; return { updateAvailable: false };
} }
try { try {
console.log('Checking for updates'); mainLogger.info('Checking for updates');
const effectiveChannel = store.get('release_channel') as string; const effectiveChannel = store.get('release_channel') as string;
let result: null | UpdateCheckResult; let result: null | UpdateCheckResult;
let updater: UpdaterInstance; let updater: UpdaterInstance;
@@ -541,9 +542,9 @@ async function createWindow(first = true): Promise<void> {
} }
const updateAvailable = result?.isUpdateAvailable ?? false; const updateAvailable = result?.isUpdateAvailable ?? false;
console.log('Update available:', updateAvailable); mainLogger.info('Update available:', updateAvailable);
if (updateAvailable && store.get('disable_auto_updates') !== true) { if (updateAvailable && store.get('disable_auto_updates') !== true) {
console.log('Downloading update'); mainLogger.info('Downloading update');
updater.downloadUpdate(); updater.downloadUpdate();
} }
@@ -552,7 +553,7 @@ async function createWindow(first = true): Promise<void> {
version: result?.updateInfo?.version, version: result?.updateInfo?.version,
}; };
} catch { } catch {
console.log('Error checking for updates'); mainLogger.error('Error checking for updates');
return { updateAvailable: false }; return { updateAvailable: false };
} }
}, },
+36
View File
@@ -0,0 +1,36 @@
const pad = (n: number) => String(n).padStart(2, '0');
const timestamp = () => {
const d = new Date();
return `${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
};
const format = (level: string, message: string, ...args: unknown[]) => {
const prefix = `[${timestamp()}] [${level}] ${message}`;
if (args.length > 0) {
console.log(prefix, ...args);
} else {
console.log(prefix);
}
};
export const mainLogger = {
debug: (message: string, ...args: unknown[]) => format('DEBUG', message, ...args),
error: (message: string, ...args: unknown[]) => {
const prefix = `[${timestamp()}] [ERROR] ${message}`;
if (args.length > 0) {
console.error(prefix, ...args);
} else {
console.error(prefix);
}
},
info: (message: string, ...args: unknown[]) => format('INFO', message, ...args),
warn: (message: string, ...args: unknown[]) => {
const prefix = `[${timestamp()}] [WARN] ${message}`;
if (args.length > 0) {
console.warn(prefix, ...args);
} else {
console.warn(prefix);
}
},
};
+59 -87
View File
@@ -4,7 +4,6 @@ import { immer } from 'zustand/middleware/immer';
import { createWithEqualityFn } from 'zustand/traditional'; import { createWithEqualityFn } from 'zustand/traditional';
import { LogCategory, logFn } from '/@/renderer/utils/logger'; import { LogCategory, logFn } from '/@/renderer/utils/logger';
import { logMsg } from '/@/renderer/utils/logger-message';
import { toast } from '/@/shared/components/toast/toast'; import { toast } from '/@/shared/components/toast/toast';
import { ClientEvent, ServerEvent, SongUpdateSocket } from '/@/shared/types/remote-types'; import { ClientEvent, ServerEvent, SongUpdateSocket } from '/@/shared/types/remote-types';
@@ -42,7 +41,7 @@ export const useRemoteStore = createWithEqualityFn<SettingsSlice>()(
immer((set, get) => ({ immer((set, get) => ({
actions: { actions: {
reconnect: async () => { reconnect: async () => {
logFn.debug(logMsg[LogCategory.REMOTE].reconnectInitiated, { logFn.debug('Reconnect initiated', {
category: LogCategory.REMOTE, category: LogCategory.REMOTE,
}); });
const existing = get().socket; const existing = get().socket;
@@ -52,7 +51,7 @@ export const useRemoteStore = createWithEqualityFn<SettingsSlice>()(
existing.readyState === WebSocket.OPEN || existing.readyState === WebSocket.OPEN ||
existing.readyState === WebSocket.CONNECTING existing.readyState === WebSocket.CONNECTING
) { ) {
logFn.debug(logMsg[LogCategory.REMOTE].closingExistingSocket, { logFn.debug('Closing existing socket', {
category: LogCategory.REMOTE, category: LogCategory.REMOTE,
meta: { readyState: existing.readyState }, meta: { readyState: existing.readyState },
}); });
@@ -64,17 +63,17 @@ export const useRemoteStore = createWithEqualityFn<SettingsSlice>()(
let authHeader: string | undefined; let authHeader: string | undefined;
try { try {
logFn.debug(logMsg[LogCategory.REMOTE].fetchingCredentials, { logFn.debug('Fetching credentials', {
category: LogCategory.REMOTE, category: LogCategory.REMOTE,
}); });
const credentials = await fetch('/credentials'); const credentials = await fetch('/credentials');
authHeader = await credentials.text(); authHeader = await credentials.text();
logFn.debug(logMsg[LogCategory.REMOTE].credentialsFetched, { logFn.debug('Credentials fetched', {
category: LogCategory.REMOTE, category: LogCategory.REMOTE,
meta: { hasAuthHeader: !!authHeader }, meta: { hasAuthHeader: !!authHeader },
}); });
} catch (error) { } catch (error) {
logFn.error(logMsg[LogCategory.REMOTE].failedToGetCredentials, { logFn.error('Failed to get credentials', {
category: LogCategory.REMOTE, category: LogCategory.REMOTE,
meta: { error }, meta: { error },
}); });
@@ -82,7 +81,7 @@ export const useRemoteStore = createWithEqualityFn<SettingsSlice>()(
set((state) => { set((state) => {
const wsUrl = location.href.replace('http', 'ws'); const wsUrl = location.href.replace('http', 'ws');
logFn.debug(logMsg[LogCategory.REMOTE].creatingWebSocket, { logFn.debug('Creating new WebSocket', {
category: LogCategory.REMOTE, category: LogCategory.REMOTE,
meta: { url: wsUrl }, meta: { url: wsUrl },
}); });
@@ -93,34 +92,28 @@ export const useRemoteStore = createWithEqualityFn<SettingsSlice>()(
socket.addEventListener('message', (message) => { socket.addEventListener('message', (message) => {
const { data, event } = JSON.parse(message.data) as ServerEvent; const { data, event } = JSON.parse(message.data) as ServerEvent;
logFn.debug(logMsg[LogCategory.REMOTE].webSocketMessageReceived, { logFn.debug('WebSocket message received', {
category: LogCategory.REMOTE, category: LogCategory.REMOTE,
meta: { data, event }, meta: { data, event },
}); });
switch (event) { switch (event) {
case 'error': { case 'error': {
logFn.error( logFn.error('WebSocket error event', {
logMsg[LogCategory.REMOTE].webSocketErrorEvent, category: LogCategory.REMOTE,
{ meta: { data },
category: LogCategory.REMOTE, });
meta: { data },
},
);
toast.error({ message: data, title: 'Socket error' }); toast.error({ message: data, title: 'Socket error' });
break; break;
} }
case 'favorite': { case 'favorite': {
logFn.debug( logFn.debug('Favorite event received', {
logMsg[LogCategory.REMOTE].favoriteEventReceived, category: LogCategory.REMOTE,
{ meta: {
category: LogCategory.REMOTE, favorite: data.favorite,
meta: { id: data.id,
favorite: data.favorite,
id: data.id,
},
}, },
); });
set((state) => { set((state) => {
if (state.info.song?.id === data.id) { if (state.info.song?.id === data.id) {
state.info.song.userFavorite = data.favorite; state.info.song.userFavorite = data.favorite;
@@ -129,33 +122,27 @@ export const useRemoteStore = createWithEqualityFn<SettingsSlice>()(
break; break;
} }
case 'playback': { case 'playback': {
logFn.debug( logFn.debug('Playback event received', {
logMsg[LogCategory.REMOTE].playbackEventReceived, category: LogCategory.REMOTE,
{ meta: { status: data },
category: LogCategory.REMOTE, });
meta: { status: data },
},
);
set((state) => { set((state) => {
state.info.status = data; state.info.status = data;
}); });
break; break;
} }
case 'position': { case 'position': {
logFn.debug( logFn.debug('Position event received', {
logMsg[LogCategory.REMOTE].positionEventReceived, category: LogCategory.REMOTE,
{ meta: { position: data },
category: LogCategory.REMOTE, });
meta: { position: data },
},
);
set((state) => { set((state) => {
state.info.position = data; state.info.position = data;
}); });
break; break;
} }
case 'proxy': { case 'proxy': {
logFn.debug(logMsg[LogCategory.REMOTE].proxyEventReceived, { logFn.debug('Proxy event received (image update)', {
category: LogCategory.REMOTE, category: LogCategory.REMOTE,
meta: { meta: {
dataLength: data?.length, dataLength: data?.length,
@@ -170,16 +157,13 @@ export const useRemoteStore = createWithEqualityFn<SettingsSlice>()(
break; break;
} }
case 'rating': { case 'rating': {
logFn.debug( logFn.debug('Rating event received', {
logMsg[LogCategory.REMOTE].ratingEventReceived, category: LogCategory.REMOTE,
{ meta: {
category: LogCategory.REMOTE, id: data.id,
meta: { rating: data.rating,
id: data.id,
rating: data.rating,
},
}, },
); });
set((state) => { set((state) => {
if (state.info.song?.id === data.id) { if (state.info.song?.id === data.id) {
state.info.song.userRating = data.rating; state.info.song.userRating = data.rating;
@@ -188,33 +172,27 @@ export const useRemoteStore = createWithEqualityFn<SettingsSlice>()(
break; break;
} }
case 'repeat': { case 'repeat': {
logFn.debug( logFn.debug('Repeat event received', {
logMsg[LogCategory.REMOTE].repeatEventReceived, category: LogCategory.REMOTE,
{ meta: { repeat: data },
category: LogCategory.REMOTE, });
meta: { repeat: data },
},
);
set((state) => { set((state) => {
state.info.repeat = data; state.info.repeat = data;
}); });
break; break;
} }
case 'shuffle': { case 'shuffle': {
logFn.debug( logFn.debug('Shuffle event received', {
logMsg[LogCategory.REMOTE].shuffleEventReceived, category: LogCategory.REMOTE,
{ meta: { shuffle: data },
category: LogCategory.REMOTE, });
meta: { shuffle: data },
},
);
set((state) => { set((state) => {
state.info.shuffle = data; state.info.shuffle = data;
}); });
break; break;
} }
case 'song': { case 'song': {
logFn.debug(logMsg[LogCategory.REMOTE].songEventReceived, { logFn.debug('Song event received', {
category: LogCategory.REMOTE, category: LogCategory.REMOTE,
meta: { meta: {
artistName: data?.artistName, artistName: data?.artistName,
@@ -228,7 +206,7 @@ export const useRemoteStore = createWithEqualityFn<SettingsSlice>()(
break; break;
} }
case 'state': { case 'state': {
logFn.debug(logMsg[LogCategory.REMOTE].stateEventReceived, { logFn.debug('State event received (full state update)', {
category: LogCategory.REMOTE, category: LogCategory.REMOTE,
meta: { meta: {
hasSong: !!data.song, hasSong: !!data.song,
@@ -243,13 +221,10 @@ export const useRemoteStore = createWithEqualityFn<SettingsSlice>()(
break; break;
} }
case 'volume': { case 'volume': {
logFn.debug( logFn.debug('Volume event received', {
logMsg[LogCategory.REMOTE].volumeEventReceived, category: LogCategory.REMOTE,
{ meta: { volume: data },
category: LogCategory.REMOTE, });
meta: { volume: data },
},
);
set((state) => { set((state) => {
state.info.volume = data; state.info.volume = data;
}); });
@@ -258,7 +233,7 @@ export const useRemoteStore = createWithEqualityFn<SettingsSlice>()(
}); });
socket.addEventListener('open', () => { socket.addEventListener('open', () => {
logFn.debug(logMsg[LogCategory.REMOTE].webSocketOpened, { logFn.debug('WebSocket opened', {
category: LogCategory.REMOTE, category: LogCategory.REMOTE,
meta: { meta: {
hasAuthHeader: !!authHeader, hasAuthHeader: !!authHeader,
@@ -266,7 +241,7 @@ export const useRemoteStore = createWithEqualityFn<SettingsSlice>()(
}, },
}); });
if (authHeader) { if (authHeader) {
logFn.debug(logMsg[LogCategory.REMOTE].sendingAuthentication, { logFn.debug('Sending authentication', {
category: LogCategory.REMOTE, category: LogCategory.REMOTE,
}); });
socket.send( socket.send(
@@ -280,7 +255,7 @@ export const useRemoteStore = createWithEqualityFn<SettingsSlice>()(
}); });
socket.addEventListener('close', (reason) => { socket.addEventListener('close', (reason) => {
logFn.debug(logMsg[LogCategory.REMOTE].webSocketClosed, { logFn.debug('WebSocket closed', {
category: LogCategory.REMOTE, category: LogCategory.REMOTE,
meta: { meta: {
code: reason.code, code: reason.code,
@@ -290,13 +265,13 @@ export const useRemoteStore = createWithEqualityFn<SettingsSlice>()(
}, },
}); });
if (reason.code === 4002 || reason.code === 4003) { if (reason.code === 4002 || reason.code === 4003) {
logFn.debug(logMsg[LogCategory.REMOTE].reloadingPage, { logFn.debug('Reloading page due to close code', {
category: LogCategory.REMOTE, category: LogCategory.REMOTE,
meta: { code: reason.code }, meta: { code: reason.code },
}); });
location.reload(); location.reload();
} else if (reason.code === 4000) { } else if (reason.code === 4000) {
logFn.warn(logMsg[LogCategory.REMOTE].serverIsDown, { logFn.warn('Server is down', {
category: LogCategory.REMOTE, category: LogCategory.REMOTE,
}); });
toast.warn({ toast.warn({
@@ -304,16 +279,13 @@ export const useRemoteStore = createWithEqualityFn<SettingsSlice>()(
title: 'Connection closed', title: 'Connection closed',
}); });
} else if (reason.code !== 4001 && !socket.natural) { } else if (reason.code !== 4001 && !socket.natural) {
logFn.error( logFn.error('Socket closed unexpectedly', {
logMsg[LogCategory.REMOTE].socketClosedUnexpectedly, category: LogCategory.REMOTE,
{ meta: {
category: LogCategory.REMOTE, code: reason.code,
meta: { reason: reason.reason,
code: reason.code,
reason: reason.reason,
},
}, },
); });
toast.error({ toast.error({
message: 'Socket closed for unexpected reason', message: 'Socket closed for unexpected reason',
title: 'Connection closed', title: 'Connection closed',
@@ -331,7 +303,7 @@ export const useRemoteStore = createWithEqualityFn<SettingsSlice>()(
send: (data: ClientEvent) => { send: (data: ClientEvent) => {
const socket = get().socket; const socket = get().socket;
if (socket) { if (socket) {
logFn.debug(logMsg[LogCategory.REMOTE].sendingEventToServer, { logFn.debug('Sending event to server', {
category: LogCategory.REMOTE, category: LogCategory.REMOTE,
meta: { meta: {
data: data, data: data,
@@ -341,7 +313,7 @@ export const useRemoteStore = createWithEqualityFn<SettingsSlice>()(
}); });
socket.send(JSON.stringify(data)); socket.send(JSON.stringify(data));
} else { } else {
logFn.warn(logMsg[LogCategory.REMOTE].cannotSendEvent, { logFn.warn('Cannot send event - socket not available', {
category: LogCategory.REMOTE, category: LogCategory.REMOTE,
meta: { event: data.event }, meta: { event: data.event },
}); });
+10
View File
@@ -4,6 +4,7 @@ import { NavidromeController } from '/@/renderer/api/navidrome/navidrome-control
import { SubsonicController } from '/@/renderer/api/subsonic/subsonic-controller'; import { SubsonicController } from '/@/renderer/api/subsonic/subsonic-controller';
import { mergeMusicFolderId } from '/@/renderer/api/utils-music-folder'; import { mergeMusicFolderId } from '/@/renderer/api/utils-music-folder';
import { getServerById, useAuthStore, useSettingsStore } from '/@/renderer/store'; import { getServerById, useAuthStore, useSettingsStore } from '/@/renderer/store';
import { LogCategory, logFn } from '/@/renderer/utils/logger';
import { toast } from '/@/shared/components/toast/toast'; import { toast } from '/@/shared/components/toast/toast';
import { import {
AuthenticationResponse, AuthenticationResponse,
@@ -31,6 +32,7 @@ const apiController = <K extends keyof ControllerEndpoint>(
const serverType = type || useAuthStore.getState().currentServer?.type; const serverType = type || useAuthStore.getState().currentServer?.type;
if (!serverType) { if (!serverType) {
logFn.warn('No server selected', { category: LogCategory.API });
toast.error({ toast.error({
message: i18n.t('error.serverNotSelectedError', { message: i18n.t('error.serverNotSelectedError', {
postProcess: 'sentenceCase', postProcess: 'sentenceCase',
@@ -43,6 +45,10 @@ const apiController = <K extends keyof ControllerEndpoint>(
const controllerFn = endpoints?.[serverType]?.[endpoint]; const controllerFn = endpoints?.[serverType]?.[endpoint];
if (typeof controllerFn !== 'function') { if (typeof controllerFn !== 'function') {
logFn.warn('Endpoint not implemented', {
category: LogCategory.API,
meta: { endpoint, serverType },
});
toast.error({ toast.error({
message: `Endpoint ${endpoint} is not implemented for ${serverType}`, message: `Endpoint ${endpoint} is not implemented for ${serverType}`,
title: i18n.t('error.apiRouteError', { postProcess: 'sentenceCase' }) as string, title: i18n.t('error.apiRouteError', { postProcess: 'sentenceCase' }) as string,
@@ -57,6 +63,10 @@ const apiController = <K extends keyof ControllerEndpoint>(
); );
} }
logFn.debug('API controller call', {
category: LogCategory.API,
meta: { endpoint, serverType },
});
return controllerFn; return controllerFn;
}; };
+17 -3
View File
@@ -8,6 +8,7 @@ import qs from 'qs';
import i18n from '/@/i18n/i18n'; import i18n from '/@/i18n/i18n';
import { authenticationFailure } from '/@/renderer/api/utils'; import { authenticationFailure } from '/@/renderer/api/utils';
import { useAuthStore } from '/@/renderer/store'; import { useAuthStore } from '/@/renderer/store';
import { LogCategory, logFn } from '/@/renderer/utils/logger';
import { getServerUrl } from '/@/renderer/utils/normalize-server-url'; import { getServerUrl } from '/@/renderer/utils/normalize-server-url';
import { ndType } from '/@/shared/api/navidrome/navidrome-types'; import { ndType } from '/@/shared/api/navidrome/navidrome-types';
import { resultWithHeaders } from '/@/shared/api/utils'; import { resultWithHeaders } from '/@/shared/api/utils';
@@ -367,11 +368,21 @@ axiosClient.interceptors.response.use(
}) })
.catch((newError: any) => { .catch((newError: any) => {
if (newError !== TIMEOUT_ERROR) { if (newError !== TIMEOUT_ERROR) {
console.error('Error when trying to reauthenticate: ', newError); logFn.error('Reauthentication failed', {
category: LogCategory.API,
meta: {
message: (newError as Error)?.message,
serverId: currentServer.id,
},
});
if (isAxiosError(newError) && newError.code === 'ERR_NETWORK') { if (isAxiosError(newError) && newError.code === 'ERR_NETWORK') {
console.log( logFn.info(
'Network error during reauthentication - preserving credentials', 'Network error during reauthentication - preserving credentials',
{
category: LogCategory.API,
meta: { serverId: currentServer.id },
},
); );
} else { } else {
limitedFail(currentServer); limitedFail(currentServer);
@@ -387,7 +398,10 @@ axiosClient.interceptors.response.use(
} }
if (isAxiosError(error) && error.code === 'ERR_NETWORK') { if (isAxiosError(error) && error.code === 'ERR_NETWORK') {
console.log('Network error during authentication - preserving credentials'); logFn.info('Network error during authentication - preserving credentials', {
category: LogCategory.API,
meta: { serverId: useAuthStore.getState().currentServer?.id },
});
} else { } else {
limitedFail(currentServer); limitedFail(currentServer);
} }
+5 -2
View File
@@ -1,16 +1,19 @@
import { useAuthStore } from '/@/renderer/store'; import { useAuthStore } from '/@/renderer/store';
import { LogCategory, logFn } from '/@/renderer/utils/logger';
import { toast } from '/@/shared/components/toast/toast'; import { toast } from '/@/shared/components/toast/toast';
import { ServerListItem } from '/@/shared/types/types'; import { ServerListItem } from '/@/shared/types/types';
export const authenticationFailure = (currentServer: null | ServerListItem) => { export const authenticationFailure = (currentServer: null | ServerListItem) => {
logFn.error('Token expired', {
category: LogCategory.API,
meta: { serverId: currentServer?.id },
});
toast.error({ toast.error({
message: 'Your session has expired.', message: 'Your session has expired.',
}); });
if (currentServer) { if (currentServer) {
const serverId = currentServer.id; const serverId = currentServer.id;
const token = currentServer.ndCredential;
console.error(`token is expired: ${token}`);
useAuthStore.getState().actions.updateServer(serverId, { ndCredential: undefined }); useAuthStore.getState().actions.updateServer(serverId, { ndCredential: undefined });
useAuthStore.getState().actions.setCurrentServer(null); useAuthStore.getState().actions.setCurrentServer(null);
} }
@@ -17,7 +17,6 @@ import {
useSettingsStore, useSettingsStore,
} from '/@/renderer/store'; } from '/@/renderer/store';
import { LogCategory, logFn } from '/@/renderer/utils/logger'; import { LogCategory, logFn } from '/@/renderer/utils/logger';
import { logMsg } from '/@/renderer/utils/logger-message';
import { LyricSource, ServerType } from '/@/shared/types/domain-types'; import { LyricSource, ServerType } from '/@/shared/types/domain-types';
import { FontType, Platform, PlayerStyle, PlayerType } from '/@/shared/types/types'; import { FontType, Platform, PlayerStyle, PlayerType } from '/@/shared/types/types';
@@ -270,7 +269,7 @@ export const useAppTracker = () => {
if (lastTrackedDate !== todayUTC) { if (lastTrackedDate !== todayUTC) {
appTrackerInFlight = true; appTrackerInFlight = true;
const properties = getProperties(); const properties = getProperties();
logFn.info(logMsg[LogCategory.ANALYTICS].appTracked, { logFn.info('Analytics sent', {
category: LogCategory.ANALYTICS, category: LogCategory.ANALYTICS,
meta: { properties, todayUTC }, meta: { properties, todayUTC },
}); });
@@ -290,7 +289,7 @@ export const useAppTracker = () => {
appTrackerLastSentDate = utcDate; appTrackerLastSentDate = utcDate;
localStorage.setItem('analytics_app_tracker_timestamp', utcDate); localStorage.setItem('analytics_app_tracker_timestamp', utcDate);
logFn.debug(logMsg[LogCategory.ANALYTICS].appTracked, { logFn.debug('Analytics sent', {
category: LogCategory.ANALYTICS, category: LogCategory.ANALYTICS,
meta: { properties }, meta: { properties },
}); });
@@ -5,7 +5,6 @@ import { useLocation } from 'react-router';
import { isAnalyticsDisabled } from '/@/renderer/features/analytics/hooks/use-analytics-disabled'; import { isAnalyticsDisabled } from '/@/renderer/features/analytics/hooks/use-analytics-disabled';
import { getRoutePattern } from '/@/renderer/features/analytics/utils/get-route-pattern'; import { getRoutePattern } from '/@/renderer/features/analytics/utils/get-route-pattern';
import { LogCategory, logFn } from '/@/renderer/utils/logger'; import { LogCategory, logFn } from '/@/renderer/utils/logger';
import { logMsg } from '/@/renderer/utils/logger-message';
const trackPageView = (routePattern: string) => { const trackPageView = (routePattern: string) => {
window.umami?.track((props) => ({ window.umami?.track((props) => ({
@@ -28,7 +27,7 @@ export const usePageTracker = () => {
trackPageViewMutation(routePattern, { trackPageViewMutation(routePattern, {
onSettled: () => { onSettled: () => {
logFn.debug(logMsg[LogCategory.ANALYTICS].pageViewTracked, { logFn.debug('Page view tracked', {
category: LogCategory.ANALYTICS, category: LogCategory.ANALYTICS,
meta: { route: routePattern }, meta: { route: routePattern },
}); });
@@ -21,7 +21,6 @@ import {
} from '/@/renderer/store'; } from '/@/renderer/store';
import { sentenceCase } from '/@/renderer/utils'; import { sentenceCase } from '/@/renderer/utils';
import { LogCategory, logFn } from '/@/renderer/utils/logger'; import { LogCategory, logFn } from '/@/renderer/utils/logger';
import { logMsg } from '/@/renderer/utils/logger-message';
import { useDebouncedCallback } from '/@/shared/hooks/use-debounced-callback'; import { useDebouncedCallback } from '/@/shared/hooks/use-debounced-callback';
import { LibraryItem, QueueSong, ServerType } from '/@/shared/types/domain-types'; import { LibraryItem, QueueSong, ServerType } from '/@/shared/types/domain-types';
import { PlayerStatus } from '/@/shared/types/types'; import { PlayerStatus } from '/@/shared/types/types';
@@ -90,7 +89,7 @@ export const useDiscordRpc = () => {
reason = 'paused_with_show_paused_disabled'; reason = 'paused_with_show_paused_disabled';
} }
logFn.debug(logMsg[LogCategory.EXTERNAL].discordRpcActivityCleared, { logFn.debug('Activity was cleared for Discord RPC', {
category: LogCategory.EXTERNAL, category: LogCategory.EXTERNAL,
meta: { meta: {
reason, reason,
@@ -128,7 +127,7 @@ export const useDiscordRpc = () => {
const isConnected = await discordRpc?.isConnected(); const isConnected = await discordRpc?.isConnected();
if (!isConnected) { if (!isConnected) {
logFn.debug(logMsg[LogCategory.EXTERNAL].discordRpcInitialized, { logFn.debug('Discord RPC was initialized', {
category: LogCategory.EXTERNAL, category: LogCategory.EXTERNAL,
meta: { clientId: discordSettings.clientId }, meta: { clientId: discordSettings.clientId },
}); });
@@ -136,7 +135,7 @@ export const useDiscordRpc = () => {
await discordRpc?.initialize(discordSettings.clientId); await discordRpc?.initialize(discordSettings.clientId);
} }
logFn.debug(logMsg[LogCategory.EXTERNAL].discordRpcSetActivity, { logFn.debug('Activity was set for Discord RPC', {
category: LogCategory.EXTERNAL, category: LogCategory.EXTERNAL,
meta: { meta: {
currentStatus: current[2], currentStatus: current[2],
@@ -168,7 +167,7 @@ export const useDiscordRpc = () => {
current[2] !== previous[2] current[2] !== previous[2]
) { ) {
if (trackChangedByState || trackChanged) { if (trackChangedByState || trackChanged) {
logFn.debug(logMsg[LogCategory.EXTERNAL].discordRpcTrackChanged, { logFn.debug('Track was changed for Discord RPC', {
category: LogCategory.EXTERNAL, category: LogCategory.EXTERNAL,
meta: { meta: {
artistName: song.artists?.[0]?.name, artistName: song.artists?.[0]?.name,
@@ -315,7 +314,7 @@ export const useDiscordRpc = () => {
// Initialize if needed // Initialize if needed
const isConnected = await discordRpc?.isConnected(); const isConnected = await discordRpc?.isConnected();
if (!isConnected) { if (!isConnected) {
logFn.debug(logMsg[LogCategory.EXTERNAL].discordRpcInitialized, { logFn.debug('Discord RPC was initialized', {
category: LogCategory.EXTERNAL, category: LogCategory.EXTERNAL,
meta: { meta: {
clientId: discordSettings.clientId, clientId: discordSettings.clientId,
@@ -327,7 +326,7 @@ export const useDiscordRpc = () => {
await discordRpc?.initialize(discordSettings.clientId); await discordRpc?.initialize(discordSettings.clientId);
} }
logFn.debug(logMsg[LogCategory.EXTERNAL].discordRpcSetActivity, { logFn.debug('Activity was set for Discord RPC', {
category: LogCategory.EXTERNAL, category: LogCategory.EXTERNAL,
meta: { meta: {
albumName: song.album, albumName: song.album,
@@ -347,7 +346,7 @@ export const useDiscordRpc = () => {
}); });
discordRpc?.setActivity(activity); discordRpc?.setActivity(activity);
} else { } else {
logFn.debug(logMsg[LogCategory.EXTERNAL].discordRpcUpdateSkipped, { logFn.debug('Activity was not updated for Discord RPC', {
category: LogCategory.EXTERNAL, category: LogCategory.EXTERNAL,
meta: { meta: {
currentStatus: current[2], currentStatus: current[2],
@@ -384,7 +383,7 @@ export const useDiscordRpc = () => {
// Quit Discord RPC if it was enabled and is now disabled // Quit Discord RPC if it was enabled and is now disabled
useEffect(() => { useEffect(() => {
if ((!discordSettings.enabled || privateMode) && Boolean(previousEnabledRef.current)) { if ((!discordSettings.enabled || privateMode) && Boolean(previousEnabledRef.current)) {
logFn.info(logMsg[LogCategory.EXTERNAL].discordRpcQuit, { logFn.info('Discord RPC was quit', {
category: LogCategory.EXTERNAL, category: LogCategory.EXTERNAL,
meta: { meta: {
enabled: discordSettings.enabled, enabled: discordSettings.enabled,
@@ -18,6 +18,7 @@ import { AnimatedPage } from '/@/renderer/features/shared/components/animated-pa
import { PageErrorBoundary } from '/@/renderer/features/shared/components/page-error-boundary'; import { PageErrorBoundary } from '/@/renderer/features/shared/components/page-error-boundary';
import { AppRoute } from '/@/renderer/router/routes'; import { AppRoute } from '/@/renderer/router/routes';
import { useAuthStoreActions, useCurrentServer } from '/@/renderer/store'; import { useAuthStoreActions, useCurrentServer } from '/@/renderer/store';
import { LogCategory, logFn } from '/@/renderer/utils/logger';
import { Button } from '/@/shared/components/button/button'; import { Button } from '/@/shared/components/button/button';
import { Center } from '/@/shared/components/center/center'; import { Center } from '/@/shared/components/center/center';
import { Code } from '/@/shared/components/code/code'; import { Code } from '/@/shared/components/code/code';
@@ -136,6 +137,10 @@ const LoginRoute = () => {
); );
if (!data) { if (!data) {
logFn.error('Login failed (no data returned)', {
category: LogCategory.SYSTEM,
meta: { serverName, serverType, serverUrl },
});
return toast.error({ return toast.error({
message: t('error.authenticationFailed', { postProcess: 'sentenceCase' }), message: t('error.authenticationFailed', { postProcess: 'sentenceCase' }),
}); });
@@ -159,6 +164,10 @@ const LoginRoute = () => {
addServer(serverItem); addServer(serverItem);
setCurrentServer(serverItem); setCurrentServer(serverItem);
logFn.info('Login successful', {
category: LogCategory.SYSTEM,
meta: { serverName, serverType, serverUrl, userId: data.userId },
});
toast.success({ toast.success({
message: t('form.addServer.success', { postProcess: 'sentenceCase' }), message: t('form.addServer.success', { postProcess: 'sentenceCase' }),
}); });
@@ -175,6 +184,10 @@ const LoginRoute = () => {
} }
} }
} catch (err: any) { } catch (err: any) {
logFn.error('Login failed', {
category: LogCategory.SYSTEM,
meta: { message: err?.message, serverName, serverType, serverUrl },
});
setIsLoading(false); setIsLoading(false);
return toast.error({ message: err?.message }); return toast.error({ message: err?.message });
} }
@@ -6,7 +6,6 @@ import { useCallback, useEffect, useImperativeHandle, useRef, useState } from 'r
import { AudioPlayer, PlayerOnProgressProps } from '/@/renderer/features/player/audio-player/types'; import { AudioPlayer, PlayerOnProgressProps } from '/@/renderer/features/player/audio-player/types';
import { convertToLogVolume } from '/@/renderer/features/player/audio-player/utils/player-utils'; import { convertToLogVolume } from '/@/renderer/features/player/audio-player/utils/player-utils';
import { LogCategory, logFn } from '/@/renderer/utils/logger'; import { LogCategory, logFn } from '/@/renderer/utils/logger';
import { logMsg } from '/@/renderer/utils/logger-message';
import { PlayerStatus } from '/@/shared/types/types'; import { PlayerStatus } from '/@/shared/types/types';
export interface WebPlayerEngineHandle extends AudioPlayer { export interface WebPlayerEngineHandle extends AudioPlayer {
@@ -160,7 +159,7 @@ export const WebPlayerEngine = (props: WebPlayerEngineProps) => {
const { error } = target; const { error } = target;
logFn.error(logMsg[LogCategory.PLAYER].playbackError, { logFn.error('An error occurred during playback', {
category: LogCategory.PLAYER, category: LogCategory.PLAYER,
meta: { error }, meta: { error },
}); });
@@ -19,7 +19,6 @@ import { playlistsQueries } from '/@/renderer/features/playlists/api/playlists-a
import { songsQueries } from '/@/renderer/features/songs/api/songs-api'; import { songsQueries } from '/@/renderer/features/songs/api/songs-api';
import { AddToQueueType, usePlayerActions, useSettingsStore } from '/@/renderer/store'; import { AddToQueueType, usePlayerActions, useSettingsStore } from '/@/renderer/store';
import { LogCategory, logFn } from '/@/renderer/utils/logger'; import { LogCategory, logFn } from '/@/renderer/utils/logger';
import { logMsg } from '/@/renderer/utils/logger-message';
import { shuffle as shuffleArray } from '/@/renderer/utils/shuffle'; import { shuffle as shuffleArray } from '/@/renderer/utils/shuffle';
import { sortSongsByFetchedOrder } from '/@/shared/api/utils'; import { sortSongsByFetchedOrder } from '/@/shared/api/utils';
import { Checkbox } from '/@/shared/components/checkbox/checkbox'; import { Checkbox } from '/@/shared/components/checkbox/checkbox';
@@ -202,7 +201,7 @@ export const PlayerProvider = ({ children }: { children: React.ReactNode }) => {
if (typeof type === 'object' && 'edge' in type && type.edge !== null) { if (typeof type === 'object' && 'edge' in type && type.edge !== null) {
const edge = type.edge === 'top' ? 'top' : 'bottom'; const edge = type.edge === 'top' ? 'top' : 'bottom';
logFn.debug(logMsg[LogCategory.PLAYER].addToQueueByData, { logFn.debug('Added to queue by data', {
category: LogCategory.PLAYER, category: LogCategory.PLAYER,
meta: { meta: {
data: data.length, data: data.length,
@@ -215,7 +214,7 @@ export const PlayerProvider = ({ children }: { children: React.ReactNode }) => {
storeActions.addToQueueByUniqueId(filteredData, type.uniqueId, edge, playSongId); storeActions.addToQueueByUniqueId(filteredData, type.uniqueId, edge, playSongId);
} else { } else {
logFn.debug(logMsg[LogCategory.PLAYER].addToQueueByType, { logFn.debug('Added to queue by type', {
category: LogCategory.PLAYER, category: LogCategory.PLAYER,
meta: { data: data.length, filtered: filteredData.length, type }, meta: { data: data.length, filtered: filteredData.length, type },
}); });
@@ -258,7 +257,7 @@ export const PlayerProvider = ({ children }: { children: React.ReactNode }) => {
}; };
try { try {
logFn.debug(logMsg[LogCategory.PLAYER].addToQueueByFetch, { logFn.debug('Added to queue by fetch', {
category: LogCategory.PLAYER, category: LogCategory.PLAYER,
meta: { ids: id, itemType, serverId, type }, meta: { ids: id, itemType, serverId, type },
}); });
@@ -324,7 +323,7 @@ export const PlayerProvider = ({ children }: { children: React.ReactNode }) => {
let toastId: null | string = null; let toastId: null | string = null;
let fetchId: null | string = null; let fetchId: null | string = null;
logFn.debug(logMsg[LogCategory.PLAYER].addToQueueByListQuery, { logFn.debug('Added to queue by list query', {
category: LogCategory.PLAYER, category: LogCategory.PLAYER,
meta: { itemType, query, serverId, type }, meta: { itemType, query, serverId, type },
}); });
@@ -405,7 +404,7 @@ export const PlayerProvider = ({ children }: { children: React.ReactNode }) => {
postProcess: 'sentenceCase', postProcess: 'sentenceCase',
}), }),
onClose: () => { onClose: () => {
logFn.debug(logMsg[LogCategory.PLAYER].cancelledFetch, { logFn.debug('Cancelled fetch', {
category: LogCategory.PLAYER, category: LogCategory.PLAYER,
meta: { itemType, serverId }, meta: { itemType, serverId },
}); });
@@ -505,7 +504,7 @@ export const PlayerProvider = ({ children }: { children: React.ReactNode }) => {
); );
const clearQueue = useCallback(() => { const clearQueue = useCallback(() => {
logFn.debug(logMsg[LogCategory.PLAYER].clearQueue, { logFn.debug('Cleared queue', {
category: LogCategory.PLAYER, category: LogCategory.PLAYER,
}); });
@@ -514,7 +513,7 @@ export const PlayerProvider = ({ children }: { children: React.ReactNode }) => {
const clearSelected = useCallback( const clearSelected = useCallback(
(items: QueueSong[]) => { (items: QueueSong[]) => {
logFn.debug(logMsg[LogCategory.PLAYER].clearSelected, { logFn.debug('Cleared selected', {
category: LogCategory.PLAYER, category: LogCategory.PLAYER,
meta: { items: items.length }, meta: { items: items.length },
}); });
@@ -526,7 +525,7 @@ export const PlayerProvider = ({ children }: { children: React.ReactNode }) => {
const decreaseVolume = useCallback( const decreaseVolume = useCallback(
(amount: number) => { (amount: number) => {
logFn.debug(logMsg[LogCategory.PLAYER].decreaseVolume, { logFn.debug('Decreased volume', {
category: LogCategory.PLAYER, category: LogCategory.PLAYER,
meta: { amount }, meta: { amount },
}); });
@@ -538,7 +537,7 @@ export const PlayerProvider = ({ children }: { children: React.ReactNode }) => {
const increaseVolume = useCallback( const increaseVolume = useCallback(
(amount: number) => { (amount: number) => {
logFn.debug(logMsg[LogCategory.PLAYER].increaseVolume, { logFn.debug('Increased volume', {
category: LogCategory.PLAYER, category: LogCategory.PLAYER,
meta: { amount }, meta: { amount },
}); });
@@ -549,7 +548,7 @@ export const PlayerProvider = ({ children }: { children: React.ReactNode }) => {
); );
const mediaNext = useCallback(() => { const mediaNext = useCallback(() => {
logFn.debug(logMsg[LogCategory.PLAYER].mediaNext, { logFn.debug('Media next', {
category: LogCategory.PLAYER, category: LogCategory.PLAYER,
}); });
@@ -557,7 +556,7 @@ export const PlayerProvider = ({ children }: { children: React.ReactNode }) => {
}, [storeActions]); }, [storeActions]);
const mediaPause = useCallback(() => { const mediaPause = useCallback(() => {
logFn.debug(logMsg[LogCategory.PLAYER].mediaPause, { logFn.debug('Media pause', {
category: LogCategory.PLAYER, category: LogCategory.PLAYER,
}); });
@@ -566,7 +565,7 @@ export const PlayerProvider = ({ children }: { children: React.ReactNode }) => {
const mediaPlay = useCallback( const mediaPlay = useCallback(
(id?: string) => { (id?: string) => {
logFn.debug(logMsg[LogCategory.PLAYER].mediaPlay, { logFn.debug('Media play', {
category: LogCategory.PLAYER, category: LogCategory.PLAYER,
meta: { id }, meta: { id },
}); });
@@ -578,7 +577,7 @@ export const PlayerProvider = ({ children }: { children: React.ReactNode }) => {
const mediaPlayByIndex = useCallback( const mediaPlayByIndex = useCallback(
(index: number) => { (index: number) => {
logFn.debug(logMsg[LogCategory.PLAYER].mediaPlayByIndex, { logFn.debug('Media play by index', {
category: LogCategory.PLAYER, category: LogCategory.PLAYER,
meta: { index }, meta: { index },
}); });
@@ -589,7 +588,7 @@ export const PlayerProvider = ({ children }: { children: React.ReactNode }) => {
); );
const mediaPrevious = useCallback(() => { const mediaPrevious = useCallback(() => {
logFn.debug(logMsg[LogCategory.PLAYER].mediaPrevious, { logFn.debug('Media previous', {
category: LogCategory.PLAYER, category: LogCategory.PLAYER,
}); });
@@ -597,7 +596,7 @@ export const PlayerProvider = ({ children }: { children: React.ReactNode }) => {
}, [storeActions]); }, [storeActions]);
const mediaStop = useCallback(() => { const mediaStop = useCallback(() => {
logFn.debug(logMsg[LogCategory.PLAYER].mediaStop, { logFn.debug('Media stop', {
category: LogCategory.PLAYER, category: LogCategory.PLAYER,
}); });
@@ -606,7 +605,7 @@ export const PlayerProvider = ({ children }: { children: React.ReactNode }) => {
const mediaSeekToTimestamp = useCallback( const mediaSeekToTimestamp = useCallback(
(timestamp: number) => { (timestamp: number) => {
logFn.debug(logMsg[LogCategory.PLAYER].mediaSeekToTimestamp, { logFn.debug('Media seek to timestamp', {
category: LogCategory.PLAYER, category: LogCategory.PLAYER,
meta: { timestamp }, meta: { timestamp },
}); });
@@ -617,7 +616,7 @@ export const PlayerProvider = ({ children }: { children: React.ReactNode }) => {
); );
const mediaSkipBackward = useCallback(() => { const mediaSkipBackward = useCallback(() => {
logFn.debug(logMsg[LogCategory.PLAYER].mediaSkipBackward, { logFn.debug('Media skip backward', {
category: LogCategory.PLAYER, category: LogCategory.PLAYER,
}); });
@@ -625,7 +624,7 @@ export const PlayerProvider = ({ children }: { children: React.ReactNode }) => {
}, [storeActions]); }, [storeActions]);
const mediaSkipForward = useCallback(() => { const mediaSkipForward = useCallback(() => {
logFn.debug(logMsg[LogCategory.PLAYER].mediaSkipForward, { logFn.debug('Media skip forward', {
category: LogCategory.PLAYER, category: LogCategory.PLAYER,
}); });
@@ -634,7 +633,7 @@ export const PlayerProvider = ({ children }: { children: React.ReactNode }) => {
const setQueue = useCallback( const setQueue = useCallback(
(data: Song[], index?: number, position?: number) => { (data: Song[], index?: number, position?: number) => {
logFn.debug(logMsg[LogCategory.PLAYER].setQueue, { logFn.debug('Set queue', {
category: LogCategory.PLAYER, category: LogCategory.PLAYER,
meta: { meta: {
data: data.length, data: data.length,
@@ -650,7 +649,7 @@ export const PlayerProvider = ({ children }: { children: React.ReactNode }) => {
const setSpeed = useCallback( const setSpeed = useCallback(
(speed: number) => { (speed: number) => {
logFn.debug(logMsg[LogCategory.PLAYER].setSpeed, { logFn.debug('Set speed', {
category: LogCategory.PLAYER, category: LogCategory.PLAYER,
meta: { speed }, meta: { speed },
}); });
@@ -661,7 +660,7 @@ export const PlayerProvider = ({ children }: { children: React.ReactNode }) => {
); );
const mediaToggleMute = useCallback(() => { const mediaToggleMute = useCallback(() => {
logFn.debug(logMsg[LogCategory.PLAYER].mediaToggleMute, { logFn.debug('Media toggle mute', {
category: LogCategory.PLAYER, category: LogCategory.PLAYER,
}); });
@@ -669,7 +668,7 @@ export const PlayerProvider = ({ children }: { children: React.ReactNode }) => {
}, [storeActions]); }, [storeActions]);
const mediaTogglePlayPause = useCallback(() => { const mediaTogglePlayPause = useCallback(() => {
logFn.debug(logMsg[LogCategory.PLAYER].mediaTogglePlayPause, { logFn.debug('Media toggle play pause', {
category: LogCategory.PLAYER, category: LogCategory.PLAYER,
}); });
@@ -678,7 +677,7 @@ export const PlayerProvider = ({ children }: { children: React.ReactNode }) => {
const moveSelectedTo = useCallback( const moveSelectedTo = useCallback(
(items: QueueSong[], edge: 'bottom' | 'top', uniqueId: string) => { (items: QueueSong[], edge: 'bottom' | 'top', uniqueId: string) => {
logFn.debug(logMsg[LogCategory.PLAYER].moveSelectedTo, { logFn.debug('Moved selected to', {
category: LogCategory.PLAYER, category: LogCategory.PLAYER,
meta: { edge, items, uniqueId }, meta: { edge, items, uniqueId },
}); });
@@ -690,7 +689,7 @@ export const PlayerProvider = ({ children }: { children: React.ReactNode }) => {
const moveSelectedToBottom = useCallback( const moveSelectedToBottom = useCallback(
(items: QueueSong[]) => { (items: QueueSong[]) => {
logFn.debug(logMsg[LogCategory.PLAYER].moveSelectedToBottom, { logFn.debug('Moved selected to bottom', {
category: LogCategory.PLAYER, category: LogCategory.PLAYER,
meta: { items }, meta: { items },
}); });
@@ -702,7 +701,7 @@ export const PlayerProvider = ({ children }: { children: React.ReactNode }) => {
const moveSelectedToNext = useCallback( const moveSelectedToNext = useCallback(
(items: QueueSong[]) => { (items: QueueSong[]) => {
logFn.debug(logMsg[LogCategory.PLAYER].moveSelectedToNext, { logFn.debug('Moved selected to next', {
category: LogCategory.PLAYER, category: LogCategory.PLAYER,
meta: { items }, meta: { items },
}); });
@@ -714,7 +713,7 @@ export const PlayerProvider = ({ children }: { children: React.ReactNode }) => {
const moveSelectedToTop = useCallback( const moveSelectedToTop = useCallback(
(items: QueueSong[]) => { (items: QueueSong[]) => {
logFn.debug(logMsg[LogCategory.PLAYER].moveSelectedToTop, { logFn.debug('Moved selected to top', {
category: LogCategory.PLAYER, category: LogCategory.PLAYER,
meta: { items }, meta: { items },
}); });
@@ -726,7 +725,7 @@ export const PlayerProvider = ({ children }: { children: React.ReactNode }) => {
const setVolume = useCallback( const setVolume = useCallback(
(volume: number) => { (volume: number) => {
logFn.debug(logMsg[LogCategory.PLAYER].setVolume, { logFn.debug('Set volume', {
category: LogCategory.PLAYER, category: LogCategory.PLAYER,
meta: { volume }, meta: { volume },
}); });
@@ -738,7 +737,7 @@ export const PlayerProvider = ({ children }: { children: React.ReactNode }) => {
const setRepeat = useCallback( const setRepeat = useCallback(
(repeat: PlayerRepeat) => { (repeat: PlayerRepeat) => {
logFn.debug(logMsg[LogCategory.PLAYER].setRepeat, { logFn.debug('Set repeat', {
category: LogCategory.PLAYER, category: LogCategory.PLAYER,
meta: { repeat }, meta: { repeat },
}); });
@@ -750,7 +749,7 @@ export const PlayerProvider = ({ children }: { children: React.ReactNode }) => {
const setShuffle = useCallback( const setShuffle = useCallback(
(shuffle: PlayerShuffle) => { (shuffle: PlayerShuffle) => {
logFn.debug(logMsg[LogCategory.PLAYER].setShuffle, { logFn.debug('Set shuffle', {
category: LogCategory.PLAYER, category: LogCategory.PLAYER,
meta: { shuffle }, meta: { shuffle },
}); });
@@ -761,7 +760,7 @@ export const PlayerProvider = ({ children }: { children: React.ReactNode }) => {
); );
const shuffle = useCallback(() => { const shuffle = useCallback(() => {
logFn.debug(logMsg[LogCategory.PLAYER].shuffle, { logFn.debug('Shuffle', {
category: LogCategory.PLAYER, category: LogCategory.PLAYER,
}); });
@@ -769,7 +768,7 @@ export const PlayerProvider = ({ children }: { children: React.ReactNode }) => {
}, [storeActions]); }, [storeActions]);
const shuffleAll = useCallback(() => { const shuffleAll = useCallback(() => {
logFn.debug(logMsg[LogCategory.PLAYER].shuffleAll, { logFn.debug('Shuffle all', {
category: LogCategory.PLAYER, category: LogCategory.PLAYER,
}); });
@@ -778,7 +777,7 @@ export const PlayerProvider = ({ children }: { children: React.ReactNode }) => {
const shuffleSelected = useCallback( const shuffleSelected = useCallback(
(items: QueueSong[]) => { (items: QueueSong[]) => {
logFn.debug(logMsg[LogCategory.PLAYER].shuffleSelected, { logFn.debug('Shuffle selected', {
category: LogCategory.PLAYER, category: LogCategory.PLAYER,
meta: { items }, meta: { items },
}); });
@@ -789,7 +788,7 @@ export const PlayerProvider = ({ children }: { children: React.ReactNode }) => {
); );
const toggleRepeat = useCallback(() => { const toggleRepeat = useCallback(() => {
logFn.debug(logMsg[LogCategory.PLAYER].toggleRepeat, { logFn.debug('Toggle repeat', {
category: LogCategory.PLAYER, category: LogCategory.PLAYER,
}); });
@@ -797,7 +796,7 @@ export const PlayerProvider = ({ children }: { children: React.ReactNode }) => {
}, [storeActions]); }, [storeActions]);
const toggleShuffle = useCallback(() => { const toggleShuffle = useCallback(() => {
logFn.debug(logMsg[LogCategory.PLAYER].toggleShuffle, { logFn.debug('Toggle shuffle', {
category: LogCategory.PLAYER, category: LogCategory.PLAYER,
}); });
@@ -16,7 +16,6 @@ import {
useSettingsStore, useSettingsStore,
} from '/@/renderer/store'; } from '/@/renderer/store';
import { LogCategory, logFn } from '/@/renderer/utils/logger'; import { LogCategory, logFn } from '/@/renderer/utils/logger';
import { logMsg } from '/@/renderer/utils/logger-message';
import { shuffleInPlace } from '/@/renderer/utils/shuffle'; import { shuffleInPlace } from '/@/renderer/utils/shuffle';
import { hasFeature } from '/@/shared/api/utils'; import { hasFeature } from '/@/shared/api/utils';
import { Played, Song, SongListSort, SortOrder } from '/@/shared/types/domain-types'; import { Played, Song, SongListSort, SortOrder } from '/@/shared/types/domain-types';
@@ -63,7 +62,7 @@ export const useAutoDJ = () => {
return; return;
} }
logFn.debug(logMsg[LogCategory.PLAYER].autoPlayTriggered, { logFn.debug('Auto play triggered', {
category: LogCategory.PLAYER, category: LogCategory.PLAYER,
meta: { remaining: properties.remaining, songId: properties.song?.id }, meta: { remaining: properties.remaining, songId: properties.song?.id },
}); });
@@ -207,7 +206,7 @@ export const useAutoDJ = () => {
songCount: songsToAdd.length, songCount: songsToAdd.length,
}); });
} catch (error) { } catch (error) {
logFn.error(logMsg[LogCategory.PLAYER].autoPlayFailed, { logFn.error('Auto play failed', {
category: LogCategory.PLAYER, category: LogCategory.PLAYER,
meta: { error: (error as Error).message, songId: properties.song?.id }, meta: { error: (error as Error).message, songId: properties.song?.id },
}); });
@@ -12,7 +12,6 @@ import {
useTimestampStoreBase, useTimestampStoreBase,
} from '/@/renderer/store'; } from '/@/renderer/store';
import { LogCategory, logFn } from '/@/renderer/utils/logger'; import { LogCategory, logFn } from '/@/renderer/utils/logger';
import { logMsg } from '/@/renderer/utils/logger-message';
import { LibraryItem, QueueSong, ServerType } from '/@/shared/types/domain-types'; import { LibraryItem, QueueSong, ServerType } from '/@/shared/types/domain-types';
import { PlayerStatus } from '/@/shared/types/types'; import { PlayerStatus } from '/@/shared/types/types';
@@ -131,7 +130,7 @@ export const useScrobble = () => {
}, },
{ {
onSuccess: () => { onSuccess: () => {
logFn.debug(logMsg[LogCategory.SCROBBLE].scrobbledTimeupdate, { logFn.debug('Scrobbled a timeupdate event', {
category: LogCategory.SCROBBLE, category: LogCategory.SCROBBLE,
meta: { meta: {
id: currentSong.id, id: currentSong.id,
@@ -173,7 +172,7 @@ export const useScrobble = () => {
}, },
{ {
onSuccess: () => { onSuccess: () => {
logFn.debug(logMsg[LogCategory.SCROBBLE].scrobbledSubmission, { logFn.debug('Scrobbled a submission event', {
category: LogCategory.SCROBBLE, category: LogCategory.SCROBBLE,
meta: { meta: {
id: currentSong.id, id: currentSong.id,
@@ -257,7 +256,7 @@ export const useScrobble = () => {
}, },
{ {
onSuccess: () => { onSuccess: () => {
logFn.debug(logMsg[LogCategory.SCROBBLE].scrobbledStart, { logFn.debug('Scrobbled a start event', {
category: LogCategory.SCROBBLE, category: LogCategory.SCROBBLE,
meta: { meta: {
id: currentSong.id, id: currentSong.id,
@@ -319,7 +318,7 @@ export const useScrobble = () => {
}, },
{ {
onSuccess: () => { onSuccess: () => {
logFn.debug(logMsg[LogCategory.SCROBBLE].scrobbledTimeupdate, { logFn.debug('Scrobbled a timeupdate event', {
category: LogCategory.SCROBBLE, category: LogCategory.SCROBBLE,
meta: { meta: {
id: currentSong.id, id: currentSong.id,
@@ -367,7 +366,7 @@ export const useScrobble = () => {
}, },
{ {
onSuccess: () => { onSuccess: () => {
logFn.debug(logMsg[LogCategory.SCROBBLE].scrobbledPause, { logFn.debug('Scrobbled a pause event', {
category: LogCategory.SCROBBLE, category: LogCategory.SCROBBLE,
meta: { meta: {
id: currentSong.id, id: currentSong.id,
@@ -393,7 +392,7 @@ export const useScrobble = () => {
}, },
{ {
onSuccess: () => { onSuccess: () => {
logFn.debug(logMsg[LogCategory.SCROBBLE].scrobbledUnpause, { logFn.debug('Scrobbled an unpause event', {
category: LogCategory.SCROBBLE, category: LogCategory.SCROBBLE,
meta: { meta: {
id: currentSong.id, id: currentSong.id,
@@ -436,7 +435,7 @@ export const useScrobble = () => {
}, },
{ {
onSuccess: () => { onSuccess: () => {
logFn.debug(logMsg[LogCategory.SCROBBLE].scrobbledStart, { logFn.debug('Scrobbled a start event', {
category: LogCategory.SCROBBLE, category: LogCategory.SCROBBLE,
meta: { meta: {
id: currentSong.id, id: currentSong.id,
+1 -2
View File
@@ -5,7 +5,6 @@ import { queryKeys } from '/@/renderer/api/query-keys';
import { folderQueries } from '/@/renderer/features/folders/api/folder-api'; import { folderQueries } from '/@/renderer/features/folders/api/folder-api';
import { PlayerFilter, useSettingsStore } from '/@/renderer/store'; import { PlayerFilter, useSettingsStore } from '/@/renderer/store';
import { LogCategory, logFn } from '/@/renderer/utils/logger'; import { LogCategory, logFn } from '/@/renderer/utils/logger';
import { logMsg } from '/@/renderer/utils/logger-message';
import { sortSongList } from '/@/shared/api/utils'; import { sortSongList } from '/@/shared/api/utils';
import { import {
PlaylistSongListQuery, PlaylistSongListQuery,
@@ -434,7 +433,7 @@ export const filterSongsByPlayerFilters = (songs: Song[], filters: PlayerFilter[
}); });
if (filteredSongs.length > 0) { if (filteredSongs.length > 0) {
logFn.debug(logMsg[LogCategory.PLAYER].playerFiltersApplied, { logFn.debug('Player filters applied', {
category: LogCategory.PLAYER, category: LogCategory.PLAYER,
meta: { meta: {
filteredCount: filteredSongs.length, filteredCount: filteredSongs.length,
@@ -6,6 +6,7 @@ import { queryKeys } from '/@/renderer/api/query-keys';
import { useRecentPlaylists } from '/@/renderer/features/playlists/hooks/use-recent-playlists'; import { useRecentPlaylists } from '/@/renderer/features/playlists/hooks/use-recent-playlists';
import { MutationHookArgs } from '/@/renderer/lib/react-query'; import { MutationHookArgs } from '/@/renderer/lib/react-query';
import { useCurrentServerId } from '/@/renderer/store'; import { useCurrentServerId } from '/@/renderer/store';
import { LogCategory, logFn } from '/@/renderer/utils/logger';
import { AddToPlaylistArgs, AddToPlaylistResponse } from '/@/shared/types/domain-types'; import { AddToPlaylistArgs, AddToPlaylistResponse } from '/@/shared/types/domain-types';
export const useAddToPlaylist = (args: MutationHookArgs) => { export const useAddToPlaylist = (args: MutationHookArgs) => {
@@ -22,6 +23,17 @@ export const useAddToPlaylist = (args: MutationHookArgs) => {
apiClientProps: { serverId: args.apiClientProps.serverId }, apiClientProps: { serverId: args.apiClientProps.serverId },
}); });
}, },
onError: (error, variables) => {
logFn.error('Add to playlist failed', {
category: LogCategory.API,
meta: {
message: error?.message,
playlistId: variables.query.id,
serverId: variables.apiClientProps.serverId,
},
});
options?.onError?.(error);
},
onSuccess: (_data, variables, context) => { onSuccess: (_data, variables, context) => {
const { apiClientProps } = variables; const { apiClientProps } = variables;
const serverId = apiClientProps.serverId; const serverId = apiClientProps.serverId;
@@ -4,6 +4,7 @@ import { AxiosError } from 'axios';
import { api } from '/@/renderer/api'; import { api } from '/@/renderer/api';
import { queryKeys } from '/@/renderer/api/query-keys'; import { queryKeys } from '/@/renderer/api/query-keys';
import { MutationHookArgs } from '/@/renderer/lib/react-query'; import { MutationHookArgs } from '/@/renderer/lib/react-query';
import { LogCategory, logFn } from '/@/renderer/utils/logger';
import { CreatePlaylistArgs, CreatePlaylistResponse } from '/@/shared/types/domain-types'; import { CreatePlaylistArgs, CreatePlaylistResponse } from '/@/shared/types/domain-types';
export const useCreatePlaylist = (args: MutationHookArgs) => { export const useCreatePlaylist = (args: MutationHookArgs) => {
@@ -17,6 +18,16 @@ export const useCreatePlaylist = (args: MutationHookArgs) => {
apiClientProps: { serverId: args.apiClientProps.serverId }, apiClientProps: { serverId: args.apiClientProps.serverId },
}); });
}, },
onError: (error, variables) => {
logFn.error('Create playlist failed', {
category: LogCategory.API,
meta: {
message: error?.message,
serverId: variables.apiClientProps.serverId,
},
});
options?.onError?.(error);
},
onSuccess: (_args, variables) => { onSuccess: (_args, variables) => {
queryClient.invalidateQueries({ queryClient.invalidateQueries({
exact: false, exact: false,
@@ -9,6 +9,7 @@ import {
restorePlaylistQueryData, restorePlaylistQueryData,
} from '/@/renderer/features/playlists/mutations/playlist-optimistic-updates'; } from '/@/renderer/features/playlists/mutations/playlist-optimistic-updates';
import { MutationHookArgs } from '/@/renderer/lib/react-query'; import { MutationHookArgs } from '/@/renderer/lib/react-query';
import { LogCategory, logFn } from '/@/renderer/utils/logger';
import { DeletePlaylistArgs, DeletePlaylistResponse } from '/@/shared/types/domain-types'; import { DeletePlaylistArgs, DeletePlaylistResponse } from '/@/shared/types/domain-types';
export const useDeletePlaylist = (args: MutationHookArgs) => { export const useDeletePlaylist = (args: MutationHookArgs) => {
@@ -24,6 +25,14 @@ export const useDeletePlaylist = (args: MutationHookArgs) => {
}); });
}, },
onError: (_error, _variables, context) => { onError: (_error, _variables, context) => {
logFn.error('Delete playlist failed', {
category: LogCategory.API,
meta: {
message: _error?.message,
playlistId: _variables.query.id,
serverId: _variables.apiClientProps.serverId,
},
});
if (context) { if (context) {
restorePlaylistQueryData(queryClient, context); restorePlaylistQueryData(queryClient, context);
} }
@@ -4,6 +4,7 @@ import { AxiosError } from 'axios';
import { api } from '/@/renderer/api'; import { api } from '/@/renderer/api';
import { queryKeys } from '/@/renderer/api/query-keys'; import { queryKeys } from '/@/renderer/api/query-keys';
import { MutationOptions } from '/@/renderer/lib/react-query'; import { MutationOptions } from '/@/renderer/lib/react-query';
import { LogCategory, logFn } from '/@/renderer/utils/logger';
import { RemoveFromPlaylistArgs, RemoveFromPlaylistResponse } from '/@/shared/types/domain-types'; import { RemoveFromPlaylistArgs, RemoveFromPlaylistResponse } from '/@/shared/types/domain-types';
export const useRemoveFromPlaylist = (options?: MutationOptions) => { export const useRemoveFromPlaylist = (options?: MutationOptions) => {
@@ -16,6 +17,17 @@ export const useRemoveFromPlaylist = (options?: MutationOptions) => {
apiClientProps: { serverId: args.apiClientProps.serverId }, apiClientProps: { serverId: args.apiClientProps.serverId },
}); });
}, },
onError: (error, variables) => {
logFn.error('Remove from playlist failed', {
category: LogCategory.API,
meta: {
message: error?.message,
playlistId: variables.query.id,
serverId: variables.apiClientProps.serverId,
},
});
options?.onError?.(error);
},
onSuccess: (_data, variables) => { onSuccess: (_data, variables) => {
const { apiClientProps } = variables; const { apiClientProps } = variables;
const serverId = apiClientProps.serverId; const serverId = apiClientProps.serverId;
@@ -6,6 +6,7 @@ import { queryKeys } from '/@/renderer/api/query-keys';
import { useRecentPlaylists } from '/@/renderer/features/playlists/hooks/use-recent-playlists'; import { useRecentPlaylists } from '/@/renderer/features/playlists/hooks/use-recent-playlists';
import { MutationHookArgs } from '/@/renderer/lib/react-query'; import { MutationHookArgs } from '/@/renderer/lib/react-query';
import { useCurrentServerId } from '/@/renderer/store'; import { useCurrentServerId } from '/@/renderer/store';
import { LogCategory, logFn } from '/@/renderer/utils/logger';
import { ReplacePlaylistArgs, ReplacePlaylistResponse } from '/@/shared/types/domain-types'; import { ReplacePlaylistArgs, ReplacePlaylistResponse } from '/@/shared/types/domain-types';
export const useReplacePlaylist = (args: MutationHookArgs) => { export const useReplacePlaylist = (args: MutationHookArgs) => {
@@ -22,6 +23,17 @@ export const useReplacePlaylist = (args: MutationHookArgs) => {
apiClientProps: { serverId: args.apiClientProps.serverId }, apiClientProps: { serverId: args.apiClientProps.serverId },
}); });
}, },
onError: (error, variables) => {
logFn.error('Replace playlist failed', {
category: LogCategory.API,
meta: {
message: error?.message,
playlistId: variables.query.id,
serverId: variables.apiClientProps.serverId,
},
});
options?.onError?.(error);
},
onSuccess: (_data, variables, context) => { onSuccess: (_data, variables, context) => {
const { apiClientProps } = variables; const { apiClientProps } = variables;
const serverId = apiClientProps.serverId; const serverId = apiClientProps.serverId;
@@ -4,6 +4,7 @@ import { AxiosError } from 'axios';
import { api } from '/@/renderer/api'; import { api } from '/@/renderer/api';
import { queryKeys } from '/@/renderer/api/query-keys'; import { queryKeys } from '/@/renderer/api/query-keys';
import { MutationHookArgs } from '/@/renderer/lib/react-query'; import { MutationHookArgs } from '/@/renderer/lib/react-query';
import { LogCategory, logFn } from '/@/renderer/utils/logger';
import { UpdatePlaylistArgs, UpdatePlaylistResponse } from '/@/shared/types/domain-types'; import { UpdatePlaylistArgs, UpdatePlaylistResponse } from '/@/shared/types/domain-types';
export const useUpdatePlaylist = (args: MutationHookArgs) => { export const useUpdatePlaylist = (args: MutationHookArgs) => {
@@ -17,6 +18,17 @@ export const useUpdatePlaylist = (args: MutationHookArgs) => {
apiClientProps: { serverId: args.apiClientProps.serverId }, apiClientProps: { serverId: args.apiClientProps.serverId },
}); });
}, },
onError: (error, variables) => {
logFn.error('Update playlist failed', {
category: LogCategory.API,
meta: {
message: error?.message,
playlistId: variables.query?.id,
serverId: variables.apiClientProps.serverId,
},
});
options?.onError?.(error);
},
onSuccess: (_data, variables) => { onSuccess: (_data, variables) => {
const { apiClientProps, query } = variables; const { apiClientProps, query } = variables;
const serverId = apiClientProps.serverId; const serverId = apiClientProps.serverId;
@@ -4,8 +4,7 @@ import { useTranslation } from 'react-i18next';
import { useUpdateRadioStation } from '/@/renderer/features/radio/mutations/update-radio-station-mutation'; import { useUpdateRadioStation } from '/@/renderer/features/radio/mutations/update-radio-station-mutation';
import { useCurrentServer } from '/@/renderer/store'; import { useCurrentServer } from '/@/renderer/store';
import { logFn } from '/@/renderer/utils/logger'; import { LogCategory, logFn } from '/@/renderer/utils/logger';
import { logMsg } from '/@/renderer/utils/logger-message';
import { Group } from '/@/shared/components/group/group'; import { Group } from '/@/shared/components/group/group';
import { closeAllModals, openModal } from '/@/shared/components/modal/modal'; import { closeAllModals, openModal } from '/@/shared/components/modal/modal';
import { ModalButton } from '/@/shared/components/modal/model-shared'; import { ModalButton } from '/@/shared/components/modal/model-shared';
@@ -48,7 +47,8 @@ export const EditRadioStationForm = ({ onCancel, station }: EditRadioStationForm
}, },
{ {
onError: (error) => { onError: (error) => {
logFn.error(logMsg.other.error, { logFn.error('An error occurred', {
category: LogCategory.OTHER,
meta: { error: error as Error }, meta: { error: error as Error },
}); });
@@ -4,6 +4,7 @@ import { AxiosError } from 'axios';
import { api } from '/@/renderer/api'; import { api } from '/@/renderer/api';
import { queryKeys } from '/@/renderer/api/query-keys'; import { queryKeys } from '/@/renderer/api/query-keys';
import { MutationHookArgs } from '/@/renderer/lib/react-query'; import { MutationHookArgs } from '/@/renderer/lib/react-query';
import { LogCategory, logFn } from '/@/renderer/utils/logger';
import { import {
CreateInternetRadioStationArgs, CreateInternetRadioStationArgs,
CreateInternetRadioStationResponse, CreateInternetRadioStationResponse,
@@ -25,6 +26,16 @@ export const useCreateRadioStation = (args: MutationHookArgs) => {
apiClientProps: { serverId: args.apiClientProps.serverId }, apiClientProps: { serverId: args.apiClientProps.serverId },
}); });
}, },
onError: (error, variables) => {
logFn.error('Create radio station failed', {
category: LogCategory.API,
meta: {
message: error?.message,
serverId: variables.apiClientProps.serverId,
},
});
options?.onError?.(error);
},
onSuccess: (_args, variables) => { onSuccess: (_args, variables) => {
queryClient.invalidateQueries({ queryClient.invalidateQueries({
exact: false, exact: false,
@@ -4,6 +4,7 @@ import { AxiosError } from 'axios';
import { api } from '/@/renderer/api'; import { api } from '/@/renderer/api';
import { queryKeys } from '/@/renderer/api/query-keys'; import { queryKeys } from '/@/renderer/api/query-keys';
import { MutationHookArgs } from '/@/renderer/lib/react-query'; import { MutationHookArgs } from '/@/renderer/lib/react-query';
import { LogCategory, logFn } from '/@/renderer/utils/logger';
import { import {
DeleteInternetRadioStationArgs, DeleteInternetRadioStationArgs,
DeleteInternetRadioStationResponse, DeleteInternetRadioStationResponse,
@@ -25,6 +26,17 @@ export const useDeleteRadioStation = (args: MutationHookArgs) => {
apiClientProps: { serverId: args.apiClientProps.serverId }, apiClientProps: { serverId: args.apiClientProps.serverId },
}); });
}, },
onError: (error, variables) => {
logFn.error('Delete radio station failed', {
category: LogCategory.API,
meta: {
message: error?.message,
serverId: variables.apiClientProps.serverId,
stationId: variables.query?.id,
},
});
options?.onError?.(error);
},
onSuccess: (_args, variables) => { onSuccess: (_args, variables) => {
queryClient.invalidateQueries({ queryClient.invalidateQueries({
exact: false, exact: false,
@@ -4,6 +4,7 @@ import { AxiosError } from 'axios';
import { api } from '/@/renderer/api'; import { api } from '/@/renderer/api';
import { queryKeys } from '/@/renderer/api/query-keys'; import { queryKeys } from '/@/renderer/api/query-keys';
import { MutationHookArgs } from '/@/renderer/lib/react-query'; import { MutationHookArgs } from '/@/renderer/lib/react-query';
import { LogCategory, logFn } from '/@/renderer/utils/logger';
import { import {
UpdateInternetRadioStationArgs, UpdateInternetRadioStationArgs,
UpdateInternetRadioStationResponse, UpdateInternetRadioStationResponse,
@@ -25,6 +26,17 @@ export const useUpdateRadioStation = (args: MutationHookArgs) => {
apiClientProps: { serverId: args.apiClientProps.serverId }, apiClientProps: { serverId: args.apiClientProps.serverId },
}); });
}, },
onError: (error, variables) => {
logFn.error('Update radio station failed', {
category: LogCategory.API,
meta: {
message: error?.message,
serverId: variables.apiClientProps.serverId,
stationId: variables.query?.id,
},
});
options?.onError?.(error);
},
onSuccess: (_args, variables) => { onSuccess: (_args, variables) => {
queryClient.invalidateQueries({ queryClient.invalidateQueries({
exact: false, exact: false,
@@ -8,7 +8,6 @@ import { useCreateFavorite } from '/@/renderer/features/shared/mutations/create-
import { useDeleteFavorite } from '/@/renderer/features/shared/mutations/delete-favorite-mutation'; import { useDeleteFavorite } from '/@/renderer/features/shared/mutations/delete-favorite-mutation';
import { usePlayerActions, usePlayerStore, useRemoteSettings } from '/@/renderer/store'; import { usePlayerActions, usePlayerStore, useRemoteSettings } from '/@/renderer/store';
import { LogCategory, logFn } from '/@/renderer/utils/logger'; import { LogCategory, logFn } from '/@/renderer/utils/logger';
import { logMsg } from '/@/renderer/utils/logger-message';
import { toast } from '/@/shared/components/toast/toast'; import { toast } from '/@/shared/components/toast/toast';
import { LibraryItem } from '/@/shared/types/domain-types'; import { LibraryItem } from '/@/shared/types/domain-types';
import { PlayerShuffle } from '/@/shared/types/types'; import { PlayerShuffle } from '/@/shared/types/types';
@@ -33,7 +32,7 @@ export const useRemote = () => {
return; return;
} }
logFn.debug(logMsg[LogCategory.REMOTE].initializingRemoteSettings, { logFn.debug('Initializing remote settings', {
category: LogCategory.REMOTE, category: LogCategory.REMOTE,
meta: { meta: {
enabled: remoteSettings.enabled, enabled: remoteSettings.enabled,
@@ -50,7 +49,7 @@ export const useRemote = () => {
remoteSettings.password, remoteSettings.password,
) )
.catch((error) => { .catch((error) => {
logFn.error(logMsg[LogCategory.REMOTE].failedToEnableRemote, { logFn.error('Failed to enable remote', {
category: LogCategory.REMOTE, category: LogCategory.REMOTE,
meta: { error }, meta: { error },
}); });
@@ -66,7 +65,7 @@ export const useRemote = () => {
} }
remote.requestPosition((_e: unknown, data: { position: number }) => { remote.requestPosition((_e: unknown, data: { position: number }) => {
logFn.debug(logMsg[LogCategory.REMOTE].requestPositionReceived, { logFn.debug('Request position received', {
category: LogCategory.REMOTE, category: LogCategory.REMOTE,
meta: { position: data.position }, meta: { position: data.position },
}); });
@@ -75,7 +74,7 @@ export const useRemote = () => {
}); });
remote.requestSeek((_e: unknown, data: { offset: number }) => { remote.requestSeek((_e: unknown, data: { offset: number }) => {
logFn.debug(logMsg[LogCategory.REMOTE].requestSeekReceived, { logFn.debug('Request seek received', {
category: LogCategory.REMOTE, category: LogCategory.REMOTE,
meta: { offset: data.offset }, meta: { offset: data.offset },
}); });
@@ -84,7 +83,7 @@ export const useRemote = () => {
remote.requestRating( remote.requestRating(
(_e: unknown, data: { id: string; rating: number; serverId: string }) => { (_e: unknown, data: { id: string; rating: number; serverId: string }) => {
logFn.debug(logMsg[LogCategory.REMOTE].requestRatingReceived, { logFn.debug('Request rating received', {
category: LogCategory.REMOTE, category: LogCategory.REMOTE,
meta: { id: data.id, rating: data.rating, serverId: data.serverId }, meta: { id: data.id, rating: data.rating, serverId: data.serverId },
}); });
@@ -93,7 +92,7 @@ export const useRemote = () => {
); );
remote.requestVolume((_e: unknown, data: { volume: number }) => { remote.requestVolume((_e: unknown, data: { volume: number }) => {
logFn.debug(logMsg[LogCategory.REMOTE].requestVolumeReceived, { logFn.debug('Request volume received', {
category: LogCategory.REMOTE, category: LogCategory.REMOTE,
meta: { volume: data.volume }, meta: { volume: data.volume },
}); });
@@ -102,7 +101,7 @@ export const useRemote = () => {
remote.requestFavorite( remote.requestFavorite(
(_e: unknown, data: { favorite: boolean; id: string; serverId: string }) => { (_e: unknown, data: { favorite: boolean; id: string; serverId: string }) => {
logFn.debug(logMsg[LogCategory.REMOTE].requestFavoriteReceived, { logFn.debug('Request favorite received', {
category: LogCategory.REMOTE, category: LogCategory.REMOTE,
meta: { favorite: data.favorite, id: data.id, serverId: data.serverId }, meta: { favorite: data.favorite, id: data.id, serverId: data.serverId },
}); });
@@ -148,7 +147,7 @@ export const useRemote = () => {
const currentSong = player.getCurrentSong(); const currentSong = player.getCurrentSong();
if (currentSong) { if (currentSong) {
logFn.debug(logMsg[LogCategory.REMOTE].sendingInitialSong, { logFn.debug('Sending initial song', {
category: LogCategory.REMOTE, category: LogCategory.REMOTE,
meta: { meta: {
artistName: currentSong.artistName, artistName: currentSong.artistName,
@@ -178,7 +177,7 @@ export const useRemote = () => {
return; return;
} }
logFn.debug(logMsg[LogCategory.REMOTE].updateSongSent, { logFn.debug('Update song sent', {
category: LogCategory.REMOTE, category: LogCategory.REMOTE,
meta: { meta: {
artistName: properties.song?.artistName, artistName: properties.song?.artistName,
@@ -209,7 +208,7 @@ export const useRemote = () => {
return; return;
} }
logFn.debug(logMsg[LogCategory.REMOTE].updatePositionSent, { logFn.debug('Update position sent', {
category: LogCategory.REMOTE, category: LogCategory.REMOTE,
meta: { timestamp: properties.timestamp }, meta: { timestamp: properties.timestamp },
}); });
@@ -220,7 +219,7 @@ export const useRemote = () => {
return; return;
} }
logFn.debug(logMsg[LogCategory.REMOTE].updateRepeatSent, { logFn.debug('Update repeat sent', {
category: LogCategory.REMOTE, category: LogCategory.REMOTE,
meta: { repeat: properties.repeat }, meta: { repeat: properties.repeat },
}); });
@@ -232,7 +231,7 @@ export const useRemote = () => {
} }
const isShuffleEnabled = properties.shuffle !== PlayerShuffle.NONE; const isShuffleEnabled = properties.shuffle !== PlayerShuffle.NONE;
logFn.debug(logMsg[LogCategory.REMOTE].updateShuffleSent, { logFn.debug('Update shuffle sent', {
category: LogCategory.REMOTE, category: LogCategory.REMOTE,
meta: { isShuffleEnabled, shuffle: properties.shuffle }, meta: { isShuffleEnabled, shuffle: properties.shuffle },
}); });
@@ -243,7 +242,7 @@ export const useRemote = () => {
return; return;
} }
logFn.debug(logMsg[LogCategory.REMOTE].updatePlaybackSent, { logFn.debug('Update playback sent', {
category: LogCategory.REMOTE, category: LogCategory.REMOTE,
meta: { status: properties.status }, meta: { status: properties.status },
}); });
@@ -254,7 +253,7 @@ export const useRemote = () => {
return; return;
} }
logFn.debug(logMsg[LogCategory.REMOTE].updateVolumeSent, { logFn.debug('Update volume sent', {
category: LogCategory.REMOTE, category: LogCategory.REMOTE,
meta: { volume: properties.volume }, meta: { volume: properties.volume },
}); });
@@ -265,7 +264,7 @@ export const useRemote = () => {
return; return;
} }
logFn.debug(logMsg[LogCategory.REMOTE].updateFavoriteSent, { logFn.debug('Update favorite sent', {
category: LogCategory.REMOTE, category: LogCategory.REMOTE,
meta: { meta: {
favorite: properties.favorite, favorite: properties.favorite,
@@ -280,7 +279,7 @@ export const useRemote = () => {
return; return;
} }
logFn.debug(logMsg[LogCategory.REMOTE].updateRatingSent, { logFn.debug('Update rating sent', {
category: LogCategory.REMOTE, category: LogCategory.REMOTE,
meta: { meta: {
id: properties.id, id: properties.id,
@@ -14,6 +14,7 @@ import NavidromeIcon from '/@/renderer/features/servers/assets/navidrome.png';
import SubsonicIcon from '/@/renderer/features/servers/assets/opensubsonic.png'; import SubsonicIcon from '/@/renderer/features/servers/assets/opensubsonic.png';
import { IgnoreCorsSslSwitches } from '/@/renderer/features/servers/components/ignore-cors-ssl-switches'; import { IgnoreCorsSslSwitches } from '/@/renderer/features/servers/components/ignore-cors-ssl-switches';
import { useAuthStoreActions } from '/@/renderer/store'; import { useAuthStoreActions } from '/@/renderer/store';
import { LogCategory, logFn } from '/@/renderer/utils/logger';
import { Checkbox } from '/@/shared/components/checkbox/checkbox'; import { Checkbox } from '/@/shared/components/checkbox/checkbox';
import { Divider } from '/@/shared/components/divider/divider'; import { Divider } from '/@/shared/components/divider/divider';
import { Group } from '/@/shared/components/group/group'; import { Group } from '/@/shared/components/group/group';
@@ -149,6 +150,10 @@ export const AddServerForm = ({ onCancel }: AddServerFormProps) => {
); );
if (!data) { if (!data) {
logFn.error('Add server failed (no data returned)', {
category: LogCategory.SYSTEM,
meta: { name: values.name, serverType: values.type, url: values.url },
});
return toast.error({ return toast.error({
message: t('error.authenticationFailed', { postProcess: 'sentenceCase' }), message: t('error.authenticationFailed', { postProcess: 'sentenceCase' }),
}); });
@@ -189,6 +194,15 @@ export const AddServerForm = ({ onCancel }: AddServerFormProps) => {
setCurrentServer(serverItem); setCurrentServer(serverItem);
closeAllModals(); closeAllModals();
logFn.info('Add server successful', {
category: LogCategory.SYSTEM,
meta: {
name: values.name,
serverId: serverItem.id,
serverType: values.type,
url: values.url,
},
});
toast.success({ toast.success({
message: t('form.addServer.success', { postProcess: 'sentenceCase' }), message: t('form.addServer.success', { postProcess: 'sentenceCase' }),
}); });
@@ -205,6 +219,15 @@ export const AddServerForm = ({ onCancel }: AddServerFormProps) => {
} }
} }
} catch (err: any) { } catch (err: any) {
logFn.error('Add server failed', {
category: LogCategory.SYSTEM,
meta: {
message: err?.message,
name: values.name,
serverType: values.type,
url: values.url,
},
});
setIsLoading(false); setIsLoading(false);
return toast.error({ message: err?.message }); return toast.error({ message: err?.message });
} }
@@ -1,6 +1,7 @@
import { ErrorBoundary } from 'react-error-boundary'; import { ErrorBoundary } from 'react-error-boundary';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { LogCategory, logFn } from '/@/renderer/utils/logger';
import { Box } from '/@/shared/components/box/box'; import { Box } from '/@/shared/components/box/box';
import { Button } from '/@/shared/components/button/button'; import { Button } from '/@/shared/components/button/button';
import { Center } from '/@/shared/components/center/center'; import { Center } from '/@/shared/components/center/center';
@@ -43,5 +44,22 @@ interface ComponentErrorBoundaryProps {
} }
export const ComponentErrorBoundary = ({ children }: ComponentErrorBoundaryProps) => { export const ComponentErrorBoundary = ({ children }: ComponentErrorBoundaryProps) => {
return <ErrorBoundary FallbackComponent={ComponentErrorFallback}>{children}</ErrorBoundary>; return (
<ErrorBoundary
FallbackComponent={ComponentErrorFallback}
onError={(error, errorInfo) => {
logFn.error('Component error boundary caught an error', {
category: LogCategory.OTHER,
meta: {
componentStack: errorInfo?.componentStack,
message: error?.message,
name: error?.name,
stack: error?.stack,
},
});
}}
>
{children}
</ErrorBoundary>
);
}; };
@@ -2,6 +2,7 @@ import { ErrorBoundary } from 'react-error-boundary';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { ServerSelector } from '/@/renderer/features/sidebar/components/server-selector'; import { ServerSelector } from '/@/renderer/features/sidebar/components/server-selector';
import { LogCategory, logFn } from '/@/renderer/utils/logger';
import { Box } from '/@/shared/components/box/box'; import { Box } from '/@/shared/components/box/box';
import { Button } from '/@/shared/components/button/button'; import { Button } from '/@/shared/components/button/button';
import { Center } from '/@/shared/components/center/center'; import { Center } from '/@/shared/components/center/center';
@@ -85,9 +86,15 @@ export const PageErrorBoundary = ({ children }: PageErrorBoundaryProps) => {
<ErrorBoundary <ErrorBoundary
FallbackComponent={PageErrorFallback} FallbackComponent={PageErrorFallback}
onError={(error, errorInfo) => { onError={(error, errorInfo) => {
if (process.env.NODE_ENV === 'development') { logFn.error('Page error boundary caught an error', {
console.error('Page error boundary caught an error:', error, errorInfo); category: LogCategory.OTHER,
} meta: {
componentStack: errorInfo?.componentStack,
message: error?.message,
name: error?.name,
stack: error?.stack,
},
});
}} }}
onReset={() => {}} onReset={() => {}}
> >
@@ -2,6 +2,7 @@ import { ErrorBoundary } from 'react-error-boundary';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { ServerSelector } from '/@/renderer/features/sidebar/components/server-selector'; import { ServerSelector } from '/@/renderer/features/sidebar/components/server-selector';
import { LogCategory, logFn } from '/@/renderer/utils/logger';
import { Box } from '/@/shared/components/box/box'; import { Box } from '/@/shared/components/box/box';
import { Button } from '/@/shared/components/button/button'; import { Button } from '/@/shared/components/button/button';
import { Center } from '/@/shared/components/center/center'; import { Center } from '/@/shared/components/center/center';
@@ -91,9 +92,15 @@ export const RouterErrorBoundary = ({ children }: RouterErrorBoundaryProps) => {
<ErrorBoundary <ErrorBoundary
FallbackComponent={RouterErrorFallback} FallbackComponent={RouterErrorFallback}
onError={(error, errorInfo) => { onError={(error, errorInfo) => {
if (process.env.NODE_ENV === 'development') { logFn.error('Router error boundary caught an error', {
console.error('Root error boundary caught an error:', error, errorInfo); category: LogCategory.OTHER,
} meta: {
componentStack: errorInfo?.componentStack,
message: error?.message,
name: error?.name,
stack: error?.stack,
},
});
}} }}
onReset={() => {}} onReset={() => {}}
> >
@@ -12,6 +12,7 @@ import {
restoreFavoriteQueryData, restoreFavoriteQueryData,
} from '/@/renderer/features/shared/mutations/favorite-optimistic-updates'; } from '/@/renderer/features/shared/mutations/favorite-optimistic-updates';
import { MutationHookArgs } from '/@/renderer/lib/react-query'; import { MutationHookArgs } from '/@/renderer/lib/react-query';
import { LogCategory, logFn } from '/@/renderer/utils/logger';
import { toast } from '/@/shared/components/toast/toast'; import { toast } from '/@/shared/components/toast/toast';
import { FavoriteArgs, FavoriteResponse, LibraryItem } from '/@/shared/types/domain-types'; import { FavoriteArgs, FavoriteResponse, LibraryItem } from '/@/shared/types/domain-types';
@@ -33,6 +34,15 @@ export const useCreateFavorite = (args: MutationHookArgs) => {
}, },
mutationKey: createFavoriteMutationKey, mutationKey: createFavoriteMutationKey,
onError: (_error, variables, context) => { onError: (_error, variables, context) => {
logFn.error('Create favorite failed', {
category: LogCategory.API,
meta: {
id: variables.query.id,
message: _error?.message,
serverId: variables.apiClientProps.serverId,
type: variables.query.type,
},
});
if (context) { if (context) {
restoreFavoriteQueryData(queryClient, context); restoreFavoriteQueryData(queryClient, context);
} }
@@ -12,6 +12,7 @@ import {
restoreFavoriteQueryData, restoreFavoriteQueryData,
} from '/@/renderer/features/shared/mutations/favorite-optimistic-updates'; } from '/@/renderer/features/shared/mutations/favorite-optimistic-updates';
import { MutationHookArgs } from '/@/renderer/lib/react-query'; import { MutationHookArgs } from '/@/renderer/lib/react-query';
import { LogCategory, logFn } from '/@/renderer/utils/logger';
import { toast } from '/@/shared/components/toast/toast'; import { toast } from '/@/shared/components/toast/toast';
import { FavoriteArgs, FavoriteResponse, LibraryItem } from '/@/shared/types/domain-types'; import { FavoriteArgs, FavoriteResponse, LibraryItem } from '/@/shared/types/domain-types';
@@ -33,6 +34,15 @@ export const useDeleteFavorite = (args: MutationHookArgs) => {
}, },
mutationKey: deleteFavoriteMutationKey, mutationKey: deleteFavoriteMutationKey,
onError: (_error, _variables, context) => { onError: (_error, _variables, context) => {
logFn.error('Delete favorite failed', {
category: LogCategory.API,
meta: {
id: _variables.query.id,
message: _error?.message,
serverId: _variables.apiClientProps.serverId,
type: _variables.query.type,
},
});
if (context) { if (context) {
restoreFavoriteQueryData(queryClient, context); restoreFavoriteQueryData(queryClient, context);
} }
@@ -11,6 +11,7 @@ import {
restoreRatingQueryData, restoreRatingQueryData,
} from '/@/renderer/features/shared/mutations/rating-optimistic-updates'; } from '/@/renderer/features/shared/mutations/rating-optimistic-updates';
import { MutationHookArgs } from '/@/renderer/lib/react-query'; import { MutationHookArgs } from '/@/renderer/lib/react-query';
import { LogCategory, logFn } from '/@/renderer/utils/logger';
import { toast } from '/@/shared/components/toast/toast'; import { toast } from '/@/shared/components/toast/toast';
import { LibraryItem, RatingResponse, SetRatingArgs } from '/@/shared/types/domain-types'; import { LibraryItem, RatingResponse, SetRatingArgs } from '/@/shared/types/domain-types';
@@ -30,6 +31,16 @@ export const useSetRatingMutation = (args: MutationHookArgs) => {
}, },
mutationKey: setRatingMutationKey, mutationKey: setRatingMutationKey,
onError: (_error, _variables, context) => { onError: (_error, _variables, context) => {
logFn.error('Set rating failed', {
category: LogCategory.API,
meta: {
id: _variables.query.id,
message: _error?.message,
rating: _variables.query.rating,
serverId: _variables.apiClientProps.serverId,
type: _variables.query.type,
},
});
if (context) { if (context) {
restoreRatingQueryData(queryClient, context); restoreRatingQueryData(queryClient, context);
} }
@@ -3,6 +3,7 @@ import { AxiosError } from 'axios';
import { api } from '/@/renderer/api'; import { api } from '/@/renderer/api';
import { MutationHookArgs } from '/@/renderer/lib/react-query'; import { MutationHookArgs } from '/@/renderer/lib/react-query';
import { LogCategory, logFn } from '/@/renderer/utils/logger';
import { AnyLibraryItems, ShareItemArgs, ShareItemResponse } from '/@/shared/types/domain-types'; import { AnyLibraryItems, ShareItemArgs, ShareItemResponse } from '/@/shared/types/domain-types';
export const useShareItem = (args: MutationHookArgs) => { export const useShareItem = (args: MutationHookArgs) => {
@@ -20,6 +21,17 @@ export const useShareItem = (args: MutationHookArgs) => {
apiClientProps: { serverId: args.apiClientProps.serverId }, apiClientProps: { serverId: args.apiClientProps.serverId },
}); });
}, },
onError: (error, variables) => {
logFn.error('Share item failed', {
category: LogCategory.API,
meta: {
itemType: variables.body?.resourceType,
message: error?.message,
serverId: variables.apiClientProps.serverId,
},
});
options?.onError?.(error);
},
retry: false, retry: false,
...options, ...options,
}); });
+13 -1
View File
@@ -2,6 +2,8 @@ import { useQuery } from '@tanstack/react-query';
import isElectron from 'is-electron'; import isElectron from 'is-electron';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { LogCategory, logFn } from '/@/renderer/utils/logger';
const CHECK_FOR_UPDATES_INTERVAL_MS = 6 * 60 * 60 * 1000; const CHECK_FOR_UPDATES_INTERVAL_MS = 6 * 60 * 60 * 1000;
const utils = isElectron() ? window.api?.utils : null; const utils = isElectron() ? window.api?.utils : null;
@@ -21,7 +23,17 @@ export const useCheckForUpdates = () => {
return useQuery({ return useQuery({
enabled: isEnabled, enabled: isEnabled,
queryFn: () => utils?.checkForUpdates?.(), queryFn: async () => {
const result = await utils?.checkForUpdates?.();
logFn.info('Check for updates completed', {
category: LogCategory.SYSTEM,
meta: {
updateAvailable: result?.updateAvailable ?? false,
version: result?.version,
},
});
return result;
},
queryKey: ['app-check-for-updates'], queryKey: ['app-check-for-updates'],
refetchInterval: CHECK_FOR_UPDATES_INTERVAL_MS, refetchInterval: CHECK_FOR_UPDATES_INTERVAL_MS,
refetchIntervalInBackground: true, refetchIntervalInBackground: true,
+21 -13
View File
@@ -10,7 +10,6 @@ import { controller } from '/@/renderer/api/controller';
import { AppRoute } from '/@/renderer/router/routes'; import { AppRoute } from '/@/renderer/router/routes';
import { getServerById, useAuthStoreActions, useCurrentServer } from '/@/renderer/store'; import { getServerById, useAuthStoreActions, useCurrentServer } from '/@/renderer/store';
import { LogCategory, logFn } from '/@/renderer/utils/logger'; import { LogCategory, logFn } from '/@/renderer/utils/logger';
import { logMsg } from '/@/renderer/utils/logger-message';
import { toast } from '/@/shared/components/toast/toast'; import { toast } from '/@/shared/components/toast/toast';
import { AuthState } from '/@/shared/types/types'; import { AuthState } from '/@/shared/types/types';
@@ -62,7 +61,7 @@ export const useServerAuthenticated = () => {
} }
// First, try getUserInfo to check if current credentials are still valid // First, try getUserInfo to check if current credentials are still valid
logFn.info(logMsg[LogCategory.SYSTEM].authenticatingServer, { logFn.info('Authenticating server', {
category: LogCategory.SYSTEM, category: LogCategory.SYSTEM,
meta: { meta: {
method: 'getUserInfo', method: 'getUserInfo',
@@ -117,7 +116,7 @@ export const useServerAuthenticated = () => {
} }
} catch (serverInfoError) { } catch (serverInfoError) {
// Log but don't fail authentication if server info fetch fails // Log but don't fail authentication if server info fetch fails
logFn.warn(logMsg[LogCategory.SYSTEM].serverAuthenticationSuccess, { logFn.warn('Server authentication successful', {
category: LogCategory.SYSTEM, category: LogCategory.SYSTEM,
meta: { meta: {
action: 'server_info_fetch_failed', action: 'server_info_fetch_failed',
@@ -128,7 +127,7 @@ export const useServerAuthenticated = () => {
}); });
} }
logFn.info(logMsg[LogCategory.SYSTEM].serverAuthenticationSuccess, { logFn.info('Server authentication successful', {
category: LogCategory.SYSTEM, category: LogCategory.SYSTEM,
meta: { meta: {
isAdmin: userInfo.isAdmin, isAdmin: userInfo.isAdmin,
@@ -162,7 +161,7 @@ export const useServerAuthenticated = () => {
const password = await localSettings.passwordGet(serverWithAuth.id); const password = await localSettings.passwordGet(serverWithAuth.id);
if (password) { if (password) {
logFn.info(logMsg[LogCategory.SYSTEM].authenticatingServer, { logFn.info('Authenticating server', {
category: LogCategory.SYSTEM, category: LogCategory.SYSTEM,
meta: { meta: {
method: 'authenticate', method: 'authenticate',
@@ -227,7 +226,7 @@ export const useServerAuthenticated = () => {
} }
} catch (serverInfoError) { } catch (serverInfoError) {
// Log but don't fail authentication if server info fetch fails // Log but don't fail authentication if server info fetch fails
logFn.warn(logMsg[LogCategory.SYSTEM].serverAuthenticationSuccess, { logFn.warn('Server authentication successful', {
category: LogCategory.SYSTEM, category: LogCategory.SYSTEM,
meta: { meta: {
action: 'server_info_fetch_failed', action: 'server_info_fetch_failed',
@@ -238,7 +237,7 @@ export const useServerAuthenticated = () => {
}); });
} }
logFn.info(logMsg[LogCategory.SYSTEM].serverAuthenticationSuccess, { logFn.info('Server authentication successful', {
category: LogCategory.SYSTEM, category: LogCategory.SYSTEM,
meta: { meta: {
isAdmin: authData.isAdmin, isAdmin: authData.isAdmin,
@@ -275,7 +274,7 @@ export const useServerAuthenticated = () => {
if (isNetwork && retryAttempt < MAX_NETWORK_RETRIES) { if (isNetwork && retryAttempt < MAX_NETWORK_RETRIES) {
const nextRetry = retryAttempt + 1; const nextRetry = retryAttempt + 1;
logFn.warn(logMsg[LogCategory.SYSTEM].serverAuthenticationFailed, { logFn.warn('Server authentication failed', {
category: LogCategory.SYSTEM, category: LogCategory.SYSTEM,
meta: { meta: {
action: 'network_error_retry', action: 'network_error_retry',
@@ -298,7 +297,7 @@ export const useServerAuthenticated = () => {
// If network error and retries exhausted, redirect to no-network page // If network error and retries exhausted, redirect to no-network page
if (isNetwork && retryAttempt >= MAX_NETWORK_RETRIES) { if (isNetwork && retryAttempt >= MAX_NETWORK_RETRIES) {
logFn.error(logMsg[LogCategory.SYSTEM].serverAuthenticationFailed, { logFn.error('Server authentication failed', {
category: LogCategory.SYSTEM, category: LogCategory.SYSTEM,
meta: { meta: {
action: 'network_error_max_retries_exceeded', action: 'network_error_max_retries_exceeded',
@@ -317,7 +316,7 @@ export const useServerAuthenticated = () => {
} }
// For non-network errors, handle normally // For non-network errors, handle normally
logFn.error(logMsg[LogCategory.SYSTEM].serverAuthenticationFailed, { logFn.error('Server authentication failed', {
category: LogCategory.SYSTEM, category: LogCategory.SYSTEM,
meta: { meta: {
error: errorMessage, error: errorMessage,
@@ -346,14 +345,23 @@ export const useServerAuthenticated = () => {
const debouncedAuth = debounce( const debouncedAuth = debounce(
(serverWithAuth: NonNullable<ReturnType<typeof getServerById>>) => { (serverWithAuth: NonNullable<ReturnType<typeof getServerById>>) => {
authenticateServer(serverWithAuth).catch(console.error); authenticateServer(serverWithAuth).catch((err) => {
logFn.error('Server authentication failed (debounced)', {
category: LogCategory.SYSTEM,
meta: {
message: (err as Error)?.message,
serverId: serverWithAuth.id,
serverName: serverWithAuth.name,
},
});
});
}, },
300, 300,
); );
useEffect(() => { useEffect(() => {
if (!server) { if (!server) {
logFn.debug(logMsg[LogCategory.SYSTEM].serverAuthenticationInvalid, { logFn.debug('Server authentication invalid', {
category: LogCategory.SYSTEM, category: LogCategory.SYSTEM,
meta: { meta: {
reason: 'No server selected', reason: 'No server selected',
@@ -369,7 +377,7 @@ export const useServerAuthenticated = () => {
retryCountRef.current = 0; // Reset retry count when server changes retryCountRef.current = 0; // Reset retry count when server changes
if (!serverWithAuth) { if (!serverWithAuth) {
logFn.error(logMsg[LogCategory.SYSTEM].serverAuthenticationError, { logFn.error('Server authentication error', {
category: LogCategory.SYSTEM, category: LogCategory.SYSTEM,
meta: { meta: {
reason: 'Server not found in store', reason: 'Server not found in store',
@@ -4,8 +4,7 @@ import { useEffect, useRef } from 'react';
import i18n from '/@/i18n/i18n'; import i18n from '/@/i18n/i18n';
import { openRestartRequiredToast } from '/@/renderer/features/settings/restart-toast'; import { openRestartRequiredToast } from '/@/renderer/features/settings/restart-toast';
import { useSettingsStore } from '/@/renderer/store/settings.store'; import { useSettingsStore } from '/@/renderer/store/settings.store';
import { logFn } from '/@/renderer/utils/logger'; import { LogCategory, logFn } from '/@/renderer/utils/logger';
import { logMsg } from '/@/renderer/utils/logger-message';
// Synchronizes settings from the renderer store to the main process electron store // Synchronizes settings from the renderer store to the main process electron store
// on app initialization. If there are differences, it updates the main store and shows // on app initialization. If there are differences, it updates the main store and shows
@@ -120,7 +119,8 @@ export const useSyncSettingsToMain = () => {
JSON.stringify(rendererValueNormalized) JSON.stringify(rendererValueNormalized)
) { ) {
hasDifferences = true; hasDifferences = true;
logFn.warn(logMsg.system.settingsSynchronized, { logFn.warn('Differences found between renderer and main process settings', {
category: LogCategory.SYSTEM,
meta: { meta: {
mainStoreKey: mapping.mainStoreKey, mainStoreKey: mapping.mainStoreKey,
mainValue: mainValueNormalized, mainValue: mainValueNormalized,
+8 -1
View File
@@ -8,12 +8,19 @@ import type {
import { QueryCache, QueryClient } from '@tanstack/react-query'; import { QueryCache, QueryClient } from '@tanstack/react-query';
import { LogCategory, logFn } from '/@/renderer/utils/logger';
import { toast } from '/@/shared/components/toast/toast'; import { toast } from '/@/shared/components/toast/toast';
const queryCache = new QueryCache({ const queryCache = new QueryCache({
onError: (error: any, query) => { onError: (error: any, query) => {
logFn.error('Query failed', {
category: LogCategory.API,
meta: {
message: error?.message,
queryKey: query.queryKey,
},
});
if (query.state.data !== undefined) { if (query.state.data !== undefined) {
console.error(error);
toast.show({ message: `${error.message}`, type: 'error' }); toast.show({ message: `${error.message}`, type: 'error' });
} }
}, },
+17 -4
View File
@@ -8,6 +8,7 @@ import { useTranslation } from 'react-i18next';
import packageJson from '../../package.json'; import packageJson from '../../package.json';
import { formatHrDateTime } from '/@/renderer/utils/format'; import { formatHrDateTime } from '/@/renderer/utils/format';
import { LogCategory, logFn } from '/@/renderer/utils/logger';
import { Button } from '/@/shared/components/button/button'; import { Button } from '/@/shared/components/button/button';
import { Center } from '/@/shared/components/center/center'; import { Center } from '/@/shared/components/center/center';
import { Group } from '/@/shared/components/group/group'; import { Group } from '/@/shared/components/group/group';
@@ -70,10 +71,22 @@ const ReleaseNotesContent = ({ onDismiss, version }: ReleaseNotesContentProps) =
// Fetch list of recent releases for the selector // Fetch list of recent releases for the selector
const { data: releasesList = [] } = useQuery({ const { data: releasesList = [] } = useQuery({
queryFn: async () => { queryFn: async () => {
const response = await axios.get<GitHubRelease[]>(GITHUB_RELEASES_URL, { try {
params: { per_page: RELEASES_TO_FETCH }, const response = await axios.get<GitHubRelease[]>(GITHUB_RELEASES_URL, {
}); params: { per_page: RELEASES_TO_FETCH },
return response.data; });
logFn.info('Release notes fetched', {
category: LogCategory.GENERAL,
meta: { count: response.data?.length ?? 0 },
});
return response.data;
} catch (error) {
logFn.error('Release notes fetch failed', {
category: LogCategory.GENERAL,
meta: { message: (error as Error)?.message },
});
throw error;
}
}, },
queryKey: ['github-releases-list'], queryKey: ['github-releases-list'],
retry: 2, retry: 2,
+26
View File
@@ -5,6 +5,7 @@ import { immer } from 'zustand/middleware/immer';
import { shallow } from 'zustand/shallow'; import { shallow } from 'zustand/shallow';
import { createWithEqualityFn } from 'zustand/traditional'; import { createWithEqualityFn } from 'zustand/traditional';
import { LogCategory, logFn } from '/@/renderer/utils/logger';
import { ServerListItem, ServerListItemWithCredential } from '/@/shared/types/domain-types'; import { ServerListItem, ServerListItemWithCredential } from '/@/shared/types/domain-types';
export interface AuthSlice extends AuthState { export interface AuthSlice extends AuthState {
@@ -30,6 +31,16 @@ export const useAuthStore = createWithEqualityFn<AuthSlice>()(
immer((set, get) => ({ immer((set, get) => ({
actions: { actions: {
addServer: (args) => { addServer: (args) => {
if (process.env.NODE_ENV === 'development') {
logFn.debug('Auth store: add server', {
category: LogCategory.SYSTEM,
meta: {
serverId: args.id,
serverName: args.name,
serverType: args.type,
},
});
}
set((state) => { set((state) => {
state.serverList[args.id] = args; state.serverList[args.id] = args;
}); });
@@ -49,6 +60,15 @@ export const useAuthStore = createWithEqualityFn<AuthSlice>()(
return null; return null;
}, },
setCurrentServer: (server) => { setCurrentServer: (server) => {
if (process.env.NODE_ENV === 'development') {
logFn.debug('Auth store: set current server', {
category: LogCategory.SYSTEM,
meta: {
serverId: server?.id ?? null,
serverName: server?.name ?? null,
},
});
}
set((state) => { set((state) => {
state.currentServer = server; state.currentServer = server;
}); });
@@ -65,6 +85,12 @@ export const useAuthStore = createWithEqualityFn<AuthSlice>()(
}); });
}, },
updateServer: (id: string, args: Partial<ServerListItemWithCredential>) => { updateServer: (id: string, args: Partial<ServerListItemWithCredential>) => {
if (process.env.NODE_ENV === 'development') {
logFn.debug('Auth store: update server', {
category: LogCategory.SYSTEM,
meta: { keys: Object.keys(args || {}), serverId: id },
});
}
set((state) => { set((state) => {
const updatedServer = { const updatedServer = {
...state.serverList[id], ...state.serverList[id],
-123
View File
@@ -1,123 +0,0 @@
import { LogCategory } from '/@/renderer/utils/logger';
export const logMsg = {
[LogCategory.ANALYTICS]: {
appTracked: 'Analytics sent',
pageViewTracked: 'Page view tracked',
},
[LogCategory.API]: {},
[LogCategory.EXTERNAL]: {
discordRpcActivityCleared: 'Activity was cleared for Discord RPC',
discordRpcInitialized: 'Discord RPC was initialized',
discordRpcQuit: 'Discord RPC was quit',
discordRpcSetActivity: 'Activity was set for Discord RPC',
discordRpcTrackChanged: 'Track was changed for Discord RPC',
discordRpcUpdateSkipped: 'Activity was not updated for Discord RPC',
},
[LogCategory.OTHER]: {
error: 'An error occurred',
warning: 'A warning occurred',
},
[LogCategory.PLAYER]: {
addToQueueByData: 'Added to queue by data',
addToQueueByFetch: 'Added to queue by fetch',
addToQueueByListQuery: 'Added to queue by list query',
addToQueueByType: 'Added to queue by type',
autoPlayFailed: 'Auto play failed',
autoPlayTriggered: 'Auto play triggered',
cancelledFetch: 'Cancelled fetch',
clearQueue: 'Cleared queue',
clearSelected: 'Cleared selected',
decreaseVolume: 'Decreased volume',
increaseVolume: 'Increased volume',
mediaNext: 'Media next',
mediaPause: 'Media pause',
mediaPlay: 'Media play',
mediaPlayByIndex: 'Media play by index',
mediaPrevious: 'Media previous',
mediaSeekToTimestamp: 'Media seek to timestamp',
mediaSkipBackward: 'Media skip backward',
mediaSkipForward: 'Media skip forward',
mediaStop: 'Media stop',
mediaToggleMute: 'Media toggle mute',
mediaTogglePlayPause: 'Media toggle play pause',
moveSelectedTo: 'Moved selected to',
moveSelectedToBottom: 'Moved selected to bottom',
moveSelectedToNext: 'Moved selected to next',
moveSelectedToTop: 'Moved selected to top',
playbackError: 'An error occurred during playback',
playerFiltersApplied: 'Player filters applied',
setFavorite: 'Set favorite',
setQueue: 'Set queue',
setRating: 'Set rating',
setRepeat: 'Set repeat',
setShuffle: 'Set shuffle',
setSpeed: 'Set speed',
setVolume: 'Set volume',
shuffle: 'Shuffle',
shuffleAll: 'Shuffle all',
shuffleSelected: 'Shuffle selected',
toggleRepeat: 'Toggle repeat',
toggleShuffle: 'Toggle shuffle',
},
[LogCategory.REMOTE]: {
cannotSendEvent: 'Cannot send event - socket not available',
closingExistingSocket: 'Closing existing socket',
creatingWebSocket: 'Creating new WebSocket',
credentialsFetched: 'Credentials fetched',
failedToEnableRemote: 'Failed to enable remote',
failedToGetCredentials: 'Failed to get credentials',
favoriteEventReceived: 'Favorite event received',
fetchingCredentials: 'Fetching credentials',
initializingRemoteSettings: 'Initializing remote settings',
playbackEventReceived: 'Playback event received',
positionEventReceived: 'Position event received',
proxyEventReceived: 'Proxy event received (image update)',
ratingEventReceived: 'Rating event received',
reconnectInitiated: 'Reconnect initiated',
reloadingPage: 'Reloading page due to close code',
repeatEventReceived: 'Repeat event received',
requestFavoriteReceived: 'Request favorite received',
requestPositionReceived: 'Request position received',
requestRatingReceived: 'Request rating received',
requestSeekReceived: 'Request seek received',
requestVolumeReceived: 'Request volume received',
sendingAuthentication: 'Sending authentication',
sendingEventToServer: 'Sending event to server',
sendingInitialSong: 'Sending initial song',
serverIsDown: 'Server is down',
shuffleEventReceived: 'Shuffle event received',
socketClosedUnexpectedly: 'Socket closed unexpectedly',
songEventReceived: 'Song event received',
stateEventReceived: 'State event received (full state update)',
updateFavoriteSent: 'Update favorite sent',
updatePlaybackSent: 'Update playback sent',
updatePositionSent: 'Update position sent',
updateRatingSent: 'Update rating sent',
updateRepeatSent: 'Update repeat sent',
updateShuffleSent: 'Update shuffle sent',
updateSongSent: 'Update song sent',
updateVolumeSent: 'Update volume sent',
volumeEventReceived: 'Volume event received',
webSocketClosed: 'WebSocket closed',
webSocketErrorEvent: 'WebSocket error event',
webSocketMessageReceived: 'WebSocket message received',
webSocketOpened: 'WebSocket opened',
},
[LogCategory.SCROBBLE]: {
scrobbledPause: 'Scrobbled a pause event',
scrobbledStart: 'Scrobbled a start event',
scrobbledSubmission: 'Scrobbled a submission event',
scrobbledTimeupdate: 'Scrobbled a timeupdate event',
scrobbledUnpause: 'Scrobbled an unpause event',
},
[LogCategory.SYSTEM]: {
authenticatingServer: 'Authenticating server',
serverAuthenticationAborted: 'Server authentication aborted',
serverAuthenticationError: 'Server authentication error',
serverAuthenticationFailed: 'Server authentication failed',
serverAuthenticationInvalid: 'Server authentication invalid',
serverAuthenticationSuccess: 'Server authentication successful',
settingsSynchronized: 'Differences found between renderer and main process settings',
},
};