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>
166 lines
7.2 KiB
SQL
166 lines
7.2 KiB
SQL
-- Import with verification (M502 #5522).
|
|
|
|
-- name: ListLiveGrabs :many
|
|
-- Every download still running or waiting on its import, oldest first.
|
|
SELECT g.*, w.release_group_mbid, w.artist_mbid, w.artist_name, w.title AS want_title,
|
|
w.reason AS want_reason, w.album_id AS want_album_id
|
|
FROM grabs g
|
|
LEFT JOIN wants w ON w.id = g.want_id
|
|
WHERE g.ended_at IS NULL
|
|
ORDER BY g.grabbed_at, g.id;
|
|
|
|
-- name: GetGrab :one
|
|
SELECT * FROM grabs WHERE id = $1;
|
|
|
|
-- name: SetGrabProgress :exec
|
|
-- What the download client says of a download not yet finished, or finished
|
|
-- and waiting for what the import needs. Leaves a claimed, decided or failed
|
|
-- import alone.
|
|
UPDATE grabs
|
|
SET progress = sqlc.arg(progress),
|
|
import_state = sqlc.arg(import_state),
|
|
import_reason = sqlc.arg(import_reason),
|
|
import_updated_at = now()
|
|
WHERE id = sqlc.arg(id) AND ended_at IS NULL
|
|
AND import_state IN ('', 'downloading', 'waiting');
|
|
|
|
-- name: ClaimGrabImport :one
|
|
-- Takes a finished download for import. The update is the lock: a second
|
|
-- pass finds nothing to claim.
|
|
UPDATE grabs
|
|
SET import_state = 'importing', import_attempts = import_attempts + 1,
|
|
import_updated_at = now(), progress = 1
|
|
WHERE id = $1 AND ended_at IS NULL AND import_state IN ('', 'downloading', 'waiting')
|
|
RETURNING *;
|
|
|
|
-- name: ReclaimStaleImports :execrows
|
|
-- An import cut short (a restart mid-copy) is tried again.
|
|
UPDATE grabs
|
|
SET import_state = 'waiting',
|
|
import_reason = 'The last import was cut short, so it is being tried again.'
|
|
WHERE ended_at IS NULL AND import_state = 'importing'
|
|
AND import_updated_at < now() - make_interval(mins => sqlc.arg(minutes)::int);
|
|
|
|
-- name: RecordImport :one
|
|
-- The import's outcome. An end reason ends the grab: imported, refused or
|
|
-- held. Without one the grab stays live: review and failed wait for a person.
|
|
UPDATE grabs
|
|
SET import_state = sqlc.arg(import_state),
|
|
import_reason = sqlc.arg(import_reason),
|
|
import_detail = sqlc.arg(import_detail),
|
|
import_updated_at = now(),
|
|
import_forced = false,
|
|
imported_at = CASE WHEN sqlc.arg(import_state)::text = 'imported' THEN now() ELSE imported_at END,
|
|
-- The files were copied, so an imported download goes on sharing
|
|
-- until the lifecycle removes it (#5523).
|
|
seeding = sqlc.arg(import_state)::text = 'imported',
|
|
imported_files = sqlc.arg(imported_files),
|
|
destination = sqlc.arg(destination),
|
|
ended_at = CASE WHEN sqlc.arg(end_reason)::text <> '' THEN now() ELSE ended_at END,
|
|
end_reason = sqlc.arg(end_reason),
|
|
end_detail = CASE WHEN sqlc.arg(end_reason)::text <> '' THEN sqlc.arg(import_reason)::text ELSE end_detail END
|
|
WHERE id = sqlc.arg(id)
|
|
RETURNING *;
|
|
|
|
-- name: ForceGrabImport :one
|
|
-- "Import anyway": the next pass imports the download whatever the check said.
|
|
UPDATE grabs
|
|
SET import_state = '', import_forced = true, import_reason = '', import_updated_at = now()
|
|
WHERE id = $1 AND ended_at IS NULL AND import_state IN ('review', 'failed')
|
|
RETURNING *;
|
|
|
|
-- name: RetryGrabImport :one
|
|
UPDATE grabs
|
|
SET import_state = '', import_reason = '', import_updated_at = now()
|
|
WHERE id = $1 AND ended_at IS NULL AND import_state = 'failed'
|
|
RETURNING *;
|
|
|
|
-- name: RehomeGrab :one
|
|
-- A download that turned out to be another wanted album is that want's grab,
|
|
-- when the want has none running.
|
|
UPDATE grabs g
|
|
SET want_id = sqlc.arg(want_id)
|
|
WHERE g.id = sqlc.arg(id) AND g.ended_at IS NULL
|
|
AND NOT EXISTS (SELECT 1 FROM grabs o WHERE o.want_id = sqlc.arg(want_id) AND o.ended_at IS NULL)
|
|
RETURNING *;
|
|
|
|
-- name: OpenWantsForGroups :many
|
|
-- Open wants for these release groups with no download running.
|
|
SELECT w.* FROM wants w
|
|
WHERE w.fulfilled_at IS NULL AND w.release_group_mbid = ANY(sqlc.arg(groups)::text[])
|
|
AND NOT EXISTS (SELECT 1 FROM grabs g WHERE g.want_id = w.id AND g.ended_at IS NULL);
|
|
|
|
-- name: MarkWantSearched :exec
|
|
-- Imported: nothing to search for until the wanted pass settles the want.
|
|
UPDATE wants SET searched_at = now() WHERE id = $1;
|
|
|
|
-- name: SearchWantAgain :exec
|
|
-- Refused: search again on the next pass.
|
|
UPDATE wants SET searched_at = NULL, search_outcome = '' WHERE id = $1;
|
|
|
|
-- name: HeldRecordings :many
|
|
-- Which of these recordings the library holds a file for.
|
|
SELECT DISTINCT mbid::text AS mbid FROM tracks
|
|
WHERE mbid = ANY(sqlc.arg(recordings)::text[]) AND missing_since IS NULL;
|
|
|
|
-- name: HeldPositions :many
|
|
-- The disc and track positions the library holds for an album of this group.
|
|
SELECT DISTINCT COALESCE(t.disc_number, 1)::int AS disc, t.track_number::int AS track
|
|
FROM tracks t
|
|
JOIN albums a ON a.id = t.album_id
|
|
WHERE a.release_group_mbid = sqlc.arg(release_group_mbid)
|
|
AND t.missing_since IS NULL AND t.track_number IS NOT NULL;
|
|
|
|
-- name: ListGrabs :many
|
|
-- The Wanted view's Activity tab: downloads waiting on a person first, then
|
|
-- the rest still running, then the ended ones newest first. Each carries
|
|
-- what is wrong with it now, if anything (#5523).
|
|
SELECT g.id, g.want_id, g.name, g.indexer, g.client, g.quality, g.origin, g.forced,
|
|
g.grabbed_at, g.ended_at, g.end_reason, g.end_detail, g.progress, g.import_state,
|
|
g.import_reason, g.import_detail, g.import_updated_at, g.imported_at, g.imported_files,
|
|
g.destination, g.seeding, g.ratio, g.seed_released_at,
|
|
w.title AS want_title, w.artist_name AS want_artist,
|
|
di.trigger AS condition, di.detail AS condition_detail, di.remediations AS condition_retries,
|
|
di.first_seen AS condition_since
|
|
FROM grabs g
|
|
LEFT JOIN wants w ON w.id = g.want_id
|
|
LEFT JOIN download_interventions di
|
|
ON di.id = (SELECT x.id FROM download_interventions x
|
|
WHERE x.grab_id = g.id AND x.outcome = 'open'
|
|
ORDER BY x.last_seen DESC LIMIT 1)
|
|
ORDER BY (g.ended_at IS NULL AND g.import_state IN ('review', 'failed')) DESC,
|
|
g.ended_at IS NULL DESC, g.grabbed_at DESC, g.id
|
|
LIMIT sqlc.arg(max_rows) OFFSET sqlc.arg(skip);
|
|
|
|
-- name: ListReleaseBlocksPage :many
|
|
SELECT b.info_hash, b.name, b.reason, b.detail, b.blocked_at, b.expires_at, w.title AS want_title,
|
|
w.artist_name AS want_artist
|
|
FROM release_blocks b
|
|
LEFT JOIN wants w ON w.id = b.want_id
|
|
WHERE b.expires_at IS NULL OR b.expires_at > now()
|
|
ORDER BY b.blocked_at DESC, b.info_hash
|
|
LIMIT sqlc.arg(max_rows) OFFSET sqlc.arg(skip);
|
|
|
|
-- name: ListUpgradeTracks :many
|
|
-- The tracks an upgrade may replace (#5524): the present tracks of the album
|
|
-- the want holds.
|
|
SELECT id, file_path, mbid, COALESCE(disc_number, 1)::int AS disc, track_number,
|
|
file_format, file_size, duration_ms, source_verdict
|
|
FROM tracks
|
|
WHERE album_id = $1 AND missing_since IS NULL;
|
|
|
|
-- name: FinishUpgrade :exec
|
|
-- An upgrade was imported: the want holds the album the new tracks are in,
|
|
-- and is judged again by the next upgrade check.
|
|
UPDATE wants
|
|
SET album_id = COALESCE(sqlc.narg(album_id), album_id),
|
|
upgrade = '',
|
|
upgrade_checked_at = NULL,
|
|
updated_at = now()
|
|
WHERE id = sqlc.arg(id);
|
|
|
|
-- name: ClearTrackSourceVerdict :exec
|
|
-- A track whose file was replaced in place is no longer the rip it was.
|
|
UPDATE tracks SET source_verdict = NULL, source_verdict_at = NULL
|
|
WHERE id = $1 AND source_verdict = 'suspect';
|