mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-16 14:16:16 -08:00
The Similar rail only existed on Xtream because it matched against the locally loaded catalog; Stalker catalogs are server-paginated, so its detail views had no rail despite tmdb_recommendations being cached. New CrossPortalSimilarService (libs/services) matches recommendations against ALL imported Xtream playlists with one batched DB_MATCH_TITLES worker request — the same two-tier normalized-title + year-compatibility rule as actor pages and the trending rail. Electron-only; resolves to [] in the PWA. - Stalker: the shared VodDetailsComponent (movies; covers catalog and inline detail hosts) and stalker-series-view (series) now render a "Similar" rail from cross-portal matches, each card badged with the source playlist and navigating into that portal's detail view. - Xtream: vod/serial detail rails keep instant local-catalog matches and append cross-portal matches (current playlist excluded, deduplicated against local hits by normalized exact title), also playlist-badged. - Loads async after the detail view renders, staleness-guarded; the section only appears when there is something to show. Tests: cross-portal-similar.service.spec.ts (PWA gate, navigation targets, playlist exclusion, type/year guards). Docs updated. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
104 lines
2.9 KiB
TypeScript
104 lines
2.9 KiB
TypeScript
import { XtreamVodDetails } from './xtream-vod-details.interface';
|
|
import { StalkerVodDetails } from './stalker-vod-details.interface';
|
|
import { TmdbEnrichedCastMember,
|
|
TmdbRecommendation,
|
|
} from './tmdb.interface';
|
|
|
|
/**
|
|
* Discriminated union for VOD details across portal types.
|
|
* Enables type-safe handling without runtime type checks.
|
|
*
|
|
* @example
|
|
* ```typescript
|
|
* function handleVod(item: VodDetailsItem) {
|
|
* if (item.type === 'xtream') {
|
|
* // TypeScript knows item.data is XtreamVodDetails
|
|
* console.log(item.data.movie_data.stream_id);
|
|
* } else {
|
|
* // TypeScript knows item.data is StalkerVodDetails
|
|
* console.log(item.data.cmd);
|
|
* }
|
|
* }
|
|
* ```
|
|
*/
|
|
export type VodDetailsItem = XtreamVodItem | StalkerVodItem;
|
|
|
|
/**
|
|
* Xtream Codes VOD item wrapper with context.
|
|
*/
|
|
export interface XtreamVodItem {
|
|
/** Discriminator for Xtream Codes portals */
|
|
readonly type: 'xtream';
|
|
/** Full VOD details from Xtream API */
|
|
data: XtreamVodDetails;
|
|
/** Playlist/portal ID for favorites and downloads */
|
|
playlistId: string;
|
|
/** Stream ID from Xtream API */
|
|
vodId: number;
|
|
}
|
|
|
|
/**
|
|
* Stalker portal VOD item wrapper with context.
|
|
*/
|
|
export interface StalkerVodItem {
|
|
/** Discriminator for Stalker portals */
|
|
readonly type: 'stalker';
|
|
/** Full VOD details from Stalker API */
|
|
data: StalkerVodDetails;
|
|
/** Playlist/portal ID for favorites and downloads */
|
|
playlistId: string;
|
|
/** Command string for stream playback */
|
|
cmd: string;
|
|
}
|
|
|
|
/**
|
|
* Normalized metadata for display in templates.
|
|
* Provides a consistent interface regardless of portal type.
|
|
*/
|
|
export interface NormalizedVodMeta {
|
|
/** Movie title */
|
|
title: string;
|
|
/** Plot description */
|
|
description?: string;
|
|
/** Poster/cover image URL */
|
|
posterUrl?: string;
|
|
/** Backdrop image URL (Xtream only) */
|
|
backdropUrl?: string;
|
|
/** Release year (4 digits) */
|
|
year?: string;
|
|
/** Genre(s) */
|
|
genre?: string;
|
|
/** Duration string (e.g., "1h 30m") */
|
|
duration?: string;
|
|
/** Country of origin (Xtream only) */
|
|
country?: string;
|
|
/** Director name */
|
|
director?: string;
|
|
/** Actors/cast (comma-separated) */
|
|
actors?: string;
|
|
/** IMDB rating */
|
|
ratingImdb?: string;
|
|
/** Kinopoisk rating */
|
|
ratingKinopoisk?: string;
|
|
/** YouTube trailer ID (Xtream only) */
|
|
youtubeTrailer?: string;
|
|
/** Cast with profile photos, populated by TMDB enrichment */
|
|
tmdbCast?: TmdbEnrichedCastMember[];
|
|
/** TMDB recommendations (drives the cross-portal "Similar" rail) */
|
|
tmdbRecommendations?: TmdbRecommendation[];
|
|
}
|
|
|
|
/**
|
|
* Type guard to check if item is Xtream VOD.
|
|
*/
|
|
export function isXtreamVod(item: VodDetailsItem): item is XtreamVodItem {
|
|
return item.type === 'xtream';
|
|
}
|
|
|
|
/**
|
|
* Type guard to check if item is Stalker VOD.
|
|
*/
|
|
export function isStalkerVod(item: VodDetailsItem): item is StalkerVodItem {
|
|
return item.type === 'stalker';
|
|
}
|