-- 0082_search_and_grab.up.sql — searching for wants and grabbing releases -- (Scribe milestone #502, #5521). -- -- 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), -- Lidarr's "Any" profile (note #5527): 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() );