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>
273 lines
8.5 KiB
Go
273 lines
8.5 KiB
Go
// Code generated by sqlc. DO NOT EDIT.
|
|
// versions:
|
|
// sqlc v1.31.1
|
|
// source: reacquisition.sql
|
|
|
|
package dbq
|
|
|
|
import (
|
|
"context"
|
|
|
|
"github.com/jackc/pgx/v5/pgtype"
|
|
)
|
|
|
|
const countAlbumsMissingWithoutMbid = `-- name: CountAlbumsMissingWithoutMbid :one
|
|
SELECT COUNT(DISTINCT albums.id)::bigint
|
|
FROM albums
|
|
JOIN artists ON artists.id = albums.artist_id
|
|
JOIN tracks ON tracks.album_id = albums.id
|
|
WHERE tracks.missing_since IS NOT NULL
|
|
AND ((albums.mbid IS NULL AND albums.release_group_mbid IS NULL) OR artists.mbid IS NULL)
|
|
`
|
|
|
|
// Albums with missing files that can never be wanted automatically because
|
|
// nothing identifies them to MusicBrainz. Surfaced on the admin card so the
|
|
// gap is visible: silently doing nothing for these would read as broken.
|
|
func (q *Queries) CountAlbumsMissingWithoutMbid(ctx context.Context) (int64, error) {
|
|
row := q.db.QueryRow(ctx, countAlbumsMissingWithoutMbid)
|
|
var column_1 int64
|
|
err := row.Scan(&column_1)
|
|
return column_1, err
|
|
}
|
|
|
|
const getReacquisitionSettings = `-- name: GetReacquisitionSettings :one
|
|
|
|
SELECT id, enabled, grace_hours, max_per_pass FROM reacquisition_settings WHERE id = true
|
|
`
|
|
|
|
// Re-acquisition of missing files.
|
|
// The unit is the album: an album whose files went missing becomes a want for
|
|
// its release group, and the search schedule looks for it.
|
|
func (q *Queries) GetReacquisitionSettings(ctx context.Context) (ReacquisitionSetting, error) {
|
|
row := q.db.QueryRow(ctx, getReacquisitionSettings)
|
|
var i ReacquisitionSetting
|
|
err := row.Scan(
|
|
&i.ID,
|
|
&i.Enabled,
|
|
&i.GraceHours,
|
|
&i.MaxPerPass,
|
|
)
|
|
return i, err
|
|
}
|
|
|
|
const getWantsForAlbums = `-- name: GetWantsForAlbums :many
|
|
SELECT a.id AS album_id, w.id AS want_id, w.wanted_at, w.fulfilled_at
|
|
FROM albums a
|
|
JOIN wants w ON w.album_id = a.id OR w.release_group_mbid = a.release_group_mbid
|
|
WHERE a.id = ANY($1::uuid[])
|
|
ORDER BY w.wanted_at DESC
|
|
`
|
|
|
|
type GetWantsForAlbumsRow struct {
|
|
AlbumID pgtype.UUID
|
|
WantID pgtype.UUID
|
|
WantedAt pgtype.Timestamptz
|
|
FulfilledAt pgtype.Timestamptz
|
|
}
|
|
|
|
// What the wanted list says about each album on the missing-files page.
|
|
func (q *Queries) GetWantsForAlbums(ctx context.Context, albumIds []pgtype.UUID) ([]GetWantsForAlbumsRow, error) {
|
|
rows, err := q.db.Query(ctx, getWantsForAlbums, albumIds)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
var items []GetWantsForAlbumsRow
|
|
for rows.Next() {
|
|
var i GetWantsForAlbumsRow
|
|
if err := rows.Scan(
|
|
&i.AlbumID,
|
|
&i.WantID,
|
|
&i.WantedAt,
|
|
&i.FulfilledAt,
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, i)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
const listAlbumsDueReacquisition = `-- name: ListAlbumsDueReacquisition :many
|
|
SELECT albums.id AS album_id,
|
|
albums.title AS album_title,
|
|
albums.mbid AS album_mbid,
|
|
albums.release_group_mbid AS album_release_group_mbid,
|
|
artists.name AS artist_name,
|
|
artists.mbid AS artist_mbid,
|
|
COUNT(tracks.id)::bigint AS missing_track_count,
|
|
MAX(tracks.missing_since)::timestamptz AS last_missing_since
|
|
FROM albums
|
|
JOIN artists ON artists.id = albums.artist_id
|
|
JOIN tracks ON tracks.album_id = albums.id
|
|
WHERE tracks.missing_since IS NOT NULL
|
|
AND tracks.missing_since <= now() - make_interval(hours => $1::int)
|
|
AND (albums.mbid IS NOT NULL OR albums.release_group_mbid IS NOT NULL)
|
|
AND artists.mbid IS NOT NULL
|
|
GROUP BY albums.id, albums.title, albums.mbid, albums.release_group_mbid,
|
|
artists.name, artists.mbid
|
|
HAVING EXISTS (
|
|
SELECT 1 FROM wants w
|
|
WHERE w.album_id = albums.id OR w.release_group_mbid = albums.release_group_mbid)
|
|
AND NOT EXISTS (
|
|
SELECT 1 FROM wants w
|
|
WHERE (w.album_id = albums.id OR w.release_group_mbid = albums.release_group_mbid)
|
|
AND (w.fulfilled_at IS NULL OR w.fulfilled_at > MAX(tracks.missing_since)))
|
|
ORDER BY MIN(tracks.missing_since), albums.sort_title
|
|
LIMIT $2
|
|
`
|
|
|
|
type ListAlbumsDueReacquisitionParams struct {
|
|
GraceHours int32
|
|
PageLimit int32
|
|
}
|
|
|
|
type ListAlbumsDueReacquisitionRow struct {
|
|
AlbumID pgtype.UUID
|
|
AlbumTitle string
|
|
AlbumMbid *string
|
|
AlbumReleaseGroupMbid *string
|
|
ArtistName string
|
|
ArtistMbid *string
|
|
MissingTrackCount int64
|
|
LastMissingSince pgtype.Timestamptz
|
|
}
|
|
|
|
// The sweeper's selection. An album qualifies when:
|
|
//
|
|
// - it still has at least one track whose file has been missing longer than
|
|
// the grace window. Measured on missing_since, which reconcile never
|
|
// re-stamps, so it is a genuine "gone since" clock;
|
|
// - MusicBrainz can name it: the artist has an MBID, and the album a
|
|
// release MBID or a release group. The release MBID is only ever used to
|
|
// find the group, and an album matched to a group may carry no release
|
|
// id. Albums failing this are counted separately
|
|
// (CountAlbumsMissingWithoutMbid);
|
|
// - it is on the wanted list: an album taken off the list, or
|
|
// dropped into the library by hand, is not fetched back;
|
|
// - no want already covers it: none open, and none held since its files
|
|
// went missing. A want held after the loss means the library has a full
|
|
// copy despite the missing rows, so asking again would loop.
|
|
func (q *Queries) ListAlbumsDueReacquisition(ctx context.Context, arg ListAlbumsDueReacquisitionParams) ([]ListAlbumsDueReacquisitionRow, error) {
|
|
rows, err := q.db.Query(ctx, listAlbumsDueReacquisition, arg.GraceHours, arg.PageLimit)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
var items []ListAlbumsDueReacquisitionRow
|
|
for rows.Next() {
|
|
var i ListAlbumsDueReacquisitionRow
|
|
if err := rows.Scan(
|
|
&i.AlbumID,
|
|
&i.AlbumTitle,
|
|
&i.AlbumMbid,
|
|
&i.AlbumReleaseGroupMbid,
|
|
&i.ArtistName,
|
|
&i.ArtistMbid,
|
|
&i.MissingTrackCount,
|
|
&i.LastMissingSince,
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, i)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
const updateReacquisitionSettings = `-- name: UpdateReacquisitionSettings :one
|
|
UPDATE reacquisition_settings
|
|
SET enabled = $1,
|
|
grace_hours = $2,
|
|
max_per_pass = $3
|
|
WHERE id = true
|
|
RETURNING id, enabled, grace_hours, max_per_pass
|
|
`
|
|
|
|
type UpdateReacquisitionSettingsParams struct {
|
|
Enabled bool
|
|
GraceHours int32
|
|
MaxPerPass int32
|
|
}
|
|
|
|
// Whole-row write from the admin card; the CHECKs in migration 0056 are the
|
|
// validation, so a bad value fails loudly rather than being clamped silently.
|
|
func (q *Queries) UpdateReacquisitionSettings(ctx context.Context, arg UpdateReacquisitionSettingsParams) (ReacquisitionSetting, error) {
|
|
row := q.db.QueryRow(ctx, updateReacquisitionSettings, arg.Enabled, arg.GraceHours, arg.MaxPerPass)
|
|
var i ReacquisitionSetting
|
|
err := row.Scan(
|
|
&i.ID,
|
|
&i.Enabled,
|
|
&i.GraceHours,
|
|
&i.MaxPerPass,
|
|
)
|
|
return i, err
|
|
}
|
|
|
|
const wantMissingAlbum = `-- name: WantMissingAlbum :one
|
|
INSERT INTO wants (release_group_mbid, artist_mbid, artist_name, title, reason, album_id)
|
|
VALUES ($1, $2, $3,
|
|
$4, 'missing', $5)
|
|
ON CONFLICT (release_group_mbid) DO UPDATE
|
|
SET reason = 'missing',
|
|
album_id = EXCLUDED.album_id,
|
|
wanted_at = now(),
|
|
fulfilled_at = NULL,
|
|
updated_at = now()
|
|
WHERE wants.fulfilled_at IS NOT NULL
|
|
RETURNING id, release_group_mbid, artist_mbid, artist_name, title, primary_type, first_release_date, reason, album_id, created_by, wanted_at, fulfilled_at, created_at, updated_at, searched_at, search_outcome, search_considered, best_release, best_verdict, search_error, upgrade, held_tier, held_quality, upgrade_checked_at
|
|
`
|
|
|
|
type WantMissingAlbumParams struct {
|
|
ReleaseGroupMbid string
|
|
ArtistMbid string
|
|
ArtistName string
|
|
Title string
|
|
AlbumID pgtype.UUID
|
|
}
|
|
|
|
// Records an album with missing files as wanted. A held want for the same
|
|
// group is reopened; an open one is left as it is, and no row comes back.
|
|
func (q *Queries) WantMissingAlbum(ctx context.Context, arg WantMissingAlbumParams) (Want, error) {
|
|
row := q.db.QueryRow(ctx, wantMissingAlbum,
|
|
arg.ReleaseGroupMbid,
|
|
arg.ArtistMbid,
|
|
arg.ArtistName,
|
|
arg.Title,
|
|
arg.AlbumID,
|
|
)
|
|
var i Want
|
|
err := row.Scan(
|
|
&i.ID,
|
|
&i.ReleaseGroupMbid,
|
|
&i.ArtistMbid,
|
|
&i.ArtistName,
|
|
&i.Title,
|
|
&i.PrimaryType,
|
|
&i.FirstReleaseDate,
|
|
&i.Reason,
|
|
&i.AlbumID,
|
|
&i.CreatedBy,
|
|
&i.WantedAt,
|
|
&i.FulfilledAt,
|
|
&i.CreatedAt,
|
|
&i.UpdatedAt,
|
|
&i.SearchedAt,
|
|
&i.SearchOutcome,
|
|
&i.SearchConsidered,
|
|
&i.BestRelease,
|
|
&i.BestVerdict,
|
|
&i.SearchError,
|
|
&i.Upgrade,
|
|
&i.HeldTier,
|
|
&i.HeldQuality,
|
|
&i.UpgradeCheckedAt,
|
|
)
|
|
return i, err
|
|
}
|