Files
YouTubeDL/host/ffmpeg_progress.py
DepressedCat 5f2aad00f4
Check extension and helper / check (push) Canceled after 0s
Publish YouTubeDL 1.4.5 and website on Gitea
2026-10-10 23:53:17 +03:00

72 lines
3.1 KiB
Python

"""Read FFmpeg's real timeline and output size without blocking stderr."""
from __future__ import annotations
import math
import subprocess
import threading
from host.media import NO_WINDOW
def run_ffmpeg(command, duration, stage, engine, report):
duration = float(duration or 0)
if not math.isfinite(duration) or duration < 0:
duration = 0
report(stage=stage, percent=0 if duration else None, engine=engine,
speed="", eta=None, processedSeconds=0, durationSeconds=duration, written=0, message="")
process = subprocess.Popen(command, stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
text=True, encoding="utf-8", errors="replace", creationflags=NO_WINDOW)
errors, finalizing = [], threading.Event()
progress_lock = threading.Lock()
def read_errors():
for line in process.stderr:
errors.append(line)
if len(errors) > 80:
del errors[:40]
if "Starting second pass" in line:
with progress_lock:
finalizing.set()
report(stage="finalizing", percent=99, engine="copy", speed="", eta=None,
message="Финализация файла")
reader = threading.Thread(target=read_errors, daemon=True)
reader.start()
stats = {}
elapsed = 0.0
try:
for raw in process.stdout:
key, _, value = raw.strip().partition("=")
stats[key] = value
if key != "progress":
continue
try:
elapsed = max(elapsed, max(0, int(stats.get("out_time_us", "0")) / 1_000_000))
except ValueError:
pass
speed = stats.get("speed", "")
try:
rate = float(speed.rstrip("x"))
except ValueError:
rate = 0
eta = max(0, (duration - elapsed) / rate) if duration and rate > 0 and math.isfinite(rate) and not finalizing.is_set() else None
try:
written = max(0, int(stats.get("total_size", "0")))
except ValueError:
written = None
with progress_lock:
is_finalizing = finalizing.is_set()
report(stage="finalizing" if is_finalizing else stage, percent=99 if is_finalizing else min(99, elapsed / duration * 100) if duration else None,
engine="copy" if is_finalizing else engine, speed=speed if rate > 0 and math.isfinite(rate) and not is_finalizing else "", eta=None if is_finalizing else eta,
processedSeconds=min(elapsed, duration) if duration else elapsed,
durationSeconds=duration, written=written,
message="Финализация файла" if is_finalizing else "")
process.wait()
reader.join(timeout=3)
return process.returncode, "".join(errors)[-2000:]
finally:
if process.poll() is None:
process.kill()
process.wait(timeout=10)
for stream in (process.stdout, process.stderr):
stream.close()