feat(db): add series, season and episode model #71
Reference in New Issue
Block a user
Delete Branch "issue/34-series-model"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Issue
Closes #34
Test plan
just ciReviewed
870f9d20e498573aff3fe160316020e3c75b44dd. Findings on the diff below, plus one that has no line to sit on:0003 added
movie_releasesso a cached release can be associated with a title. There is no episode counterpart, so §9.3 manual search has nowhere to record an episode/release association. Out of scope for #34 — worth its own issue rather than widening this one.@@ -184,2 +187,4 @@}#[derive(Clone, Debug, Default, Eq, PartialEq)]pub struct SeriesOverrides {SeriesOverridesis field-for-field identical toMovieOverrides, butpolicy::evaluatetakes&MovieOverrides(policy.rs:58,policy.rs:106). As written, a series cannot be evaluated against a policy without a conversion that does not exist yet, and the two structs will drift.DESIGN §5.1 describes one override mechanism, not one per aggregate. Reuse a single type — rename
MovieOverridestoTitleOverridesand drop this one.@@ -186,0 +214,4 @@}#[derive(Clone, Copy, Debug, Eq, PartialEq)]pub enum EpisodeState {EpisodeStateisMissing | Downloading | Available; the column added in this migration isCHECK (state IN ('missing', 'grabbed', 'imported')).Movies already have this split, and
0001_movies.sqlsays arr-core "owns the canonical enum this mirrors" — it does not mirror it. This PR makes it two mismatches instead of one. Pick one vocabulary here, or leave a comment saying which side is authoritative.@@ -0,0 +1,97 @@CREATE TABLE series (seriesstores nothing about upstream status, so DESIGN §4.2ended— "series finished upstream and complete" — cannot be derived from this schema. The other four statuses come from episode rows; this one needs TMDB's series status persisted.§4's sketch omits it, §4.2 requires it. Per CLAUDE.md the design document wins, so either add the column or record on #34 that
endedis unimplementable until it exists.@@ -0,0 +14,4 @@CREATE INDEX series_root ON series (root_id);CREATE TRIGGER movies_require_movie_rootTwo things about this block:
movies_require_movie_rootand its update twin change the movies invariant, in a migration named for series. Nothing in the file says why they arrive here (they do belong to this change — TV roots only start existing now — but a reader of0005_series.sqlhas no way to know that).@@ -0,0 +74,4 @@CREATE INDEX episodes_state ON episodes (state);INSERT INTO policies (The TV policies are seeded by copying rows matched on
name = 'Movies — main'/'Movies — kids', and the roots below are then matched onname = 'TV — main'/'TV — kids'.If a later migration renames or removes the 0002 seed rows, both inserts match zero rows and this migration succeeds having created no TV policies and no TV roots — a silent seed failure on any database migrating through it. Spell the policy values out as 0002 does, or guard with
RAISE(ABORT, ...)when the source row is absent.@@ -0,0 +76,4 @@INSERT INTO policies (name, required_audio, dub_blacklist, hdr_rules, size_bands,resolution_pref, source_weights, score_weightsThe TV policies inherit
source_weightsfrom the movie policies:{"Remux":4,"BluRay":3,"WEB-DL":2,...}for main, BluRay-biased.TV releases are overwhelmingly WEB-DL, so the tiebreaker points the wrong way out of the box. 0002 set these per root deliberately (the kids policy biases WEB-DL for pt-PT dubs); copying them wholesale drops that reasoning.
@@ -250,0 +302,4 @@.fetch_one(db.pool()).await.expect("movie schema remains available");assert_eq!(movie_count, 0);Counting rows in an untouched
moviestable asserts nothing — it is 0 whether or not this migration broke the movie schema. Either drop it or assert something the migration could plausibly break, e.g. that a movie insert against a movie root still succeeds.Kept the main TV source order because DESIGN §5.5 explicitly defines
Remux > BluRay > WEB-DLas the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.Addressed the review in
b4c6c87: shared title overrides, canonical episode states, persisted upstream completion, explicit TV policy seeds, migration rationale, and a real movie-schema compatibility assertion.Kept the main TV source order because DESIGN §5.5 explicitly defines
Remux > BluRay > WEB-DL; the TV kids policy retains the §5.2 WEB-DL bias for pt-PT dubbing.Reviewed
b4c6c872bbae061a1eb61591044adc1da556b14e. Previous findings addressed — explicit policy seeds,upstream_ended,TitleOverrides, migration comments, and a test that actually writes a movie row. Three follow-ups on the new commit, all on the diff below.@@ -186,0 +211,4 @@}#[derive(Clone, Copy, Debug, Eq, PartialEq)]pub enum EpisodeState {MovieStateandEpisodeStateare now the same two-line enum twice over, which is the duplicationTitleOverridesjust removed one instance of. Worth the same treatment, or a comment saying the two are expected to diverge.Related:
pub type MovieOverrides = TitleOverrideskeeps the old name alive for five in-tree call sites (policy.rs:58,policy.rs:106,search.rs:6,search.rs:296,search.rs:463). Nothing outside this workspace consumes it, so the alias can go with a rename in the same pass.@@ -0,0 +67,4 @@wanted INTEGER NOT NULL DEFAULT 0 CHECK (wanted IN (0, 1)),-- Mirrors arr-core's canonical EpisodeState vocabulary.state TEXT NOT NULL DEFAULT 'missing'CHECK (state IN ('missing', 'downloading', 'available')),Episodes now mirror
EpisodeState, butmovies.statein0001_movies.sqlis stillCHECK (state IN ('missing', 'grabbed', 'imported')). One database, two vocabularies for the same concept, and 0001's comment — "arr-core (#7) owns the canonical enum this mirrors" — is now false for the table it sits on.Either bring
movies.stateacross in this migration (it is a rewrite of the CHECK plus an UPDATE mapping grabbed→downloading, imported→available), or open an issue and say here which table is the odd one out. Leaving it silent means the next reader has to diff two migrations to find out.@@ -0,0 +91,4 @@json('{"dv_profile_allow":["8.1"],"dv_profile_reject":["5","7"],"allow_hdr10":true,"allow_hdr10plus":true,"allow_sdr":true}'),json('{"2160p":{"floor_gib":8,"target_gib":22,"penalty_points_per_gib_over":60},"1080p":{"floor_gib":3,"target_gib":8,"penalty_points_per_gib_over":60}}'),json('["2160p","1080p"]'),json('{"Remux":4,"BluRay":3,"WEB-DL":2,"WEBRip":1,"HDTV":0}'),TV — kidspicked up the WEB-DL bias and a comment, butTV — mainstill carries the movie weights verbatim:Remux 4, BluRay 3, WEB-DL 2.Same point as before — TV releases are mostly WEB-DL, so on the main root the tiebreaker still leans towards the rarer source. If that is deliberate (you want the remux when one exists), a one-line comment saying so closes it.
Addressed the follow-up in
f7a6295: movies and episodes now shareMediaState, all in-tree callers useTitleOverridesdirectly, and the main TV policy documents its DESIGN §5.5 source ordering.The legacy movie database vocabulary requires rewriting its CHECK constraint, which #34 explicitly excludes; filed #73 for that additive migration and documented the exception here.
Reviewed
f7a62958fd9f4618e1bf7975da4647970ef24785. No findings.All three follow-ups closed:
MediaStatereplaces the duplicated enums and theMovieOverridesalias is gone with every call site renamed;movies.stateis tracked in #73 with the divergence noted at the column; and theTV — mainsource weights are right — DESIGN §5.5 fixes the tier asRemux > BluRay > WEB-DLas a small tiebreaker, so my earlier point about WEB-DL was wrong.Reviewed
c911cebceffa809440be2e0a795054d60d6e8b49. No findings.Clean merge of
origin/main(#70, #55). It touches nothing underarr-coreorarr-db, and theTitleOverrides/MediaStaterename holds — no stale references in the merged tree.