Add ability to save/restore queue (#111)

* add ability to save/restore play queue

* Add restoreQueue action

* Add optional pause param on setQueue

---------

Co-authored-by: jeffvli <jeffvictorli@gmail.com>
This commit is contained in:
Kendall Garner 2023-05-21 09:29:58 +00:00 committed by GitHub
parent c1c6ce33e4
commit 106fc90c4a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 180 additions and 7 deletions

View file

@ -17,14 +17,15 @@ import { PlayQueueHandlerContext } from '/@/renderer/features/player';
import { AddToPlaylistContextModal } from '/@/renderer/features/playlists';
import isElectron from 'is-electron';
import { getMpvProperties } from '/@/renderer/features/settings/components/playback/mpv-settings';
import { usePlayerStore } from '/@/renderer/store';
import { PlaybackType } from '/@/renderer/types';
import { PlayerState, usePlayerStore, useQueueControls } from '/@/renderer/store';
import { PlaybackType, PlayerStatus } from '/@/renderer/types';
ModuleRegistry.registerModules([ClientSideRowModelModule, InfiniteRowModelModule]);
initSimpleImg({ threshold: 0.05 }, true);
const mpvPlayer = isElectron() ? window.electron.mpvPlayer : null;
const mpvPlayerListener = isElectron() ? window.electron.mpvPlayerListener : null;
const ipc = isElectron() ? window.electron.ipc : null;
export const App = () => {
@ -33,6 +34,7 @@ export const App = () => {
const { type: playbackType } = usePlaybackSettings();
const { bindings } = useHotkeySettings();
const handlePlayQueueAdd = useHandlePlayQueueAdd();
const { restoreQueue } = useQueueControls();
useEffect(() => {
const root = document.documentElement;
@ -65,6 +67,36 @@ export const App = () => {
}
}, [bindings]);
useEffect(() => {
if (isElectron()) {
mpvPlayer.restoreQueue();
mpvPlayerListener.rendererSaveQueue(() => {
const { current, queue } = usePlayerStore.getState();
const stateToSave: Partial<Pick<PlayerState, 'current' | 'queue'>> = {
current: {
...current,
status: PlayerStatus.PAUSED,
},
queue,
};
mpvPlayer.saveQueue(stateToSave);
});
mpvPlayerListener.rendererRestoreQueue((_event: any, data: Partial<PlayerState>) => {
const playerData = restoreQueue(data);
if (playbackType === PlaybackType.LOCAL) {
mpvPlayer.setQueue(playerData, true);
}
});
}
return () => {
ipc?.removeAllListeners('renderer-player-restore-queue');
ipc?.removeAllListeners('renderer-player-save-queue');
};
}, [playbackType, restoreQueue]);
return (
<MantineProvider
withGlobalStyles

View file

@ -1,3 +1,5 @@
// 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';

View file

@ -1,3 +1,4 @@
import isElectron from 'is-electron';
import { Group } from '@mantine/core';
import { Select, Tooltip, NumberInput, Switch, Slider } from '/@/renderer/components';
import { SettingsSection } from '/@/renderer/features/settings/components/settings-section';
@ -8,6 +9,8 @@ import {
} from '/@/renderer/store/settings.store';
import { Play } from '/@/renderer/types';
const localSettings = isElectron() ? window.electron.localSettings : null;
const SIDE_QUEUE_OPTIONS = [
{ label: 'Fixed', value: 'sideQueue' },
{ label: 'Floating', value: 'sideDrawerQueue' },
@ -170,6 +173,25 @@ export const ControlSettings = () => {
isHidden: false,
title: 'Volume wheel step',
},
{
control: (
<Switch
defaultChecked={settings.resume}
onChange={(e) => {
localSettings?.set('resume', e.target.checked);
setSettings({
general: {
...settings,
resume: e.currentTarget.checked,
},
});
}}
/>
),
description: 'When exiting, save the current play queue and restore it when reopening',
isHidden: !isElectron(),
title: 'Save play queue',
},
];
return <SettingsSection options={controlOptions} />;

View file

@ -1,5 +1,5 @@
import { IpcRendererEvent } from 'electron';
import { PlayerData } from './store';
import { PlayerData, PlayerState } from './store';
declare global {
interface Window {
@ -17,6 +17,8 @@ declare global {
PLAYER_PAUSE(): void;
PLAYER_PLAY(): void;
PLAYER_PREVIOUS(): void;
PLAYER_RESTORE_DATA(): void;
PLAYER_SAVE_QUEUE(data: PlayerState): void;
PLAYER_SEEK(seconds: number): void;
PLAYER_SEEK_TO(seconds: number): void;
PLAYER_SET_QUEUE(data: PlayerData): void;
@ -30,6 +32,8 @@ declare global {
RENDERER_PLAYER_PLAY(cb: (event: IpcRendererEvent, data: any) => void): void;
RENDERER_PLAYER_PLAY_PAUSE(cb: (event: IpcRendererEvent, data: any) => void): void;
RENDERER_PLAYER_PREVIOUS(cb: (event: IpcRendererEvent, data: any) => void): void;
RENDERER_PLAYER_RESTORE_QUEUE(cb: (event: IpcRendererEvent, data: any) => void): void;
RENDERER_PLAYER_SAVE_QUEUE(cb: (event: IpcRendererEvent, data: any) => void): void;
RENDERER_PLAYER_STOP(cb: (event: IpcRendererEvent, data: any) => void): void;
SETTINGS_GET(data: { property: string }): any;
SETTINGS_SET(data: { property: string; value: any }): void;

View file

@ -74,6 +74,7 @@ export interface PlayerSlice extends PlayerState {
previous: () => PlayerData;
removeFromQueue: (uniqueIds: string[]) => PlayerData;
reorderQueue: (rowUniqueIds: string[], afterUniqueId?: string) => PlayerData;
restoreQueue: (data: Partial<PlayerState>) => PlayerData;
setCurrentIndex: (index: number) => PlayerData;
setCurrentTime: (time: number) => void;
setCurrentTrack: (uniqueId: string) => PlayerData;
@ -615,6 +616,20 @@ export const usePlayerStore = create<PlayerSlice>()(
return get().actions.getPlayerData();
},
restoreQueue: (data) => {
set((state) => {
state.current = {
...state.current,
...data.current,
};
state.queue = {
...state.queue,
...data.queue,
};
});
return get().actions.getPlayerData();
},
setCurrentIndex: (index) => {
if (get().shuffle === PlayerShuffle.TRACK) {
const foundSong = get().queue.default.find(
@ -874,6 +889,7 @@ export const useQueueControls = () =>
moveToTopOfQueue: state.actions.moveToTopOfQueue,
removeFromQueue: state.actions.removeFromQueue,
reorderQueue: state.actions.reorderQueue,
restoreQueue: state.actions.restoreQueue,
setCurrentIndex: state.actions.setCurrentIndex,
setCurrentTrack: state.actions.setCurrentTrack,
setShuffledIndex: state.actions.setShuffledIndex,

View file

@ -69,6 +69,7 @@ export interface SettingsState {
followSystemTheme: boolean;
fontContent: string;
playButtonBehavior: Play;
resume: boolean;
showQueueDrawerButton: boolean;
sideQueueType: SideQueueType;
skipButtons: {
@ -128,6 +129,7 @@ const initialState: SettingsState = {
followSystemTheme: false,
fontContent: 'Poppins',
playButtonBehavior: Play.NOW,
resume: false,
showQueueDrawerButton: false,
sideQueueType: 'sideQueue',
skipButtons: {