diff --git a/src/renderer/features/discord-rpc/use-discord-rpc.ts b/src/renderer/features/discord-rpc/use-discord-rpc.ts index 2a8f6b7c4..74133c619 100644 --- a/src/renderer/features/discord-rpc/use-discord-rpc.ts +++ b/src/renderer/features/discord-rpc/use-discord-rpc.ts @@ -1,6 +1,6 @@ import { SetActivity, StatusDisplayType } from '@xhayper/discord-rpc'; import isElectron from 'is-electron'; -import { useCallback, useEffect, useState } from 'react'; +import { useCallback, useEffect, useRef, useState } from 'react'; import { controller } from '/@/renderer/api/controller'; import { @@ -13,6 +13,8 @@ import { useTimestampStoreBase, } from '/@/renderer/store'; import { sentenceCase } from '/@/renderer/utils'; +import { LogCategory, logFn } from '/@/renderer/utils/logger'; +import { logMsg } from '/@/renderer/utils/logger-message'; import { QueueSong, ServerType } from '/@/shared/types/domain-types'; import { PlayerStatus } from '/@/shared/types/types'; @@ -30,6 +32,8 @@ export const useDiscordRpc = () => { const privateMode = useAppStore((state) => state.privateMode); const [lastUniqueId, setlastUniqueId] = useState(''); + const previousEnabledRef = useRef(discordSettings.enabled); + const setActivity = useCallback( async (current: ActivityState, previous: ActivityState) => { if ( @@ -37,6 +41,22 @@ export const useDiscordRpc = () => { current[1] === 0 || // Start of track (current[2] === 'paused' && !discordSettings.showPaused) // Track paused with show paused setting disabled ) { + let reason: string; + if (!current[0]) { + reason = 'no_track'; + } else if (current[1] === 0) { + reason = 'start_of_track'; + } else { + reason = 'paused_with_show_paused_disabled'; + } + + logFn.debug(logMsg[LogCategory.EXTERNAL].discordRpcActivityCleared, { + category: LogCategory.EXTERNAL, + meta: { + reason, + status: current[2], + }, + }); return discordRpc?.clearActivity(); } @@ -57,9 +77,40 @@ export const useDiscordRpc = () => { current[2] !== previous[2] ) { if (trackChanged) { + logFn.debug(logMsg[LogCategory.EXTERNAL].discordRpcTrackChanged, { + category: LogCategory.EXTERNAL, + meta: { + artistName: song.artists?.[0]?.name, + songId: song._uniqueId, + songName: song.name, + }, + }); setlastUniqueId(song._uniqueId); } + let reason: string; + if (previous[1] === 0) { + reason = 'song_started'; + } else if (Math.abs(current[1] - previous[1]) > 1.2) { + reason = 'time_jump'; + } else if (trackChanged) { + reason = 'track_changed'; + } else { + reason = 'player_state_changed'; + } + + logFn.debug(logMsg[LogCategory.EXTERNAL].discordRpcActivityUpdate, { + category: LogCategory.EXTERNAL, + meta: { + currentStatus: current[2], + currentTime: current[1], + previousStatus: previous[2], + previousTime: previous[1], + reason, + trackChanged, + }, + }); + const start = Math.round(Date.now() - current[1] * 1000); const end = Math.round(start + song.duration); @@ -170,10 +221,41 @@ export const useDiscordRpc = () => { // Initialize if needed const isConnected = await discordRpc?.isConnected(); if (!isConnected) { + logFn.debug(logMsg[LogCategory.EXTERNAL].discordRpcInitialized, { + category: LogCategory.EXTERNAL, + meta: { + clientId: discordSettings.clientId, + }, + }); await discordRpc?.initialize(discordSettings.clientId); } + logFn.debug(logMsg[LogCategory.EXTERNAL].discordRpcSetActivity, { + category: LogCategory.EXTERNAL, + meta: { + albumName: song.album, + artistName: song.artists?.[0]?.name, + displayType: discordSettings.displayType, + hasLargeImage: !!activity.largeImageKey, + hasTimestamps: !!(activity.startTimestamp && activity.endTimestamp), + showAsListening: discordSettings.showAsListening, + songName: song.name, + status: current[2], + }, + }); discordRpc?.setActivity(activity); + } else { + logFn.debug(logMsg[LogCategory.EXTERNAL].discordRpcUpdateSkipped, { + category: LogCategory.EXTERNAL, + meta: { + currentStatus: current[2], + currentTime: current[1], + previousStatus: previous[2], + previousTime: previous[1], + timeDiff: Math.abs(current[1] - previous[1]), + trackChanged, + }, + }); } }, [ @@ -188,20 +270,36 @@ export const useDiscordRpc = () => { ], ); + // Quit Discord RPC if it was enabled and is now disabled useEffect(() => { - if (!discordSettings.enabled || privateMode) { + if ((!discordSettings.enabled || privateMode) && Boolean(previousEnabledRef.current)) { + logFn.info(logMsg[LogCategory.EXTERNAL].discordRpcQuit, { + category: LogCategory.EXTERNAL, + meta: { + enabled: discordSettings.enabled, + privateMode, + }, + }); + + previousEnabledRef.current = false; + return discordRpc?.quit(); } - - return () => { - discordRpc?.quit(); - }; }, [discordSettings.clientId, privateMode, discordSettings.enabled]); useEffect(() => { if (!discordSettings.enabled || privateMode) { return; } + + logFn.info(logMsg[LogCategory.EXTERNAL].discordRpcEnabled, { + category: LogCategory.EXTERNAL, + meta: { + clientId: discordSettings.clientId, + subscribed: true, + }, + }); + const unsubSongChange = usePlayerStore.subscribe((state): ActivityState => { const currentSong = state.getCurrentSong(); const currentTime = useTimestampStoreBase.getState().timestamp; @@ -209,8 +307,9 @@ export const useDiscordRpc = () => { return [currentSong, currentTime, status]; }, setActivity); + return () => { unsubSongChange(); }; - }, [discordSettings.enabled, privateMode, setActivity]); + }, [discordSettings.clientId, discordSettings.enabled, privateMode, setActivity]); }; diff --git a/src/renderer/utils/logger-message.ts b/src/renderer/utils/logger-message.ts index b7d84c2d4..18c275a0d 100644 --- a/src/renderer/utils/logger-message.ts +++ b/src/renderer/utils/logger-message.ts @@ -6,6 +6,16 @@ export const logMsg = { pageViewTracked: 'Page view tracked', }, [LogCategory.API]: {}, + [LogCategory.EXTERNAL]: { + discordRpcActivityCleared: 'Activity was cleared for Discord RPC', + discordRpcActivityUpdate: 'Activity was updated for Discord RPC', + discordRpcEnabled: 'Discord RPC was enabled', + 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]: {}, [LogCategory.PLAYER]: { addToQueueByData: 'Added to queue by data', diff --git a/src/renderer/utils/logger.ts b/src/renderer/utils/logger.ts index e72f6d692..3868ebc15 100644 --- a/src/renderer/utils/logger.ts +++ b/src/renderer/utils/logger.ts @@ -3,6 +3,7 @@ import dayjs from 'dayjs'; export enum LogCategory { ANALYTICS = 'analytics', API = 'api', + EXTERNAL = 'external', OTHER = 'other', PLAYER = 'player', SCROBBLE = 'scrobble',