mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-13 12:36:15 -08:00
fix(xtream): search all vod and series items
Entire-Checkpoint: 3f453cc0085a
This commit is contained in:
1 parent
0bcb89aaaf
commit
c14b5d5aff
3 files changed
+140
-7
No files matched your search
@@ -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`.
|
||||
+91
-2
@@ -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;
|
||||
});
|
||||
|
||||
|
||||
Reference in new issue
Block a user