Compare commits
3 Commits
56aeafbf41
...
618fd2d933
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
618fd2d933 | ||
|
|
54f841355b | ||
|
|
5fa6b5b616 |
@@ -5,6 +5,8 @@ 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;
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
use std::os::windows::process::CommandExt;
|
||||||
use zip::ZipArchive;
|
use zip::ZipArchive;
|
||||||
use std::io::Cursor;
|
use std::io::Cursor;
|
||||||
|
|
||||||
@@ -104,9 +106,13 @@ pub async fn update_ytdlp(app: &AppHandle) -> Result<String> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Use built-in update for yt-dlp
|
// Use built-in update for yt-dlp
|
||||||
let output = std::process::Command::new(&path)
|
let mut cmd = std::process::Command::new(&path);
|
||||||
.arg("-U")
|
cmd.arg("-U");
|
||||||
.output()?;
|
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
cmd.creation_flags(0x08000000);
|
||||||
|
|
||||||
|
let output = cmd.output()?;
|
||||||
|
|
||||||
if !output.status.success() {
|
if !output.status.success() {
|
||||||
let stderr = String::from_utf8_lossy(&output.stderr).to_string();
|
let stderr = String::from_utf8_lossy(&output.stderr).to_string();
|
||||||
@@ -127,9 +133,13 @@ pub fn get_ytdlp_version(app: &AppHandle) -> Result<String> {
|
|||||||
return Ok("未安装".to_string());
|
return Ok("未安装".to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
let output = std::process::Command::new(&path)
|
let mut cmd = std::process::Command::new(&path);
|
||||||
.arg("--version")
|
cmd.arg("--version");
|
||||||
.output()?;
|
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
cmd.creation_flags(0x08000000);
|
||||||
|
|
||||||
|
let output = cmd.output()?;
|
||||||
|
|
||||||
if output.status.success() {
|
if output.status.success() {
|
||||||
Ok(String::from_utf8_lossy(&output.stdout).trim().to_string())
|
Ok(String::from_utf8_lossy(&output.stdout).trim().to_string())
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ pub struct LogEvent {
|
|||||||
pub async fn fetch_metadata(app: &AppHandle, url: &str, parse_mix_playlist: bool) -> Result<MetadataResult> {
|
pub async fn fetch_metadata(app: &AppHandle, url: &str, parse_mix_playlist: bool) -> Result<MetadataResult> {
|
||||||
app.emit("download-log", LogEvent {
|
app.emit("download-log", LogEvent {
|
||||||
id: "Analysis".to_string(),
|
id: "Analysis".to_string(),
|
||||||
message: format!("Starting metadata fetch for URL: {}", url),
|
message: format!("正在为 URL: {} 获取元数据", url),
|
||||||
level: "info".to_string(),
|
level: "info".to_string(),
|
||||||
}).ok();
|
}).ok();
|
||||||
|
|
||||||
@@ -66,6 +66,8 @@ pub async fn fetch_metadata(app: &AppHandle, url: &str, parse_mix_playlist: bool
|
|||||||
let qjs_path = binary_manager::get_qjs_path(app)?; // Get absolute path to quickjs
|
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);
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
cmd.creation_flags(0x08000000);
|
||||||
|
|
||||||
// Pass the runtime and its absolute path to --js-runtimes
|
// Pass the runtime and its absolute path to --js-runtimes
|
||||||
cmd.arg("--js-runtimes").arg(format!("quickjs:{}", qjs_path.to_string_lossy()));
|
cmd.arg("--js-runtimes").arg(format!("quickjs:{}", qjs_path.to_string_lossy()));
|
||||||
@@ -74,6 +76,10 @@ pub async fn fetch_metadata(app: &AppHandle, url: &str, parse_mix_playlist: bool
|
|||||||
.arg("--flat-playlist")
|
.arg("--flat-playlist")
|
||||||
.arg("--no-warnings");
|
.arg("--no-warnings");
|
||||||
|
|
||||||
|
// Optimize metadata fetching: skip heavy manifests and player JS execution.
|
||||||
|
// Skipping JS prevents slow QuickJS spin-up and signature decryption, drastically speeding up single video parsing.
|
||||||
|
cmd.arg("--extractor-args").arg("youtube:skip=dash,hls,translated_subs;player_skip=js");
|
||||||
|
|
||||||
if parse_mix_playlist {
|
if parse_mix_playlist {
|
||||||
cmd.arg("--playlist-end").arg("20");
|
cmd.arg("--playlist-end").arg("20");
|
||||||
}
|
}
|
||||||
@@ -87,7 +93,7 @@ pub async fn fetch_metadata(app: &AppHandle, url: &str, parse_mix_playlist: bool
|
|||||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||||
app.emit("download-log", LogEvent {
|
app.emit("download-log", LogEvent {
|
||||||
id: "Analysis".to_string(),
|
id: "Analysis".to_string(),
|
||||||
message: format!("Metadata fetch failed: {}", stderr),
|
message: format!("元数据获取失败: {}", stderr),
|
||||||
level: "error".to_string(),
|
level: "error".to_string(),
|
||||||
}).ok();
|
}).ok();
|
||||||
return Err(anyhow!("yt-dlp error: {}", stderr));
|
return Err(anyhow!("yt-dlp error: {}", stderr));
|
||||||
@@ -114,7 +120,7 @@ pub async fn fetch_metadata(app: &AppHandle, url: &str, parse_mix_playlist: bool
|
|||||||
|
|
||||||
app.emit("download-log", LogEvent {
|
app.emit("download-log", LogEvent {
|
||||||
id: "Analysis".to_string(),
|
id: "Analysis".to_string(),
|
||||||
message: "Metadata fetch success (Playlist)".to_string(),
|
message: "元数据获取成功(播放列表)".to_string(),
|
||||||
level: "info".to_string(),
|
level: "info".to_string(),
|
||||||
}).ok();
|
}).ok();
|
||||||
|
|
||||||
@@ -126,7 +132,7 @@ pub async fn fetch_metadata(app: &AppHandle, url: &str, parse_mix_playlist: bool
|
|||||||
let result = MetadataResult::Video(parse_video_metadata(&json));
|
let result = MetadataResult::Video(parse_video_metadata(&json));
|
||||||
app.emit("download-log", LogEvent {
|
app.emit("download-log", LogEvent {
|
||||||
id: "Analysis".to_string(),
|
id: "Analysis".to_string(),
|
||||||
message: "Metadata fetch success (Video)".to_string(),
|
message: "元数据获取成功(视频)".to_string(),
|
||||||
level: "info".to_string(),
|
level: "info".to_string(),
|
||||||
}).ok();
|
}).ok();
|
||||||
|
|
||||||
@@ -195,11 +201,13 @@ pub async fn download_video(
|
|||||||
let full_cmd_str = format!("{} {}", ytdlp_path.to_string_lossy(), args.join(" "));
|
let full_cmd_str = format!("{} {}", ytdlp_path.to_string_lossy(), args.join(" "));
|
||||||
app.emit("download-log", LogEvent {
|
app.emit("download-log", LogEvent {
|
||||||
id: id.clone(),
|
id: id.clone(),
|
||||||
message: format!("Executing command: {}", full_cmd_str),
|
message: format!("正在执行命令: {}", full_cmd_str),
|
||||||
level: "info".to_string(),
|
level: "info".to_string(),
|
||||||
}).ok();
|
}).ok();
|
||||||
|
|
||||||
let mut cmd = Command::new(ytdlp_path);
|
let mut cmd = Command::new(ytdlp_path);
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
cmd.creation_flags(0x08000000);
|
||||||
|
|
||||||
let mut child = cmd
|
let mut child = cmd
|
||||||
.args(&args)
|
.args(&args)
|
||||||
@@ -233,7 +241,7 @@ pub async fn download_video(
|
|||||||
app.emit("download-progress", ProgressEvent {
|
app.emit("download-progress", ProgressEvent {
|
||||||
id: id.clone(),
|
id: id.clone(),
|
||||||
progress: pct,
|
progress: pct,
|
||||||
speed: "TODO".to_string(),
|
speed: "待定".to_string(),
|
||||||
status: "downloading".to_string(),
|
status: "downloading".to_string(),
|
||||||
}).ok();
|
}).ok();
|
||||||
}
|
}
|
||||||
@@ -268,7 +276,7 @@ pub async fn download_video(
|
|||||||
speed: "-".to_string(),
|
speed: "-".to_string(),
|
||||||
status: "finished".to_string(),
|
status: "finished".to_string(),
|
||||||
}).ok();
|
}).ok();
|
||||||
Ok("Download complete".to_string())
|
Ok("下载完成".to_string())
|
||||||
} else {
|
} else {
|
||||||
app.emit("download-progress", ProgressEvent {
|
app.emit("download-progress", ProgressEvent {
|
||||||
id: id.clone(),
|
id: id.clone(),
|
||||||
@@ -276,6 +284,6 @@ pub async fn download_video(
|
|||||||
speed: "-".to_string(),
|
speed: "-".to_string(),
|
||||||
status: "error".to_string(),
|
status: "error".to_string(),
|
||||||
}).ok();
|
}).ok();
|
||||||
Err(anyhow!("Download process failed"))
|
Err(anyhow!("下载进程失败"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ onMounted(async () => {
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex h-screen bg-gray-50 dark:bg-zinc-950 text-zinc-900 dark:text-gray-100 font-sans overflow-hidden">
|
<div class="flex h-screen bg-gray-50 dark:bg-zinc-950 text-zinc-900 dark:text-gray-100 font-sans overflow-hidden">
|
||||||
<!-- Sidebar -->
|
<!-- Sidebar -->
|
||||||
<aside class="w-20 lg:w-64 bg-white dark:bg-zinc-900 border-r border-gray-200 dark:border-zinc-800 flex flex-col flex-shrink-0">
|
<aside class="w-20 lg:w-56 bg-white dark:bg-zinc-900 border-r border-gray-200 dark:border-zinc-800 flex flex-col flex-shrink-0">
|
||||||
<div class="p-6 flex items-center gap-3 justify-center lg:justify-start">
|
<div class="p-6 flex items-center gap-3 justify-center lg:justify-start">
|
||||||
<div class="w-8 h-8 bg-blue-600 rounded-lg flex items-center justify-center text-white shrink-0">
|
<div class="w-8 h-8 bg-blue-600 rounded-lg flex items-center justify-center text-white shrink-0">
|
||||||
<Download class="w-5 h-5" />
|
<Download class="w-5 h-5" />
|
||||||
|
|||||||
@@ -225,23 +225,22 @@ async function startDownload() {
|
|||||||
<!-- Right: Settings -->
|
<!-- Right: Settings -->
|
||||||
<div class="flex items-center gap-6 w-full md:w-auto justify-end">
|
<div class="flex items-center gap-6 w-full md:w-auto justify-end">
|
||||||
<!-- Audio Only Toggle -->
|
<!-- Audio Only Toggle -->
|
||||||
<div class="flex items-center gap-3">
|
<div class="flex items-center gap-x-4 p-3 bg-gray-50 dark:bg-zinc-800 rounded-xl">
|
||||||
<span class="font-medium text-sm text-zinc-700 dark:text-gray-300">仅音频</span>
|
<span class="font-medium text-base text-zinc-700 dark:text-gray-300">仅音频</span>
|
||||||
<button
|
<button
|
||||||
@click="analysisStore.options.is_audio_only = !analysisStore.options.is_audio_only"
|
@click="analysisStore.options.is_audio_only = !analysisStore.options.is_audio_only"
|
||||||
class="w-10 h-5 rounded-full relative transition-colors duration-200 ease-in-out shrink-0"
|
class="w-12 h-6 rounded-full relative transition-colors duration-200 ease-in-out"
|
||||||
:class="analysisStore.options.is_audio_only ? 'bg-blue-600' : 'bg-gray-300 dark:bg-zinc-600'"
|
:class="analysisStore.options.is_audio_only ? 'bg-blue-600' : 'bg-gray-300 dark:bg-zinc-600'"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="absolute top-1 left-1 w-3 h-3 bg-white rounded-full transition-transform duration-200"
|
class="absolute top-1 left-1 w-4 h-4 bg-white rounded-full transition-transform duration-200"
|
||||||
:class="analysisStore.options.is_audio_only ? 'translate-x-5' : 'translate-x-0'"
|
:class="analysisStore.options.is_audio_only ? 'translate-x-6' : 'translate-x-0'"
|
||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Quality Dropdown -->
|
<!-- Quality Dropdown -->
|
||||||
<div class="flex items-center gap-3">
|
<div class="flex items-center gap-3">
|
||||||
<span class="font-medium text-sm text-zinc-700 dark:text-gray-300">画质</span>
|
|
||||||
<div class="w-44">
|
<div class="w-44">
|
||||||
<AppSelect
|
<AppSelect
|
||||||
v-model="analysisStore.options.quality"
|
v-model="analysisStore.options.quality"
|
||||||
@@ -302,7 +301,7 @@ async function startDownload() {
|
|||||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mt-6">
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mt-6">
|
||||||
<!-- Audio Only Toggle -->
|
<!-- Audio Only Toggle -->
|
||||||
<div class="flex items-center justify-between p-3 bg-gray-50 dark:bg-zinc-800 rounded-xl">
|
<div class="flex items-center justify-between p-3 bg-gray-50 dark:bg-zinc-800 rounded-xl">
|
||||||
<span class="font-medium text-sm">仅音频</span>
|
<span class="font-medium text-base">仅音频</span>
|
||||||
<button
|
<button
|
||||||
@click="analysisStore.options.is_audio_only = !analysisStore.options.is_audio_only"
|
@click="analysisStore.options.is_audio_only = !analysisStore.options.is_audio_only"
|
||||||
class="w-12 h-6 rounded-full relative transition-colors duration-200 ease-in-out"
|
class="w-12 h-6 rounded-full relative transition-colors duration-200 ease-in-out"
|
||||||
|
|||||||
Reference in New Issue
Block a user