mirror of
https://github.com/antebudimir/feishin.git
synced 2026-01-01 02:13:33 +00:00
Merge pull request #501 from kgarner7/allow-no-mpv
- OOBE default to web player - Allow mpv to run using PATH env - Add improved mpv error logging - Add web player fallback on mpv error
This commit is contained in:
commit
eab11658bb
25 changed files with 706 additions and 418 deletions
|
|
@ -1,23 +1,36 @@
|
|||
import { useEffect, useState } from 'react';
|
||||
import isElectron from 'is-electron';
|
||||
import { FileInput, Text, Button } from '/@/renderer/components';
|
||||
import { FileInput, Text, Button, Checkbox } from '/@/renderer/components';
|
||||
import { usePlaybackSettings, useSettingsStoreActions } from '/@/renderer/store';
|
||||
import { PlaybackType } from '/@/renderer/types';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const localSettings = isElectron() ? window.electron.localSettings : null;
|
||||
|
||||
export const MpvRequired = () => {
|
||||
const [mpvPath, setMpvPath] = useState('');
|
||||
const settings = usePlaybackSettings();
|
||||
const { setSettings } = useSettingsStoreActions();
|
||||
const [disabled, setDisabled] = useState(false);
|
||||
const { t } = useTranslation();
|
||||
|
||||
const handleSetMpvPath = (e: File) => {
|
||||
localSettings?.set('mpv_path', e.path);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const getMpvPath = async () => {
|
||||
if (!localSettings) return setMpvPath('');
|
||||
const mpvPath = localSettings.get('mpv_path') as string;
|
||||
return setMpvPath(mpvPath);
|
||||
};
|
||||
const handleSetDisableMpv = (disabled: boolean) => {
|
||||
setDisabled(disabled);
|
||||
localSettings?.set('disable_mpv', disabled);
|
||||
|
||||
getMpvPath();
|
||||
setSettings({
|
||||
playback: { ...settings, type: disabled ? PlaybackType.WEB : PlaybackType.LOCAL },
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!localSettings) return setMpvPath('');
|
||||
const mpvPath = localSettings.get('mpv_path') as string;
|
||||
return setMpvPath(mpvPath);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
|
|
@ -34,9 +47,15 @@ export const MpvRequired = () => {
|
|||
</a>
|
||||
</Text>
|
||||
<FileInput
|
||||
disabled={disabled}
|
||||
placeholder={mpvPath}
|
||||
onChange={handleSetMpvPath}
|
||||
/>
|
||||
<Text>{t('setting.disable_mpv', { context: 'description' })}</Text>
|
||||
<Checkbox
|
||||
label={t('setting.disableMpv')}
|
||||
onChange={(e) => handleSetDisableMpv(e.currentTarget.checked)}
|
||||
/>
|
||||
<Button onClick={() => localSettings?.restart()}>Restart</Button>
|
||||
</>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,48 +1,22 @@
|
|||
import { useState, useEffect } from 'react';
|
||||
import { Center, Group, Stack } from '@mantine/core';
|
||||
import isElectron from 'is-electron';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { RiCheckFill } from 'react-icons/ri';
|
||||
import { Link, Navigate } from 'react-router-dom';
|
||||
import { Button, PageHeader, Text } from '/@/renderer/components';
|
||||
import { ActionRequiredContainer } from '/@/renderer/features/action-required/components/action-required-container';
|
||||
import { MpvRequired } from '/@/renderer/features/action-required/components/mpv-required';
|
||||
import { ServerCredentialRequired } from '/@/renderer/features/action-required/components/server-credential-required';
|
||||
import { ServerRequired } from '/@/renderer/features/action-required/components/server-required';
|
||||
import { AnimatedPage } from '/@/renderer/features/shared';
|
||||
import { AppRoute } from '/@/renderer/router/routes';
|
||||
import { useCurrentServer } from '/@/renderer/store';
|
||||
|
||||
const localSettings = isElectron() ? window.electron.localSettings : null;
|
||||
|
||||
const ActionRequiredRoute = () => {
|
||||
const { t } = useTranslation();
|
||||
const currentServer = useCurrentServer();
|
||||
const [isMpvRequired, setIsMpvRequired] = useState(false);
|
||||
const isServerRequired = !currentServer;
|
||||
const isCredentialRequired = false;
|
||||
|
||||
useEffect(() => {
|
||||
const getMpvPath = async () => {
|
||||
if (!localSettings) return setIsMpvRequired(false);
|
||||
const mpvPath = await localSettings.get('mpv_path');
|
||||
|
||||
if (mpvPath) {
|
||||
return setIsMpvRequired(false);
|
||||
}
|
||||
|
||||
return setIsMpvRequired(true);
|
||||
};
|
||||
|
||||
getMpvPath();
|
||||
}, []);
|
||||
|
||||
const checks = [
|
||||
{
|
||||
component: <MpvRequired />,
|
||||
title: t('error.mpvRequired', { postProcess: 'sentenceCase' }),
|
||||
valid: !isMpvRequired,
|
||||
},
|
||||
{
|
||||
component: <ServerCredentialRequired />,
|
||||
title: t('error.credentialsRequired', { postProcess: 'sentenceCase' }),
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ import {
|
|||
usePlayerStore,
|
||||
useQueueControls,
|
||||
} from '/@/renderer/store';
|
||||
import { usePlayerType } from '/@/renderer/store/settings.store';
|
||||
import { usePlaybackType } from '/@/renderer/store/settings.store';
|
||||
import { Play, PlaybackType } from '/@/renderer/types';
|
||||
|
||||
type ContextMenuContextProps = {
|
||||
|
|
@ -575,7 +575,7 @@ export const ContextMenuProvider = ({ children }: ContextMenuProviderProps) => {
|
|||
[ctx.data, ctx.dataNodes, updateRatingMutation],
|
||||
);
|
||||
|
||||
const playerType = usePlayerType();
|
||||
const playbackType = usePlaybackType();
|
||||
const { moveToBottomOfQueue, moveToTopOfQueue, removeFromQueue } = useQueueControls();
|
||||
|
||||
const handleMoveToBottom = useCallback(() => {
|
||||
|
|
@ -584,10 +584,10 @@ export const ContextMenuProvider = ({ children }: ContextMenuProviderProps) => {
|
|||
|
||||
const playerData = moveToBottomOfQueue(uniqueIds);
|
||||
|
||||
if (playerType === PlaybackType.LOCAL) {
|
||||
if (playbackType === PlaybackType.LOCAL) {
|
||||
mpvPlayer!.setQueueNext(playerData);
|
||||
}
|
||||
}, [ctx.dataNodes, moveToBottomOfQueue, playerType]);
|
||||
}, [ctx.dataNodes, moveToBottomOfQueue, playbackType]);
|
||||
|
||||
const handleMoveToTop = useCallback(() => {
|
||||
const uniqueIds = ctx.dataNodes?.map((row) => row.data.uniqueId);
|
||||
|
|
@ -595,10 +595,10 @@ export const ContextMenuProvider = ({ children }: ContextMenuProviderProps) => {
|
|||
|
||||
const playerData = moveToTopOfQueue(uniqueIds);
|
||||
|
||||
if (playerType === PlaybackType.LOCAL) {
|
||||
if (playbackType === PlaybackType.LOCAL) {
|
||||
mpvPlayer!.setQueueNext(playerData);
|
||||
}
|
||||
}, [ctx.dataNodes, moveToTopOfQueue, playerType]);
|
||||
}, [ctx.dataNodes, moveToTopOfQueue, playbackType]);
|
||||
|
||||
const handleRemoveSelected = useCallback(() => {
|
||||
const uniqueIds = ctx.dataNodes?.map((row) => row.data.uniqueId);
|
||||
|
|
@ -608,7 +608,7 @@ export const ContextMenuProvider = ({ children }: ContextMenuProviderProps) => {
|
|||
const playerData = removeFromQueue(uniqueIds);
|
||||
const isCurrentSongRemoved = currentSong && uniqueIds.includes(currentSong?.uniqueId);
|
||||
|
||||
if (playerType === PlaybackType.LOCAL) {
|
||||
if (playbackType === PlaybackType.LOCAL) {
|
||||
if (isCurrentSongRemoved) {
|
||||
mpvPlayer!.setQueue(playerData);
|
||||
} else {
|
||||
|
|
@ -621,7 +621,7 @@ export const ContextMenuProvider = ({ children }: ContextMenuProviderProps) => {
|
|||
if (isCurrentSongRemoved) {
|
||||
remote?.updateSong({ song: playerData.current.song });
|
||||
}
|
||||
}, [ctx.dataNodes, ctx.tableApi, playerType, removeFromQueue]);
|
||||
}, [ctx.dataNodes, ctx.tableApi, playbackType, removeFromQueue]);
|
||||
|
||||
const handleDeselectAll = useCallback(() => {
|
||||
ctx.tableApi?.deselectAll();
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import {
|
|||
useCurrentStatus,
|
||||
useCurrentTime,
|
||||
useLyricsSettings,
|
||||
usePlayerType,
|
||||
usePlaybackType,
|
||||
useSeeked,
|
||||
} from '/@/renderer/store';
|
||||
import { PlaybackType, PlayerStatus } from '/@/renderer/types';
|
||||
|
|
@ -59,7 +59,7 @@ export const SynchronizedLyrics = ({
|
|||
}: SynchronizedLyricsProps) => {
|
||||
const playersRef = PlayersRef;
|
||||
const status = useCurrentStatus();
|
||||
const playerType = usePlayerType();
|
||||
const playbackType = usePlaybackType();
|
||||
const now = useCurrentTime();
|
||||
const settings = useLyricsSettings();
|
||||
|
||||
|
|
@ -96,7 +96,7 @@ export const SynchronizedLyrics = ({
|
|||
};
|
||||
|
||||
const getCurrentTime = useCallback(async () => {
|
||||
if (isElectron() && playerType !== PlaybackType.WEB) {
|
||||
if (isElectron() && playbackType !== PlaybackType.WEB) {
|
||||
if (mpvPlayer) {
|
||||
return mpvPlayer.getCurrentTime();
|
||||
}
|
||||
|
|
@ -116,7 +116,7 @@ export const SynchronizedLyrics = ({
|
|||
if (!player) return 0;
|
||||
|
||||
return player.currentTime;
|
||||
}, [playerType, playersRef]);
|
||||
}, [playbackType, playersRef]);
|
||||
|
||||
const setCurrentLyric = useCallback(
|
||||
(timeInMs: number, epoch?: number, targetIndex?: number) => {
|
||||
|
|
@ -222,7 +222,7 @@ export const SynchronizedLyrics = ({
|
|||
}
|
||||
|
||||
return () => {};
|
||||
}, [getCurrentTime, lyrics, playerType, setCurrentLyric, status]);
|
||||
}, [getCurrentTime, lyrics, playbackType, setCurrentLyric, status]);
|
||||
|
||||
useEffect(() => {
|
||||
// This handler is used to deal with changes to the current delay. If the offset
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import {
|
|||
import { Song } from '/@/renderer/api/types';
|
||||
import { usePlayerControls, useQueueControls } from '/@/renderer/store';
|
||||
import { PlaybackType, TableType } from '/@/renderer/types';
|
||||
import { usePlayerType } from '/@/renderer/store/settings.store';
|
||||
import { usePlaybackType } from '/@/renderer/store/settings.store';
|
||||
import { usePlayerStore, useSetCurrentTime } from '../../../store/player.store';
|
||||
import { TableConfigDropdown } from '/@/renderer/components/virtual-table';
|
||||
|
||||
|
|
@ -34,7 +34,7 @@ export const PlayQueueListControls = ({ type, tableRef }: PlayQueueListOptionsPr
|
|||
|
||||
const { pause } = usePlayerControls();
|
||||
|
||||
const playerType = usePlayerType();
|
||||
const playbackType = usePlaybackType();
|
||||
const setCurrentTime = useSetCurrentTime();
|
||||
|
||||
const handleMoveToBottom = () => {
|
||||
|
|
@ -44,7 +44,7 @@ export const PlayQueueListControls = ({ type, tableRef }: PlayQueueListOptionsPr
|
|||
|
||||
const playerData = moveToBottomOfQueue(uniqueIds);
|
||||
|
||||
if (playerType === PlaybackType.LOCAL) {
|
||||
if (playbackType === PlaybackType.LOCAL) {
|
||||
mpvPlayer!.setQueueNext(playerData);
|
||||
}
|
||||
};
|
||||
|
|
@ -56,7 +56,7 @@ export const PlayQueueListControls = ({ type, tableRef }: PlayQueueListOptionsPr
|
|||
|
||||
const playerData = moveToTopOfQueue(uniqueIds);
|
||||
|
||||
if (playerType === PlaybackType.LOCAL) {
|
||||
if (playbackType === PlaybackType.LOCAL) {
|
||||
mpvPlayer!.setQueueNext(playerData);
|
||||
}
|
||||
};
|
||||
|
|
@ -70,7 +70,7 @@ export const PlayQueueListControls = ({ type, tableRef }: PlayQueueListOptionsPr
|
|||
const playerData = removeFromQueue(uniqueIds);
|
||||
const isCurrentSongRemoved = currentSong && uniqueIds.includes(currentSong.uniqueId);
|
||||
|
||||
if (playerType === PlaybackType.LOCAL) {
|
||||
if (playbackType === PlaybackType.LOCAL) {
|
||||
if (isCurrentSongRemoved) {
|
||||
mpvPlayer!.setQueue(playerData);
|
||||
} else {
|
||||
|
|
@ -86,7 +86,7 @@ export const PlayQueueListControls = ({ type, tableRef }: PlayQueueListOptionsPr
|
|||
const handleClearQueue = () => {
|
||||
const playerData = clearQueue();
|
||||
|
||||
if (playerType === PlaybackType.LOCAL) {
|
||||
if (playbackType === PlaybackType.LOCAL) {
|
||||
mpvPlayer!.setQueue(playerData);
|
||||
mpvPlayer!.pause();
|
||||
}
|
||||
|
|
@ -100,7 +100,7 @@ export const PlayQueueListControls = ({ type, tableRef }: PlayQueueListOptionsPr
|
|||
const handleShuffleQueue = () => {
|
||||
const playerData = shuffleQueue();
|
||||
|
||||
if (playerType === PlaybackType.LOCAL) {
|
||||
if (playbackType === PlaybackType.LOCAL) {
|
||||
mpvPlayer!.setQueueNext(playerData);
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ import {
|
|||
useVolume,
|
||||
} from '/@/renderer/store';
|
||||
import {
|
||||
usePlayerType,
|
||||
usePlaybackType,
|
||||
useSettingsStore,
|
||||
useSettingsStoreActions,
|
||||
useTableSettings,
|
||||
|
|
@ -56,7 +56,7 @@ export const PlayQueue = forwardRef(({ type }: QueueProps, ref: Ref<any>) => {
|
|||
const { setAppStore } = useAppStoreActions();
|
||||
const tableConfig = useTableSettings(type);
|
||||
const [gridApi, setGridApi] = useState<AgGridReactType | undefined>();
|
||||
const playerType = usePlayerType();
|
||||
const playbackType = usePlaybackType();
|
||||
const { play } = usePlayerControls();
|
||||
const volume = useVolume();
|
||||
const isFocused = useAppFocus();
|
||||
|
|
@ -87,7 +87,7 @@ export const PlayQueue = forwardRef(({ type }: QueueProps, ref: Ref<any>) => {
|
|||
status: PlayerStatus.PLAYING,
|
||||
});
|
||||
|
||||
if (playerType === PlaybackType.LOCAL) {
|
||||
if (playbackType === PlaybackType.LOCAL) {
|
||||
mpvPlayer!.volume(volume);
|
||||
mpvPlayer!.setQueue(playerData);
|
||||
mpvPlayer!.play();
|
||||
|
|
@ -111,7 +111,7 @@ export const PlayQueue = forwardRef(({ type }: QueueProps, ref: Ref<any>) => {
|
|||
|
||||
const playerData = reorderQueue(selectedUniqueIds as string[], e.overNode?.data?.uniqueId);
|
||||
|
||||
if (playerType === PlaybackType.LOCAL) {
|
||||
if (playbackType === PlaybackType.LOCAL) {
|
||||
mpvPlayer!.setQueueNext(playerData);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ import {
|
|||
} from '/@/renderer/store';
|
||||
import {
|
||||
useHotkeySettings,
|
||||
usePlayerType,
|
||||
usePlaybackType,
|
||||
useSettingsStore,
|
||||
} from '/@/renderer/store/settings.store';
|
||||
import { PlayerStatus, PlaybackType, PlayerShuffle, PlayerRepeat } from '/@/renderer/types';
|
||||
|
|
@ -99,7 +99,7 @@ export const CenterControls = ({ playersRef }: CenterControlsProps) => {
|
|||
const currentSong = useCurrentSong();
|
||||
const skip = useSettingsStore((state) => state.general.skipButtons);
|
||||
const buttonSize = useSettingsStore((state) => state.general.buttonSize);
|
||||
const playerType = usePlayerType();
|
||||
const playbackType = usePlaybackType();
|
||||
const player1 = playersRef?.current?.player1;
|
||||
const player2 = playersRef?.current?.player2;
|
||||
const status = useCurrentStatus();
|
||||
|
|
@ -134,7 +134,7 @@ export const CenterControls = ({ playersRef }: CenterControlsProps) => {
|
|||
let interval: any;
|
||||
|
||||
if (status === PlayerStatus.PLAYING && !isSeeking) {
|
||||
if (!isElectron() || playerType === PlaybackType.WEB) {
|
||||
if (!isElectron() || playbackType === PlaybackType.WEB) {
|
||||
interval = setInterval(() => {
|
||||
setCurrentTime(currentPlayerRef.getCurrentTime());
|
||||
}, 1000);
|
||||
|
|
@ -144,7 +144,7 @@ export const CenterControls = ({ playersRef }: CenterControlsProps) => {
|
|||
}
|
||||
|
||||
return () => clearInterval(interval);
|
||||
}, [currentPlayerRef, isSeeking, setCurrentTime, playerType, status]);
|
||||
}, [currentPlayerRef, isSeeking, setCurrentTime, playbackType, status]);
|
||||
|
||||
const [seekValue, setSeekValue] = useState(0);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { useCallback } from 'react';
|
||||
import isElectron from 'is-electron';
|
||||
import styled from 'styled-components';
|
||||
import { useSettingsStore } from '/@/renderer/store/settings.store';
|
||||
import { usePlaybackType, useSettingsStore } from '/@/renderer/store/settings.store';
|
||||
import { PlaybackType } from '/@/renderer/types';
|
||||
import { AudioPlayer } from '/@/renderer/components';
|
||||
import {
|
||||
|
|
@ -64,6 +64,7 @@ const remote = isElectron() ? window.electron.remote : null;
|
|||
export const Playerbar = () => {
|
||||
const playersRef = PlayersRef;
|
||||
const settings = useSettingsStore((state) => state.playback);
|
||||
const playbackType = usePlaybackType();
|
||||
const volume = useVolume();
|
||||
const player1 = usePlayer1Data();
|
||||
const player2 = usePlayer2Data();
|
||||
|
|
@ -96,7 +97,7 @@ export const Playerbar = () => {
|
|||
<RightControls />
|
||||
</RightGridItem>
|
||||
</PlayerbarControlsGrid>
|
||||
{settings.type === PlaybackType.WEB && (
|
||||
{playbackType === PlaybackType.WEB && (
|
||||
<AudioPlayer
|
||||
ref={playersRef}
|
||||
autoNext={autoNextFn}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// import { write, writeFile } from 'fs';
|
||||
// import { deflate } from 'zlib';
|
||||
import { useCallback, useEffect } from 'react';
|
||||
import isElectron from 'is-electron';
|
||||
import { PlaybackType, PlayerRepeat, PlayerShuffle, PlayerStatus } from '/@/renderer/types';
|
||||
|
|
@ -13,7 +11,7 @@ import {
|
|||
useSetCurrentTime,
|
||||
useShuffleStatus,
|
||||
} from '/@/renderer/store';
|
||||
import { usePlayerType, useSettingsStore } from '/@/renderer/store/settings.store';
|
||||
import { usePlaybackType } from '/@/renderer/store/settings.store';
|
||||
import { useScrobble } from '/@/renderer/features/player/hooks/use-scrobble';
|
||||
import debounce from 'lodash/debounce';
|
||||
import { QueueSong } from '/@/renderer/api/types';
|
||||
|
|
@ -32,7 +30,6 @@ export const useCenterControls = (args: { playersRef: any }) => {
|
|||
const { t } = useTranslation();
|
||||
const { playersRef } = args;
|
||||
|
||||
const settings = useSettingsStore((state) => state.playback);
|
||||
const currentPlayer = useCurrentPlayer();
|
||||
const { setShuffle, setRepeat, play, pause, previous, next, setCurrentIndex, autoNext } =
|
||||
usePlayerControls();
|
||||
|
|
@ -41,7 +38,7 @@ export const useCenterControls = (args: { playersRef: any }) => {
|
|||
const playerStatus = useCurrentStatus();
|
||||
const repeatStatus = useRepeatStatus();
|
||||
const shuffleStatus = useShuffleStatus();
|
||||
const playerType = usePlayerType();
|
||||
const playbackType = usePlaybackType();
|
||||
const player1Ref = playersRef?.current?.player1;
|
||||
const player2Ref = playersRef?.current?.player2;
|
||||
const currentPlayerRef = currentPlayer === 1 ? player1Ref : player2Ref;
|
||||
|
|
@ -77,7 +74,7 @@ export const useCenterControls = (args: { playersRef: any }) => {
|
|||
resetPlayers();
|
||||
}, [player1Ref, player2Ref, resetPlayers]);
|
||||
|
||||
const isMpvPlayer = isElectron() && settings.type === PlaybackType.LOCAL;
|
||||
const isMpvPlayer = isElectron() && playbackType === PlaybackType.LOCAL;
|
||||
|
||||
const mprisUpdateSong = (args?: {
|
||||
currentTime?: number;
|
||||
|
|
@ -282,13 +279,13 @@ export const useCenterControls = (args: { playersRef: any }) => {
|
|||
|
||||
switch (repeatStatus) {
|
||||
case PlayerRepeat.NONE:
|
||||
handleRepeatNone[playerType]();
|
||||
handleRepeatNone[playbackType]();
|
||||
break;
|
||||
case PlayerRepeat.ALL:
|
||||
handleRepeatAll[playerType]();
|
||||
handleRepeatAll[playbackType]();
|
||||
break;
|
||||
case PlayerRepeat.ONE:
|
||||
handleRepeatOne[playerType]();
|
||||
handleRepeatOne[playbackType]();
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
@ -299,7 +296,7 @@ export const useCenterControls = (args: { playersRef: any }) => {
|
|||
checkIsLastTrack,
|
||||
pause,
|
||||
play,
|
||||
playerType,
|
||||
playbackType,
|
||||
repeatStatus,
|
||||
resetPlayers,
|
||||
setCurrentIndex,
|
||||
|
|
@ -380,13 +377,13 @@ export const useCenterControls = (args: { playersRef: any }) => {
|
|||
|
||||
switch (repeatStatus) {
|
||||
case PlayerRepeat.NONE:
|
||||
handleRepeatNone[playerType]();
|
||||
handleRepeatNone[playbackType]();
|
||||
break;
|
||||
case PlayerRepeat.ALL:
|
||||
handleRepeatAll[playerType]();
|
||||
handleRepeatAll[playbackType]();
|
||||
break;
|
||||
case PlayerRepeat.ONE:
|
||||
handleRepeatOne[playerType]();
|
||||
handleRepeatOne[playbackType]();
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
@ -398,7 +395,7 @@ export const useCenterControls = (args: { playersRef: any }) => {
|
|||
checkIsLastTrack,
|
||||
next,
|
||||
pause,
|
||||
playerType,
|
||||
playbackType,
|
||||
repeatStatus,
|
||||
resetPlayers,
|
||||
setCurrentIndex,
|
||||
|
|
@ -511,13 +508,13 @@ export const useCenterControls = (args: { playersRef: any }) => {
|
|||
|
||||
switch (repeatStatus) {
|
||||
case PlayerRepeat.NONE:
|
||||
handleRepeatNone[playerType]();
|
||||
handleRepeatNone[playbackType]();
|
||||
break;
|
||||
case PlayerRepeat.ALL:
|
||||
handleRepeatAll[playerType]();
|
||||
handleRepeatAll[playbackType]();
|
||||
break;
|
||||
case PlayerRepeat.ONE:
|
||||
handleRepeatOne[playerType]();
|
||||
handleRepeatOne[playbackType]();
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
@ -531,7 +528,7 @@ export const useCenterControls = (args: { playersRef: any }) => {
|
|||
handleScrobbleFromSongRestart,
|
||||
isMpvPlayer,
|
||||
pause,
|
||||
playerType,
|
||||
playbackType,
|
||||
previous,
|
||||
queue.length,
|
||||
repeatStatus,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { useCallback, useRef } from 'react';
|
||||
import { useQueryClient } from '@tanstack/react-query';
|
||||
import { useCurrentServer, usePlayerControls, usePlayerStore } from '/@/renderer/store';
|
||||
import { usePlayerType } from '/@/renderer/store/settings.store';
|
||||
import { usePlaybackType } from '/@/renderer/store/settings.store';
|
||||
import {
|
||||
PlayQueueAddOptions,
|
||||
Play,
|
||||
|
|
@ -65,7 +65,7 @@ const addToQueue = usePlayerStore.getState().actions.addToQueue;
|
|||
export const useHandlePlayQueueAdd = () => {
|
||||
const { t } = useTranslation();
|
||||
const queryClient = useQueryClient();
|
||||
const playerType = usePlayerType();
|
||||
const playbackType = usePlaybackType();
|
||||
const server = useCurrentServer();
|
||||
const { play } = usePlayerControls();
|
||||
const timeoutIds = useRef<Record<string, ReturnType<typeof setTimeout>> | null>({});
|
||||
|
|
@ -170,7 +170,7 @@ export const useHandlePlayQueueAdd = () => {
|
|||
const hadSong = usePlayerStore.getState().queue.default.length > 0;
|
||||
const playerData = addToQueue({ initialIndex: initialSongIndex, playType, songs });
|
||||
|
||||
if (playerType === PlaybackType.LOCAL) {
|
||||
if (playbackType === PlaybackType.LOCAL) {
|
||||
mpvPlayer!.volume(usePlayerStore.getState().volume);
|
||||
|
||||
if (playType === Play.NOW || !hadSong) {
|
||||
|
|
@ -198,7 +198,7 @@ export const useHandlePlayQueueAdd = () => {
|
|||
|
||||
return null;
|
||||
},
|
||||
[play, playerType, queryClient, server, t],
|
||||
[play, playbackType, queryClient, server, t],
|
||||
);
|
||||
|
||||
return handlePlayQueueAdd;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,15 @@
|
|||
import { useEffect, useState } from 'react';
|
||||
import { Divider, Stack } from '@mantine/core';
|
||||
import { Divider, Group, Stack } from '@mantine/core';
|
||||
import isElectron from 'is-electron';
|
||||
import { FileInput, Textarea, Text, Select, NumberInput, Switch } from '/@/renderer/components';
|
||||
import {
|
||||
FileInput,
|
||||
Textarea,
|
||||
Text,
|
||||
Select,
|
||||
NumberInput,
|
||||
Switch,
|
||||
Button,
|
||||
} from '/@/renderer/components';
|
||||
import {
|
||||
SettingsSection,
|
||||
SettingOption,
|
||||
|
|
@ -9,10 +17,13 @@ import {
|
|||
import {
|
||||
SettingsState,
|
||||
usePlaybackSettings,
|
||||
useSettingsStore,
|
||||
useSettingsStoreActions,
|
||||
} from '/@/renderer/store/settings.store';
|
||||
import { PlaybackType } from '/@/renderer/types';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { RiCloseLine, RiRestartLine } from 'react-icons/ri';
|
||||
import { usePlayerControls, usePlayerStore, useQueueControls } from '/@/renderer/store';
|
||||
|
||||
const localSettings = isElectron() ? window.electron.localSettings : null;
|
||||
const mpvPlayer = isElectron() ? window.electron.mpvPlayer : null;
|
||||
|
|
@ -64,11 +75,20 @@ export const MpvSettings = () => {
|
|||
const { t } = useTranslation();
|
||||
const settings = usePlaybackSettings();
|
||||
const { setSettings } = useSettingsStoreActions();
|
||||
const { pause } = usePlayerControls();
|
||||
const { clearQueue } = useQueueControls();
|
||||
|
||||
const [mpvPath, setMpvPath] = useState('');
|
||||
|
||||
const handleSetMpvPath = (e: File) => {
|
||||
const handleSetMpvPath = (e: File | null) => {
|
||||
if (e === null) {
|
||||
localSettings?.set('mpv_path', undefined);
|
||||
setMpvPath('');
|
||||
return;
|
||||
}
|
||||
|
||||
localSettings?.set('mpv_path', e.path);
|
||||
setMpvPath(e.path);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -100,6 +120,22 @@ export const MpvSettings = () => {
|
|||
mpvPlayer?.setProperties(mpvSetting);
|
||||
};
|
||||
|
||||
const handleReloadMpv = () => {
|
||||
pause();
|
||||
clearQueue();
|
||||
|
||||
const extraParameters = useSettingsStore.getState().playback.mpvExtraParameters;
|
||||
const properties: Record<string, any> = {
|
||||
speed: usePlayerStore.getState().current.speed,
|
||||
...getMpvProperties(useSettingsStore.getState().playback.mpvProperties),
|
||||
};
|
||||
mpvPlayer?.restart({
|
||||
binaryPath: mpvPath || undefined,
|
||||
extraParameters,
|
||||
properties,
|
||||
});
|
||||
};
|
||||
|
||||
const handleSetExtraParameters = (data: string[]) => {
|
||||
setSettings({
|
||||
playback: {
|
||||
|
|
@ -112,11 +148,38 @@ export const MpvSettings = () => {
|
|||
const options: SettingOption[] = [
|
||||
{
|
||||
control: (
|
||||
<FileInput
|
||||
placeholder={mpvPath}
|
||||
width={225}
|
||||
onChange={handleSetMpvPath}
|
||||
/>
|
||||
<Group spacing="sm">
|
||||
<Button
|
||||
tooltip={{
|
||||
label: t('common.reload', { postProcess: 'titleCase' }),
|
||||
openDelay: 0,
|
||||
}}
|
||||
variant="subtle"
|
||||
onClick={handleReloadMpv}
|
||||
>
|
||||
<RiRestartLine />
|
||||
</Button>
|
||||
<FileInput
|
||||
placeholder={mpvPath}
|
||||
rightSection={
|
||||
mpvPath && (
|
||||
<Button
|
||||
compact
|
||||
tooltip={{
|
||||
label: t('common.clear', { postProcess: 'titleCase' }),
|
||||
openDelay: 0,
|
||||
}}
|
||||
variant="subtle"
|
||||
onClick={() => handleSetMpvPath(null)}
|
||||
>
|
||||
<RiCloseLine />
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
width={200}
|
||||
onChange={handleSetMpvPath}
|
||||
/>
|
||||
</Group>
|
||||
),
|
||||
description: t('setting.mpvExecutablePath', {
|
||||
context: 'description',
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue