Skip to content

fix(importer): small follow ups to #3000, #3003 and #2988 - #3031

Merged
vavallee merged 4 commits into
mainfrom
fix/importer-small-followups
Oct 6, 2026
Merged

vavallee merged 4 commits into
mainfrom
fix/importer-small-followups

Conversation

@vavallee

@vavallee vavallee commented Oct 6, 2026 •

Copy link
Copy Markdown
Owner

Summary

Three small follow ups left by #3000, #3003 and #2988, one commit each.

Refs #2998, #2937, #2942.

Per item notes

Item From Change
Untagged EPUB beside a disallowed one #3000 (#2998) Reverted after review (8d0ad7ce). The first commit counted an undeclared EPUB (none, und, mul) as acceptable, which let a Swedish edition plus the same edition with stripped metadata import under an eng profile, the #2998 symptom. Net effect is main's rule, now pinned by tests and spelled out in the guide: an undeclared EPUB imports alone or beside an allowed one, and does not rescue a release whose only declared language is disallowed.
Legacy ISO 639-1 codes #3000 (#2998) NormalizeLanguageCode folds iw, in, ji onto he, id, yi (so heb, ind, yid). They used to pass through raw, count as no language, and relabel the book to iw. Every caller passes a language field value, never a title word, so in cannot be read off a release name.
Orphan row blocks adoption #3003 (#2937) New PathOwnedByLiveOtherBook (joins books, ignores a row whose book is gone). Used only in AdoptionHandler.checkOwnership and existingFileOwnedByOtherBook (the add author existing file bind), both guarding a write through Track, which already reclaims orphan rows. The delete guards in path_safety.go, reassign cleanup in manual_import.go and the reorganize collision check keep PathOwnedByOtherBook, where an orphan still counts as owned. The user guide line saying adoption still refuses such a file is updated. The #3003 line in the released v1.40.1 CHANGELOG section is left alone; the new fragment covers the change.
Placeholder author credits #2988 (#2942) isPlaceholderAuthor folds the tag first (brackets, dots, slashes, apostrophes dropped, other punctuation read as a space) and the single table gains "Author Unknown", "Unknown Artists" and the common audiobook publishers (Brilliance, Recorded Books, Tantor, Blackstone, Penguin, HarperAudio, Macmillan, Random House, Podium). The old v.a., v/a, n/a keys are now va, na.

Known, not changed here: under a restricted profile an imported und EPUB still relabels the book to und (relabelLanguage falls back to meta.Language), the same as a lone und EPUB does today. #3000 already lists the any language variant of this as a follow up.

Checklist

  • Tests added or updated
  • Doc-update gate cleared (docs/User-Guide-Wiki.md, godoc on the new symbol)
  • Wiki pages updated if user-facing behaviour changed

Test plan

Fail before (new tests against origin/main sources):

  • TestLanguageEnforcement_UndeclaredFileBesideDisallowedBlocks (none, und, mul), against the first commit: download status = "imported", want "importBlocked". Passes on main and after 8d0ad7ce.
  • TestLanguageEnforcement_UndeclaredFileBesideAllowedImports (undeclared + en + sv): passes before and after, a guard. One EPUB is placed per book, so the undeclared file is named first to show it is taken, not skipped.
  • TestLanguageEnforcement_TwoLetterCodesOutsideTheOldTable new iw/in/ji cases: "imported", want "importBlocked"
  • TestNormalizeLanguageCode: NormalizeLanguageCode("iw") = "iw", want "heb", and the in/ji cases
  • TestAdopt_FileLeftByADeletedBookIsAdopted: adopt = 409 ... already belongs to a book in your library
  • TestHandleNewWantedBook_BindsAFileLeftByADeletedBook: declined the bind
  • TestIsPlaceholderAuthor: 17 spellings returned false, and "Brilliance Audio" raised a conflict

Guards that pass before and after: TestAdopt_FileOfALiveBookIsStill409, TestHandleNewWantedBook_DoesNotBindAnotherBooksFile, the real name half of TestIsPlaceholderAuthor. TestBookFileRepo_PathOwnedVariantsOnAnOrphanRow pins both variants (new symbol, so it cannot run on main).

Ran:

  • gofmt -l, go vet ./...
  • golangci-lint run v2.11.4 on internal/importer, internal/api, internal/db, internal/models: 0 issues
  • go test -timeout 40m ./internal/importer/ ./internal/api/ ./internal/db/ ./internal/models/: all ok (before 8d0ad7ce)
  • after 8d0ad7ce: go test -timeout 40m ./internal/importer/ -run TestLanguageEnforcement, ./internal/models/, golangci-lint on internal/importer: ok, 0 issues
  • go test ./cmd/... ./internal/...: one run under load average 40 to 60 from other sessions. Everything passed except timing failures in code this PR does not touch, each passing when rerun alone: internal/api hit the 10m timeout (plus TestAddBook_OrphanAuthorDeletedOnTimeout got a 500), and internal/duplicates TestScanScale took 160ms against a 100ms budget. Not rerun in full on an idle machine.
  • go test -race
  • make smoke

