From 86bd77d9954664c353882d1f08097aac615dabb8 Mon Sep 17 00:00:00 2001 From: Nexmoe <16796652+nexmoe@users.noreply.github.com> Date: Fri, 26 Dec 2025 13:02:07 +0800 Subject: [PATCH] fix(download): improve format fallbacks (#55) --- conductor.json | 2 +- src/main/download-engine/args-builder.ts | 4 ++ .../components/download/DownloadDialog.tsx | 50 +++++++++-------- .../src/components/video/FormatSelector.tsx | 55 +++++++++++-------- 4 files changed, 66 insertions(+), 45 deletions(-) diff --git a/conductor.json b/conductor.json index 5ce006c..1a1b483 100644 --- a/conductor.json +++ b/conductor.json @@ -1,6 +1,6 @@ { "scripts": { - "setup": "cp $CONDUCTOR_ROOT_PATH/resources/ resources/ && pnpm install", + "setup": "cp -r $CONDUCTOR_ROOT_PATH/resources resources/ && pnpm install", "run": "pnpm run dev" } } diff --git a/src/main/download-engine/args-builder.ts b/src/main/download-engine/args-builder.ts index 6dd033d..53aa440 100644 --- a/src/main/download-engine/args-builder.ts +++ b/src/main/download-engine/args-builder.ts @@ -21,6 +21,10 @@ export const resolveVideoFormatSelector = (options: DownloadOptions): string => const format = options.format const audioFormat = options.audioFormat + if (format && audioFormat === '') { + return format + } + if (format && (format.includes('/') || (audioFormat === undefined && format.includes('+')))) { return format } diff --git a/src/renderer/src/components/download/DownloadDialog.tsx b/src/renderer/src/components/download/DownloadDialog.tsx index efc96d8..179d1a6 100644 --- a/src/renderer/src/components/download/DownloadDialog.tsx +++ b/src/renderer/src/components/download/DownloadDialog.tsx @@ -114,10 +114,12 @@ const buildVideoFormatPreference = (settings: AppSettings): string => { combinations.push(video) } } else { - // Use bestvideo+bestaudio as fallback instead of 'best' to ensure merging + // Prefer merged formats, then allow 'best' as a compatibility fallback. combinations.push('bestvideo+bestaudio') } + combinations.push('best') + return dedupe(combinations).join('/') } @@ -935,29 +937,33 @@ export function DownloadDialog({ onOpenSupportedSites, onOpenSettings }: Downloa {t('sites.viewAll')} - - {settings.oneClickDownload && ( -
-
- {t('download.oneClickDownloadEnabled')} - {onOpenSettings && ( - - )} -
- )}
+ {/* One-click download indicator */} + {settings.oneClickDownload && ( +
+
+
+ {t('download.oneClickDownloadEnabled')} + {onOpenSettings && ( + + )} +
+
+
+ )} + {/* Error Display */} {error && (
diff --git a/src/renderer/src/components/video/FormatSelector.tsx b/src/renderer/src/components/video/FormatSelector.tsx index 5cf2769..762ce38 100644 --- a/src/renderer/src/components/video/FormatSelector.tsx +++ b/src/renderer/src/components/video/FormatSelector.tsx @@ -73,23 +73,31 @@ export function FormatSelector({ useEffect(() => { // Filter and sort formats // Exclude m3u8/HLS formats as they are streaming formats not suitable for direct download - const videos = formats.filter( - (f) => - f.video_ext !== 'none' && - f.vcodec && - f.vcodec !== 'none' && - f.protocol !== 'm3u8' && - f.protocol !== 'm3u8_native' + const isVideoFormat = (format: VideoFormat) => + format.video_ext !== 'none' && format.vcodec && format.vcodec !== 'none' + const isAudioFormat = (format: VideoFormat) => + format.acodec && + format.acodec !== 'none' && + (format.video_ext === 'none' || !format.video_ext) + const isHlsFormat = (format: VideoFormat) => + format.protocol === 'm3u8' || format.protocol === 'm3u8_native' + + const videoCandidates = formats.filter( + (format) => isVideoFormat(format) && !isHlsFormat(format) ) - const audios = formats.filter( - (f) => - f.acodec && - f.acodec !== 'none' && - (f.video_ext === 'none' || !f.video_ext) && - f.protocol !== 'm3u8' && - f.protocol !== 'm3u8_native' + const audioCandidates = formats.filter( + (format) => isAudioFormat(format) && !isHlsFormat(format) ) + const videos = + videoCandidates.length > 0 + ? videoCandidates + : formats.filter((format) => isVideoFormat(format)) + const audios = + audioCandidates.length > 0 + ? audioCandidates + : formats.filter((format) => isAudioFormat(format)) + // Apply showMoreFormats filter const filteredVideos = settings.showMoreFormats ? videos @@ -99,6 +107,9 @@ export function FormatSelector({ ? audios : audios.filter((f) => f.ext !== 'webm') + const finalVideos = filteredVideos.length > 0 ? filteredVideos : videos + const finalAudios = filteredAudios.length > 0 ? filteredAudios : audios + // Sort formats by quality (best first) const sortVideoFormatsByQuality = (a: VideoFormat, b: VideoFormat) => { // Sort by height (higher is better) @@ -138,23 +149,23 @@ export function FormatSelector({ return 0 } - filteredVideos.sort(sortVideoFormatsByQuality) - filteredAudios.sort(sortAudioFormatsByQuality) + finalVideos.sort(sortVideoFormatsByQuality) + finalAudios.sort(sortAudioFormatsByQuality) - setVideoFormats(filteredVideos) - setAudioFormats(filteredAudios) + setVideoFormats(finalVideos) + setAudioFormats(finalAudios) // Auto-select best format based on preferences - if (filteredVideos.length > 0 && !selectedVideo) { - const preferred = pickVideoFormatForPreset(filteredVideos, settings.oneClickQuality) + if (finalVideos.length > 0 && !selectedVideo) { + const preferred = pickVideoFormatForPreset(finalVideos, settings.oneClickQuality) if (preferred) { setSelectedVideo(preferred.format_id) onVideoFormatChange?.(preferred.format_id) } } - if (filteredAudios.length > 0 && !selectedAudio) { - const best = filteredAudios[0] + if (finalAudios.length > 0 && !selectedAudio) { + const best = finalAudios[0] setSelectedAudio(best.format_id) onAudioFormatChange?.(best.format_id) }