Conversation
|
On #780 I said the MP4 key ought to be checked before anyone leaned on it, so I checked it rather than leave the hedge sitting there. Your assumption holds. Tagged files in each dialect, read with the command Listenarr runs (
The freeform atom does come through unprefixed, so There is one thing I'd think about before this lands, because it runs into a bug that's already live.
The adopt only fires when the audiobook has no ASIN, which is the library-import case this PR is aimed at, so the two conditions overlap rather than excluding each other. A wrongly linked file donates its ASIN, A cheap guard would be to require the linked files to agree: if the files linked to one audiobook carry more than one distinct ASIN, that disagreement is itself a signal the attribution is off, so adopt nothing instead of picking one. When attribution is right it changes nothing. Waiting until attribution is tightened would also do it, since #717 rewrites this area and wants a title match rather than accepting the author on its own. Two smaller things:
On the tests: they assert against hand-built JSON, so they'd pass unchanged even if ffprobe surfaced the tag under some other key, which is the one thing this PR depends on. The table above is that assumption actually checked. I have a corpus of tagged public-domain files covering every dialect if a fixture test would help, and I'm happy to hand over the files or the generator that makes them. |
…arrs#781) Per @m4bard's review: - ASIN adoption now requires agreement: it only adopts when every linked file that carries an ASIN carries the same one. If the linked files disagree, that's a signal the file-to-book attribution is wrong, so nothing is adopted rather than picking one and letting the metadata auto-refresh act on a wrong identifier (ResolveUnanimousFileAsinAsync). - ISBN is now adopted only when the book has none, mirroring ASIN. It no longer appends new values on top of an existing set, which was the route a wrongly linked file could take to accumulate a stray identifier. - The already-registered-file re-read now reuses the per-file/mtime metadata cache, so rescanning a book that has no embedded ASIN no longer re-runs ffprobe on every file each time. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Thanks for actually tagging files in each dialect and checking Pushed 4def14c addressing the three points:
One honest limitation I'd rather name than paper over: the agreement check sees the files linked at the moment adoption fires, and adoption fires on the first file that carries an ASIN, so a conflicting file linked later in the same scan can still slip through the window. Tightening that fully means moving adoption to a post-scan step once the file set is settled — happy to do that if you'd prefer it over the incremental guard, but it felt like more surface than this PR should carry given #717/#784 are removing the mis-attribution at the source. |
|
All three land, and the unanimity rule is a better shape than what I suggested. Refusing on disagreement rather than trying to pick a winner is the part that makes it safe. Readarr's answer to this is neither of the two options we were choosing between. It never adopts an identifier onto a book at all: it groups the files, identifies the group against candidate editions, and treats the embedded ASIN as one weighted input to that choice.
releases = _trackGroupingService.GroupTracks(localTracks);
foreach (var localRelease in releases) { ... IdentifyRelease(localRelease, ...); }
var asin = localTracks.MostCommon(x => x.FileTrackInfo.Asin);
if (asin.IsNotNullOrWhiteSpace() && edition.Asin.IsNotNullOrWhiteSpace())
dist.AddBool("asin", asin != edition.Asin);
else if (asin.IsNullOrWhiteSpace() != edition.Asin.IsNullOrWhiteSpace())
dist.AddBool("asin_missing", true);with the weights in Grouping before identification has been there since
The weights are worth a look either way. On the window you asked about. It is narrower than "later in the same scan", and it cuts in a direction worth naming. Adoption runs from That makes the two paths behave differently. Re-scanning a book that already has its files linked, your new None of which is a reason to hold the PR. Deciding once the file set has settled is what actually closes the window, but it is a different shape of change, and you are right that #784 and #717 remove the cause rather than the symptom. The one thing I would add now is a sentence on I can measure it instead of leaving it as a reading. The corpus has a deliberate wrong-ASIN tag state and the attribution harness can build a library where a scan links a file belonging to another book, so pointing those at each other would show whether a conflicting ASIN reaches adoption before the check sees it. Say the word and I will run it against On fixtures, yes, take whatever is useful. |
|
#717 changes AudiobookFile registration into a database-enforced filesystem identity/ownership contract and serializes path-bearing mutations through the audiobook operation boundary. The ASIN/ISBN tag extraction in this PR remains distinct, but please rebase on #717 and make identifier adoption run through the new registration/operation-lock flow so it cannot race file ownership or concurrent audiobook updates. |
Document that ResolveUnanimousFileAsinAsync only sees files linked at the moment it runs, so a lone early tagged file is trivially 'unanimous' and the guard is weakest on first-scan mis-attribution. Addresses @m4bard review feedback on Listenarrs#781. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
@therobbiedavis sounds good — I'll rebase this on #717 once it lands and route the identifier adoption through the new registration/operation-lock flow so it can't race file ownership or concurrent audiobook updates. Happy to hold until #717 merges so I'm building against its final shape rather than a moving target. @m4bard thanks for the careful read. You're right that the guard is weakest on a first scan, where the first tagged file is "unanimous" just by being the only member — I've pushed a doc comment on |
|
Fixtures are ready. Six directories, each one book whose files are tagged individually:
Each set writes a Two things I checked rather than assumed. Every file was read back through ffprobe to confirm the identifier actually surfaces, because fixtures that silently carry nothing would turn a passing test into a test of nothing. And rebuilding replaces the set rather than merging into it, which matters because a stale file surviving into If the shape is not what you need, say what is missing and I will add cases. Adding one is a few lines. |
4a2e9d2 to
08996b8
Compare
|
Pushed the rework onto current canary (post-#717). Summary of how it now fits the new file-registration flow:
Build + suite green: ffprobe-mapper tests (incl. the @m4bard — I'd love to take you up on the tag-dialect fixtures for real integration coverage here (the |
… rules CI on ubuntu-24.04 ran the backend suite for the first time: 3 failures out of 3264, all from merged PRs that had not been through upstream review. - Conform AudiobookMetadataRefreshServiceTests (Listenarrs#781), FfprobeTagMetadataMapperTests (Listenarrs#781) and SabnzbdResponseMapperTests (Listenarrs#840) to TestClasses_FollowRepositoryConventions. - Split the claim diagnostics helpers out of AudiobookFileService.cs, which Listenarrs#849 and Listenarrs#781 together pushed to 503 lines against the 500-line cap. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
The generator is It calls I read the diff at 08996b8 but have not run it. The guard and the post-lock refresh both look like your description: Two cases the tests do not seem to reach. Disclosure: this was produced with Claude Code at my direction. Every file and line cited was re-read against the stated commit, and anything described as verified was verified by running it. I reviewed the text before posting. I have not read every line of the diffs I am citing. |
08996b8 to
8fd898b
Compare
|
Thanks for the fixtures and the code read. I've pinned both cases you raised with unit tests at the
Your fixtures are still valuable for exercising the extraction end-to-end (the |
|
Agreed, and I think you put them in the right place. Both behaviours are inside I read both tests at So I am not adding either case to the generator, and the fixture set stays as it is. If the broader integration pass happens, Disclosure: drafted with Claude Code at my direction; I read the cited code at the stated commit and reviewed this before posting. |
Rework of this PR onto Listenarrs#717's rewritten file-registration flow. - FfprobeTagMetadataMapper now reads ASIN / AUDIBLE_ASIN / ISBN tags into AudioMetadata, so a scanned file's embedded identifier is available during registration. - New AudiobookFileService.IdentifierAdoption partial adopts that identifier onto a bare audiobook from INSIDE the per-audiobook operation lock (EnsureAudiobookFileCoreAsync), so the write cannot race file ownership or a concurrent audiobook update. A unanimity guard refuses to adopt when the book's linked files carry disagreeing ASINs (a sign of mis-attribution). - The upstream metadata refresh (Audible lookup) runs AFTER the lock is released -- signalled out via a StrongBox -- so the network call never holds the global filesystem lock. It fills only empty fields and never fails the scan. Restores the IAudiobookMetadataRefreshService dropped in the rebase. Tests: FfprobeTagMetadataMapper (incl. AUDIBLE_ASIN dialect + no-overwrite), AudiobookMetadataRefreshService.FillMissingFields, and the coordination test updated for the new constructor dependency. Build + suite green, no regressions. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ntra-file precedence - A whitespace-only ASIN tag is skipped and the next spelling wins. - A single file with disagreeing ASIN/AUDIBLE_ASIN resolves to the first name in the lookup order (the cross-file unanimity guard is deliberately cross-file only). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
c6428cb to
262187b
Compare
Fixes #780
Problem
When ListenArr scans an existing audio file that has an ASIN embedded in its tags (the freeform iTunes atom
----:com.apple.iTunes:ASINthat Audible rips and most audiobook taggers write), the ASIN is never extracted. The imported audiobook gets no identifier, and Rescan Metadata then fails immediately with "No ASIN or ISBN identifiers are available for metadata rescan" — even though the value is sitting in the file.Root cause:
FfprobeTagMetadataMapper.Apply()maps title/artist/album/track/disc/year but never reads the ASIN (or ISBN) tag.Fix
FfprobeTagMetadataMapper.Apply()now reads theASINandISBNtags intoAudioMetadata.AudioMetadataalready hadAsin/Isbnproperties, andGetTagalready matches case-insensitively (so the----:com.apple.iTunes:ASINatom, which ffprobe surfaces asASIN, is picked up).AudiobookFileService.EnsureAudiobookFileAsync()— the shared file-registration path the scan job uses — now adopts those identifiers onto the audiobook when it has none, persists via the audiobook repository, and records a history entry. Existing identifiers are never overwritten. SinceAudiobookIdentifierMapper.GetEffectiveIdentifiersbackfills from the legacyAsin/Isbnfields, setting them is sufficient for Rescan Metadata to succeed.Scope / notes
Tests
FfprobeTagMetadataMapperTests: ASIN + ISBN extraction, case-insensitive ASIN match, absent-tag leavesnull, and existing ASIN not overwritten.dotnet buildclean (0 warnings). Related suites (Files / Scanning / Ffmpeg / Metadata) — 174/174 pass.🤖 Generated with Claude Code