sdfsdf
This commit is contained in:
@@ -48,6 +48,64 @@ coerce_to_pipe_object = sh.coerce_to_pipe_object
|
||||
get_field = sh.get_field
|
||||
|
||||
|
||||
def _live_ui_and_pipe_index() -> tuple[Optional[Any], int]:
|
||||
ui = None
|
||||
try:
|
||||
ui = pipeline_context.get_live_progress() if hasattr(pipeline_context, "get_live_progress") else None
|
||||
except Exception:
|
||||
ui = None
|
||||
|
||||
pipe_idx: int = 0
|
||||
try:
|
||||
stage_ctx = pipeline_context.get_stage_context() if hasattr(pipeline_context, "get_stage_context") else None
|
||||
maybe_idx = getattr(stage_ctx, "pipe_index", None) if stage_ctx is not None else None
|
||||
if isinstance(maybe_idx, int):
|
||||
pipe_idx = int(maybe_idx)
|
||||
except Exception:
|
||||
pipe_idx = 0
|
||||
|
||||
return ui, pipe_idx
|
||||
|
||||
|
||||
def _begin_live_steps(total_steps: int) -> None:
|
||||
"""Declare the total number of steps for the current pipe."""
|
||||
ui, pipe_idx = _live_ui_and_pipe_index()
|
||||
if ui is None:
|
||||
return
|
||||
try:
|
||||
begin = getattr(ui, "begin_pipe_steps", None)
|
||||
if callable(begin):
|
||||
begin(int(pipe_idx), total_steps=int(total_steps))
|
||||
except Exception:
|
||||
return
|
||||
|
||||
|
||||
def _step(text: str) -> None:
|
||||
"""Emit a *new* step (increments i/N and advances percent automatically)."""
|
||||
ui, pipe_idx = _live_ui_and_pipe_index()
|
||||
if ui is None:
|
||||
return
|
||||
try:
|
||||
adv = getattr(ui, "advance_pipe_step", None)
|
||||
if callable(adv):
|
||||
adv(int(pipe_idx), str(text))
|
||||
except Exception:
|
||||
return
|
||||
|
||||
|
||||
def _set_pipe_percent(percent: int) -> None:
|
||||
"""Best-effort percent update without changing step text."""
|
||||
ui, pipe_idx = _live_ui_and_pipe_index()
|
||||
if ui is None:
|
||||
return
|
||||
try:
|
||||
set_pct = getattr(ui, "set_pipe_percent", None)
|
||||
if callable(set_pct):
|
||||
set_pct(int(pipe_idx), int(percent))
|
||||
except Exception:
|
||||
return
|
||||
|
||||
|
||||
# Minimal inlined helpers from helper/download.py (is_url_supported_by_ytdlp, list_formats)
|
||||
try:
|
||||
import yt_dlp # type: ignore
|
||||
@@ -353,7 +411,17 @@ def _download_with_sections_via_cli(url: str, ytdl_options: Dict[str, Any], sect
|
||||
session_id = hashlib.md5((url + str(time.time()) + ''.join(random.choices(string.ascii_letters, k=10))).encode()).hexdigest()[:12]
|
||||
first_section_info = None
|
||||
|
||||
total_sections = len(sections_list)
|
||||
for section_idx, section in enumerate(sections_list, 1):
|
||||
# While step 1/2 is "downloading", keep the pipe bar moving for multi-section clips.
|
||||
# Map sections onto 50..99 so step 2/2 can still jump to 100.
|
||||
try:
|
||||
if total_sections > 0:
|
||||
pct = 50 + int(((section_idx - 1) / max(1, total_sections)) * 49)
|
||||
_set_pipe_percent(pct)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
base_outtmpl = ytdl_options.get("outtmpl", "%(title)s.%(ext)s")
|
||||
output_dir_path = Path(base_outtmpl).parent
|
||||
filename_tmpl = f"{session_id}_{section_idx}"
|
||||
@@ -385,6 +453,17 @@ def _download_with_sections_via_cli(url: str, ytdl_options: Dict[str, Any], sect
|
||||
debug(f"Error extracting metadata: {e}")
|
||||
|
||||
cmd = ["yt-dlp"]
|
||||
if quiet:
|
||||
cmd.append("--quiet")
|
||||
cmd.append("--no-warnings")
|
||||
cmd.append("--no-progress")
|
||||
# Keep ffmpeg/merger output from taking over the terminal.
|
||||
cmd.extend(["--postprocessor-args", "ffmpeg:-hide_banner -loglevel error"])
|
||||
if ytdl_options.get("ffmpeg_location"):
|
||||
try:
|
||||
cmd.extend(["--ffmpeg-location", str(ytdl_options["ffmpeg_location"])])
|
||||
except Exception:
|
||||
pass
|
||||
if ytdl_options.get("format"):
|
||||
cmd.extend(["-f", ytdl_options["format"]])
|
||||
if ytdl_options.get("merge_output_format"):
|
||||
@@ -413,7 +492,7 @@ def _download_with_sections_via_cli(url: str, ytdl_options: Dict[str, Any], sect
|
||||
cmd.append("--write-auto-sub")
|
||||
cmd.extend(["--sub-format", "vtt"])
|
||||
if ytdl_options.get("force_keyframes_at_cuts"):
|
||||
cmd.extend(["--force-keyframes-at-cuts"]) if ytdl_options.get("force_keyframes_at_cuts") else None
|
||||
cmd.append("--force-keyframes-at-cuts")
|
||||
cmd.extend(["-o", section_outtmpl])
|
||||
if ytdl_options.get("cookiefile"):
|
||||
cookies_path = ytdl_options["cookiefile"].replace("\\", "/")
|
||||
@@ -428,10 +507,23 @@ def _download_with_sections_via_cli(url: str, ytdl_options: Dict[str, Any], sect
|
||||
if not quiet:
|
||||
debug(f"Running yt-dlp for section: {section}")
|
||||
try:
|
||||
subprocess.run(cmd, check=True)
|
||||
if quiet:
|
||||
subprocess.run(cmd, check=True, capture_output=True, text=True)
|
||||
else:
|
||||
subprocess.run(cmd, check=True)
|
||||
except subprocess.CalledProcessError as exc:
|
||||
stderr_text = (exc.stderr or "")
|
||||
tail = "\n".join(stderr_text.splitlines()[-12:]).strip()
|
||||
details = f"\n{tail}" if tail else ""
|
||||
raise DownloadError(f"yt-dlp failed for section {section} (exit {exc.returncode}){details}") from exc
|
||||
except Exception as exc:
|
||||
if not quiet:
|
||||
debug(f"yt-dlp error for section {section}: {exc}")
|
||||
raise DownloadError(f"yt-dlp failed for section {section}: {exc}") from exc
|
||||
|
||||
# Mark near-complete before returning so the runner can finalize cleanly.
|
||||
try:
|
||||
_set_pipe_percent(99)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return session_id, first_section_info or {}
|
||||
|
||||
@@ -720,30 +812,16 @@ def download_media(
|
||||
session_id = None
|
||||
first_section_info = {}
|
||||
if ytdl_options.get("download_sections"):
|
||||
# The CLI path emits yt-dlp's own progress output; pause the pipeline Live UI
|
||||
# so those progress bars remain visible instead of being clobbered.
|
||||
try:
|
||||
from contextlib import nullcontext
|
||||
except Exception:
|
||||
nullcontext = None # type: ignore
|
||||
|
||||
suspend = getattr(pipeline_context, "suspend_live_progress", None)
|
||||
cm = suspend() if callable(suspend) else (nullcontext() if nullcontext else None)
|
||||
if cm is None:
|
||||
session_id, first_section_info = _download_with_sections_via_cli(
|
||||
opts.url,
|
||||
ytdl_options,
|
||||
ytdl_options.get("download_sections", []),
|
||||
quiet=opts.quiet,
|
||||
)
|
||||
else:
|
||||
with cm:
|
||||
session_id, first_section_info = _download_with_sections_via_cli(
|
||||
opts.url,
|
||||
ytdl_options,
|
||||
ytdl_options.get("download_sections", []),
|
||||
quiet=opts.quiet,
|
||||
)
|
||||
# For clip (download_sections), keep pipeline Live UI active and suppress
|
||||
# yt-dlp/ffmpeg CLI spam when running in quiet/pipeline mode.
|
||||
live_ui, _ = _live_ui_and_pipe_index()
|
||||
quiet_sections = bool(opts.quiet) or (live_ui is not None)
|
||||
session_id, first_section_info = _download_with_sections_via_cli(
|
||||
opts.url,
|
||||
ytdl_options,
|
||||
ytdl_options.get("download_sections", []),
|
||||
quiet=quiet_sections,
|
||||
)
|
||||
info = None
|
||||
else:
|
||||
with yt_dlp.YoutubeDL(ytdl_options) as ydl: # type: ignore[arg-type]
|
||||
@@ -2168,7 +2246,6 @@ class Download_Media(Cmdlet):
|
||||
pipeline_context.set_last_result_table(table, results_list)
|
||||
|
||||
log(f"", file=sys.stderr)
|
||||
log(f"Use: @N to select and download format", file=sys.stderr)
|
||||
return 0
|
||||
|
||||
# Download each URL
|
||||
@@ -2196,6 +2273,11 @@ class Download_Media(Cmdlet):
|
||||
log(f"Skipping download: {url}", file=sys.stderr)
|
||||
continue
|
||||
|
||||
# Step progress is per-URL download.
|
||||
# Keep steps meaningful: long-running download + finalize.
|
||||
# (Fast internal bookkeeping should not be steps.)
|
||||
_begin_live_steps(2)
|
||||
|
||||
# If playlist_items is specified but looks like a format ID (e.g. from table selection),
|
||||
# treat it as a format selector instead of playlist items.
|
||||
# This handles the case where @N selection passes -item <format_id>
|
||||
@@ -2274,6 +2356,7 @@ class Download_Media(Cmdlet):
|
||||
write_sub=write_sub,
|
||||
)
|
||||
|
||||
_step("downloading")
|
||||
# Use timeout wrapper to prevent hanging
|
||||
debug(f"Starting download with 5-minute timeout...")
|
||||
result_obj = _download_with_timeout(opts, timeout_seconds=300)
|
||||
@@ -2416,6 +2499,10 @@ class Download_Media(Cmdlet):
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Complete the step sequence: we return here and the user must
|
||||
# re-run with @N selection.
|
||||
_step("awaiting selection")
|
||||
|
||||
log("Requested format is not available; select a working format with @N", file=sys.stderr)
|
||||
return 0
|
||||
|
||||
@@ -2522,6 +2609,10 @@ class Download_Media(Cmdlet):
|
||||
|
||||
debug(f"Emitting {len(pipe_objects)} result(s) to pipeline...")
|
||||
|
||||
# Mark complete *before* the first emit, because the pipeline clears the
|
||||
# status line on emit().
|
||||
_step("finalized")
|
||||
|
||||
stage_ctx = pipeline_context.get_stage_context()
|
||||
emit_enabled = bool(stage_ctx is not None and not getattr(stage_ctx, "is_last_stage", False))
|
||||
for pipe_obj_dict in pipe_objects:
|
||||
|
||||
Reference in New Issue
Block a user