import { VIDEO_STATE_LABELS, type VideoState } from "../lib/availability"; const badgeBase = "shrink-0 text-[10px] font-semibold uppercase tracking-wide px-1.5 py-0.5 rounded"; // Presence on the source platform. Nothing renders for "available" — the // overwhelming majority — so a card only grows a badge when something is // actually wrong. // // The `?` on MISSING? is load-bearing: that state is a suspicion (the video // dropped out of its channel's listing) and not yet a finding, and its amber // separates it from the confirmed-gone states. Unlisted is slate rather than // red because such a video is still watchable by direct link; it sits under // "missing" only because it left the listing. const STATE_STYLES: Record< Exclude, { label: string; className: string; title: string } > = { maybe_missing: { label: "Missing?", className: "bg-amber-100 text-amber-800 dark:bg-amber-900/40 dark:text-amber-200", title: "Gone from its channel's listing at the last check, but not yet confirmed — it may be deleted, private, members-only, or merely unlisted", }, deleted: { label: "Deleted", className: "bg-rose-100 text-rose-800 dark:bg-rose-900/40 dark:text-rose-200", title: "Removed from the source platform", }, private: { label: "Private", className: "bg-rose-100 text-rose-800 dark:bg-rose-900/40 dark:text-rose-200", title: "Made private on the source platform", }, members_only: { label: "Members", className: "bg-rose-100 text-rose-800 dark:bg-rose-900/40 dark:text-rose-200", title: "Members-only on the source platform", }, unlisted: { label: "Unlisted", className: "bg-slate-200 text-slate-700 dark:bg-slate-700/50 dark:text-slate-200", title: "Not in its channel's listing, but still watchable by direct link", }, }; export function VideoStateBadge({ state }: { state: VideoState }) { if (state === "available") return null; const style = STATE_STYLES[state]; return ( {style.label} ); } export function LivestreamBadge() { return ( Live ); } export function AgeRestrictedBadge() { return ( 18+ ); } // Flags a search result that also exists elsewhere in the archive — a mirror on // another platform, or a re-upload on another channel. Non-interactive on // purpose: it sits inside the card's open-the-video button, and the jump // controls live in their own strip below the header where they can be real // buttons (and where there is room for more than one sibling). export function DuplicateBadge({ count }: { count: number }) { return ( {count === 1 ? "Dupe" : `${count} dupes`} ); } // Flags a stream VOD that is likely past its platform's retention window (Kick // ~30d, Twitch 7–60d), so playback probably fails. `tooltip` explains the // platform-specific retention on hover (see lib/vodExpiry). export function VodExpiredBadge({ tooltip }: { tooltip: string }) { return ( Likely expired ); }