Files
arr/crates/arr-db/migrations/0029_one_sidecar_per_language.sql
T
Miguel Palhas 29c31beceb feat(arr): one sidecar per language, in the schema
DESIGN.md §15 as amended: a language is satisfied by exactly one
sidecar, and no filename segment distinguishes forced from plain from
SDH. A unique index over sidecar rows says so; embedded rows keep their
own key, since several tracks for one language can legitimately coexist
inside a video.

Existing databases may hold a duplicate from a manual grab that beat the
API's path check, so the migration resolves them rather than failing: a
real subtitle beats a machine translation, and of two of the same kind
the newest wins. The files stay on disk for the manual delete to clean
up.

`record_file` no longer swallows every conflict — only the two that mean
"arr already knows this file".
2026-08-25 08:28:51 +01:00

42 lines
1.8 KiB
SQL

-- #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;