diff --git a/src-tauri/src/downloader.rs b/src-tauri/src/downloader.rs index 7b2ee7a..e3148ba 100644 --- a/src-tauri/src/downloader.rs +++ b/src-tauri/src/downloader.rs @@ -7,6 +7,7 @@ use serde::{Deserialize, Serialize}; use anyhow::{Result, anyhow}; use regex::Regex; use crate::binary_manager; +use crate::storage; #[derive(Serialize, Deserialize, Debug, Clone)] pub struct VideoMetadata { @@ -38,6 +39,7 @@ pub struct DownloadOptions { pub quality: String, // e.g., "1080", "720", "best" pub output_path: String, // Directory pub output_format: String, // "original", "mp4", "webm", "mkv", "m4a", "aac", "opus", "vorbis", "wav", etc. + pub cookies_path: Option, } #[derive(Serialize, Clone, Debug)] @@ -67,6 +69,9 @@ pub async fn fetch_metadata(app: &AppHandle, url: &str, parse_mix_playlist: bool let ytdlp_path = binary_manager::get_ytdlp_path(app)?; let qjs_path = binary_manager::get_qjs_path(app)?; // Get absolute path to quickjs + // Load settings to check for cookies + let settings = storage::load_settings(app)?; + let mut cmd = Command::new(ytdlp_path); #[cfg(target_os = "windows")] cmd.creation_flags(0x08000000); @@ -75,6 +80,12 @@ pub async fn fetch_metadata(app: &AppHandle, url: &str, parse_mix_playlist: bool // Rust's Command automatically handles spaces in arguments, so we should NOT quote the path here. cmd.arg("--js-runtimes").arg(format!("quickjs:{}", qjs_path.to_string_lossy())); + if let Some(cookies) = settings.cookies_path { + if !cookies.is_empty() { + cmd.arg("--cookies").arg(cookies); + } + } + cmd.arg("--dump-single-json") .arg("--flat-playlist") .arg("--no-warnings"); @@ -185,6 +196,13 @@ pub async fn download_video( args.push("--ffmpeg-location".to_string()); args.push(ffmpeg_path.to_string_lossy().to_string()); + if let Some(cookies) = &options.cookies_path { + if !cookies.is_empty() { + args.push("--cookies".to_string()); + args.push(cookies.clone()); + } + } + args.push(url); // Output template diff --git a/src-tauri/src/storage.rs b/src-tauri/src/storage.rs index 108e935..a288676 100644 --- a/src-tauri/src/storage.rs +++ b/src-tauri/src/storage.rs @@ -9,6 +9,7 @@ use chrono::{DateTime, Utc}; #[derive(Serialize, Deserialize, Debug, Clone)] pub struct Settings { pub download_path: String, + pub cookies_path: Option, pub theme: String, // 'light', 'dark', 'system' pub last_updated: Option>, } @@ -19,6 +20,7 @@ impl Default for Settings { // but for default struct we can keep it empty or a placeholder. Self { download_path: "".to_string(), + cookies_path: None, theme: "system".to_string(), last_updated: None, } diff --git a/src/stores/analysis.ts b/src/stores/analysis.ts index 11baadb..6482fba 100644 --- a/src/stores/analysis.ts +++ b/src/stores/analysis.ts @@ -19,7 +19,8 @@ export const useAnalysisStore = defineStore('analysis', () => { is_audio_only: false, quality: 'best', output_path: '', - output_format: 'original' + output_format: 'original', + cookies_path: '' }) function toggleEntry(id: string) { diff --git a/src/stores/settings.ts b/src/stores/settings.ts index aaa250c..54c41f1 100644 --- a/src/stores/settings.ts +++ b/src/stores/settings.ts @@ -5,6 +5,7 @@ import { ref } from 'vue' export interface Settings { download_path: string + cookies_path?: string theme: 'light' | 'dark' | 'system' last_updated: string | null } @@ -12,6 +13,7 @@ export interface Settings { export const useSettingsStore = defineStore('settings', () => { const settings = ref({ download_path: '', + cookies_path: '', theme: 'system', last_updated: null }) diff --git a/src/views/Home.vue b/src/views/Home.vue index d8885db..0ff15c0 100644 --- a/src/views/Home.vue +++ b/src/views/Home.vue @@ -214,6 +214,9 @@ async function startDownload() { analysisStore.options.output_path = settingsStore.settings.download_path } + // Ensure cookies path is set from settings + analysisStore.options.cookies_path = settingsStore.settings.cookies_path || '' + try { if (analysisStore.metadata.entries) { // Playlist Download diff --git a/src/views/Settings.vue b/src/views/Settings.vue index 7a44435..62ac315 100644 --- a/src/views/Settings.vue +++ b/src/views/Settings.vue @@ -3,7 +3,7 @@ import { ref } from 'vue' import { useSettingsStore } from '../stores/settings' import { invoke } from '@tauri-apps/api/core' import { open } from '@tauri-apps/plugin-dialog' -import { Folder, RefreshCw, Monitor, Sun, Moon, Terminal } from 'lucide-vue-next' +import { Folder, RefreshCw, Monitor, Sun, Moon, Terminal, Download, FileText, X } from 'lucide-vue-next' import { format } from 'date-fns' // Import format const settingsStore = useSettingsStore() @@ -25,6 +25,25 @@ async function browsePath() { } } +async function browseCookies() { + const selected = await open({ + multiple: false, + directory: false, + filters: [{ name: 'Text Files', extensions: ['txt', 'cookies'] }, { name: 'All Files', extensions: ['*'] }], + defaultPath: settingsStore.settings.cookies_path || undefined + }) + + if (selected) { + settingsStore.settings.cookies_path = selected as string + await settingsStore.save() + } +} + +async function clearCookies() { + settingsStore.settings.cookies_path = '' + await settingsStore.save() +} + async function updateYtdlp() { updatingYtdlp.value = true updateStatus.value = '正在更新 yt-dlp...' @@ -98,6 +117,34 @@ function setTheme(theme: 'light' | 'dark' | 'system') { + +
+

Cookies 文件

+
+
+
+ {{ settingsStore.settings.cookies_path || '未设置 (默认空)' }} +
+ +
+ +
+

选填。如果遇到需要登录的视频(如会员专享),请指定包含 cookies 的文本文件(Netscape 格式)。

+
+

外观