move player timestamp to separate store

- for performance related issue
This commit is contained in:
jeffvli
2025-11-16 21:49:31 -08:00
parent 243d29f7a7
commit 4451389b6a
3 changed files with 89 additions and 62 deletions
+1
View File
@@ -7,3 +7,4 @@ export * from './list.store';
export * from './player.store';
export * from './playlist.store';
export * from './settings.store';
export * from './timestamp.store';
+44 -62
View File
@@ -7,6 +7,10 @@ import { useShallow } from 'zustand/react/shallow';
import { createSelectors } from '/@/renderer/lib/zustand';
import { useSettingsStore } from '/@/renderer/store/settings.store';
import {
setTimestamp as setTimestampStore,
useTimestampStoreBase,
} from '/@/renderer/store/timestamp.store';
import { idbStateStorage } from '/@/renderer/store/utils';
import { shuffleInPlace } from '/@/renderer/utils/shuffle';
import { PlayerData, QueueData, QueueSong, Song } from '/@/shared/types/domain-types';
@@ -56,7 +60,6 @@ interface Actions {
setRepeat: (repeat: PlayerRepeat) => void;
setShuffle: (shuffle: PlayerShuffle) => void;
setSpeed: (speed: number) => void;
setTimestamp: (timestamp: number) => void;
setTransitionType: (transitionType: PlayerStyle) => void;
setVolume: (volume: number) => void;
shuffle: () => void;
@@ -83,7 +86,6 @@ interface State {
shuffle: PlayerShuffle;
speed: number;
status: PlayerStatus;
timestamp: number;
transitionType: PlayerStyle;
volume: number;
};
@@ -102,7 +104,6 @@ const initialState: State = {
shuffle: PlayerShuffle.NONE,
speed: 1,
status: PlayerStatus.PAUSED,
timestamp: 0,
transitionType: PlayerStyle.GAPLESS,
volume: 30,
},
@@ -191,7 +192,7 @@ export const usePlayerStoreBase = create<PlayerState>()(
state.player.index = 0;
state.player.status = PlayerStatus.PLAYING;
state.player.playerNum = 1;
state.player.timestamp = 0;
setTimestampStore(0);
state.queue.default = newUniqueIds;
if (state.player.shuffle === PlayerShuffle.TRACK) {
@@ -215,7 +216,7 @@ export const usePlayerStoreBase = create<PlayerState>()(
state.player.index = 0;
state.player.status = PlayerStatus.PLAYING;
state.player.playerNum = 1;
state.player.timestamp = 0;
setTimestampStore(0);
state.queue.default = shuffledIds;
// Always maintain shuffled array when using Play.SHUFFLE
@@ -293,7 +294,7 @@ export const usePlayerStoreBase = create<PlayerState>()(
state.queue.default = [];
state.player.status = PlayerStatus.PLAYING;
state.player.playerNum = 1;
state.player.timestamp = 0;
setTimestampStore(0);
// Add the first item after the current playing track
@@ -369,7 +370,7 @@ export const usePlayerStoreBase = create<PlayerState>()(
state.player.index = 0;
state.player.status = PlayerStatus.PLAYING;
state.player.playerNum = 1;
state.player.timestamp = 0;
setTimestampStore(0);
// Add first item to priority queue, rest to default
state.queue.priority = [shuffledIds[0]];
@@ -612,7 +613,7 @@ export const usePlayerStoreBase = create<PlayerState>()(
set((state) => {
state.player.index = newIndex;
state.player.playerNum = newPlayerNum;
state.player.timestamp = 0;
setTimestampStore(0);
state.player.status = newStatus;
});
@@ -644,7 +645,7 @@ export const usePlayerStoreBase = create<PlayerState>()(
set((state) => {
state.player.index = Math.min(queue.items.length - 1, currentIndex + 1);
state.player.playerNum = 1;
state.player.timestamp = 0;
setTimestampStore(0);
});
},
mediaPause: () => {
@@ -661,7 +662,7 @@ export const usePlayerStoreBase = create<PlayerState>()(
if (index !== -1) {
state.player.index = index;
state.player.timestamp = 0;
setTimestampStore(0);
}
}
@@ -674,7 +675,7 @@ export const usePlayerStoreBase = create<PlayerState>()(
set((state) => {
// Only decrement if we're not at the start
state.player.index = Math.max(0, currentIndex - 1);
state.player.timestamp = 0;
setTimestampStore(0);
});
},
mediaSeekToTimestamp: (timestamp: number) => {
@@ -683,41 +684,41 @@ export const usePlayerStoreBase = create<PlayerState>()(
});
},
mediaSkipBackward: (offset?: number) => {
set((state) => {
const offsetFromSettings =
useSettingsStore.getState().general.skipButtons.skipBackwardSeconds;
const timeToSkip = offset ?? offsetFromSettings ?? 5;
const newTimestamp = Math.max(0, state.player.timestamp - timeToSkip);
const offsetFromSettings =
useSettingsStore.getState().general.skipButtons.skipBackwardSeconds;
const timeToSkip = offset ?? offsetFromSettings ?? 5;
const currentTimestamp = useTimestampStoreBase.getState().timestamp;
const newTimestamp = Math.max(0, currentTimestamp - timeToSkip);
set((state) => {
state.player.seekToTimestamp = uniqueSeekToTimestamp(newTimestamp);
});
},
mediaSkipForward: (offset?: number) => {
const state = get();
const queue = state.getQueue();
const index = state.player.index;
const currentTrack = queue.items[index];
const duration = currentTrack?.duration;
const offsetFromSettings =
useSettingsStore.getState().general.skipButtons.skipForwardSeconds;
const timeToSkip = offset ?? offsetFromSettings ?? 5;
if (!duration) {
return;
}
const currentTimestamp = useTimestampStoreBase.getState().timestamp;
const newTimestamp = Math.min(duration - 1, currentTimestamp + timeToSkip);
set((state) => {
const queue = state.getQueue();
const index = state.player.index;
const currentTrack = queue.items[index];
const duration = currentTrack?.duration;
const offsetFromSettings =
useSettingsStore.getState().general.skipButtons.skipForwardSeconds;
const timeToSkip = offset ?? offsetFromSettings ?? 5;
if (!duration) {
return;
}
const newTimestamp = Math.min(
duration - 1,
state.player.timestamp + timeToSkip,
);
state.player.seekToTimestamp = uniqueSeekToTimestamp(newTimestamp);
});
},
mediaStop: () => {
set((state) => {
state.player.status = PlayerStatus.PAUSED;
state.player.timestamp = 0;
setTimestampStore(0);
state.player.index = -1;
state.queue.default = [];
state.queue.priority = [];
@@ -1056,11 +1057,6 @@ export const usePlayerStoreBase = create<PlayerState>()(
state.player.speed = normalizedSpeed;
});
},
setTimestamp: (timestamp: number) => {
set((state) => {
state.player.timestamp = timestamp;
});
},
setTransitionType: (transitionType: PlayerStyle) => {
set((state) => {
state.player.transitionType = transitionType;
@@ -1154,9 +1150,10 @@ export const usePlayerStoreBase = create<PlayerState>()(
partialize: (state) => {
const shouldRestorePlayQueue = useSettingsStore.getState().general.resume;
// Exclude playerNum, seekToTimestamp, status, and timestamp from stored player object
// Exclude playerNum, seekToTimestamp, and status from stored player object
// These are not needed to be stored since they are ephemeral properties
const excludedPlayerKeys = ['playerNum', 'seekToTimestamp', 'status', 'timestamp'];
// Note: timestamp is now in a separate store and doesn't need to be excluded here
const excludedPlayerKeys = ['playerNum', 'seekToTimestamp', 'status'];
// If we're not restoring the play queue, we don't need the index property
if (!shouldRestorePlayQueue) {
@@ -1196,7 +1193,7 @@ export const usePlayerStoreBase = create<PlayerState>()(
export const usePlayerStore = createSelectors(usePlayerStoreBase);
export const usePlayerActions = () => {
return usePlayerStoreBase(
const actions = usePlayerStoreBase(
useShallow((state) => ({
addToQueueByType: state.addToQueueByType,
addToQueueByUniqueId: state.addToQueueByUniqueId,
@@ -1224,7 +1221,6 @@ export const usePlayerActions = () => {
setRepeat: state.setRepeat,
setShuffle: state.setShuffle,
setSpeed: state.setSpeed,
setTimestamp: state.setTimestamp,
setTransitionType: state.setTransitionType,
setVolume: state.setVolume,
shuffle: state.shuffle,
@@ -1234,6 +1230,11 @@ export const usePlayerActions = () => {
toggleShuffle: state.toggleShuffle,
})),
);
return {
...actions,
setTimestamp: setTimestampStore,
};
};
export type AddToQueueByPlayType = Play;
@@ -1290,17 +1291,6 @@ export const subscribeCurrentTrack = (
);
};
export const subscribePlayerProgress = (
onChange: (properties: { timestamp: number }, prev: { timestamp: number }) => void,
) => {
return usePlayerStoreBase.subscribe(
(state) => state.player.timestamp,
(timestamp, prevTimestamp) => {
onChange({ timestamp }, { timestamp: prevTimestamp });
},
);
};
export const subscribePlayerVolume = (
onChange: (properties: { volume: number }, prev: { volume: number }) => void,
) => {
@@ -1398,10 +1388,6 @@ export const usePlayerProperties = () => {
);
};
export const usePlayerProgress = () => {
return usePlayerStoreBase((state) => state.player.timestamp);
};
export const usePlayerDuration = () => {
return usePlayerStoreBase((state) => {
const queue = state.getQueue();
@@ -1481,10 +1467,6 @@ export const usePlayerSpeed = () => {
return usePlayerStoreBase((state) => state.player.speed);
};
export const usePlayerTimestamp = () => {
return usePlayerStoreBase((state) => state.player.timestamp);
};
export const usePlayerSong = () => {
return usePlayerStoreBase((state) => {
const queue = state.getQueue();
+44
View File
@@ -0,0 +1,44 @@
import { subscribeWithSelector } from 'zustand/middleware';
import { createWithEqualityFn } from 'zustand/traditional';
interface TimestampState {
setTimestamp: (timestamp: number) => void;
timestamp: number;
}
export const useTimestampStoreBase = createWithEqualityFn<TimestampState>()(
subscribeWithSelector((set) => ({
setTimestamp: (timestamp: number) => {
set({ timestamp });
},
timestamp: 0,
})),
);
export const subscribePlayerProgress = (
onChange: (properties: { timestamp: number }, prev: { timestamp: number }) => void,
) => {
return useTimestampStoreBase.subscribe(
(state) => state.timestamp,
(timestamp, prevTimestamp) => {
onChange({ timestamp }, { timestamp: prevTimestamp });
},
{
equalityFn: (a, b) => {
return a === b;
},
},
);
};
export const usePlayerProgress = () => {
return useTimestampStoreBase((state) => state.timestamp);
};
export const usePlayerTimestamp = () => {
return useTimestampStoreBase((state) => state.timestamp);
};
export const setTimestamp = (timestamp: number) => {
useTimestampStoreBase.getState().setTimestamp(timestamp);
};