v0.15.0: ask the song what it is

Right-click → Identify Track… fingerprints the file with Chromaprint's
fpcalc and asks AcoustID who it is, then proposes the tags it found.

The year is the point. A 60s song kept getting stamped with the year its
CD reissue came out, which sorts the library wrong, so every candidate
carries the recording's *original* year — the earliest release across all
of its release groups — including the candidate that proposes a later
greatest-hits as the album. Ranking separately prefers a plain studio
Album over EP/Single over anything wearing a Compilation or Live
secondary type, so the default proposal is the real album as well.

Nothing is written until accepted: the dialog shows each current value
beside an editable proposed one, and a row starts checked only where the
proposal actually differs from what the file already has, so an untouched
field can never quietly blank a tag. Applying goes through
edit_track_fields, which is what buys the tag write, the abort-if-it-fails
rule, artist/album file relocation and one undo step without re-deriving
any of them.

A selection is a queue reviewed one track at a time; a failure is a
status-bar message that moves on rather than ending the batch. fpcalc is
detected at runtime like ffmpeg and the AcoustID key lives in preferences
(so it syncs, and stays out of git) — the round adds no new dependency.

Also fixes a pre-existing round 40 test that used the real tummult mount
point as its stand-in for an unreachable path, so it failed whenever that
drive was plugged in.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NsiFHdyVg1UhBSxDJfTNRm
This commit is contained in:
2026-09-02 15:40:52 -04:00
co-authored by Claude Opus 5
parent 8c097faacc
commit f0d4d1eff2
12 changed files with 946 additions and 2 deletions
+47
View File
@@ -1,5 +1,52 @@
## Done
### Round 45 (2026-09-02) — Ask the song what it is (v0.15.0)
Some files arrive with the title right and everything else missing or wrong,
and there was no way to fix that except typing. Now the audio itself is the
question: right-click → **Identify Track…** fingerprints the file and asks
AcoustID who it is.
- [x] **Fingerprint, not filename guessing.** New `lintunes/fingerprint.py`:
`fpcalc -json` (Chromaprint's CLI, detected with `shutil.which` like
`ffmpeg_available()` — a runtime tool, not a pip dependency) produces a
duration + fingerprint, which `lookup_fingerprint` POSTs to the AcoustID
web API with `meta=recordings releasegroups releases tracks compress`.
`requests` was already a dep, so the round adds none.
- [x] **The year is the song's, not the pressing's.** trav's actual complaint:
"I don't care when the CD of something from the 60s came out." So
`parse_lookup` proposes the *original* release year — the earliest date
across every release group the recording appears on — and every candidate
carries it, including the one proposing a 1998 greatest-hits as the album.
Years sort by when the song came out. `_releasegroup_sort_key` separately
ranks a plain studio Album above EP/Single above anything wearing a
Compilation/Live secondary type, so the *default* proposal is the real
album too.
- [x] **Nothing is written until you say so.** `gui/identify_dialog.py` is
passive the way `AlbumArtDialog` is — candidates arrive pre-parsed, and
the caller applies the result afterwards. Each row shows the current value
beside an editable proposed one, with a checkbox that starts checked only
where the proposal actually differs from what the file already has (a row
the lookup knows nothing about is disabled, so it can't quietly blank a
tag). A dropdown switches between alternate releases.
- [x] **Applied through the one funnel.** `LibraryManager.edit_track_fields`
does it, so tag writes, the abort-if-the-write-fails rule, artist/album
file relocation and a single undo step all come free rather than being
re-implemented.
- [x] **A selection is a queue, one dialog at a time.** Each track is reviewed
on its own; "Stop Identifying (N left)" abandons the rest, and a failure
(unfingerprintable file, network error, no match) is a status-bar message
that moves on to the next rather than ending the batch.
- [x] **The key is trav's, and stays out of git.** `preferences.acoustid`
+ a Preferences row linking acoustid.org/new-application, following the
Last.fm precedent — preferences.json rides the Syncthing share, so
pasting it once covers both machines.
- [x] Fixed an unrelated pre-existing test failure: round 40's
`test_the_pre_010_music_root_key_counts_as_an_override` used the real
tummult mount point as its stand-in for an unreachable path, so it failed
whenever that drive was actually plugged in. It now builds the path under
`tmp_path` and never creates it.
### Round 44 (2026-08-27) — A removal you make sticks (v0.14.0)
Round 43 made the merge report honest, which made its one real limitation