mirror of
https://github.com/jeffvli/feishin.git
synced 2026-08-08 13:23:16 +02:00
Add support for OpenSubsonic enhanced lyrics (#2208)
This commit is contained in:
@@ -162,7 +162,7 @@ export function MpvPlayer() {
|
||||
try {
|
||||
const time = await mpvPlayer.getCurrentTime();
|
||||
if (time !== undefined) {
|
||||
setTimestamp(Number(time.toFixed(0)));
|
||||
setTimestamp(time);
|
||||
}
|
||||
} catch {
|
||||
// Do nothing
|
||||
|
||||
@@ -78,6 +78,10 @@ export function WaveSurferPlayer() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (num === 1) {
|
||||
setTimestamp(e.playedSeconds);
|
||||
}
|
||||
|
||||
switch (transitionType) {
|
||||
case PlayerStyle.CROSSFADE:
|
||||
crossfadeHandler({
|
||||
@@ -106,7 +110,7 @@ export function WaveSurferPlayer() {
|
||||
break;
|
||||
}
|
||||
},
|
||||
[crossfadeDuration, isTransitioning, num, player2, transitionType, volume],
|
||||
[crossfadeDuration, isTransitioning, num, player2, setTimestamp, transitionType, volume],
|
||||
);
|
||||
|
||||
const onProgressPlayer2 = useCallback(
|
||||
@@ -115,6 +119,10 @@ export function WaveSurferPlayer() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (num === 2) {
|
||||
setTimestamp(e.playedSeconds);
|
||||
}
|
||||
|
||||
switch (transitionType) {
|
||||
case PlayerStyle.CROSSFADE:
|
||||
crossfadeHandler({
|
||||
@@ -143,7 +151,7 @@ export function WaveSurferPlayer() {
|
||||
break;
|
||||
}
|
||||
},
|
||||
[crossfadeDuration, isTransitioning, num, player1, transitionType, volume],
|
||||
[crossfadeDuration, isTransitioning, num, player1, setTimestamp, transitionType, volume],
|
||||
);
|
||||
|
||||
const handleOnEndedPlayer1 = useCallback(() => {
|
||||
@@ -237,7 +245,7 @@ export function WaveSurferPlayer() {
|
||||
transitionType === PlayerStyle.CROSSFADE ||
|
||||
transitionType === PlayerStyle.GAPLESS
|
||||
) {
|
||||
setTimestamp(Number(currentTime.toFixed(0)));
|
||||
setTimestamp(currentTime);
|
||||
}
|
||||
}, 500);
|
||||
|
||||
|
||||
@@ -127,6 +127,10 @@ export function WebPlayer() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (num === 1) {
|
||||
setTimestamp(e.playedSeconds);
|
||||
}
|
||||
|
||||
if (repeat === PlayerRepeat.ONE) {
|
||||
handleRepeatOne(1, e.playedSeconds, getDuration(playerRef.current.player1().ref));
|
||||
return;
|
||||
@@ -170,6 +174,7 @@ export function WebPlayer() {
|
||||
num,
|
||||
player2,
|
||||
repeat,
|
||||
setTimestamp,
|
||||
transitionType,
|
||||
volume,
|
||||
],
|
||||
@@ -181,6 +186,10 @@ export function WebPlayer() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (num === 2) {
|
||||
setTimestamp(e.playedSeconds);
|
||||
}
|
||||
|
||||
if (repeat === PlayerRepeat.ONE) {
|
||||
handleRepeatOne(2, e.playedSeconds, getDuration(playerRef.current.player2().ref));
|
||||
return;
|
||||
@@ -224,6 +233,7 @@ export function WebPlayer() {
|
||||
num,
|
||||
player1,
|
||||
repeat,
|
||||
setTimestamp,
|
||||
transitionType,
|
||||
volume,
|
||||
],
|
||||
@@ -388,7 +398,7 @@ export function WebPlayer() {
|
||||
transitionType === PlayerStyle.CROSSFADE ||
|
||||
transitionType === PlayerStyle.GAPLESS
|
||||
) {
|
||||
setTimestamp(Number(currentTime.toFixed(0)));
|
||||
setTimestamp(currentTime);
|
||||
}
|
||||
}, 500);
|
||||
|
||||
|
||||
@@ -248,7 +248,14 @@ const Controls = () => {
|
||||
};
|
||||
|
||||
const handleLyricsSettings = (property: string, value: any) => {
|
||||
const displayProperties = ['fontSize', 'fontSizeUnsync', 'gap', 'gapUnsync'];
|
||||
const displayProperties = [
|
||||
'fontSize',
|
||||
'fontSizeUnsync',
|
||||
'gap',
|
||||
'gapUnsync',
|
||||
'paddingLeft',
|
||||
'paddingRight',
|
||||
];
|
||||
if (displayProperties.includes(property)) {
|
||||
const currentDisplay = useSettingsStore.getState().lyricsDisplay;
|
||||
setSettings({
|
||||
@@ -475,6 +482,38 @@ const Controls = () => {
|
||||
</Group>
|
||||
</Option.Control>
|
||||
</Option>
|
||||
<Option>
|
||||
<Option.Label>
|
||||
{t('page.fullscreenPlayer.config.lyricPaddingLeft')}
|
||||
</Option.Label>
|
||||
<Option.Control>
|
||||
<Slider
|
||||
defaultValue={lyricConfig.paddingLeft ?? 0}
|
||||
label={(value) => `${value}%`}
|
||||
max={20}
|
||||
min={0}
|
||||
onChangeEnd={(value) => handleLyricsSettings('paddingLeft', value)}
|
||||
step={1}
|
||||
w="100%"
|
||||
/>
|
||||
</Option.Control>
|
||||
</Option>
|
||||
<Option>
|
||||
<Option.Label>
|
||||
{t('page.fullscreenPlayer.config.lyricPaddingRight')}
|
||||
</Option.Label>
|
||||
<Option.Control>
|
||||
<Slider
|
||||
defaultValue={lyricConfig.paddingRight ?? 0}
|
||||
label={(value) => `${value}%`}
|
||||
max={20}
|
||||
min={0}
|
||||
onChangeEnd={(value) => handleLyricsSettings('paddingRight', value)}
|
||||
step={1}
|
||||
w="100%"
|
||||
/>
|
||||
</Option.Control>
|
||||
</Option>
|
||||
<Option>
|
||||
<Option.Label>
|
||||
{t('page.fullscreenPlayer.config.lyricAlignment')}
|
||||
|
||||
@@ -51,7 +51,14 @@ export const MobileFullscreenPlayerHeader = memo(
|
||||
const lyricConfig = { ...lyricsSettings, ...displaySettings };
|
||||
|
||||
const handleLyricsSettings = (property: string, value: any) => {
|
||||
const displayProperties = ['fontSize', 'fontSizeUnsync', 'gap', 'gapUnsync'];
|
||||
const displayProperties = [
|
||||
'fontSize',
|
||||
'fontSizeUnsync',
|
||||
'gap',
|
||||
'gapUnsync',
|
||||
'paddingLeft',
|
||||
'paddingRight',
|
||||
];
|
||||
if (displayProperties.includes(property)) {
|
||||
const currentDisplay = useSettingsStore.getState().lyricsDisplay;
|
||||
setSettings({
|
||||
@@ -284,6 +291,42 @@ export const MobileFullscreenPlayerHeader = memo(
|
||||
</Group>
|
||||
</Option.Control>
|
||||
</Option>
|
||||
<Option>
|
||||
<Option.Label>
|
||||
{t('page.fullscreenPlayer.config.lyricPaddingLeft')}
|
||||
</Option.Label>
|
||||
<Option.Control>
|
||||
<Slider
|
||||
defaultValue={lyricConfig.paddingLeft ?? 0}
|
||||
label={(value) => `${value}%`}
|
||||
max={20}
|
||||
min={0}
|
||||
onChangeEnd={(value) =>
|
||||
handleLyricsSettings('paddingLeft', value)
|
||||
}
|
||||
step={1}
|
||||
w="100%"
|
||||
/>
|
||||
</Option.Control>
|
||||
</Option>
|
||||
<Option>
|
||||
<Option.Label>
|
||||
{t('page.fullscreenPlayer.config.lyricPaddingRight')}
|
||||
</Option.Label>
|
||||
<Option.Control>
|
||||
<Slider
|
||||
defaultValue={lyricConfig.paddingRight ?? 0}
|
||||
label={(value) => `${value}%`}
|
||||
max={20}
|
||||
min={0}
|
||||
onChangeEnd={(value) =>
|
||||
handleLyricsSettings('paddingRight', value)
|
||||
}
|
||||
step={1}
|
||||
w="100%"
|
||||
/>
|
||||
</Option.Control>
|
||||
</Option>
|
||||
<Option>
|
||||
<Option.Label>
|
||||
{t('page.fullscreenPlayer.config.lyricAlignment')}
|
||||
|
||||
@@ -32,7 +32,6 @@ export const PlayerbarSlider = () => {
|
||||
|
||||
const formattedDuration = formatDuration(songDuration * 1000 || 0);
|
||||
const formattedTimeRemaining = formatDuration((currentTime - songDuration) * 1000 || 0);
|
||||
const formattedTime = formatDuration(currentTime * 1000 || 0);
|
||||
|
||||
const showTimeRemaining = useAppStore((state) => state.showTimeRemaining);
|
||||
const { setShowTimeRemaining } = useAppStoreActions();
|
||||
@@ -43,7 +42,7 @@ export const PlayerbarSlider = () => {
|
||||
<>
|
||||
<div className={styles.sliderContainer}>
|
||||
<div className={styles.sliderValueWrapper}>
|
||||
<ScrobbleStatus formattedTime={formattedTime} />
|
||||
<ScrobbleStatus />
|
||||
</div>
|
||||
<div className={styles.sliderWrapper}>
|
||||
{isWaveform ? (
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
import formatDuration from 'format-duration';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import {
|
||||
invokeScrobbleForceSubmit,
|
||||
invokeScrobbleResetListenedState,
|
||||
} from '/@/renderer/features/player/hooks/use-scrobble';
|
||||
import { useAppStore, useScrobbleDebugStore, useSettingsStore } from '/@/renderer/store';
|
||||
import {
|
||||
useAppStore,
|
||||
usePlayerTimestamp,
|
||||
useScrobbleDebugSnapshot,
|
||||
useSettingsStore,
|
||||
} from '/@/renderer/store';
|
||||
import { Button } from '/@/shared/components/button/button';
|
||||
import { Group } from '/@/shared/components/group/group';
|
||||
import { HoverCard } from '/@/shared/components/hover-card/hover-card';
|
||||
@@ -32,11 +38,12 @@ const ScrobbleConditionProgress = ({ value }: { value: number }) => (
|
||||
<Progress {...scrobbleProgressProps} value={value} w="100%" />
|
||||
);
|
||||
|
||||
export const ScrobbleStatus = ({ formattedTime }: { formattedTime: string }) => {
|
||||
export const ScrobbleStatus = () => {
|
||||
const { t } = useTranslation();
|
||||
const scrobbleEnabled = useSettingsStore((state) => state.playback.scrobble.enabled);
|
||||
const privateMode = useAppStore((state) => state.privateMode);
|
||||
const snapshot = useScrobbleDebugStore((state) => state.snapshot);
|
||||
const snapshot = useScrobbleDebugSnapshot();
|
||||
const formattedTime = formatDuration(usePlayerTimestamp() * 1000 || 0);
|
||||
|
||||
const hookInactive = !scrobbleEnabled || privateMode;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user