mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-09 17:36:15 -08:00
fix(portals): read the year with one rule everywhere
The shared detail path fed the chip `meta.year`, which the adapter built with a fixed-prefix fallback: a day-first `31-03-1999` became `31-0`, so that path both displayed the wrong label and lost the facet, since the guard could not parse it back. `parseFacetYear()` moves to shared/interfaces and both callers delegate to it, so the adapters and the Discover chips cannot drift into disagreeing about what a date says. The adapter keeps its date-parse fallback for shapes stating no four-digit run, but no longer invents one by slicing. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
5534e51c8d
commit
ec55e4910e
4 files changed
+105
-25
No files matched your search
@@ -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 });
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)', () => {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in new issue
Block a user