2026-09-21 10:57:43 -04:00
|
|
|
use crate::models::{CookieSource, VideoMetadata};
|
2026-09-21 11:00:46 -04:00
|
|
|
use crate::services::{download_manager, video_resolver};
|
|
|
|
|
use serde::Serialize;
|
|
|
|
|
use std::io::{BufRead, BufReader};
|
|
|
|
|
use tauri::ipc::Channel;
|
2026-09-21 10:57:43 -04:00
|
|
|
|
|
|
|
|
#[tauri::command]
|
|
|
|
|
pub async fn resolve_url(
|
|
|
|
|
url: String,
|
|
|
|
|
cookie_source: CookieSource,
|
|
|
|
|
) -> Result<VideoMetadata, String> {
|
|
|
|
|
tokio::task::spawn_blocking(move || video_resolver::resolve(&url, &cookie_source))
|
|
|
|
|
.await
|
|
|
|
|
.map_err(|e| format!("Task failed: {e}"))?
|
|
|
|
|
}
|
2026-09-21 11:00:46 -04:00
|
|
|
|
|
|
|
|
#[derive(Clone, Serialize)]
|
|
|
|
|
#[serde(rename_all = "camelCase", tag = "event", content = "data")]
|
|
|
|
|
pub enum DownloadEvent {
|
|
|
|
|
Progress { percent: f64 },
|
|
|
|
|
FilePath { path: String },
|
|
|
|
|
Finished { success: bool, path: String },
|
|
|
|
|
Error { message: String },
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[tauri::command]
|
|
|
|
|
pub async fn start_download(
|
|
|
|
|
url: String,
|
|
|
|
|
cookie_source: CookieSource,
|
|
|
|
|
on_event: Channel<DownloadEvent>,
|
|
|
|
|
) -> Result<(), String> {
|
|
|
|
|
tokio::task::spawn_blocking(move || {
|
|
|
|
|
let temp_dir = std::env::temp_dir()
|
|
|
|
|
.join("video-clipper")
|
|
|
|
|
.to_string_lossy()
|
|
|
|
|
.to_string();
|
|
|
|
|
std::fs::create_dir_all(&temp_dir)
|
|
|
|
|
.map_err(|e| format!("Failed to create temp dir: {e}"))?;
|
|
|
|
|
|
|
|
|
|
let (mut child, _template) =
|
|
|
|
|
download_manager::start_download(&url, &cookie_source, &temp_dir)?;
|
|
|
|
|
|
|
|
|
|
let Some(stderr) = child.stderr.take() else {
|
|
|
|
|
let status = child
|
|
|
|
|
.wait()
|
|
|
|
|
.map_err(|e| format!("Download failed: {e}"))?;
|
|
|
|
|
let _ = on_event.send(DownloadEvent::Finished {
|
|
|
|
|
success: status.success(),
|
|
|
|
|
path: String::new(),
|
|
|
|
|
});
|
|
|
|
|
return Ok(());
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let reader = BufReader::new(stderr);
|
|
|
|
|
let mut last_file_path: Option<String> = None;
|
|
|
|
|
|
|
|
|
|
for line in reader.lines().map_while(Result::ok) {
|
|
|
|
|
if let Some(pct) = download_manager::parse_progress_line(&line) {
|
|
|
|
|
let _ = on_event.send(DownloadEvent::Progress { percent: pct });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if line.contains("[download] Destination:")
|
|
|
|
|
|| line.contains("[Merger] Merging formats into")
|
|
|
|
|
{
|
|
|
|
|
if let Some(path) = line.split(": ").nth(1) {
|
|
|
|
|
let trimmed = path.trim().trim_matches('"').to_string();
|
|
|
|
|
let _ = on_event.send(DownloadEvent::FilePath {
|
|
|
|
|
path: trimmed.clone(),
|
|
|
|
|
});
|
|
|
|
|
last_file_path = Some(trimmed);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let status = child
|
|
|
|
|
.wait()
|
|
|
|
|
.map_err(|e| format!("Download failed: {e}"))?;
|
|
|
|
|
let path = last_file_path.unwrap_or_default();
|
|
|
|
|
let _ = on_event.send(DownloadEvent::Finished {
|
|
|
|
|
success: status.success(),
|
|
|
|
|
path,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
})
|
|
|
|
|
.await
|
|
|
|
|
.map_err(|e| format!("Task failed: {e}"))?
|
|
|
|
|
}
|