Merge main into blitz/subtitles
Feedback pass 2 and the size-band work landed on main while this branch was finishing. Brings them in ahead of the merge back. # Conflicts: # crates/arr-api/src/movies.rs # crates/arr-api/src/state.rs # crates/arr-daemon/src/main.rs # web/src/main.ts
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
-- §5.5 as amended by #187/#208: size bands are rates against a 45-minute
|
||||
-- reference runtime, scaled by the series' minutes per episode. NULL is a
|
||||
-- missing runtime — TMDB's episode_run_time is frequently empty — and means
|
||||
-- the bands apply unscaled.
|
||||
ALTER TABLE series ADD COLUMN runtime_minutes INTEGER
|
||||
CHECK (runtime_minutes IS NULL OR runtime_minutes > 0);
|
||||
@@ -0,0 +1,8 @@
|
||||
-- #239: §5.7's 30-day attention window runs from the failure, but only
|
||||
-- grabbed_at existed, so a torrent that stalled past the window and then
|
||||
-- hard-failed at import was born outside it. failed_at records the moment
|
||||
-- the grab entered 'failed'. Existing failed rows get grabbed_at as the
|
||||
-- best available approximation.
|
||||
ALTER TABLE grabs ADD COLUMN failed_at TEXT;
|
||||
|
||||
UPDATE grabs SET failed_at = grabbed_at WHERE state = 'failed';
|
||||
@@ -0,0 +1,34 @@
|
||||
-- Issue #244. A root's path could be stored with a trailing separator until
|
||||
-- #243 normalised the incoming value. `roots::update` normalises the payload
|
||||
-- and compares it against the stored value, so such a row never compares
|
||||
-- equal: every edit, a policy change included, takes the relocation branch,
|
||||
-- and there every planned destination is its own source. Normalising the
|
||||
-- payload alone fixed the half that cannot bite; this is the other half.
|
||||
--
|
||||
-- `rtrim` strips every trailing separator at once, so '/mnt/x//' normalises
|
||||
-- in one pass. A bare '/' rtrims to the empty string and is put back, which
|
||||
-- is what `normalize_path` in arr-api does.
|
||||
--
|
||||
-- Guarded, because `roots.path` is UNIQUE and a migration that cannot apply
|
||||
-- stops the daemon booting -- worse than the bug it fixes. A row is
|
||||
-- normalised only when no other row shares its normalised path: neither a
|
||||
-- row already holding the stripped value, nor another trailing-separator row
|
||||
-- that would strip to the same thing. Every row in such a group is left
|
||||
-- exactly as it is. That leaves two roots naming one directory, which is a
|
||||
-- settings mistake for the operator to resolve by hand, not a reason to
|
||||
-- refuse to boot.
|
||||
--
|
||||
-- This cannot introduce a collision either. An updated row's new value is a
|
||||
-- normalised path no other row normalises to, and a row left alone whose raw
|
||||
-- path equalled that value would have had the same normalised path, which is
|
||||
-- the case the guard excludes.
|
||||
UPDATE roots
|
||||
SET path = CASE WHEN rtrim(path, '/') = '' THEN '/' ELSE rtrim(path, '/') END
|
||||
WHERE path <> CASE WHEN rtrim(path, '/') = '' THEN '/' ELSE rtrim(path, '/') END
|
||||
AND NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM roots AS other
|
||||
WHERE other.id <> roots.id
|
||||
AND CASE WHEN rtrim(other.path, '/') = '' THEN '/' ELSE rtrim(other.path, '/') END
|
||||
= CASE WHEN rtrim(roots.path, '/') = '' THEN '/' ELSE rtrim(roots.path, '/') END
|
||||
);
|
||||
@@ -0,0 +1,68 @@
|
||||
-- no-transaction
|
||||
-- #211: a waived release could not record which rule it relaxed. The old
|
||||
-- constraint made the rule name an exact synonym for rejection:
|
||||
--
|
||||
-- CHECK ((verdict = 'rejected') = (rejected_rule IS NOT NULL))
|
||||
--
|
||||
-- so §9.3's deck showed a bare `waived` beside rejected rows that each named
|
||||
-- their own rule, and §5.7's "watchable but not what was asked" lost the half
|
||||
-- that says what was not asked for. The relaxed form still demands a rule on
|
||||
-- a rejection and stops demanding its absence elsewhere.
|
||||
--
|
||||
-- SQLite cannot alter a CHECK, so the table is rebuilt (see 0014). Unlike the
|
||||
-- rebuilds there, `releases` is a parent: `grabs`, `movie_releases`,
|
||||
-- `episode_releases` and `season_releases` all point at it, three of them
|
||||
-- ON DELETE CASCADE. Dropping the old table with foreign keys enforced would
|
||||
-- delete those children (or, for `grabs`, refuse outright), so this follows
|
||||
-- SQLite's own procedure — foreign keys off, the rebuild in one transaction,
|
||||
-- foreign keys back on. `PRAGMA foreign_keys` is a no-op inside a
|
||||
-- transaction, which is why the file opens `-- no-transaction` and manages
|
||||
-- its own; the migration is still all-or-nothing.
|
||||
--
|
||||
-- Rows are copied verbatim. Every existing `waived` row keeps its NULL and
|
||||
-- goes on reading as it does today; only rows written after this migration
|
||||
-- carry a waived rule.
|
||||
PRAGMA foreign_keys = OFF;
|
||||
|
||||
BEGIN;
|
||||
|
||||
CREATE TABLE releases_new (
|
||||
id INTEGER PRIMARY KEY,
|
||||
-- Prowlarr's indexer id. Not a foreign key: indexers live in Prowlarr.
|
||||
indexer_id INTEGER NOT NULL,
|
||||
guid TEXT NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
size INTEGER NOT NULL,
|
||||
seeders INTEGER,
|
||||
publish_date TEXT,
|
||||
download_url TEXT NOT NULL,
|
||||
-- §5.6. What the release name claims, before anything is downloaded.
|
||||
parsed TEXT NOT NULL CHECK (json_valid(parsed)),
|
||||
score REAL,
|
||||
-- §9.3. Three buckets. `rejected_rule` names the rule that killed it so
|
||||
-- an over-strict filter is visible without reading release names, and
|
||||
-- §5.7's waiver names the rule it relaxed for the same reason.
|
||||
verdict TEXT CHECK (verdict IN ('eligible', 'waived', 'rejected')),
|
||||
rejected_rule TEXT,
|
||||
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
||||
UNIQUE (indexer_id, guid),
|
||||
CHECK (verdict != 'rejected' OR rejected_rule IS NOT NULL)
|
||||
) STRICT;
|
||||
|
||||
INSERT INTO releases_new (
|
||||
id, indexer_id, guid, name, size, seeders, publish_date, download_url,
|
||||
parsed, score, verdict, rejected_rule, created_at
|
||||
)
|
||||
SELECT
|
||||
id, indexer_id, guid, name, size, seeders, publish_date, download_url,
|
||||
parsed, score, verdict, rejected_rule, created_at
|
||||
FROM releases;
|
||||
|
||||
DROP TABLE releases;
|
||||
ALTER TABLE releases_new RENAME TO releases;
|
||||
|
||||
CREATE INDEX releases_verdict ON releases (verdict, score);
|
||||
|
||||
COMMIT;
|
||||
|
||||
PRAGMA foreign_keys = ON;
|
||||
Reference in New Issue
Block a user