Update app.py
Browse files
app.py
CHANGED
|
@@ -20,8 +20,8 @@ def render_logo_html(px: int = 96) -> str:
|
|
| 20 |
<div style="display:flex;align-items:center;gap:16px;">
|
| 21 |
{img}
|
| 22 |
<div>
|
| 23 |
-
<div style="font-size:1.6rem;font-weight:800;">
|
| 24 |
-
<div style="opacity:0.8;">Frames → Video, optional audio
|
| 25 |
</div>
|
| 26 |
</div>
|
| 27 |
<hr>
|
|
@@ -69,24 +69,34 @@ def prepare_frames_from_upload(files: List[gr.File] | None, prefix: str = "enc")
|
|
| 69 |
counter += 1
|
| 70 |
return str(frames_dir), prefix
|
| 71 |
|
| 72 |
-
def build_ffmpeg_encode(frames_dir: str, prefix: str, fps: float, fmt: str,
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
if
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 80 |
if include_audio and orig_video:
|
| 81 |
args += ["-i", orig_video, "-map", "0:v:0", "-map", "1:a:0", "-shortest"]
|
|
|
|
| 82 |
if fmt == "h265":
|
| 83 |
vcodec = ["-c:v", "libx265"]
|
| 84 |
elif fmt == "vp9":
|
| 85 |
vcodec = ["-c:v", "libvpx-vp9"]
|
| 86 |
else:
|
| 87 |
vcodec = ["-c:v", "libx264"]
|
| 88 |
-
|
| 89 |
-
|
|
|
|
|
|
|
| 90 |
|
| 91 |
def step3_encode(
|
| 92 |
uploaded_frames: List[gr.File] | None,
|
|
@@ -109,16 +119,21 @@ def step3_encode(
|
|
| 109 |
|
| 110 |
# Add progress pipe and run in frames_dir for clean output pathing
|
| 111 |
cmd = [cmd[0], "-progress", "pipe:2"] + cmd[1:]
|
| 112 |
-
proc = subprocess.Popen(
|
| 113 |
-
|
|
|
|
|
|
|
|
|
|
| 114 |
# Approximate by counting files
|
| 115 |
total_frames = len(list(Path(frames_dir).glob(f"{prefix}_*.jpg"))) + len(list(Path(frames_dir).glob(f"{prefix}_*.png")))
|
| 116 |
current = 0
|
| 117 |
-
|
|
|
|
| 118 |
while True:
|
| 119 |
line = proc.stderr.readline()
|
| 120 |
if not line and proc.poll() is not None:
|
| 121 |
break
|
|
|
|
| 122 |
if "frame=" in line:
|
| 123 |
try:
|
| 124 |
current = int(line.strip().split("=")[-1])
|
|
@@ -129,18 +144,20 @@ def step3_encode(
|
|
| 129 |
yield None, f"Encoding… {current}/{total_frames} frames", render_progress(pct, f"Encoding {pct:.0f}%")
|
| 130 |
else:
|
| 131 |
yield None, "Encoding…", render_progress(50.0, "Encoding…")
|
| 132 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 133 |
ret = proc.wait()
|
| 134 |
out_file = Path(frames_dir) / ("output.mp4" if fmt in ("h264", "h265") else "output.webm")
|
|
|
|
| 135 |
if ret != 0 or not out_file.exists():
|
| 136 |
-
err = ""
|
| 137 |
-
try:
|
| 138 |
-
err = proc.stderr.read() if proc.stderr else ""
|
| 139 |
-
except Exception:
|
| 140 |
-
pass
|
| 141 |
yield None, f"Encoding failed.\n\n{err}", render_progress(0.0, "Failed")
|
| 142 |
return
|
| 143 |
|
|
|
|
| 144 |
yield str(out_file), f"Video created: {out_file.name}", render_progress(100.0, "Encoding complete")
|
| 145 |
|
| 146 |
def build_ui():
|
|
|
|
| 20 |
<div style="display:flex;align-items:center;gap:16px;">
|
| 21 |
{img}
|
| 22 |
<div>
|
| 23 |
+
<div style="font-size:1.6rem;font-weight:800;">Valknut · Re-encode Video</div>
|
| 24 |
+
<div style="opacity:0.8;">Frames → Video, optional audio mix</div>
|
| 25 |
</div>
|
| 26 |
</div>
|
| 27 |
<hr>
|
|
|
|
| 69 |
counter += 1
|
| 70 |
return str(frames_dir), prefix
|
| 71 |
|
| 72 |
+
def build_ffmpeg_encode(frames_dir: str, prefix: str, fps: float, fmt: str,
|
| 73 |
+
include_audio: bool, orig_video: str | None) -> list[str]:
|
| 74 |
+
jpgs = sorted(Path(frames_dir).glob(f"{prefix}_*.jpg"))
|
| 75 |
+
pngs = sorted(Path(frames_dir).glob(f"{prefix}_*.png"))
|
| 76 |
+
imgs = jpgs if jpgs else pngs
|
| 77 |
+
if not imgs:
|
| 78 |
+
return []
|
| 79 |
+
|
| 80 |
+
first_frame = imgs[0].name
|
| 81 |
+
pattern = str(imgs[0].with_name(f"{prefix}_%05d{imgs[0].suffix}"))
|
| 82 |
+
start_num = int(Path(first_frame).stem.split("_")[-1])
|
| 83 |
+
|
| 84 |
+
args = [FFMPEG, "-y", "-start_number", str(start_num),
|
| 85 |
+
"-framerate", f"{fps:.6f}", "-i", pattern]
|
| 86 |
+
|
| 87 |
if include_audio and orig_video:
|
| 88 |
args += ["-i", orig_video, "-map", "0:v:0", "-map", "1:a:0", "-shortest"]
|
| 89 |
+
|
| 90 |
if fmt == "h265":
|
| 91 |
vcodec = ["-c:v", "libx265"]
|
| 92 |
elif fmt == "vp9":
|
| 93 |
vcodec = ["-c:v", "libvpx-vp9"]
|
| 94 |
else:
|
| 95 |
vcodec = ["-c:v", "libx264"]
|
| 96 |
+
|
| 97 |
+
out_name = "output.mp4" if fmt in ("h264", "h265") else "output.webm"
|
| 98 |
+
return args + vcodec + ["-pix_fmt", "yuv420p", "-crf", "18", out_name]
|
| 99 |
+
|
| 100 |
|
| 101 |
def step3_encode(
|
| 102 |
uploaded_frames: List[gr.File] | None,
|
|
|
|
| 119 |
|
| 120 |
# Add progress pipe and run in frames_dir for clean output pathing
|
| 121 |
cmd = [cmd[0], "-progress", "pipe:2"] + cmd[1:]
|
| 122 |
+
proc = subprocess.Popen(
|
| 123 |
+
cmd, stderr=subprocess.PIPE, stdout=subprocess.DEVNULL,
|
| 124 |
+
text=True, bufsize=1, cwd=frames_dir
|
| 125 |
+
)
|
| 126 |
+
|
| 127 |
# Approximate by counting files
|
| 128 |
total_frames = len(list(Path(frames_dir).glob(f"{prefix}_*.jpg"))) + len(list(Path(frames_dir).glob(f"{prefix}_*.png")))
|
| 129 |
current = 0
|
| 130 |
+
errors = [] # ✅ collect errors while streaming
|
| 131 |
+
|
| 132 |
while True:
|
| 133 |
line = proc.stderr.readline()
|
| 134 |
if not line and proc.poll() is not None:
|
| 135 |
break
|
| 136 |
+
|
| 137 |
if "frame=" in line:
|
| 138 |
try:
|
| 139 |
current = int(line.strip().split("=")[-1])
|
|
|
|
| 144 |
yield None, f"Encoding… {current}/{total_frames} frames", render_progress(pct, f"Encoding {pct:.0f}%")
|
| 145 |
else:
|
| 146 |
yield None, "Encoding…", render_progress(50.0, "Encoding…")
|
| 147 |
+
|
| 148 |
+
# ✅ collect suspicious lines (errors, missing files, etc.)
|
| 149 |
+
if "Error" in line or "No such file" in line:
|
| 150 |
+
errors.append(line.strip())
|
| 151 |
+
|
| 152 |
ret = proc.wait()
|
| 153 |
out_file = Path(frames_dir) / ("output.mp4" if fmt in ("h264", "h265") else "output.webm")
|
| 154 |
+
|
| 155 |
if ret != 0 or not out_file.exists():
|
| 156 |
+
err = "\n".join(errors) or "Unknown ffmpeg error."
|
|
|
|
|
|
|
|
|
|
|
|
|
| 157 |
yield None, f"Encoding failed.\n\n{err}", render_progress(0.0, "Failed")
|
| 158 |
return
|
| 159 |
|
| 160 |
+
|
| 161 |
yield str(out_file), f"Video created: {out_file.name}", render_progress(100.0, "Encoding complete")
|
| 162 |
|
| 163 |
def build_ui():
|