mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-10 18:36:15 -08:00
fix(stalker): derive missing season numbers
This commit is contained in:
1 parent
1c3cc6c9fa
commit
8cfc64a3dc
5 files changed
+76
-14
No files matched your search
@@ -43,7 +43,9 @@ Do not stop after making the detail view render.
|
||||
`series`.
|
||||
4. Before either inline or external episode playback, persist the parent
|
||||
`seriesXtreamId` plus resolved `seasonNumber` and `episodeNumber`. Keep
|
||||
generated episode tracking IDs stable for lazy `is_series` episodes.
|
||||
generated episode tracking IDs stable for lazy `is_series` episodes. When
|
||||
`season_number` is absent, derive the coordinate from the same naturally
|
||||
ordered season list used by quick start; do not default every season to 1.
|
||||
5. The dashboard reads saved playback positions; it must not infer episode
|
||||
numbers from provider payloads. Legacy rows without season/episode metadata
|
||||
remain badge-less until that episode is played again.
|
||||
|
||||
@@ -205,6 +205,9 @@ Series inline playback behavior is shared across all three modes:
|
||||
metadata for workspace surfaces to render an episode badge. Existing rows
|
||||
without those fields are intentionally not migrated and remain badge-less
|
||||
until the episode is played again.
|
||||
- Ministra payloads may omit `season_number`. Episode mapping and lazy
|
||||
quick-start labels share the same naturally ordered season fallback so later
|
||||
seasons are not persisted as season 1.
|
||||
|
||||
The VOD-series contract is cross-surface:
|
||||
|
||||
|
||||
@@ -78,6 +78,44 @@ describe('stalker-series.adapters', () => {
|
||||
expect(firstEpisode.id).not.toBe(mapped['1'][1].id);
|
||||
});
|
||||
|
||||
it('derives missing VOD-series season numbers from natural season order', () => {
|
||||
const mapped = mapVodSeriesEpisodes([
|
||||
{
|
||||
id: 'season-2',
|
||||
video_id: 'v1',
|
||||
name: 'Season 2',
|
||||
season_number: '',
|
||||
episodes: [
|
||||
{
|
||||
id: 'episode-2',
|
||||
series_number: 1,
|
||||
name: 'Second season pilot',
|
||||
},
|
||||
],
|
||||
isLoading: false,
|
||||
isExpanded: false,
|
||||
},
|
||||
{
|
||||
id: 'season-1',
|
||||
video_id: 'v1',
|
||||
name: 'Season 1',
|
||||
season_number: '',
|
||||
episodes: [
|
||||
{
|
||||
id: 'episode-1',
|
||||
series_number: 1,
|
||||
name: 'Pilot',
|
||||
},
|
||||
],
|
||||
isLoading: false,
|
||||
isExpanded: false,
|
||||
},
|
||||
]);
|
||||
|
||||
expect(mapped['Season 1'][0].season).toBe(1);
|
||||
expect(mapped['Season 2'][0].season).toBe(2);
|
||||
});
|
||||
|
||||
it('maps embedded series payload into regular season episodes', () => {
|
||||
const regularSeasons = mapRegularSeriesSeasons(
|
||||
{
|
||||
|
||||
@@ -7,6 +7,11 @@ import {
|
||||
} from './models';
|
||||
import { isStalkerSeriesFlag } from './stalker-vod.utils';
|
||||
|
||||
const naturalSeasonCollator = new Intl.Collator(undefined, {
|
||||
numeric: true,
|
||||
sensitivity: 'base',
|
||||
});
|
||||
|
||||
export interface VodSeriesSeasonVm {
|
||||
id: string;
|
||||
video_id: string;
|
||||
@@ -154,7 +159,7 @@ export function mapVodSeriesEpisodes(
|
||||
|
||||
seasons.forEach((season) => {
|
||||
const seasonKey = season.season_number || season.name || season.id;
|
||||
const seasonNum = toEpisodeNumber(seasonKey) || 1;
|
||||
const seasonNum = getVodSeriesSeasonNumber(season, seasons);
|
||||
|
||||
mapped[seasonKey] = (season.episodes ?? []).map((episode) => {
|
||||
const episodeNum =
|
||||
@@ -230,3 +235,24 @@ export function mapRegularSeriesEpisodes(
|
||||
export function getVodSeriesSeasonKey(season: VodSeriesSeasonVm): string {
|
||||
return season.season_number || season.name || season.id;
|
||||
}
|
||||
|
||||
export function getVodSeriesSeasonNumber(
|
||||
season: VodSeriesSeasonVm,
|
||||
seasons: ReadonlyArray<VodSeriesSeasonVm>
|
||||
): number {
|
||||
const parsedSeasonNumber = Number(season.season_number);
|
||||
if (Number.isInteger(parsedSeasonNumber) && parsedSeasonNumber > 0) {
|
||||
return parsedSeasonNumber;
|
||||
}
|
||||
|
||||
const orderedSeasons = [...seasons].sort((seasonA, seasonB) =>
|
||||
naturalSeasonCollator.compare(
|
||||
getVodSeriesSeasonKey(seasonA),
|
||||
getVodSeriesSeasonKey(seasonB)
|
||||
)
|
||||
);
|
||||
const seasonIndex = orderedSeasons.findIndex(
|
||||
(candidate) => candidate.id === season.id
|
||||
);
|
||||
return seasonIndex >= 0 ? seasonIndex + 1 : 1;
|
||||
}
|
||||
+5
-12
@@ -6,6 +6,7 @@ import {
|
||||
} from '@iptvnator/portal/shared/util';
|
||||
import {
|
||||
getVodSeriesSeasonKey,
|
||||
getVodSeriesSeasonNumber,
|
||||
type VodSeriesSeasonVm,
|
||||
} from '@iptvnator/portal/stalker/data-access';
|
||||
import type {
|
||||
@@ -160,16 +161,8 @@ function getLazyVodSeriesEpisodeLabel(
|
||||
season: VodSeriesSeasonVm,
|
||||
seasons: ReadonlyArray<VodSeriesSeasonVm>
|
||||
): string {
|
||||
const seasonIndex = seasons.findIndex((item) => item.id === season.id);
|
||||
const parsedSeasonNumber = Number(season.season_number);
|
||||
const seasonNumber =
|
||||
Number.isInteger(parsedSeasonNumber) && parsedSeasonNumber > 0
|
||||
? parsedSeasonNumber
|
||||
: getFallbackSeasonNumber(seasonIndex);
|
||||
|
||||
return formatSeriesEpisodeCode(seasonNumber, 1);
|
||||
}
|
||||
|
||||
function getFallbackSeasonNumber(seasonIndex: number): number {
|
||||
return seasonIndex >= 0 ? seasonIndex + 1 : 1;
|
||||
return formatSeriesEpisodeCode(
|
||||
getVodSeriesSeasonNumber(season, seasons),
|
||||
1
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user