fix(xtream): search all vod and series items

Entire-Checkpoint: 3f453cc0085a
This commit is contained in:
4gray committed 2026-05-02 00:35:37 +02:00
1 parent 0bcb89aaaf
commit c14b5d5aff
3 files changed
+140 -7

No files matched your search

+35
View File
@@ -0,0 +1,35 @@
# Fix Xtream Type-Level VOD/Series Search
## Summary
- Root cause: `withSelection()` receives the `q` query through the Xtream catalog facade, but `filteredAndSortedContent` returns `sortedContent()` immediately when `selectedCategoryId` is null, so `categorySearchTerm` is ignored on all-items VOD/series routes.
- Fix the store selector so VOD and series search terms filter the current type even when no category is selected.
- No public API, route, schema, or Electron IPC contract changes are needed.
## Key Changes
- Update `libs/portal/xtream/data-access/src/lib/stores/features/with-selection.feature.ts` so:
- `vod` and `series` apply `filterBySearchTerm` whether `selectedCategoryId` is null or set.
- Existing selected-category behavior, sorting, pagination reset, and `live` behavior stay unchanged.
- Empty search terms still return the full sorted current type.
- Update `libs/portal/xtream/data-access/src/lib/stores/features/with-selection.feature.spec.ts` with regression coverage for:
- VOD all-items search with `selectedCategoryId === null`.
- Series all-items search with `selectedCategoryId === null`, ideally across multiple categories.
- Existing category-scoped search remains covered and unchanged.
## Test Plan
- Run `pnpm nx test portal-xtream-data-access`.
- Run `pnpm nx test portal-xtream-feature` to confirm the shared catalog route/facade integration still passes.
- Run `pnpm nx build web` as the Angular/Nx build validation.
- Manual Electron check:
- Start `pnpm run serve:backend`.
- Load agent-browser guidance with `agent-browser skills get core` and `agent-browser skills get electron`.
- Attach using `agent-browser --cdp 9222 snapshot -i`.
- Verify an Xtream playlist can search from `/vod?q=...` and `/series?q=...` with no category selected, and still search inside a selected category.
## Assumptions
- Scope is limited to Xtream VOD/series catalog search, not the separate global search page.
- Documentation updates are not expected because this is a regression fix for existing behavior; reassess after implementation.
- Finalized plan file target: `.plans/2026-05-01-xtream-type-search.md`.
@@ -18,6 +18,12 @@ const TestSelectionStore = signalStore(
category_name: 'Movies',
type: 'vod',
},
{
id: 20,
category_id: '20',
category_name: 'Documentaries',
type: 'vod',
},
],
vodStreams: [
{
@@ -44,9 +50,59 @@ const TestSelectionStore = signalStore(
title: 'Fourth',
added: '1',
},
{
xtream_id: 5,
category_id: '20',
title: 'First Contact',
added: '5',
},
{
xtream_id: 6,
category_id: '20',
title: 'Cosmos',
added: '6',
},
],
serialCategories: [
{
id: 30,
category_id: '30',
category_name: 'Sci-Fi',
type: 'series',
},
{
id: 40,
category_id: '40',
category_name: 'Drama',
type: 'series',
},
],
serialStreams: [
{
xtream_id: 101,
category_id: '30',
title: 'Stargate SG-1',
last_modified: '10',
},
{
xtream_id: 102,
category_id: '30',
title: 'The Expanse',
last_modified: '9',
},
{
xtream_id: 103,
category_id: '40',
title: 'Stargate Atlantis',
last_modified: '8',
},
{
xtream_id: 104,
category_id: '40',
title: 'The Wire',
last_modified: '7',
},
],
serialCategories: [],
serialStreams: [],
}),
withSelection()
);
@@ -96,4 +152,37 @@ describe('withSelection', () => {
'First',
]);
});
it('filters all VOD items when no category is selected', () => {
store.setSelectedContentType('vod');
store.setSelectedCategory(null);
store.setCategorySearchTerm('first');
expect(
store.selectItemsFromSelectedCategory().map((item) => item.title)
).toEqual(['First Contact', 'First']);
});
it('filters all series items across categories when no category is selected', () => {
store.setSelectedContentType('series');
store.setSelectedCategory(null);
store.setCategorySearchTerm('stargate');
expect(
store.selectItemsFromSelectedCategory().map((item) => item.title)
).toEqual(['Stargate SG-1', 'Stargate Atlantis']);
});
it('keeps VOD search scoped to the selected category', () => {
store.setSelectedContentType('vod');
store.setSelectedCategory(10);
store.setCategorySearchTerm('first');
expect(
store.selectItemsFromSelectedCategory().map((item) => item.title)
).toEqual(['First']);
});
});
@@ -268,17 +268,26 @@ export function withSelection() {
? storeAny.vodStreams?.() || []
: storeAny.serialStreams?.() || [];
if (categoryType === 'vod' || categoryType === 'series') {
let filtered = categoryId
? content.filter(
(item) => Number(item.category_id) === categoryId
)
: sortedContent();
filtered = filterBySearchTerm(filtered, searchTerm);
return categoryId
? sortByMode(filtered, sortMode, categoryType)
: filtered;
}
if (!categoryId) {
return sortedContent();
}
let filtered = content.filter(
const filtered = content.filter(
(item) => Number(item.category_id) === categoryId
);
if (categoryType === 'vod' || categoryType === 'series') {
filtered = filterBySearchTerm(filtered, searchTerm);
filtered = sortByMode(filtered, sortMode, categoryType);
}
return filtered;
});