Files
iptvnator/libs/shared/interfaces/src/lib/provider-metadata.util.ts
T
4grayandClaude Opus 5.5 ebb1ab3467 fix(portals): hide provider placeholder metadata (N/A, -, 0) in details and cards (#1923)
* fix(portals): drop provider placeholder metadata at the mapping layer

Stalker and Xtream portals send "N/A", "-" or an unrated "0" score for
fields they have nothing for, and the detail templates rendered them as
data. Normalize them where provider rows are mapped: createStalkerInfo,
the shared VOD view-model adapters and the Xtream detail selection.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(portals): hide placeholder ratings on catalog cards

formatGridRating passed a provider "N/A" through as the badge text and
rendered an unrated zero as "0.0". Read the value through the shared
readProviderRating so both hide the badge and resolveGridRating reaches
the generic rating behind a placeholder rating_imdb.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(xtream): keep placeholder ratings off the sparse VOD fallback page

A get_vod_info made only of placeholders is now stripped to sparse and
routed to the fallback page, which read the top-level catalog ratings
raw: "N/A" survived and a zero became "0.0". Read every candidate
through readProviderRating so the next usable rating wins.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 08:06:03 +02:00

60 lines
2.2 KiB
TypeScript

/**
* Readers for provider detail metadata (`get_vod_info`, Stalker VOD rows).
*
* Portals fill a field they have nothing for with a stand-in instead of
* leaving it empty — `"N/A"` (in any case) or a lone `"-"` — and a detail
* template that renders any truthy value then shows the stand-in as if it
* were data: a `⭐ N/A` chip, an "N/A" director, an "N/A" plot. The detail
* shell's contract is that missing data simply is not rendered, so these
* readers turn every stand-in into "absent" where provider rows are MAPPED,
* and templates keep plain truthiness guards instead of each learning the
* list.
*
* Keep the list to stand-ins providers were actually seen sending: every
* entry also erases a genuine value that happens to read the same.
*/
const PROVIDER_PLACEHOLDERS: ReadonlySet<string> = new Set(['n/a', '-']);
/**
* Trimmed provider text, or `undefined` when it is blank or a placeholder.
* Finite numbers are read as their text form; anything else is absent.
*/
export function readProviderText(value: unknown): string | undefined {
const text =
typeof value === 'string'
? value.trim()
: typeof value === 'number' && Number.isFinite(value)
? String(value)
: '';
return text && !PROVIDER_PLACEHOLDERS.has(text.toLowerCase())
? text
: undefined;
}
/**
* The first candidate that carries real text. `a ?? b` would stop at a
* placeholder (or a blank) and never reach the usable fallback behind it.
*/
export function firstProviderText(...values: unknown[]): string | undefined {
for (const value of values) {
const text = readProviderText(value);
if (text !== undefined) {
return text;
}
}
return undefined;
}
/**
* {@link readProviderText} for an IMDb / Kinopoisk score, where zero is a
* placeholder too: both scales start at 1, so a `"0"` / `"0.0"` is the
* panel's way of saying "unrated" (TMDB's `vote_average` of 0 is read the
* same way by the enrichment merge).
*/
export function readProviderRating(value: unknown): string | undefined {
const text = readProviderText(value);
return text !== undefined && Number(text.replace(',', '.')) === 0
? undefined
: text;
}