mirror of
https://github.com/nimbold/Firelink.git
synced 2026-08-02 07:30:18 +00:00
feat(torrents): add peer discovery controls
This commit is contained in:
@@ -14,6 +14,22 @@ fn default_sidebar_position() -> String {
|
||||
"auto".to_string()
|
||||
}
|
||||
|
||||
fn default_torrent_enable_dht() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn default_torrent_enable_dht6() -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn default_torrent_enable_pex() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn default_torrent_enable_lpd() -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize, TS)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
#[ts(export, export_to = "../../src/bindings/")]
|
||||
@@ -408,6 +424,14 @@ pub struct PersistedSettings {
|
||||
pub proxy_mode: ProxyMode,
|
||||
pub proxy_host: String,
|
||||
pub proxy_port: u16,
|
||||
#[serde(default = "default_torrent_enable_dht")]
|
||||
pub torrent_enable_dht: bool,
|
||||
#[serde(default = "default_torrent_enable_dht6")]
|
||||
pub torrent_enable_dht6: bool,
|
||||
#[serde(default = "default_torrent_enable_pex")]
|
||||
pub torrent_enable_pex: bool,
|
||||
#[serde(default = "default_torrent_enable_lpd")]
|
||||
pub torrent_enable_lpd: bool,
|
||||
pub custom_user_agent: String,
|
||||
pub ask_where_to_save_each_file: bool,
|
||||
pub remember_last_used_download_directory: bool,
|
||||
|
||||
@@ -6324,6 +6324,23 @@ pub(crate) fn normalize_speed_limit_for_aria2(limit: &str) -> Option<String> {
|
||||
})
|
||||
}
|
||||
|
||||
fn apply_aria2_torrent_peer_discovery_options(
|
||||
command: &mut std::process::Command,
|
||||
enable_dht: bool,
|
||||
enable_dht6: bool,
|
||||
enable_pex: bool,
|
||||
enable_lpd: bool,
|
||||
) {
|
||||
// These are daemon-global BitTorrent options. Keep them explicit at
|
||||
// launch so Firelink does not silently inherit a different aria2
|
||||
// configuration from the host or a future bundled-engine default.
|
||||
command
|
||||
.arg(format!("--enable-dht={enable_dht}"))
|
||||
.arg(format!("--enable-dht6={enable_dht6}"))
|
||||
.arg(format!("--enable-peer-exchange={enable_pex}"))
|
||||
.arg(format!("--bt-enable-lpd={enable_lpd}"));
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn set_global_speed_limit(
|
||||
state: tauri::State<'_, AppState>,
|
||||
@@ -7400,6 +7417,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_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,
|
||||
has_resumable_download_assets, is_media_artifact_name,
|
||||
@@ -7429,6 +7447,25 @@ mod tests {
|
||||
assert!(validate_keychain_grant_request_id(&"x".repeat(129)).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn aria2_torrent_peer_discovery_options_are_explicit_and_launch_scoped() {
|
||||
let mut command = std::process::Command::new("aria2c");
|
||||
apply_aria2_torrent_peer_discovery_options(&mut command, false, true, false, true);
|
||||
let args = command
|
||||
.get_args()
|
||||
.map(|arg| arg.to_string_lossy().into_owned())
|
||||
.collect::<Vec<_>>();
|
||||
assert_eq!(
|
||||
args,
|
||||
vec![
|
||||
"--enable-dht=false",
|
||||
"--enable-dht6=true",
|
||||
"--enable-peer-exchange=false",
|
||||
"--bt-enable-lpd=true",
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn aria2_active_connection_count_uses_only_nonnegative_daemon_values() {
|
||||
assert_eq!(
|
||||
@@ -10033,6 +10070,17 @@ pub fn run() {
|
||||
.as_ref()
|
||||
.map(|settings| settings.global_speed_limit.clone())
|
||||
.unwrap_or_default();
|
||||
let torrent_peer_discovery = persisted_settings
|
||||
.as_ref()
|
||||
.map(|settings| {
|
||||
(
|
||||
settings.torrent_enable_dht,
|
||||
settings.torrent_enable_dht6,
|
||||
settings.torrent_enable_pex,
|
||||
settings.torrent_enable_lpd,
|
||||
)
|
||||
})
|
||||
.unwrap_or((true, false, true, false));
|
||||
|
||||
let aria2_secret_clone = aria2_secret.clone();
|
||||
let app_handle_bg = app.handle().clone();
|
||||
@@ -10067,6 +10115,14 @@ pub fn run() {
|
||||
.arg("--check-certificate=true")
|
||||
.arg(format!("--stop-with-process={}", std::process::id()));
|
||||
|
||||
apply_aria2_torrent_peer_discovery_options(
|
||||
&mut cmd,
|
||||
torrent_peer_discovery.0,
|
||||
torrent_peer_discovery.1,
|
||||
torrent_peer_discovery.2,
|
||||
torrent_peer_discovery.3,
|
||||
);
|
||||
|
||||
if let Some(limit) = normalize_speed_limit_for_aria2(&global_speed_limit) {
|
||||
cmd.arg(format!("--max-overall-download-limit={}", limit));
|
||||
}
|
||||
|
||||
@@ -188,6 +188,14 @@ 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());
|
||||
for key in [
|
||||
"torrentEnableDht",
|
||||
"torrentEnableDht6",
|
||||
"torrentEnablePex",
|
||||
"torrentEnableLpd",
|
||||
] {
|
||||
sanitize_boolean_setting(state, key);
|
||||
}
|
||||
sanitize_allowed_string(
|
||||
state,
|
||||
"theme",
|
||||
@@ -273,6 +281,12 @@ fn sanitize_integer_setting(
|
||||
}
|
||||
}
|
||||
|
||||
fn sanitize_boolean_setting(state: &mut serde_json::Map<String, Value>, key: &str) {
|
||||
if state.get(key).is_some_and(|value| !value.is_boolean()) {
|
||||
state.remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
fn sanitize_allowed_string(
|
||||
state: &mut serde_json::Map<String, Value>,
|
||||
key: &str,
|
||||
@@ -482,6 +496,10 @@ fn default_settings() -> PersistedSettings {
|
||||
proxy_mode: ProxyMode::None,
|
||||
proxy_host: String::new(),
|
||||
proxy_port: 8080,
|
||||
torrent_enable_dht: true,
|
||||
torrent_enable_dht6: false,
|
||||
torrent_enable_pex: true,
|
||||
torrent_enable_lpd: false,
|
||||
custom_user_agent: String::new(),
|
||||
ask_where_to_save_each_file: false,
|
||||
remember_last_used_download_directory: false,
|
||||
@@ -770,6 +788,10 @@ mod tests {
|
||||
"maxConcurrentDownloads": "not-a-number",
|
||||
"perServerConnections": 5,
|
||||
"showNotifications": "yes",
|
||||
"torrentEnableDht": "yes",
|
||||
"torrentEnableDht6": 1,
|
||||
"torrentEnablePex": null,
|
||||
"torrentEnableLpd": [],
|
||||
"theme": "not-a-theme",
|
||||
"calendarPreference": "lunar",
|
||||
"siteLogins": [{"id": "valid", "urlPattern": "example.com", "username": "user"}, {"id": 3}]
|
||||
@@ -783,6 +805,10 @@ mod tests {
|
||||
assert_eq!(settings.max_concurrent_downloads, 3);
|
||||
assert_eq!(settings.per_server_connections, 5);
|
||||
assert!(settings.show_notifications);
|
||||
assert!(settings.torrent_enable_dht);
|
||||
assert!(!settings.torrent_enable_dht6);
|
||||
assert!(settings.torrent_enable_pex);
|
||||
assert!(!settings.torrent_enable_lpd);
|
||||
assert!(matches!(settings.theme, crate::ipc::Theme::System));
|
||||
assert!(matches!(
|
||||
settings.calendar_preference,
|
||||
|
||||
@@ -11,4 +11,4 @@ import type { SiteLogin } from "./SiteLogin";
|
||||
import type { Theme } from "./Theme";
|
||||
import type { WindowControlStyle } from "./WindowControlStyle";
|
||||
|
||||
export type PersistedSettings = { theme: Theme, fontFamily: FontFamily, windowControlStyle: WindowControlStyle, calendarPreference: CalendarPreference, language: string, baseDownloadFolder: string, categorySubfoldersEnabled: boolean, categorySubfolders: { [key in string]: string }, categoryDirectoryOverrides: { [key in string]: string }, approvedDownloadRoots: Array<string>, maxConcurrentDownloads: number, globalSpeedLimit: string, speedLimitPresetValues: Array<number>, logsEnabled: boolean, isSidebarVisible: boolean, sidebarPosition: string, activeSettingsTab: SettingsTab, scheduler: SchedulerSettings, schedulerRunning: boolean, schedulerActiveDownloadIds: Array<string>, schedulerLastStartKey: string, schedulerLastStopKey: string, lastCustomSpeedLimitKiB: number, lastCustomSpeedLimitUnit: string, perServerConnections: number, maxAutomaticRetries: number, showNotifications: boolean, playCompletionSound: boolean, autoAddClipboardLinks: boolean, appFontSize: AppFontSize, listRowDensity: ListRowDensity, showDockBadge: boolean, showMenuBarIcon: boolean, proxyMode: ProxyMode, proxyHost: string, proxyPort: number, customUserAgent: string, askWhereToSaveEachFile: boolean, rememberLastUsedDownloadDirectory: boolean, preventsSleepWhileDownloading: boolean, preventsDisplaySleepWhileDownloading: boolean, mediaCookieSource: MediaCookieSource, siteLogins: Array<SiteLogin>, autoCheckUpdates: boolean, keychainAccessGranted: boolean, };
|
||||
export type PersistedSettings = { theme: Theme, fontFamily: FontFamily, windowControlStyle: WindowControlStyle, calendarPreference: CalendarPreference, language: string, baseDownloadFolder: string, categorySubfoldersEnabled: boolean, categorySubfolders: { [key in string]: string }, categoryDirectoryOverrides: { [key in string]: string }, approvedDownloadRoots: Array<string>, maxConcurrentDownloads: number, globalSpeedLimit: string, speedLimitPresetValues: Array<number>, logsEnabled: boolean, isSidebarVisible: boolean, sidebarPosition: string, activeSettingsTab: SettingsTab, scheduler: SchedulerSettings, schedulerRunning: boolean, schedulerActiveDownloadIds: Array<string>, schedulerLastStartKey: string, schedulerLastStopKey: string, lastCustomSpeedLimitKiB: number, lastCustomSpeedLimitUnit: string, perServerConnections: number, maxAutomaticRetries: number, showNotifications: boolean, playCompletionSound: boolean, autoAddClipboardLinks: boolean, appFontSize: AppFontSize, listRowDensity: ListRowDensity, showDockBadge: boolean, showMenuBarIcon: boolean, proxyMode: ProxyMode, proxyHost: string, proxyPort: number, torrentEnableDht: boolean, torrentEnableDht6: boolean, torrentEnablePex: boolean, torrentEnableLpd: boolean, customUserAgent: string, askWhereToSaveEachFile: boolean, rememberLastUsedDownloadDirectory: boolean, preventsSleepWhileDownloading: boolean, preventsDisplaySleepWhileDownloading: boolean, mediaCookieSource: MediaCookieSource, siteLogins: Array<SiteLogin>, autoCheckUpdates: boolean, keychainAccessGranted: boolean, };
|
||||
|
||||
@@ -1157,6 +1157,61 @@ runEngineChecks(false);
|
||||
)}
|
||||
</div>
|
||||
|
||||
<h2 className="settings-section-title">{t($ => $.settings.network.torrentPeerDiscovery)}</h2>
|
||||
<div className="mac-settings-group">
|
||||
<label className="mac-settings-row cursor-default">
|
||||
<div className="settings-row-label">
|
||||
<span>{t($ => $.settings.network.torrentDht)}</span>
|
||||
<small>{t($ => $.settings.network.torrentDhtDescription)}</small>
|
||||
</div>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={settings.torrentEnableDht}
|
||||
onChange={(event) => settings.setTorrentEnableDht(event.target.checked)}
|
||||
className="mac-switch"
|
||||
/>
|
||||
</label>
|
||||
<label className="mac-settings-row cursor-default">
|
||||
<div className="settings-row-label">
|
||||
<span>{t($ => $.settings.network.torrentDht6)}</span>
|
||||
<small>{t($ => $.settings.network.torrentDht6Description)}</small>
|
||||
</div>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={settings.torrentEnableDht6}
|
||||
onChange={(event) => settings.setTorrentEnableDht6(event.target.checked)}
|
||||
className="mac-switch"
|
||||
/>
|
||||
</label>
|
||||
<label className="mac-settings-row cursor-default">
|
||||
<div className="settings-row-label">
|
||||
<span>{t($ => $.settings.network.torrentPex)}</span>
|
||||
<small>{t($ => $.settings.network.torrentPexDescription)}</small>
|
||||
</div>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={settings.torrentEnablePex}
|
||||
onChange={(event) => settings.setTorrentEnablePex(event.target.checked)}
|
||||
className="mac-switch"
|
||||
/>
|
||||
</label>
|
||||
<label className="mac-settings-row cursor-default">
|
||||
<div className="settings-row-label">
|
||||
<span>{t($ => $.settings.network.torrentLpd)}</span>
|
||||
<small>{t($ => $.settings.network.torrentLpdDescription)}</small>
|
||||
</div>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={settings.torrentEnableLpd}
|
||||
onChange={(event) => settings.setTorrentEnableLpd(event.target.checked)}
|
||||
className="mac-switch"
|
||||
/>
|
||||
</label>
|
||||
<p className="settings-group-footer">
|
||||
{t($ => $.settings.network.torrentPeerDiscoveryRestartNote)}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<h2 className="settings-section-title">{t($ => $.settings.network.identity)}</h2>
|
||||
<div className="mac-settings-group settings-popup-group">
|
||||
<div className="mac-settings-row settings-network-row">
|
||||
|
||||
@@ -700,6 +700,16 @@ const common = {
|
||||
detectedSystemProxy: 'A system proxy was detected. Normal file downloads require an HTTP or HTTPS endpoint; media downloads can use SOCKS.',
|
||||
noSystemProxy: 'No usable system proxy was detected. Downloads will use no proxy.',
|
||||
systemProxyReadFailed: 'System proxy configuration could not be read. Choose No Proxy or try again when it is available.',
|
||||
torrentPeerDiscovery: 'BitTorrent peer discovery',
|
||||
torrentDht: 'IPv4 DHT and UDP trackers',
|
||||
torrentDhtDescription: 'Find peers without relying only on trackers. Disabling this also disables UDP tracker support.',
|
||||
torrentDht6: 'IPv6 DHT',
|
||||
torrentDht6Description: 'Use IPv6 for distributed peer discovery when the network provides a usable IPv6 path.',
|
||||
torrentPex: 'Peer Exchange (PEX)',
|
||||
torrentPexDescription: 'Allow connected peers to share additional peer addresses.',
|
||||
torrentLpd: 'Local Peer Discovery (LPD)',
|
||||
torrentLpdDescription: 'Discover compatible peers on the local network. This increases local network visibility.',
|
||||
torrentPeerDiscoveryRestartNote: 'These options are global to Aria2 and take effect after Firelink restarts. Aria2 still disables peer discovery for private torrents.',
|
||||
identity: 'Identity',
|
||||
customUserAgent: 'Custom User-Agent',
|
||||
userAgentDescription: 'Applied to metadata fetches and download engines.',
|
||||
|
||||
@@ -700,6 +700,16 @@ const fa = {
|
||||
detectedSystemProxy: 'یک پروکسی سیستم شناسایی شد. دانلودهای فایل عادی به یک نقطه پایانی HTTP یا HTTPS نیاز دارند؛ دانلودهای رسانه میتوانند از SOCKS استفاده کنند.',
|
||||
noSystemProxy: 'هیچ پروکسی سیستم قابل استفادهای شناسایی نشد. دانلودها از هیچ پروکسیای استفاده نخواهند کرد.',
|
||||
systemProxyReadFailed: 'پیکربندی پروکسی سیستم قابل خواندن نیست. بدون پروکسی را انتخاب کنید یا هنگامی که در دسترس است دوباره امتحان کنید.',
|
||||
torrentPeerDiscovery: 'کشف همتا در بیتتورنت',
|
||||
torrentDht: 'DHT نسخه IPv4 و ترکرهای UDP',
|
||||
torrentDhtDescription: 'همتاها را بدون تکیه صرف بر ترکرها پیدا میکند. خاموش کردن آن پشتیبانی از ترکرهای UDP را هم خاموش میکند.',
|
||||
torrentDht6: 'DHT نسخه IPv6',
|
||||
torrentDht6Description: 'وقتی مسیر IPv6 قابل استفاده باشد، از آن برای کشف توزیعشده همتاها استفاده میکند.',
|
||||
torrentPex: 'تبادل همتا (PEX)',
|
||||
torrentPexDescription: 'به همتاهای متصل اجازه میدهد آدرس همتاهای بیشتری را به اشتراک بگذارند.',
|
||||
torrentLpd: 'کشف همتای محلی (LPD)',
|
||||
torrentLpdDescription: 'همتاهای سازگار در شبکه محلی را پیدا میکند و دیدهشدن ترافیک در شبکه محلی را افزایش میدهد.',
|
||||
torrentPeerDiscoveryRestartNote: 'این گزینهها سراسری و مربوط به Aria2 هستند و پس از راهاندازی مجدد Firelink اعمال میشوند. Aria2 همچنان کشف همتا را برای تورنتهای خصوصی خاموش میکند.',
|
||||
identity: 'هویت',
|
||||
customUserAgent: 'User-Agent سفارشی',
|
||||
userAgentDescription: 'در دریافتهای متادیتا و موتورهای دانلود اعمال میشود.',
|
||||
|
||||
@@ -700,6 +700,16 @@ const he = {
|
||||
detectedSystemProxy: 'זוהה פרוקסי מערכת. הורדות קבצים רגילות דורשות נקודת קצה של HTTP או HTTPS; הורדות מדיה יכולות להשתמש ב-SOCKS.',
|
||||
noSystemProxy: 'לא זוהה פרוקסי מערכת שימושי. ההורדות לא ישתמשו בפרוקסי.',
|
||||
systemProxyReadFailed: 'לא ניתן לקרוא את תצורת פרוקסי המערכת. בחר "ללא פרוקסי" או נסה שוב כשהיא תהיה זמינה.',
|
||||
torrentPeerDiscovery: 'גילוי עמיתים ב-BitTorrent',
|
||||
torrentDht: 'DHT של IPv4 ומעקבי UDP',
|
||||
torrentDhtDescription: 'מאתר עמיתים בלי להסתמך רק על מעקבים. השבתה מכבה גם תמיכה במעקבי UDP.',
|
||||
torrentDht6: 'DHT של IPv6',
|
||||
torrentDht6Description: 'משתמש ב-IPv6 לגילוי מבוזר של עמיתים כשיש נתיב IPv6 זמין.',
|
||||
torrentPex: 'החלפת עמיתים (PEX)',
|
||||
torrentPexDescription: 'מאפשר לעמיתים מחוברים לשתף כתובות של עמיתים נוספים.',
|
||||
torrentLpd: 'גילוי עמיתים מקומיים (LPD)',
|
||||
torrentLpdDescription: 'מאתר עמיתים תואמים ברשת המקומית ומגדיל את החשיפה המקומית של התעבורה.',
|
||||
torrentPeerDiscoveryRestartNote: 'האפשרויות האלה הן כלליות ל-Aria2 ונכנסות לתוקף לאחר הפעלה מחדש של Firelink. Aria2 עדיין משבית גילוי עמיתים בטורנטים פרטיים.',
|
||||
identity: 'זהות',
|
||||
customUserAgent: 'User-Agent מותאם אישית',
|
||||
userAgentDescription: 'מוחל על משיכות מטא נתונים ומנועי הורדה.',
|
||||
|
||||
@@ -700,6 +700,16 @@ const ru = {
|
||||
detectedSystemProxy: 'Обнаружен системный прокси. Для обычных загрузок файлов требуется HTTP- или HTTPS-прокси; для загрузки медиа можно использовать SOCKS.',
|
||||
noSystemProxy: 'Подходящий системный прокси не обнаружен. Загрузки будут выполняться без прокси.',
|
||||
systemProxyReadFailed: 'Не удалось прочитать конфигурацию системного прокси. Выберите «Без прокси» или повторите попытку, когда она станет доступна.',
|
||||
torrentPeerDiscovery: 'Обнаружение пиров BitTorrent',
|
||||
torrentDht: 'IPv4 DHT и UDP-трекеры',
|
||||
torrentDhtDescription: 'Ищет пиры не только через трекеры. Отключение также отключает поддержку UDP-трекеров.',
|
||||
torrentDht6: 'DHT по IPv6',
|
||||
torrentDht6Description: 'Использует IPv6 для распределённого поиска пиров, если доступен рабочий IPv6-маршрут.',
|
||||
torrentPex: 'Обмен пирами (PEX)',
|
||||
torrentPexDescription: 'Позволяет подключённым пирам передавать адреса дополнительных пиров.',
|
||||
torrentLpd: 'Локальное обнаружение пиров (LPD)',
|
||||
torrentLpdDescription: 'Ищет подходящие пиры в локальной сети и увеличивает видимость трафика в ней.',
|
||||
torrentPeerDiscoveryRestartNote: 'Эти параметры являются глобальными для Aria2 и применяются после перезапуска Firelink. Aria2 по-прежнему отключает обнаружение пиров для приватных торрентов.',
|
||||
identity: 'Идентификация',
|
||||
customUserAgent: 'Собственный User-Agent',
|
||||
userAgentDescription: 'Применяется при получении метаданных и работе движков загрузки.',
|
||||
|
||||
@@ -700,6 +700,16 @@ const uk = {
|
||||
detectedSystemProxy: 'Виявлено системний проксі. Звичайні завантаження файлів вимагають HTTP або HTTPS проксі; медіазавантаження можуть використовувати SOCKS.',
|
||||
noSystemProxy: 'Не виявлено придатного системного проксі. Завантаження використовуватимуть пряме підключення (без проксі).',
|
||||
systemProxyReadFailed: 'Не вдалося прочитати конфігурацію системного проксі. Виберіть "Без проксі" або спробуйте ще раз, коли він стане доступним.',
|
||||
torrentPeerDiscovery: 'Пошук пірів BitTorrent',
|
||||
torrentDht: 'IPv4 DHT і UDP-трекери',
|
||||
torrentDhtDescription: 'Шукає пірів не лише через трекери. Вимкнення також вимикає підтримку UDP-трекерів.',
|
||||
torrentDht6: 'DHT через IPv6',
|
||||
torrentDht6Description: 'Використовує IPv6 для розподіленого пошуку пірів, якщо доступний робочий маршрут IPv6.',
|
||||
torrentPex: 'Обмін пірами (PEX)',
|
||||
torrentPexDescription: 'Дозволяє підключеним пірам передавати адреси додаткових пірів.',
|
||||
torrentLpd: 'Локальний пошук пірів (LPD)',
|
||||
torrentLpdDescription: 'Шукає сумісних пірів у локальній мережі та збільшує видимість трафіку в ній.',
|
||||
torrentPeerDiscoveryRestartNote: 'Ці параметри є глобальними для Aria2 і застосовуються після перезапуску Firelink. Aria2 і надалі вимикає пошук пірів для приватних торрентів.',
|
||||
identity: 'Ідентифікація',
|
||||
customUserAgent: 'Власний User-Agent',
|
||||
userAgentDescription: 'Застосовується до запитів метаданих та рушіїв завантаження.',
|
||||
|
||||
@@ -700,6 +700,16 @@ const zhCN = {
|
||||
detectedSystemProxy: '检测到系统代理。普通文件下载需要 HTTP 或 HTTPS 端点;媒体下载可以使用 SOCKS。',
|
||||
noSystemProxy: '未检测到可用的系统代理。下载将不使用代理。',
|
||||
systemProxyReadFailed: '无法读取系统代理配置。请选择“无代理”,或者在其可用时重试。',
|
||||
torrentPeerDiscovery: 'BitTorrent 对等节点发现',
|
||||
torrentDht: 'IPv4 DHT 与 UDP Tracker',
|
||||
torrentDhtDescription: '不只依赖 Tracker 查找节点。关闭后也会禁用 UDP Tracker 支持。',
|
||||
torrentDht6: 'IPv6 分布式哈希表',
|
||||
torrentDht6Description: '当网络提供可用的 IPv6 路径时,使用 IPv6 进行分布式节点发现。',
|
||||
torrentPex: '节点交换(PEX)',
|
||||
torrentPexDescription: '允许已连接的节点共享其他节点的地址。',
|
||||
torrentLpd: '本地节点发现(LPD)',
|
||||
torrentLpdDescription: '在本地网络中发现兼容节点,这会增加本地网络中的流量可见性。',
|
||||
torrentPeerDiscoveryRestartNote: '这些选项是 Aria2 的全局设置,需要重启 Firelink 后生效。Aria2 仍会对私有 Torrent 禁用节点发现。',
|
||||
identity: '身份',
|
||||
customUserAgent: '自定义 User-Agent',
|
||||
userAgentDescription: '应用于元数据获取和下载引擎。',
|
||||
|
||||
@@ -21,6 +21,42 @@ describe('last used download directory preference', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('Torrent peer discovery preferences', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
useSettingsStore.setState({
|
||||
torrentEnableDht: true,
|
||||
torrentEnableDht6: false,
|
||||
torrentEnablePex: true,
|
||||
torrentEnableLpd: false
|
||||
});
|
||||
});
|
||||
|
||||
it('matches Aria2 defaults and persists explicit changes', async () => {
|
||||
expect(useSettingsStore.getState().torrentEnableDht).toBe(true);
|
||||
expect(useSettingsStore.getState().torrentEnableDht6).toBe(false);
|
||||
expect(useSettingsStore.getState().torrentEnablePex).toBe(true);
|
||||
expect(useSettingsStore.getState().torrentEnableLpd).toBe(false);
|
||||
|
||||
useSettingsStore.getState().setTorrentEnableDht(false);
|
||||
useSettingsStore.getState().setTorrentEnableDht6(true);
|
||||
useSettingsStore.getState().setTorrentEnablePex(false);
|
||||
useSettingsStore.getState().setTorrentEnableLpd(true);
|
||||
await vi.waitFor(() => {
|
||||
const save = vi.mocked(ipc.invokeCommand).mock.calls
|
||||
.filter(([command]) => command === 'db_save_settings')
|
||||
.slice(-1)[0];
|
||||
expect(save).toBeDefined();
|
||||
expect(JSON.parse((save?.[1] as { data: string }).data).state).toMatchObject({
|
||||
torrentEnableDht: false,
|
||||
torrentEnableDht6: true,
|
||||
torrentEnablePex: false,
|
||||
torrentEnableLpd: true
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('calendar preference', () => {
|
||||
it('keeps Gregorian as the default and persists explicit calendar choices', async () => {
|
||||
vi.clearAllMocks();
|
||||
|
||||
@@ -234,6 +234,10 @@ export interface SettingsState {
|
||||
proxyMode: ProxyMode;
|
||||
proxyHost: string;
|
||||
proxyPort: number;
|
||||
torrentEnableDht: boolean;
|
||||
torrentEnableDht6: boolean;
|
||||
torrentEnablePex: boolean;
|
||||
torrentEnableLpd: boolean;
|
||||
customUserAgent: string;
|
||||
askWhereToSaveEachFile: boolean;
|
||||
preventsSleepWhileDownloading: boolean;
|
||||
@@ -284,6 +288,10 @@ export interface SettingsState {
|
||||
setProxyMode: (mode: ProxyMode) => void;
|
||||
setProxyHost: (host: string) => void;
|
||||
setProxyPort: (port: number) => void;
|
||||
setTorrentEnableDht: (enabled: boolean) => void;
|
||||
setTorrentEnableDht6: (enabled: boolean) => void;
|
||||
setTorrentEnablePex: (enabled: boolean) => void;
|
||||
setTorrentEnableLpd: (enabled: boolean) => void;
|
||||
setCustomUserAgent: (userAgent: string) => void;
|
||||
setAskWhereToSaveEachFile: (ask: boolean) => void;
|
||||
setPreventsSleepWhileDownloading: (prevent: boolean) => void;
|
||||
@@ -360,6 +368,10 @@ export const useSettingsStore = create<SettingsState>()(
|
||||
proxyMode: 'none',
|
||||
proxyHost: '',
|
||||
proxyPort: 8080,
|
||||
torrentEnableDht: true,
|
||||
torrentEnableDht6: false,
|
||||
torrentEnablePex: true,
|
||||
torrentEnableLpd: false,
|
||||
customUserAgent: '',
|
||||
askWhereToSaveEachFile: false,
|
||||
preventsSleepWhileDownloading: true,
|
||||
@@ -454,6 +466,10 @@ export const useSettingsStore = create<SettingsState>()(
|
||||
? Math.min(65535, Math.max(1, Math.trunc(proxyPort)))
|
||||
: 8080
|
||||
}),
|
||||
setTorrentEnableDht: (torrentEnableDht) => set({ torrentEnableDht }),
|
||||
setTorrentEnableDht6: (torrentEnableDht6) => set({ torrentEnableDht6 }),
|
||||
setTorrentEnablePex: (torrentEnablePex) => set({ torrentEnablePex }),
|
||||
setTorrentEnableLpd: (torrentEnableLpd) => set({ torrentEnableLpd }),
|
||||
setCustomUserAgent: (customUserAgent) => set({ customUserAgent }),
|
||||
setAskWhereToSaveEachFile: (askWhereToSaveEachFile) => set({ askWhereToSaveEachFile }),
|
||||
setPreventsSleepWhileDownloading: (preventsSleepWhileDownloading) => {
|
||||
@@ -635,6 +651,10 @@ export const useSettingsStore = create<SettingsState>()(
|
||||
proxyMode: state.proxyMode,
|
||||
proxyHost: state.proxyHost,
|
||||
proxyPort: state.proxyPort,
|
||||
torrentEnableDht: state.torrentEnableDht,
|
||||
torrentEnableDht6: state.torrentEnableDht6,
|
||||
torrentEnablePex: state.torrentEnablePex,
|
||||
torrentEnableLpd: state.torrentEnableLpd,
|
||||
customUserAgent: state.customUserAgent,
|
||||
askWhereToSaveEachFile: state.askWhereToSaveEachFile,
|
||||
preventsSleepWhileDownloading: state.preventsSleepWhileDownloading,
|
||||
@@ -680,6 +700,10 @@ export const useSettingsStore = create<SettingsState>()(
|
||||
listRowDensity: isAllowedSetting(LIST_ROW_DENSITY_VALUES, persisted.listRowDensity)
|
||||
? persisted.listRowDensity
|
||||
: currentState.listRowDensity,
|
||||
torrentEnableDht: persistedBoolean(persisted.torrentEnableDht, currentState.torrentEnableDht),
|
||||
torrentEnableDht6: persistedBoolean(persisted.torrentEnableDht6, currentState.torrentEnableDht6),
|
||||
torrentEnablePex: persistedBoolean(persisted.torrentEnablePex, currentState.torrentEnablePex),
|
||||
torrentEnableLpd: persistedBoolean(persisted.torrentEnableLpd, currentState.torrentEnableLpd),
|
||||
sidebarPosition: isAllowedSetting(SIDEBAR_POSITION_VALUES, persisted.sidebarPosition)
|
||||
? persisted.sidebarPosition
|
||||
: currentState.sidebarPosition,
|
||||
|
||||
Reference in New Issue
Block a user