release / govulncheck (push) Successful in 21s
release / go (push) Failing after 33s
release / web (push) Successful in 56s
release / integration (push) Failing after 4m48s
release / android (push) Successful in 5m6s
release / Build signed APK (releases and dev) (push) Successful in 5m16s
release / Attach APK to the Release (tag releases only) (push) Skipped
release / Build + push container image (push) Skipped
release / Verify release artifacts (tag releases only) (push) Skipped
- internal/release: reads a release name (format, bitrate, bit depth and sample rate, source, year, video, discography) and judges it against the album wanted: the artist and title must be in it; video rips, discographies, other versions (drumless, instrumental, karaoke, remix, live unless the album is live…) and singles or EPs for an album are refused. Quality tiers on Lidarr's ladder; a .torrent's file list is read for format, video files and a track count short of the album. - internal/acquire: fan-out across every search source with a narrowed search kept, a process-wide pacer per source, every candidate judged and kept with its reasons, and the grab: one live grab per want, tie-broken (a person's grab beats the schedule's; a better release replaces a worse one unless it is nearly done). Each download lands in its own folder under the client's downloads folder. - internal/wanted: the search schedule, every ten minutes, never-searched first, then requests, missing files and followed artists, taking the best release when auto-grab is on (the default). - Migration 0082: acquisition_settings (auto-grab, interval, allowed qualities seeded from Lidarr's "Any"), want search columns, want_candidates, grabs, release_blocks. - Web: the Wanted page shows each want's last search, a Search button and every release found with its verdict (Grab, Grab anyway, Never this one, Allow again). Integrations gains "Searching and grabbing". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
103 lines
5.0 KiB
SQL
103 lines
5.0 KiB
SQL
-- 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()
|
|
);
|