mirror of
https://github.com/jeffvli/feishin.git
synced 2026-05-09 20:29:36 +02:00
optimize player state for large queues
This commit is contained in:
@@ -340,12 +340,14 @@ export const PlayerProvider = ({ children }: { children: React.ReactNode }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get total count
|
// Get total count
|
||||||
const countResult = (await queryClient.fetchQuery(
|
const countResult = (await queryClient.fetchQuery({
|
||||||
listCountQueryFn({
|
...listCountQueryFn({
|
||||||
query: { ...query },
|
query: { ...query },
|
||||||
serverId,
|
serverId,
|
||||||
}),
|
}),
|
||||||
)) as number;
|
gcTime: 0,
|
||||||
|
staleTime: 0,
|
||||||
|
})) as number;
|
||||||
totalCount = countResult || 0;
|
totalCount = countResult || 0;
|
||||||
|
|
||||||
// Check if we need confirmation
|
// Check if we need confirmation
|
||||||
@@ -392,12 +394,14 @@ export const PlayerProvider = ({ children }: { children: React.ReactNode }) => {
|
|||||||
startIndex,
|
startIndex,
|
||||||
};
|
};
|
||||||
|
|
||||||
const pageResult = (await queryClient.fetchQuery(
|
const pageResult = (await queryClient.fetchQuery({
|
||||||
listQueryFn({
|
...listQueryFn({
|
||||||
query: pageQuery,
|
query: pageQuery,
|
||||||
serverId,
|
serverId,
|
||||||
}),
|
}),
|
||||||
)) as { items: any[] };
|
gcTime: 0,
|
||||||
|
staleTime: 0,
|
||||||
|
})) as { items: any[] };
|
||||||
|
|
||||||
if (pageResult?.items) {
|
if (pageResult?.items) {
|
||||||
const pageIds = pageResult.items.map((item: any) => item.id);
|
const pageIds = pageResult.items.map((item: any) => item.id);
|
||||||
@@ -729,8 +733,8 @@ export async function fetchSongsByItemType(
|
|||||||
|
|
||||||
for (const id of args.id) {
|
for (const id of args.id) {
|
||||||
promises.push(
|
promises.push(
|
||||||
queryClient.fetchQuery(
|
queryClient.fetchQuery({
|
||||||
songsQueries.list({
|
...songsQueries.list({
|
||||||
query: {
|
query: {
|
||||||
albumIds: [id],
|
albumIds: [id],
|
||||||
sortBy: SongListSort.ID,
|
sortBy: SongListSort.ID,
|
||||||
@@ -740,7 +744,9 @@ export async function fetchSongsByItemType(
|
|||||||
},
|
},
|
||||||
serverId: serverId,
|
serverId: serverId,
|
||||||
}),
|
}),
|
||||||
),
|
gcTime: 0,
|
||||||
|
staleTime: 0,
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -756,8 +762,8 @@ export async function fetchSongsByItemType(
|
|||||||
|
|
||||||
for (const id of args.id) {
|
for (const id of args.id) {
|
||||||
promises.push(
|
promises.push(
|
||||||
queryClient.fetchQuery(
|
queryClient.fetchQuery({
|
||||||
songsQueries.list({
|
...songsQueries.list({
|
||||||
query: {
|
query: {
|
||||||
albumArtistIds: [id],
|
albumArtistIds: [id],
|
||||||
limit: -1,
|
limit: -1,
|
||||||
@@ -768,7 +774,9 @@ export async function fetchSongsByItemType(
|
|||||||
},
|
},
|
||||||
serverId: serverId,
|
serverId: serverId,
|
||||||
}),
|
}),
|
||||||
),
|
gcTime: 0,
|
||||||
|
staleTime: 0,
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -783,8 +791,8 @@ export async function fetchSongsByItemType(
|
|||||||
|
|
||||||
for (const id of args.id) {
|
for (const id of args.id) {
|
||||||
promises.push(
|
promises.push(
|
||||||
queryClient.fetchQuery(
|
queryClient.fetchQuery({
|
||||||
songsQueries.list({
|
...songsQueries.list({
|
||||||
query: {
|
query: {
|
||||||
genreIds: [id],
|
genreIds: [id],
|
||||||
limit: -1,
|
limit: -1,
|
||||||
@@ -795,7 +803,9 @@ export async function fetchSongsByItemType(
|
|||||||
},
|
},
|
||||||
serverId: serverId,
|
serverId: serverId,
|
||||||
}),
|
}),
|
||||||
),
|
gcTime: 0,
|
||||||
|
staleTime: 0,
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -809,15 +819,17 @@ export async function fetchSongsByItemType(
|
|||||||
|
|
||||||
for (const id of args.id) {
|
for (const id of args.id) {
|
||||||
promises.push(
|
promises.push(
|
||||||
queryClient.fetchQuery(
|
queryClient.fetchQuery({
|
||||||
playlistsQueries.songList({
|
...playlistsQueries.songList({
|
||||||
query: {
|
query: {
|
||||||
id: id,
|
id: id,
|
||||||
...args.params,
|
...args.params,
|
||||||
},
|
},
|
||||||
serverId: serverId,
|
serverId: serverId,
|
||||||
}),
|
}),
|
||||||
),
|
gcTime: 0,
|
||||||
|
staleTime: 0,
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -135,7 +135,6 @@ export const usePlayerStoreBase = create<PlayerState>()(
|
|||||||
case Play.LAST: {
|
case Play.LAST: {
|
||||||
set((state) => {
|
set((state) => {
|
||||||
const currentIndex = state.player.index;
|
const currentIndex = state.player.index;
|
||||||
// Add new songs to songs object
|
|
||||||
newItems.forEach((item) => {
|
newItems.forEach((item) => {
|
||||||
state.queue.songs[item._uniqueId] = item;
|
state.queue.songs[item._uniqueId] = item;
|
||||||
});
|
});
|
||||||
@@ -161,7 +160,6 @@ export const usePlayerStoreBase = create<PlayerState>()(
|
|||||||
case Play.NEXT: {
|
case Play.NEXT: {
|
||||||
set((state) => {
|
set((state) => {
|
||||||
const currentIndex = state.player.index;
|
const currentIndex = state.player.index;
|
||||||
// Add new songs to songs object
|
|
||||||
newItems.forEach((item) => {
|
newItems.forEach((item) => {
|
||||||
state.queue.songs[item._uniqueId] = item;
|
state.queue.songs[item._uniqueId] = item;
|
||||||
});
|
});
|
||||||
@@ -187,7 +185,6 @@ export const usePlayerStoreBase = create<PlayerState>()(
|
|||||||
}
|
}
|
||||||
case Play.NOW: {
|
case Play.NOW: {
|
||||||
set((state) => {
|
set((state) => {
|
||||||
// Add new songs to songs object
|
|
||||||
newItems.forEach((item) => {
|
newItems.forEach((item) => {
|
||||||
state.queue.songs[item._uniqueId] = item;
|
state.queue.songs[item._uniqueId] = item;
|
||||||
});
|
});
|
||||||
@@ -208,7 +205,6 @@ export const usePlayerStoreBase = create<PlayerState>()(
|
|||||||
}
|
}
|
||||||
case Play.SHUFFLE: {
|
case Play.SHUFFLE: {
|
||||||
set((state) => {
|
set((state) => {
|
||||||
// Add new songs to songs object
|
|
||||||
newItems.forEach((item) => {
|
newItems.forEach((item) => {
|
||||||
state.queue.songs[item._uniqueId] = item;
|
state.queue.songs[item._uniqueId] = item;
|
||||||
});
|
});
|
||||||
@@ -485,33 +481,27 @@ export const usePlayerStoreBase = create<PlayerState>()(
|
|||||||
state.player.index = -1;
|
state.player.index = -1;
|
||||||
state.queue.default = [];
|
state.queue.default = [];
|
||||||
state.queue.priority = [];
|
state.queue.priority = [];
|
||||||
|
state.queue.shuffled = [];
|
||||||
state.queue.songs = {};
|
state.queue.songs = {};
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
clearSelected: (items: QueueSong[]) => {
|
clearSelected: (items: QueueSong[]) => {
|
||||||
set((state) => {
|
set((state) => {
|
||||||
const uniqueIds = items.map((item) => item._uniqueId);
|
const uniqueIds = new Set(items.map((item) => item._uniqueId));
|
||||||
|
|
||||||
state.queue.default = state.queue.default.filter(
|
state.queue.default = state.queue.default.filter(
|
||||||
(id) => !uniqueIds.includes(id),
|
(id) => !uniqueIds.has(id),
|
||||||
);
|
);
|
||||||
|
|
||||||
state.queue.priority = state.queue.priority.filter(
|
state.queue.priority = state.queue.priority.filter(
|
||||||
(id) => !uniqueIds.includes(id),
|
(id) => !uniqueIds.has(id),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Remove songs from songs object if they're not in default or priority
|
state.queue.shuffled = state.queue.shuffled.filter(
|
||||||
const remainingIds = new Set([
|
(id) => !uniqueIds.has(id),
|
||||||
...state.queue.default,
|
);
|
||||||
...state.queue.priority,
|
|
||||||
]);
|
cleanupOrphanedSongs(state);
|
||||||
const filteredSongs: Record<string, QueueSong> = {};
|
|
||||||
Object.values(state.queue.songs).forEach((song) => {
|
|
||||||
if (remainingIds.has(song._uniqueId)) {
|
|
||||||
filteredSongs[song._uniqueId] = song;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
state.queue.songs = filteredSongs;
|
|
||||||
|
|
||||||
const newQueue = [...state.queue.priority, ...state.queue.default];
|
const newQueue = [...state.queue.priority, ...state.queue.default];
|
||||||
|
|
||||||
@@ -569,18 +559,24 @@ export const usePlayerStoreBase = create<PlayerState>()(
|
|||||||
getQueueOrder: () => {
|
getQueueOrder: () => {
|
||||||
const queueType = getQueueType();
|
const queueType = getQueueType();
|
||||||
const state = get();
|
const state = get();
|
||||||
const songsMap = new Map(Object.entries(state.queue.songs));
|
const songs = state.queue.songs;
|
||||||
|
|
||||||
if (queueType === PlayerQueueType.PRIORITY) {
|
if (queueType === PlayerQueueType.PRIORITY) {
|
||||||
const defaultIds = state.queue.default;
|
const defaultIds = state.queue.default;
|
||||||
const priorityIds = state.queue.priority;
|
const priorityIds = state.queue.priority;
|
||||||
|
|
||||||
const defaultQueue = defaultIds
|
const defaultQueue: QueueSong[] = [];
|
||||||
.map((id) => songsMap.get(id))
|
const priorityQueue: QueueSong[] = [];
|
||||||
.filter((song): song is QueueSong => song !== undefined);
|
|
||||||
const priorityQueue = priorityIds
|
for (const id of priorityIds) {
|
||||||
.map((id) => songsMap.get(id))
|
const song = songs[id];
|
||||||
.filter((song): song is QueueSong => song !== undefined);
|
if (song) priorityQueue.push(song);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const id of defaultIds) {
|
||||||
|
const song = songs[id];
|
||||||
|
if (song) defaultQueue.push(song);
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
groups: [
|
groups: [
|
||||||
@@ -592,9 +588,12 @@ export const usePlayerStoreBase = create<PlayerState>()(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const defaultIds = state.queue.default;
|
const defaultIds = state.queue.default;
|
||||||
const defaultQueue = defaultIds
|
const defaultQueue: QueueSong[] = [];
|
||||||
.map((id) => songsMap.get(id))
|
|
||||||
.filter((song): song is QueueSong => song !== undefined);
|
for (const id of defaultIds) {
|
||||||
|
const song = songs[id];
|
||||||
|
if (song) defaultQueue.push(song);
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
groups: [{ count: defaultQueue.length, name: 'All' }],
|
groups: [{ count: defaultQueue.length, name: 'All' }],
|
||||||
@@ -736,6 +735,7 @@ export const usePlayerStoreBase = create<PlayerState>()(
|
|||||||
state.player.index = -1;
|
state.player.index = -1;
|
||||||
state.queue.default = [];
|
state.queue.default = [];
|
||||||
state.queue.priority = [];
|
state.queue.priority = [];
|
||||||
|
state.queue.shuffled = [];
|
||||||
state.queue.songs = {};
|
state.queue.songs = {};
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -1073,6 +1073,7 @@ export const usePlayerStoreBase = create<PlayerState>()(
|
|||||||
}
|
}
|
||||||
|
|
||||||
state.player.queueType = queueType;
|
state.player.queueType = queueType;
|
||||||
|
cleanupOrphanedSongs(state);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
setRepeat: (repeat: PlayerRepeat) => {
|
setRepeat: (repeat: PlayerRepeat) => {
|
||||||
@@ -1085,6 +1086,7 @@ export const usePlayerStoreBase = create<PlayerState>()(
|
|||||||
state.player.shuffle = shuffle;
|
state.player.shuffle = shuffle;
|
||||||
const queue = state.queue.default;
|
const queue = state.queue.default;
|
||||||
state.queue.shuffled = shuffleInPlace([...queue]);
|
state.queue.shuffled = shuffleInPlace([...queue]);
|
||||||
|
cleanupOrphanedSongs(state);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
setSpeed: (speed: number) => {
|
setSpeed: (speed: number) => {
|
||||||
@@ -1218,6 +1220,28 @@ export const usePlayerStoreBase = create<PlayerState>()(
|
|||||||
) as typeof filteredState.player;
|
) as typeof filteredState.player;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (filteredState.queue) {
|
||||||
|
const allQueueIds = new Set([
|
||||||
|
...(filteredState.queue.default || []),
|
||||||
|
...(filteredState.queue.priority || []),
|
||||||
|
...(filteredState.queue.shuffled || []),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const songs = filteredState.queue.songs || {};
|
||||||
|
const cleanedSongs: Record<string, QueueSong> = {};
|
||||||
|
|
||||||
|
for (const [id, song] of Object.entries(songs)) {
|
||||||
|
if (allQueueIds.has(id)) {
|
||||||
|
cleanedSongs[id] = song;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
filteredState.queue = {
|
||||||
|
...filteredState.queue,
|
||||||
|
songs: cleanedSongs,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
return filteredState;
|
return filteredState;
|
||||||
},
|
},
|
||||||
storage: createJSONStorage(() => idbStateStorage),
|
storage: createJSONStorage(() => idbStateStorage),
|
||||||
@@ -1531,25 +1555,73 @@ export const usePlayerQueue = () => {
|
|||||||
return usePlayerStoreBase(
|
return usePlayerStoreBase(
|
||||||
useShallow((state) => {
|
useShallow((state) => {
|
||||||
const queueType = state.player.queueType;
|
const queueType = state.player.queueType;
|
||||||
|
const songs = state.queue.songs;
|
||||||
|
|
||||||
switch (queueType) {
|
switch (queueType) {
|
||||||
case PlayerQueueType.DEFAULT: {
|
case PlayerQueueType.DEFAULT: {
|
||||||
const queue = state.queue.default;
|
const queue = state.queue.default;
|
||||||
return queue.map((id) => state.queue.songs[id]);
|
const result: QueueSong[] = [];
|
||||||
|
for (const id of queue) {
|
||||||
|
const song = songs[id];
|
||||||
|
if (song) result.push(song);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
case PlayerQueueType.PRIORITY: {
|
case PlayerQueueType.PRIORITY: {
|
||||||
const priorityQueue = state.queue.priority;
|
const priorityQueue = state.queue.priority;
|
||||||
return priorityQueue.map((id) => state.queue.songs[id]);
|
const result: QueueSong[] = [];
|
||||||
|
for (const id of priorityQueue) {
|
||||||
|
const song = songs[id];
|
||||||
|
if (song) result.push(song);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
const defaultQueue = state.queue.default;
|
const defaultQueue = state.queue.default;
|
||||||
return defaultQueue.map((id) => state.queue.songs[id]);
|
const result: QueueSong[] = [];
|
||||||
|
for (const id of defaultQueue) {
|
||||||
|
const song = songs[id];
|
||||||
|
if (song) result.push(song);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function cleanupOrphanedSongs(state: any): boolean {
|
||||||
|
const allQueueIds = new Set([
|
||||||
|
...state.queue.default,
|
||||||
|
...state.queue.priority,
|
||||||
|
...state.queue.shuffled,
|
||||||
|
]);
|
||||||
|
|
||||||
|
const songs = state.queue.songs;
|
||||||
|
const songIds = Object.keys(songs);
|
||||||
|
let hasOrphans = false;
|
||||||
|
const orphanedIds: string[] = [];
|
||||||
|
|
||||||
|
for (const songId of songIds) {
|
||||||
|
if (!allQueueIds.has(songId)) {
|
||||||
|
orphanedIds.push(songId);
|
||||||
|
hasOrphans = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasOrphans) {
|
||||||
|
const cleanedSongs: Record<string, QueueSong> = {};
|
||||||
|
for (const songId of songIds) {
|
||||||
|
if (!orphanedIds.includes(songId)) {
|
||||||
|
cleanedSongs[songId] = songs[songId];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
state.queue.songs = cleanedSongs;
|
||||||
|
}
|
||||||
|
|
||||||
|
return hasOrphans;
|
||||||
|
}
|
||||||
|
|
||||||
function getQueueType() {
|
function getQueueType() {
|
||||||
const queueType: PlayerQueueType = usePlayerStore.getState().player.queueType;
|
const queueType: PlayerQueueType = usePlayerStore.getState().player.queueType;
|
||||||
return queueType;
|
return queueType;
|
||||||
|
|||||||
Reference in New Issue
Block a user