release / web (push) Successful in 1m44s
release / go (push) Successful in 2m1s
release / govulncheck (push) Successful in 17s
release / integration (push) Successful in 5m22s
release / android (push) Successful in 5m48s
release / Build signed APK (releases and dev) (push) Successful in 5m53s
release / Attach APK to the Release (tag releases only) (push) Skipped
release / Build + push container image (push) Successful in 2m6s
release / Verify release artifacts (tag releases only) (push) Skipped
Migration 0069 adds tracks.mbid_source (tag | acoustid), a lookup state per track (matched | ambiguous | no_match | failed) and the acoustid_settings row (off, no key, min score 0.85). The file's tag outranks a lookup (D4). UpsertTrack keeps a looked-up id through a re-read that finds no tag id and replaces it as soon as one appears. SetTrackMbidFromAcoustID refuses to write over a tag id. The worker fingerprints each untagged track with fpcalc's compressed print, looks it up and writes an id only when D5 settles it: one recording at or above the threshold, or one left after matching title and length. Ambiguous and no-match results write nothing. A key AcoustID refuses, or the service being unreachable, stops the pass and is reported in the worker's status. It never counts as a verdict on a track. A changed file drops its lookup in the scan. The re-lookup takes back an id that no longer matches. Admin API: GET /api/admin/library/acoustid (settings, status, coverage by source), PUT …/acoustid-settings (write-only key), POST …/acoustid/run, GET …/acoustid/unsettled. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
70 lines
3.6 KiB
SQL
70 lines
3.6 KiB
SQL
-- 0069_acoustid_lookup.up.sql — recording MBIDs looked up through AcoustID
|
|
-- for tracks whose tags carry none (Scribe milestone #401).
|
|
--
|
|
-- A track with no recording MBID is invisible to the ListenBrainz similarity
|
|
-- arm in both directions, so this fills the ids the tags leave empty.
|
|
|
|
-- Where tracks.mbid came from. The file's own tag always outranks a lookup
|
|
-- (M401 D4): the scan's upsert keeps a looked-up id while the tag carries
|
|
-- none, and replaces it the moment the tag carries one.
|
|
ALTER TABLE tracks ADD COLUMN mbid_source text;
|
|
-- Every id stored so far was read from a tag (the scan or the tag backfill),
|
|
-- or inherited from a merged duplicate's tag.
|
|
UPDATE tracks SET mbid_source = 'tag' WHERE mbid IS NOT NULL;
|
|
ALTER TABLE tracks
|
|
ADD CONSTRAINT tracks_mbid_source_check CHECK (mbid_source IN ('tag', 'acoustid')),
|
|
ADD CONSTRAINT tracks_mbid_source_pair CHECK ((mbid IS NULL) = (mbid_source IS NULL));
|
|
|
|
-- What happened the last time a track was looked up. A table of its own, as
|
|
-- track_loudness is (0065): tracks is read with SELECT * on the hot path.
|
|
--
|
|
-- What a row means, which the lookup worker depends on:
|
|
-- no row never looked up, or the file changed since (the scan deletes
|
|
-- the row): look it up
|
|
-- matched one recording cleared the score threshold, or one was left
|
|
-- after disambiguating by title and length; recording_mbid is it
|
|
-- ambiguous several recordings cleared it and nothing told them apart.
|
|
-- Nothing is written: a wrong MBID would feed similarity the
|
|
-- wrong neighbours, which is worse than none (M401 D5)
|
|
-- no_match AcoustID linked nothing at or above the threshold
|
|
-- failed fpcalc rejected the file, or AcoustID rejected its print
|
|
-- Every state is settled until the file changes. A failure that says nothing
|
|
-- about the file (AcoustID unreachable, a timeout, a cancelled pass) writes no
|
|
-- row, so the worker tries again.
|
|
CREATE TABLE track_acoustid_lookups (
|
|
track_id uuid PRIMARY KEY REFERENCES tracks (id) ON DELETE CASCADE,
|
|
state text NOT NULL,
|
|
-- The best score any linked recording had; NULL when none was linked.
|
|
best_score real,
|
|
-- How many distinct recordings were linked, at any score.
|
|
candidates integer NOT NULL DEFAULT 0,
|
|
recording_mbid text,
|
|
-- Why a lookup failed, for the admin list.
|
|
detail text,
|
|
looked_up_at timestamptz NOT NULL DEFAULT now(),
|
|
|
|
CONSTRAINT track_acoustid_lookups_state_check
|
|
CHECK (state IN ('matched', 'ambiguous', 'no_match', 'failed')),
|
|
CONSTRAINT track_acoustid_lookups_matched_has_mbid
|
|
CHECK ((state = 'matched') = (recording_mbid IS NOT NULL))
|
|
);
|
|
CREATE INDEX track_acoustid_lookups_state ON track_acoustid_lookups (state);
|
|
|
|
-- AcoustID's knobs, in admin Settings (rule 25). Singleton in the style of
|
|
-- loudness_settings (0065).
|
|
CREATE TABLE acoustid_settings (
|
|
id boolean PRIMARY KEY DEFAULT true,
|
|
-- Off by default (rule 26): AcoustID needs a key the operator registers,
|
|
-- and the lookup sends fingerprints to a third party.
|
|
enabled boolean NOT NULL DEFAULT false,
|
|
api_key text,
|
|
-- The score a recording needs before it is considered at all. AcoustID
|
|
-- scores a true match close to 1; 0.85 leaves room for a lossy encode.
|
|
min_score real NOT NULL DEFAULT 0.85,
|
|
updated_at timestamptz NOT NULL DEFAULT now(),
|
|
|
|
CONSTRAINT acoustid_settings_singleton CHECK (id = true),
|
|
CONSTRAINT acoustid_settings_min_score_range CHECK (min_score >= 0.5 AND min_score <= 1)
|
|
);
|
|
INSERT INTO acoustid_settings (id) VALUES (true) ON CONFLICT (id) DO NOTHING;
|