Fills the MusicBrainz recording ids that file tags leave empty, so the similarity arms can place untagged tracks.
Client (#3919): compressed fpcalc print (first 120s), gzip POST to AcoustID's v2 lookup, 20s deadline, spaced to stay under 3 requests per second.
Provenance (#3920): migration 0069 adds tracks.mbid_source (tag | acoustid), the track_acoustid_lookups state table and the acoustid_settings singleton. A tag id always wins; a rescan never drops a looked-up id.
Worker (#3921): writes an id only for an unambiguous match (one candidate at or above min score, or exactly one left after matching title and length within 3s). Stops a pass on a refused key or an unreachable service and reports why.
Admin UI (#3922): Admin → Integrations → AcoustID card with the switch, a write-only key, the min score, coverage by source, "Look up now" and the unsettled list.
Off by default. Does nothing until a key is entered and the switch is on.
CI: run 8494 green on every lane, including the APK and image builds, for e8eee553.
Fills the MusicBrainz recording ids that file tags leave empty, so the similarity arms can place untagged tracks.
- **Client (#3919):** compressed fpcalc print (first 120s), gzip POST to AcoustID's v2 lookup, 20s deadline, spaced to stay under 3 requests per second.
- **Provenance (#3920):** migration 0069 adds `tracks.mbid_source` (tag | acoustid), the `track_acoustid_lookups` state table and the `acoustid_settings` singleton. A tag id always wins; a rescan never drops a looked-up id.
- **Worker (#3921):** writes an id only for an unambiguous match (one candidate at or above min score, or exactly one left after matching title and length within 3s). Stops a pass on a refused key or an unreachable service and reports why.
- **Admin UI (#3922):** Admin → Integrations → AcoustID card with the switch, a write-only key, the min score, coverage by source, "Look up now" and the unsettled list.
Off by default. Does nothing until a key is entered and the switch is on.
CI: run 8494 green on every lane, including the APK and image builds, for e8eee553.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
internal/acoustid posts AcoustID's v2 lookup as a gzip form with
meta=recordings, one request per 400ms (their limit is 3/s) and a 20s
deadline. It returns every linked recording with its best score; choosing
among them is the worker's job (D5). The server's error codes map to an
invalid key (stop and say so), a rejected fingerprint (a verdict on the
track) or unavailable (try again later). A cancelled caller stays a
cancellation.
fpcalcLookupArgs and parseFpcalcCompressed read fpcalc's default output,
the compressed string the lookup takes (D1), always over the first 120s.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>
The card takes the slot of the unimplemented "MusicBrainz overrides"
placeholder. Rows:
- the on switch
- a write-only key field (the stored key is never sent back), with a
link to register an application
- the minimum score (0.5 to 1)
Below them, recording-id coverage reads as a column: from tags, looked
up, none, and of the none how many are waiting, no match, ambiguous or
failed. There is a "Look up now" button and a folded list of the tracks
the lookup could not settle.
Off, keyless and stopped-short passes are each a visible state with the
reason (rule 164).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
svelte-check on 0a7f7883: api.post takes a body. Every other body-less POST
in the client passes {}.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Its bare "Loading…" made the Integrations page's cover-providers test find
two matches for /loading…/i (Vitest, run 8487). "Loading AcoustID
settings…" also says which card is loading.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Fills the MusicBrainz recording ids that file tags leave empty, so the similarity arms can place untagged tracks.
tracks.mbid_source(tag | acoustid), thetrack_acoustid_lookupsstate table and theacoustid_settingssingleton. A tag id always wins; a rescan never drops a looked-up id.Off by default. Does nothing until a key is entered and the switch is on.
CI: run 8494 green on every lane, including the APK and image builds, for
e8eee553.🤖 Generated with Claude Code