add format convert support
This commit is contained in:
@@ -77,7 +77,7 @@ pub async fn start_download(app: AppHandle, url: String, options: DownloadOption
|
|||||||
output_path: output_dir,
|
output_path: output_dir,
|
||||||
timestamp: chrono::Utc::now(),
|
timestamp: chrono::Utc::now(),
|
||||||
status: status.to_string(),
|
status: status.to_string(),
|
||||||
format: options.quality,
|
format: options.output_format,
|
||||||
};
|
};
|
||||||
|
|
||||||
let _ = storage::add_history_item(&app, item);
|
let _ = storage::add_history_item(&app, item);
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ pub struct DownloadOptions {
|
|||||||
pub is_audio_only: bool,
|
pub is_audio_only: bool,
|
||||||
pub quality: String, // e.g., "1080", "720", "best"
|
pub quality: String, // e.g., "1080", "720", "best"
|
||||||
pub output_path: String, // Directory
|
pub output_path: String, // Directory
|
||||||
|
pub output_format: String, // "original", "mp4", "webm", "mkv", "m4a", "aac", "opus", "vorbis", "wav", etc.
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Clone, Debug)]
|
#[derive(Serialize, Clone, Debug)]
|
||||||
@@ -188,8 +189,11 @@ pub async fn download_video(
|
|||||||
// Formats
|
// Formats
|
||||||
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());
|
// Only set audio format if not "original"
|
||||||
args.push("mp3".to_string());
|
if options.output_format != "original" {
|
||||||
|
args.push("--audio-format".to_string());
|
||||||
|
args.push(options.output_format.clone());
|
||||||
|
}
|
||||||
} 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()
|
||||||
@@ -198,6 +202,12 @@ pub async fn download_video(
|
|||||||
};
|
};
|
||||||
args.push("-f".to_string());
|
args.push("-f".to_string());
|
||||||
args.push(format_arg);
|
args.push(format_arg);
|
||||||
|
|
||||||
|
// Only set merge output format if not "original"
|
||||||
|
if options.output_format != "original" {
|
||||||
|
args.push("--merge-output-format".to_string());
|
||||||
|
args.push(options.output_format.clone());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Progress output
|
// Progress output
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
"app": {
|
"app": {
|
||||||
"windows": [
|
"windows": [
|
||||||
{
|
{
|
||||||
"title": "流萤 - 视频下载",
|
"title": "流萤 - 视频下载 v1.0.0",
|
||||||
"width": 1300,
|
"width": 1300,
|
||||||
"height": 900
|
"height": 900
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,8 @@ export const useAnalysisStore = defineStore('analysis', () => {
|
|||||||
const options = ref({
|
const options = ref({
|
||||||
is_audio_only: false,
|
is_audio_only: false,
|
||||||
quality: 'best',
|
quality: 'best',
|
||||||
output_path: ''
|
output_path: '',
|
||||||
|
output_format: 'original'
|
||||||
})
|
})
|
||||||
|
|
||||||
function toggleEntry(id: string) {
|
function toggleEntry(id: string) {
|
||||||
|
|||||||
@@ -18,6 +18,26 @@ const qualityOptions = [
|
|||||||
{ label: '480p', value: '480' },
|
{ label: '480p', value: '480' },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
const videoFormatOptions = [
|
||||||
|
{ label: '原格式', value: 'original' },
|
||||||
|
{ label: 'MP4', value: 'mp4' },
|
||||||
|
{ label: 'WebM', value: 'webm' },
|
||||||
|
{ label: 'Matroska (MKV)', value: 'mkv' },
|
||||||
|
{ label: 'FLV', value: 'flv' },
|
||||||
|
{ label: 'AVI', value: 'avi' },
|
||||||
|
]
|
||||||
|
|
||||||
|
const audioFormatOptions = [
|
||||||
|
{ label: '原格式', value: 'original' },
|
||||||
|
{ label: 'MP3', value: 'mp3' },
|
||||||
|
{ label: 'M4A', value: 'm4a' },
|
||||||
|
{ label: 'AAC', value: 'aac' },
|
||||||
|
{ label: 'Opus', value: 'opus' },
|
||||||
|
{ label: 'Vorbis', value: 'vorbis' },
|
||||||
|
{ label: 'WAV', value: 'wav' },
|
||||||
|
{ label: 'FLAC', value: 'flac' },
|
||||||
|
]
|
||||||
|
|
||||||
// Sync default download path if not set
|
// Sync default download path if not set
|
||||||
watch(() => settingsStore.settings.download_path, (newPath) => {
|
watch(() => settingsStore.settings.download_path, (newPath) => {
|
||||||
if (newPath && !analysisStore.options.output_path) {
|
if (newPath && !analysisStore.options.output_path) {
|
||||||
@@ -36,6 +56,11 @@ watch(() => analysisStore.url, (newUrl) => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Reset format to original when toggling audio-only mode
|
||||||
|
watch(() => analysisStore.options.is_audio_only, () => {
|
||||||
|
analysisStore.options.output_format = 'original'
|
||||||
|
})
|
||||||
|
|
||||||
async function analyze() {
|
async function analyze() {
|
||||||
if (!analysisStore.url) return
|
if (!analysisStore.url) return
|
||||||
analysisStore.loading = true
|
analysisStore.loading = true
|
||||||
@@ -217,9 +242,9 @@ async function startDownload() {
|
|||||||
|
|
||||||
<!-- Left: Selection Controls -->
|
<!-- Left: Selection Controls -->
|
||||||
<div class="flex items-center gap-2 w-full md:w-auto">
|
<div class="flex items-center gap-2 w-full md:w-auto">
|
||||||
<button @click="analysisStore.setAllEntries(true)" class="text-xs font-medium px-3 py-1.5 rounded-lg bg-white dark:bg-zinc-700 hover:bg-gray-100 dark:hover:bg-zinc-600 text-zinc-700 dark:text-gray-200 border border-gray-200 dark:border-zinc-600 transition-colors shadow-sm">全选</button>
|
<button @click="analysisStore.setAllEntries(true)" class="text-base font-medium px-3 py-1.5 rounded-lg bg-white dark:bg-zinc-700 hover:bg-gray-100 dark:hover:bg-zinc-600 text-zinc-700 dark:text-gray-200 border border-gray-200 dark:border-zinc-600 transition-colors shadow-sm">全选</button>
|
||||||
<button @click="analysisStore.setAllEntries(false)" class="text-xs font-medium px-3 py-1.5 rounded-lg bg-white dark:bg-zinc-700 hover:bg-gray-100 dark:hover:bg-zinc-600 text-zinc-700 dark:text-gray-200 border border-gray-200 dark:border-zinc-600 transition-colors shadow-sm">取消全选</button>
|
<button @click="analysisStore.setAllEntries(false)" class="text-base font-medium px-3 py-1.5 rounded-lg bg-white dark:bg-zinc-700 hover:bg-gray-100 dark:hover:bg-zinc-600 text-zinc-700 dark:text-gray-200 border border-gray-200 dark:border-zinc-600 transition-colors shadow-sm">取消全选</button>
|
||||||
<button @click="analysisStore.invertSelection()" class="text-xs font-medium px-3 py-1.5 rounded-lg bg-white dark:bg-zinc-700 hover:bg-gray-100 dark:hover:bg-zinc-600 text-zinc-700 dark:text-gray-200 border border-gray-200 dark:border-zinc-600 transition-colors shadow-sm">反选</button>
|
<button @click="analysisStore.invertSelection()" class="text-base font-medium px-3 py-1.5 rounded-lg bg-white dark:bg-zinc-700 hover:bg-gray-100 dark:hover:bg-zinc-600 text-zinc-700 dark:text-gray-200 border border-gray-200 dark:border-zinc-600 transition-colors shadow-sm">反选</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Right: Settings -->
|
<!-- Right: Settings -->
|
||||||
@@ -249,6 +274,16 @@ async function startDownload() {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Format Dropdown -->
|
||||||
|
<div class="flex items-center gap-3">
|
||||||
|
<div class="w-48">
|
||||||
|
<AppSelect
|
||||||
|
v-model="analysisStore.options.output_format"
|
||||||
|
:options="analysisStore.options.is_audio_only ? audioFormatOptions : videoFormatOptions"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -298,7 +333,7 @@ async function startDownload() {
|
|||||||
<p v-if="analysisStore.metadata.entries" class="text-blue-500 mt-1 font-medium">{{ analysisStore.metadata.entries.length }} 个视频 (播放列表)</p>
|
<p v-if="analysisStore.metadata.entries" class="text-blue-500 mt-1 font-medium">{{ analysisStore.metadata.entries.length }} 个视频 (播放列表)</p>
|
||||||
|
|
||||||
<!-- Options -->
|
<!-- Options -->
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mt-6">
|
<div class="grid grid-cols-1 md:grid-cols-3 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-base">仅音频</span>
|
<span class="font-medium text-base">仅音频</span>
|
||||||
@@ -322,6 +357,14 @@ async function startDownload() {
|
|||||||
:disabled="analysisStore.options.is_audio_only"
|
:disabled="analysisStore.options.is_audio_only"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Format Dropdown -->
|
||||||
|
<div class="relative">
|
||||||
|
<AppSelect
|
||||||
|
v-model="analysisStore.options.output_format"
|
||||||
|
:options="analysisStore.options.is_audio_only ? audioFormatOptions : videoFormatOptions"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user