Files
feishin/src/renderer/features/remote/hooks/use-remote.tsx
T

304 lines
11 KiB
TypeScript

import isElectron from 'is-electron';
import { useEffect, useRef } from 'react';
import { getItemImageUrl } from '/@/renderer/components/item-image/item-image';
import { usePlayerEvents } from '/@/renderer/features/player/audio-player/hooks/use-player-events';
import { useCreateFavorite } from '/@/renderer/features/shared/mutations/create-favorite-mutation';
import { useDeleteFavorite } from '/@/renderer/features/shared/mutations/delete-favorite-mutation';
import { useSetRating } from '/@/renderer/features/shared/mutations/set-rating-mutation';
import { usePlayerActions, usePlayerStore, useRemoteSettings } from '/@/renderer/store';
import { LogCategory, logFn } from '/@/renderer/utils/logger';
import { logMsg } from '/@/renderer/utils/logger-message';
import { toast } from '/@/shared/components/toast/toast';
import { LibraryItem } from '/@/shared/types/domain-types';
import { PlayerShuffle } from '/@/shared/types/types';
const remote = isElectron() ? window.api.remote : null;
const ipc = isElectron() ? window.api.ipc : null;
export const useRemote = () => {
const { mediaSkipForward, setVolume } = usePlayerActions();
const player = usePlayerStore();
const remoteSettings = useRemoteSettings();
const updateRatingMutation = useSetRating({});
const addToFavoritesMutation = useCreateFavorite({});
const removeFromFavoritesMutation = useDeleteFavorite({});
const isRemoteEnabled = remoteSettings.enabled;
// Initialize the remote
useEffect(() => {
if (!isRemoteEnabled) {
return;
}
logFn.debug(logMsg[LogCategory.REMOTE].initializingRemoteSettings, {
category: LogCategory.REMOTE,
meta: {
enabled: remoteSettings.enabled,
port: remoteSettings.port,
username: remoteSettings.username,
},
});
remote
?.updateSetting(
remoteSettings.enabled,
remoteSettings.port,
remoteSettings.username,
remoteSettings.password,
)
.catch((error) => {
logFn.error(logMsg[LogCategory.REMOTE].failedToEnableRemote, {
category: LogCategory.REMOTE,
meta: { error },
});
toast.warn({ message: error, title: 'Failed to enable remote' });
});
// We only want to fire this once
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
useEffect(() => {
if (!isRemoteEnabled || !remote) {
return;
}
remote.requestPosition((_e: unknown, data: { position: number }) => {
logFn.debug(logMsg[LogCategory.REMOTE].requestPositionReceived, {
category: LogCategory.REMOTE,
meta: { position: data.position },
});
const newTime = data.position;
player.mediaSeekToTimestamp(newTime);
});
remote.requestSeek((_e: unknown, data: { offset: number }) => {
logFn.debug(logMsg[LogCategory.REMOTE].requestSeekReceived, {
category: LogCategory.REMOTE,
meta: { offset: data.offset },
});
mediaSkipForward(data.offset);
});
remote.requestRating(
(_e: unknown, data: { id: string; rating: number; serverId: string }) => {
logFn.debug(logMsg[LogCategory.REMOTE].requestRatingReceived, {
category: LogCategory.REMOTE,
meta: { id: data.id, rating: data.rating, serverId: data.serverId },
});
updateRatingMutation.mutate({
apiClientProps: { serverId: data.serverId },
query: {
id: [data.id],
rating: data.rating,
type: LibraryItem.SONG,
},
});
},
);
remote.requestVolume((_e: unknown, data: { volume: number }) => {
logFn.debug(logMsg[LogCategory.REMOTE].requestVolumeReceived, {
category: LogCategory.REMOTE,
meta: { volume: data.volume },
});
setVolume(data.volume);
});
remote.requestFavorite(
(_e: unknown, data: { favorite: boolean; id: string; serverId: string }) => {
logFn.debug(logMsg[LogCategory.REMOTE].requestFavoriteReceived, {
category: LogCategory.REMOTE,
meta: { favorite: data.favorite, id: data.id, serverId: data.serverId },
});
const mutator = data.favorite
? addToFavoritesMutation
: removeFromFavoritesMutation;
mutator.mutate({
apiClientProps: { serverId: data.serverId },
query: {
id: [data.id],
type: LibraryItem.SONG,
},
});
},
);
return () => {
ipc?.removeAllListeners('request-position');
ipc?.removeAllListeners('request-seek');
ipc?.removeAllListeners('request-volume');
ipc?.removeAllListeners('request-favorite');
ipc?.removeAllListeners('request-rating');
};
}, [
addToFavoritesMutation,
isRemoteEnabled,
mediaSkipForward,
player,
removeFromFavoritesMutation,
setVolume,
updateRatingMutation,
]);
// Send initial song if one is already playing
const isInitializedRef = useRef(false);
useEffect(() => {
if (isInitializedRef.current || !isRemoteEnabled || !remote) {
return;
}
isInitializedRef.current = true;
const currentSong = player.getCurrentSong();
if (currentSong) {
logFn.debug(logMsg[LogCategory.REMOTE].sendingInitialSong, {
category: LogCategory.REMOTE,
meta: {
artistName: currentSong.artistName,
id: currentSong.id,
name: currentSong.name,
},
});
const imageUrl =
getItemImageUrl({
id: currentSong.id,
imageUrl: currentSong.imageUrl,
itemType: LibraryItem.SONG,
serverId: currentSong._serverId,
type: 'itemCard',
useRemoteUrl: true,
}) || null;
remote.updateSong(currentSong, imageUrl);
}
}, [isRemoteEnabled, player]);
usePlayerEvents(
{
onCurrentSongChange: (properties) => {
if (!isRemoteEnabled || !remote) {
return;
}
logFn.debug(logMsg[LogCategory.REMOTE].updateSongSent, {
category: LogCategory.REMOTE,
meta: {
artistName: properties.song?.artistName,
id: properties.song?.id,
index: properties.index,
name: properties.song?.name,
},
});
if (properties.song) {
const song = properties.song;
const imageUrl =
getItemImageUrl({
id: song.id,
imageUrl: song.imageUrl,
itemType: LibraryItem.SONG,
serverId: song._serverId,
type: 'itemCard',
useRemoteUrl: true,
}) || null;
remote.updateSong(song, imageUrl);
} else {
remote.updateSong(undefined);
}
},
onPlayerProgress: (properties) => {
if (!isRemoteEnabled || !remote) {
return;
}
logFn.debug(logMsg[LogCategory.REMOTE].updatePositionSent, {
category: LogCategory.REMOTE,
meta: { timestamp: properties.timestamp },
});
remote.updatePosition(properties.timestamp);
},
onPlayerRepeat: (properties) => {
if (!isRemoteEnabled || !remote) {
return;
}
logFn.debug(logMsg[LogCategory.REMOTE].updateRepeatSent, {
category: LogCategory.REMOTE,
meta: { repeat: properties.repeat },
});
remote.updateRepeat(properties.repeat);
},
onPlayerShuffle: (properties) => {
if (!isRemoteEnabled || !remote) {
return;
}
const isShuffleEnabled = properties.shuffle !== PlayerShuffle.NONE;
logFn.debug(logMsg[LogCategory.REMOTE].updateShuffleSent, {
category: LogCategory.REMOTE,
meta: { isShuffleEnabled, shuffle: properties.shuffle },
});
remote.updateShuffle(isShuffleEnabled);
},
onPlayerStatus: (properties) => {
if (!isRemoteEnabled || !remote) {
return;
}
logFn.debug(logMsg[LogCategory.REMOTE].updatePlaybackSent, {
category: LogCategory.REMOTE,
meta: { status: properties.status },
});
remote.updatePlayback(properties.status);
},
onPlayerVolume: (properties) => {
if (!isRemoteEnabled || !remote) {
return;
}
logFn.debug(logMsg[LogCategory.REMOTE].updateVolumeSent, {
category: LogCategory.REMOTE,
meta: { volume: properties.volume },
});
remote.updateVolume(properties.volume);
},
onUserFavorite: (properties) => {
if (!isRemoteEnabled || !remote) {
return;
}
logFn.debug(logMsg[LogCategory.REMOTE].updateFavoriteSent, {
category: LogCategory.REMOTE,
meta: {
favorite: properties.favorite,
id: properties.id,
serverId: properties.serverId,
},
});
remote.updateFavorite(properties.favorite, properties.serverId, properties.id);
},
onUserRating: (properties) => {
if (!isRemoteEnabled || !remote) {
return;
}
logFn.debug(logMsg[LogCategory.REMOTE].updateRatingSent, {
category: LogCategory.REMOTE,
meta: {
id: properties.id,
rating: properties.rating || 0,
serverId: properties.serverId,
},
});
remote.updateRating(properties.rating || 0, properties.serverId, properties.id);
},
},
[],
);
};