/* [RFC-0210] Canonical media playback contract — the single, source-agnostic config that drives every Do not resolve files — that is @warpgogol/werkstatt-site/content-source (resolveMedia) + the generated manifest. Do not implement playback or transcoding — that is @warpgogol/werkstatt-site/ui + the kernel command. RFC-0210: introduced the unified media playback contract + video manifest types. RFC-0525: added av1 field to VideoManifestSources for AV1 progressive delivery. RFC-0525: added opt-in av1 boolean to mediaSchema (default false) to skip slow AV1 encoding. RFC-0591: added maxSizeMb field to mediaSchema for two-pass bitrate-capped MP4 encoding. RFC-0950: added fullscreen field to mediaSchema for content-controlled edge-to-edge layout. */ import { z } from "zod"; import type { LivePhoto } from "./live-photo.ts"; /** The three delivery profiles. Profile drives generated formats, transport, player JS, and a11y. */ export const mediaProfileSchema = z.enum(["feature", "background", "ambient"]); export type MediaProfile = z.infer; /** When the loop starts (ambient/background) — mirrors RFC-0202 livePhotoTriggerSchema. */ export const mediaTriggerSchema = z.enum(["in-viewport", "tap", "autoplay"]); export type MediaTrigger = z.infer; /** Source addressing. Exactly one of `name` (explicit token) or `fromImage` (ambient convention). */ export const mediaSourceSchema = z .object({ /** Bare media token (RFC-0053 style). Resolved like resolveImage: /assets/.. */ name: z.string().min(1).optional(), /** Ambient only: derive the clip from a sibling image token (RFC-0202 convention). */ fromImage: z.string().min(1).optional(), }) .strict(); export type MediaSource = z.infer; /** One caption/subtitle track (WCAG 1.2.2). VTT resolved by convention (..vtt) or named. */ export const mediaCaptionSchema = z .object({ lang: z.string().min(2), label: z.string().optional(), /** Default true: this track is shown by default for its language. */ default: z.boolean().optional(), }) .strict(); export type MediaCaption = z.infer; export const mediaSchema = z .object({ profile: mediaProfileSchema.default("feature"), source: mediaSourceSchema, /** Poster image token. Defaults: sibling raster, else generated first-frame poster. */ poster: z.string().optional(), /** * Seconds offset for the auto-generated first-frame poster (ffmpeg `-ss`). Default 1. * Use to skip an intro/title card and capture a representative, fully-rendered frame. */ posterTime: z.number().nonnegative().optional(), /** Required by the validator for feature; ambient/background