feat(plex): make the Collection filter multi-select with Any/All
A title can belong to several Plex collections, so the collection filter becomes multi-select combining with Any (OR, default) / All (AND) — the same shape as the genre/actor filters. Backend (query.py): `collection` (single) → `collections` CSV + `collection_mode` on LibraryFilters; meta_filter_conds ORs/ANDs `collection_keys.contains` per id. Frontend: PlexFilters `collection`/`collectionTitle` → `collections` + optional `collectionMode` + `collectionTitles` map; CollectionPicker is now a multi-select Combobox with the any/all match footer; one chip per collection (usePlexActive- Filters); the info-page "Browse collection" strip unions into the list; applyFilter now shallow-merges label maps so adding one keeps the others' titles. PlexProvider migrates a legacy persisted single `collection` into the new shape on read. Test: test_plex_collections_filter (single / any-union / all-intersection).
This commit is contained in:
@@ -272,10 +272,15 @@ export default function PlexBrowse() {
|
||||
// (cast/crew) widens to the 'both' scope so their movies AND shows show up together (mixed feed).
|
||||
function applyFilter(patch: Partial<PlexFilters>) {
|
||||
const merged = { ...filters } as unknown as Record<string, unknown>;
|
||||
const isPlainObject = (x: unknown): x is Record<string, unknown> =>
|
||||
typeof x === "object" && x !== null && !Array.isArray(x);
|
||||
for (const [k, v] of Object.entries(patch)) {
|
||||
const cur = merged[k];
|
||||
if (Array.isArray(v) && Array.isArray(cur)) {
|
||||
merged[k] = Array.from(new Set([...(cur as unknown[]), ...(v as unknown[])]));
|
||||
} else if (isPlainObject(v) && isPlainObject(cur)) {
|
||||
// Merge label maps (collectionTitles / playlistTitles) so adding one keeps the others' titles.
|
||||
merged[k] = { ...cur, ...v };
|
||||
} else {
|
||||
merged[k] = v;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user