-- Extension du schéma DanceVideos/ pour le projet video_analysis/ -- Voir adr/0004-db-extension-strategy.md -- Convention héritée de DanceVideos : la PK de `videos` est `file_name VARCHAR(255)`, -- les tables filles utilisent `video_file_name VARCHAR(255)` comme FK. PRAGMA foreign_keys = ON; -- Groupes de vidéos d'un même cours (V0.6, voir adr/0010) CREATE TABLE IF NOT EXISTS video_groups ( id INTEGER PRIMARY KEY AUTOINCREMENT, label TEXT, time_start DATETIME, time_end DATETIME, n_videos INTEGER, style TEXT, pipeline_version TEXT NOT NULL, notes TEXT ); CREATE TABLE IF NOT EXISTS video_group_members ( video_file_name VARCHAR(255) PRIMARY KEY, group_id INTEGER NOT NULL, role TEXT CHECK (role IN ('explicative', 'demonstration', 'mixte', 'unknown')), seq_idx INTEGER, FOREIGN KEY (video_file_name) REFERENCES videos(file_name) ON DELETE CASCADE, FOREIGN KEY (group_id) REFERENCES video_groups(id) ON DELETE CASCADE ); CREATE INDEX IF NOT EXISTS idx_vgm_group ON video_group_members(group_id); -- Une analyse par version de pipeline et par vidéo CREATE TABLE IF NOT EXISTS analyses ( id INTEGER PRIMARY KEY AUTOINCREMENT, video_file_name VARCHAR(255) NOT NULL, niveau TEXT NOT NULL, -- "audio_demix" | "beats" | "asr" | "pose" | "segment" | "label" pipeline_version TEXT NOT NULL, started_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, finished_at DATETIME, summary_json TEXT, FOREIGN KEY (video_file_name) REFERENCES videos(file_name) ON DELETE CASCADE ); CREATE INDEX IF NOT EXISTS idx_analyses_video ON analyses(video_file_name, niveau); -- Segmentation audio (parole / musique / silence / mixed) CREATE TABLE IF NOT EXISTS audio_segments ( id INTEGER PRIMARY KEY AUTOINCREMENT, video_file_name VARCHAR(255) NOT NULL, start_s REAL NOT NULL, end_s REAL NOT NULL, kind TEXT NOT NULL CHECK (kind IN ('parole', 'musique', 'silence', 'mixed')), speaker_id TEXT, text TEXT, confidence REAL, FOREIGN KEY (video_file_name) REFERENCES videos(file_name) ON DELETE CASCADE ); CREATE INDEX IF NOT EXISTS idx_audio_segments_video ON audio_segments(video_file_name, start_s); -- Beats / downbeats / tempo CREATE TABLE IF NOT EXISTS beats ( id INTEGER PRIMARY KEY AUTOINCREMENT, video_file_name VARCHAR(255) NOT NULL, t_s REAL NOT NULL, beat_in_bar INTEGER NOT NULL, -- 1..4 typiquement is_downbeat BOOLEAN NOT NULL, bpm REAL, FOREIGN KEY (video_file_name) REFERENCES videos(file_name) ON DELETE CASCADE ); CREATE INDEX IF NOT EXISTS idx_beats_video ON beats(video_file_name, t_s); -- Régimes intra-vidéo (avec_compte / en_musique / mixte / explication / silence) — voir adr/0009 CREATE TABLE IF NOT EXISTS regime_segments ( id INTEGER PRIMARY KEY AUTOINCREMENT, video_file_name VARCHAR(255) NOT NULL, start_s REAL NOT NULL, end_s REAL NOT NULL, regime TEXT NOT NULL CHECK (regime IN ('avec_compte', 'en_musique', 'mixte', 'explication', 'compte_avec_explication', 'silence')), confidence REAL, pipeline_version TEXT NOT NULL, FOREIGN KEY (video_file_name) REFERENCES videos(file_name) ON DELETE CASCADE ); CREATE INDEX IF NOT EXISTS idx_regime_video_t ON regime_segments(video_file_name, start_s); -- Transcription word-level (WhisperX) — voir adr/0008 CREATE TABLE IF NOT EXISTS transcription_words ( id INTEGER PRIMARY KEY AUTOINCREMENT, video_file_name VARCHAR(255) NOT NULL, audio_segment_id INTEGER, t_start_s REAL NOT NULL, t_end_s REAL NOT NULL, word TEXT NOT NULL, speaker_id TEXT, confidence REAL, pipeline_version TEXT NOT NULL, FOREIGN KEY (video_file_name) REFERENCES videos(file_name) ON DELETE CASCADE, FOREIGN KEY (audio_segment_id) REFERENCES audio_segments(id) ON DELETE SET NULL ); CREATE INDEX IF NOT EXISTS idx_words_video_t ON transcription_words(video_file_name, t_start_s); CREATE INDEX IF NOT EXISTS idx_words_text ON transcription_words(word); -- Labels sémantiques attachés aux dance_segments depuis l'ASR (V0.5.4) CREATE TABLE IF NOT EXISTS dance_segment_labels ( id INTEGER PRIMARY KEY AUTOINCREMENT, segment_id TEXT NOT NULL, label TEXT NOT NULL, category TEXT, -- "body_part" | "movement" | "direction" | "figure" | "rhythm" source TEXT NOT NULL CHECK (source IN ('asr-keyword', 'asr-figure', 'user', 'manual')), t_offset_s REAL, -- décalage du keyword par rapport au début du segment (négatif = avant) confidence REAL, pipeline_version TEXT NOT NULL, FOREIGN KEY (segment_id) REFERENCES dance_segments(segment_id) ON DELETE CASCADE ); CREATE INDEX IF NOT EXISTS idx_dsl_segment ON dance_segment_labels(segment_id); CREATE INDEX IF NOT EXISTS idx_dsl_label ON dance_segment_labels(label); -- Personnes détectées dans la vidéo (prof, partenaire, audience) CREATE TABLE IF NOT EXISTS persons ( id INTEGER PRIMARY KEY AUTOINCREMENT, video_file_name VARCHAR(255) NOT NULL, person_id INTEGER NOT NULL, -- ID stable inter-frames (ByteTrack) role TEXT NOT NULL CHECK (role IN ('prof', 'partner', 'audience', 'unknown')), notes TEXT, FOREIGN KEY (video_file_name) REFERENCES videos(file_name) ON DELETE CASCADE, UNIQUE (video_file_name, person_id) ); -- Pose keypoints (frame-decimated, 1/N frame typiquement) CREATE TABLE IF NOT EXISTS pose_keypoints ( id INTEGER PRIMARY KEY AUTOINCREMENT, video_file_name VARCHAR(255) NOT NULL, frame INTEGER NOT NULL, person_id INTEGER NOT NULL, kp_json TEXT NOT NULL, -- JSON compressé : 133 keypoints {x, y, score} FOREIGN KEY (video_file_name) REFERENCES videos(file_name) ON DELETE CASCADE ); CREATE INDEX IF NOT EXISTS idx_pose_kp_video_frame ON pose_keypoints(video_file_name, frame); -- Segments hiérarchiques de chorégraphie (cœur de l'UX cumulation/réduction) CREATE TABLE IF NOT EXISTS dance_segments ( segment_id TEXT PRIMARY KEY, -- ex. "v17:meso:0008" video_file_name VARCHAR(255) NOT NULL, start_s REAL NOT NULL, end_s REAL NOT NULL, level TEXT NOT NULL CHECK (level IN ('macro', 'meso', 'micro', 'nano')), parent_segment_id TEXT, label TEXT, source TEXT NOT NULL CHECK (source IN ('auto-rules', 'auto-tcn', 'teacher-said', 'user')), confidence REAL, FOREIGN KEY (video_file_name) REFERENCES videos(file_name) ON DELETE CASCADE, FOREIGN KEY (parent_segment_id) REFERENCES dance_segments(segment_id) ON DELETE CASCADE ); CREATE INDEX IF NOT EXISTS idx_dance_segments_video ON dance_segments(video_file_name, level, start_s); CREATE INDEX IF NOT EXISTS idx_dance_segments_parent ON dance_segments(parent_segment_id); -- Patterns nommés (mise en espagnol, dile que no, pas de bourré...) CREATE TABLE IF NOT EXISTS patterns ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT UNIQUE NOT NULL, description TEXT, style TEXT, -- "salsa cubaine" | "bachata" | "common" | ... templates_json TEXT, -- liste d'occurrences (video_file_name + segment_id) servant de templates occurrences_count INTEGER DEFAULT 0 ); -- Benchmarks — résultats d'évaluation par pipeline_version × méthode × tolérance × vidéo -- (voir doc/observabilite-backoffice.md § 5, notebooks/02-segmentation-eval) CREATE TABLE IF NOT EXISTS benchmarks ( id INTEGER PRIMARY KEY AUTOINCREMENT, pipeline_version TEXT NOT NULL, -- ex. "v0.5.0", "v0.5.0-beat_this-raw-audio" run_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, notebook TEXT, -- nom/chemin du notebook qui a produit la mesure golden_set TEXT, -- ex. "decoupe/diegoRiviera.json" ou "decoupe/all" video_file_name VARCHAR(255), -- vidéo ; nullable si métrique agrégée method TEXT NOT NULL, -- "V0_meso" | "4bar_grid" | "uniform_5s" | ... tol_s REAL NOT NULL, -- tolérance en secondes precision_score REAL NOT NULL, recall_score REAL NOT NULL, f1_score REAL NOT NULL, n_pred INTEGER NOT NULL, n_gold INTEGER NOT NULL, notes TEXT, FOREIGN KEY (video_file_name) REFERENCES videos(file_name) ON DELETE SET NULL ); CREATE INDEX IF NOT EXISTS idx_benchmarks_version ON benchmarks(pipeline_version, method, tol_s); CREATE INDEX IF NOT EXISTS idx_benchmarks_run ON benchmarks(run_at); -- Journal d'éditions (audit trail + undo/redo unifiés) — voir adr/0013 CREATE TABLE IF NOT EXISTS edits_journal ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id TEXT NOT NULL DEFAULT 'default', video_file_name VARCHAR(255), transaction_id TEXT NOT NULL, op_type TEXT NOT NULL, target_table TEXT NOT NULL, target_id TEXT NOT NULL, before_json TEXT, after_json TEXT, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, undone_at DATETIME, redone_at DATETIME, pipeline_version TEXT, FOREIGN KEY (video_file_name) REFERENCES videos(file_name) ON DELETE SET NULL ); CREATE INDEX IF NOT EXISTS idx_edits_video_t ON edits_journal(video_file_name, created_at); CREATE INDEX IF NOT EXISTS idx_edits_txn ON edits_journal(transaction_id); CREATE INDEX IF NOT EXISTS idx_edits_active ON edits_journal(undone_at, created_at); -- Handles d'ancrage cross-vidéo (paires _C_/_M_) — voir adr/0014 CREATE TABLE IF NOT EXISTS video_group_handles ( id INTEGER PRIMARY KEY AUTOINCREMENT, group_id INTEGER NOT NULL, source_video VARCHAR(255) NOT NULL, source_t_s REAL NOT NULL, target_video VARCHAR(255) NOT NULL, target_t_s REAL NOT NULL, label TEXT, created_by TEXT NOT NULL DEFAULT 'default', created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, notes TEXT, FOREIGN KEY (group_id) REFERENCES video_groups(id) ON DELETE CASCADE, FOREIGN KEY (source_video) REFERENCES videos(file_name) ON DELETE CASCADE, FOREIGN KEY (target_video) REFERENCES videos(file_name) ON DELETE CASCADE ); CREATE INDEX IF NOT EXISTS idx_handle_group ON video_group_handles(group_id); CREATE INDEX IF NOT EXISTS idx_handle_pair ON video_group_handles(source_video, target_video); -- Prédictions de style par classifieur RF (V0.6.1, voir adr/0011) CREATE TABLE IF NOT EXISTS style_predictions ( id INTEGER PRIMARY KEY AUTOINCREMENT, file_name VARCHAR(255) NOT NULL, style TEXT NOT NULL, confidence REAL, model_version TEXT NOT NULL, predicted_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (file_name) REFERENCES videos(file_name) ON DELETE CASCADE ); CREATE INDEX IF NOT EXISTS idx_stylepred_file ON style_predictions(file_name); CREATE INDEX IF NOT EXISTS idx_stylepred_style ON style_predictions(style); -- État de navigation persistant (UX cumulation/réduction) CREATE TABLE IF NOT EXISTS nav_state ( id INTEGER PRIMARY KEY AUTOINCREMENT, video_file_name VARCHAR(255) NOT NULL, user_id TEXT NOT NULL DEFAULT 'default', current_segment_id TEXT, view_level TEXT CHECK (view_level IN ('macro', 'meso', 'micro', 'nano')), updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (video_file_name) REFERENCES videos(file_name) ON DELETE CASCADE, FOREIGN KEY (current_segment_id) REFERENCES dance_segments(segment_id) ON DELETE SET NULL, UNIQUE (video_file_name, user_id) ); -- Atomes de mouvement (V0.10, voir adr/0018) — granularité beat / demi-beat. -- Modèle hybride : continu + soft distribution + argmax + confidence. CREATE TABLE IF NOT EXISTS beat_atoms ( id INTEGER PRIMARY KEY AUTOINCREMENT, video_file_name VARCHAR(255) NOT NULL, person_id INTEGER NOT NULL DEFAULT 0, t_s REAL NOT NULL, beat_position REAL NOT NULL, is_downbeat BOOLEAN NOT NULL, -- Pied gauche left_foot_height REAL, left_foot_speed REAL, left_foot_stability REAL, left_foot_state VARCHAR(20), left_foot_state_confidence REAL, left_foot_state_dist_json TEXT, -- Pied droit right_foot_height REAL, right_foot_speed REAL, right_foot_stability REAL, right_foot_state VARCHAR(20), right_foot_state_confidence REAL, right_foot_state_dist_json TEXT, -- Transfert de poids weight_ratio REAL, weight_transfer_velocity REAL, weight_on VARCHAR(10), -- Cinématique bassin (en hip-widths) pelvis_x REAL, pelvis_y REAL, pelvis_vel_x REAL, pelvis_vel_y REAL, -- Qualité globale pose_confidence REAL NOT NULL, pipeline_version TEXT NOT NULL, computed_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (video_file_name) REFERENCES videos(file_name) ON DELETE CASCADE ); CREATE INDEX IF NOT EXISTS idx_atom_video_t ON beat_atoms(video_file_name, t_s); CREATE INDEX IF NOT EXISTS idx_atom_video_beat ON beat_atoms(video_file_name, beat_position);