mod engine; use serde::{Deserialize, Serialize}; use std::fs; use std::io::Write; use std::path::PathBuf; use tauri::Manager; use engine::{ aria2_add_torrent, aria2_add_uri, aria2_change_global_option, aria2_get_task_detail, aria2_list_tasks, aria2_pause_all, aria2_pause_task, aria2_remove_task, aria2_remove_task_record, aria2_resume_all, aria2_resume_task, detect_aria2_binary, engine_start, engine_status, engine_stop, load_torrent_file, open_path_in_file_manager, stop_engine_for_exit, EngineState, }; #[tauri::command] async fn focus_main_window(app: tauri::AppHandle) -> Result<(), String> { let window = app .get_webview_window("main") .ok_or_else(|| "메인 창을 찾을 수 없습니다.".to_string())?; window .show() .map_err(|error| format!("창 표시 실패: {error}"))?; window .unminimize() .map_err(|error| format!("창 복원 실패: {error}"))?; window .set_focus() .map_err(|error| format!("창 포커스 실패: {error}"))?; Ok(()) } #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] struct ExternalAddRequest { url: String, out: Option, dir: Option, referer: Option, user_agent: Option, authorization: Option, cookie: Option, proxy: Option, split: Option, } fn external_add_queue_path() -> Result { let home = std::env::var("HOME").map_err(|err| format!("HOME 경로 확인 실패: {err}"))?; Ok(PathBuf::from(home).join(".gdown").join("external_add_queue.jsonl")) } #[tauri::command] fn take_external_add_requests() -> Result, String> { let path = external_add_queue_path()?; let parent = path .parent() .ok_or_else(|| "큐 디렉터리 경로를 계산할 수 없습니다.".to_string())?; fs::create_dir_all(parent).map_err(|err| format!("큐 디렉터리 생성 실패: {err}"))?; if !path.exists() { return Ok(Vec::new()); } let content = fs::read_to_string(&path).map_err(|err| format!("큐 읽기 실패: {err}"))?; let mut requests: Vec = Vec::new(); for line in content.lines() { let trimmed = line.trim(); if trimmed.is_empty() { continue; } if let Ok(item) = serde_json::from_str::(trimmed) { requests.push(item); } } let mut file = fs::OpenOptions::new() .write(true) .truncate(true) .open(&path) .map_err(|err| format!("큐 초기화 실패: {err}"))?; file .write_all(b"") .map_err(|err| format!("큐 파일 쓰기 실패: {err}"))?; Ok(requests) } #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { tauri::Builder::default() .manage(EngineState::default()) .plugin(tauri_plugin_dialog::init()) .plugin(tauri_plugin_deep_link::init()) .on_window_event(|window, event| { if let tauri::WindowEvent::CloseRequested { .. } = event { if window.label() == "main" { let state = window.state::(); stop_engine_for_exit(&state); } } }) .setup(|app| { if cfg!(debug_assertions) { app.handle().plugin( tauri_plugin_log::Builder::default() .level(log::LevelFilter::Info) .build(), )?; } Ok(()) }) .invoke_handler(tauri::generate_handler![ engine_start, engine_stop, engine_status, detect_aria2_binary, aria2_add_torrent, aria2_add_uri, aria2_change_global_option, aria2_get_task_detail, aria2_list_tasks, aria2_pause_task, aria2_resume_task, aria2_remove_task, aria2_remove_task_record, aria2_pause_all, aria2_resume_all, load_torrent_file, open_path_in_file_manager, focus_main_window, take_external_add_requests ]) .run(tauri::generate_context!()) .expect("error while running tauri application"); }