Track multiple narrations of the same audiobook - #58
Open
JordanFromIT wants to merge 1 commit into
Open
Conversation
Selecting a narrator for a book with no files pinned that narration in place, so choosing a second one silently replaced the first. Only one narration could ever be wanted at a time, and the wanted-instance path that creates a separate row was unreachable until the book already had files on disk. AddWantedEdition takes an asNewVariant flag, surfaced on the API as AsNewVariant. When set, the no-files branch is skipped so the narration is tracked alongside the current selection instead of replacing it. The flag defaults to false, so existing callers keep the previous behaviour. Wanted-instance de-duplication now also matches on narrator, not just on edition provider ids. Two printings of the same narration are distinct edition records but the same request, so asking for a narrator that is already tracked returns the existing row. The base book is checked the same way, so a variant is not created for a narration it is already pinned to. Narrator names are compared on normalised tokens because providers return inconsistent spacing such as "Peter Noble". The narrator modal now routes both cases through the same endpoint. It previously reimplemented edition pinning client-side for books with no files, which bypassed this logic entirely. The test doubles for IBookService are updated for the new signature.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Some works have narrations worth keeping side by side — Jim Dale and Stephen Fry, Roy Dotrice and John Lee. Chaptarr already has most of what that needs: wanted instances carry
BaseBookId, get a_wanted_{editionId}slug, de-duplicate against existing rows, and the{Narrator}naming token files them into separate folders. The narrator modal is already wired toaddNarratorVariant, and it lists the narrators you don't yet have, which is additive by intent.But you can only ever want one narration at a time.
AddWantedEditioncreates a separate row only when the book already has files on disk. With no files it pins the chosen edition in place, so picking a second narrator overwrites the first. That means a variant can only be created for a book you already partly own — you cannot say "I want both of these" from a clean library, which is the normal starting point.Observed on a book with 116 audiobook editions, selecting three narrators in turn:
Before, every selection returns the same
bookIdand the previous choice is gone. After, the three narrations coexist and the repeat request de-duplicates.What changed
AddWantedEditiontakes anasNewVariantflag, exposed on the API asAsNewVariant. When set, the no-files branch is skipped so the narration is tracked alongside the current selection rather than replacing it. It defaults tofalse, so every existing caller keeps the current behaviour — there is a test pinning that.De-duplication now also matches on narrator, not only on edition provider ids. Two printings of the same narration are distinct edition records but the same user request, so asking for a narrator already tracked returns the existing row instead of creating a near-duplicate. The base book is checked the same way, so no variant is created for a narration it is already pinned to. Names are compared on normalised tokens, because providers return inconsistent spacing — there are real records like
"Peter Noble"and"Frank Muller".The narrator modal now routes both cases through the endpoint. It previously reimplemented edition pinning client-side (
GET /editionthenPUT /book) for books with no files, which bypassed all of the above; deleting that branch removes the duplicated logic and is why the frontend diff is mostly red.On the file count
30 files sounds worse than it is: 25 are one-line signature updates to
IBookServicetest doubles. The real change is 3 source files plus tests.Deliberately not included:
Books.WantedNarratorIdis never assigned anywhere in the codebase — only nulled by housekeeping and migrations — so variants remain keyed on the edition. Connecting theNarratorstable to this flow looks like your call on the narrator entity's lifecycle rather than something to decide inside a bugfix-shaped change, so I left it alone. Happy to follow up if you want it.This is independent of #56 and can merge in either order. They complement each other: #56 makes releases actually match the pinned narrator, this makes more than one narration trackable at once.
Database Migration
NO.
How was this tested?
Unit tests — 4 added to
BookServiceWantedNarratorSeriesLinksFixture, each written first and observed failing for the right reason before implementing:should_create_a_separate_variant_for_a_second_narrator_when_the_book_has_no_filesshould_pin_in_place_without_creating_a_variant_when_not_requested— backward-compatibility guardshould_not_duplicate_the_base_book_when_it_already_wants_that_narratorshould_reuse_an_existing_variant_that_already_wants_the_same_narratorThe third caught a real duplicate-creation bug in my first implementation. Two unimplemented members on the fixture's in-memory doubles (
InMemoryBookRepository.Update,InMemoryEditionService.UpdateMany) were filled in, since these paths now exercise them.Debug and Release both clean;
dotnet build src/Chaptarr.NoTests.sln -c Releasereports 0 warnings, 0 errors. Thebuild.ymlguard steps pass too — conflict markers,package.jsonparse, andversion_guard.pyinsync,monotonicandcommit-hygienemodes. Frontendyarn typecheckpasses andeslintis clean on the changed file (worth stating explicitly, sincebuild.ymlruns no yarn steps).End-to-end, on Linux with .NET 10. Two containers from
chaptarr/chaptarr:0.9.929, identical config databases, differing only by the patched assemblies, driven throughPOST /book/{id}/editions/wanted. That produced the table above — before, one row that keeps getting overwritten; after:and the repeat request for Wincott through a different edition record returned
647rather than creating a fourth row.Screenshots (UI changes only)
No visual changes — the narrator modal looks and behaves the same; only the request it issues changed.