mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-11 02:46:16 -08:00
fix(stalker): normalize catalog series flags
This commit is contained in:
1 parent
ec2a8137f9
commit
3081f3e5ad
5 files changed
+118
-49
No files matched your search
+32
-10
@@ -1,7 +1,10 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { patchState, signalStore, withMethods, withState } from '@ngrx/signals';
|
||||
import { DataService, TmdbEnrichmentService } from '@iptvnator/services';
|
||||
import { PlaylistMeta, StalkerPortalActions } from '@iptvnator/shared/interfaces';
|
||||
import {
|
||||
PlaylistMeta,
|
||||
StalkerPortalActions,
|
||||
} from '@iptvnator/shared/interfaces';
|
||||
import { StalkerSessionService } from '../../stalker-session.service';
|
||||
import { withStalkerSelection } from './with-stalker-selection.feature';
|
||||
import { withStalkerSeries } from './with-stalker-series.feature';
|
||||
@@ -187,22 +190,41 @@ describe('withStalkerSeries serialSeasonsResource gating', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('does not fire a series request for a Ministra VOD-series item', async () => {
|
||||
it.each([true, 1, '1'] as const)(
|
||||
'does not fire a series request for a Ministra VOD-series item with is_series=%p',
|
||||
async (isSeries) => {
|
||||
store.setSelectedContentType('vod');
|
||||
store.setSelectedItem({
|
||||
id: '11',
|
||||
name: 'VOD series',
|
||||
is_series: isSeries,
|
||||
});
|
||||
|
||||
// The legit vod-series season request (type=vod) may fire; the
|
||||
// wasted regular-series request (type=series) must not.
|
||||
await waitForCondition(
|
||||
() => dataService.sendIpcEvent.mock.calls.length > 0
|
||||
);
|
||||
await flushResources();
|
||||
|
||||
expect(seriesRequestCalls(dataService.sendIpcEvent)).toHaveLength(
|
||||
0
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
it('does not fetch VOD-series seasons for unsupported truthy is_series', async () => {
|
||||
store.setSelectedContentType('vod');
|
||||
store.setSelectedItem({
|
||||
id: '11',
|
||||
name: 'VOD series',
|
||||
is_series: '1',
|
||||
name: 'Plain VOD',
|
||||
is_series: 'true',
|
||||
});
|
||||
|
||||
// The legit vod-series season request (type=vod) may fire; the
|
||||
// wasted regular-series request (type=series) must not.
|
||||
await waitForCondition(
|
||||
() => dataService.sendIpcEvent.mock.calls.length > 0
|
||||
);
|
||||
await flushResources();
|
||||
await flushResources();
|
||||
|
||||
expect(seriesRequestCalls(dataService.sendIpcEvent)).toHaveLength(0);
|
||||
expect(dataService.sendIpcEvent).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('does not refetch seasons when a VOD item is selected after a series', async () => {
|
||||
|
||||
+2
-1
@@ -17,6 +17,7 @@ import {
|
||||
} from '../../models';
|
||||
import { StalkerContentTypes } from '../../stalker-content-types';
|
||||
import { StalkerSessionService } from '../../stalker-session.service';
|
||||
import { isStalkerSeriesFlag } from '../../stalker-vod.utils';
|
||||
import { StalkerSeriesFeatureStoreContract } from '../stalker-store.contracts';
|
||||
import {
|
||||
executeStalkerRequest,
|
||||
@@ -155,7 +156,7 @@ export function withStalkerSeries() {
|
||||
!selectedItem ||
|
||||
selectedItem.id === undefined ||
|
||||
selectedItem.id === null ||
|
||||
!selectedItem.is_series
|
||||
!isStalkerSeriesFlag(selectedItem.is_series)
|
||||
) {
|
||||
logger.debug(
|
||||
'vodSeriesSeasonsResource skipped - conditions not met'
|
||||
|
||||
+2
-2
@@ -20,6 +20,7 @@ import {
|
||||
import {
|
||||
createPortalFavoritesResource,
|
||||
createRefreshTrigger,
|
||||
isStalkerSeriesFlag,
|
||||
isSelectedStalkerVodFavorite,
|
||||
StalkerSelectedVodItem,
|
||||
toggleStalkerVodFavorite,
|
||||
@@ -92,8 +93,7 @@ export class StalkerCatalogDetailComponent implements OnDestroy {
|
||||
return Boolean(
|
||||
item &&
|
||||
(this.contentType() === 'series' ||
|
||||
item.is_series === true ||
|
||||
String(item.is_series) === '1')
|
||||
isStalkerSeriesFlag(item.is_series))
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -18,8 +18,7 @@ describe('StalkerCatalogFacadeService', () => {
|
||||
};
|
||||
const unsubscribe = jest.fn();
|
||||
let playbackUpdateHandler:
|
||||
| ((data: PlaybackPositionData) => void)
|
||||
| undefined;
|
||||
((data: PlaybackPositionData) => void) | undefined;
|
||||
let playbackPositionBridge: {
|
||||
onPlaybackPositionUpdate: jest.Mock<
|
||||
(() => void) | undefined,
|
||||
@@ -49,6 +48,10 @@ describe('StalkerCatalogFacadeService', () => {
|
||||
[string, number, 'vod' | 'episode']
|
||||
>;
|
||||
};
|
||||
let stalkerStoreMock: Record<string, unknown> & {
|
||||
setSearchPhrase: jest.Mock;
|
||||
setSelectedItem: jest.Mock;
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
playbackUpdateHandler = undefined;
|
||||
@@ -68,38 +71,37 @@ describe('StalkerCatalogFacadeService', () => {
|
||||
}
|
||||
),
|
||||
};
|
||||
stalkerStoreMock = {
|
||||
selectedContentType: signal<'vod' | 'series' | 'itv'>('vod'),
|
||||
limit: signal(14),
|
||||
page: signal(0),
|
||||
getSelectedCategory: signal(null),
|
||||
getPaginatedContent: signal([]),
|
||||
selectedItem: signal(null),
|
||||
getTotalPages: signal(0),
|
||||
isPaginatedContentLoading: signal(false),
|
||||
currentPlaylist: signal(playlist),
|
||||
getSelectedCategoryName: jest.fn(() => null),
|
||||
setSelectedCategory: jest.fn(),
|
||||
clearSelectedItem: jest.fn(),
|
||||
setSearchPhrase: jest.fn(),
|
||||
setPage: jest.fn(),
|
||||
setLimit: jest.fn(),
|
||||
setSelectedItem: jest.fn(),
|
||||
createLinkToPlayVod: jest.fn(),
|
||||
addToFavorites: jest.fn(),
|
||||
removeFromFavorites: jest.fn(),
|
||||
fetchMovieFileId: jest.fn(),
|
||||
fetchLinkToPlay: jest.fn(),
|
||||
resolveVodPlayback: jest.fn(),
|
||||
};
|
||||
|
||||
TestBed.configureTestingModule({
|
||||
providers: [
|
||||
StalkerCatalogFacadeService,
|
||||
{
|
||||
provide: StalkerStore,
|
||||
useValue: {
|
||||
selectedContentType: signal<'vod' | 'series' | 'itv'>(
|
||||
'vod'
|
||||
),
|
||||
limit: signal(14),
|
||||
page: signal(0),
|
||||
getSelectedCategory: signal(null),
|
||||
getPaginatedContent: signal([]),
|
||||
selectedItem: signal(null),
|
||||
getTotalPages: signal(0),
|
||||
isPaginatedContentLoading: signal(false),
|
||||
currentPlaylist: signal(playlist),
|
||||
getSelectedCategoryName: jest.fn(() => null),
|
||||
setSelectedCategory: jest.fn(),
|
||||
clearSelectedItem: jest.fn(),
|
||||
setSearchPhrase: jest.fn(),
|
||||
setPage: jest.fn(),
|
||||
setLimit: jest.fn(),
|
||||
setSelectedItem: jest.fn(),
|
||||
createLinkToPlayVod: jest.fn(),
|
||||
addToFavorites: jest.fn(),
|
||||
removeFromFavorites: jest.fn(),
|
||||
fetchMovieFileId: jest.fn(),
|
||||
fetchLinkToPlay: jest.fn(),
|
||||
resolveVodPlayback: jest.fn(),
|
||||
},
|
||||
useValue: stalkerStoreMock,
|
||||
},
|
||||
{
|
||||
provide: PORTAL_PLAYBACK_POSITIONS,
|
||||
@@ -115,15 +117,60 @@ describe('StalkerCatalogFacadeService', () => {
|
||||
|
||||
it('delegates category search query updates to the Stalker store', () => {
|
||||
const service = TestBed.inject(StalkerCatalogFacadeService);
|
||||
const store = TestBed.inject(StalkerStore) as unknown as {
|
||||
setSearchPhrase: jest.Mock;
|
||||
};
|
||||
|
||||
service.setSearchQuery('matrix');
|
||||
|
||||
expect(store.setSearchPhrase).toHaveBeenCalledWith('matrix');
|
||||
expect(stalkerStoreMock.setSearchPhrase).toHaveBeenCalledWith('matrix');
|
||||
});
|
||||
|
||||
it.each([true, 1, '1'] as const)(
|
||||
'normalizes supported is_series flag %p when selecting an item',
|
||||
(isSeries) => {
|
||||
const service = TestBed.inject(StalkerCatalogFacadeService);
|
||||
|
||||
service.selectItem({ id: '42', is_series: isSeries });
|
||||
|
||||
expect(stalkerStoreMock.setSelectedItem).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
id: '42',
|
||||
is_series: true,
|
||||
})
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
it.each([true, 1, '1'] as const)(
|
||||
'returns empty series progress for supported is_series flag %p',
|
||||
(isSeries) => {
|
||||
const service = TestBed.inject(StalkerCatalogFacadeService);
|
||||
|
||||
expect(
|
||||
service.getItemProgress({ id: '42', is_series: isSeries })
|
||||
).toEqual({ hasSeriesProgress: false });
|
||||
}
|
||||
);
|
||||
|
||||
it.each([false, 0] as const)(
|
||||
'keeps non-series flag %p on the ordinary VOD path',
|
||||
(isSeries) => {
|
||||
const service = TestBed.inject(StalkerCatalogFacadeService);
|
||||
const item = { id: '42', is_series: isSeries };
|
||||
|
||||
service.selectItem(item);
|
||||
|
||||
expect(stalkerStoreMock.setSelectedItem).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
id: '42',
|
||||
is_series: undefined,
|
||||
})
|
||||
);
|
||||
expect(service.getItemProgress(item)).toEqual({
|
||||
progress: 0,
|
||||
isWatched: false,
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
it('persists matching external playback updates for the current playlist', async () => {
|
||||
TestBed.inject(StalkerCatalogFacadeService);
|
||||
await Promise.resolve();
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
} from '@angular/core';
|
||||
import {
|
||||
buildStalkerSelectedVodItem,
|
||||
isStalkerSeriesFlag,
|
||||
StalkerStore,
|
||||
StalkerVodSource,
|
||||
} from '@iptvnator/portal/stalker/data-access';
|
||||
@@ -185,8 +186,7 @@ export class StalkerCatalogFacadeService implements StalkerPortalCatalogFacade<
|
||||
|
||||
selectItem(item: StalkerVodSource): string[] | null {
|
||||
const needsSeriesFetch =
|
||||
this.contentType() === 'vod' &&
|
||||
(item.is_series === '1' || item.is_series === 1);
|
||||
this.contentType() === 'vod' && isStalkerSeriesFlag(item.is_series);
|
||||
|
||||
this.stalkerStore.setSelectedItem(
|
||||
buildStalkerSelectedVodItem(item, needsSeriesFetch)
|
||||
@@ -209,8 +209,7 @@ export class StalkerCatalogFacadeService implements StalkerPortalCatalogFacade<
|
||||
);
|
||||
const isSeries =
|
||||
this.contentType() === 'series' ||
|
||||
item.is_series === '1' ||
|
||||
item.is_series === 1;
|
||||
isStalkerSeriesFlag(item.is_series);
|
||||
|
||||
if (hasSeriesProgress) {
|
||||
return { hasSeriesProgress: true };
|
||||
|
||||
Reference in new issue
Block a user