fix mpv player queue behavior to handle gapless playback

This commit is contained in:
jeffvli
2025-12-11 20:36:47 -08:00
parent f7d488ba84
commit 61e70342a4
7 changed files with 283 additions and 102 deletions
+111 -2
View File
@@ -5,6 +5,7 @@ import { immer } from 'zustand/middleware/immer';
import { useShallow } from 'zustand/react/shallow';
import { createWithEqualityFn } from 'zustand/traditional';
import { eventEmitter } from '/@/renderer/events/event-emitter';
import { createSelectors } from '/@/renderer/lib/zustand';
import { useSettingsStore } from '/@/renderer/store/settings.store';
import {
@@ -40,6 +41,7 @@ interface Actions {
clearSelected: (items: QueueSong[]) => void;
decreaseVolume: (value: number) => void;
getCurrentSong: () => QueueSong | undefined;
getPlayerData: () => PlayerData;
getQueue: (groupBy?: QueueGroupingProperty) => GroupedQueue;
getQueueOrder: () => {
groups: { count: number; name: string }[];
@@ -1130,6 +1132,74 @@ export const usePlayerStoreBase = createWithEqualityFn<PlayerState>()(
return queue.items[index];
},
getPlayerData: () => {
const state = get();
const queue = state.getQueue();
const index = state.player.index;
// If shuffle is enabled and not in priority mode, map shuffled position to actual queue position for display
let queueIndex = index;
if (isShuffleEnabled(state)) {
queueIndex = mapShuffledToQueueIndex(index, state.queue.shuffled);
}
const currentSong = queue.items[queueIndex];
const repeat = state.player.repeat;
// For previousSong calculation, we need to consider the shuffled order (only if not in priority mode)
let previousSong: QueueSong | undefined;
if (isShuffleEnabled(state)) {
// Calculate previous in shuffled order
const previousShuffledIndex = index - 1;
if (previousShuffledIndex >= 0) {
const previousQueueIndex = state.queue.shuffled[previousShuffledIndex];
previousSong = queue.items[previousQueueIndex];
} else if (repeat === PlayerRepeat.ALL) {
// Wrap to last in shuffled order
const lastShuffledIndex = state.queue.shuffled.length - 1;
const lastQueueIndex = state.queue.shuffled[lastShuffledIndex];
previousSong = queue.items[lastQueueIndex];
}
} else {
previousSong = queueIndex > 0 ? queue.items[queueIndex - 1] : undefined;
}
// For nextSong calculation, we need to consider the shuffled order (only if not in priority mode)
let nextSong: QueueSong | undefined;
if (isShuffleEnabled(state)) {
// Calculate next in shuffled order
const nextShuffledIndex = index + 1;
if (nextShuffledIndex < state.queue.shuffled.length) {
const nextQueueIndex = state.queue.shuffled[nextShuffledIndex];
nextSong = queue.items[nextQueueIndex];
} else if (repeat === PlayerRepeat.ALL) {
// Wrap to first in shuffled order
const firstQueueIndex = state.queue.shuffled[0];
nextSong = queue.items[firstQueueIndex];
}
} else {
nextSong = calculateNextSong(queueIndex, queue.items, repeat);
}
return {
currentSong,
index: queueIndex, // Return the actual queue position for display
muted: state.player.muted,
nextSong,
num: state.player.playerNum,
player1: state.player.playerNum === 1 ? currentSong : nextSong,
player2: state.player.playerNum === 2 ? currentSong : nextSong,
previousSong,
queue: state.queue,
queueLength: state.queue.default.length + state.queue.priority.length,
repeat: state.player.repeat,
shuffle: state.player.shuffle,
speed: state.player.speed,
status: state.player.status,
transitionType: state.player.transitionType,
volume: state.player.volume,
};
},
getQueue: (groupBy?: QueueGroupingProperty) => {
const queue = get().getQueueOrder();
const queueType = getQueueType();
@@ -1301,6 +1371,11 @@ export const usePlayerStoreBase = createWithEqualityFn<PlayerState>()(
state.player.playerNum = 1;
setTimestampStore(0);
});
eventEmitter.emit('MEDIA_NEXT', {
currentIndex,
nextIndex,
});
},
mediaPause: () => {
set((state) => {
@@ -1308,6 +1383,8 @@ export const usePlayerStoreBase = createWithEqualityFn<PlayerState>()(
});
},
mediaPlay: (id?: string) => {
let playIndex: number | undefined;
set((state) => {
if (id) {
const queue = state.getQueue();
@@ -1328,11 +1405,14 @@ export const usePlayerStoreBase = createWithEqualityFn<PlayerState>()(
);
if (shuffledPosition !== -1) {
state.player.index = shuffledPosition;
playIndex = shuffledPosition;
} else {
state.player.index = queueIndex;
playIndex = queueIndex;
}
} else {
state.player.index = queueIndex;
playIndex = queueIndex;
}
setTimestampStore(0);
}
@@ -1340,8 +1420,18 @@ export const usePlayerStoreBase = createWithEqualityFn<PlayerState>()(
state.player.status = PlayerStatus.PLAYING;
});
if (id && playIndex !== undefined) {
eventEmitter.emit('PLAYER_PLAY', {
id,
index: playIndex,
});
}
},
mediaPlayByIndex: (index: number) => {
let playIndex: number | undefined;
let songId: string | undefined;
set((state) => {
const queue = state.getQueue();
@@ -1350,6 +1440,12 @@ export const usePlayerStoreBase = createWithEqualityFn<PlayerState>()(
return;
}
// Get the song's unique ID from the queue
const song = queue.items[index];
if (song) {
songId = song._uniqueId;
}
// index is the position in the original queue
if (isShuffleEnabled(state)) {
// Find the shuffled position for this queue index
@@ -1357,15 +1453,23 @@ export const usePlayerStoreBase = createWithEqualityFn<PlayerState>()(
index,
state.queue.shuffled,
);
state.player.index =
shuffledPosition !== undefined ? shuffledPosition : index;
playIndex = shuffledPosition !== undefined ? shuffledPosition : index;
state.player.index = playIndex;
} else {
playIndex = index;
state.player.index = index;
}
setTimestampStore(0);
state.player.status = PlayerStatus.PLAYING;
});
if (songId && playIndex !== undefined) {
eventEmitter.emit('PLAYER_PLAY', {
id: songId,
index: playIndex,
});
}
},
mediaPrevious: () => {
const currentIndex = get().player.index;
@@ -1400,6 +1504,11 @@ export const usePlayerStoreBase = createWithEqualityFn<PlayerState>()(
state.player.playerNum = 1;
setTimestampStore(0);
});
eventEmitter.emit('MEDIA_PREV', {
currentIndex,
prevIndex: previousIndex,
});
},
mediaSeekToTimestamp: (timestamp: number) => {
set((state) => {