fix(paths): harden imports, moves, and root relocation#717
fix(paths): harden imports, moves, and root relocation#717therobbiedavis wants to merge 372 commits into
Conversation
There was a problem hiding this comment.
Pull request overview
This PR fixes download/import path handling so client-reported torrent paths preserve meaningful leading/trailing whitespace (important on Unix-like filesystems), while adding stricter, OS-aware validation for Listenarr-owned destination paths (root folders, add-to-library, and move operations).
Changes:
- Preserve whitespace in torrent client path mapping and remote path translation by avoiding whitespace-trimming/whitespace-only rejection in key path plumbing.
- Add OS-aware normalization/validation for user-provided destination directories, including explicit support for filesystem roots in root-folder configuration and blocking parent traversal for concrete destinations.
- Add regression tests covering whitespace-bearing torrent folders (Transmission/qBittorrent), root-folder root paths, and invalid destination rejection in library add/move workflows.
Reviewed changes
Copilot reviewed 20 out of 20 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/Mocks/Api/TransmissionApiMock.cs | Adds a canned Transmission torrent response with whitespace-bearing folder names for regression coverage. |
| tests/Features/Infrastructure/DownloadClients/Common/TorrentClientPathMapperTests.cs | New tests asserting whitespace preservation and rooted-child handling for torrent file mapping. |
| tests/Features/Infrastructure/DownloadClients/Common/DownloadClientAdapterTests.cs | Adds Transmission adapter regression asserting whitespace-bearing ContentPath/SourceFiles are preserved. |
| tests/Features/Domain/Utils/FileUtilsTests.cs | Adds tests for whitespace-preserving combining/normalization and new destination-path validation rules. |
| tests/Features/Application/Downloads/Common/DownloadClientGatewayTests.cs | Adds tests ensuring remote path mapping and directory expansion keep whitespace-bearing paths intact. |
| tests/Features/Application/Audiobooks/RootFolders/RootFolderServiceTests.cs | Adds coverage for allowing filesystem roots and validating/normalizing root folder paths. |
| tests/Features/Api/Features/Library/LibraryController_MoveTests.cs | Adds API test ensuring invalid move destinations are rejected with 400. |
| tests/Features/Api/Features/Library/LibraryController_AddToLibraryTests.cs | Updates custom path test to be OS-safe and adds parent-traversal/invalid-path rejection tests. |
| listenarr.infrastructure/DownloadClients/Transmission/TransmissionImportPathResolver.cs | Stops treating whitespace-bearing strings as “empty” when building content/source paths. |
| listenarr.infrastructure/DownloadClients/Qbittorrent/QbittorrentImportPathResolver.cs | Same as above for qBittorrent source file translation. |
| listenarr.infrastructure/DownloadClients/Common/TorrentClientPathMapper.cs | Reworks torrent path mapping to avoid trimming path-segment whitespace and better handle rooted-looking child paths. |
| listenarr.infrastructure/Configuration/Paths/RemotePathMappingService.cs | Adjusts empty-path detection to avoid rejecting whitespace-bearing remote paths. |
| listenarr.domain/Common/FileUtils.UserProvidedPaths.cs | New OS-aware user-provided directory path validation/normalization helper with root/traversal options. |
| listenarr.domain/Common/FileUtils.PathCombining.cs | Documents CombineWithOptionalBase’s whitespace-preserving intent and relaxes base-path emptiness check. |
| listenarr.application/Downloads/Common/DownloadClientGateway.cs | Preserves whitespace-bearing paths during mapping/translation and directory-expansion flows. |
| listenarr.application/Audiobooks/RootFolders/RootFolderService.cs | Root folder create/update now uses OS-aware normalization and allows filesystem root paths. |
| listenarr.application/Audiobooks/Contracts/ILibraryAddService.cs | Extends result contract to surface validation failures cleanly to API workflows. |
| listenarr.application/Audiobooks/Catalog/LibraryAddService.cs | Validates custom/generated destination paths with OS-aware normalization and blocks parent traversal. |
| listenarr.api/Features/Library/LibraryMoveWorkflow.cs | Validates and normalizes move destinations (blocking traversal) instead of silently normalizing invalid targets. |
| listenarr.api/Features/Library/LibraryAddWorkflow.cs | Returns 400 for validation failures and validates custom destination paths via OS-aware normalization. |
|
Temporary formatter/compiler diagnostic superseded and cleaned up. |
3 similar comments
|
Temporary formatter/compiler diagnostic superseded and cleaned up. |
|
Temporary formatter/compiler diagnostic superseded and cleaned up. |
|
Temporary formatter/compiler diagnostic superseded and cleaned up. |
|
Temporary verbose Windows test diagnostic superseded by compact output. |
Temporary Windows test diagnosticsExit code: |
Temporary Windows test diagnosticsExit code: |
Temporary Windows test diagnosticsExit code: |
Temporary Windows test diagnosticsExit code: |
|
Was going to use Listenarr to reorganize my files, this appears to be a solution to many of the problems I got in testing. Is there a release schedule that will include this PR? |
Summary
This PR hardens Listenarr's filesystem path handling across download imports, manual/library destinations, durable audiobook moves, root-folder relocation, metadata-only path repair, scanning, and frontend destination editing.
The main rule this branch enforces is:
It also replaces fragile BasePath pre-save behavior with durable move orchestration, adds root-folder relocation workflows, restores EF migration discoverability/schema coverage from PR #727, and adds guardrails so future path/migration regressions fail tests instead of silently shipping.
Added
PUT /library/{id}compatibility path.Changed
BasePath, so metadata is updated only after move completion or through explicit metadata-only rewrite paths.PUT /library/{id}BasePath changes through the same destination rewrite service and prevents stale full-object payloads from undoing rewritten path fields.basePath,imageUrl, or file-path fields.NeedsAttentionand continuing recovery.Fixed
BasePathis unavailable.BasePathto the author folder.ProcessingvsRunning/NeedsAttention/Superseded) and modal-local subscriptions being torn down before background updates arrive.AddMoveJobSourcePathdesigner, adding the missingProcessExecutionLogsmigration, regenerating durable move migrations via EF tooling, and adding schema coverage.Removed / replaced
Testing
Validated locally on the final branch state with:
cd fe && npx vitest run EditAudiobookModal.moveOptions.spec.ts moveJobs.store.spec.ts --reporter=dot --silent=truecd fe && npm run test:unitcd fe && npm run builddotnet test tests/Listenarr.Tests.csproj --no-restore --verbosity minimal --filter "FullyQualifiedName~EfMoveQueuePersistenceTests|FullyQualifiedName~MoveQueueServiceTests|FullyQualifiedName~LibraryController_MoveTests" -p:OutDir=C:/Users/local_oqoz17v/AppData/Local/Temp/listenarr-test-out/dotnet test tests/Listenarr.Tests.csproj --no-build --verbosity minimal --filter "FullyQualifiedName~BackendArchitectureTests|FullyQualifiedName~HostedServicesRegistrationTests.BackgroundWorkerOwnership_DocumentsEveryHostedService"cd fe && npx prettier --check src/__tests__/EditAudiobookModal.moveOptions.spec.ts src/__tests__/moveJobs.store.spec.ts src/components/domain/audiobook/EditAudiobookModal.vue src/stores/moveJobs.tsgit diff --check origin/canary...HEADAlso previously validated the broader backend suite during review; the only temp-output failures were repo-root fixture/doc lookup tests, which passed when rerun from the normal repository output.
Issues and related PRs
Notes
dotnet ef migrations add; do not hand-author or hand-split migration files.20260702200000./booksor/downloads.