use std::collections::HashMap; use tauri::{AppHandle, Manager, State}; use tauri_plugin_opener::OpenerExt; use super::engine::{CheckUrlResult, DownloadEngine}; use super::task::{DownloadTask, DownloaderSettings}; use super::torrent::TorrentInfo; /// 获取所有任务 #[tauri::command] #[specta::specta] pub fn downloader_get_tasks(engine: State<'_, DownloadEngine>) -> Vec { engine.get_tasks() } /// 解析磁力链 / .torrent 文件,返回种子信息(名称 / infohash / 文件列表),供前端做文件勾选 #[tauri::command] #[specta::specta] pub async fn downloader_inspect(engine: State<'_, DownloadEngine>, input: String) -> Result { engine.inspect(&input).await } /// 磁力任务:元数据解析成功后,用户勾选文件并确认开始下载 #[tauri::command] #[specta::specta] pub async fn downloader_select_bt_files(engine: State<'_, DownloadEngine>, id: String, only_files: Vec) -> Result<(), String> { engine.select_bt_files(&id, only_files).await } /// 检查 URL 重复性并探测文件信息(添加下载前调用) #[tauri::command] #[specta::specta] pub async fn downloader_check_url( engine: State<'_, DownloadEngine>, url: String, dir: Option, headers: Option>, ) -> Result { let headers = headers.unwrap_or_default(); let (probe, duplicate, existing) = engine.check_url(&url, dir.as_deref(), &headers).await; let result = match probe { Ok(p) => CheckUrlResult { ok: true, error: None, filename: p.filename.clone(), total_size: p.total_size, supports_resume: p.supports_resume, duplicate, existing, }, Err(e) => CheckUrlResult { ok: false, error: Some(e), filename: None, total_size: None, supports_resume: false, duplicate, existing, }, }; Ok(result) } /// 添加下载任务 #[tauri::command] #[specta::specta] pub async fn downloader_add_task( engine: State<'_, DownloadEngine>, url: String, filename: Option, dir: Option, headers: Option>, auto_rename: Option, only_files: Option>, ) -> Result { engine.add_task(url, filename, dir, headers.unwrap_or_default(), auto_rename.unwrap_or(false), only_files).await } /// 暂停任务 #[tauri::command] #[specta::specta] pub fn downloader_pause_task(engine: State<'_, DownloadEngine>, id: String) -> Result<(), String> { engine.pause_task(&id) } /// 恢复任务 #[tauri::command] #[specta::specta] pub fn downloader_resume_task(engine: State<'_, DownloadEngine>, id: String) -> Result<(), String> { engine.resume_task(&id) } /// 取消任务(置为已取消,清空进度并删除下载文件,但保留记录) #[tauri::command] #[specta::specta] pub fn downloader_cancel_task(engine: State<'_, DownloadEngine>, id: String) -> Result<(), String> { engine.cancel_task(&id) } /// 重新下载已取消/出错的任务 #[tauri::command] #[specta::specta] pub async fn downloader_redownload(engine: State<'_, DownloadEngine>, id: String) -> Result<(), String> { engine.redownload(&id).await } /// 移除任务 #[tauri::command] #[specta::specta] pub fn downloader_remove_task( engine: State<'_, DownloadEngine>, id: String, delete_files: Option, ) -> Result<(), String> { engine.remove_task(&id, delete_files.unwrap_or(false)) } /// 获取设置 #[tauri::command] #[specta::specta] pub fn downloader_get_settings(engine: State<'_, DownloadEngine>) -> DownloaderSettings { engine.get_settings() } /// 保存设置 #[tauri::command] #[specta::specta] pub fn downloader_save_settings( engine: State<'_, DownloadEngine>, settings: DownloaderSettings, ) -> Result<(), String> { engine.save_settings(settings); Ok(()) } /// 引擎状态(始终运行中) #[tauri::command] pub fn downloader_status(engine: State<'_, DownloadEngine>) -> serde_json::Value { serde_json::json!({ "running": engine.is_started(), }) } /// 获取扩展服务信息 #[tauri::command] pub fn downloader_get_extension_info(engine: State<'_, DownloadEngine>) -> serde_json::Value { let settings = engine.get_settings(); serde_json::json!({ "url": format!("http://127.0.0.1:{}/", settings.extension_port), "port": settings.extension_port, "secret": settings.extension_secret, "hasSecret": !settings.extension_secret.is_empty(), }) } /// 用系统资源管理器打开目录 #[tauri::command] #[specta::specta] pub fn downloader_open_dir(app: AppHandle, path: String) -> Result<(), String> { app.opener() .open_path(path, None::<&str>) .map_err(|e| format!("打开目录失败: {}", e)) } /// 用系统默认浏览器打开 URL #[tauri::command] #[specta::specta] pub fn downloader_open_url(app: AppHandle, url: String) -> Result<(), String> { app.opener() .open_url(url, None::<&str>) .map_err(|e| format!("打开链接失败: {}", e)) } /// 将指定 label 的下载窗口显示并强制置为前台。 /// Tauri 的 set_focus 在 Windows 上受前台锁定限制(尤其下载窗口由后台进程创建、 /// 或创建到非主显示器时更明显),改用原生 SetForegroundWindow + BringWindowToTop /// (模拟 Alt 键重置前台锁定),保证开始/完成下载时窗口能正确定位到前台。 #[tauri::command] #[specta::specta] pub fn downloader_focus_window(app: AppHandle, label: String) -> Result<(), String> { let Some(window) = app.get_webview_window(&label) else { return Ok(()); // 窗口已关闭则忽略 }; window.show().map_err(|e| e.to_string())?; window.unminimize().map_err(|e| e.to_string())?; match window.hwnd() { Ok(hwnd) => crate::win32_util::force_foreground(hwnd.0 as isize), Err(_) => { window.set_focus().ok(); } } Ok(()) }