Add the subtitle schema
Vellum had no subtitle support of any kind. This is the storage half. Three sources, and they are genuinely different shapes: a sidecar file beside the video has a path, an embedded stream has an index into the container and no file of its own, and a downloaded track has a path but is regenerable. stream_index is -1 for the file-backed sources rather than NULL, and that is deliberate: NULL is never equal to NULL in SQL, so a unique index over a nullable column would happily accept the same sidecar twice. With -1 the UNIQUE (item_id, source, path, stream_index) actually does its job and a rescan can upsert. Nothing here is authoritative about the media. Deleting every row and rescanning reproduces it exactly, which is what lets a vanished subtitle file have its row removed - unlike a vanished video, which is marked instead (ADR-003) because progress hangs off it and cannot be regenerated. Verified on both paths: a real database at version 13 upgraded to 14 by an older binary-then-newer sequence, and a fresh database applied 0001..0014.
This commit is contained in:
parent
82ded6a9c7
commit
c247539ac0
1 changed files with 45 additions and 0 deletions
45
migrations/0014_subtitles.sql
Normal file
45
migrations/0014_subtitles.sql
Normal file
|
|
@ -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);
|
||||||
Loading…
Add table
Reference in a new issue