Adds advanced save controls, load-video resize options, archive-sequence master preset, FFmpeg capability self-check cache, optional UV_TranscodeVideo utility node, Comfy registry metadata scaffolding, updated docs/workflows, and expanded tests. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
81 lines
2.3 KiB
Python
81 lines
2.3 KiB
Python
import json
|
|
import shutil
|
|
import subprocess
|
|
import tempfile
|
|
from pathlib import Path
|
|
|
|
|
|
_CACHE_PATH = Path(tempfile.gettempdir()) / "uvio_ffmpeg_probe_cache.json"
|
|
|
|
|
|
def get_ffmpeg_path(prefer_system=True):
|
|
if prefer_system:
|
|
found = shutil.which("ffmpeg")
|
|
if found:
|
|
return found
|
|
try:
|
|
import imageio_ffmpeg
|
|
|
|
return imageio_ffmpeg.get_ffmpeg_exe()
|
|
except Exception as exc:
|
|
raise RuntimeError("FFmpeg binary not found. Install ffmpeg or imageio-ffmpeg.") from exc
|
|
|
|
|
|
def ffmpeg_codec_probe(ffmpeg_path=None):
|
|
ffmpeg_path = ffmpeg_path or get_ffmpeg_path()
|
|
proc = subprocess.run([ffmpeg_path, "-hide_banner", "-codecs"], capture_output=True, text=True, check=False)
|
|
if proc.returncode != 0:
|
|
return {"error": proc.stderr.strip() or "ffmpeg codec probe failed", "encoders": {}}
|
|
txt = proc.stdout.lower()
|
|
return {
|
|
"encoders": {
|
|
"libx264": "libx264" in txt,
|
|
"libx265": "libx265" in txt,
|
|
"libsvtav1": "libsvtav1" in txt,
|
|
"libvpx-vp9": "libvpx-vp9" in txt,
|
|
"prores_ks": "prores_ks" in txt,
|
|
"dnxhd": "dnxhd" in txt,
|
|
}
|
|
}
|
|
|
|
|
|
def ensure_ffmpeg_ready():
|
|
if _CACHE_PATH.exists():
|
|
try:
|
|
return json.loads(_CACHE_PATH.read_text())
|
|
except Exception:
|
|
pass
|
|
ffmpeg_path = get_ffmpeg_path()
|
|
codecs = ffmpeg_codec_probe(ffmpeg_path)
|
|
payload = {"ffmpeg_path": ffmpeg_path, **codecs}
|
|
try:
|
|
_CACHE_PATH.write_text(json.dumps(payload))
|
|
except Exception:
|
|
pass
|
|
return payload
|
|
|
|
|
|
def _ffprobe_path(ffmpeg_path):
|
|
folder = ffmpeg_path.rsplit("/", 1)[0] if "/" in ffmpeg_path else ""
|
|
candidate = f"{folder}/ffprobe" if folder else "ffprobe"
|
|
return candidate if shutil.which(candidate) else "ffprobe"
|
|
|
|
|
|
def ffprobe_json(path, ffmpeg_path=None):
|
|
ffmpeg_path = ffmpeg_path or get_ffmpeg_path()
|
|
ffprobe = _ffprobe_path(ffmpeg_path)
|
|
cmd = [
|
|
ffprobe,
|
|
"-v",
|
|
"error",
|
|
"-print_format",
|
|
"json",
|
|
"-show_streams",
|
|
"-show_format",
|
|
path,
|
|
]
|
|
proc = subprocess.run(cmd, capture_output=True, text=True, check=False)
|
|
if proc.returncode != 0:
|
|
return {"error": proc.stderr.strip() or "ffprobe failed"}
|
|
return json.loads(proc.stdout or "{}")
|