From 8d33e1a5b38fcd6adae03f26bab2864b8a6ab92d Mon Sep 17 00:00:00 2001 From: Locon213 <167447571+Locon213@users.noreply.github.com> Date: Thu, 22 Jan 2026 04:32:14 +0300 Subject: [PATCH] feat: add automatic cleanup options for completed torrents and missing files (#1243) --- pkg/base/model.go | 4 ++ pkg/download/downloader.go | 58 ++++++++++++++++++- .../lib/api/model/downloader_config.dart | 6 +- .../lib/api/model/downloader_config.g.dart | 6 +- .../modules/setting/views/setting_view.dart | 44 +++++++++++++- ui/flutter/lib/i18n/langs/de_de.dart | 2 + ui/flutter/lib/i18n/langs/en_us.dart | 4 +- ui/flutter/lib/i18n/langs/es_es.dart | 2 + ui/flutter/lib/i18n/langs/fa_ir.dart | 2 + ui/flutter/lib/i18n/langs/fr_fr.dart | 2 + ui/flutter/lib/i18n/langs/hu_hu.dart | 2 + ui/flutter/lib/i18n/langs/id_id.dart | 2 + ui/flutter/lib/i18n/langs/it_it.dart | 2 + ui/flutter/lib/i18n/langs/ja_jp.dart | 2 + ui/flutter/lib/i18n/langs/pl_pl.dart | 2 + ui/flutter/lib/i18n/langs/pt_br.dart | 2 + ui/flutter/lib/i18n/langs/ru_ru.dart | 4 +- ui/flutter/lib/i18n/langs/ta_ta.dart | 2 + ui/flutter/lib/i18n/langs/tr_tr.dart | 2 + ui/flutter/lib/i18n/langs/uk_ua.dart | 2 + ui/flutter/lib/i18n/langs/vi_vn.dart | 2 + ui/flutter/lib/i18n/langs/zh_cn.dart | 2 + ui/flutter/lib/i18n/langs/zh_tw.dart | 2 + 23 files changed, 152 insertions(+), 6 deletions(-) diff --git a/pkg/base/model.go b/pkg/base/model.go index 6b36652a..62ec8d50 100644 --- a/pkg/base/model.go +++ b/pkg/base/model.go @@ -185,6 +185,10 @@ type DownloaderStoreConfig struct { Proxy *DownloaderProxyConfig `json:"proxy"` Webhook *WebhookConfig `json:"webhook"` // Webhook is the webhook configuration Archive *ArchiveConfig `json:"archive"` // Archive is the archive extraction configuration + + // New fields added for auto-management + AutoDeleteTorrents bool `json:"autoDeleteTorrents"` // AutoDeleteTorrents enables automatic deletion of .torrent files after download + AutoCleanMissingFiles bool `json:"autoCleanMissingFiles"` // AutoCleanMissingFiles enables automatic cleanup of tasks with missing files } func (cfg *DownloaderStoreConfig) Init() *DownloaderStoreConfig { diff --git a/pkg/download/downloader.go b/pkg/download/downloader.go index de0ebc42..03114767 100644 --- a/pkg/download/downloader.go +++ b/pkg/download/downloader.go @@ -205,6 +205,9 @@ func (d *Downloader) Setup() error { } d.extensions = extensions + // Auto-cleanup non-existing tasks on startup + d.cleanupNonExistingTasks() + // handle upload go func() { for _, task := range d.tasks { @@ -271,6 +274,51 @@ func (d *Downloader) Setup() error { return nil } +// cleanupNonExistingTasks checks for tasks whose files are missing on disk +// and removes them if the AutoCleanMissingFiles config is enabled. +func (d *Downloader) cleanupNonExistingTasks() { + cfg, err := d.GetConfig() + if err != nil { + return + } + + // If the feature is disabled, do nothing + if !cfg.AutoCleanMissingFiles { + return + } + + var tasksToDelete []string + + for _, task := range d.tasks { + if task.Meta == nil || task.Meta.Res == nil { + continue + } + + var targetPath string + // Determine if it is a single file or a directory (multi-file torrent) + if task.Meta.Res.Name != "" { + targetPath = task.Meta.FolderPath() + } else { + targetPath = task.Meta.SingleFilepath() + } + + // Skip if path is empty + if targetPath == "" { + continue + } + + // Check if file/folder exists + if _, err := os.Stat(targetPath); os.IsNotExist(err) { + d.Logger.Info().Msgf("Auto-cleanup: task %s file not found at %s, removing from list", task.ID, targetPath) + tasksToDelete = append(tasksToDelete, task.ID) + } + } + + if len(tasksToDelete) > 0 { + d.Delete(&TaskFilter{IDs: tasksToDelete}, false) + } +} + func (d *Downloader) parseFm(url string) (fetcher.FetcherManager, error) { for _, fm := range d.cfg.FetchManagers { for _, filter := range fm.Filters() { @@ -910,6 +958,10 @@ func (d *Downloader) watch(task *Task) { if e, ok := task.Meta.Opts.Extra.(*http.OptsExtra); ok { downloadFilePath := task.Meta.SingleFilepath() if e.AutoTorrent && strings.HasSuffix(downloadFilePath, ".torrent") { + + cfg, err := d.GetConfig() + shouldDelete := err == nil && cfg.AutoDeleteTorrents + go func() { _, err2 := d.CreateDirect( &base.Request{ @@ -921,8 +973,12 @@ func (d *Downloader) watch(task *Task) { }) if err2 != nil { d.Logger.Error().Err(err2).Msgf("auto create torrent task failed, task id: %s", task.ID) - } + } else { + if shouldDelete { + d.Delete(&TaskFilter{IDs: []string{task.ID}}, true) + } + } }() } diff --git a/ui/flutter/lib/api/model/downloader_config.dart b/ui/flutter/lib/api/model/downloader_config.dart index 868e60db..064b398a 100644 --- a/ui/flutter/lib/api/model/downloader_config.dart +++ b/ui/flutter/lib/api/model/downloader_config.dart @@ -12,6 +12,10 @@ class DownloaderConfig { WebhookConfig webhook = WebhookConfig(); ArchiveConfig archive = ArchiveConfig(); + // New fields for auto-management + bool autoDeleteTorrents = false; + bool autoCleanMissingFiles = false; + DownloaderConfig({ this.downloadDir = '', this.maxRunning = 0, @@ -244,4 +248,4 @@ class ArchiveConfig { json == null ? ArchiveConfig() : _$ArchiveConfigFromJson(json); Map toJson() => _$ArchiveConfigToJson(this); -} +} \ No newline at end of file diff --git a/ui/flutter/lib/api/model/downloader_config.g.dart b/ui/flutter/lib/api/model/downloader_config.g.dart index d89538d1..efc835dc 100644 --- a/ui/flutter/lib/api/model/downloader_config.g.dart +++ b/ui/flutter/lib/api/model/downloader_config.g.dart @@ -18,7 +18,9 @@ DownloaderConfig _$DownloaderConfigFromJson(Map json) => ..webhook = WebhookConfig.fromJson(json['webhook'] as Map?) ..archive = - ArchiveConfig.fromJson(json['archive'] as Map?); + ArchiveConfig.fromJson(json['archive'] as Map?) + ..autoDeleteTorrents = json['autoDeleteTorrents'] as bool + ..autoCleanMissingFiles = json['autoCleanMissingFiles'] as bool; Map _$DownloaderConfigToJson(DownloaderConfig instance) => { @@ -29,6 +31,8 @@ Map _$DownloaderConfigToJson(DownloaderConfig instance) => 'proxy': instance.proxy.toJson(), 'webhook': instance.webhook.toJson(), 'archive': instance.archive.toJson(), + 'autoDeleteTorrents': instance.autoDeleteTorrents, + 'autoCleanMissingFiles': instance.autoCleanMissingFiles, }; ProtocolConfig _$ProtocolConfigFromJson(Map json) => diff --git a/ui/flutter/lib/app/modules/setting/views/setting_view.dart b/ui/flutter/lib/app/modules/setting/views/setting_view.dart index b18f2746..5e23c4a3 100644 --- a/ui/flutter/lib/app/modules/setting/views/setting_view.dart +++ b/ui/flutter/lib/app/modules/setting/views/setting_view.dart @@ -169,6 +169,46 @@ class SettingView extends GetView { ); }); + // New: Auto Delete Torrents + final buildAutoDeleteTorrents = _buildConfigItem('autoDeleteTorrents', () { + return appController.downloaderConfig.value.autoDeleteTorrents + ? 'on'.tr + : 'off'.tr; + }, (Key key) { + return Container( + alignment: Alignment.centerLeft, + child: Switch( + value: appController.downloaderConfig.value.autoDeleteTorrents, + onChanged: (bool value) async { + appController.downloaderConfig.update((val) { + val!.autoDeleteTorrents = value; + }); + await debounceSave(); + }, + ), + ); + }); + + // New: Auto Clean Missing Files + final buildAutoCleanMissingFiles = _buildConfigItem('autoCleanMissingFiles', () { + return appController.downloaderConfig.value.autoCleanMissingFiles + ? 'on'.tr + : 'off'.tr; + }, (Key key) { + return Container( + alignment: Alignment.centerLeft, + child: Switch( + value: appController.downloaderConfig.value.autoCleanMissingFiles, + onChanged: (bool value) async { + appController.downloaderConfig.update((val) { + val!.autoCleanMissingFiles = value; + }); + await debounceSave(); + }, + ), + ); + }); + // Archive auto extract configuration final buildAutoExtract = _buildConfigItem('autoExtract', () { return appController.downloaderConfig.value.archive.autoExtract @@ -1414,6 +1454,8 @@ class SettingView extends GetView { buildMaxRunning(), buildDefaultDirectDownload(), buildAutoStartTasks(), + buildAutoDeleteTorrents(), + buildAutoCleanMissingFiles(), buildBrowserExtension(), buildAutoStartup(), buildMenubarMode(), @@ -1948,4 +1990,4 @@ extension ProxyMode on ProxyConfig { break; } } -} +} \ No newline at end of file diff --git a/ui/flutter/lib/i18n/langs/de_de.dart b/ui/flutter/lib/i18n/langs/de_de.dart index 46b59d70..740e0fe5 100644 --- a/ui/flutter/lib/i18n/langs/de_de.dart +++ b/ui/flutter/lib/i18n/langs/de_de.dart @@ -42,6 +42,8 @@ const deDE = { 'maxRunning': 'Maximale Anzahl ausgeführter Aufgaben', 'defaultDirectDownload': 'Direkten Download standardmäßig aktivieren', 'autoStartTasks': 'Unvollständige Aufgaben beim Start automatisch starten', + 'autoDeleteTorrents': 'Automatisches Löschen von .torrent-Dateien', + 'autoCleanMissingFiles': 'Automatisches Bereinigen fehlender Dateien', 'items': '@count Artikel', 'subscribeTracker': 'Abonnement-Tracker', 'subscribeFail': diff --git a/ui/flutter/lib/i18n/langs/en_us.dart b/ui/flutter/lib/i18n/langs/en_us.dart index 783111fd..9f3cec18 100644 --- a/ui/flutter/lib/i18n/langs/en_us.dart +++ b/ui/flutter/lib/i18n/langs/en_us.dart @@ -39,6 +39,8 @@ const enUS = { 'maxRunning': 'Max Running Tasks', 'defaultDirectDownload': 'Check direct download by default', 'autoStartTasks': 'Auto-start incomplete tasks on startup', + 'autoDeleteTorrents': 'Auto delete .torrent files', + 'autoCleanMissingFiles': 'Auto clean missing files', 'items': '@count items', 'subscribeTracker': 'Subscribe Tracker', 'subscribeFail': @@ -172,4 +174,4 @@ const enUS = { 'githubMirrorUrl': 'Mirror URL', 'githubMirrorUrlHint': 'Enter mirror URL', }, -}; +}; \ No newline at end of file diff --git a/ui/flutter/lib/i18n/langs/es_es.dart b/ui/flutter/lib/i18n/langs/es_es.dart index 7bb793f8..537ddb99 100644 --- a/ui/flutter/lib/i18n/langs/es_es.dart +++ b/ui/flutter/lib/i18n/langs/es_es.dart @@ -39,6 +39,8 @@ const esES = { 'useServerCtime': 'Usar tiempo del servidor para la creación de archivos', 'maxRunning': 'Máximo de Tareas en Ejecución', 'autoStartTasks': 'Iniciar automáticamente tareas incompletas al arrancar', + 'autoDeleteTorrents': 'Eliminar automáticamente archivos .torrent', + 'autoCleanMissingFiles': 'Limpiar automáticamente archivos faltantes', 'items': '@count elementos', 'subscribeTracker': 'Suscribirse al Tracker', 'subscribeFail': diff --git a/ui/flutter/lib/i18n/langs/fa_ir.dart b/ui/flutter/lib/i18n/langs/fa_ir.dart index 555b9e65..db43da8d 100644 --- a/ui/flutter/lib/i18n/langs/fa_ir.dart +++ b/ui/flutter/lib/i18n/langs/fa_ir.dart @@ -26,6 +26,8 @@ const faIR = { 'downloadDir': 'دایرکتوری دانلود', 'downloadDirValid': 'لطفا دایرکتوری دانلود را انتخاب کنید', 'connections': 'اتصالات', + 'autoDeleteTorrents': 'حذف خودکار فایل‌های .torrent', + 'autoCleanMissingFiles': 'پاکسازی خودکار فایل‌های گم شده', 'items': '@count آیتم ها', 'subscribeTracker': 'Subscribe Tracker', 'subscribeFail': diff --git a/ui/flutter/lib/i18n/langs/fr_fr.dart b/ui/flutter/lib/i18n/langs/fr_fr.dart index 7bcca821..a805822b 100644 --- a/ui/flutter/lib/i18n/langs/fr_fr.dart +++ b/ui/flutter/lib/i18n/langs/fr_fr.dart @@ -40,6 +40,8 @@ const frFR = { 'maxRunning': 'Tâches en cours maximum', 'autoStartTasks': 'Démarrer automatiquement les tâches incomplètes au lancement', + 'autoDeleteTorrents': 'Supprimer automatiquement les fichiers .torrent', + 'autoCleanMissingFiles': 'Nettoyer automatiquement les fichiers manquants', 'items': '@count éléments', 'subscribeTracker': 'S\'abonner au tracker', 'subscribeFail': diff --git a/ui/flutter/lib/i18n/langs/hu_hu.dart b/ui/flutter/lib/i18n/langs/hu_hu.dart index 9c40ba9b..60bbb7e1 100644 --- a/ui/flutter/lib/i18n/langs/hu_hu.dart +++ b/ui/flutter/lib/i18n/langs/hu_hu.dart @@ -40,6 +40,8 @@ const huHU = { 'maxRunning': 'Maximális futó feladatok', 'defaultDirectDownload': 'Alapértelmezettként ellenőrizze a közvetlen letöltést', + 'autoDeleteTorrents': 'Automatikus .torrent fájlok törlése', + 'autoCleanMissingFiles': 'Hiányzó fájlok automatikus tisztítása', 'items': '@count elem', 'subscribeTracker': 'Nyomkövető előfizetése', 'subscribeFail': diff --git a/ui/flutter/lib/i18n/langs/id_id.dart b/ui/flutter/lib/i18n/langs/id_id.dart index 6be4d1f5..f29d1ce0 100644 --- a/ui/flutter/lib/i18n/langs/id_id.dart +++ b/ui/flutter/lib/i18n/langs/id_id.dart @@ -39,6 +39,8 @@ const idID = { 'useServerCtime': 'Gunakan waktu server untuk pembuatan file', 'maxRunning': 'Tugas Maksimum yang Berjalan', 'defaultDirectDownload': 'Pilih unduhan langsung sebagai opsi standar', + 'autoDeleteTorrents': 'Otomatis hapus file .torrent', + 'autoCleanMissingFiles': 'Otomatis bersihkan file yang hilang', 'items': '@count items', 'subscribeTracker': 'Berlangganan Tracker', 'subscribeFail': diff --git a/ui/flutter/lib/i18n/langs/it_it.dart b/ui/flutter/lib/i18n/langs/it_it.dart index 90cdcd39..0589c9ca 100644 --- a/ui/flutter/lib/i18n/langs/it_it.dart +++ b/ui/flutter/lib/i18n/langs/it_it.dart @@ -37,6 +37,8 @@ const itIT = { 'connections': 'Connessioni', 'useServerCtime': "Utilizza l'ora del server per la creazione dei file", 'maxRunning': 'Numero massimo di attività in esecuzione', + 'autoDeleteTorrents': 'Elimina automaticamente i file .torrent', + 'autoCleanMissingFiles': 'Pulisci automaticamente i file mancanti', 'items': '@count elementi', 'subscribeTracker': 'Iscriviti al tracker', 'subscribeFail': diff --git a/ui/flutter/lib/i18n/langs/ja_jp.dart b/ui/flutter/lib/i18n/langs/ja_jp.dart index 94f156fe..bf294b91 100644 --- a/ui/flutter/lib/i18n/langs/ja_jp.dart +++ b/ui/flutter/lib/i18n/langs/ja_jp.dart @@ -30,6 +30,8 @@ const jaJP = { 'connections': '接続', 'maxRunning': '最大実行タスク', 'autoStartTasks': '起動時に未完了のタスクを自動的に開始', + 'autoDeleteTorrents': '.torrentファイルを自動削除', + 'autoCleanMissingFiles': '欠落ファイルを自動クリーン', 'items': '@count アイテム', 'subscribeTracker': 'トラッカーを購読', 'subscribeFail': '登録に失敗しました。ネットワークを確認するか、後でもう一度お試しください', diff --git a/ui/flutter/lib/i18n/langs/pl_pl.dart b/ui/flutter/lib/i18n/langs/pl_pl.dart index 2f86e3e8..8d4aa1b4 100644 --- a/ui/flutter/lib/i18n/langs/pl_pl.dart +++ b/ui/flutter/lib/i18n/langs/pl_pl.dart @@ -36,6 +36,8 @@ const plPL = { 'connections': 'Połączenia', 'useServerCtime': 'Użyj czasu serwera do utworzenia pliku', 'maxRunning': 'Maksymalna liczba zadań', + 'autoDeleteTorrents': 'Automatycznie usuń pliki .torrent', + 'autoCleanMissingFiles': 'Automatycznie wyczyść brakujące pliki', 'items': '@count items', 'subscribeTracker': 'Subskrybuj tracker', 'subscribeFail': 'Błąd subskrypcji, sprawdź łączność z internetem', diff --git a/ui/flutter/lib/i18n/langs/pt_br.dart b/ui/flutter/lib/i18n/langs/pt_br.dart index b15145e2..2c42ae2c 100644 --- a/ui/flutter/lib/i18n/langs/pt_br.dart +++ b/ui/flutter/lib/i18n/langs/pt_br.dart @@ -39,6 +39,8 @@ const ptBR = { 'useServerCtime': 'Use server time for file creation', 'maxRunning': 'Máximo de Tarefas Simultâneas', 'defaultDirectDownload': 'Marcar download direto por padrão', + 'autoDeleteTorrents': 'Excluir automaticamente arquivos .torrent', + 'autoCleanMissingFiles': 'Limpar automaticamente arquivos ausentes', 'items': '@count itens', 'subscribeTracker': 'Assinar Rastreador', 'subscribeFail': diff --git a/ui/flutter/lib/i18n/langs/ru_ru.dart b/ui/flutter/lib/i18n/langs/ru_ru.dart index 335405e5..6a5e9976 100644 --- a/ui/flutter/lib/i18n/langs/ru_ru.dart +++ b/ui/flutter/lib/i18n/langs/ru_ru.dart @@ -40,6 +40,8 @@ const ruRU = { 'maxRunning': 'Максимальное количество активных задач', 'defaultDirectDownload': 'Прямая загрузка по умолчанию', 'autoStartTasks': 'Автоматически запускать незавершённые задачи при старте', + 'autoDeleteTorrents': 'Автоудаление торрент-файлов', + 'autoCleanMissingFiles': 'Автоочистка отсутствующих файлов', 'items': '@подсчет элементов', 'subscribeTracker': 'Подпись трекера', 'subscribeFail': @@ -149,4 +151,4 @@ const ruRU = { 'builtInCategory': 'Встроенную категорию нельзя удалить', 'selectCategory': 'Выбрать категорию', } -}; +}; \ No newline at end of file diff --git a/ui/flutter/lib/i18n/langs/ta_ta.dart b/ui/flutter/lib/i18n/langs/ta_ta.dart index 9fa1e35a..af02b75c 100644 --- a/ui/flutter/lib/i18n/langs/ta_ta.dart +++ b/ui/flutter/lib/i18n/langs/ta_ta.dart @@ -37,6 +37,8 @@ const taTA = { 'connections': 'தொடர்புகள்', 'useServerCtime': 'Use server time for file creation', 'maxRunning': 'அதிகபட்ச இயங்கும் பணிகள்', + 'autoDeleteTorrents': 'தானாக .torrent கோப்புகளை நீக்கு', + 'autoCleanMissingFiles': 'காணாமல் போன கோப்புகளை தானாக சுத்தம் செய்', 'items': '@count items', 'subscribeTracker': 'Subscribe Tracker', 'subscribeFail': diff --git a/ui/flutter/lib/i18n/langs/tr_tr.dart b/ui/flutter/lib/i18n/langs/tr_tr.dart index 15f99862..64741ab9 100644 --- a/ui/flutter/lib/i18n/langs/tr_tr.dart +++ b/ui/flutter/lib/i18n/langs/tr_tr.dart @@ -39,6 +39,8 @@ const trTR = { 'useServerCtime': 'Dosya oluşturma için sunucu zamanını kullan', 'maxRunning': 'Maksimum Çalışan Görev', 'defaultDirectDownload': 'Varsayılan olarak doğrudan indirmeyi seç', + 'autoDeleteTorrents': '.torrent dosyalarını otomatik sil', + 'autoCleanMissingFiles': 'Eksik dosyaları otomatik temizle', 'items': '@count öğe', 'subscribeTracker': 'Tracker Aboneliği', 'subscribeFail': diff --git a/ui/flutter/lib/i18n/langs/uk_ua.dart b/ui/flutter/lib/i18n/langs/uk_ua.dart index 71c51597..5d2f571e 100644 --- a/ui/flutter/lib/i18n/langs/uk_ua.dart +++ b/ui/flutter/lib/i18n/langs/uk_ua.dart @@ -39,6 +39,8 @@ const ukUA = { 'useServerCtime': 'Використовувати час сервера для створення файлу', 'maxRunning': 'Макс. завдань у черзі', 'defaultDirectDownload': 'Пряме завантаження за замовчуванням', + 'autoDeleteTorrents': 'Автоматично видаляти файли .torrent', + 'autoCleanMissingFiles': 'Автоматично очищати відсутні файли', 'items': '@count елементів', 'subscribeTracker': 'Підписатися на трекер', 'subscribeFail': diff --git a/ui/flutter/lib/i18n/langs/vi_vn.dart b/ui/flutter/lib/i18n/langs/vi_vn.dart index e2787d88..5d2e4666 100644 --- a/ui/flutter/lib/i18n/langs/vi_vn.dart +++ b/ui/flutter/lib/i18n/langs/vi_vn.dart @@ -36,6 +36,8 @@ const viVN = { 'connections': 'Kết nối', 'useServerCtime': 'Sử dụng thời gian máy chủ cho việc tạo tệp', 'maxRunning': 'Số nhiệm vụ tối đa', + 'autoDeleteTorrents': 'Tự động xóa tệp .torrent', + 'autoCleanMissingFiles': 'Tự động dọn dẹp tệp bị thiếu', 'items': '@count mục', 'subscribeTracker': 'Theo dõi Tracker', 'subscribeFail': diff --git a/ui/flutter/lib/i18n/langs/zh_cn.dart b/ui/flutter/lib/i18n/langs/zh_cn.dart index 39c4fe9e..30ff6b3f 100644 --- a/ui/flutter/lib/i18n/langs/zh_cn.dart +++ b/ui/flutter/lib/i18n/langs/zh_cn.dart @@ -39,6 +39,8 @@ const zhCN = { 'maxRunning': '最大下载数', 'defaultDirectDownload': '默认勾选直接下载', 'autoStartTasks': '启动时自动开始未完成的任务', + 'autoDeleteTorrents': '自动删除 .torrent 文件', + 'autoCleanMissingFiles': '自动清理缺失文件', 'items': '@count 项', 'subscribeTracker': '订阅 Tracker', 'subscribeFail': '订阅失败,请检查网络或稍后重试', diff --git a/ui/flutter/lib/i18n/langs/zh_tw.dart b/ui/flutter/lib/i18n/langs/zh_tw.dart index aa162750..f44c0bc0 100644 --- a/ui/flutter/lib/i18n/langs/zh_tw.dart +++ b/ui/flutter/lib/i18n/langs/zh_tw.dart @@ -39,6 +39,8 @@ const zhTW = { 'maxRunning': '最大執行任務數', 'defaultDirectDownload': '預設勾選直接下載', 'autoStartTasks': '啟動時自動開始未完成的任務', + 'autoDeleteTorrents': '自動刪除 .torrent 檔案', + 'autoCleanMissingFiles': '自動清理缺失檔案', 'items': '@count 個項目', 'subscribeTracker': '訂閱 Tracker', 'subscribeFail': '訂閱失敗,請檢查網路或稍後再試',