-- Re-acquisition of missing files (milestone #290, reworked for M502 #5520). -- The unit is the album: an album whose files went missing becomes a want for -- its release group, and the search schedule (#5521) looks for it. -- name: GetReacquisitionSettings :one SELECT * FROM reacquisition_settings WHERE id = true; -- name: UpdateReacquisitionSettings :one -- 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. UPDATE reacquisition_settings SET enabled = sqlc.arg(enabled), grace_hours = sqlc.arg(grace_hours), max_per_pass = sqlc.arg(max_per_pass) WHERE id = true RETURNING *; -- name: ListAlbumsDueReacquisition :many -- 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 (#2523), 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 238 albums matched to a group carried no release -- id, so their losses were never wanted back (#6167). Albums failing this -- are counted separately (CountAlbumsMissingWithoutMbid); -- * it is on the wanted list (#5567): 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 (#5245), so asking again would loop. 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 => sqlc.arg(grace_hours)::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 sqlc.arg(page_limit); -- name: CountAlbumsMissingWithoutMbid :one -- 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. 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); -- name: WantMissingAlbum :one -- 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. INSERT INTO wants (release_group_mbid, artist_mbid, artist_name, title, reason, album_id) VALUES (sqlc.arg(release_group_mbid), sqlc.arg(artist_mbid), sqlc.arg(artist_name), sqlc.arg(title), 'missing', sqlc.arg(album_id)) 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 *; -- name: GetWantsForAlbums :many -- What the wanted list says about each album on the missing-files page. 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(sqlc.arg(album_ids)::uuid[]) ORDER BY w.wanted_at DESC;