mirror of
https://github.com/nimbold/Firelink.git
synced 2026-07-26 12:08:27 +00:00
fix(downloads): reconcile lost aria2 completion events
This commit is contained in:
+67
-2
@@ -3,7 +3,7 @@
|
||||
// Learn more about Tauri commands at https://tauri.app/develop/calling-rust/
|
||||
use regex::Regex;
|
||||
use serde::Serialize;
|
||||
use std::collections::{BTreeSet, HashMap, VecDeque};
|
||||
use std::collections::{BTreeSet, HashMap, HashSet, VecDeque};
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::path::PathBuf;
|
||||
use std::sync::OnceLock;
|
||||
@@ -6204,7 +6204,8 @@ pub fn run() {
|
||||
let params = serde_json::json!([["gid", "status", "totalLength", "completedLength", "downloadSpeed", "connections", "errorMessage"]]);
|
||||
if let Ok(active_list) = rpc_call(poll_port.load(std::sync::atomic::Ordering::Relaxed), &poll_secret, "aria2.tellActive", params).await {
|
||||
if let Some(active_arr) = active_list.as_array() {
|
||||
let mut seen_ids = std::collections::HashSet::new();
|
||||
let mut seen_ids = HashSet::new();
|
||||
let mut seen_gids = HashSet::new();
|
||||
for status_info in active_arr {
|
||||
let gid = status_info.get("gid").and_then(|s| s.as_str()).unwrap_or("");
|
||||
let id = poll_mgr
|
||||
@@ -6215,6 +6216,7 @@ pub fn run() {
|
||||
.map(|mapping| mapping.id.clone());
|
||||
if let Some(id) = id {
|
||||
seen_ids.insert(id.clone());
|
||||
seen_gids.insert(gid.to_string());
|
||||
let status = status_info.get("status").and_then(|value| value.as_str()).unwrap_or("");
|
||||
let total = status_info.get("totalLength").and_then(|s| s.as_str()).unwrap_or("0").parse::<u64>().unwrap_or(0);
|
||||
let completed = status_info.get("completedLength").and_then(|s| s.as_str()).unwrap_or("0").parse::<u64>().unwrap_or(0);
|
||||
@@ -6319,6 +6321,69 @@ pub fn run() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A completion notification can be lost while the
|
||||
// WebSocket is reconnecting. Completed GIDs also
|
||||
// disappear from tellActive, so reconcile mapped
|
||||
// GIDs that were not present in this active list.
|
||||
// Paused and waiting downloads are intentionally
|
||||
// left alone; only terminal statuses are applied.
|
||||
for (gid, id) in poll_mgr.aria2_gid_mappings() {
|
||||
if seen_gids.contains(&gid) {
|
||||
continue;
|
||||
}
|
||||
let status = match rpc_call(
|
||||
poll_port.load(std::sync::atomic::Ordering::Relaxed),
|
||||
&poll_secret,
|
||||
"aria2.tellStatus",
|
||||
serde_json::json!([gid, ["status", "errorCode", "errorMessage"]]),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(status) => status,
|
||||
Err(error) => {
|
||||
log::debug!(
|
||||
"aria2 poller reconciliation [{}]: could not query gid {}: {}",
|
||||
id,
|
||||
gid,
|
||||
error
|
||||
);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let status_name = status
|
||||
.get("status")
|
||||
.and_then(|value| value.as_str())
|
||||
.unwrap_or("");
|
||||
let outcome = match status_name {
|
||||
"complete" => Some(crate::queue::PendingOutcome::Complete),
|
||||
"error" | "removed" => {
|
||||
let error_code = status
|
||||
.get("errorCode")
|
||||
.and_then(|value| value.as_str())
|
||||
.filter(|value| !value.is_empty());
|
||||
let error_message = status
|
||||
.get("errorMessage")
|
||||
.and_then(|value| value.as_str())
|
||||
.filter(|value| !value.is_empty())
|
||||
.unwrap_or("aria2 download ended outside the active poll");
|
||||
Some(crate::queue::PendingOutcome::Error(match error_code {
|
||||
Some(code) => format!("aria2 error code {code}: {error_message}"),
|
||||
None => error_message.to_string(),
|
||||
}))
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
if let Some(outcome) = outcome {
|
||||
log::info!(
|
||||
"aria2 poller reconciliation [{}]: gid {} reported terminal status {} outside tellActive",
|
||||
id,
|
||||
gid,
|
||||
status_name
|
||||
);
|
||||
poll_mgr.handle_aria2_event(&gid, outcome).await;
|
||||
}
|
||||
}
|
||||
observations.retain(|id, _| seen_ids.contains(id));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user