Compare commits
2 Commits
63648f9d7c
...
v1.1.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1554be25d2 | ||
|
|
05887fd7a3 |
1
src-tauri/Cargo.lock
generated
1
src-tauri/Cargo.lock
generated
@@ -3974,6 +3974,7 @@ name = "stream-capture"
|
||||
version = "1.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base64 0.22.1",
|
||||
"chrono",
|
||||
"futures-util",
|
||||
"regex",
|
||||
|
||||
@@ -19,6 +19,7 @@ tauri-plugin-dialog = "2"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
reqwest = { version = "0.12", features = ["json", "rustls-tls", "stream"] }
|
||||
base64 = "0.22"
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
anyhow = "1.0"
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
|
||||
@@ -49,6 +49,28 @@ pub fn get_ffmpeg_version(app: AppHandle) -> Result<String, String> {
|
||||
binary_manager::get_ffmpeg_version(&app).map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn fetch_image(url: String) -> Result<String, String> {
|
||||
use base64::{Engine as _, engine::general_purpose};
|
||||
|
||||
let client = reqwest::Client::new();
|
||||
let res = client.get(&url)
|
||||
.header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36")
|
||||
.send()
|
||||
.await
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
let bytes = res.bytes().await.map_err(|e| e.to_string())?;
|
||||
|
||||
// Convert to base64
|
||||
let b64 = general_purpose::STANDARD.encode(&bytes);
|
||||
|
||||
// Simple heuristic for mime type
|
||||
let mime = if url.to_lowercase().ends_with(".png") { "image/png" } else { "image/jpeg" };
|
||||
|
||||
Ok(format!("data:{};base64,{}", mime, b64))
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn fetch_metadata(app: AppHandle, url: String, parse_mix_playlist: bool) -> Result<downloader::MetadataResult, String> {
|
||||
downloader::fetch_metadata(&app, &url, parse_mix_playlist).await.map_err(|e| e.to_string())
|
||||
|
||||
@@ -204,7 +204,7 @@ pub async fn download_video(
|
||||
let format_arg = if options.quality == "best" {
|
||||
"bestvideo+bestaudio/best".to_string()
|
||||
} else {
|
||||
format!("bestvideo[height<={}]+bestaudio/best[height<={}]", options.quality, options.quality)
|
||||
format!("bestvideo[height<={}]+bestaudio/best[height<={}]/best", options.quality, options.quality)
|
||||
};
|
||||
args.push("-f".to_string());
|
||||
args.push(format_arg);
|
||||
|
||||
@@ -17,6 +17,7 @@ pub fn run() {
|
||||
commands::get_ytdlp_version,
|
||||
commands::get_quickjs_version,
|
||||
commands::get_ffmpeg_version,
|
||||
commands::fetch_image,
|
||||
commands::fetch_metadata,
|
||||
commands::start_download,
|
||||
commands::get_settings,
|
||||
|
||||
@@ -61,6 +61,38 @@ watch(() => analysisStore.options.is_audio_only, () => {
|
||||
analysisStore.options.output_format = 'original'
|
||||
})
|
||||
|
||||
async function processThumbnail(url: string | undefined): Promise<string | undefined> {
|
||||
if (!url) return undefined;
|
||||
// Check if it's an Instagram URL or similar that needs proxying
|
||||
if (url.includes('instagram.com') || url.includes('fbcdn.net') || url.includes('cdninstagram.com')) {
|
||||
try {
|
||||
return await invoke<string>('fetch_image', { url });
|
||||
} catch (e) {
|
||||
console.warn('Thumbnail fetch failed, falling back to URL', e);
|
||||
return url;
|
||||
}
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
async function processMetadataThumbnails(metadata: any) {
|
||||
if (!metadata) return;
|
||||
|
||||
// Process single video thumbnail
|
||||
if (metadata.thumbnail) {
|
||||
metadata.thumbnail = await processThumbnail(metadata.thumbnail);
|
||||
}
|
||||
|
||||
// Process playlist entries
|
||||
if (metadata.entries && Array.isArray(metadata.entries)) {
|
||||
await Promise.all(metadata.entries.map(async (entry: any) => {
|
||||
if (entry.thumbnail) {
|
||||
entry.thumbnail = await processThumbnail(entry.thumbnail);
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
async function analyze() {
|
||||
if (!analysisStore.url) return
|
||||
analysisStore.loading = true
|
||||
@@ -122,6 +154,9 @@ async function analyze() {
|
||||
throw new Error("所有链接解析失败或均为播放列表。");
|
||||
}
|
||||
|
||||
// Process thumbnails for batch results
|
||||
await Promise.all(results.map(r => processMetadataThumbnails(r)));
|
||||
|
||||
// Construct synthetic playlist
|
||||
analysisStore.metadata = {
|
||||
id: 'batch_download_' + Date.now(),
|
||||
@@ -155,6 +190,8 @@ async function analyze() {
|
||||
|
||||
const res = await invoke<any>('fetch_metadata', { url: urlToScan, parseMixPlaylist: parseMix })
|
||||
|
||||
await processMetadataThumbnails(res);
|
||||
|
||||
// Initialize selected state for playlist entries
|
||||
if (res.entries) {
|
||||
res.entries = res.entries.map((e: any) => ({ ...e, selected: true }))
|
||||
|
||||
Reference in New Issue
Block a user