From 3aeeaea74f6d8679f97045e77a7e7333a6eb026a Mon Sep 17 00:00:00 2001 From: Maxim Isaev Date: Fri, 24 Apr 2026 17:55:59 +0300 Subject: [PATCH] fix(playlists): respect active library in playlist card playback Ensure playlist card Play action uses tracks filtered by the active library and show filtered song count and duration in the list, so card metadata matches actual playback scope. --- src/pages/Playlists.tsx | 53 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 50 insertions(+), 3 deletions(-) diff --git a/src/pages/Playlists.tsx b/src/pages/Playlists.tsx index 65ad6ceb..164fda9a 100644 --- a/src/pages/Playlists.tsx +++ b/src/pages/Playlists.tsx @@ -191,6 +191,8 @@ export default function Playlists() { const [editingSmartId, setEditingSmartId] = useState(null); const [pendingSmart, setPendingSmart] = useState([]); const [smartCoverIdsByPlaylist, setSmartCoverIdsByPlaylist] = useState>({}); + const [filteredSongCountByPlaylist, setFilteredSongCountByPlaylist] = useState>({}); + const [filteredDurationByPlaylist, setFilteredDurationByPlaylist] = useState>({}); const [playingId, setPlayingId] = useState(null); const [deleteConfirmId, setDeleteConfirmId] = useState(null); const nameInputRef = useRef(null); @@ -271,6 +273,48 @@ export default function Playlists() { return () => { cancelled = true; }; }, [playlists, musicLibraryFilterVersion]); + // Playlist list should reflect active library scope for song counts. + useEffect(() => { + let cancelled = false; + const run = async () => { + if (playlists.length === 0) { + if (!cancelled) { + setFilteredSongCountByPlaylist({}); + setFilteredDurationByPlaylist({}); + } + return; + } + const ids = playlists.map((pl) => pl.id); + const next: Record = {}; + const nextDuration: Record = {}; + for (let i = 0; i < ids.length; i += 4) { + const chunk = ids.slice(i, i + 4); + const rows = await Promise.all( + chunk.map(async (id) => { + try { + const { songs } = await getPlaylist(id); + const filtered = await filterSongsToActiveLibrary(songs); + const duration = filtered.reduce((acc, s) => acc + (s.duration ?? 0), 0); + return [id, filtered.length, duration] as const; + } catch { + return [id, -1, -1] as const; + } + }), + ); + for (const [id, count, duration] of rows) { + if (count >= 0) next[id] = count; + if (duration >= 0) nextDuration[id] = duration; + } + } + if (!cancelled) { + setFilteredSongCountByPlaylist(next); + setFilteredDurationByPlaylist(nextDuration); + } + }; + run(); + return () => { cancelled = true; }; + }, [playlists, musicLibraryFilterVersion]); + useEffect(() => { if (creating) nameInputRef.current?.focus(); }, [creating]); @@ -503,7 +547,8 @@ export default function Playlists() { setPlayingId(pl.id); try { const data = await getPlaylist(pl.id); - const tracks = data.songs.map(songToTrack); + const filteredSongs = await filterSongsToActiveLibrary(data.songs); + const tracks = filteredSongs.map(songToTrack); if (tracks.length > 0) { touchPlaylist(pl.id); playTrack(tracks[0], tracks); @@ -948,8 +993,10 @@ export default function Playlists() { {displayPlaylistName(pl.name)}
- {t('playlists.songs', { n: pl.songCount })} - {pl.duration > 0 && <> · {formatDuration(pl.duration)}} + {t('playlists.songs', { n: filteredSongCountByPlaylist[pl.id] ?? pl.songCount })} + {(filteredDurationByPlaylist[pl.id] ?? pl.duration) > 0 && ( + <> · {formatDuration(filteredDurationByPlaylist[pl.id] ?? pl.duration)} + )}