Repository navigation
Conversation
…lently Item 269's containment fix holds (companions never escape the right root), but item 269/21's round-25 live measurement against a real multi-book library found a second defect this fix does not cover: two companions nested under a subdirectory of the selected file vanished with zero log output, across a 37-line import window, with only an aggregate "N imported companion file(s)" count as a trace. Root cause: the companion-file candidate scan in ImportWithOutcomeAsync uses SearchOption.TopDirectoryOnly, so a file sitting one or more directories below the selected file's directory is never enumerated as a candidate at all. It never reaches the per-file loop, so it never reaches any of this importer's existing LogWarning calls -- the drop happens before any of them run. LogNestedCompanionCandidatesSkipped re-walks the same selected directories recursively, after companionFiles is computed, purely to find what that flat scan left out, and logs a warning naming the file and the directory it is nested under. It does not change what gets imported or placed -- only what gets said about what did not. Split into a new partial-class file (ManualImportCompanionImporter. CompanionDiscovery.cs) to keep ManualImportCompanionImporter.cs at the 500-line ceiling BackendArchitectureTests.ActiveProductionSourceFiles_ RemainFocused enforces once this merges with Listenarrs#832's rootFolders changes, which this item's branch is built against (stack.items). An earlier, unsplit draft of this fix tripped that ceiling at 560 lines; the split version lands at exactly 500 in the merged tree. TDD, measured against this branch merged with Listenarrs#832 (the shape the stack tool builds and the shape production runs, since this branch alone does not carry rootFolders and does not compile its test project standalone -- pre-existing, documented in stack.items and in PR Listenarrs#1022's own text): ImportAsync_CompanionNestedUnderASubdirectory_LogsWhyItWasSkipped reproduces the drop first (red: Assert.Single found an empty collection), then confirms the warning fires with the file path and the selected directory named (green). ManualImportCompanionPlacementTests gained a RecordingLogger so a test can assert on what was actually logged, replacing NullLogger there. Full native suite on the merged tree, with this fix applied: 6156 total, 6023 passed, 0 failed, 133 skipped. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
A chapter-per-file book named like "Title 001 of 006", with no album tag, came back from Library Import as one scan item per file. ExtractTitleStem now strips a trailing "N of M" index before the leading-track strip, so the files share a stem and group into one item. Originally Listenarrs#845. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A manual import with includeCompanionFiles dropped every companion and still reported success: the companion pass handed the ownership store the book folder as its boundary, and the authorizer requires a configured root folder. The boundary is now selected over the root folders the same way the audio file's own import does, and a companion with no boundary marks the pass failed instead of throwing. Originally Listenarrs#832 (first half). Fixes Listenarrs#831 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Once companions could arrive, they landed in the wrong place. Automatic import relativized them against the common directory of the temp extraction dir and the download dir, which on the default container layout is "/", so both source trees were rebuilt inside the book folder. Manual import rebuilt the caller's browse path the same way. Both paths now share one placement rule: mirror only under the root a companion came from, otherwise beside the audio imported from its own directory, otherwise refuse it as a skip. Originally Listenarrs#832 (second half). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…probe An audiobook in a bare .mp4 container could not be imported, though the bytes are often the same MP4/AAC an .m4b carries. .mp4 stays out of AudioExtensions; a second, probed tier admits it in manual import and at registration only when ffprobe finds an audio stream and no playable video (cover art does not count). The library scanner stays extension-only. Originally Listenarrs#999. Refs Listenarrs#890, Listenarrs#995 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
af6ebd4 to
049f162
Compare
|
I rebased this bundle onto current canary ( I also replaced the example titles in the chapter-grouping tests and in one code comment with public-domain ones. The tests keep the properties they were written for: the Book/Volume carve-out tests still fail when the carve-out is removed, and the chapter-rip tests still fail on canary without the fix. Backend suite, full and unfiltered: 3936 passed, 0 failed, 152 skipped on the new head; 3880 passed, 0 failed, 152 skipped on canary The question from earlier still stands. #993 closed #831 and brought its own configured-root boundary fix, so I think the production half of I'm updating the PR description to match. Disclosure: drafted with Claude Code at my direction; I read the cited code at the stated commit and reviewed this before posting. |
This bundle collects fixes from three of my PRs (#845, still open, and #832 and #999, now closed), all about files reaching the library correctly. It is one commit per fix, rebased onto canary
e1a1d1270(v1.5.9). Any fix can be dropped or cherry-picked on its own, except that the two #832 commits go together.4a03068267f3091fe4f6171badc049f162dd.mp4after an ffprobe content checkWhat each one fixes
ExtractTitleStemdid not strip a trailing "001 of 006", so a chapter-per-file book with no album tag came back from Library Import as one item per file. The fix strips a trailingN of Mbefore the leading-track strip (the order matters: a bare001 of 498would otherwise lose its001first). An index introduced by Book, Bk, Vol or Volume is left alone, since "Book 1 of 6" usually means one work in a boxed set.BoundaryPath, which I think already carries the configured root, so the production half of this commit is probably redundant now. I have asked in a comment whether to keep it, cut it to the tests, or drop it./and a companion was mirrored under its whole absolute path inside the book folder. On manual import, a browse folder above the files was rebuilt inside the book folder. A companion now goes under the extraction root it came from, otherwise beside the audio imported from its own directory, otherwise it is refused (aSkippedresult on automatic import, not a job failure)..mp4in manual import. Every gate compares the extension withFileUtils.AudioExtensions, so an.mp4is refused even when its bytes match an.m4b..mp4stays out of that list, because it carries films just as well. A second tier admits it only when ffprobe finds an audio stream and no playable video stream; embedded cover art does not count as video.IsAudioFileis unchanged, so the scanner never probes.Evidence
Full unfiltered backend suite, including
BackendArchitectureTests:049f162dde1a1d1270The 56 extra tests all come from the bundle. I did not re-run vue-tsc or vitest, since the bundle does not touch
fe/.Controls, run on the rebased series (each discriminating test must fail without its fix and pass with it):
BoundaryPathrestored.mp4probe, parent plus the new testsThe boundary failure with canary's line comes from the fixture: the test feeds a resolution carrying the book folder, which the controller never produces. So it does not show a user-visible bug on canary.
Not covered
.mp4.Behaviour changes
FfmpegService.Probingnow accepts a.mp4target for any caller.PlanUniqueAsyncsuffixes the second.If you'd rather take any of these as separate PRs, I'll split them back out.
Disclosure: drafted with Claude Code at my direction; I read the cited code at the stated commit and reviewed this before posting.