release / govulncheck (push) Successful in 15s
release / web (push) Successful in 1m23s
release / go (push) Successful in 1m41s
release / integration (push) Successful in 5m20s
release / android (push) Successful in 5m28s
release / Build signed APK (releases and dev) (push) Successful in 5m30s
release / Attach APK to the Release (tag releases only) (push) Skipped
release / Build + push container image (push) Successful in 27s
release / Verify release artifacts (tag releases only) (push) Skipped
- acquisition_settings.upgrade_until (default High quality lossy, Lidarr's cutoff, note #5527; "" is off), an "Upgrade until" row under Searching. - The wanted worker judges held albums hourly: tier per format from size per second (the scanner records no bitrate), the album's worst. Below the cutoff it is wanted for quality; a video rip among its tracks always. - The schedule searches upgrades weekly, after open wants; a release no better than the copy held is refused ("not_better"). - The import matches each file to the track it would replace (recording, then disc/track) and replaces it when it is a rip or the file is a better tier: over the same path in one rename, or filed beside it and the old track folded in (library.ReplaceTrack: file first, then foldTrackInto). Nothing better: the release is set aside. - Collection › Upgrades (Lidarr's Cutoff Unmet); Suspect sources says when a clean copy was looked for instead of Lidarr's search. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
142 lines
6.9 KiB
SQL
142 lines
6.9 KiB
SQL
-- Searching for wants and grabbing releases (M502 #5521).
|
|
|
|
-- name: GetAcquisitionSettings :one
|
|
SELECT * FROM acquisition_settings WHERE id = true;
|
|
|
|
-- name: UpdateAcquisitionSettings :one
|
|
UPDATE acquisition_settings
|
|
SET auto_grab = sqlc.arg(auto_grab),
|
|
search_interval_hours = sqlc.arg(search_interval_hours),
|
|
allowed_qualities = sqlc.arg(allowed_qualities),
|
|
import_root = sqlc.arg(import_root),
|
|
seed_ratio = sqlc.arg(seed_ratio),
|
|
seed_days = sqlc.arg(seed_days),
|
|
stall_hours = sqlc.arg(stall_hours),
|
|
upgrade_until = sqlc.arg(upgrade_until),
|
|
updated_at = now()
|
|
WHERE id = true
|
|
RETURNING *;
|
|
|
|
-- name: RecordSearchPass :exec
|
|
UPDATE acquisition_settings SET last_pass_at = now(), last_pass = sqlc.arg(report) WHERE id = true;
|
|
|
|
-- name: ListWantsDueForSearch :many
|
|
-- Wants with no download running that were never searched or not since
|
|
-- @hours ago: open wants first (never-searched, then requests, then missing
|
|
-- files, then followed artists' releases), then held albums wanted for an
|
|
-- upgrade (#5524), which are searched at most weekly.
|
|
SELECT w.id, w.release_group_mbid, w.artist_name, w.title, w.reason, w.upgrade, w.held_tier, w.held_quality,
|
|
COALESCE(NULLIF(w.primary_type, ''), g.primary_type, '')::text AS primary_type,
|
|
COALESCE(g.secondary_types, '{}')::text[] AS secondary_types,
|
|
COALESCE(NULLIF(w.first_release_date, ''), g.first_release_date, '')::text AS first_release_date
|
|
FROM wants w
|
|
LEFT JOIN mb_release_groups g ON g.mbid = w.release_group_mbid
|
|
WHERE (w.fulfilled_at IS NULL OR w.upgrade <> '')
|
|
AND NOT EXISTS (SELECT 1 FROM grabs gr WHERE gr.want_id = w.id AND gr.ended_at IS NULL)
|
|
AND (w.searched_at IS NULL
|
|
OR w.searched_at < now() - make_interval(hours => CASE WHEN w.fulfilled_at IS NULL
|
|
THEN sqlc.arg(hours)::int
|
|
ELSE GREATEST(sqlc.arg(hours)::int, 168) END))
|
|
ORDER BY w.fulfilled_at IS NOT NULL,
|
|
w.searched_at IS NOT NULL,
|
|
CASE w.reason WHEN 'request' THEN 0 WHEN 'missing' THEN 1 ELSE 2 END,
|
|
w.searched_at NULLS FIRST, w.wanted_at, w.id
|
|
LIMIT sqlc.arg(max_rows);
|
|
|
|
-- name: GetWantForSearch :one
|
|
SELECT w.id, w.release_group_mbid, w.artist_mbid, w.artist_name, w.title, w.reason, w.fulfilled_at,
|
|
w.upgrade, w.held_tier, w.held_quality, w.album_id,
|
|
COALESCE(NULLIF(w.primary_type, ''), g.primary_type, '')::text AS primary_type,
|
|
COALESCE(g.secondary_types, '{}')::text[] AS secondary_types,
|
|
COALESCE(NULLIF(w.first_release_date, ''), g.first_release_date, '')::text AS first_release_date
|
|
FROM wants w
|
|
LEFT JOIN mb_release_groups g ON g.mbid = w.release_group_mbid
|
|
WHERE w.id = $1;
|
|
|
|
-- name: EditionTrackSpan :one
|
|
-- The fewest tracks an official edition of the group has (any edition when
|
|
-- none is official), and the most any has. Zero when none is catalogued.
|
|
SELECT COALESCE(MIN(track_count) FILTER (WHERE status = 'Official'), MIN(track_count), 0)::int AS min_tracks,
|
|
COALESCE(MAX(track_count), 0)::int AS max_tracks
|
|
FROM mb_releases
|
|
WHERE release_group_mbid = $1 AND track_count > 0;
|
|
|
|
-- name: RecordWantSearch :exec
|
|
UPDATE wants
|
|
SET searched_at = now(),
|
|
search_outcome = sqlc.arg(outcome),
|
|
search_considered = sqlc.arg(considered),
|
|
best_release = sqlc.arg(best_release),
|
|
best_verdict = sqlc.arg(best_verdict),
|
|
search_error = sqlc.arg(search_error),
|
|
updated_at = now()
|
|
WHERE id = sqlc.arg(id);
|
|
|
|
-- name: DeleteWantCandidates :exec
|
|
DELETE FROM want_candidates WHERE want_id = $1;
|
|
|
|
-- name: InsertWantCandidate :exec
|
|
INSERT INTO want_candidates (want_id, key, name, info_hash, indexer, source, size_bytes, seeders,
|
|
files_count, format, quality, year, verdict, reason, reasons, folded_from, rank)
|
|
VALUES (sqlc.arg(want_id), sqlc.arg(key), sqlc.arg(name), sqlc.arg(info_hash), sqlc.arg(indexer),
|
|
sqlc.arg(source), sqlc.arg(size_bytes), sqlc.arg(seeders), sqlc.arg(files_count), sqlc.arg(format),
|
|
sqlc.arg(quality), sqlc.arg(year), sqlc.arg(verdict), sqlc.arg(reason), sqlc.arg(reasons),
|
|
sqlc.arg(folded_from), sqlc.arg(rank))
|
|
ON CONFLICT (want_id, key) DO NOTHING;
|
|
|
|
-- name: ListWantCandidates :many
|
|
SELECT * FROM want_candidates WHERE want_id = $1 ORDER BY rank, key;
|
|
|
|
-- name: GetWantCandidate :one
|
|
SELECT * FROM want_candidates WHERE want_id = $1 AND key = $2;
|
|
|
|
-- name: MarkCandidatesBlocked :exec
|
|
UPDATE want_candidates
|
|
SET verdict = 'blocked', reason = sqlc.arg(reason),
|
|
reasons = jsonb_build_array(jsonb_build_object('kind', 'hard', 'code', 'blocked', 'text', sqlc.arg(reason)::text))
|
|
WHERE info_hash = sqlc.arg(info_hash);
|
|
|
|
-- name: MarkCandidatesUnblocked :exec
|
|
UPDATE want_candidates
|
|
SET verdict = 'review', reason = sqlc.arg(reason),
|
|
reasons = jsonb_build_array(jsonb_build_object('kind', 'soft', 'code', 'unblocked', 'text', sqlc.arg(reason)::text))
|
|
WHERE info_hash = sqlc.arg(info_hash) AND verdict = 'blocked';
|
|
|
|
-- name: GetLiveGrab :one
|
|
SELECT * FROM grabs WHERE want_id = $1 AND ended_at IS NULL;
|
|
|
|
-- name: InsertGrab :one
|
|
INSERT INTO grabs (want_id, info_hash, name, key, indexer, source, client, save_path, quality,
|
|
origin, forced, grabbed_by)
|
|
VALUES (sqlc.narg(want_id), sqlc.arg(info_hash), sqlc.arg(name), sqlc.arg(key), sqlc.arg(indexer),
|
|
sqlc.arg(source), sqlc.arg(client), sqlc.arg(save_path), sqlc.arg(quality),
|
|
sqlc.arg(origin), sqlc.arg(forced), sqlc.narg(grabbed_by))
|
|
RETURNING *;
|
|
|
|
-- name: EndGrab :exec
|
|
UPDATE grabs
|
|
SET ended_at = now(), end_reason = sqlc.arg(end_reason), end_detail = sqlc.arg(end_detail)
|
|
WHERE id = sqlc.arg(id) AND ended_at IS NULL;
|
|
|
|
-- name: ListReleaseBlocks :many
|
|
-- Every release set aside and still held back: a stalled one lifts by itself
|
|
-- at expires_at (#5523).
|
|
SELECT * FROM release_blocks
|
|
WHERE expires_at IS NULL OR expires_at > now()
|
|
ORDER BY blocked_at DESC;
|
|
|
|
-- name: BlockRelease :exec
|
|
-- A release set aside. expires_at NULL is for good. A block for good is never
|
|
-- shortened by a timed one: only a timed block, or another for good, is
|
|
-- replaced.
|
|
INSERT INTO release_blocks (info_hash, name, reason, detail, want_id, blocked_by, expires_at)
|
|
VALUES (sqlc.arg(info_hash), sqlc.arg(name), sqlc.arg(reason), sqlc.arg(detail),
|
|
sqlc.narg(want_id), sqlc.narg(blocked_by), sqlc.narg(expires_at))
|
|
ON CONFLICT (info_hash) DO UPDATE
|
|
SET reason = EXCLUDED.reason, detail = EXCLUDED.detail, blocked_at = now(),
|
|
expires_at = EXCLUDED.expires_at, blocked_by = EXCLUDED.blocked_by
|
|
WHERE release_blocks.expires_at IS NOT NULL OR EXCLUDED.expires_at IS NULL;
|
|
|
|
-- name: UnblockRelease :execrows
|
|
DELETE FROM release_blocks WHERE info_hash = $1;
|