Files
minstrel/internal/db/migrations/0082_search_and_grab.up.sql
T
bvandeusenandClaude Opus 5.5 6028d78844
release / govulncheck (push) Successful in 17s
release / web (push) Successful in 1m33s
release / go (push) Successful in 1m49s
release / integration (push) Successful in 5m42s
release / android (push) Successful in 5m59s
release / Build signed APK (releases and dev) (push) Successful in 6m9s
release / Attach APK to the Release (tag releases only) (push) Skipped
release / Build + push container image (push) Successful in 16s
release / Verify release artifacts (tag releases only) (push) Skipped
docs: migration headers keep the before and after, without ids, quotes or one library's numbers (#6277)
Applies rule 32's migration exception: each header still says what the
schema was, what changes and what it prevents, but task, milestone,
decision and rule ids are replaced by the practice they stood for, the
operator quotes by the decision, single-install counts are dropped, and
pointers to a deleted package and an external design doc are gone.
golang-migrate keeps no checksum, so editing applied migrations'
comments is safe.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 12:34:39 -04:00

102 lines
4.9 KiB
SQL

-- 0082_search_and_grab.up.sql — searching for wants and grabbing releases.
--
-- acquisition_settings one row: whether the schedule takes the best release
-- by itself, how often a want is searched again, which
-- quality tiers are taken, and the last pass's report.
-- wants (columns) when each want was last searched and what came of it.
-- want_candidates every release the last search found for a want, with
-- its verdict and the reasons behind it.
-- grabs downloads started for wants; at most one live per want.
-- release_blocks releases never to take again, by infohash.
CREATE TABLE acquisition_settings (
id boolean PRIMARY KEY DEFAULT true CONSTRAINT acquisition_settings_one_row CHECK (id),
auto_grab boolean NOT NULL DEFAULT true,
-- 0 switches the schedule off.
search_interval_hours integer NOT NULL DEFAULT 24
CONSTRAINT acquisition_settings_interval_check CHECK (search_interval_hours BETWEEN 0 AND 720),
-- As Lidarr's "Any" profile: trash lossy to lossless; unknown off.
allowed_qualities text[] NOT NULL DEFAULT ARRAY['lossless_hires', 'lossless', 'lossy_high',
'lossy_standard', 'lossy_low', 'lossy_poor', 'lossy_trash'],
last_pass_at timestamptz,
last_pass jsonb NOT NULL DEFAULT '{}',
updated_at timestamptz NOT NULL DEFAULT now()
);
INSERT INTO acquisition_settings DEFAULT VALUES;
ALTER TABLE wants
ADD COLUMN searched_at timestamptz,
ADD COLUMN search_outcome text NOT NULL DEFAULT ''
CONSTRAINT wants_search_outcome_check CHECK (search_outcome IN ('', 'grabbed', 'waiting', 'nothing', 'error')),
ADD COLUMN search_considered integer NOT NULL DEFAULT 0,
ADD COLUMN best_release text NOT NULL DEFAULT '',
ADD COLUMN best_verdict text NOT NULL DEFAULT '',
ADD COLUMN search_error text NOT NULL DEFAULT '';
CREATE INDEX wants_search_due_idx ON wants (searched_at NULLS FIRST) WHERE fulfilled_at IS NULL;
CREATE TABLE want_candidates (
want_id uuid NOT NULL REFERENCES wants (id) ON DELETE CASCADE,
-- The infohash, or the name when the source gave none.
key text NOT NULL,
name text NOT NULL,
info_hash text NOT NULL DEFAULT '',
indexer text NOT NULL DEFAULT '',
source text NOT NULL DEFAULT '',
size_bytes bigint NOT NULL DEFAULT 0,
seeders integer NOT NULL DEFAULT 0,
files_count integer NOT NULL DEFAULT 0,
format text NOT NULL DEFAULT '',
quality text NOT NULL DEFAULT '',
year integer NOT NULL DEFAULT 0,
verdict text NOT NULL
CONSTRAINT want_candidates_verdict_check CHECK (verdict IN ('grab', 'review', 'refused', 'rejected', 'blocked')),
reason text NOT NULL DEFAULT '',
reasons jsonb NOT NULL DEFAULT '[]',
folded_from text[] NOT NULL DEFAULT '{}',
-- Position in the search's order, best first.
rank integer NOT NULL DEFAULT 0,
searched_at timestamptz NOT NULL DEFAULT now(),
PRIMARY KEY (want_id, key)
);
CREATE INDEX want_candidates_info_hash_idx ON want_candidates (info_hash) WHERE info_hash <> '';
CREATE TABLE grabs (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
want_id uuid REFERENCES wants (id) ON DELETE SET NULL,
info_hash text NOT NULL DEFAULT '',
name text NOT NULL,
key text NOT NULL,
indexer text NOT NULL DEFAULT '',
source text NOT NULL DEFAULT '',
-- The acquisition_services row of the download client it went to.
client text NOT NULL,
save_path text NOT NULL,
quality text NOT NULL DEFAULT '',
origin text NOT NULL CONSTRAINT grabs_origin_check CHECK (origin IN ('schedule', 'operator')),
forced boolean NOT NULL DEFAULT false,
grabbed_by uuid REFERENCES users (id) ON DELETE SET NULL,
grabbed_at timestamptz NOT NULL DEFAULT now(),
ended_at timestamptz,
end_reason text NOT NULL DEFAULT ''
CONSTRAINT grabs_end_reason_check CHECK (end_reason IN ('', 'superseded')),
end_detail text NOT NULL DEFAULT ''
);
-- One live grab per want; the service decides which, this is the backstop.
CREATE UNIQUE INDEX grabs_one_live_per_want ON grabs (want_id) WHERE ended_at IS NULL AND want_id IS NOT NULL;
CREATE INDEX grabs_info_hash_idx ON grabs (info_hash) WHERE info_hash <> '';
CREATE TABLE release_blocks (
info_hash text PRIMARY KEY,
name text NOT NULL DEFAULT '',
-- operator: "never this one"; files: its .torrent held the wrong thing.
reason text NOT NULL CONSTRAINT release_blocks_reason_check CHECK (reason IN ('operator', 'files')),
detail text NOT NULL DEFAULT '',
want_id uuid REFERENCES wants (id) ON DELETE SET NULL,
blocked_by uuid REFERENCES users (id) ON DELETE SET NULL,
blocked_at timestamptz NOT NULL DEFAULT now()
);