release / govulncheck (push) Successful in 15s
release / web (push) Failing after 1m21s
release / go (push) Successful in 1m35s
release / integration (push) Failing after 4m57s
release / android (push) Successful in 5m11s
release / Build signed APK (releases and dev) (push) Successful in 5m28s
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
Minstrel keeps its own list of what it is to get, keyed by MusicBrainz release group. A want comes from an approved request, a new release by a followed artist, or an album whose files went missing, and is held once the library has a full edition of it. - Migration 0081: release_type_profiles (Standard seeded: Album, EP, Single, studio only), follows, follow_seen_groups and wants. lidarr_requests becomes requests with plain *_mbid columns; approved album and track requests move onto the wanted list, approved artist requests become follows, and the library's album artists start out followed. missing_reacquisitions and the retry settings go. - internal/wanted: follows are checked against MusicBrainz discographies daily. The first check sets a baseline, and only releases after it are wanted unless the follow asked for what already exists. Profiles filter by primary and secondary type, so live recordings stay out unless a profile ticks Live. - internal/requests (was lidarrrequests): approving puts the request on the wanted list, and an artist request can be approved on a chosen profile. A request completes when its want is held. - The re-acquisition sweeper wants a missing album back instead of asking Lidarr; searching for it is the search schedule's (#5521). - Web: an admin Wanted page (Wanted, Missing files, Held, Following), a follow control on artist pages, a release-type profiles card on Integrations, "Approve on a profile" on Requests, and want state on Missing files. The artist detail API returns the artist's MBID. - Android: the request body sends artist_mbid, album_mbid and track_mbid. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
166 lines
7.9 KiB
SQL
166 lines
7.9 KiB
SQL
-- 0081_wanted.up.sql — the wanted list (Scribe milestone #502, #5520).
|
|
--
|
|
-- With Lidarr gone, Minstrel keeps its own record of what it is looking for:
|
|
--
|
|
-- release_type_profiles which kinds of release a follow wants: primary
|
|
-- types (Album, EP, Single…) and the secondary types
|
|
-- allowed (Live, Compilation, Remix…). An empty
|
|
-- secondary list means studio releases only, which
|
|
-- is how live recordings are left out.
|
|
-- follows artists whose new releases are wanted, each on a
|
|
-- profile.
|
|
-- follow_seen_groups release groups a follow has already judged, so
|
|
-- each is judged once.
|
|
-- wants release groups wanted: a request, a followed
|
|
-- artist's new release, or an album whose files went
|
|
-- missing. A want is held once the library has a
|
|
-- full copy (internal/wanted).
|
|
--
|
|
-- lidarr_requests becomes requests with no Lidarr columns: approving a request
|
|
-- now records a want or a follow instead of sending an add to Lidarr. The
|
|
-- missing-file sweeper's attempt bookkeeping goes too; retrying belongs to the
|
|
-- search schedule (#5521), which works per want.
|
|
|
|
CREATE TABLE release_type_profiles (
|
|
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
name text NOT NULL UNIQUE,
|
|
primary_types text[] NOT NULL,
|
|
secondary_types text[] NOT NULL DEFAULT '{}',
|
|
is_default boolean NOT NULL DEFAULT false,
|
|
created_at timestamptz NOT NULL DEFAULT now(),
|
|
updated_at timestamptz NOT NULL DEFAULT now(),
|
|
CONSTRAINT release_type_profiles_primary_nonempty CHECK (cardinality(primary_types) > 0)
|
|
);
|
|
|
|
-- At most one default; the service keeps exactly one by moving it.
|
|
CREATE UNIQUE INDEX release_type_profiles_one_default
|
|
ON release_type_profiles (is_default) WHERE is_default;
|
|
|
|
-- Lidarr's "Standard" metadata profile, which every followed artist used
|
|
-- (note #5527): albums, EPs and singles, studio releases only.
|
|
INSERT INTO release_type_profiles (name, primary_types, secondary_types, is_default)
|
|
VALUES ('Standard', ARRAY['Album', 'EP', 'Single'], '{}', true);
|
|
|
|
CREATE TABLE follows (
|
|
artist_mbid text PRIMARY KEY,
|
|
artist_name text NOT NULL,
|
|
-- RESTRICT: a profile in use cannot be deleted out from under a follow.
|
|
profile_id uuid NOT NULL REFERENCES release_type_profiles (id) ON DELETE RESTRICT,
|
|
followed_by uuid REFERENCES users (id) ON DELETE SET NULL,
|
|
followed_at timestamptz NOT NULL DEFAULT now(),
|
|
-- When the first discography read marked everything then listed as seen.
|
|
-- NULL until then: a follow wants only what appears after it.
|
|
baseline_at timestamptz,
|
|
-- Whether the baseline also wants what the artist had already released
|
|
-- that the profile passes and the library lacks: an approved artist
|
|
-- request, or the operator ticking "also want…". Cleared once applied.
|
|
want_existing boolean NOT NULL DEFAULT false,
|
|
-- The discography read this follow last applied.
|
|
checked_at timestamptz,
|
|
updated_at timestamptz NOT NULL DEFAULT now()
|
|
);
|
|
|
|
CREATE INDEX follows_profile_idx ON follows (profile_id);
|
|
|
|
CREATE TABLE follow_seen_groups (
|
|
artist_mbid text NOT NULL REFERENCES follows (artist_mbid) ON DELETE CASCADE,
|
|
release_group_mbid text NOT NULL,
|
|
seen_at timestamptz NOT NULL DEFAULT now(),
|
|
PRIMARY KEY (artist_mbid, release_group_mbid)
|
|
);
|
|
|
|
CREATE TABLE wants (
|
|
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
release_group_mbid text NOT NULL UNIQUE,
|
|
artist_mbid text NOT NULL,
|
|
artist_name text NOT NULL,
|
|
title text NOT NULL,
|
|
primary_type text NOT NULL DEFAULT '',
|
|
first_release_date text NOT NULL DEFAULT '',
|
|
reason text NOT NULL
|
|
CONSTRAINT wants_reason_check CHECK (reason IN ('request', 'follow', 'missing')),
|
|
-- The library album this want is about (a missing want), or the one that
|
|
-- answered it.
|
|
album_id uuid REFERENCES albums (id) ON DELETE SET NULL,
|
|
created_by uuid REFERENCES users (id) ON DELETE SET NULL,
|
|
-- When it was last wanted: set again when a held want is reopened, so
|
|
-- "arrived since it was wanted" measures from the reopening.
|
|
wanted_at timestamptz NOT NULL DEFAULT now(),
|
|
fulfilled_at timestamptz,
|
|
created_at timestamptz NOT NULL DEFAULT now(),
|
|
updated_at timestamptz NOT NULL DEFAULT now()
|
|
);
|
|
|
|
CREATE INDEX wants_open_idx ON wants (wanted_at) WHERE fulfilled_at IS NULL;
|
|
CREATE INDEX wants_artist_idx ON wants (artist_mbid);
|
|
|
|
-- Requests lose Lidarr.
|
|
DROP TABLE missing_reacquisitions;
|
|
|
|
ALTER TABLE lidarr_requests RENAME TO requests;
|
|
ALTER TYPE lidarr_request_status RENAME TO request_status;
|
|
ALTER TYPE lidarr_request_kind RENAME TO request_kind;
|
|
ALTER TABLE requests RENAME COLUMN lidarr_artist_mbid TO artist_mbid;
|
|
ALTER TABLE requests RENAME COLUMN lidarr_album_mbid TO album_mbid;
|
|
ALTER TABLE requests RENAME COLUMN lidarr_track_mbid TO track_mbid;
|
|
ALTER TABLE requests DROP COLUMN quality_profile_id;
|
|
ALTER TABLE requests DROP COLUMN root_folder_path;
|
|
ALTER TABLE requests DROP COLUMN lidarr_add_confirmed_at;
|
|
ALTER INDEX lidarr_requests_user_id_idx RENAME TO requests_user_id_idx;
|
|
ALTER INDEX lidarr_requests_status_idx RENAME TO requests_status_idx;
|
|
ALTER INDEX lidarr_requests_artist_mbid_idx RENAME TO requests_artist_mbid_idx;
|
|
ALTER INDEX lidarr_requests_album_mbid_idx RENAME TO requests_album_mbid_idx;
|
|
|
|
-- Requests made before #5241 name albums by release id. A want is keyed by
|
|
-- release group, so repoint every request the library can translate.
|
|
UPDATE requests r
|
|
SET album_mbid = a.release_group_mbid,
|
|
updated_at = now()
|
|
FROM albums a
|
|
WHERE a.mbid = r.album_mbid
|
|
AND a.release_group_mbid IS NOT NULL
|
|
AND a.release_group_mbid <> r.album_mbid;
|
|
|
|
-- Approved album and track requests still waiting become wants, judged by
|
|
-- the same test as any other (internal/wanted).
|
|
INSERT INTO wants (release_group_mbid, artist_mbid, artist_name, title, reason, created_by, wanted_at)
|
|
SELECT DISTINCT ON (r.album_mbid)
|
|
r.album_mbid, r.artist_mbid, r.artist_name, COALESCE(r.album_title, ''), 'request', r.user_id, r.requested_at
|
|
FROM requests r
|
|
WHERE r.status = 'approved'
|
|
AND r.kind IN ('album', 'track')
|
|
AND r.album_mbid IS NOT NULL
|
|
ORDER BY r.album_mbid, r.requested_at
|
|
ON CONFLICT (release_group_mbid) DO NOTHING;
|
|
|
|
-- The library's album artists start out followed on the default profile
|
|
-- (operator, 2026-10-09: "we already have artists in minstrel those start as
|
|
-- the wanted artists"). Various Artists and MusicBrainz's bracketed
|
|
-- placeholders ([unknown], [anonymous]…) are not artists anyone follows.
|
|
INSERT INTO follows (artist_mbid, artist_name, profile_id)
|
|
SELECT ar.mbid, ar.name, p.id
|
|
FROM artists ar
|
|
CROSS JOIN (SELECT id FROM release_type_profiles WHERE is_default) p
|
|
WHERE ar.mbid IS NOT NULL AND ar.mbid <> ''
|
|
AND ar.mbid <> '89ad4ac3-39f7-470e-963a-56509c546377'
|
|
AND ar.name NOT LIKE '[%]'
|
|
AND EXISTS (SELECT 1 FROM albums al WHERE al.artist_id = ar.id)
|
|
ON CONFLICT (artist_mbid) DO NOTHING;
|
|
|
|
-- An approved artist request was an artist added to Lidarr: it is followed.
|
|
INSERT INTO follows (artist_mbid, artist_name, profile_id, followed_by)
|
|
SELECT DISTINCT ON (r.artist_mbid) r.artist_mbid, r.artist_name, p.id, r.decided_by
|
|
FROM requests r
|
|
CROSS JOIN (SELECT id FROM release_type_profiles WHERE is_default) p
|
|
WHERE r.status = 'approved' AND r.kind = 'artist'
|
|
ORDER BY r.artist_mbid, r.requested_at
|
|
ON CONFLICT (artist_mbid) DO NOTHING;
|
|
|
|
-- The sweeper now only records a want; how often to look again is the
|
|
-- search schedule's business.
|
|
ALTER TABLE reacquisition_settings
|
|
DROP COLUMN backoff_base_hours,
|
|
DROP COLUMN backoff_max_hours,
|
|
DROP COLUMN max_attempts,
|
|
DROP COLUMN auto_approve;
|