Skip to content

Fix download import pipeline for single-file torrents#350

Closed
zombopanda wants to merge 5 commits intoListenarrs:canaryfrom
zombopanda:fix/download-import-pipeline
Closed

Fix download import pipeline for single-file torrents#350
zombopanda wants to merge 5 commits intoListenarrs:canaryfrom
zombopanda:fix/download-import-pipeline

Conversation

@zombopanda
Copy link
Copy Markdown
Contributor

Summary

Fixes multiple issues causing single-file torrent downloads (e.g. from MyAnonamouse) to fail to import, or to catastrophically import the entire downloads directory instead of the target file.

  • Single-file torrent path resolution (QbittorrentAdapter.GetImportItemAsync): For single-file torrents the file name has no directory component, so subfolder was empty and outputPath resolved to save_path (the entire downloads directory). Now uses Path.Combine(savePath, fileName) for single-file torrents. Fixed in both overloads of the method.
  • Race condition (DownloadMonitorService): Was setting dl.Status = DownloadStatus.Completed immediately when a completion candidate was first observed, before the stability window expired. This allowed DownloadProcessingBackgroundService to pick up the download and start importing prematurely. Now only updates progress to 100% without changing status until after the stability window and finalization.
  • Path fallback (DownloadProcessingBackgroundService): Was using dl.FinalPath ?? dl.DownloadPath which resolved to save_path for single-file torrents. Now prefers ClientContentPath from download metadata (content_path = actual file/folder path).
  • Purge safety check (DownloadService): Skips orphan purge when the download client returns 0 queue items but active downloads exist, preventing accidental record deletion during transient client connectivity issues.

Test plan

  • Rebased on latest canary (0.2.48 + Prowlarr integration)
  • Built and deployed to homelab Docker container
  • Triggered download of single-file M4B torrent from MyAnonamouse
  • Verified DownloadMonitorService detects completion via cookie-enabled HttpClient
  • Verified stability window prevents premature processing (race condition fix)
  • Verified correct file path resolution (/data/downloads/file.m4b, not /data/downloads/)
  • Verified successful import to audiobook library (1 file imported, not entire directory)
  • Verified purge safety check present

🤖 Generated with Claude Code

@zombopanda zombopanda marked this pull request as ready for review February 20, 2026 08:18
@therobbiedavis
Copy link
Copy Markdown
Collaborator

@zombopanda Are you sure you checked against 0.2.48? There was an issue with the image package being private rather than public so anyone using the Github image url was not getting the latest version.

Fixes multiple issues causing single-file torrent downloads (e.g. from
MyAnonamouse) to fail to import or to catastrophically import the entire
downloads directory:

1. DownloadMonitorService cookie auth: The factory HttpClient
   "DownloadClient" has UseCookies=false, which drops the qBittorrent
   SID cookie. Create a local HttpClient with its own CookieContainer
   so the login cookie persists across requests.

2. Single-file torrent path resolution (QbittorrentAdapter): For
   single-file torrents the file name has no directory component, so
   subfolder was empty and outputPath resolved to just save_path (the
   entire downloads directory). Now use Path.Combine(savePath, fileName)
   for single-file torrents.

3. Race condition between completion and processing: DownloadMonitorService
   was setting status to Completed immediately when a candidate was first
   observed, before the stability window expired. This allowed
   DownloadProcessingBackgroundService to pick up the download too early.
   Now only update progress to 100% without changing status until after
   finalization.

4. DownloadProcessingBackgroundService path fallback: Was using
   dl.FinalPath ?? dl.DownloadPath which resolved to save_path for
   single-file torrents. Now prefers ClientContentPath from metadata
   (the torrent's content_path) which points to the actual file.

5. Purge safety check (DownloadService): Skip orphan purge when the
   download client returns 0 queue items but active downloads exist,
   preventing accidental deletion during transient connectivity issues.

6. EF Core version wildcards: Use 8.* for all EF Core packages to
   avoid NU1605 version mismatch warnings during restore.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@zombopanda zombopanda force-pushed the fix/download-import-pipeline branch from 3ff7ed8 to aa45756 Compare February 22, 2026 08:58
@zombopanda
Copy link
Copy Markdown
Contributor Author

Hey @therobbiedavis, yes — confirmed these issues are still present in both 0.2.48 and 0.2.49 (canary). I verified against the latest origin/canary (42ddbd1):

  1. Single-file torrent path (QbittorrentAdapter.cs:873-878): When pathParts.Length == 1, subfolder is empty and outputPath resolves to savePath (the entire downloads directory) instead
    of the actual file.
  2. Race condition (DownloadMonitorService.cs:998, 2028, 2357): All three pollers (qBittorrent, SABnzbd, NZBGet) set dl.Status = DownloadStatus.Completed immediately when a candidate is
    first observed, before the stability window expires. This lets DownloadProcessingBackgroundService pick up downloads too early.
  3. Path fallback (DownloadProcessingBackgroundService.cs:240): Uses dl.FinalPath ?? dl.DownloadPath which resolves to save_path for single-file torrents. Missing the ClientContentPath
    metadata fallback that points to the actual file.
  4. Purge safety (DownloadService.cs): No guard against orphan purge when the client returns 0 queue items but tracked downloads exist (transient connectivity issue).

zombopanda and others added 4 commits February 22, 2026 12:30
Playwright services were removed from the codebase but the Dockerfile
still tried to install Playwright via PowerShell, causing build failures.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When ApplicationSettings.OutputPath is not configured, the finalization
code fell back to "./completed" which resolves to /app/completed inside
the container — a path the process cannot write to. Now queries the
first configured root folder path as the fallback instead.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@therobbiedavis
Copy link
Copy Markdown
Collaborator

@zombopanda I am going to cherry pick this into a larger effort branch around download client behavior reliability, but this will be in the next release barring any major critical issues cropping up.

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.

2 participants