import type { ChannelMeta } from './channel.model'; import type { ProviderLongId } from '../providers/provider-ids'; /** * Vue « grille » d'une vidéo. Miroir du contrat `Suggestion` v2 * (server/providers/registry.mjs) — voir src/app/search/api.v1.ts. * * Règle structurante (phase 2.1) : une métadonnée absente reste `undefined`. * `durationSec: 0` est interdit : les règles de classification s'appuient sur * « pas de durée connue » pour distinguer une verticale d'un short. */ export interface VideoItem { id: string; // id vidéo canonicalisé provider: ProviderLongId; title: string; thumbnailUrl: string; /** Secondes, > 0. `undefined` = inconnue. */ durationSec?: number; channelName?: string; channelExternalId?: string; channelAvatarUrl?: string; channelMeta?: ChannelMeta; viewCount?: number; /** * Phase 7.3 — spectateurs EN DIRECT (Twitch `viewer_count`). Ce n'est pas un * compteur de vues : à n'afficher que si `isLive === true`. */ viewers?: number; /** ISO 8601. */ publishedAt?: string; // Provider-specific routing hints slug?: string; // Odysee slug when available channel?: string; // Twitch channel when item represents a channel/live // Live status (only providers exposing it, e.g. Twitch, set these) type?: string; // e.g. 'live' | 'channel' | 'video' isLive?: boolean; // true when actually broadcasting now isShort?: boolean; // true when the provider flagged a Short/clip kind?: string; // e.g. Twitch 'vod' | 'clip' game?: string; // e.g. Twitch game/category name width?: number; // video width in px (orientation signal, when exposed) height?: number; // video height in px (orientation signal, when exposed) // --- v2 (phase 1 + 2) --- likes?: number; dislikes?: number; uploaderAvatar?: string; description?: string; tags?: string[]; /** BCP-47. */ language?: string; hasSubtitles?: boolean; embeddable?: boolean; /** Libellés bruts : permettent d'afficher une valeur approchée plutôt qu'un vide. */ durationRaw?: string; viewCountRaw?: string; // --- Phase 7.3 : provenance --- /** * Epoch ms : instant RÉEL de la collecte. `undefined` = inconnu (jamais * `Date.now()` côté front : une donnée vieille de 4 min ne doit pas passer * pour fraîche). */ capturedAt?: number; /** Voie de collecte : `innertube` / `api` / `scrape` / `cache`… */ source?: string; }