mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-14 05:06:16 -08:00
refactor: add Stalker VOD and serial details interfaces with normalization functions
This commit is contained in:
1 parent
6c52ee349d
commit
522e18e13d
5 files changed
+323
No files matched your search
@@ -27,3 +27,11 @@ export * from './lib/xtream-serie-details.interface';
|
||||
export * from './lib/xtream-serie-item.interface';
|
||||
export * from './lib/xtream-vod-details.interface';
|
||||
export * from './lib/xtream-vod-stream.interface';
|
||||
|
||||
// Stalker interfaces
|
||||
export * from './lib/stalker-vod-details.interface';
|
||||
export * from './lib/stalker-serial-details.interface';
|
||||
|
||||
// Unified VOD details (discriminated union + adapters)
|
||||
export * from './lib/vod-details-item.interface';
|
||||
export * from './lib/vod-details-adapters';
|
||||
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* Stalker portal series/serial details response structure.
|
||||
* Contains series metadata from Stalker/Ministra portals.
|
||||
*/
|
||||
export interface StalkerSerialDetails {
|
||||
/** Series title */
|
||||
name: string;
|
||||
/** Plot description */
|
||||
description: string;
|
||||
/** Director name */
|
||||
director: string;
|
||||
/** Comma-separated list of actors */
|
||||
actors: string;
|
||||
/** Release year */
|
||||
year: string;
|
||||
/** Comma-separated genres */
|
||||
genres_str: string;
|
||||
/** Age rating */
|
||||
age: string;
|
||||
/** IMDB rating as string */
|
||||
rating_imdb: string;
|
||||
/** Kinopoisk rating as string */
|
||||
rating_kinopoisk: string;
|
||||
/** Screenshot/poster URL */
|
||||
screenshot_uri: string;
|
||||
/** Date added to portal */
|
||||
added: string;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* Stalker portal VOD details response structure.
|
||||
* Contains movie metadata from Stalker/Ministra portals.
|
||||
*/
|
||||
export interface StalkerVodDetails {
|
||||
/** Unique identifier for the VOD item */
|
||||
id: string;
|
||||
/** Command string used to play the stream (e.g., "/media/file_123.mpg") */
|
||||
cmd: string;
|
||||
/** VOD metadata */
|
||||
info: StalkerVodInfo;
|
||||
}
|
||||
|
||||
export interface StalkerVodInfo {
|
||||
/** Poster/cover image URL */
|
||||
movie_image: string;
|
||||
/** Plot description */
|
||||
description: string;
|
||||
/** Movie title */
|
||||
name: string;
|
||||
/** Comma-separated list of actors */
|
||||
actors: string;
|
||||
/** Director name */
|
||||
director: string;
|
||||
/** Release date (format varies) */
|
||||
releasedate: string;
|
||||
/** Genre(s) */
|
||||
genre: string;
|
||||
/** IMDB rating as string */
|
||||
rating_imdb: string;
|
||||
/** Kinopoisk rating as string */
|
||||
rating_kinopoisk: string;
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
import { XtreamVodDetails } from './xtream-vod-details.interface';
|
||||
import { StalkerVodDetails } from './stalker-vod-details.interface';
|
||||
import {
|
||||
NormalizedVodMeta,
|
||||
VodDetailsItem,
|
||||
XtreamVodItem,
|
||||
StalkerVodItem,
|
||||
} from './vod-details-item.interface';
|
||||
|
||||
/**
|
||||
* Normalizes Xtream VOD details into a display-friendly format.
|
||||
* Handles the various field name variations in Xtream API responses.
|
||||
*/
|
||||
export function normalizeXtreamVod(item: XtreamVodDetails): NormalizedVodMeta {
|
||||
const info = item?.info;
|
||||
const movieData = item?.movie_data;
|
||||
|
||||
return {
|
||||
title: info?.name || movieData?.name || 'Unknown',
|
||||
description: info?.description || info?.plot,
|
||||
posterUrl: info?.movie_image || info?.cover_big,
|
||||
backdropUrl: info?.backdrop_path?.[0],
|
||||
year: extractYear(info?.releasedate),
|
||||
genre: info?.genre,
|
||||
duration: info?.duration || formatDuration(info?.duration_secs),
|
||||
country: info?.country,
|
||||
director: info?.director,
|
||||
actors: info?.actors || info?.cast,
|
||||
ratingImdb: info?.rating_imdb,
|
||||
ratingKinopoisk: info?.rating_kinopoisk,
|
||||
youtubeTrailer: info?.youtube_trailer,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes Stalker VOD details into a display-friendly format.
|
||||
* Stalker has fewer metadata fields than Xtream.
|
||||
*/
|
||||
export function normalizeStalkerVod(item: StalkerVodDetails): NormalizedVodMeta {
|
||||
const info = item?.info;
|
||||
|
||||
return {
|
||||
title: info?.name || 'Unknown',
|
||||
description: info?.description,
|
||||
posterUrl: info?.movie_image,
|
||||
backdropUrl: undefined, // Stalker doesn't provide backdrop
|
||||
year: extractYear(info?.releasedate),
|
||||
genre: info?.genre,
|
||||
duration: undefined, // Stalker doesn't provide duration
|
||||
country: undefined, // Stalker doesn't provide country
|
||||
director: info?.director,
|
||||
actors: info?.actors,
|
||||
ratingImdb: info?.rating_imdb,
|
||||
ratingKinopoisk: info?.rating_kinopoisk,
|
||||
youtubeTrailer: undefined, // Stalker doesn't provide trailers
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes any VodDetailsItem to display-friendly format.
|
||||
* Uses discriminated union for type-safe handling.
|
||||
*/
|
||||
export function normalizeVodDetails(item: VodDetailsItem): NormalizedVodMeta {
|
||||
if (item.type === 'xtream') {
|
||||
return normalizeXtreamVod(item.data);
|
||||
}
|
||||
return normalizeStalkerVod(item.data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an XtreamVodItem from raw data and context.
|
||||
*/
|
||||
export function createXtreamVodItem(
|
||||
data: XtreamVodDetails,
|
||||
playlistId: string,
|
||||
vodId?: number
|
||||
): XtreamVodItem {
|
||||
return {
|
||||
type: 'xtream',
|
||||
data,
|
||||
playlistId,
|
||||
vodId: vodId ?? data.movie_data?.stream_id ?? 0,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a StalkerVodItem from raw data and context.
|
||||
*/
|
||||
export function createStalkerVodItem(
|
||||
data: StalkerVodDetails,
|
||||
playlistId: string
|
||||
): StalkerVodItem {
|
||||
return {
|
||||
type: 'stalker',
|
||||
data,
|
||||
playlistId,
|
||||
cmd: data.cmd,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the unique identifier for a VOD item.
|
||||
* Returns vodId for Xtream, id for Stalker.
|
||||
*/
|
||||
export function getVodId(item: VodDetailsItem): number | string {
|
||||
if (item.type === 'xtream') {
|
||||
return item.vodId;
|
||||
}
|
||||
return item.data.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the numeric ID for download/favorite tracking.
|
||||
*/
|
||||
export function getVodNumericId(item: VodDetailsItem): number {
|
||||
if (item.type === 'xtream') {
|
||||
return item.vodId;
|
||||
}
|
||||
return Number(item.data.id) || 0;
|
||||
}
|
||||
|
||||
// ============ Helper Functions ============
|
||||
|
||||
/**
|
||||
* Extracts 4-digit year from various date formats.
|
||||
*/
|
||||
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];
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats duration from seconds to human-readable string.
|
||||
*/
|
||||
function formatDuration(seconds?: number): string | undefined {
|
||||
if (!seconds || seconds <= 0) return undefined;
|
||||
|
||||
const hours = Math.floor(seconds / 3600);
|
||||
const minutes = Math.floor((seconds % 3600) / 60);
|
||||
|
||||
if (hours > 0) {
|
||||
return `${hours}h ${minutes}m`;
|
||||
}
|
||||
return `${minutes}m`;
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
import { XtreamVodDetails } from './xtream-vod-details.interface';
|
||||
import { StalkerVodDetails } from './stalker-vod-details.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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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';
|
||||
}
|
||||
Reference in new issue
Block a user