-- Migration 033 — SCTE-35 ad-break markers for playout. -- -- Adds the missing SCTE-35 splice feature to the playout (MCR) subsystem. An -- operator can either schedule an ad break on a channel's timeline (relative to -- the active playlist position, or at a wall-clock time) or fire one immediately -- ("splice now"). Each break is recorded here and, when fired, also written to -- the append-only as-run log so it shows in the compliance record alongside the -- clips that aired. -- -- type: -- splice_insert — a scheduled break (out → return), duration_s seconds long -- immediate — fire-now splice (operator pressed "Trigger ad break now") -- splice_out — open-ended avail out (provider break start) -- splice_in — return-to-network (provider break end) -- -- status: pending → fired (when the engine acts on it) → done (when the break -- window has elapsed). cancelled is set if the operator removes a pending break. -- -- The engine (services/playout) acts on a break by logging the cue, marking the -- as-run row, and — where the output path supports it — injecting a real -- SCTE-35 cue (see playout-manager.triggerScte for the injection point/TODO). CREATE TABLE IF NOT EXISTS playout_scte_breaks ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), channel_id UUID NOT NULL REFERENCES playout_channels(id) ON DELETE CASCADE, -- Position on the active playlist this break should fire after (0-based item -- index). NULL for immediate/wall-clock breaks. playlist_pos INTEGER, -- Wall-clock fire time for scheduled breaks. NULL for immediate breaks. scheduled_at TIMESTAMPTZ, duration_s INTEGER NOT NULL DEFAULT 30, -- SCTE-35 event id (the splice_event_id carried in the cue). Auto-assigned. event_id INTEGER NOT NULL DEFAULT 1, type TEXT NOT NULL DEFAULT 'splice_insert', status TEXT NOT NULL DEFAULT 'pending', fired_at TIMESTAMPTZ, created_by UUID REFERENCES users(id) ON DELETE SET NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), CHECK (type IN ('splice_insert','immediate','splice_out','splice_in')), CHECK (status IN ('pending','fired','done','cancelled')) ); CREATE INDEX IF NOT EXISTS idx_playout_scte_channel ON playout_scte_breaks (channel_id, created_at DESC); CREATE INDEX IF NOT EXISTS idx_playout_scte_status ON playout_scte_breaks (status); -- As-run gains a 'scte' result so fired breaks land in the compliance log next to -- the clips. The original migration constrained result to played/skipped/error; -- widen it. ALTER TABLE playout_as_run DROP CONSTRAINT IF EXISTS playout_as_run_result_check; ALTER TABLE playout_as_run ADD CONSTRAINT playout_as_run_result_check CHECK (result IN ('played','skipped','error','scte'));