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