Skip to content

fix(import): accept a same-work different-media-type pocket match for a completed download - #213

Open
jordanfelle wants to merge 4 commits into
Chaptarr:developfrom
jordanfelle:fix-same-work-media-type-pocket-match
Open

jordanfelle wants to merge 4 commits into
Chaptarr:developfrom
jordanfelle:fix-same-work-media-type-pocket-match

Conversation

@jordanfelle

@jordanfelle jordanfelle commented Sep 26, 2026 •

Copy link
Copy Markdown

Depends on the parity-retry PR (fix-auto-import-local-preview-parity); it is stacked on that branch.

Problem

EnforceTrackedDownloadTargetBooks rejects any match whose BookId differs from a grabbed target book. That blocks audio downloads whose files correctly resolve to the audiobook pocket of a work that was grabbed against its ebook pocket ("grabbed for 'The Vines' (BookId 166297, work hc:514913), but import matched 'The Vines' (BookId 290880, work hc:514913)"). RetargetSameWorkMatchesToGrabbedBook cannot help: it deliberately requires identical MediaType, because audio files must not be filed under the ebook row.

Fix

An off-target match is accepted when all off-target files resolved to the same book row (more than one stays an ambiguous multi-book rejection), that row has the same author as exactly one grabbed target book, shares a canonical work provider id with it (WorkIdMatcher.WorkProviderIdMatches, work-level ids only), and its MediaType matches the media type of the files. Different works, different authors and one-sided work ids keep being rejected.

Verification

  • New DownloadedBooksImportService tests for each accepted/rejected combination.
  • Core tests pass (3,032 with the parity PR underneath).
  • Live: confirmed on a live instance against "grabbed for X but import matched Y" queue items.

The rule requires the grabbed target to have a different media type than the matched row: a same-format sibling row (for example another narrator's audiobook) is handled by the existing retarget step and is not accepted here, including in multi-book grabs.

  • Two tests in DownloadedBooksImportServiceFixture: a same-media-type sibling in a multi-book grab is rejected (fails without the fix), and a different-media pocket is still accepted in a multi-book grab (3,034 core tests pass).

…g context

Automatic download import blocked files that the Manual Import screen resolves
locally on sight ("No match in local library. Suggested author: X (hc:NNN)."
while GET /api/v1/manualimport returns a local editionId and no rejections).

The two paths differ in exactly one matching flag. ProcessFolder gates
path/filename evidence behind ShouldAllowTrackedDownloadPathFallback
(DownloadedBooksImportService.cs:1552), which requires a tracked download with
a resolved single author AND exactly one grabbed target book. A completed
download with no usable grabbed identity therefore matches with
DisablePathFallback=true, so HolyGrailEvaluation returns early for a file whose
embedded tags carry nothing searchable (FileMatchingService.cs:4328) and never
runs the supplemental path pass (FileMatchingService.cs:4426). The preview's
MatchingContextPresets.ForManualPreview() defaults allowPathFallback to true,
so the same file resolves. Everything else in the two contexts is identical,
and both build path tokens through the same BuildPathDerivedTags/SplitTokens
helpers, so underscored folder names ("Galileo_And_The_Solar_System") were
never the problem - underscores are already separators.

Fix: after the strict pass (and after the author-auto-add rematch), re-run
only the still-unmatched files with ForDownloaded(false, targetBookIds,
allowPathFallback: true) - flag-for-flag the preview's context minus provider
identification, which the second pass does not need. Rejected alternatives:
widening ShouldAllowTrackedDownloadPathFallback would also loosen the first
pass for files that did match on weaker evidence, and lowering thresholds was
out of the question. This keeps the strict pass authoritative and only adds a
fail-open retry where today's outcome is an unconditional block.

Trade-off: path evidence now reaches the library for downloads with no grabbed
identity, which is strictly more permissive than before. It is bounded by the
same guards the preview already relies on - the matcher's own smoke test and
author/title proofs are untouched, UsePathAsTagsFallback and Strict strictness
still disable it, the author restriction and grabbed target books carry over
(EnforceTrackedDownloadTargetBooks still rejects off-target matches), no author
is imported, and a multi-file leftover set resolving to more than one book is
discarded as ambiguous rather than guessed at.

Tests: new AutoImportLocalPreviewParityFixture reproduces the Galileo case
against FileMatchingService (strict context blocks, parity/preview contexts
match, near-miss folder name stays unmatched, same-title twin is decided by
author evidence) plus three DownloadedBooksImportService tests for the second
pass, the ambiguity discard, and the no-extra-pass case.
jordanfelle added a commit to jordanfelle/chaptarr that referenced this pull request Sep 26, 2026
…ftover sets

The parity pass also ran for a folder with no download client item, where nothing bounds the path evidence (no grabbed release, no author restriction) and no one reviews the result. It now only runs for a tracked download.

A multi-file leftover set that matched only in part was imported for the matching files while the rest were rejected, leaving the edition partially filled so a later attempt to add the remainder failed with 'Edition already has files'. Path evidence is folder-level, so a genuine match covers every leftover file; a partial match is now discarded like an ambiguous one.
…download

EnforceTrackedDownloadTargetBooks rejected any match whose BookId differed from
a grabbed target book, which blocked audio downloads whose files correctly
resolve to the audiobook pocket of a work that was grabbed against its ebook
pocket ("grabbed for 'The Vines' (BookId 166297, work hc:514913), but import
matched 'The Vines' (BookId 290880, work hc:514913)"). RetargetSameWorkMatches-
ToGrabbedBook cannot help here: it deliberately requires identical MediaType,
because audio files must not be filed under the ebook row.

An off-target match is now accepted when all off-target files resolved to the
same book row (more than one stays an ambiguous multi-book rejection), that row
has the same author as exactly one grabbed target book, shares a canonical work
provider id with it (WorkIdMatcher.WorkProviderIdMatches, work-level ids only),
and its MediaType matches the media type of the files themselves. Different
works, different authors and one-sided work ids keep being rejected.
IsSameWorkMediaPocketMatch only compared the matched row's media type with the files, so for a multi-book grab (where RetargetSameWorkMatchesToGrabbedBook is skipped) a same-format sibling row of the grabbed work, for example another narrator's audiobook row, was accepted as a pocket. Require the grabbed target to have a different media type than the matched row.
@jordanfelle
jordanfelle force-pushed the fix-same-work-media-type-pocket-match branch from 5e7aee6 to 761c62f Compare September 26, 2026 21:52
jordanfelle added a commit to jordanfelle/chaptarr that referenced this pull request Sep 26, 2026
jordanfelle added a commit to jordanfelle/chaptarr that referenced this pull request Sep 26, 2026
jordanfelle added a commit to jordanfelle/chaptarr that referenced this pull request Sep 27, 2026
jordanfelle added a commit to jordanfelle/chaptarr that referenced this pull request Sep 27, 2026
jordanfelle added a commit to jordanfelle/chaptarr that referenced this pull request Sep 27, 2026
jordanfelle added a commit to jordanfelle/chaptarr that referenced this pull request Sep 27, 2026
jordanfelle added a commit to jordanfelle/chaptarr that referenced this pull request Sep 27, 2026
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