j
This commit is contained in:
@@ -33,6 +33,7 @@ from rich.prompt import Confirm
|
||||
from tool.ytdlp import (
|
||||
YtDlpTool,
|
||||
_best_subtitle_sidecar,
|
||||
_SUBTITLE_EXTS,
|
||||
_download_with_timeout,
|
||||
_format_chapters_note,
|
||||
_read_text_file,
|
||||
@@ -2413,7 +2414,7 @@ class Download_File(Cmdlet):
|
||||
except Exception:
|
||||
continue
|
||||
try:
|
||||
if p_path.suffix.lower() in _best_subtitle_sidecar.__defaults__[0]:
|
||||
if p_path.suffix.lower() in _SUBTITLE_EXTS:
|
||||
continue
|
||||
except Exception:
|
||||
pass
|
||||
@@ -2936,6 +2937,223 @@ class Download_File(Cmdlet):
|
||||
"media_kind": "video" if opts.mode == "video" else "audio",
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def download_streaming_url_as_pipe_objects(
|
||||
url: str,
|
||||
config: Dict[str, Any],
|
||||
*,
|
||||
mode_hint: Optional[str] = None,
|
||||
ytdl_format_hint: Optional[str] = None,
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""Download a yt-dlp-supported URL and return PipeObject-style dict(s).
|
||||
|
||||
This is a lightweight helper intended for cmdlets that need to expand streaming URLs
|
||||
into local files without re-implementing yt-dlp glue.
|
||||
"""
|
||||
url_str = str(url or "").strip()
|
||||
if not url_str:
|
||||
return []
|
||||
|
||||
if not is_url_supported_by_ytdlp(url_str):
|
||||
return []
|
||||
|
||||
try:
|
||||
from SYS.config import resolve_output_dir
|
||||
|
||||
out_dir = resolve_output_dir(config)
|
||||
if out_dir is None:
|
||||
return []
|
||||
except Exception:
|
||||
return []
|
||||
|
||||
cookies_path = None
|
||||
try:
|
||||
cookie_candidate = YtDlpTool(config).resolve_cookiefile()
|
||||
if cookie_candidate is not None and cookie_candidate.is_file():
|
||||
cookies_path = cookie_candidate
|
||||
except Exception:
|
||||
cookies_path = None
|
||||
|
||||
quiet_download = False
|
||||
try:
|
||||
quiet_download = bool((config or {}).get("_quiet_background_output"))
|
||||
except Exception:
|
||||
quiet_download = False
|
||||
|
||||
mode = str(mode_hint or "").strip().lower() if mode_hint else ""
|
||||
if mode not in {"audio", "video"}:
|
||||
mode = "video"
|
||||
try:
|
||||
cf = (
|
||||
str(cookies_path)
|
||||
if cookies_path is not None and cookies_path.is_file() else None
|
||||
)
|
||||
fmts_probe = list_formats(
|
||||
url_str,
|
||||
no_playlist=False,
|
||||
playlist_items=None,
|
||||
cookiefile=cf,
|
||||
)
|
||||
if isinstance(fmts_probe, list) and fmts_probe:
|
||||
has_video = False
|
||||
for f in fmts_probe:
|
||||
if not isinstance(f, dict):
|
||||
continue
|
||||
vcodec = str(f.get("vcodec", "none") or "none").strip().lower()
|
||||
if vcodec and vcodec != "none":
|
||||
has_video = True
|
||||
break
|
||||
mode = "video" if has_video else "audio"
|
||||
except Exception:
|
||||
mode = "video"
|
||||
|
||||
fmt_hint = str(ytdl_format_hint).strip() if ytdl_format_hint else ""
|
||||
chosen_format: Optional[str]
|
||||
if fmt_hint:
|
||||
chosen_format = fmt_hint
|
||||
else:
|
||||
chosen_format = None
|
||||
if mode == "audio":
|
||||
chosen_format = "bestaudio/best"
|
||||
|
||||
opts = DownloadOptions(
|
||||
url=url_str,
|
||||
mode=mode,
|
||||
output_dir=Path(out_dir),
|
||||
cookies_path=cookies_path,
|
||||
ytdl_format=chosen_format,
|
||||
quiet=quiet_download,
|
||||
embed_chapters=True,
|
||||
write_sub=True,
|
||||
)
|
||||
|
||||
try:
|
||||
result_obj = _download_with_timeout(opts, timeout_seconds=300)
|
||||
except Exception as exc:
|
||||
log(f"[download-file] Download failed for {url_str}: {exc}", file=sys.stderr)
|
||||
return []
|
||||
|
||||
results: List[Any]
|
||||
if isinstance(result_obj, list):
|
||||
results = list(result_obj)
|
||||
else:
|
||||
paths = getattr(result_obj, "paths", None)
|
||||
if isinstance(paths, list) and paths:
|
||||
results = []
|
||||
for p in paths:
|
||||
try:
|
||||
p_path = Path(p)
|
||||
except Exception:
|
||||
continue
|
||||
if not p_path.exists() or p_path.is_dir():
|
||||
continue
|
||||
try:
|
||||
hv = sha256_file(p_path)
|
||||
except Exception:
|
||||
hv = None
|
||||
try:
|
||||
results.append(
|
||||
DownloadMediaResult(
|
||||
path=p_path,
|
||||
info=getattr(result_obj, "info", {}) or {},
|
||||
tag=list(getattr(result_obj, "tag", []) or []),
|
||||
source_url=getattr(result_obj, "source_url", None) or url_str,
|
||||
hash_value=hv,
|
||||
)
|
||||
)
|
||||
except Exception:
|
||||
continue
|
||||
else:
|
||||
results = [result_obj]
|
||||
|
||||
out: List[Dict[str, Any]] = []
|
||||
for downloaded in results:
|
||||
try:
|
||||
info = (
|
||||
downloaded.info
|
||||
if isinstance(getattr(downloaded, "info", None), dict) else {}
|
||||
)
|
||||
except Exception:
|
||||
info = {}
|
||||
|
||||
try:
|
||||
media_path = Path(str(getattr(downloaded, "path", "") or ""))
|
||||
except Exception:
|
||||
continue
|
||||
if not media_path.exists() or media_path.is_dir():
|
||||
continue
|
||||
|
||||
try:
|
||||
hash_value = getattr(downloaded, "hash_value", None) or sha256_file(media_path)
|
||||
except Exception:
|
||||
hash_value = None
|
||||
|
||||
title = None
|
||||
try:
|
||||
title = info.get("title")
|
||||
except Exception:
|
||||
title = None
|
||||
title = title or media_path.stem
|
||||
|
||||
tags = list(getattr(downloaded, "tag", []) or [])
|
||||
if title and f"title:{title}" not in tags:
|
||||
tags.insert(0, f"title:{title}")
|
||||
|
||||
final_url = None
|
||||
try:
|
||||
page_url = info.get("webpage_url") or info.get("original_url") or info.get("url")
|
||||
if page_url:
|
||||
final_url = str(page_url)
|
||||
except Exception:
|
||||
final_url = None
|
||||
if not final_url:
|
||||
final_url = url_str
|
||||
|
||||
po: Dict[str, Any] = {
|
||||
"path": str(media_path),
|
||||
"hash": hash_value,
|
||||
"title": title,
|
||||
"url": final_url,
|
||||
"tag": tags,
|
||||
"action": "cmdlet:download-file",
|
||||
"is_temp": True,
|
||||
"ytdl_format": getattr(opts, "ytdl_format", None),
|
||||
"store": getattr(opts, "storage_name", None) or getattr(opts, "storage_location", None) or "PATH",
|
||||
"media_kind": "video" if opts.mode == "video" else "audio",
|
||||
}
|
||||
|
||||
try:
|
||||
chapters_text = _format_chapters_note(info)
|
||||
except Exception:
|
||||
chapters_text = None
|
||||
if chapters_text:
|
||||
notes = po.get("notes")
|
||||
if not isinstance(notes, dict):
|
||||
notes = {}
|
||||
notes.setdefault("chapters", chapters_text)
|
||||
po["notes"] = notes
|
||||
|
||||
try:
|
||||
sub_path = _best_subtitle_sidecar(media_path)
|
||||
except Exception:
|
||||
sub_path = None
|
||||
if sub_path is not None:
|
||||
sub_text = _read_text_file(sub_path)
|
||||
if sub_text:
|
||||
notes = po.get("notes")
|
||||
if not isinstance(notes, dict):
|
||||
notes = {}
|
||||
notes["sub"] = sub_text
|
||||
po["notes"] = notes
|
||||
try:
|
||||
sub_path.unlink()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
out.append(po)
|
||||
|
||||
return out
|
||||
|
||||
@staticmethod
|
||||
def _normalise_hash_hex(value: Optional[str]) -> Optional[str]:
|
||||
if not value or not isinstance(value, str):
|
||||
|
||||
Reference in New Issue
Block a user