🤖 Generated with Claude Code

https://claude.ai/code/session_016fJcCVbNnKsmj2MAAMwWj9

vavallee and others added 3 commits October 5, 2026 14:44
…ne (#2998)

The language gate counted an EPUB declaring no specific language (none,
und, mul) as neither allowed nor disallowed, so a release holding one
plus a Swedish EPUB under an English only profile was rejected whole,
although the untagged file alone imports. It now counts as acceptable:
it is imported and the Swedish one skipped, the shape of the format gate.

NormalizeLanguageCode also folds the withdrawn ISO 639-1 codes iw, in
and ji onto he, id and yi. They used to pass through raw, read as no
language, and relabel the book to "iw".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016fJcCVbNnKsmj2MAAMwWj9
Signed-off-by: vavallee <vavallee@protonmail.com>
…2937)

PathOwnedByOtherBook counts a book_files row whose book no longer exists
as owned. That is right for the delete guards, but adoption's pre-check
and the existing file bind on add author used it too, so adoption gave a
409 naming no book and add author searched for a file it already had,
while the write behind both (Track) would take the orphan row over.

Add PathOwnedByLiveOtherBook, which joins books and ignores an orphan
row, and use it only in AdoptionHandler.checkOwnership and
existingFileOwnedByOtherBook. The delete guards (path_safety.go),
reassign cleanup (manual_import.go) and the reorganize collision check
keep the orphan counts as owned behaviour.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016fJcCVbNnKsmj2MAAMwWj9
Signed-off-by: vavallee <vavallee@protonmail.com>
…credits (#2942)

isPlaceholderAuthor only trimmed outer brackets and folded case, so
"Various Artists.", "Various Authors;", "Unknown Author(s)", "Full-Cast"
and "Anon." read as authors, and publisher credits such as "Brilliance
Audio", "Recorded Books", "Tantor Audio" or "HarperAudio" were not on the
list at all. Each raised an author conflict with the folder and an Add
author for nobody.

Fold the tag before comparing (brackets, dots, slashes and apostrophes
dropped, other punctuation read as a space) and extend the single table
with "Author Unknown" and the common audiobook publishers and imprints.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016fJcCVbNnKsmj2MAAMwWj9
Signed-off-by: vavallee <vavallee@protonmail.com>
@codecov

codecov Bot commented Oct 6, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 85.18519% with 4 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
internal/db/book_files.go 80.00% 1 Missing and 1 partial ⚠️
internal/db/books.go 0.00% 2 Missing ⚠️

📢 Thoughts on this report? Let us know!

…ne (#2998)

Review of the first commit: counting an undeclared (none, und, mul) EPUB
as acceptable let a release of a Swedish edition plus the same edition
with its metadata stripped import under an English only profile, be
marked done and never searched again, the #2998 symptom.

An undeclared EPUB now imports on its own or beside an EPUB in an allowed
language, as on main, and no longer rescues a release whose only
declared language is disallowed. The allowed plus disallowed skip path
from #3000 is unchanged. The iw/in/ji normalisation stays.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016fJcCVbNnKsmj2MAAMwWj9
Signed-off-by: vavallee <vavallee@protonmail.com>
@vavallee
vavallee marked this pull request as ready for review October 6, 2026 17:52
@vavallee
vavallee enabled auto-merge (squash) October 6, 2026 17:52

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Three independent fixes, all look correct.

Legacy ISO codes (internal/models/language.go:186): lookup sits correctly inside the len(code) == 2 block that runs after subtag stripping, so iw-IL strips to iw then maps iw -> he -> heb. ji -> yi -> yid confirmed in the table.

PathOwnedByLiveOtherBook (internal/db/book_files.go:388): SQL JOIN correctly excludes orphan rows. The pre-check/write pairing is sound: Track atomically reclaims an orphan when !ownerTitle.Valid (line 164), matching the pre-check returning false. Conservative callers (delete guards in path_safety.go:289,322, manual_import.go:459, reorganize.go:202) are untouched. The TOCTOU window between the second ownership check (line 346) and registerFile is covered by catching PathOwnedError at line 352.

isPlaceholderAuthor / placeholderKey (internal/importer/unmatched_evidence.go:110): key transformations are consistent (v.a. folds to va, n/a to na, semicolons become a space that Fields collapses). Test covers false-positive risk for real names (Penguin, Harper Lee, Cast, Unknown Soldier) -- all pass through.

Language enforcement: comment-only cleanup after the revert, no logic change.

One note, non-blocking: bookFileOwner interface in path_safety.go:362 declares only PathOwnedByOtherBook. The new method is used through the concrete db.BookRepo in both new call sites, so there is no compilation issue; the interface covers only the delete-guard path and does not need extending.

--- Bindery triage bot (automated). Reply to correct me; a human will see it.

@vavallee
vavallee merged commit a9d9970 into main Oct 6, 2026
39 of 41 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant