mirror of
https://github.com/nimbold/Firelink.git
synced 2026-08-06 01:17:48 +00:00
feat(torrents): add global open-file limit
This commit is contained in:
@@ -30,6 +30,10 @@ fn default_torrent_enable_lpd() -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn default_torrent_max_open_files() -> u32 {
|
||||
crate::queue::DEFAULT_TORRENT_MAX_OPEN_FILES
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize, TS)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
#[ts(export, export_to = "../../src/bindings/")]
|
||||
@@ -493,6 +497,8 @@ pub struct PersistedSettings {
|
||||
pub torrent_enable_pex: bool,
|
||||
#[serde(default = "default_torrent_enable_lpd")]
|
||||
pub torrent_enable_lpd: bool,
|
||||
#[serde(default = "default_torrent_max_open_files")]
|
||||
pub torrent_max_open_files: u32,
|
||||
pub custom_user_agent: String,
|
||||
pub ask_where_to_save_each_file: bool,
|
||||
pub remember_last_used_download_directory: bool,
|
||||
|
||||
+71
-1
@@ -6626,6 +6626,32 @@ fn apply_aria2_torrent_peer_discovery_options(
|
||||
.arg(format!("--bt-enable-lpd={enable_lpd}"));
|
||||
}
|
||||
|
||||
fn apply_aria2_torrent_global_options(
|
||||
command: &mut std::process::Command,
|
||||
max_open_files: u32,
|
||||
) {
|
||||
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}"));
|
||||
}
|
||||
|
||||
#[tauri::command(rename_all = "snake_case")]
|
||||
async fn set_torrent_max_open_files(
|
||||
state: tauri::State<'_, AppState>,
|
||||
max_open_files: u32,
|
||||
) -> Result<(), String> {
|
||||
let max_open_files = queue::normalize_torrent_max_open_files(max_open_files)?;
|
||||
rpc_call(
|
||||
state.aria2_port.load(std::sync::atomic::Ordering::Relaxed),
|
||||
&state.aria2_secret,
|
||||
"aria2.changeGlobalOption",
|
||||
serde_json::json!([{"bt-max-open-files": max_open_files.to_string()}]),
|
||||
)
|
||||
.await
|
||||
.map(|_| ())
|
||||
.map_err(|error| format!("Failed to set Torrent maximum open files: {error}"))
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn set_global_speed_limit(
|
||||
state: tauri::State<'_, AppState>,
|
||||
@@ -7718,6 +7744,7 @@ mod tests {
|
||||
cookie_scope_for_url, metadata_authentication_error, metadata_cookie_header_present,
|
||||
metadata_headers, metadata_response_error,
|
||||
normalize_speed_limit_for_aria2,
|
||||
apply_aria2_torrent_global_options,
|
||||
apply_aria2_torrent_peer_discovery_options,
|
||||
parse_firelink_deep_link, parse_ffmpeg_version, parse_media_progress_line,
|
||||
redact_log_line, redact_log_line_for_output, sanitize_ytdlp_config_value,
|
||||
@@ -7739,6 +7766,7 @@ mod tests {
|
||||
#[cfg(target_os = "macos")]
|
||||
use super::should_apply_dock_badge_update;
|
||||
use serde_json::json;
|
||||
use crate::queue;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
#[test]
|
||||
@@ -7768,6 +7796,31 @@ 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);
|
||||
assert_eq!(
|
||||
command
|
||||
.get_args()
|
||||
.map(|arg| arg.to_string_lossy().into_owned())
|
||||
.collect::<Vec<_>>(),
|
||||
vec!["--bt-max-open-files=256"]
|
||||
);
|
||||
let mut fallback_command = std::process::Command::new("aria2c");
|
||||
apply_aria2_torrent_global_options(
|
||||
&mut fallback_command,
|
||||
queue::MAX_TORRENT_MAX_OPEN_FILES + 1,
|
||||
);
|
||||
assert_eq!(
|
||||
fallback_command
|
||||
.get_args()
|
||||
.map(|arg| arg.to_string_lossy().into_owned())
|
||||
.collect::<Vec<_>>(),
|
||||
vec!["--bt-max-open-files=100"]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn retained_torrent_metadata_survives_unrelated_persisted_field_corruption() {
|
||||
let record = json!({
|
||||
@@ -10419,6 +10472,21 @@ pub fn run() {
|
||||
)
|
||||
})
|
||||
.unwrap_or((true, false, true, false));
|
||||
let torrent_max_open_files = persisted_settings
|
||||
.as_ref()
|
||||
.map(|settings| settings.torrent_max_open_files)
|
||||
.unwrap_or(queue::DEFAULT_TORRENT_MAX_OPEN_FILES);
|
||||
let torrent_max_open_files = match queue::normalize_torrent_max_open_files(
|
||||
torrent_max_open_files,
|
||||
) {
|
||||
Ok(value) => value,
|
||||
Err(error) => {
|
||||
log::error!(
|
||||
"invalid persisted Torrent open-file limit; using Aria2 default: {error}"
|
||||
);
|
||||
queue::DEFAULT_TORRENT_MAX_OPEN_FILES
|
||||
}
|
||||
};
|
||||
|
||||
let aria2_secret_clone = aria2_secret.clone();
|
||||
let app_handle_bg = app.handle().clone();
|
||||
@@ -10453,6 +10521,8 @@ 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_peer_discovery_options(
|
||||
&mut cmd,
|
||||
torrent_peer_discovery.0,
|
||||
@@ -11017,7 +11087,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_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_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,
|
||||
|
||||
@@ -24,11 +24,23 @@ pub const DOWNLOAD_CONNECTIONS_MAX: i32 = 16;
|
||||
pub const MAX_TORRENT_PIECE_PRIORITY_SIZE_MIB: u64 = 1024;
|
||||
pub const MAX_TORRENT_TRACKER_TIMEOUT: u32 = 604_800;
|
||||
pub const MAX_TORRENT_TRACKER_INTERVAL: u32 = 604_800;
|
||||
pub const DEFAULT_TORRENT_MAX_OPEN_FILES: u32 = 100;
|
||||
pub const MIN_TORRENT_MAX_OPEN_FILES: u32 = 1;
|
||||
pub const MAX_TORRENT_MAX_OPEN_FILES: u32 = 4_096;
|
||||
|
||||
pub fn clamp_download_connections(connections: i32) -> i32 {
|
||||
connections.clamp(DOWNLOAD_CONNECTIONS_MIN, DOWNLOAD_CONNECTIONS_MAX)
|
||||
}
|
||||
|
||||
pub fn normalize_torrent_max_open_files(value: u32) -> Result<u32, String> {
|
||||
if !(MIN_TORRENT_MAX_OPEN_FILES..=MAX_TORRENT_MAX_OPEN_FILES).contains(&value) {
|
||||
return Err(format!(
|
||||
"torrent maximum open files must be between {MIN_TORRENT_MAX_OPEN_FILES} and {MAX_TORRENT_MAX_OPEN_FILES}"
|
||||
));
|
||||
}
|
||||
Ok(value)
|
||||
}
|
||||
|
||||
fn reorder_selected_queue_tasks(
|
||||
queue_tasks: &[QueuedTask],
|
||||
ids: &[String],
|
||||
@@ -4831,6 +4843,17 @@ mod tests {
|
||||
assert!(!options.contains_key("bt-tracker-interval"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn torrent_max_open_files_is_bounded() {
|
||||
assert_eq!(normalize_torrent_max_open_files(1).unwrap(), 1);
|
||||
assert_eq!(
|
||||
normalize_torrent_max_open_files(MAX_TORRENT_MAX_OPEN_FILES).unwrap(),
|
||||
MAX_TORRENT_MAX_OPEN_FILES
|
||||
);
|
||||
assert!(normalize_torrent_max_open_files(0).is_err());
|
||||
assert!(normalize_torrent_max_open_files(MAX_TORRENT_MAX_OPEN_FILES + 1).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn torrent_trackers_are_emitted_as_the_aria2_tracker_option() {
|
||||
let mut options = serde_json::Map::new();
|
||||
|
||||
@@ -188,6 +188,15 @@ fn sanitize_persisted_setting_values(state: &mut Value) {
|
||||
sanitize_integer_setting(state, "maxConcurrentDownloads", |value| value.as_u64().is_some());
|
||||
sanitize_integer_setting(state, "perServerConnections", |value| value.as_i64().is_some());
|
||||
sanitize_integer_setting(state, "maxAutomaticRetries", |value| value.as_i64().is_some());
|
||||
sanitize_integer_setting(state, "torrentMaxOpenFiles", |value| {
|
||||
value
|
||||
.as_u64()
|
||||
.is_some_and(|value| {
|
||||
(crate::queue::MIN_TORRENT_MAX_OPEN_FILES as u64..=
|
||||
crate::queue::MAX_TORRENT_MAX_OPEN_FILES as u64)
|
||||
.contains(&value)
|
||||
})
|
||||
});
|
||||
for key in [
|
||||
"torrentEnableDht",
|
||||
"torrentEnableDht6",
|
||||
@@ -308,6 +317,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_max_open_files = crate::queue::normalize_torrent_max_open_files(
|
||||
settings.torrent_max_open_files,
|
||||
)
|
||||
.unwrap_or(crate::queue::DEFAULT_TORRENT_MAX_OPEN_FILES);
|
||||
if !matches!(
|
||||
settings.last_custom_speed_limit_unit.as_str(),
|
||||
"KB/s" | "MB/s"
|
||||
@@ -500,6 +513,7 @@ fn default_settings() -> PersistedSettings {
|
||||
torrent_enable_dht6: false,
|
||||
torrent_enable_pex: true,
|
||||
torrent_enable_lpd: false,
|
||||
torrent_max_open_files: crate::queue::DEFAULT_TORRENT_MAX_OPEN_FILES,
|
||||
custom_user_agent: String::new(),
|
||||
ask_where_to_save_each_file: false,
|
||||
remember_last_used_download_directory: false,
|
||||
@@ -792,6 +806,7 @@ mod tests {
|
||||
"torrentEnableDht6": 1,
|
||||
"torrentEnablePex": null,
|
||||
"torrentEnableLpd": [],
|
||||
"torrentMaxOpenFiles": 0,
|
||||
"theme": "not-a-theme",
|
||||
"calendarPreference": "lunar",
|
||||
"siteLogins": [{"id": "valid", "urlPattern": "example.com", "username": "user"}, {"id": 3}]
|
||||
@@ -809,6 +824,10 @@ mod tests {
|
||||
assert!(!settings.torrent_enable_dht6);
|
||||
assert!(settings.torrent_enable_pex);
|
||||
assert!(!settings.torrent_enable_lpd);
|
||||
assert_eq!(
|
||||
settings.torrent_max_open_files,
|
||||
crate::queue::DEFAULT_TORRENT_MAX_OPEN_FILES
|
||||
);
|
||||
assert!(matches!(settings.theme, crate::ipc::Theme::System));
|
||||
assert!(matches!(
|
||||
settings.calendar_preference,
|
||||
|
||||
Reference in New Issue
Block a user