import type { Platform } from "./platform"; import { daysSince } from "./format"; // Retention window (days) after which a platform's stream VODs are typically // deleted, making playback impossible. Only platforms whose VODs expire appear // here; YouTube/Rumble/Odysee keep uploads indefinitely and are omitted. // // These are best-effort thresholds, not exact per-video promises: // - Kick: free-tier VODs live in a `kick-prod-videos-30-day` bucket (~30d). // - Twitch: past broadcasts last 7d (regular) / 14d (affiliate) / // 60d (partner, Prime, Turbo). We can't detect a streamer's tier from // metadata, so we use 14d (the affiliate tier) as the flag point. The // "Likely expired" wording + the tooltip acknowledge the uncertainty for // partner (still up to 60d) and regular (already gone at 7d) accounts. // Tune here if the retention policy changes. export const VOD_RETENTION_DAYS: Partial> = { kick: 30, twitch: 14, }; const RETENTION_TOOLTIP: Partial> = { kick: "Kick keeps VODs about 30 days after the stream, so this one has most likely been deleted. The transcript is still searchable here.", twitch: "Twitch keeps VODs 7–60 days depending on the streamer's account tier, so this one has most likely been deleted. The transcript is still searchable here.", }; export type VodExpiryInfo = { likelyExpired: boolean; tooltip: string }; // Whether a VOD is likely past its platform's retention window, with a tooltip // explaining why. Returns null for platforms whose content doesn't expire (or // when the upload date is unknown), so callers render nothing. export function vodExpiry( platform: Platform, uploadDate: string | undefined | null, ): VodExpiryInfo | null { const retention = VOD_RETENTION_DAYS[platform]; if (retention === undefined) return null; const age = daysSince(uploadDate); if (age === null) return null; return { likelyExpired: age > retention, tooltip: RETENTION_TOOLTIP[platform] ?? "This VOD has most likely expired.", }; }