feat: Collection › Unmatched lists the library albums no MusicBrainz album matched, each matched by hand from a search or a link, or left off (M502 #5579)
release / govulncheck (push) Successful in 16s
release / web (push) Successful in 1m17s
release / go (push) Successful in 1m34s
release / integration (push) Failing after 5m34s
release / android (push) Successful in 5m36s
release / Build signed APK (releases and dev) (push) Successful in 5m44s
release / Attach APK to the Release (tag releases only) (push) Skipped
release / Build + push container image (push) Skipped
release / Verify release artifacts (tag releases only) (push) Skipped
release / govulncheck (push) Successful in 16s
release / web (push) Successful in 1m17s
release / go (push) Successful in 1m34s
release / integration (push) Failing after 5m34s
release / android (push) Successful in 5m36s
release / Build signed APK (releases and dev) (push) Successful in 5m44s
release / Attach APK to the Release (tag releases only) (push) Skipped
release / Build + push container image (push) Skipped
release / Verify release artifacts (tag releases only) (push) Skipped
Like Lidarr's Unmapped Files. A local band's album, which MusicBrainz will never have, is left off: never matched again and not counted. A re-tagged album is matched again on the next listing. The Collection's verdict now says when library albums need matching. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+124
-16
@@ -20,14 +20,29 @@ func (q *Queries) ClearDefaultReleaseTypeProfile(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
const countAlbumsToRematch = `-- name: CountAlbumsToRematch :one
|
||||
SELECT COUNT(*) FROM library_album_matches
|
||||
WHERE outcome = 'unmatched' AND matcher < $1::int
|
||||
const countAlbumsToMatch = `-- name: CountAlbumsToMatch :one
|
||||
SELECT COUNT(*)
|
||||
FROM albums a
|
||||
WHERE (a.created_at <= $1::timestamptz
|
||||
OR EXISTS (SELECT 1 FROM tracks t WHERE t.album_id = a.id AND t.added_at <= $1::timestamptz))
|
||||
AND EXISTS (SELECT 1 FROM tracks t WHERE t.album_id = a.id)
|
||||
AND NOT EXISTS (SELECT 1 FROM library_album_matches m
|
||||
WHERE m.album_id = a.id
|
||||
AND (m.outcome IN ('listed', 'dismissed')
|
||||
OR (m.matcher >= $2::int
|
||||
AND NOT EXISTS (SELECT 1 FROM tracks t
|
||||
WHERE t.album_id = a.id AND t.updated_at > m.decided_at))))
|
||||
`
|
||||
|
||||
// Refusals by an older matcher, which a finished listing goes back to.
|
||||
func (q *Queries) CountAlbumsToRematch(ctx context.Context, matcher int32) (int64, error) {
|
||||
row := q.db.QueryRow(ctx, countAlbumsToRematch, matcher)
|
||||
type CountAlbumsToMatchParams struct {
|
||||
Cutoff pgtype.Timestamptz
|
||||
Matcher int32
|
||||
}
|
||||
|
||||
// Whether a finished listing has albums to go back to: those
|
||||
// ListAlbumsToMatch would hand it.
|
||||
func (q *Queries) CountAlbumsToMatch(ctx context.Context, arg CountAlbumsToMatchParams) (int64, error) {
|
||||
row := q.db.QueryRow(ctx, countAlbumsToMatch, arg.Cutoff, arg.Matcher)
|
||||
var count int64
|
||||
err := row.Scan(&count)
|
||||
return count, err
|
||||
@@ -38,6 +53,10 @@ SELECT COUNT(*) FILTER (WHERE fulfilled_at IS NULL AND reason <> 'missing')::big
|
||||
COUNT(*) FILTER (WHERE fulfilled_at IS NULL AND reason = 'missing')::bigint AS missing,
|
||||
COUNT(*) FILTER (WHERE fulfilled_at IS NOT NULL)::bigint AS held,
|
||||
COUNT(DISTINCT artist_mbid)::bigint AS artists,
|
||||
-- Library albums waiting to be matched by hand (#5579).
|
||||
(SELECT COUNT(*) FROM library_album_matches m
|
||||
WHERE m.outcome = 'unmatched'
|
||||
AND EXISTS (SELECT 1 FROM tracks t WHERE t.album_id = m.album_id))::bigint AS unmatched,
|
||||
(SELECT COUNT(*) FROM follows)::bigint AS following,
|
||||
-- The Wanted view's other tabs (#5522): downloads running, those
|
||||
-- waiting on a person, and releases set aside.
|
||||
@@ -54,6 +73,7 @@ type CountWantsRow struct {
|
||||
Missing int64
|
||||
Held int64
|
||||
Artists int64
|
||||
Unmatched int64
|
||||
Following int64
|
||||
Activity int64
|
||||
Attention int64
|
||||
@@ -68,6 +88,7 @@ func (q *Queries) CountWants(ctx context.Context) (CountWantsRow, error) {
|
||||
&i.Missing,
|
||||
&i.Held,
|
||||
&i.Artists,
|
||||
&i.Unmatched,
|
||||
&i.Following,
|
||||
&i.Activity,
|
||||
&i.Attention,
|
||||
@@ -143,7 +164,9 @@ func (q *Queries) DeleteWant(ctx context.Context, id pgtype.UUID) (int64, error)
|
||||
const finishLibraryListing = `-- name: FinishLibraryListing :one
|
||||
UPDATE acquisition_settings
|
||||
SET library_listed_at = COALESCE(library_listed_at, now()),
|
||||
library_unlisted = (SELECT COUNT(*) FROM library_album_matches WHERE outcome = 'unmatched')
|
||||
library_unlisted = (SELECT COUNT(*) FROM library_album_matches m
|
||||
WHERE m.outcome = 'unmatched'
|
||||
AND EXISTS (SELECT 1 FROM tracks t WHERE t.album_id = m.album_id))
|
||||
WHERE id = true
|
||||
RETURNING library_listed_at, library_unlisted
|
||||
`
|
||||
@@ -180,6 +203,38 @@ func (q *Queries) FulfillWant(ctx context.Context, arg FulfillWantParams) error
|
||||
return err
|
||||
}
|
||||
|
||||
const getAlbumToMatch = `-- name: GetAlbumToMatch :one
|
||||
SELECT a.id, a.title, a.mbid, a.release_group_mbid, ar.name AS artist_name, ar.mbid AS artist_mbid
|
||||
FROM albums a
|
||||
JOIN artists ar ON ar.id = a.artist_id
|
||||
JOIN library_album_matches m ON m.album_id = a.id AND m.outcome IN ('unmatched', 'dismissed')
|
||||
WHERE a.id = $1
|
||||
`
|
||||
|
||||
type GetAlbumToMatchRow struct {
|
||||
ID pgtype.UUID
|
||||
Title string
|
||||
Mbid *string
|
||||
ReleaseGroupMbid *string
|
||||
ArtistName string
|
||||
ArtistMbid *string
|
||||
}
|
||||
|
||||
// One album a person is matching by hand, as the matcher reads albums.
|
||||
func (q *Queries) GetAlbumToMatch(ctx context.Context, id pgtype.UUID) (GetAlbumToMatchRow, error) {
|
||||
row := q.db.QueryRow(ctx, getAlbumToMatch, id)
|
||||
var i GetAlbumToMatchRow
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.Title,
|
||||
&i.Mbid,
|
||||
&i.ReleaseGroupMbid,
|
||||
&i.ArtistName,
|
||||
&i.ArtistMbid,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getDefaultReleaseTypeProfile = `-- name: GetDefaultReleaseTypeProfile :one
|
||||
SELECT id, name, primary_types, secondary_types, is_default, created_at, updated_at FROM release_type_profiles WHERE is_default
|
||||
`
|
||||
@@ -438,10 +493,15 @@ const listAlbumsToMatch = `-- name: ListAlbumsToMatch :many
|
||||
SELECT a.id, a.title, a.mbid, a.release_group_mbid, ar.name AS artist_name, ar.mbid AS artist_mbid
|
||||
FROM albums a
|
||||
JOIN artists ar ON ar.id = a.artist_id
|
||||
WHERE a.created_at <= $1::timestamptz
|
||||
WHERE (a.created_at <= $1::timestamptz
|
||||
OR EXISTS (SELECT 1 FROM tracks t WHERE t.album_id = a.id AND t.added_at <= $1::timestamptz))
|
||||
AND EXISTS (SELECT 1 FROM tracks t WHERE t.album_id = a.id)
|
||||
AND NOT EXISTS (SELECT 1 FROM library_album_matches m
|
||||
WHERE m.album_id = a.id AND (m.outcome = 'listed' OR m.matcher >= $2::int))
|
||||
WHERE m.album_id = a.id
|
||||
AND (m.outcome IN ('listed', 'dismissed')
|
||||
OR (m.matcher >= $2::int
|
||||
AND NOT EXISTS (SELECT 1 FROM tracks t
|
||||
WHERE t.album_id = a.id AND t.updated_at > m.decided_at))))
|
||||
ORDER BY a.created_at, a.id
|
||||
LIMIT $3
|
||||
`
|
||||
@@ -461,9 +521,12 @@ type ListAlbumsToMatchRow struct {
|
||||
ArtistMbid *string
|
||||
}
|
||||
|
||||
// Library albums from before the cutoff that the listing has not decided,
|
||||
// or that an older matcher refused: each is matched to MusicBrainz (#5569).
|
||||
// An album row with no tracks is not an album in the library.
|
||||
// Library albums the listing has not decided, or that an older matcher
|
||||
// refused, or whose tracks changed since they were refused: each is matched
|
||||
// to MusicBrainz (#5569). A library album is one from before the cutoff, or
|
||||
// one whose tracks were: a re-tag files a track under a new album row but
|
||||
// keeps the track (#5579). An album row with no tracks is not an album in
|
||||
// the library; one a person left off stays off.
|
||||
func (q *Queries) ListAlbumsToMatch(ctx context.Context, arg ListAlbumsToMatchParams) ([]ListAlbumsToMatchRow, error) {
|
||||
rows, err := q.db.Query(ctx, listAlbumsToMatch, arg.Cutoff, arg.Matcher, arg.MaxRows)
|
||||
if err != nil {
|
||||
@@ -813,12 +876,14 @@ func (q *Queries) ListReleaseTypeProfiles(ctx context.Context) ([]ListReleaseTyp
|
||||
}
|
||||
|
||||
const listUnmatchedLibraryAlbums = `-- name: ListUnmatchedLibraryAlbums :many
|
||||
SELECT m.album_id, a.title, ar.name AS artist_name, m.detail
|
||||
SELECT m.album_id, a.title, ar.name AS artist_name, m.detail, m.outcome, m.decided_at,
|
||||
(SELECT COUNT(*) FROM tracks t WHERE t.album_id = a.id AND t.missing_since IS NULL)::int AS tracks
|
||||
FROM library_album_matches m
|
||||
JOIN albums a ON a.id = m.album_id
|
||||
JOIN artists ar ON ar.id = a.artist_id
|
||||
WHERE m.outcome = 'unmatched'
|
||||
ORDER BY ar.name, a.title
|
||||
WHERE m.outcome IN ('unmatched', 'dismissed')
|
||||
AND EXISTS (SELECT 1 FROM tracks t WHERE t.album_id = m.album_id)
|
||||
ORDER BY m.outcome DESC, lower(ar.name), lower(a.title)
|
||||
LIMIT $1
|
||||
`
|
||||
|
||||
@@ -827,9 +892,14 @@ type ListUnmatchedLibraryAlbumsRow struct {
|
||||
Title string
|
||||
ArtistName string
|
||||
Detail string
|
||||
Outcome string
|
||||
DecidedAt pgtype.Timestamptz
|
||||
Tracks int32
|
||||
}
|
||||
|
||||
// The albums the listing could not match, for the Held tab.
|
||||
// The Collection's Unmatched section (#5579): the albums the listing could
|
||||
// not match, and below them the ones a person left off. An album whose
|
||||
// tracks have all moved to another row is gone, not unmatched.
|
||||
func (q *Queries) ListUnmatchedLibraryAlbums(ctx context.Context, maxRows int32) ([]ListUnmatchedLibraryAlbumsRow, error) {
|
||||
rows, err := q.db.Query(ctx, listUnmatchedLibraryAlbums, maxRows)
|
||||
if err != nil {
|
||||
@@ -844,6 +914,9 @@ func (q *Queries) ListUnmatchedLibraryAlbums(ctx context.Context, maxRows int32)
|
||||
&i.Title,
|
||||
&i.ArtistName,
|
||||
&i.Detail,
|
||||
&i.Outcome,
|
||||
&i.DecidedAt,
|
||||
&i.Tracks,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -1179,6 +1252,21 @@ func (q *Queries) RecordListedAlbums(ctx context.Context, cutoff pgtype.Timestam
|
||||
return err
|
||||
}
|
||||
|
||||
const recountLibraryUnlisted = `-- name: RecountLibraryUnlisted :exec
|
||||
UPDATE acquisition_settings
|
||||
SET library_unlisted = (SELECT COUNT(*) FROM library_album_matches m
|
||||
WHERE m.outcome = 'unmatched'
|
||||
AND EXISTS (SELECT 1 FROM tracks t WHERE t.album_id = m.album_id))
|
||||
WHERE id = true
|
||||
`
|
||||
|
||||
// After a person matches or leaves off an album, the count the catalogue
|
||||
// shows.
|
||||
func (q *Queries) RecountLibraryUnlisted(ctx context.Context) error {
|
||||
_, err := q.db.Exec(ctx, recountLibraryUnlisted)
|
||||
return err
|
||||
}
|
||||
|
||||
const repointWant = `-- name: RepointWant :exec
|
||||
UPDATE wants SET release_group_mbid = $2, updated_at = now() WHERE id = $1
|
||||
`
|
||||
@@ -1194,6 +1282,26 @@ func (q *Queries) RepointWant(ctx context.Context, arg RepointWantParams) error
|
||||
return err
|
||||
}
|
||||
|
||||
const setAlbumMatchOutcome = `-- name: SetAlbumMatchOutcome :execrows
|
||||
UPDATE library_album_matches SET outcome = $1, decided_at = now()
|
||||
WHERE album_id = $2 AND outcome = $3
|
||||
`
|
||||
|
||||
type SetAlbumMatchOutcomeParams struct {
|
||||
Outcome string
|
||||
AlbumID pgtype.UUID
|
||||
Was string
|
||||
}
|
||||
|
||||
// Leave an unmatched album off the list, or put a left-off one back.
|
||||
func (q *Queries) SetAlbumMatchOutcome(ctx context.Context, arg SetAlbumMatchOutcomeParams) (int64, error) {
|
||||
result, err := q.db.Exec(ctx, setAlbumMatchOutcome, arg.Outcome, arg.AlbumID, arg.Was)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return result.RowsAffected(), nil
|
||||
}
|
||||
|
||||
const setAlbumReleaseGroup = `-- name: SetAlbumReleaseGroup :exec
|
||||
UPDATE albums SET release_group_mbid = $1, updated_at = now()
|
||||
WHERE id = $2 AND (release_group_mbid IS NULL OR release_group_mbid = '')
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
UPDATE library_album_matches SET outcome = 'unmatched' WHERE outcome = 'dismissed';
|
||||
UPDATE library_album_matches SET method = 'search' WHERE method = 'manual';
|
||||
ALTER TABLE library_album_matches DROP CONSTRAINT library_album_matches_outcome_check;
|
||||
ALTER TABLE library_album_matches ADD CONSTRAINT library_album_matches_outcome_check
|
||||
CHECK (outcome IN ('listed', 'unmatched'));
|
||||
ALTER TABLE library_album_matches DROP CONSTRAINT library_album_matches_method_check;
|
||||
ALTER TABLE library_album_matches ADD CONSTRAINT library_album_matches_method_check
|
||||
CHECK (method IN ('', 'tags', 'release', 'discography', 'search'));
|
||||
@@ -0,0 +1,10 @@
|
||||
-- The Collection's Unmatched section (#5579): a person matches an album the
|
||||
-- listing could not, by a MusicBrainz link or a search (method 'manual'),
|
||||
-- or leaves it off the list for good (outcome 'dismissed': a local band's
|
||||
-- album MusicBrainz will never have, a mix that is no album).
|
||||
ALTER TABLE library_album_matches DROP CONSTRAINT library_album_matches_outcome_check;
|
||||
ALTER TABLE library_album_matches ADD CONSTRAINT library_album_matches_outcome_check
|
||||
CHECK (outcome IN ('listed', 'unmatched', 'dismissed'));
|
||||
ALTER TABLE library_album_matches DROP CONSTRAINT library_album_matches_method_check;
|
||||
ALTER TABLE library_album_matches ADD CONSTRAINT library_album_matches_method_check
|
||||
CHECK (method IN ('', 'tags', 'release', 'discography', 'search', 'manual'));
|
||||
@@ -271,6 +271,10 @@ SELECT COUNT(*) FILTER (WHERE fulfilled_at IS NULL AND reason <> 'missing')::big
|
||||
COUNT(*) FILTER (WHERE fulfilled_at IS NULL AND reason = 'missing')::bigint AS missing,
|
||||
COUNT(*) FILTER (WHERE fulfilled_at IS NOT NULL)::bigint AS held,
|
||||
COUNT(DISTINCT artist_mbid)::bigint AS artists,
|
||||
-- Library albums waiting to be matched by hand (#5579).
|
||||
(SELECT COUNT(*) FROM library_album_matches m
|
||||
WHERE m.outcome = 'unmatched'
|
||||
AND EXISTS (SELECT 1 FROM tracks t WHERE t.album_id = m.album_id))::bigint AS unmatched,
|
||||
(SELECT COUNT(*) FROM follows)::bigint AS following,
|
||||
-- The Wanted view's other tabs (#5522): downloads running, those
|
||||
-- waiting on a person, and releases set aside.
|
||||
@@ -330,23 +334,41 @@ SELECT a.id, 'listed', 'tags', a.release_group_mbid
|
||||
ON CONFLICT (album_id) DO NOTHING;
|
||||
|
||||
-- name: ListAlbumsToMatch :many
|
||||
-- Library albums from before the cutoff that the listing has not decided,
|
||||
-- or that an older matcher refused: each is matched to MusicBrainz (#5569).
|
||||
-- An album row with no tracks is not an album in the library.
|
||||
-- Library albums the listing has not decided, or that an older matcher
|
||||
-- refused, or whose tracks changed since they were refused: each is matched
|
||||
-- to MusicBrainz (#5569). A library album is one from before the cutoff, or
|
||||
-- one whose tracks were: a re-tag files a track under a new album row but
|
||||
-- keeps the track (#5579). An album row with no tracks is not an album in
|
||||
-- the library; one a person left off stays off.
|
||||
SELECT a.id, a.title, a.mbid, a.release_group_mbid, ar.name AS artist_name, ar.mbid AS artist_mbid
|
||||
FROM albums a
|
||||
JOIN artists ar ON ar.id = a.artist_id
|
||||
WHERE a.created_at <= sqlc.arg(cutoff)::timestamptz
|
||||
WHERE (a.created_at <= sqlc.arg(cutoff)::timestamptz
|
||||
OR EXISTS (SELECT 1 FROM tracks t WHERE t.album_id = a.id AND t.added_at <= sqlc.arg(cutoff)::timestamptz))
|
||||
AND EXISTS (SELECT 1 FROM tracks t WHERE t.album_id = a.id)
|
||||
AND NOT EXISTS (SELECT 1 FROM library_album_matches m
|
||||
WHERE m.album_id = a.id AND (m.outcome = 'listed' OR m.matcher >= sqlc.arg(matcher)::int))
|
||||
WHERE m.album_id = a.id
|
||||
AND (m.outcome IN ('listed', 'dismissed')
|
||||
OR (m.matcher >= sqlc.arg(matcher)::int
|
||||
AND NOT EXISTS (SELECT 1 FROM tracks t
|
||||
WHERE t.album_id = a.id AND t.updated_at > m.decided_at))))
|
||||
ORDER BY a.created_at, a.id
|
||||
LIMIT sqlc.arg(max_rows);
|
||||
|
||||
-- name: CountAlbumsToRematch :one
|
||||
-- Refusals by an older matcher, which a finished listing goes back to.
|
||||
SELECT COUNT(*) FROM library_album_matches
|
||||
WHERE outcome = 'unmatched' AND matcher < sqlc.arg(matcher)::int;
|
||||
-- name: CountAlbumsToMatch :one
|
||||
-- Whether a finished listing has albums to go back to: those
|
||||
-- ListAlbumsToMatch would hand it.
|
||||
SELECT COUNT(*)
|
||||
FROM albums a
|
||||
WHERE (a.created_at <= sqlc.arg(cutoff)::timestamptz
|
||||
OR EXISTS (SELECT 1 FROM tracks t WHERE t.album_id = a.id AND t.added_at <= sqlc.arg(cutoff)::timestamptz))
|
||||
AND EXISTS (SELECT 1 FROM tracks t WHERE t.album_id = a.id)
|
||||
AND NOT EXISTS (SELECT 1 FROM library_album_matches m
|
||||
WHERE m.album_id = a.id
|
||||
AND (m.outcome IN ('listed', 'dismissed')
|
||||
OR (m.matcher >= sqlc.arg(matcher)::int
|
||||
AND NOT EXISTS (SELECT 1 FROM tracks t
|
||||
WHERE t.album_id = a.id AND t.updated_at > m.decided_at))));
|
||||
|
||||
-- name: ListAlbumTracksToMatch :many
|
||||
-- What an album's tracks say about themselves, to check a match against.
|
||||
@@ -384,16 +406,45 @@ ON CONFLICT (album_id) DO UPDATE
|
||||
-- count the albums no match was found for. The first finish is kept.
|
||||
UPDATE acquisition_settings
|
||||
SET library_listed_at = COALESCE(library_listed_at, now()),
|
||||
library_unlisted = (SELECT COUNT(*) FROM library_album_matches WHERE outcome = 'unmatched')
|
||||
library_unlisted = (SELECT COUNT(*) FROM library_album_matches m
|
||||
WHERE m.outcome = 'unmatched'
|
||||
AND EXISTS (SELECT 1 FROM tracks t WHERE t.album_id = m.album_id))
|
||||
WHERE id = true
|
||||
RETURNING library_listed_at, library_unlisted;
|
||||
|
||||
-- name: ListUnmatchedLibraryAlbums :many
|
||||
-- The albums the listing could not match, for the Held tab.
|
||||
SELECT m.album_id, a.title, ar.name AS artist_name, m.detail
|
||||
-- The Collection's Unmatched section (#5579): the albums the listing could
|
||||
-- not match, and below them the ones a person left off. An album whose
|
||||
-- tracks have all moved to another row is gone, not unmatched.
|
||||
SELECT m.album_id, a.title, ar.name AS artist_name, m.detail, m.outcome, m.decided_at,
|
||||
(SELECT COUNT(*) FROM tracks t WHERE t.album_id = a.id AND t.missing_since IS NULL)::int AS tracks
|
||||
FROM library_album_matches m
|
||||
JOIN albums a ON a.id = m.album_id
|
||||
JOIN artists ar ON ar.id = a.artist_id
|
||||
WHERE m.outcome = 'unmatched'
|
||||
ORDER BY ar.name, a.title
|
||||
WHERE m.outcome IN ('unmatched', 'dismissed')
|
||||
AND EXISTS (SELECT 1 FROM tracks t WHERE t.album_id = m.album_id)
|
||||
ORDER BY m.outcome DESC, lower(ar.name), lower(a.title)
|
||||
LIMIT sqlc.arg(max_rows);
|
||||
|
||||
-- name: RecountLibraryUnlisted :exec
|
||||
-- After a person matches or leaves off an album, the count the catalogue
|
||||
-- shows.
|
||||
UPDATE acquisition_settings
|
||||
SET library_unlisted = (SELECT COUNT(*) FROM library_album_matches m
|
||||
WHERE m.outcome = 'unmatched'
|
||||
AND EXISTS (SELECT 1 FROM tracks t WHERE t.album_id = m.album_id))
|
||||
WHERE id = true;
|
||||
|
||||
-- name: GetAlbumToMatch :one
|
||||
-- One album a person is matching by hand, as the matcher reads albums.
|
||||
SELECT a.id, a.title, a.mbid, a.release_group_mbid, ar.name AS artist_name, ar.mbid AS artist_mbid
|
||||
FROM albums a
|
||||
JOIN artists ar ON ar.id = a.artist_id
|
||||
JOIN library_album_matches m ON m.album_id = a.id AND m.outcome IN ('unmatched', 'dismissed')
|
||||
WHERE a.id = $1;
|
||||
|
||||
-- name: SetAlbumMatchOutcome :execrows
|
||||
-- Leave an unmatched album off the list, or put a left-off one back.
|
||||
UPDATE library_album_matches SET outcome = sqlc.arg(outcome), decided_at = now()
|
||||
WHERE album_id = sqlc.arg(album_id) AND outcome = sqlc.arg(was);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user