diff --git a/migrations/0014_subtitles.sql b/migrations/0014_subtitles.sql new file mode 100644 index 0000000..6af68a3 --- /dev/null +++ b/migrations/0014_subtitles.sql @@ -0,0 +1,45 @@ +-- 0014_subtitles.sql — subtitle tracks per item. +-- +-- Vellum had no subtitle support at all. This is the storage half: a row per +-- track, whether it came from a file next to the video, a stream muxed into it, +-- or a download. +-- +-- Three sources, and they are genuinely different shapes: +-- +-- * 'sidecar' a file beside the video (".en.srt"). Has a path, no stream. +-- * 'embedded' a stream inside the container. Has a stream index, no path +-- of its own — it only exists inside the video file. +-- * 'downloaded' fetched from a provider, cached under the config dir. Has a +-- path, like a sidecar, but is regenerable and can be +-- deleted without losing anything. +-- +-- `stream_index` is -1 for the two file-backed sources rather than NULL, so the +-- UNIQUE below can do its job: NULL is never equal to NULL in SQL, and a unique +-- index over a nullable column would happily accept the same sidecar twice. +-- +-- `language` is BCP-47 where we can tell ("en", "pt-BR") and empty when we +-- cannot. It is a display and preference key, not a correctness one: a track +-- with no language still plays, it just cannot be auto-selected. +-- +-- This table is a cache of what is on disk. Deleting every row and rescanning +-- must reproduce it exactly, which is why nothing here is authoritative about +-- the media itself. + +CREATE TABLE subtitles ( + id INTEGER PRIMARY KEY, + item_id INTEGER NOT NULL REFERENCES media_items(id) ON DELETE CASCADE, + source TEXT NOT NULL, -- 'sidecar' | 'embedded' | 'downloaded' + path TEXT NOT NULL DEFAULT '', -- relative to the library root; empty when embedded + stream_index INTEGER NOT NULL DEFAULT -1, -- ffmpeg stream index; -1 when file-backed + format TEXT NOT NULL DEFAULT '', -- 'srt' | 'ass' | 'ssa' | 'vtt' | 'sub' + language TEXT NOT NULL DEFAULT '', -- BCP-47 where known + label TEXT NOT NULL DEFAULT '', -- what the client shows; the filename or stream title + bytes INTEGER NOT NULL DEFAULT 0, + added_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now')), + + UNIQUE (item_id, source, path, stream_index) +); + +-- The read is always "every track for this item", and it happens on every +-- player open, so it is the one index worth having. +CREATE INDEX idx_subtitles_item ON subtitles(item_id, source, language);