fix(download): improve format fallbacks (#55)
This commit is contained in:
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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')}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{settings.oneClickDownload && (
|
||||
<div className="flex items-center gap-2 text-xs text-primary">
|
||||
<div className="w-1.5 h-1.5 rounded-full bg-primary" />
|
||||
<span>{t('download.oneClickDownloadEnabled')}</span>
|
||||
{onOpenSettings && (
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="text-xs h-auto"
|
||||
onClick={() => {
|
||||
setOpen(false)
|
||||
onOpenSettings()
|
||||
}}
|
||||
>
|
||||
{t('download.goToSettings')}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* One-click download indicator */}
|
||||
{settings.oneClickDownload && (
|
||||
<div className="w-full">
|
||||
<div className="rounded-lg border bg-muted/30 p-2.5">
|
||||
<div className="flex items-center gap-2 text-xs text-muted-foreground">
|
||||
<span>{t('download.oneClickDownloadEnabled')}</span>
|
||||
{onOpenSettings && (
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="text-xs h-auto"
|
||||
onClick={() => {
|
||||
setOpen(false)
|
||||
onOpenSettings()
|
||||
}}
|
||||
>
|
||||
{t('download.goToSettings')}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Error Display */}
|
||||
{error && (
|
||||
<div className="rounded-lg border border-destructive/20 bg-destructive/5 p-3">
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user