Improve format selection for audio/video downloads
Refactored format selection logic to explicitly handle audio-only and progressive formats using new flags. Updated GUI to pass format metadata, ensuring correct yt-dlp command construction and more reliable downloads for different format types.
This commit is contained in:
@@ -40,6 +40,8 @@ class DownloadThread(QThread):
|
|||||||
url,
|
url,
|
||||||
path,
|
path,
|
||||||
format_id,
|
format_id,
|
||||||
|
is_audio_only=False,
|
||||||
|
format_has_audio=False,
|
||||||
subtitle_langs=None,
|
subtitle_langs=None,
|
||||||
is_playlist=False,
|
is_playlist=False,
|
||||||
merge_subs=False,
|
merge_subs=False,
|
||||||
@@ -61,6 +63,8 @@ class DownloadThread(QThread):
|
|||||||
self.url = url
|
self.url = url
|
||||||
self.path = Path(path)
|
self.path = Path(path)
|
||||||
self.format_id = format_id
|
self.format_id = format_id
|
||||||
|
self.is_audio_only = is_audio_only
|
||||||
|
self.format_has_audio = format_has_audio
|
||||||
self.subtitle_langs = subtitle_langs if subtitle_langs else []
|
self.subtitle_langs = subtitle_langs if subtitle_langs else []
|
||||||
self.is_playlist = is_playlist
|
self.is_playlist = is_playlist
|
||||||
self.merge_subs = merge_subs
|
self.merge_subs = merge_subs
|
||||||
@@ -167,26 +171,19 @@ class DownloadThread(QThread):
|
|||||||
|
|
||||||
# Format selection strategy - use format ID if provided or fallback to resolution
|
# Format selection strategy - use format ID if provided or fallback to resolution
|
||||||
if self.format_id:
|
if self.format_id:
|
||||||
# Strip the -drc suffix if present to fix issues with certain audio formats
|
|
||||||
clean_format_id = self.format_id.split("-drc")[0] if "-drc" in self.format_id else self.format_id
|
clean_format_id = self.format_id.split("-drc")[0] if "-drc" in self.format_id else self.format_id
|
||||||
|
|
||||||
# Use a simple heuristic: if format_id contains 'audio' or ends with 'a', treat as audio-only
|
# If the selected format is audio-only, pass it directly.
|
||||||
# This avoids the need for Python API metadata extraction
|
if self.is_audio_only:
|
||||||
is_audio_format = "audio" in clean_format_id.lower() or clean_format_id.endswith("a")
|
|
||||||
|
|
||||||
# For audio-only formats, don't try to merge with video
|
|
||||||
if is_audio_format:
|
|
||||||
cmd.extend(["-f", clean_format_id])
|
cmd.extend(["-f", clean_format_id])
|
||||||
logger.debug(f"Using audio-only format selection: {clean_format_id}")
|
logger.debug(f"Using audio-only format selection: {clean_format_id}")
|
||||||
|
# If the selected format already includes an audio track (progressive), no merge needed.
|
||||||
|
elif self.format_has_audio:
|
||||||
|
cmd.extend(["-f", clean_format_id])
|
||||||
|
logger.debug(f"Using progressive format with bundled audio: {clean_format_id}")
|
||||||
else:
|
else:
|
||||||
cmd.extend(["-f", f"{clean_format_id}+bestaudio/best"])
|
cmd.extend(["-f", f"{clean_format_id}+bestaudio/best"])
|
||||||
logger.debug(f"Using video format selection with audio: {clean_format_id}+bestaudio/best")
|
logger.debug(f"Using video-only format merged with best audio: {clean_format_id}+bestaudio/best")
|
||||||
|
|
||||||
# Determine output format based on the selected format ID - only for video formats
|
|
||||||
# Note: This is a best-effort approach without Python API metadata
|
|
||||||
if not is_audio_format:
|
|
||||||
# Let yt-dlp handle format detection automatically via CLI
|
|
||||||
logger.debug("Letting yt-dlp CLI determine output format automatically")
|
|
||||||
else:
|
else:
|
||||||
# If no specific format ID, use resolution-based sorting (-S)
|
# If no specific format ID, use resolution-based sorting (-S)
|
||||||
res_value = self.resolution if self.resolution else "720" # Default to 720p if no resolution specified
|
res_value = self.resolution if self.resolution else "720" # Default to 720p if no resolution specified
|
||||||
|
|||||||
@@ -262,6 +262,8 @@ class FormatTableMixin:
|
|||||||
# Column 0: Select Checkbox (Always shown)
|
# Column 0: Select Checkbox (Always shown)
|
||||||
checkbox = QCheckBox()
|
checkbox = QCheckBox()
|
||||||
checkbox.format_id = str(f.get("format_id", "")) # type: ignore[reportAttributeAccessIssue]
|
checkbox.format_id = str(f.get("format_id", "")) # type: ignore[reportAttributeAccessIssue]
|
||||||
|
checkbox.is_audio_only = bool((f.get("vcodec") or "none").lower() == "none") # type: ignore[attr-defined]
|
||||||
|
checkbox.has_audio = bool(f.get("acodec") and f.get("acodec") != "none") # type: ignore[attr-defined]
|
||||||
checkbox.clicked.connect(lambda checked, cb=checkbox: self.handle_checkbox_click(cb))
|
checkbox.clicked.connect(lambda checked, cb=checkbox: self.handle_checkbox_click(cb))
|
||||||
self.format_checkboxes.append(checkbox)
|
self.format_checkboxes.append(checkbox)
|
||||||
checkbox_widget = QWidget()
|
checkbox_widget = QWidget()
|
||||||
@@ -427,7 +429,11 @@ class FormatTableMixin:
|
|||||||
|
|
||||||
for checkbox in self.format_checkboxes:
|
for checkbox in self.format_checkboxes:
|
||||||
if checkbox.isChecked():
|
if checkbox.isChecked():
|
||||||
return checkbox.format_id
|
return {
|
||||||
|
"format_id": checkbox.format_id,
|
||||||
|
"is_audio_only": getattr(checkbox, "is_audio_only", False),
|
||||||
|
"has_audio": getattr(checkbox, "has_audio", False),
|
||||||
|
}
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def update_format_table(self, formats) -> None:
|
def update_format_table(self, formats) -> None:
|
||||||
|
|||||||
@@ -802,10 +802,13 @@ class YTSageApp(QMainWindow, FormatTableMixin, VideoInfoMixin): # Inherit from
|
|||||||
return
|
return
|
||||||
|
|
||||||
# Get selected format
|
# Get selected format
|
||||||
format_id = self.get_selected_format()
|
selected_format = self.get_selected_format()
|
||||||
if not format_id:
|
if not selected_format:
|
||||||
self.status_label.setText(_('download.please_select_format'))
|
self.status_label.setText(_('download.please_select_format'))
|
||||||
return
|
return
|
||||||
|
format_id = selected_format["format_id"]
|
||||||
|
is_audio_only = bool(selected_format.get("is_audio_only"))
|
||||||
|
format_has_audio = bool(selected_format.get("has_audio"))
|
||||||
|
|
||||||
# Show preparation message
|
# Show preparation message
|
||||||
self.status_label.setText(_('download.preparing'))
|
self.status_label.setText(_('download.preparing'))
|
||||||
@@ -859,6 +862,8 @@ class YTSageApp(QMainWindow, FormatTableMixin, VideoInfoMixin): # Inherit from
|
|||||||
url=url,
|
url=url,
|
||||||
path=path,
|
path=path,
|
||||||
format_id=format_id,
|
format_id=format_id,
|
||||||
|
is_audio_only=is_audio_only,
|
||||||
|
format_has_audio=format_has_audio,
|
||||||
subtitle_langs=selected_subs, # Pass the list of selected subs
|
subtitle_langs=selected_subs, # Pass the list of selected subs
|
||||||
is_playlist=self.is_playlist, # Use the flag directly
|
is_playlist=self.is_playlist, # Use the flag directly
|
||||||
merge_subs=self.merge_subs_checkbox.isChecked(),
|
merge_subs=self.merge_subs_checkbox.isChecked(),
|
||||||
|
|||||||
Reference in New Issue
Block a user