diff --git a/apps/desktop/src-tauri/Cargo.lock b/apps/desktop/src-tauri/Cargo.lock index b7d7f20..5561741 100644 --- a/apps/desktop/src-tauri/Cargo.lock +++ b/apps/desktop/src-tauri/Cargo.lock @@ -316,6 +316,12 @@ dependencies = [ "serde_core", ] +[[package]] +name = "block" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" + [[package]] name = "block-buffer" version = "0.10.4" @@ -527,11 +533,43 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" dependencies = [ "iana-time-zone", + "js-sys", "num-traits", "serde", + "wasm-bindgen", "windows-link 0.2.1", ] +[[package]] +name = "cocoa" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6140449f97a6e97f9511815c5632d84c8aacf8ac271ad77c559218161a1373c" +dependencies = [ + "bitflags 1.3.2", + "block", + "cocoa-foundation", + "core-foundation 0.9.4", + "core-graphics 0.23.2", + "foreign-types 0.5.0", + "libc", + "objc", +] + +[[package]] +name = "cocoa-foundation" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c6234cbb2e4c785b456c0644748b1ac416dd045799740356f8363dfe00c93f7" +dependencies = [ + "bitflags 1.3.2", + "block", + "core-foundation 0.9.4", + "core-graphics-types 0.1.3", + "libc", + "objc", +] + [[package]] name = "combine" version = "4.6.7" @@ -607,6 +645,19 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +[[package]] +name = "core-graphics" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081" +dependencies = [ + "bitflags 1.3.2", + "core-foundation 0.9.4", + "core-graphics-types 0.1.3", + "foreign-types 0.5.0", + "libc", +] + [[package]] name = "core-graphics" version = "0.25.0" @@ -615,11 +666,22 @@ checksum = "064badf302c3194842cf2c5d61f56cc88e54a759313879cdf03abdd27d0c3b97" dependencies = [ "bitflags 2.13.0", "core-foundation 0.10.1", - "core-graphics-types", + "core-graphics-types 0.2.0", "foreign-types 0.5.0", "libc", ] +[[package]] +name = "core-graphics-types" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" +dependencies = [ + "bitflags 1.3.2", + "core-foundation 0.9.4", + "libc", +] + [[package]] name = "core-graphics-types" version = "0.2.0" @@ -2383,6 +2445,15 @@ dependencies = [ "time", ] +[[package]] +name = "malloc_buf" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +dependencies = [ + "libc", +] + [[package]] name = "markup5ever" version = "0.38.0" @@ -2602,6 +2673,15 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "objc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +dependencies = [ + "malloc_buf", +] + [[package]] name = "objc2" version = "0.6.4" @@ -4226,7 +4306,7 @@ dependencies = [ "bitflags 2.13.0", "block2", "core-foundation 0.10.1", - "core-graphics", + "core-graphics 0.25.0", "crossbeam-channel", "dbus", "dispatch2", @@ -4331,10 +4411,13 @@ name = "tauri-app" version = "0.7.3" dependencies = [ "axum", + "chrono", + "cocoa", "futures-util", "hmac", "keepawake", "keyring", + "objc", "open", "regex 1.12.4", "reqwest 0.12.28", diff --git a/apps/desktop/src-tauri/Cargo.toml b/apps/desktop/src-tauri/Cargo.toml index f75adbc..0ed1590 100644 --- a/apps/desktop/src-tauri/Cargo.toml +++ b/apps/desktop/src-tauri/Cargo.toml @@ -46,3 +46,5 @@ tokio-tungstenite = "0.29.0" futures-util = { version = "0.3.32", features = ["sink"] } rusqlite = { version = "0.40.1", features = ["bundled"] } chrono = "0.4.38" +cocoa = "0.25" +objc = "0.2.7" diff --git a/apps/desktop/src-tauri/src/lib.rs b/apps/desktop/src-tauri/src/lib.rs index 3a8d9dc..ee3820d 100644 --- a/apps/desktop/src-tauri/src/lib.rs +++ b/apps/desktop/src-tauri/src/lib.rs @@ -1,6 +1,5 @@ // Learn more about Tauri commands at https://tauri.app/develop/calling-rust/ use tauri::{Manager, Emitter}; -use std::process::Command; use tokio::process::Command as AsyncCommand; use std::process::Stdio; use tokio::io::{AsyncBufReadExt, BufReader}; @@ -183,9 +182,10 @@ async fn test_ytdlp(app_handle: tauri::AppHandle) -> Result { let ytdlp_path = resource_dir.join("binaries").join("yt-dlp"); println!("Resolved yt-dlp path: {:?}", ytdlp_path); - let output = Command::new(&ytdlp_path) + let output = AsyncCommand::new(&ytdlp_path) .arg("--version") .output() + .await .map_err(|e| { println!("Failed to execute: {}", e); format!("Failed to execute yt-dlp: {}", e) @@ -210,9 +210,10 @@ async fn test_aria2c(app_handle: tauri::AppHandle) -> Result { let aria2c_path = resource_dir.join("binaries").join("aria2c"); println!("Resolved aria2c path: {:?}", aria2c_path); - let output = Command::new(&aria2c_path) + let output = AsyncCommand::new(&aria2c_path) .arg("--version") .output() + .await .map_err(|e| { println!("Failed to execute: {}", e); format!("Failed to execute aria2c: {}", e) @@ -240,9 +241,10 @@ async fn test_ffmpeg(app_handle: tauri::AppHandle) -> Result { let ffmpeg_path = resource_dir.join("binaries").join("ffmpeg"); println!("Resolved ffmpeg path: {:?}", ffmpeg_path); - let output = Command::new(&ffmpeg_path) + let output = AsyncCommand::new(&ffmpeg_path) .arg("-version") .output() + .await .map_err(|e| { println!("Failed to execute: {}", e); format!("Failed to execute ffmpeg: {}", e) @@ -270,9 +272,10 @@ async fn test_deno(app_handle: tauri::AppHandle) -> Result { let deno_path = resource_dir.join("binaries").join("deno"); println!("Resolved deno path: {:?}", deno_path); - let output = Command::new(&deno_path) + let output = AsyncCommand::new(&deno_path) .arg("--version") .output() + .await .map_err(|e| { println!("Failed to execute: {}", e); format!("Failed to execute deno: {}", e) @@ -859,12 +862,18 @@ async fn remove_download(state: tauri::State<'_, AppState>, id: String, filepath fn update_dock_badge(_app_handle: tauri::AppHandle, count: i32) { #[cfg(target_os = "macos")] { - let label = if count > 0 { count.to_string() } else { "".to_string() }; - let script = format!("tell application \"System Events\" to set the badge of application process \"Firelink\" to \"{}\"", label); - let _ = std::process::Command::new("osascript") - .arg("-e") - .arg(script) - .status(); + use cocoa::appkit::NSApp; + use cocoa::base::{nil, id}; + use cocoa::foundation::NSString; + use objc::{msg_send, sel, sel_impl}; + + unsafe { + let app = NSApp(); + let dock_tile: id = msg_send![app, dockTile]; + let label = if count > 0 { count.to_string() } else { "".to_string() }; + let ns_label = NSString::alloc(nil).init_str(&label); + let _: () = msg_send![dock_tile, setBadgeLabel: ns_label]; + } } } @@ -919,16 +928,21 @@ async fn set_global_speed_limit(state: tauri::State<'_, AppState>, limit: Option fn request_automation_permission() -> Result<(), String> { #[cfg(target_os = "macos")] { - let status = Command::new("osascript") - .arg("-e") - .arg("tell application \"Finder\" to get name") - .status() - .map_err(|error| error.to_string())?; - return if status.success() { - Ok(()) - } else { - Err("Automation permission was not granted".to_string()) - }; + use cocoa::foundation::NSString; + use cocoa::base::{nil, id}; + use objc::{msg_send, sel, sel_impl, class}; + + unsafe { + let script_str = NSString::alloc(nil).init_str("tell application \"Finder\" to get name"); + let ns_apple_script: id = msg_send![class!(NSAppleScript), alloc]; + let ns_apple_script: id = msg_send![ns_apple_script, initWithSource: script_str]; + let mut error_dict: id = nil; + let result: id = msg_send![ns_apple_script, executeAndReturnError: &mut error_dict]; + if result == nil { + return Err("Automation permission was not granted".to_string()); + } + } + return Ok(()); } #[cfg(not(target_os = "macos"))] @@ -936,18 +950,13 @@ fn request_automation_permission() -> Result<(), String> { } #[tauri::command] -fn open_automation_settings() -> Result<(), String> { +fn open_automation_settings(app_handle: tauri::AppHandle) -> Result<(), String> { #[cfg(target_os = "macos")] { - let status = Command::new("open") - .arg("x-apple.systempreferences:com.apple.preference.security?Privacy_Automation") - .status() - .map_err(|error| error.to_string())?; - return if status.success() { - Ok(()) - } else { - Err("Failed to open Automation settings".to_string()) - }; + use tauri_plugin_opener::OpenerExt; + app_handle.opener().open_url("x-apple.systempreferences:com.apple.preference.security?Privacy_Automation", None::) + .map_err(|e| format!("Failed to open Automation settings: {}", e))?; + return Ok(()); } #[cfg(not(target_os = "macos"))] diff --git a/apps/desktop/src-tauri/src/scheduler.rs b/apps/desktop/src-tauri/src/scheduler.rs index 908232b..5cb9e38 100644 --- a/apps/desktop/src-tauri/src/scheduler.rs +++ b/apps/desktop/src-tauri/src/scheduler.rs @@ -50,8 +50,8 @@ pub fn spawn_scheduler(app_handle: tauri::AppHandle) { let date_key = now.format("%Y-%m-%d").to_string(); let trigger_key = format!("{}-{}", date_key, current_time); - let last_start_key = settings.get("schedulerLastStartKey").and_then(|v| v.as_str()).unwrap_or(""); - let last_stop_key = settings.get("schedulerLastStopKey").and_then(|v| v.as_str()).unwrap_or(""); + let last_start_key = settings.get("schedulerLastStartKey").and_then(|v| v.as_str()).unwrap_or("").to_string(); + let last_stop_key = settings.get("schedulerLastStopKey").and_then(|v| v.as_str()).unwrap_or("").to_string(); if scheduler.start_time == current_time && last_start_key != trigger_key { settings["schedulerLastStartKey"] = serde_json::json!(trigger_key.clone());