h
This commit is contained in:
137
tool/ytdlp.py
137
tool/ytdlp.py
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user