feat(db): align movie state with canonical MediaState (#85)
ci / web (push) Successful in 29s
e2e / e2e (push) Successful in 47s
ci / rust (push) Successful in 2m59s

This commit was merged in pull request #85.
This commit is contained in:
2026-08-22 23:21:09 +01:00
parent d3cea8693b
commit 62aba6315c
5 changed files with 143 additions and 14 deletions
@@ -0,0 +1,73 @@
-- #73. movies.state used missing|grabbed|imported, predating arr-core's
-- canonical MediaState vocabulary (missing|downloading|available) that
-- series/episodes (0005) already follow.
--
-- SQLite can't ALTER a CHECK constraint, so this rebuilds the table -- which
-- means dropping the old copy. `movie_releases` references it with
-- ON DELETE CASCADE, so the drop would take its rows down too; stash them
-- first and restore them once the new `movies` exists with the same ids.
CREATE TABLE movie_releases_backup AS SELECT * FROM movie_releases;
CREATE TABLE movies_new (
id INTEGER PRIMARY KEY,
tmdb_id INTEGER NOT NULL UNIQUE,
title TEXT NOT NULL,
year INTEGER,
original_language TEXT,
root_id INTEGER NOT NULL REFERENCES roots (id),
wanted INTEGER NOT NULL DEFAULT 1 CHECK (wanted IN (0, 1)),
overrides TEXT NOT NULL DEFAULT '{}' CHECK (json_valid(overrides)),
state TEXT NOT NULL DEFAULT 'missing'
CHECK (state IN ('missing', 'downloading', 'available')),
blocked INTEGER NOT NULL DEFAULT 0 CHECK (blocked IN (0, 1)),
search_attempts INTEGER NOT NULL DEFAULT 0,
last_searched_at TEXT,
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
) STRICT;
INSERT INTO movies_new (
id, tmdb_id, title, year, original_language, root_id, wanted, overrides,
state, blocked, search_attempts, last_searched_at, created_at, updated_at
)
SELECT
id, tmdb_id, title, year, original_language, root_id, wanted, overrides,
CASE state
WHEN 'grabbed' THEN 'downloading'
WHEN 'imported' THEN 'available'
ELSE state
END,
blocked, search_attempts, last_searched_at, created_at, updated_at
FROM movies;
DROP TABLE movies;
ALTER TABLE movies_new RENAME TO movies;
CREATE INDEX movies_pending_search
ON movies (last_searched_at)
WHERE wanted = 1 AND blocked = 0;
CREATE INDEX movies_state ON movies (state);
CREATE INDEX movies_root ON movies (root_id);
-- Dropping `movies` drops the triggers defined on it too (0005); recreate
-- the movie-root invariant they enforced.
CREATE TRIGGER movies_require_movie_root
BEFORE INSERT ON movies
WHEN EXISTS (SELECT 1 FROM roots WHERE id = NEW.root_id AND kind != 'movie')
BEGIN
SELECT RAISE(ABORT, 'movies require a movie root');
END;
CREATE TRIGGER movies_require_movie_root_on_update
BEFORE UPDATE OF root_id ON movies
WHEN EXISTS (SELECT 1 FROM roots WHERE id = NEW.root_id AND kind != 'movie')
BEGIN
SELECT RAISE(ABORT, 'movies require a movie root');
END;
INSERT INTO movie_releases SELECT * FROM movie_releases_backup;
DROP TABLE movie_releases_backup;