mirror of
https://github.com/nimbold/Firelink.git
synced 2026-08-07 01:44:01 +00:00
feat(torrents): add aggregate upload limit control
This commit is contained in:
@@ -462,6 +462,8 @@ pub struct PersistedSettings {
|
||||
pub approved_download_roots: Vec<String>,
|
||||
pub max_concurrent_downloads: usize,
|
||||
pub global_speed_limit: String,
|
||||
#[serde(default)]
|
||||
pub torrent_overall_upload_limit: String,
|
||||
pub speed_limit_preset_values: Vec<f64>,
|
||||
pub logs_enabled: bool,
|
||||
pub is_sidebar_visible: bool,
|
||||
|
||||
+74
-4
@@ -6609,6 +6609,18 @@ pub(crate) fn normalize_speed_limit_for_aria2(limit: &str) -> Option<String> {
|
||||
})
|
||||
}
|
||||
|
||||
fn normalize_torrent_overall_upload_limit(limit: Option<&str>) -> Result<Option<String>, String> {
|
||||
let Some(limit) = limit else {
|
||||
return Ok(None);
|
||||
};
|
||||
if limit.trim().is_empty() {
|
||||
return Ok(None);
|
||||
}
|
||||
normalize_speed_limit_for_aria2(limit)
|
||||
.map(Some)
|
||||
.ok_or_else(|| "Torrent overall upload limit is invalid".to_string())
|
||||
}
|
||||
|
||||
fn apply_aria2_torrent_peer_discovery_options(
|
||||
command: &mut std::process::Command,
|
||||
enable_dht: bool,
|
||||
@@ -6675,10 +6687,14 @@ fn aria2_rpc_port_is_occupied(port: u16) -> bool {
|
||||
fn apply_aria2_torrent_global_options(
|
||||
command: &mut std::process::Command,
|
||||
max_open_files: u32,
|
||||
overall_upload_limit: Option<&str>,
|
||||
) {
|
||||
let max_open_files = queue::normalize_torrent_max_open_files(max_open_files)
|
||||
.unwrap_or(queue::DEFAULT_TORRENT_MAX_OPEN_FILES);
|
||||
command.arg(format!("--bt-max-open-files={max_open_files}"));
|
||||
if let Some(limit) = overall_upload_limit.and_then(normalize_speed_limit_for_aria2) {
|
||||
command.arg(format!("--max-overall-upload-limit={limit}"));
|
||||
}
|
||||
}
|
||||
|
||||
#[tauri::command(rename_all = "snake_case")]
|
||||
@@ -6698,6 +6714,24 @@ async fn set_torrent_max_open_files(
|
||||
.map_err(|error| format!("Failed to set Torrent maximum open files: {error}"))
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn set_torrent_overall_upload_limit(
|
||||
state: tauri::State<'_, AppState>,
|
||||
limit: Option<String>,
|
||||
) -> Result<(), String> {
|
||||
let normalized_limit = normalize_torrent_overall_upload_limit(limit.as_deref())?;
|
||||
let limit_str = normalized_limit.as_deref().unwrap_or("0");
|
||||
rpc_call(
|
||||
state.aria2_port.load(std::sync::atomic::Ordering::Relaxed),
|
||||
&state.aria2_secret,
|
||||
"aria2.changeGlobalOption",
|
||||
serde_json::json!([{"max-overall-upload-limit": limit_str}]),
|
||||
)
|
||||
.await
|
||||
.map(|_| ())
|
||||
.map_err(|error| format!("Failed to set Torrent overall upload limit: {error}"))
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn set_global_speed_limit(
|
||||
state: tauri::State<'_, AppState>,
|
||||
@@ -7795,6 +7829,7 @@ mod tests {
|
||||
cookie_scope_for_url, metadata_authentication_error, metadata_cookie_header_present,
|
||||
metadata_headers, metadata_response_error,
|
||||
normalize_speed_limit_for_aria2,
|
||||
normalize_torrent_overall_upload_limit,
|
||||
apply_aria2_torrent_global_options,
|
||||
apply_aria2_torrent_network_options,
|
||||
apply_aria2_torrent_peer_identity_options,
|
||||
@@ -7853,18 +7888,19 @@ mod tests {
|
||||
#[test]
|
||||
fn aria2_torrent_global_options_are_bounded_and_explicit() {
|
||||
let mut command = std::process::Command::new("aria2c");
|
||||
apply_aria2_torrent_global_options(&mut command, 256);
|
||||
apply_aria2_torrent_global_options(&mut command, 256, Some("2M"));
|
||||
assert_eq!(
|
||||
command
|
||||
.get_args()
|
||||
.map(|arg| arg.to_string_lossy().into_owned())
|
||||
.collect::<Vec<_>>(),
|
||||
vec!["--bt-max-open-files=256"]
|
||||
vec!["--bt-max-open-files=256", "--max-overall-upload-limit=2M"]
|
||||
);
|
||||
let mut fallback_command = std::process::Command::new("aria2c");
|
||||
apply_aria2_torrent_global_options(
|
||||
&mut fallback_command,
|
||||
queue::MAX_TORRENT_MAX_OPEN_FILES + 1,
|
||||
Some("not-a-rate"),
|
||||
);
|
||||
assert_eq!(
|
||||
fallback_command
|
||||
@@ -7873,6 +7909,32 @@ mod tests {
|
||||
.collect::<Vec<_>>(),
|
||||
vec!["--bt-max-open-files=100"]
|
||||
);
|
||||
let mut unlimited_command = std::process::Command::new("aria2c");
|
||||
apply_aria2_torrent_global_options(&mut unlimited_command, 256, None);
|
||||
assert_eq!(
|
||||
unlimited_command
|
||||
.get_args()
|
||||
.map(|arg| arg.to_string_lossy().into_owned())
|
||||
.collect::<Vec<_>>(),
|
||||
vec!["--bt-max-open-files=256"]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn torrent_overall_upload_limit_rejects_invalid_values_but_accepts_unlimited() {
|
||||
assert_eq!(
|
||||
normalize_torrent_overall_upload_limit(None).unwrap(),
|
||||
None
|
||||
);
|
||||
assert_eq!(
|
||||
normalize_torrent_overall_upload_limit(Some(" ")).unwrap(),
|
||||
None
|
||||
);
|
||||
assert_eq!(
|
||||
normalize_torrent_overall_upload_limit(Some("1.5 MB/s")).unwrap(),
|
||||
Some("1.5M".to_string())
|
||||
);
|
||||
assert!(normalize_torrent_overall_upload_limit(Some("not-a-rate")).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -10583,6 +10645,10 @@ pub fn run() {
|
||||
.as_ref()
|
||||
.map(|settings| settings.global_speed_limit.clone())
|
||||
.unwrap_or_default();
|
||||
let torrent_overall_upload_limit = persisted_settings
|
||||
.as_ref()
|
||||
.map(|settings| settings.torrent_overall_upload_limit.clone())
|
||||
.unwrap_or_default();
|
||||
let torrent_peer_discovery = persisted_settings
|
||||
.as_ref()
|
||||
.map(|settings| {
|
||||
@@ -10658,7 +10724,11 @@ pub fn run() {
|
||||
.arg("--check-certificate=true")
|
||||
.arg(format!("--stop-with-process={}", std::process::id()));
|
||||
|
||||
apply_aria2_torrent_global_options(&mut cmd, torrent_max_open_files);
|
||||
apply_aria2_torrent_global_options(
|
||||
&mut cmd,
|
||||
torrent_max_open_files,
|
||||
Some(&torrent_overall_upload_limit),
|
||||
);
|
||||
|
||||
apply_aria2_torrent_peer_discovery_options(
|
||||
&mut cmd,
|
||||
@@ -11268,7 +11338,7 @@ pub fn run() {
|
||||
authorize_keychain_access,
|
||||
acknowledge_pairing_token_change,
|
||||
check_file_exists, toggle_tray_icon, set_extension_pairing_token,
|
||||
get_extension_server_port, set_extension_frontend_ready, ack_extension_download, set_concurrent_limit, set_queue_concurrency_limits, set_download_speed_limit, set_torrent_upload_limit, set_torrent_peer_options, get_torrent_peers, set_torrent_max_open_files, set_global_speed_limit, remove_download, get_download_primary_path,
|
||||
get_extension_server_port, set_extension_frontend_ready, ack_extension_download, set_concurrent_limit, set_queue_concurrency_limits, set_download_speed_limit, set_torrent_upload_limit, set_torrent_peer_options, get_torrent_peers, set_torrent_max_open_files, set_torrent_overall_upload_limit, set_global_speed_limit, remove_download, get_download_primary_path,
|
||||
detach_download_for_reconfigure,
|
||||
enqueue_download, enqueue_many, cancel_enqueue_generation, move_in_queue, move_many_in_queue, remove_from_queue, get_pending_order,
|
||||
commands::reveal_in_file_manager, commands::open_downloaded_file,
|
||||
|
||||
@@ -475,6 +475,10 @@ fn validate_settings(settings: &mut PersistedSettings) {
|
||||
settings.max_concurrent_downloads = settings.max_concurrent_downloads.min(12);
|
||||
settings.per_server_connections = settings.per_server_connections.clamp(1, 16);
|
||||
settings.max_automatic_retries = settings.max_automatic_retries.clamp(0, 10);
|
||||
settings.torrent_overall_upload_limit = crate::normalize_speed_limit_for_aria2(
|
||||
&settings.torrent_overall_upload_limit,
|
||||
)
|
||||
.unwrap_or_default();
|
||||
settings.torrent_max_open_files = crate::queue::normalize_torrent_max_open_files(
|
||||
settings.torrent_max_open_files,
|
||||
)
|
||||
@@ -692,6 +696,7 @@ fn default_settings() -> PersistedSettings {
|
||||
approved_download_roots: Vec::new(),
|
||||
max_concurrent_downloads: 3,
|
||||
global_speed_limit: String::new(),
|
||||
torrent_overall_upload_limit: String::new(),
|
||||
speed_limit_preset_values: vec![1.0, 5.0, 10.0],
|
||||
logs_enabled: false,
|
||||
is_sidebar_visible: true,
|
||||
@@ -793,6 +798,7 @@ mod tests {
|
||||
"state": {
|
||||
"maxConcurrentDownloads": 7,
|
||||
"globalSpeedLimit": "2M",
|
||||
"torrentOverallUploadLimit": "1.5M",
|
||||
"sidebarPosition": "right",
|
||||
"scheduler": {
|
||||
"enabled": true,
|
||||
@@ -811,6 +817,7 @@ mod tests {
|
||||
|
||||
assert_eq!(settings.max_concurrent_downloads, 7);
|
||||
assert_eq!(settings.global_speed_limit, "2M");
|
||||
assert_eq!(settings.torrent_overall_upload_limit, "1.5M");
|
||||
assert_eq!(settings.sidebar_position, "right");
|
||||
assert_eq!(settings.speed_limit_preset_values, vec![1.0, 5.0, 10.0]);
|
||||
assert!(!settings.logs_enabled);
|
||||
@@ -836,6 +843,7 @@ mod tests {
|
||||
|
||||
assert_eq!(settings.max_concurrent_downloads, 5);
|
||||
assert_eq!(settings.global_speed_limit, "512K");
|
||||
assert!(settings.torrent_overall_upload_limit.is_empty());
|
||||
assert_eq!(settings.last_custom_speed_limit_unit, "MB/s");
|
||||
assert_eq!(settings.speed_limit_preset_values, vec![1.0, 5.0, 10.0]);
|
||||
assert!(!settings.logs_enabled);
|
||||
@@ -858,6 +866,20 @@ mod tests {
|
||||
assert!(settings.logs_enabled);
|
||||
assert!(!settings.scheduler.enabled);
|
||||
assert!(settings.global_speed_limit.is_empty());
|
||||
assert!(settings.torrent_overall_upload_limit.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn normalizes_invalid_torrent_overall_upload_limit_to_unlimited() {
|
||||
let stored = json!({
|
||||
"state": {
|
||||
"torrentOverallUploadLimit": "not-a-rate"
|
||||
}
|
||||
});
|
||||
|
||||
let settings = decode_stored_settings(&Value::String(stored.to_string())).unwrap();
|
||||
|
||||
assert!(settings.torrent_overall_upload_limit.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user