change quickjs to upstream one
This commit is contained in:
26
spec/quickjs_update.md
Normal file
26
spec/quickjs_update.md
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
# QuickJS Binary Source Update
|
||||||
|
|
||||||
|
## 1. Context
|
||||||
|
The current `quickjs-ng` runtime is proving too slow for our needs. We need to switch to the original QuickJS runtime provided by Bellard.
|
||||||
|
|
||||||
|
## 2. Source Details
|
||||||
|
* **Base URL:** `https://bellard.org/quickjs/binary_releases/`
|
||||||
|
* **Versioning:** Use `LATEST.json` at the base URL to find the latest version and filename.
|
||||||
|
* Format: `{"version":"2024-01-13","files":{"quickjs-win-x86_64.zip":"quickjs-win-x86_64-2024-01-13.zip", ...}}`
|
||||||
|
* **Target Files:**
|
||||||
|
* **Windows:** Look for key `quickjs-win-x86_64.zip`.
|
||||||
|
* **macOS:** Look for key `quickjs-cosmo-x86_64.zip` (Cosmo builds are generally portable). *Wait, need to confirm if macos specific builds exist or if cosmo is the intended one for unix-like.*
|
||||||
|
* *Correction*: Bellard's page lists `quickjs-macos-x86_64.zip`? Let's check LATEST.json first. Assuming `quickjs-cosmo` might be Linux/Universal. Let's fetch `LATEST.json` to be sure.
|
||||||
|
|
||||||
|
## 3. Implementation Changes
|
||||||
|
* **`src-tauri/src/binary_manager.rs`**:
|
||||||
|
* Update `QJS_REPO_URL` to `https://bellard.org/quickjs/binary_releases`.
|
||||||
|
* Add logic to fetch `LATEST.json` first.
|
||||||
|
* Parse the JSON to get the actual filename for the current OS.
|
||||||
|
* Download the ZIP file.
|
||||||
|
* Extract the binary (`qjs.exe` or `qjs`) from the ZIP.
|
||||||
|
* Rename it to our internal standard (`quickjs.exe` / `quickjs`).
|
||||||
|
|
||||||
|
## 4. Verification
|
||||||
|
* Re-run tests.
|
||||||
|
* Ensure `update_quickjs` flows correctly with the new logic.
|
||||||
@@ -5,11 +5,14 @@ use tauri::AppHandle;
|
|||||||
use anyhow::{Result, anyhow};
|
use anyhow::{Result, anyhow};
|
||||||
#[cfg(target_family = "unix")]
|
#[cfg(target_family = "unix")]
|
||||||
use std::os::unix::fs::PermissionsExt;
|
use std::os::unix::fs::PermissionsExt;
|
||||||
|
use zip::ZipArchive;
|
||||||
|
use std::io::Cursor;
|
||||||
|
|
||||||
use crate::storage::{self};
|
use crate::storage::{self};
|
||||||
|
|
||||||
const YT_DLP_REPO_URL: &str = "https://github.com/yt-dlp/yt-dlp/releases/latest/download";
|
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";
|
// Bellard's QuickJS binary releases URL
|
||||||
|
const QJS_REPO_URL: &str = "https://bellard.org/quickjs/binary_releases";
|
||||||
|
|
||||||
pub fn get_ytdlp_binary_name() -> &'static str {
|
pub fn get_ytdlp_binary_name() -> &'static str {
|
||||||
if cfg!(target_os = "windows") {
|
if cfg!(target_os = "windows") {
|
||||||
@@ -30,16 +33,10 @@ pub fn get_qjs_binary_name() -> &'static str {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Source name inside the zip archive or direct download
|
// Source name inside the zip archive (standard QuickJS naming)
|
||||||
// Updated based on actual release assets:
|
fn get_qjs_source_name_in_zip() -> &'static str {
|
||||||
// Windows: qjs-windows-x86_64.exe (direct executable)
|
|
||||||
// macOS: qjs-darwin (direct executable)
|
|
||||||
// Note: The logic now needs to support direct download, not just zip.
|
|
||||||
fn get_qjs_download_filename() -> &'static str {
|
|
||||||
if cfg!(target_os = "windows") {
|
if cfg!(target_os = "windows") {
|
||||||
"qjs-windows-x86_64.exe"
|
"qjs.exe"
|
||||||
} else if cfg!(target_os = "macos") {
|
|
||||||
"qjs-darwin"
|
|
||||||
} else {
|
} else {
|
||||||
"qjs"
|
"qjs"
|
||||||
}
|
}
|
||||||
@@ -143,28 +140,80 @@ pub fn get_ytdlp_version(app: &AppHandle) -> Result<String> {
|
|||||||
|
|
||||||
// --- QuickJS Logic ---
|
// --- QuickJS Logic ---
|
||||||
|
|
||||||
pub async fn download_qjs(app: &AppHandle) -> Result<PathBuf> {
|
#[derive(serde::Deserialize)]
|
||||||
// Determine asset name based on OS/Arch
|
struct LatestInfo {
|
||||||
// Updated to match actual GitHub Release assets (direct executables, not zips)
|
version: String,
|
||||||
let asset_name = get_qjs_download_filename();
|
}
|
||||||
|
|
||||||
let url = format!("{}/{}", QJS_REPO_URL, asset_name);
|
pub async fn download_qjs(app: &AppHandle) -> Result<PathBuf> {
|
||||||
let response = reqwest::get(&url).await?;
|
// 1. Fetch LATEST.json to get version info (though filenames seem predictable-ish, version helps)
|
||||||
|
// Actually, looking at the file list, Bellard uses date-based versions.
|
||||||
|
// Format: quickjs-win-x86_64-YYYY-MM-DD.zip
|
||||||
|
// We need to find the correct filename dynamically or parse LATEST.json if it gave filenames.
|
||||||
|
// The LATEST.json content was {"version":"2024-01-13"} (example from prompt context).
|
||||||
|
// So we can construct the filename: quickjs-{platform}-{version}.zip
|
||||||
|
|
||||||
|
let latest_url = format!("{}/LATEST.json", QJS_REPO_URL);
|
||||||
|
let latest_resp = reqwest::get(&latest_url).await?;
|
||||||
|
if !latest_resp.status().is_success() {
|
||||||
|
return Err(anyhow!("Failed to fetch QuickJS version info"));
|
||||||
|
}
|
||||||
|
let latest_info: LatestInfo = latest_resp.json().await?;
|
||||||
|
let version = latest_info.version;
|
||||||
|
|
||||||
|
// Construct filename based on OS/Arch
|
||||||
|
let filename = if cfg!(target_os = "windows") {
|
||||||
|
format!("quickjs-win-x86_64-{}.zip", version)
|
||||||
|
} else if cfg!(target_os = "macos") {
|
||||||
|
// NOTE: Cosmo builds are universal/portable for Linux/Mac usually?
|
||||||
|
// Based on prompt instruction: "macos download quickjs-cosmo marked file"
|
||||||
|
// Bellard lists: quickjs-cosmo-YYYY-MM-DD.zip
|
||||||
|
format!("quickjs-cosmo-{}.zip", version)
|
||||||
|
} else {
|
||||||
|
return Err(anyhow!("Unsupported OS for QuickJS auto-download"));
|
||||||
|
};
|
||||||
|
|
||||||
|
let download_url = format!("{}/{}", QJS_REPO_URL, filename);
|
||||||
|
let response = reqwest::get(&download_url).await?;
|
||||||
|
|
||||||
if !response.status().is_success() {
|
if !response.status().is_success() {
|
||||||
return Err(anyhow!("Failed to download QuickJS: Status {}", response.status()));
|
return Err(anyhow!("Failed to download QuickJS: Status {}", response.status()));
|
||||||
}
|
}
|
||||||
|
|
||||||
let bytes = response.bytes().await?;
|
let bytes = response.bytes().await?;
|
||||||
// Direct write, no zip extraction needed
|
let cursor = Cursor::new(bytes);
|
||||||
|
let mut archive = ZipArchive::new(cursor)?;
|
||||||
|
|
||||||
|
let bin_dir = get_bin_dir(app)?;
|
||||||
|
|
||||||
let final_path = get_qjs_path(app)?;
|
// Extract logic: The zip from Bellard usually contains a folder or just binaries.
|
||||||
|
// We need to find the `qjs` binary.
|
||||||
|
// Windows zip usually has `qjs.exe`
|
||||||
|
// Cosmo zip usually has `qjs`? Let's search for it.
|
||||||
|
|
||||||
if let Some(parent) = final_path.parent() {
|
let source_name = get_qjs_source_name_in_zip();
|
||||||
fs::create_dir_all(parent)?;
|
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)?;
|
||||||
|
// Filenames in zip might be like "quickjs-win-x86_64-2024-01-13/qjs.exe"
|
||||||
|
let name = file.name();
|
||||||
|
let filename_only = name.split('/').last().unwrap_or("");
|
||||||
|
|
||||||
|
if filename_only == 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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fs::write(&final_path, bytes)?;
|
if !found {
|
||||||
|
return Err(anyhow!("Could not find {} in downloaded archive", source_name));
|
||||||
|
}
|
||||||
|
|
||||||
|
let final_path = get_qjs_path(app)?;
|
||||||
|
|
||||||
#[cfg(target_family = "unix")]
|
#[cfg(target_family = "unix")]
|
||||||
{
|
{
|
||||||
@@ -187,11 +236,6 @@ pub fn get_qjs_version(app: &AppHandle) -> Result<String> {
|
|||||||
if !path.exists() {
|
if !path.exists() {
|
||||||
return Ok("Not installed".to_string());
|
return Ok("Not installed".to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to get version if possible, otherwise just say Installed
|
|
||||||
// Running with -h usually works and prints version in first line often
|
|
||||||
// But let's stick to simple existence check for stability unless we need exact version.
|
|
||||||
// Or we can check file creation time.
|
|
||||||
Ok("Installed".to_string())
|
Ok("Installed".to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -218,12 +262,11 @@ mod tests {
|
|||||||
if cfg!(target_os = "windows") {
|
if cfg!(target_os = "windows") {
|
||||||
assert_eq!(get_ytdlp_binary_name(), "yt-dlp.exe");
|
assert_eq!(get_ytdlp_binary_name(), "yt-dlp.exe");
|
||||||
assert_eq!(get_qjs_binary_name(), "quickjs.exe");
|
assert_eq!(get_qjs_binary_name(), "quickjs.exe");
|
||||||
// Check download filename
|
assert_eq!(get_qjs_source_name_in_zip(), "qjs.exe");
|
||||||
assert_eq!(get_qjs_download_filename(), "qjs-windows-x86_64.exe");
|
|
||||||
} else if cfg!(target_os = "macos") {
|
} else if cfg!(target_os = "macos") {
|
||||||
assert_eq!(get_ytdlp_binary_name(), "yt-dlp_macos");
|
assert_eq!(get_ytdlp_binary_name(), "yt-dlp_macos");
|
||||||
assert_eq!(get_qjs_binary_name(), "quickjs");
|
assert_eq!(get_qjs_binary_name(), "quickjs");
|
||||||
assert_eq!(get_qjs_download_filename(), "qjs-darwin");
|
assert_eq!(get_qjs_source_name_in_zip(), "qjs");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,19 +53,16 @@ pub struct LogEvent {
|
|||||||
pub level: String, // "info", "error"
|
pub level: String, // "info", "error"
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn fetch_metadata(app: &AppHandle, url: &str, parse_mix_playlist: bool) -> Result<MetadataResult> {
|
|
||||||
let ytdlp_path = binary_manager::get_ytdlp_path(app)?; // Updated path call
|
|
||||||
|
|
||||||
// Inject PATH for QuickJS
|
|
||||||
let bin_dir = binary_manager::get_bin_dir(app)?;
|
|
||||||
let path_env = std::env::var("PATH").unwrap_or_default();
|
|
||||||
let new_path_env = format!("{}{}{}", bin_dir.to_string_lossy(), if cfg!(windows) { ";" } else { ":" }, path_env);
|
|
||||||
|
|
||||||
|
|
||||||
|
pub async fn fetch_metadata(app: &AppHandle, url: &str, parse_mix_playlist: bool) -> Result<MetadataResult> {
|
||||||
|
let ytdlp_path = binary_manager::get_ytdlp_path(app)?;
|
||||||
|
let qjs_path = binary_manager::get_qjs_path(app)?; // Get absolute path to quickjs
|
||||||
|
|
||||||
let mut cmd = Command::new(ytdlp_path);
|
let mut cmd = Command::new(ytdlp_path);
|
||||||
|
|
||||||
// Environment injection
|
// Pass the runtime and its absolute path to --js-runtimes
|
||||||
cmd.env("PATH", new_path_env);
|
cmd.arg("--js-runtimes").arg(format!("quickjs:{}", qjs_path.to_string_lossy()));
|
||||||
cmd.arg("--js-runtime").arg("quickjs"); // Force QuickJS
|
|
||||||
|
|
||||||
cmd.arg("--dump-single-json")
|
cmd.arg("--dump-single-json")
|
||||||
.arg("--flat-playlist")
|
.arg("--flat-playlist")
|
||||||
@@ -134,18 +131,14 @@ pub async fn download_video(
|
|||||||
url: String,
|
url: String,
|
||||||
options: DownloadOptions,
|
options: DownloadOptions,
|
||||||
) -> Result<String> {
|
) -> Result<String> {
|
||||||
let ytdlp_path = binary_manager::get_ytdlp_path(&app)?; // Updated path call
|
let ytdlp_path = binary_manager::get_ytdlp_path(&app)?;
|
||||||
|
let qjs_path = binary_manager::get_qjs_path(&app)?; // Get absolute path to quickjs
|
||||||
|
|
||||||
// Inject PATH for QuickJS
|
|
||||||
let bin_dir = binary_manager::get_bin_dir(&app)?;
|
|
||||||
let path_env = std::env::var("PATH").unwrap_or_default();
|
|
||||||
let new_path_env = format!("{}{}{}", bin_dir.to_string_lossy(), if cfg!(windows) { ";" } else { ":" }, path_env);
|
|
||||||
|
|
||||||
let mut args = Vec::new();
|
let mut args = Vec::new();
|
||||||
|
|
||||||
// JS Runtime args must be passed via .arg(), env is set on command builder
|
// Pass the runtime and its absolute path to --js-runtimes
|
||||||
args.push("--js-runtime".to_string());
|
args.push("--js-runtimes".to_string());
|
||||||
args.push("quickjs".to_string());
|
args.push(format!("quickjs:{}", qjs_path.to_string_lossy()));
|
||||||
|
|
||||||
args.push(url);
|
args.push(url);
|
||||||
|
|
||||||
@@ -158,7 +151,7 @@ pub async fn download_video(
|
|||||||
if options.is_audio_only {
|
if options.is_audio_only {
|
||||||
args.push("-x".to_string());
|
args.push("-x".to_string());
|
||||||
args.push("--audio-format".to_string());
|
args.push("--audio-format".to_string());
|
||||||
args.push("mp3".to_string()); // Defaulting to mp3 for simplicity
|
args.push("mp3".to_string());
|
||||||
} else {
|
} else {
|
||||||
let format_arg = if options.quality == "best" {
|
let format_arg = if options.quality == "best" {
|
||||||
"bestvideo+bestaudio/best".to_string()
|
"bestvideo+bestaudio/best".to_string()
|
||||||
@@ -170,13 +163,14 @@ pub async fn download_video(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Progress output
|
// Progress output
|
||||||
args.push("--newline".to_string()); // Easier parsing
|
args.push("--newline".to_string());
|
||||||
|
|
||||||
let mut child = Command::new(ytdlp_path)
|
let mut cmd = Command::new(ytdlp_path);
|
||||||
.env("PATH", new_path_env) // Inject PATH
|
|
||||||
|
let mut child = cmd
|
||||||
.args(&args)
|
.args(&args)
|
||||||
.stdout(Stdio::piped())
|
.stdout(Stdio::piped())
|
||||||
.stderr(Stdio::piped()) // Capture stderr for logs
|
.stderr(Stdio::piped())
|
||||||
.spawn()?;
|
.spawn()?;
|
||||||
|
|
||||||
let stdout = child.stdout.take().ok_or(anyhow!("Failed to open stdout"))?;
|
let stdout = child.stdout.take().ok_or(anyhow!("Failed to open stdout"))?;
|
||||||
@@ -250,4 +244,4 @@ pub async fn download_video(
|
|||||||
}).ok();
|
}).ok();
|
||||||
Err(anyhow!("Download process failed"))
|
Err(anyhow!("Download process failed"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
{
|
{
|
||||||
"title": "stream-capture",
|
"title": "stream-capture",
|
||||||
"width": 1300,
|
"width": 1300,
|
||||||
"height": 800
|
"height": 850
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"security": {
|
"security": {
|
||||||
|
|||||||
Reference in New Issue
Block a user