From a009b8773bb21d5333901472d96e627857babcd3 Mon Sep 17 00:00:00 2001 From: bugrax Date: Sat, 18 Jul 2026 12:52:05 +0300 Subject: [PATCH] fix: pull unmanaged active transfers from watch folders Manually added put.io transfers (e.g. a Watch List entry, or any transfer whose local state was lost) never reached the local downloader. They fell through both code paths: - the main transfer loop skips unmanaged transfers, and - scan_watch_folders skipped every file whose id belonged to an *active* transfer, regardless of whether that transfer was managed. As a result an unmanaged transfer that was still active on put.io was ignored by the loop and excluded from the orphan scan, so its completed files were never downloaded or imported. scan_watch_folders now only excludes files backed by a *managed* active transfer. Files from unmanaged active transfers are treated as orphans and routed to the correct category (radarr/sonarr) like any other orphaned watch-folder file. --- src/download_system/transfer.rs | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/src/download_system/transfer.rs b/src/download_system/transfer.rs index c27dbc3..23b655e 100644 --- a/src/download_system/transfer.rs +++ b/src/download_system/transfer.rs @@ -461,7 +461,19 @@ async fn scan_watch_folders( return; } let api_key = &app_data.config.putio.api_key; - let active_file_ids: HashSet = active_transfers.iter().filter_map(|t| t.file_id).collect(); + // Only files backed by a *managed* active transfer are handled the normal + // way by the main loop. Unmanaged active transfers (e.g. a manually added + // put.io Watch List entry, or one whose state was lost) are skipped there, + // so their completed files must be picked up here as orphans — otherwise + // both paths ignore them and they are never downloaded/imported. + let mut managed_active_file_ids: HashSet = HashSet::new(); + for t in active_transfers { + if let Some(file_id) = t.file_id { + if is_managed(app_data, t).await { + managed_active_file_ids.insert(file_id); + } + } + } for folder_id in &app_data.config.watch_folders { let resp = match putio::list_files(api_key, *folder_id).await { @@ -477,11 +489,13 @@ async fn scan_watch_folders( if file.id < 0 || !matches!(file.file_type.as_str(), "FOLDER" | "VIDEO" | "AUDIO") { continue; } - // Skip the result of an active transfer (handled the normal way) and - // any orphan we're already pulling. Using `has_orphan` as the - // "in progress" marker keeps tracking bounded and, since a failed - // orphan is dropped from it, lets a later poll retry it. - if active_file_ids.contains(&file.id) || app_data.state.has_orphan(file.id).await { + // Skip the result of a *managed* active transfer (handled the normal + // way) and any orphan we're already pulling. Files from unmanaged + // active transfers fall through the main loop, so they are handled + // here. Using `has_orphan` as the "in progress" marker keeps tracking + // bounded and, since a failed orphan is dropped from it, lets a later + // poll retry it. + if managed_active_file_ids.contains(&file.id) || app_data.state.has_orphan(file.id).await { continue; }