fix preserve pitch on player start, add to player config (#1349)

This commit is contained in:
jeffvli
2025-12-09 17:57:56 -08:00
parent 753f35f81b
commit a02fc28785
3 changed files with 68 additions and 20 deletions
@@ -1,5 +1,4 @@
import { UseSuspenseQueryOptions } from '@tanstack/react-query';
import { ref } from 'process';
import { api } from '/@/renderer/api';
import { useItemListPaginatedLoader } from '/@/renderer/components/item-list/helpers/item-list-paginated-loader';
@@ -77,7 +76,6 @@ export const AlbumArtistListPaginatedGrid = ({
itemsPerRow={itemsPerRow}
itemType={LibraryItem.ALBUM_ARTIST}
onScrollEnd={handleOnScrollEnd}
ref={ref}
rows={rows}
/>
</ItemListWithPagination>
@@ -1,6 +1,6 @@
import type { RefObject } from 'react';
import { useEffect, useImperativeHandle, useRef, useState } from 'react';
import { useCallback, useEffect, useImperativeHandle, useRef, useState } from 'react';
import ReactPlayer from 'react-player';
import { AudioPlayer, PlayerOnProgressProps } from '/@/renderer/features/player/audio-player/types';
@@ -153,15 +153,37 @@ export const WebPlayerEngine = (props: WebPlayerEngineProps) => {
useEffect(() => {
const player1 = player1Ref.current?.getInternalPlayer();
if (player1) {
if (player1 && player1 instanceof HTMLAudioElement) {
player1.preservesPitch = preservesPitch;
}
const player2 = player2Ref.current?.getInternalPlayer();
if (player2) {
if (player2 && player2 instanceof HTMLAudioElement) {
player2.preservesPitch = preservesPitch;
}
}, [preservesPitch]);
const handleOnReadyPlayer1 = useCallback(
(player: ReactPlayer) => {
const internal = player.getInternalPlayer();
if (internal && internal instanceof HTMLAudioElement) {
internal.preservesPitch = preservesPitch;
}
onStartedPlayer1(player);
},
[onStartedPlayer1, preservesPitch],
);
const handleOnReadyPlayer2 = useCallback(
(player: ReactPlayer) => {
const internal = player.getInternalPlayer();
if (internal && internal instanceof HTMLAudioElement) {
internal.preservesPitch = preservesPitch;
}
onStartedPlayer2(player);
},
[onStartedPlayer2, preservesPitch],
);
return (
<div id="web-player-engine" style={{ display: 'none' }}>
<ReactPlayer
@@ -175,7 +197,7 @@ export const WebPlayerEngine = (props: WebPlayerEngineProps) => {
onEnded={src1 ? () => onEndedPlayer1() : undefined}
onError={handleOnError(player1Ref, () => onEndedPlayer1())}
onProgress={onProgressPlayer1}
onReady={onStartedPlayer1}
onReady={handleOnReadyPlayer1}
playbackRate={speed || 1}
playing={playerNum === 1 && playerStatus === PlayerStatus.PLAYING}
progressInterval={isTransitioning ? 10 : 250}
@@ -195,7 +217,7 @@ export const WebPlayerEngine = (props: WebPlayerEngineProps) => {
onEnded={src2 ? () => onEndedPlayer2() : undefined}
onError={handleOnError(player2Ref, () => onEndedPlayer2())}
onProgress={onProgressPlayer2}
onReady={onStartedPlayer2}
onReady={handleOnReadyPlayer2}
playbackRate={speed || 1}
playing={playerNum === 2 && playerStatus === PlayerStatus.PLAYING}
progressInterval={isTransitioning ? 10 : 250}
@@ -1,5 +1,5 @@
import isElectron from 'is-electron';
import { useEffect, useMemo, useState } from 'react';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { ListConfigTable } from '/@/renderer/features/shared/components/list-config-menu';
@@ -11,12 +11,17 @@ import {
usePlayerSpeed,
usePlayerStatus,
} from '/@/renderer/store';
import { usePlaybackSettings, useSettingsStoreActions } from '/@/renderer/store/settings.store';
import {
usePlaybackSettings,
useSettingsStore,
useSettingsStoreActions,
} from '/@/renderer/store/settings.store';
import { ActionIcon } from '/@/shared/components/action-icon/action-icon';
import { Popover } from '/@/shared/components/popover/popover';
import { SegmentedControl } from '/@/shared/components/segmented-control/segmented-control';
import { Select } from '/@/shared/components/select/select';
import { Slider } from '/@/shared/components/slider/slider';
import { Switch } from '/@/shared/components/switch/switch';
import { toast } from '/@/shared/components/toast/toast';
import {
CrossfadeStyle,
@@ -42,9 +47,20 @@ export const PlayerConfig = () => {
const { crossfadeDuration, crossfadeStyle, transitionType } = usePlayerProperties();
const { setCrossfadeDuration, setCrossfadeStyle, setQueueType, setSpeed, setTransitionType } =
usePlayerActions();
const preservePitch = useSettingsStore((state) => state.playback.preservePitch);
const playbackSettings = usePlaybackSettings();
const { setSettings } = useSettingsStoreActions();
const setPreservePitch = useCallback(
(value: boolean) => {
setSettings({
playback: { ...playbackSettings, preservePitch: value },
});
},
[playbackSettings, setSettings],
);
const [audioDevices, setAudioDevices] = useState<{ label: string; value: string }[]>([]);
useEffect(() => {
@@ -288,26 +304,38 @@ export const PlayerConfig = () => {
id: 'playbackSpeed',
label: t('player.playbackSpeed', { postProcess: 'titleCase' }),
},
{
component: (
<Switch
defaultChecked={preservePitch}
onChange={(e) => setPreservePitch(e.currentTarget.checked)}
/>
),
id: 'preservePitch',
label: t('setting.preservePitch', { postProcess: 'titleCase' }),
},
];
return allOptions;
}, [
playbackSettings,
audioDevices,
status,
setSettings,
currentSong,
speed,
setSpeed,
t,
queueType,
setQueueType,
playbackSettings,
status,
audioDevices,
transitionType,
setTransitionType,
crossfadeStyle,
crossfadeDuration,
setCrossfadeDuration,
crossfadeStyle,
speed,
setSpeed,
preservePitch,
currentSong?.bpm,
setQueueType,
setSettings,
setTransitionType,
setCrossfadeStyle,
t,
setPreservePitch,
]);
return (