diff --git a/migrations/0013_series.sql b/migrations/0013_series.sql new file mode 100644 index 0000000..7ed0f43 --- /dev/null +++ b/migrations/0013_series.sql @@ -0,0 +1,40 @@ +-- 0013_series.sql — television: series, seasons, episodes. +-- +-- Vellum had no season or episode model. media_items was a flat tree and the +-- scanner never parsed S01E02, so a library of television was a list of files +-- in a folder. That is the difference between a movie server and a media +-- server, and it is also what blocks TV metadata: there is nothing to attach a +-- series' poster or overview to. +-- +-- Two decisions worth stating, because both are cheap now and expensive later. +-- +-- * The new columns are additive and nullable. A film keeps NULL for both +-- numbers, so every existing row is untouched and every existing query, the +-- web app and the Jellyfin surface keep working exactly as they do today. +-- Nothing is rewritten, so there is no backfill and no migration risk. +-- +-- * kind gains 'series' and 'season', but an EPISODE STAYS kind='video'. A +-- new 'episode' kind would mean touching every query that says video, for +-- no gain: the parent chain already tells an episode from a film, and a +-- series row is the only genuinely new concept. Keeping video also means a +-- library that has not been rescanned yet behaves identically. +-- +-- A series and a season have no file of their own, so their path is the +-- directory they stand for. That is unique per library, which is what the +-- existing UNIQUE (library_id, path) requires, and it gives the row a stable +-- identity across rescans — the scanner can upsert on it rather than +-- duplicating a series every time it runs. +-- +-- The tree itself is not new. parent_id already carries albums and audiobook +-- chapters, so this is the same shape applied to television: library -> series +-- -> season -> episode. + +ALTER TABLE media_items ADD COLUMN season_number INTEGER; +ALTER TABLE media_items ADD COLUMN episode_number INTEGER; + +-- Listing a series' seasons and a season's episodes is the hot path, and both +-- read by parent and then order by the number. +CREATE INDEX idx_items_parent_season ON media_items(parent_id, season_number); + +-- Finding every series in a library, and every season of one, without a scan. +CREATE INDEX idx_items_kind_parent ON media_items(kind, parent_id);