From 73479a46bd2a51e335ded80c7cfeeaed848786c9 Mon Sep 17 00:00:00 2001 From: oop7 <110548351+oop7@users.noreply.github.com> Date: Sat, 1 Nov 2025 12:28:32 +0200 Subject: [PATCH] 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. --- src/core/ytsage_downloader.py | 25 +++++++++++-------------- src/gui/ytsage_gui_format_table.py | 8 +++++++- src/gui/ytsage_gui_main.py | 9 +++++++-- 3 files changed, 25 insertions(+), 17 deletions(-) diff --git a/src/core/ytsage_downloader.py b/src/core/ytsage_downloader.py index 949e0ec..7d92c32 100644 --- a/src/core/ytsage_downloader.py +++ b/src/core/ytsage_downloader.py @@ -40,6 +40,8 @@ class DownloadThread(QThread): url, path, format_id, + is_audio_only=False, + format_has_audio=False, subtitle_langs=None, is_playlist=False, merge_subs=False, @@ -61,6 +63,8 @@ class DownloadThread(QThread): self.url = url self.path = Path(path) 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.is_playlist = is_playlist self.merge_subs = merge_subs @@ -167,26 +171,19 @@ class DownloadThread(QThread): # Format selection strategy - use format ID if provided or fallback to resolution 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 - # Use a simple heuristic: if format_id contains 'audio' or ends with 'a', treat as audio-only - # This avoids the need for Python API metadata extraction - 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: + # If the selected format is audio-only, pass it directly. + if self.is_audio_only: cmd.extend(["-f", 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: cmd.extend(["-f", f"{clean_format_id}+bestaudio/best"]) - logger.debug(f"Using video format selection with 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") + logger.debug(f"Using video-only format merged with best audio: {clean_format_id}+bestaudio/best") else: # 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 diff --git a/src/gui/ytsage_gui_format_table.py b/src/gui/ytsage_gui_format_table.py index bda62a2..441da3c 100644 --- a/src/gui/ytsage_gui_format_table.py +++ b/src/gui/ytsage_gui_format_table.py @@ -262,6 +262,8 @@ class FormatTableMixin: # Column 0: Select Checkbox (Always shown) checkbox = QCheckBox() 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)) self.format_checkboxes.append(checkbox) checkbox_widget = QWidget() @@ -427,7 +429,11 @@ class FormatTableMixin: for checkbox in self.format_checkboxes: 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 def update_format_table(self, formats) -> None: diff --git a/src/gui/ytsage_gui_main.py b/src/gui/ytsage_gui_main.py index 9f6779f..a9df620 100644 --- a/src/gui/ytsage_gui_main.py +++ b/src/gui/ytsage_gui_main.py @@ -802,10 +802,13 @@ class YTSageApp(QMainWindow, FormatTableMixin, VideoInfoMixin): # Inherit from return # Get selected format - format_id = self.get_selected_format() - if not format_id: + selected_format = self.get_selected_format() + if not selected_format: self.status_label.setText(_('download.please_select_format')) 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 self.status_label.setText(_('download.preparing')) @@ -859,6 +862,8 @@ class YTSageApp(QMainWindow, FormatTableMixin, VideoInfoMixin): # Inherit from url=url, path=path, 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 is_playlist=self.is_playlist, # Use the flag directly merge_subs=self.merge_subs_checkbox.isChecked(),