provide transcoding support

This commit is contained in:
Kendall Garner 2024-09-01 08:26:30 -07:00
parent da95a644c8
commit 528bef01f0
No known key found for this signature in database
GPG key ID: 18D2767419676C87
24 changed files with 347 additions and 69 deletions

View file

@ -65,6 +65,7 @@ import { ItemDetailsModal } from '/@/renderer/features/item-details/components/i
import { updateSong } from '/@/renderer/features/player/update-remote-song';
import { controller } from '/@/renderer/api/controller';
import { api } from '/@/renderer/api';
import { setQueue, setQueueNext } from '/@/renderer/utils/set-transcoded-queue-data';
type ContextMenuContextProps = {
closeContextMenu: () => void;
@ -92,7 +93,6 @@ const JELLYFIN_IGNORED_MENU_ITEMS: ContextMenuItemType[] = ['setRating', 'shareI
// const NAVIDROME_IGNORED_MENU_ITEMS: ContextMenuItemType[] = [];
// const SUBSONIC_IGNORED_MENU_ITEMS: ContextMenuItemType[] = [];
const mpvPlayer = isElectron() ? window.electron.mpvPlayer : null;
const utils = isElectron() ? window.electron.utils : null;
export interface ContextMenuProviderProps {
@ -610,7 +610,7 @@ export const ContextMenuProvider = ({ children }: ContextMenuProviderProps) => {
const playerData = moveToBottomOfQueue(uniqueIds);
if (playbackType === PlaybackType.LOCAL) {
mpvPlayer!.setQueueNext(playerData);
setQueueNext(playerData);
}
}, [ctx.dataNodes, moveToBottomOfQueue, playbackType]);
@ -621,7 +621,7 @@ export const ContextMenuProvider = ({ children }: ContextMenuProviderProps) => {
const playerData = moveToTopOfQueue(uniqueIds);
if (playbackType === PlaybackType.LOCAL) {
mpvPlayer!.setQueueNext(playerData);
setQueueNext(playerData);
}
}, [ctx.dataNodes, moveToTopOfQueue, playbackType]);
@ -651,9 +651,9 @@ export const ContextMenuProvider = ({ children }: ContextMenuProviderProps) => {
if (playbackType === PlaybackType.LOCAL) {
if (isCurrentSongRemoved) {
mpvPlayer!.setQueue(playerData);
setQueue(playerData);
} else {
mpvPlayer!.setQueueNext(playerData);
setQueueNext(playerData);
}
}
@ -687,7 +687,9 @@ export const ContextMenuProvider = ({ children }: ContextMenuProviderProps) => {
},
query: { albumArtistIds: item.albumArtistIds, songId: item.id },
});
handlePlayQueueAdd?.({ byData: [ctx.data[0], ...songs], playType: Play.NOW });
if (songs) {
handlePlayQueueAdd?.({ byData: [ctx.data[0], ...songs], playType: Play.NOW });
}
}, [ctx, handlePlayQueueAdd]);
const handleDownload = useCallback(() => {

View file

@ -19,6 +19,7 @@ import { usePlaybackType } from '/@/renderer/store/settings.store';
import { usePlayerStore, useSetCurrentTime } from '../../../store/player.store';
import { TableConfigDropdown } from '/@/renderer/components/virtual-table';
import { updateSong } from '/@/renderer/features/player/update-remote-song';
import { setQueue, setQueueNext } from '/@/renderer/utils/set-transcoded-queue-data';
const mpvPlayer = isElectron() ? window.electron.mpvPlayer : null;
@ -45,7 +46,7 @@ export const PlayQueueListControls = ({ type, tableRef }: PlayQueueListOptionsPr
const playerData = moveToBottomOfQueue(uniqueIds);
if (playbackType === PlaybackType.LOCAL) {
mpvPlayer!.setQueueNext(playerData);
setQueueNext(playerData);
}
};
@ -57,7 +58,7 @@ export const PlayQueueListControls = ({ type, tableRef }: PlayQueueListOptionsPr
const playerData = moveToTopOfQueue(uniqueIds);
if (playbackType === PlaybackType.LOCAL) {
mpvPlayer!.setQueueNext(playerData);
setQueueNext(playerData);
}
};
@ -72,9 +73,9 @@ export const PlayQueueListControls = ({ type, tableRef }: PlayQueueListOptionsPr
if (playbackType === PlaybackType.LOCAL) {
if (isCurrentSongRemoved) {
mpvPlayer!.setQueue(playerData);
setQueue(playerData);
} else {
mpvPlayer!.setQueueNext(playerData);
setQueueNext(playerData);
}
}
@ -87,7 +88,7 @@ export const PlayQueueListControls = ({ type, tableRef }: PlayQueueListOptionsPr
const playerData = clearQueue();
if (playbackType === PlaybackType.LOCAL) {
mpvPlayer!.setQueue(playerData);
setQueue(playerData);
mpvPlayer!.pause();
}
@ -101,7 +102,7 @@ export const PlayQueueListControls = ({ type, tableRef }: PlayQueueListOptionsPr
const playerData = shuffleQueue();
if (playbackType === PlaybackType.LOCAL) {
mpvPlayer!.setQueueNext(playerData);
setQueueNext(playerData);
}
};

View file

@ -38,6 +38,7 @@ import { VirtualGridAutoSizerContainer } from '/@/renderer/components/virtual-gr
import { useAppFocus } from '/@/renderer/hooks';
import { PlayersRef } from '/@/renderer/features/player/ref/players-ref';
import { updateSong } from '/@/renderer/features/player/update-remote-song';
import { setQueue, setQueueNext } from '/@/renderer/utils/set-transcoded-queue-data';
const mpvPlayer = isElectron() ? window.electron.mpvPlayer : null;
@ -86,7 +87,7 @@ export const PlayQueue = forwardRef(({ type }: QueueProps, ref: Ref<any>) => {
if (playbackType === PlaybackType.LOCAL) {
mpvPlayer!.volume(volume);
mpvPlayer!.setQueue(playerData, false);
setQueue(playerData, false);
} else {
const player =
playerData.current.player === 1
@ -117,7 +118,7 @@ export const PlayQueue = forwardRef(({ type }: QueueProps, ref: Ref<any>) => {
const playerData = reorderQueue(selectedUniqueIds as string[], e.overNode?.data?.uniqueId);
if (playbackType === PlaybackType.LOCAL) {
mpvPlayer!.setQueueNext(playerData);
setQueueNext(playerData);
}
if (type === 'sideDrawerQueue') {

View file

@ -137,7 +137,9 @@ export const CenterControls = ({ playersRef }: CenterControlsProps) => {
if (!isElectron() || playbackType === PlaybackType.WEB) {
// Update twice a second for slightly better performance
interval = setInterval(() => {
setCurrentTime(currentPlayerRef.getCurrentTime());
if (currentPlayerRef) {
setCurrentTime(currentPlayerRef.getCurrentTime());
}
}, 500);
}
}

View file

@ -17,6 +17,7 @@ import debounce from 'lodash/debounce';
import { toast } from '/@/renderer/components';
import { useTranslation } from 'react-i18next';
import { updateSong } from '/@/renderer/features/player/update-remote-song';
import { setAutoNext, setQueue, setQueueNext } from '/@/renderer/utils/set-transcoded-queue-data';
const mpvPlayer = isElectron() ? window.electron.mpvPlayer : null;
const mpvPlayerListener = isElectron() ? window.electron.mpvPlayerListener : null;
@ -131,30 +132,30 @@ export const useCenterControls = (args: { playersRef: any }) => {
if (shuffleStatus === PlayerShuffle.NONE) {
const playerData = setShuffle(PlayerShuffle.TRACK);
remote?.updateShuffle(true);
return mpvPlayer?.setQueueNext(playerData);
return setQueueNext(playerData);
}
const playerData = setShuffle(PlayerShuffle.NONE);
remote?.updateShuffle(false);
return mpvPlayer?.setQueueNext(playerData);
return setQueueNext(playerData);
}, [setShuffle, shuffleStatus]);
const handleToggleRepeat = useCallback(() => {
if (repeatStatus === PlayerRepeat.NONE) {
const playerData = setRepeat(PlayerRepeat.ALL);
remote?.updateRepeat(PlayerRepeat.ALL);
return mpvPlayer?.setQueueNext(playerData);
return setQueueNext(playerData);
}
if (repeatStatus === PlayerRepeat.ALL) {
const playerData = setRepeat(PlayerRepeat.ONE);
remote?.updateRepeat(PlayerRepeat.ONE);
return mpvPlayer?.setQueueNext(playerData);
return setQueueNext(playerData);
}
const playerData = setRepeat(PlayerRepeat.NONE);
remote?.updateRepeat(PlayerRepeat.NONE);
return mpvPlayer?.setQueueNext(playerData);
return setQueueNext(playerData);
}, [repeatStatus, setRepeat]);
const checkIsLastTrack = useCallback(() => {
@ -172,7 +173,7 @@ export const useCenterControls = (args: { playersRef: any }) => {
local: () => {
const playerData = autoNext();
updateSong(playerData.current.song);
mpvPlayer!.autoNext(playerData);
setAutoNext(playerData);
play();
},
web: () => {
@ -186,12 +187,12 @@ export const useCenterControls = (args: { playersRef: any }) => {
if (isLastTrack) {
const playerData = setCurrentIndex(0);
updateSong(playerData.current.song);
mpvPlayer!.setQueue(playerData, true);
setQueue(playerData, true);
pause();
} else {
const playerData = autoNext();
updateSong(playerData.current.song);
mpvPlayer!.autoNext(playerData);
setAutoNext(playerData);
play();
}
},
@ -211,7 +212,7 @@ export const useCenterControls = (args: { playersRef: any }) => {
local: () => {
const playerData = autoNext();
updateSong(playerData.current.song);
mpvPlayer!.autoNext(playerData);
setAutoNext(playerData);
play();
},
web: () => {
@ -257,7 +258,7 @@ export const useCenterControls = (args: { playersRef: any }) => {
local: () => {
const playerData = next();
updateSong(playerData.current.song);
mpvPlayer!.setQueue(playerData);
setQueue(playerData);
},
web: () => {
const playerData = next();
@ -270,12 +271,12 @@ export const useCenterControls = (args: { playersRef: any }) => {
if (isLastTrack) {
const playerData = setCurrentIndex(0);
updateSong(playerData.current.song);
mpvPlayer!.setQueue(playerData, true);
setQueue(playerData, true);
pause();
} else {
const playerData = next();
updateSong(playerData.current.song);
mpvPlayer!.setQueue(playerData);
setQueue(playerData);
}
},
web: () => {
@ -297,7 +298,7 @@ export const useCenterControls = (args: { playersRef: any }) => {
if (!isLastTrack) {
const playerData = next();
updateSong(playerData.current.song);
mpvPlayer!.setQueue(playerData);
setQueue(playerData);
}
},
web: () => {
@ -358,11 +359,11 @@ export const useCenterControls = (args: { playersRef: any }) => {
if (!isFirstTrack) {
const playerData = previous();
updateSong(playerData.current.song);
mpvPlayer!.setQueue(playerData);
setQueue(playerData);
} else {
const playerData = setCurrentIndex(queue.length - 1);
updateSong(playerData.current.song);
mpvPlayer!.setQueue(playerData);
setQueue(playerData);
}
},
web: () => {
@ -383,12 +384,12 @@ export const useCenterControls = (args: { playersRef: any }) => {
if (isFirstTrack) {
const playerData = setCurrentIndex(0);
updateSong(playerData.current.song);
mpvPlayer!.setQueue(playerData, true);
setQueue(playerData, true);
pause();
} else {
const playerData = previous();
updateSong(playerData.current.song);
mpvPlayer!.setQueue(playerData);
setQueue(playerData);
}
},
web: () => {
@ -407,7 +408,7 @@ export const useCenterControls = (args: { playersRef: any }) => {
local: () => {
const playerData = previous();
updateSong(playerData.current.song);
mpvPlayer!.setQueue(playerData);
setQueue(playerData);
},
web: () => {
const playerData = previous();

View file

@ -25,6 +25,7 @@ import { queryKeys } from '/@/renderer/api/query-keys';
import { useTranslation } from 'react-i18next';
import { PlayersRef } from '/@/renderer/features/player/ref/players-ref';
import { updateSong } from '/@/renderer/features/player/update-remote-song';
import { setQueue, setQueueNext } from '/@/renderer/utils/set-transcoded-queue-data';
const getRootQueryKey = (itemType: LibraryItem, serverId: string) => {
let queryKey;
@ -180,9 +181,9 @@ export const useHandlePlayQueueAdd = () => {
if (playType === Play.NOW || !hadSong) {
mpvPlayer!.pause();
mpvPlayer!.setQueue(playerData, false);
setQueue(playerData, false);
} else {
mpvPlayer!.setQueueNext(playerData);
setQueueNext(playerData);
}
} else {
const player =

View file

@ -10,8 +10,7 @@ import { useCurrentStatus, usePlayerStore } from '/@/renderer/store';
import { usePlaybackSettings, useSettingsStoreActions } from '/@/renderer/store/settings.store';
import { PlaybackType, PlayerStatus, PlaybackStyle, CrossfadeStyle } from '/@/renderer/types';
import { useTranslation } from 'react-i18next';
const mpvPlayer = isElectron() ? window.electron.mpvPlayer : null;
import { setQueue } from '/@/renderer/utils/set-transcoded-queue-data';
const getAudioDevice = async () => {
const devices = await navigator.mediaDevices.enumerateDevices();
@ -62,7 +61,7 @@ export const AudioSettings = ({ hasFancyAudio }: { hasFancyAudio: boolean }) =>
setSettings({ playback: { ...settings, type: e as PlaybackType } });
if (isElectron() && e === PlaybackType.LOCAL) {
const queueData = usePlayerStore.getState().actions.getPlayerData();
mpvPlayer!.setQueue(queueData);
setQueue(queueData);
}
}}
/>

View file

@ -6,6 +6,7 @@ import isElectron from 'is-electron';
import { LyricSettings } from '/@/renderer/features/settings/components/playback/lyric-settings';
import { useSettingsStore } from '/@/renderer/store';
import { PlaybackType } from '/@/renderer/types';
import { TranscodeSettings } from '/@/renderer/features/settings/components/playback/transcode-settings';
const MpvSettings = lazy(() =>
import('/@/renderer/features/settings/components/playback/mpv-settings').then((module) => {
@ -28,6 +29,7 @@ export const PlaybackTab = () => {
<Stack spacing="md">
<AudioSettings hasFancyAudio={hasFancyAudio} />
<Suspense fallback={<></>}>{hasFancyAudio && <MpvSettings />}</Suspense>
<TranscodeSettings />
<ScrobbleSettings />
<LyricSettings />
</Stack>

View file

@ -0,0 +1,89 @@
import { NumberInput, Switch, TextInput } from '/@/renderer/components';
import { usePlaybackSettings, useSettingsStoreActions } from '/@/renderer/store/settings.store';
import { SettingOption, SettingsSection } from '../settings-section';
import { useTranslation } from 'react-i18next';
export const TranscodeSettings = () => {
const { t } = useTranslation();
const { transcode } = usePlaybackSettings();
const { setTranscodingConfig } = useSettingsStoreActions();
const note = t('setting.transcodeNote', { postProcess: 'sentenceCase' });
const transcodeOptions: SettingOption[] = [
{
control: (
<Switch
aria-label="Toggle transcode"
defaultChecked={transcode.enabled}
onChange={(e) => {
setTranscodingConfig({
...transcode,
enabled: e.currentTarget.checked,
});
}}
/>
),
description: t('setting.transcode', {
context: 'description',
postProcess: 'sentenceCase',
}),
note,
title: t('setting.transcode', { postProcess: 'sentenceCase' }),
},
{
control: (
<NumberInput
aria-label="Transcode bitrate"
defaultValue={transcode.bitrate}
min={0}
placeholder="mp3, opus"
w={100}
onBlur={(e) => {
setTranscodingConfig({
...transcode,
bitrate: e.currentTarget.value
? Number(e.currentTarget.value)
: undefined,
});
}}
/>
),
description: t('setting.transcodeBitrate', {
context: 'description',
postProcess: 'sentenceCase',
}),
isHidden: !transcode.enabled,
note,
title: t('setting.transcodeBitrate', { postProcess: 'sentenceCase' }),
},
{
control: (
<TextInput
aria-label="transcoding format"
defaultValue={transcode.format}
width={100}
onBlur={(e) => {
setTranscodingConfig({
...transcode,
format: e.currentTarget.value || undefined,
});
}}
/>
),
description: t('setting.transcodeFormat', {
context: 'description',
postProcess: 'sentenceCase',
}),
isHidden: !transcode.enabled,
note,
title: t('setting.transcodeFormat', { postProcess: 'sentenceCase' }),
},
];
return (
<SettingsSection
divider
options={transcodeOptions}
/>
);
};