add quickjs but has bugs

This commit is contained in:
Julian Freeman
2025-12-02 11:59:30 -04:00
parent 38ad2e64cc
commit 320c95041a
10 changed files with 700 additions and 178 deletions

View File

@@ -0,0 +1,258 @@
// filepath: src-tauri/src/binary_manager.rs
use std::fs;
use std::path::PathBuf;
use tauri::AppHandle;
use anyhow::{Result, anyhow};
#[cfg(target_family = "unix")]
use std::os::unix::fs::PermissionsExt;
use zip::ZipArchive;
use std::io::Cursor;
use crate::storage::{self};
const YT_DLP_REPO_URL: &str = "https://github.com/yt-dlp/yt-dlp/releases/latest/download";
const QJS_REPO_URL: &str = "https://github.com/quickjs-ng/quickjs/releases/latest/download";
pub fn get_ytdlp_binary_name() -> &'static str {
if cfg!(target_os = "windows") {
"yt-dlp.exe"
} else if cfg!(target_os = "macos") {
"yt-dlp_macos"
} else {
"yt-dlp"
}
}
// Target name on disk (for yt-dlp usage)
pub fn get_qjs_binary_name() -> &'static str {
if cfg!(target_os = "windows") {
"quickjs.exe"
} else {
"quickjs"
}
}
// Source name inside the zip archive
fn get_qjs_source_name() -> &'static str {
if cfg!(target_os = "windows") {
"qjs.exe"
} else {
"qjs"
}
}
// Get base directory for all binaries
pub fn get_bin_dir(app: &AppHandle) -> Result<PathBuf> {
let app_data = storage::get_app_data_dir(app)?;
let bin_dir = app_data.join("bin");
if !bin_dir.exists() {
fs::create_dir_all(&bin_dir)?;
}
Ok(bin_dir)
}
pub fn get_ytdlp_path(app: &AppHandle) -> Result<PathBuf> {
Ok(get_bin_dir(app)?.join(get_ytdlp_binary_name()))
}
pub fn get_qjs_path(app: &AppHandle) -> Result<PathBuf> {
Ok(get_bin_dir(app)?.join(get_qjs_binary_name()))
}
pub fn check_binaries(app: &AppHandle) -> bool {
let ytdlp = get_ytdlp_path(app).map(|p| p.exists()).unwrap_or(false);
let qjs = get_qjs_path(app).map(|p| p.exists()).unwrap_or(false);
ytdlp && qjs
}
// --- yt-dlp Logic ---
pub async fn download_ytdlp(app: &AppHandle) -> Result<PathBuf> {
let binary_name = get_ytdlp_binary_name();
let url = format!("{}/{}", YT_DLP_REPO_URL, binary_name);
let response = reqwest::get(&url).await?;
if !response.status().is_success() {
return Err(anyhow!("Failed to download yt-dlp: Status {}", response.status()));
}
let bytes = response.bytes().await?;
let path = get_ytdlp_path(app)?;
if let Some(parent) = path.parent() {
fs::create_dir_all(parent)?;
}
fs::write(&path, bytes)?;
#[cfg(target_family = "unix")]
{
let mut perms = fs::metadata(&path)?.permissions();
perms.set_mode(0o755);
fs::set_permissions(&path, perms)?;
}
Ok(path)
}
pub async fn update_ytdlp(app: &AppHandle) -> Result<String> {
let path = get_ytdlp_path(app)?;
if !path.exists() {
download_ytdlp(app).await?;
return Ok("Downloaded fresh yt-dlp".to_string());
}
// Use built-in update for yt-dlp
let output = std::process::Command::new(&path)
.arg("-U")
.output()?;
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr).to_string();
return Err(anyhow!("yt-dlp update failed: {}", stderr));
}
// Update settings timestamp
let mut settings = storage::load_settings(app)?;
settings.last_updated = Some(chrono::Utc::now());
storage::save_settings(app, &settings)?;
Ok("yt-dlp updated".to_string())
}
pub fn get_ytdlp_version(app: &AppHandle) -> Result<String> {
let path = get_ytdlp_path(app)?;
if !path.exists() {
return Ok("Not installed".to_string());
}
let output = std::process::Command::new(&path)
.arg("--version")
.output()?;
if output.status.success() {
Ok(String::from_utf8_lossy(&output.stdout).trim().to_string())
} else {
Ok("Unknown".to_string())
}
}
// --- QuickJS Logic ---
pub async fn download_qjs(app: &AppHandle) -> Result<PathBuf> {
// Determine asset name based on OS/Arch
let asset_name = if cfg!(target_os = "windows") {
"qjs-windows-x86_64.zip"
} else if cfg!(target_os = "macos") {
if cfg!(target_arch = "aarch64") {
"qjs-macos-aarch64.zip"
} else {
"qjs-macos-x86_64.zip"
}
} else {
return Err(anyhow!("Unsupported OS for QuickJS auto-download"));
};
let url = format!("{}/{}", QJS_REPO_URL, asset_name);
let response = reqwest::get(&url).await?;
if !response.status().is_success() {
return Err(anyhow!("Failed to download QuickJS: Status {}", response.status()));
}
let bytes = response.bytes().await?;
let cursor = Cursor::new(bytes);
let mut archive = ZipArchive::new(cursor)?;
let bin_dir = get_bin_dir(app)?;
// Extract logic: find the file that starts with 'qjs' (ignoring extensions like .exe for now) and isn't a folder
let source_name = get_qjs_source_name();
let target_name = get_qjs_binary_name(); // quickjs.exe or quickjs
let mut found = false;
for i in 0..archive.len() {
let mut file = archive.by_index(i)?;
let name = file.name().split('/').last().unwrap_or("");
if name == source_name {
let mut out_file = fs::File::create(bin_dir.join(target_name))?;
std::io::copy(&mut file, &mut out_file)?;
found = true;
break;
}
}
if !found {
return Err(anyhow!("Could not find {} in downloaded archive", source_name));
}
let final_path = get_qjs_path(app)?;
#[cfg(target_family = "unix")]
{
let mut perms = fs::metadata(&final_path)?.permissions();
perms.set_mode(0o755);
fs::set_permissions(&final_path, perms)?;
}
Ok(final_path)
}
pub async fn update_qjs(app: &AppHandle) -> Result<String> {
// QuickJS doesn't have self-update, so we just re-download
download_qjs(app).await?;
Ok("QuickJS updated/installed".to_string())
}
pub fn get_qjs_version(app: &AppHandle) -> Result<String> {
let path = get_qjs_path(app)?;
if !path.exists() {
return Ok("Not installed".to_string());
}
// QuickJS might not support --version in a standard way, or might print help.
// Let's try executing it with --version or -h and see if we can grab something.
// For now, simpler: return "Installed" if it works, or check creation time?
// Let's return "Installed" to keep UI simple or try to run it.
// If we run `quickjs --version`, it often just prints the version if supported.
// quickjs-ng seems to support it?
// If not, we will just return "Ready".
// Attempt execution
// Note: using .arg("-h") might be safer if --version isn't standard.
// But let's try just checking existence for safety to avoid hanging if it opens REPL.
Ok("Installed".to_string())
}
pub async fn ensure_binaries(app: &AppHandle) -> Result<()> {
let ytdlp = get_ytdlp_path(app)?;
if !ytdlp.exists() {
download_ytdlp(app).await?;
}
let qjs = get_qjs_path(app)?;
if !qjs.exists() {
download_qjs(app).await?;
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_binary_names() {
if cfg!(target_os = "windows") {
assert_eq!(get_ytdlp_binary_name(), "yt-dlp.exe");
assert_eq!(get_qjs_binary_name(), "quickjs.exe");
assert_eq!(get_qjs_source_name(), "qjs.exe");
} else if cfg!(target_os = "macos") {
assert_eq!(get_ytdlp_binary_name(), "yt-dlp_macos");
assert_eq!(get_qjs_binary_name(), "quickjs");
assert_eq!(get_qjs_source_name(), "qjs");
}
}
}