-- #222. DESIGN.md §15, as amended: a language is satisfied by exactly one -- sidecar. No filename segment distinguishes forced from plain from SDH, so -- two sidecar rows for one (media file, language) describe two files -- competing for one name. The schema says so now rather than leaving it to -- the API's path check. -- -- Embedded rows are deliberately untouched. They describe tracks inside the -- video, not files on disk, and several can legitimately coexist for one -- language — a plain track and a forced one, say. Their key stays -- `(media_file_id, language, forced, sdh)`. -- A database that predates the constraint may already hold a duplicate from -- a manual grab that beat the path check: a fetched `.pt-PT.srt` beside a -- translated `.pt-PT.mt.srt`, for instance. Resolve rather than fail. Of the -- rows for one (media file, language): keep a real subtitle over a machine -- translation, and of two of the same kind the newest. -- -- The files stay on disk. Deleting a viewer's subtitle during a migration is -- worse than leaving an orphan, and the manual delete (#218, #223) cleans one -- up on request. DELETE FROM subtitle_files WHERE path IS NOT NULL AND id NOT IN ( SELECT id FROM ( SELECT id, ROW_NUMBER() OVER ( PARTITION BY media_file_id, language ORDER BY (origin = 'translated') ASC, created_at DESC, id DESC ) AS place FROM subtitle_files WHERE path IS NOT NULL ) WHERE place = 1 ); CREATE UNIQUE INDEX subtitle_files_one_sidecar ON subtitle_files (media_file_id, language) WHERE path IS NOT NULL;