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>
310 lines
8.7 KiB
Go
310 lines
8.7 KiB
Go
// Code generated by sqlc. DO NOT EDIT.
|
|
// versions:
|
|
// sqlc v1.31.1
|
|
// source: acoustid.sql
|
|
|
|
package dbq
|
|
|
|
import (
|
|
"context"
|
|
|
|
"github.com/jackc/pgx/v5/pgtype"
|
|
)
|
|
|
|
const clearTrackAcoustIDMbid = `-- name: ClearTrackAcoustIDMbid :exec
|
|
UPDATE tracks
|
|
SET mbid = NULL, mbid_source = NULL
|
|
WHERE id = $1
|
|
AND mbid_source = 'acoustid'
|
|
`
|
|
|
|
// A re-lookup of changed bytes that no longer matches takes back the id an
|
|
// earlier lookup wrote. A tag id is never touched.
|
|
func (q *Queries) ClearTrackAcoustIDMbid(ctx context.Context, id pgtype.UUID) error {
|
|
_, err := q.db.Exec(ctx, clearTrackAcoustIDMbid, id)
|
|
return err
|
|
}
|
|
|
|
const deleteAcoustIDLookup = `-- name: DeleteAcoustIDLookup :exec
|
|
DELETE FROM track_acoustid_lookups WHERE track_id = $1
|
|
`
|
|
|
|
// The scan drops the lookup of bytes that are gone, so the worker checks the
|
|
// new ones.
|
|
func (q *Queries) DeleteAcoustIDLookup(ctx context.Context, trackID pgtype.UUID) error {
|
|
_, err := q.db.Exec(ctx, deleteAcoustIDLookup, trackID)
|
|
return err
|
|
}
|
|
|
|
const getAcoustIDSettings = `-- name: GetAcoustIDSettings :one
|
|
SELECT id, enabled, api_key, min_score, updated_at FROM acoustid_settings WHERE id = true
|
|
`
|
|
|
|
func (q *Queries) GetAcoustIDSettings(ctx context.Context) (AcoustidSetting, error) {
|
|
row := q.db.QueryRow(ctx, getAcoustIDSettings)
|
|
var i AcoustidSetting
|
|
err := row.Scan(
|
|
&i.ID,
|
|
&i.Enabled,
|
|
&i.ApiKey,
|
|
&i.MinScore,
|
|
&i.UpdatedAt,
|
|
)
|
|
return i, err
|
|
}
|
|
|
|
const getMbidCoverage = `-- name: GetMbidCoverage :one
|
|
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
|
|
`
|
|
|
|
type GetMbidCoverageRow struct {
|
|
Total int64
|
|
FromTag int64
|
|
FromAcoustid int64
|
|
Ambiguous int64
|
|
NoMatch int64
|
|
Failed int64
|
|
Pending int64
|
|
}
|
|
|
|
// 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.
|
|
func (q *Queries) GetMbidCoverage(ctx context.Context) (GetMbidCoverageRow, error) {
|
|
row := q.db.QueryRow(ctx, getMbidCoverage)
|
|
var i GetMbidCoverageRow
|
|
err := row.Scan(
|
|
&i.Total,
|
|
&i.FromTag,
|
|
&i.FromAcoustid,
|
|
&i.Ambiguous,
|
|
&i.NoMatch,
|
|
&i.Failed,
|
|
&i.Pending,
|
|
)
|
|
return i, err
|
|
}
|
|
|
|
const listTracksNeedingAcoustIDLookup = `-- name: ListTracksNeedingAcoustIDLookup :many
|
|
|
|
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 > $1::uuid
|
|
AND NOT EXISTS (SELECT 1 FROM track_acoustid_lookups l WHERE l.track_id = t.id)
|
|
ORDER BY t.id
|
|
LIMIT $2
|
|
`
|
|
|
|
type ListTracksNeedingAcoustIDLookupParams struct {
|
|
AfterID pgtype.UUID
|
|
BatchLimit int32
|
|
}
|
|
|
|
type ListTracksNeedingAcoustIDLookupRow struct {
|
|
ID pgtype.UUID
|
|
FilePath string
|
|
Title string
|
|
DurationMs int32
|
|
}
|
|
|
|
// AcoustID recording-id lookup. Migration 0069 says what each
|
|
// lookup state means.
|
|
// 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.
|
|
func (q *Queries) ListTracksNeedingAcoustIDLookup(ctx context.Context, arg ListTracksNeedingAcoustIDLookupParams) ([]ListTracksNeedingAcoustIDLookupRow, error) {
|
|
rows, err := q.db.Query(ctx, listTracksNeedingAcoustIDLookup, arg.AfterID, arg.BatchLimit)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
var items []ListTracksNeedingAcoustIDLookupRow
|
|
for rows.Next() {
|
|
var i ListTracksNeedingAcoustIDLookupRow
|
|
if err := rows.Scan(
|
|
&i.ID,
|
|
&i.FilePath,
|
|
&i.Title,
|
|
&i.DurationMs,
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, i)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
const listUnsettledAcoustIDLookups = `-- name: ListUnsettledAcoustIDLookups :many
|
|
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 $1
|
|
`
|
|
|
|
type ListUnsettledAcoustIDLookupsRow struct {
|
|
ID pgtype.UUID
|
|
Title string
|
|
FilePath string
|
|
ArtistName string
|
|
AlbumTitle string
|
|
State string
|
|
BestScore *float32
|
|
Candidates int32
|
|
Detail *string
|
|
LookedUpAt pgtype.Timestamptz
|
|
}
|
|
|
|
// The admin list of tracks a lookup could not settle (ambiguous or failed),
|
|
// so they are findable rather than silently empty.
|
|
func (q *Queries) ListUnsettledAcoustIDLookups(ctx context.Context, rowLimit int32) ([]ListUnsettledAcoustIDLookupsRow, error) {
|
|
rows, err := q.db.Query(ctx, listUnsettledAcoustIDLookups, rowLimit)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
var items []ListUnsettledAcoustIDLookupsRow
|
|
for rows.Next() {
|
|
var i ListUnsettledAcoustIDLookupsRow
|
|
if err := rows.Scan(
|
|
&i.ID,
|
|
&i.Title,
|
|
&i.FilePath,
|
|
&i.ArtistName,
|
|
&i.AlbumTitle,
|
|
&i.State,
|
|
&i.BestScore,
|
|
&i.Candidates,
|
|
&i.Detail,
|
|
&i.LookedUpAt,
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, i)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
const recordAcoustIDLookup = `-- name: RecordAcoustIDLookup :exec
|
|
INSERT INTO track_acoustid_lookups (track_id, state, best_score, candidates, recording_mbid, detail)
|
|
VALUES ($1, $2, $3, $4,
|
|
$5, $6)
|
|
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()
|
|
`
|
|
|
|
type RecordAcoustIDLookupParams struct {
|
|
TrackID pgtype.UUID
|
|
State string
|
|
BestScore *float32
|
|
Candidates int32
|
|
RecordingMbid *string
|
|
Detail *string
|
|
}
|
|
|
|
func (q *Queries) RecordAcoustIDLookup(ctx context.Context, arg RecordAcoustIDLookupParams) error {
|
|
_, err := q.db.Exec(ctx, recordAcoustIDLookup,
|
|
arg.TrackID,
|
|
arg.State,
|
|
arg.BestScore,
|
|
arg.Candidates,
|
|
arg.RecordingMbid,
|
|
arg.Detail,
|
|
)
|
|
return err
|
|
}
|
|
|
|
const setTrackMbidFromAcoustID = `-- name: SetTrackMbidFromAcoustID :execrows
|
|
UPDATE tracks
|
|
SET mbid = $1, mbid_source = 'acoustid'
|
|
WHERE id = $2
|
|
AND (mbid IS NULL OR mbid_source = 'acoustid')
|
|
`
|
|
|
|
type SetTrackMbidFromAcoustIDParams struct {
|
|
Mbid *string
|
|
ID pgtype.UUID
|
|
}
|
|
|
|
// 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.
|
|
func (q *Queries) SetTrackMbidFromAcoustID(ctx context.Context, arg SetTrackMbidFromAcoustIDParams) (int64, error) {
|
|
result, err := q.db.Exec(ctx, setTrackMbidFromAcoustID, arg.Mbid, arg.ID)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return result.RowsAffected(), nil
|
|
}
|
|
|
|
const updateAcoustIDSettings = `-- name: UpdateAcoustIDSettings :one
|
|
UPDATE acoustid_settings
|
|
SET enabled = $1,
|
|
min_score = $2,
|
|
api_key = CASE WHEN $3::boolean
|
|
THEN NULLIF($4::text, '')
|
|
ELSE api_key END,
|
|
updated_at = now()
|
|
WHERE id = true
|
|
RETURNING id, enabled, api_key, min_score, updated_at
|
|
`
|
|
|
|
type UpdateAcoustIDSettingsParams struct {
|
|
Enabled bool
|
|
MinScore float32
|
|
SetApiKey bool
|
|
ApiKey string
|
|
}
|
|
|
|
// 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.
|
|
func (q *Queries) UpdateAcoustIDSettings(ctx context.Context, arg UpdateAcoustIDSettingsParams) (AcoustidSetting, error) {
|
|
row := q.db.QueryRow(ctx, updateAcoustIDSettings,
|
|
arg.Enabled,
|
|
arg.MinScore,
|
|
arg.SetApiKey,
|
|
arg.ApiKey,
|
|
)
|
|
var i AcoustidSetting
|
|
err := row.Scan(
|
|
&i.ID,
|
|
&i.Enabled,
|
|
&i.ApiKey,
|
|
&i.MinScore,
|
|
&i.UpdatedAt,
|
|
)
|
|
return i, err
|
|
}
|