CREATE TABLE section_categories ( id TEXT PRIMARY KEY, name TEXT NOT NULL UNIQUE, color TEXT NOT NULL, sort_order INTEGER NOT NULL CHECK (sort_order >= 0) ); CREATE TABLE formats ( id TEXT PRIMARY KEY, name TEXT NOT NULL UNIQUE ); CREATE TABLE topics ( id TEXT PRIMARY KEY, name TEXT NOT NULL UNIQUE ); CREATE TABLE pieces ( id TEXT PRIMARY KEY, title TEXT NOT NULL, summary TEXT NOT NULL DEFAULT '', format_id TEXT REFERENCES formats(id) ON DELETE SET NULL, status TEXT NOT NULL CHECK (status IN ('inbox', 'preparing', 'ready', 'archived')), duration_seconds INTEGER NOT NULL DEFAULT 0 CHECK (duration_seconds >= 0), intro TEXT NOT NULL DEFAULT '', moderation_notes TEXT NOT NULL DEFAULT '', transition_text TEXT NOT NULL DEFAULT '', presentation_text TEXT NOT NULL DEFAULT '', created_at_utc TEXT NOT NULL, updated_at_utc TEXT NOT NULL ); CREATE TABLE piece_topics ( piece_id TEXT NOT NULL REFERENCES pieces(id) ON DELETE CASCADE, topic_id TEXT NOT NULL REFERENCES topics(id) ON DELETE CASCADE, PRIMARY KEY (piece_id, topic_id) ); CREATE TABLE piece_category_suggestions ( piece_id TEXT NOT NULL REFERENCES pieces(id) ON DELETE CASCADE, category_id TEXT NOT NULL REFERENCES section_categories(id) ON DELETE CASCADE, PRIMARY KEY (piece_id, category_id) ); CREATE TABLE piece_sources ( id TEXT PRIMARY KEY, piece_id TEXT NOT NULL REFERENCES pieces(id) ON DELETE CASCADE, label TEXT NOT NULL, url TEXT NOT NULL, timecode_seconds INTEGER CHECK (timecode_seconds IS NULL OR timecode_seconds >= 0), position INTEGER NOT NULL CHECK (position >= 0) ); CREATE TABLE shows ( id TEXT PRIMARY KEY, title TEXT NOT NULL, starts_at_utc TEXT NOT NULL, twitch_title TEXT NOT NULL DEFAULT '', announcement_text TEXT NOT NULL DEFAULT '', status TEXT NOT NULL CHECK (status IN ('draft', 'ready', 'live', 'completed')), created_at_utc TEXT NOT NULL, updated_at_utc TEXT NOT NULL ); CREATE TABLE show_sections ( id TEXT PRIMARY KEY, show_id TEXT NOT NULL REFERENCES shows(id) ON DELETE CASCADE, name TEXT NOT NULL, category_id TEXT REFERENCES section_categories(id) ON DELETE SET NULL, position INTEGER NOT NULL CHECK (position >= 0), explicit_duration_seconds INTEGER CHECK ( explicit_duration_seconds IS NULL OR explicit_duration_seconds >= 0 ), UNIQUE (show_id, position) ); CREATE TABLE piece_usages ( id TEXT PRIMARY KEY, show_section_id TEXT NOT NULL REFERENCES show_sections(id) ON DELETE CASCADE, original_piece_id TEXT REFERENCES pieces(id) ON DELETE SET NULL, position INTEGER NOT NULL CHECK (position >= 0), status TEXT NOT NULL CHECK (status IN ('open', 'current', 'done', 'skipped')), duration_seconds INTEGER NOT NULL CHECK (duration_seconds >= 0), title_snapshot TEXT NOT NULL, intro_snapshot TEXT NOT NULL DEFAULT '', moderation_snapshot TEXT NOT NULL DEFAULT '', transition_snapshot TEXT NOT NULL DEFAULT '', presentation_snapshot TEXT NOT NULL DEFAULT '', is_reserve INTEGER NOT NULL DEFAULT 0 CHECK (is_reserve IN (0, 1)), UNIQUE (show_section_id, position) ); CREATE INDEX idx_pieces_status ON pieces(status); CREATE INDEX idx_shows_status_start ON shows(status, starts_at_utc); CREATE INDEX idx_sections_show ON show_sections(show_id, position); CREATE INDEX idx_usages_section ON piece_usages(show_section_id, position);