This commit is contained in:
2026-01-10 17:30:18 -08:00
parent 08fef4a5d3
commit c2edd5139f
10 changed files with 769 additions and 86 deletions

View File

@@ -268,6 +268,143 @@ def probe_url(
return cast(Optional[Dict[str, Any]], result_container[0])
def is_browseable_format(fmt: Any) -> bool:
"""Check if a format is user-browseable (not storyboard, metadata, etc).
Used by the ytdlp format selector to filter out non-downloadable formats.
Returns False for:
- MHTML, JSON sidecar metadata
- Storyboard/thumbnail formats
- Audio-only or video-only when both available
Args:
fmt: Format dict from yt-dlp with keys like format_id, ext, vcodec, acodec, format_note
Returns:
bool: True if format is suitable for browsing/selection
"""
if not isinstance(fmt, dict):
return False
format_id = str(fmt.get("format_id") or "").strip()
if not format_id:
return False
# Filter out metadata/sidecar formats
ext = str(fmt.get("ext") or "").strip().lower()
if ext in {"mhtml", "json"}:
return False
# Filter out storyboard/thumbnail formats
note = str(fmt.get("format_note") or "").lower()
if "storyboard" in note:
return False
if format_id.lower().startswith("sb"):
return False
# Filter out formats with no audio and no video
vcodec = str(fmt.get("vcodec", "none"))
acodec = str(fmt.get("acodec", "none"))
return not (vcodec == "none" and acodec == "none")
def format_for_table_selection(
fmt: Dict[str, Any],
url: str,
index: int,
*,
selection_format_id: Optional[str] = None,
) -> Dict[str, Any]:
"""Format a yt-dlp format dict into a table result row for selection.
This helper formats a single format from list_formats() into the shape
expected by the ResultTable system, ready for user selection and routing
to download-file with -format argument.
Args:
fmt: Format dict from yt-dlp
url: The URL this format came from
index: Row number for display (1-indexed)
selection_format_id: Override format_id for selection (e.g., with +ba suffix)
Returns:
dict: Format result row with _selection_args for table system
Example:
fmts = list_formats("https://youtube.com/watch?v=abc")
browseable = [f for f in fmts if is_browseable_format(f)]
results = [format_for_table_selection(f, url, i+1) for i, f in enumerate(browseable)]
"""
format_id = fmt.get("format_id", "")
resolution = fmt.get("resolution", "")
ext = fmt.get("ext", "")
vcodec = fmt.get("vcodec", "none")
acodec = fmt.get("acodec", "none")
filesize = fmt.get("filesize")
filesize_approx = fmt.get("filesize_approx")
# If not provided, compute selection format ID (add +ba for video-only)
if selection_format_id is None:
selection_format_id = format_id
try:
if vcodec != "none" and acodec == "none" and format_id:
selection_format_id = f"{format_id}+ba"
except Exception:
pass
# Format file size
size_str = ""
size_prefix = ""
size_bytes = filesize or filesize_approx
try:
if isinstance(size_bytes, (int, float)) and size_bytes > 0:
size_mb = float(size_bytes) / (1024 * 1024)
size_str = f"{size_prefix}{size_mb:.1f}MB"
except Exception:
pass
# Build description
desc_parts: List[str] = []
if resolution and resolution != "audio only":
desc_parts.append(resolution)
if ext:
desc_parts.append(str(ext).upper())
if vcodec != "none":
desc_parts.append(f"v:{vcodec}")
if acodec != "none":
desc_parts.append(f"a:{acodec}")
if size_str:
desc_parts.append(size_str)
format_desc = " | ".join(desc_parts)
# Build table row
return {
"table": "download-file",
"title": f"Format {format_id}",
"url": url,
"target": url,
"detail": format_desc,
"annotations": [ext, resolution] if resolution else [ext],
"media_kind": "format",
"columns": [
("ID", format_id),
("Resolution", resolution or "N/A"),
("Ext", ext),
("Size", size_str or ""),
("Video", vcodec),
("Audio", acodec),
],
"full_metadata": {
"format_id": format_id,
"url": url,
"item_selector": selection_format_id,
"_selection_args": ["-format", selection_format_id],
},
"_selection_args": ["-format", selection_format_id],
}
@dataclass(slots=True)
class YtDlpDefaults:
"""User-tunable defaults for yt-dlp behavior.