diff --git a/libs/portal/shared/util/src/lib/navigation/discover-facet-navigation.ts b/libs/portal/shared/util/src/lib/navigation/discover-facet-navigation.ts index 6ebbba42e..df45ede38 100644 --- a/libs/portal/shared/util/src/lib/navigation/discover-facet-navigation.ts +++ b/libs/portal/shared/util/src/lib/navigation/discover-facet-navigation.ts @@ -4,7 +4,7 @@ import { TmdbCountryFacet, TmdbGenreFacet, TmdbMediaType, - isTmdbYearFacet, + parseFacetYear, } from '@iptvnator/shared/interfaces'; import { discoverLink } from './discover-link.util'; @@ -33,19 +33,6 @@ export interface DiscoverFacetNavigation { openCountry(country: TmdbCountryFacet): void; } -/** - * Year stated by a provider date field, whatever shape it arrives in — - * `1976`, `1999-03-31` and `31-03-1999` all resolve. Reads the first - * four-digit run rather than a fixed slice, which the day-first form - * would otherwise turn into `NaN`, and rejects the `0000-00-00` - * placeholder rather than offering a chip that filters by nothing. - */ -function facetYear(releaseDate: string | null | undefined): number | null { - const match = releaseDate?.match(/\d{4}/); - const year = match ? Number(match[0]) : null; - return year !== null && isTmdbYearFacet(year) ? year : null; -} - /** * Shared behavior behind the clickable year/genre/country chips on every * detail page. Call it from a field initializer (injection context). @@ -80,16 +67,16 @@ export function createDiscoverFacetNavigation( }; const canOpenYear = (releaseDate: string | null | undefined): boolean => - target() !== null && facetYear(releaseDate) !== null; + target() !== null && parseFacetYear(releaseDate) !== null; return { yearLabel(releaseDate) { - const year = facetYear(releaseDate); + const year = parseFacetYear(releaseDate); return year === null ? null : String(year); }, canOpenYear, openYear(releaseDate) { - const year = facetYear(releaseDate); + const year = parseFacetYear(releaseDate); if (year !== null) { navigate({ kind: 'year', year }); } diff --git a/libs/shared/interfaces/src/lib/tmdb.interface.ts b/libs/shared/interfaces/src/lib/tmdb.interface.ts index 1f4e81936..7a183a2be 100644 --- a/libs/shared/interfaces/src/lib/tmdb.interface.ts +++ b/libs/shared/interfaces/src/lib/tmdb.interface.ts @@ -131,6 +131,23 @@ export function isTmdbYearFacet(year: number): boolean { return Number.isInteger(year) && year >= 1000 && year <= 9999; } +/** + * The year stated by a provider date field, whatever shape it arrives in + * — `1976`, `1999-03-31` and `31-03-1999` all resolve to the same year. + * + * Reads the first four-digit run rather than a fixed prefix: slicing + * turns a day-first date into `31-0`, which is both the wrong label and + * an unusable filter. Lives here so the Discover chips and the detail + * adapters cannot drift into disagreeing about what a date says. + */ +export function parseFacetYear( + releaseDate: string | null | undefined +): number | null { + const match = releaseDate?.match(/\d{4}/); + const year = match ? Number(match[0]) : null; + return year !== null && isTmdbYearFacet(year) ? year : null; +} + /** * One cached TMDB lookup. Two kinds of rows share the table, discriminated * by the `lookupKey` prefix: diff --git a/libs/shared/interfaces/src/lib/vod-details-adapters.spec.ts b/libs/shared/interfaces/src/lib/vod-details-adapters.spec.ts index e4da637e5..790d9d049 100644 --- a/libs/shared/interfaces/src/lib/vod-details-adapters.spec.ts +++ b/libs/shared/interfaces/src/lib/vod-details-adapters.spec.ts @@ -1,4 +1,75 @@ -import { youtubeEmbedUrl } from './vod-details-adapters'; +import { + normalizeStalkerVod, + normalizeXtreamVod, + youtubeEmbedUrl, +} from './vod-details-adapters'; + +describe('normalized release year', () => { + const stalker = (releasedate: string) => + normalizeStalkerVod({ + id: '1', + cmd: '', + info: { + movie_image: '', + description: '', + name: 'Film', + actors: '', + director: '', + releasedate, + genre: '', + rating_imdb: '', + rating_kinopoisk: '', + }, + }).year; + + it('reads the year out of a day-first provider date', () => { + // A fixed prefix renders '31-0' here, which is both the wrong + // label and a year the Discover chip cannot filter by + expect(stalker('31-03-1999')).toBe('1999'); + }); + + it('keeps reading ordinary date shapes', () => { + expect(stalker('1976')).toBe('1976'); + expect(stalker('1999-03-31')).toBe('1999'); + expect( + normalizeXtreamVod({ + info: { + kinopoisk_url: '', + tmdb_id: 0, + name: 'Film', + o_name: '', + cover_big: '', + movie_image: '', + releasedate: '2018-05-01', + episode_run_time: 0, + youtube_trailer: '', + director: '', + actors: '', + cast: '', + description: '', + plot: '', + age: '', + mpaa_rating: '', + rating_count_kinopoisk: 0, + country: '', + genre: '', + backdrop_path: [], + duration_secs: 0, + duration: '', + video: [], + audio: [], + bitrate: 0, + rating: 0, + }, + }).year + ).toBe('2018'); + }); + + it('states no year for a placeholder or unusable date', () => { + expect(stalker('0000-00-00')).toBeUndefined(); + expect(stalker('')).toBeUndefined(); + }); +}); describe('youtubeEmbedUrl', () => { it('builds an embed URL from a plain video id (TMDB format)', () => { diff --git a/libs/shared/interfaces/src/lib/vod-details-adapters.ts b/libs/shared/interfaces/src/lib/vod-details-adapters.ts index 55e8db4d7..4334dee27 100644 --- a/libs/shared/interfaces/src/lib/vod-details-adapters.ts +++ b/libs/shared/interfaces/src/lib/vod-details-adapters.ts @@ -1,5 +1,6 @@ import { XtreamVodDetails, getXtreamVodInfo } from './xtream-vod-details.interface'; import { StalkerVodDetails } from './stalker-vod-details.interface'; +import { parseFacetYear } from './tmdb.interface'; import { NormalizedVodMeta, VodDetailsItem, @@ -170,24 +171,28 @@ export function getVodNumericId(item: VodDetailsItem): number { // ============ Helper Functions ============ /** - * Extracts 4-digit year from various date formats. + * Extracts the 4-digit year from the various date formats providers send. + * + * Delegates to the shared facet parser so the displayed year and the + * Discover chip built from it cannot disagree — the old fixed-prefix + * fallback turned a day-first `31-03-1999` into `31-0`, which is both + * the wrong label and an unusable filter. The date-parse fallback stays + * for shapes stating no four-digit run of their own. */ function extractYear(dateString?: string): string | undefined { if (!dateString) return undefined; - // Try to extract 4-digit year from beginning - const yearMatch = dateString.match(/^(\d{4})/); - if (yearMatch) { - return yearMatch[1]; + const facetYear = parseFacetYear(dateString); + if (facetYear !== null) { + return String(facetYear); } - // Try to parse as date and extract year const date = new Date(dateString); if (!isNaN(date.getTime())) { return date.getFullYear().toString(); } - return dateString.slice(0, 4); + return undefined; } /**