f
This commit is contained in:
@@ -1177,7 +1177,7 @@ class Download_File(Cmdlet):
|
||||
return f"https://www.youtube.com/watch?v={entry_id.strip()}"
|
||||
return None
|
||||
|
||||
table = Table()
|
||||
table = Table(preserve_order=True)
|
||||
safe_url = str(url or "").strip()
|
||||
table.title = f'download-file -url "{safe_url}"' if safe_url else "download-file"
|
||||
if table_type:
|
||||
@@ -1477,7 +1477,7 @@ class Download_File(Cmdlet):
|
||||
actual_playlist_items = None
|
||||
|
||||
if mode == "audio" and not actual_format:
|
||||
actual_format = "bestaudio"
|
||||
actual_format = "bestaudio/best"
|
||||
|
||||
if mode == "video" and not actual_format:
|
||||
configured = (ytdlp_tool.default_format("video") or "").strip()
|
||||
@@ -1493,6 +1493,20 @@ class Download_File(Cmdlet):
|
||||
):
|
||||
actual_format = forced_single_format_id
|
||||
forced_single_applied = True
|
||||
|
||||
# Proactive fallback for single audio formats which might be unstable
|
||||
if (
|
||||
actual_format
|
||||
and isinstance(actual_format, str)
|
||||
and mode == "audio"
|
||||
and "/" not in actual_format
|
||||
and "+" not in actual_format
|
||||
and not forced_single_applied
|
||||
and actual_format not in {"best", "bestaudio", "bw", "ba"}
|
||||
):
|
||||
debug(f"Appending fallback to specific audio format: {actual_format} -> {actual_format}/bestaudio/best")
|
||||
actual_format = f"{actual_format}/bestaudio/best"
|
||||
|
||||
if (
|
||||
actual_format
|
||||
and isinstance(actual_format, str)
|
||||
@@ -1521,7 +1535,14 @@ class Download_File(Cmdlet):
|
||||
except Exception as e:
|
||||
pass
|
||||
|
||||
debug(
|
||||
"[download-file] Resolved format for download: "
|
||||
f"mode={mode}, format={actual_format or 'default'}, playlist_items={actual_playlist_items}"
|
||||
)
|
||||
|
||||
attempted_single_format_fallback = False
|
||||
attempted_audio_fallback_specific = False
|
||||
attempted_audio_fallback_generic = False
|
||||
while True:
|
||||
try:
|
||||
opts = DownloadOptions(
|
||||
@@ -1551,7 +1572,79 @@ class Download_File(Cmdlet):
|
||||
except Exception:
|
||||
detail = ""
|
||||
|
||||
if ("requested format is not available" in (detail or "").lower()) and mode != "audio":
|
||||
detail_lc = (detail or "").lower()
|
||||
msg_lc = ""
|
||||
try:
|
||||
msg_lc = str(e or "").lower()
|
||||
except Exception:
|
||||
msg_lc = ""
|
||||
requested_format_unavailable = (
|
||||
"requested format is not available" in detail_lc
|
||||
or "requested format is not available" in msg_lc
|
||||
)
|
||||
|
||||
if requested_format_unavailable and mode == "audio":
|
||||
# Level 1: Try to find a specific audio-only format from the list that hopefully works
|
||||
if not attempted_audio_fallback_specific:
|
||||
attempted_audio_fallback_specific = True
|
||||
audio_format_id = None
|
||||
try:
|
||||
formats = self._list_formats_cached(
|
||||
url,
|
||||
playlist_items_value=actual_playlist_items,
|
||||
formats_cache=formats_cache,
|
||||
ytdlp_tool=ytdlp_tool,
|
||||
)
|
||||
if formats:
|
||||
audio_candidates = []
|
||||
for fmt in formats:
|
||||
if not isinstance(fmt, dict):
|
||||
continue
|
||||
vcodec = str(fmt.get("vcodec", "none"))
|
||||
acodec = str(fmt.get("acodec", "none"))
|
||||
if acodec != "none" and vcodec == "none":
|
||||
audio_candidates.append(fmt)
|
||||
|
||||
if audio_candidates:
|
||||
def _score_audio(fmt: Dict[str, Any]) -> float:
|
||||
score = 0.0
|
||||
# Penalize DRC formats heavily
|
||||
fid = str(fmt.get("format_id") or "").lower()
|
||||
if "drc" in fid:
|
||||
score -= 1000.0
|
||||
|
||||
for key in ("abr", "tbr", "filesize", "filesize_approx"):
|
||||
try:
|
||||
val = fmt.get(key)
|
||||
if isinstance(val, (int, float)):
|
||||
score += float(val)
|
||||
break # Use the first valid metric found
|
||||
if isinstance(val, str) and val.strip().isdigit():
|
||||
score += float(val)
|
||||
break
|
||||
except Exception:
|
||||
continue
|
||||
return score
|
||||
|
||||
audio_candidates.sort(key=_score_audio, reverse=True)
|
||||
audio_format_id = str(audio_candidates[0].get("format_id") or "").strip() or None
|
||||
except Exception:
|
||||
audio_format_id = None
|
||||
|
||||
if audio_format_id:
|
||||
actual_format = audio_format_id
|
||||
debug(f"Requested audio format not available; retrying with best specific audio format: {actual_format}")
|
||||
continue
|
||||
|
||||
# Level 2: Fallback to generic 'bestaudio/best' if specific selection failed or wasn't found
|
||||
if not attempted_audio_fallback_generic:
|
||||
attempted_audio_fallback_generic = True
|
||||
if actual_format != "bestaudio/best":
|
||||
actual_format = "bestaudio/best"
|
||||
debug("Requested audio format not available; retrying with generic fallback: bestaudio/best")
|
||||
continue
|
||||
|
||||
if requested_format_unavailable and mode != "audio":
|
||||
if (
|
||||
forced_single_format_for_batch
|
||||
and forced_single_format_id
|
||||
@@ -1953,8 +2046,8 @@ class Download_File(Cmdlet):
|
||||
except Exception:
|
||||
height_selector = None
|
||||
if query_wants_audio:
|
||||
# Explicit audio request should map to best-audio-only selector
|
||||
ytdl_format = "bestaudio"
|
||||
# Explicit audio request should map to the configured audio selector (usually '251/140/bestaudio')
|
||||
ytdl_format = ytdlp_tool.default_format("audio")
|
||||
elif height_selector:
|
||||
ytdl_format = height_selector
|
||||
elif query_format:
|
||||
@@ -2045,6 +2138,51 @@ class Download_File(Cmdlet):
|
||||
if early_ret is not None:
|
||||
return int(early_ret)
|
||||
|
||||
# Auto-detect audio-only sources (e.g., Bandcamp albums) when no explicit format is requested.
|
||||
if mode == "video" and not ytdl_format and not query_format and not query_wants_audio:
|
||||
try:
|
||||
sample_url = str(supported_url[0]) if supported_url else ""
|
||||
fmts = self._list_formats_cached(
|
||||
sample_url,
|
||||
playlist_items_value=playlist_items,
|
||||
formats_cache=formats_cache,
|
||||
ytdlp_tool=ytdlp_tool,
|
||||
)
|
||||
if fmts:
|
||||
has_video = any(str(f.get("vcodec", "none")) != "none" for f in fmts if isinstance(f, dict))
|
||||
has_audio = any(str(f.get("acodec", "none")) != "none" for f in fmts if isinstance(f, dict))
|
||||
if has_audio and not has_video:
|
||||
mode = "audio"
|
||||
ytdl_format = ytdlp_tool.default_format("audio")
|
||||
debug("[download-file] No video formats detected; switching to audio mode")
|
||||
else:
|
||||
if "bandcamp.com/album/" in sample_url:
|
||||
mode = "audio"
|
||||
ytdl_format = ytdlp_tool.default_format("audio")
|
||||
debug("[download-file] Bandcamp album detected; switching to audio mode")
|
||||
except Exception as e:
|
||||
debug(f"[download-file] Audio-only detection error: {e}")
|
||||
|
||||
# Auto-detect audio mode for explicit format IDs
|
||||
if len(supported_url) == 1 and ytdl_format and mode != "audio":
|
||||
try:
|
||||
candidates = self._list_formats_cached(
|
||||
supported_url[0],
|
||||
playlist_items_value=playlist_items,
|
||||
formats_cache=formats_cache,
|
||||
ytdlp_tool=ytdlp_tool,
|
||||
)
|
||||
if candidates:
|
||||
match = next((f for f in candidates if str(f.get("format_id", "")) == str(ytdl_format)), None)
|
||||
if match:
|
||||
vcodec = str(match.get("vcodec", "none"))
|
||||
acodec = str(match.get("acodec", "none"))
|
||||
if vcodec == "none" and acodec != "none":
|
||||
debug(f"[download-file] Requested format {ytdl_format} is audio-only; switching mode to audio")
|
||||
mode = "audio"
|
||||
except Exception as e:
|
||||
debug(f"[download-file] Error validating format mode: {e}")
|
||||
|
||||
timeout_seconds = 300
|
||||
try:
|
||||
override = config.get("_pipeobject_timeout_seconds") if isinstance(config, dict) else None
|
||||
|
||||
Reference in New Issue
Block a user