Rewrites the internal/db/queries comments to rule 32 and regenerates dbq, whose doc comments and embedded SQL carry them. The bugs behind the tiered mixes, the md5 order seed, the score bands and the round-robin arms are restated as the constraint each one enforces; counts from one library and stale cross-references are gone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
100 lines
4.3 KiB
SQL
100 lines
4.3 KiB
SQL
-- AcoustID recording-id lookup. Migration 0069 says what each
|
|
-- lookup state means.
|
|
|
|
-- name: ListTracksNeedingAcoustIDLookup :many
|
|
-- The lookup worker's queue: present tracks with no tag MBID and no lookup
|
|
-- since their file last changed. A looked-up id is included once its row is
|
|
-- gone (the file changed), so the new bytes are checked rather than trusted.
|
|
-- Keyset-paged on id, as the loudness queue is, so a pass ends even when
|
|
-- every attempt is inconclusive and writes nothing.
|
|
SELECT t.id, t.file_path, t.title, t.duration_ms
|
|
FROM tracks t
|
|
WHERE (t.mbid IS NULL OR t.mbid_source = 'acoustid')
|
|
AND t.missing_since IS NULL
|
|
AND t.id > sqlc.arg(after_id)::uuid
|
|
AND NOT EXISTS (SELECT 1 FROM track_acoustid_lookups l WHERE l.track_id = t.id)
|
|
ORDER BY t.id
|
|
LIMIT sqlc.arg(batch_limit);
|
|
|
|
-- name: RecordAcoustIDLookup :exec
|
|
INSERT INTO track_acoustid_lookups (track_id, state, best_score, candidates, recording_mbid, detail)
|
|
VALUES (sqlc.arg(track_id), sqlc.arg(state), sqlc.narg(best_score), sqlc.arg(candidates),
|
|
sqlc.narg(recording_mbid), sqlc.narg(detail))
|
|
ON CONFLICT (track_id) DO UPDATE SET
|
|
state = EXCLUDED.state,
|
|
best_score = EXCLUDED.best_score,
|
|
candidates = EXCLUDED.candidates,
|
|
recording_mbid = EXCLUDED.recording_mbid,
|
|
detail = EXCLUDED.detail,
|
|
looked_up_at = now();
|
|
|
|
-- name: SetTrackMbidFromAcoustID :execrows
|
|
-- Never over a tag id: the guard is here, not only in the worker, so a
|
|
-- tag the scan read between the worker's list and this write still wins.
|
|
-- updated_at is left alone: it gates the scan's mtime skip, and moving it
|
|
-- past a file's mtime would hide a change made just before the lookup.
|
|
UPDATE tracks
|
|
SET mbid = sqlc.arg(mbid), mbid_source = 'acoustid'
|
|
WHERE id = sqlc.arg(id)
|
|
AND (mbid IS NULL OR mbid_source = 'acoustid');
|
|
|
|
-- name: ClearTrackAcoustIDMbid :exec
|
|
-- A re-lookup of changed bytes that no longer matches takes back the id an
|
|
-- earlier lookup wrote. A tag id is never touched.
|
|
UPDATE tracks
|
|
SET mbid = NULL, mbid_source = NULL
|
|
WHERE id = sqlc.arg(id)
|
|
AND mbid_source = 'acoustid';
|
|
|
|
-- name: DeleteAcoustIDLookup :exec
|
|
-- The scan drops the lookup of bytes that are gone, so the worker checks the
|
|
-- new ones.
|
|
DELETE FROM track_acoustid_lookups WHERE track_id = $1;
|
|
|
|
-- name: GetMbidCoverage :one
|
|
-- The admin gauge: present tracks by where their MBID came from, and the
|
|
-- lookup states of those a lookup has seen. pending is what the worker's
|
|
-- queue holds.
|
|
SELECT count(*)::bigint AS total,
|
|
count(*) FILTER (WHERE t.mbid_source = 'tag')::bigint AS from_tag,
|
|
count(*) FILTER (WHERE t.mbid_source = 'acoustid')::bigint AS from_acoustid,
|
|
count(*) FILTER (WHERE l.state = 'ambiguous')::bigint AS ambiguous,
|
|
count(*) FILTER (WHERE l.state = 'no_match')::bigint AS no_match,
|
|
count(*) FILTER (WHERE l.state = 'failed')::bigint AS failed,
|
|
count(*) FILTER (
|
|
WHERE l.track_id IS NULL AND (t.mbid IS NULL OR t.mbid_source = 'acoustid')
|
|
)::bigint AS pending
|
|
FROM tracks t
|
|
LEFT JOIN track_acoustid_lookups l ON l.track_id = t.id
|
|
WHERE t.missing_since IS NULL;
|
|
|
|
-- name: ListUnsettledAcoustIDLookups :many
|
|
-- The admin list of tracks a lookup could not settle (ambiguous or failed),
|
|
-- so they are findable rather than silently empty.
|
|
SELECT t.id, t.title, t.file_path, ar.name AS artist_name, al.title AS album_title,
|
|
l.state, l.best_score, l.candidates, l.detail, l.looked_up_at
|
|
FROM track_acoustid_lookups l
|
|
JOIN tracks t ON t.id = l.track_id
|
|
JOIN artists ar ON ar.id = t.artist_id
|
|
JOIN albums al ON al.id = t.album_id
|
|
WHERE l.state IN ('ambiguous', 'failed')
|
|
AND t.missing_since IS NULL
|
|
ORDER BY ar.name, al.title, t.disc_number NULLS FIRST, t.track_number NULLS FIRST, t.title
|
|
LIMIT sqlc.arg(row_limit);
|
|
|
|
-- name: GetAcoustIDSettings :one
|
|
SELECT * FROM acoustid_settings WHERE id = true;
|
|
|
|
-- name: UpdateAcoustIDSettings :one
|
|
-- The key is write-only from the admin card: set_api_key false keeps the
|
|
-- stored one, and an empty api_key with set_api_key true clears it.
|
|
UPDATE acoustid_settings
|
|
SET enabled = sqlc.arg(enabled),
|
|
min_score = sqlc.arg(min_score),
|
|
api_key = CASE WHEN sqlc.arg(set_api_key)::boolean
|
|
THEN NULLIF(sqlc.arg(api_key)::text, '')
|
|
ELSE api_key END,
|
|
updated_at = now()
|
|
WHERE id = true
|
|
RETURNING *;
|