// 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 (M401). 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 (D4): 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 }