Spaces:
Paused
Paused
Upload 2 files
Browse files- Dockerfile +4 -1
- app.py +10 -119
Dockerfile
CHANGED
|
@@ -71,9 +71,12 @@ RUN ffmpeg -version | head -1; \
|
|
| 71 |
ffmpeg -encoders 2>/dev/null | grep -qE "\\s${c}\\s" && echo " β
$c" || echo " β οΈ $c NΓO encontrado"; \
|
| 72 |
done; \
|
| 73 |
echo "--- decoders ---"; \
|
| 74 |
-
for c in h264 hevc vp9 aac mp3 mpeg2video; do \
|
| 75 |
ffmpeg -decoders 2>/dev/null | grep -qE "\\s${c}\\s" && echo " β
$c" || echo " β οΈ $c NΓO encontrado"; \
|
| 76 |
done; \
|
|
|
|
|
|
|
|
|
|
| 77 |
echo "--- filtros VOB ---"; \
|
| 78 |
for f in bwdif scale fps mpdecimate loudnorm overlay; do \
|
| 79 |
ffmpeg -filters 2>/dev/null | grep -qE "\\s${f}\\s" && echo " β
$f" || echo " β οΈ $f NΓO encontrado"; \
|
|
|
|
| 71 |
ffmpeg -encoders 2>/dev/null | grep -qE "\\s${c}\\s" && echo " β
$c" || echo " β οΈ $c NΓO encontrado"; \
|
| 72 |
done; \
|
| 73 |
echo "--- decoders ---"; \
|
| 74 |
+
for c in h264 hevc vp9 aac mp3 mpeg2video mpeg2_cuvid h264_cuvid hevc_cuvid; do \
|
| 75 |
ffmpeg -decoders 2>/dev/null | grep -qE "\\s${c}\\s" && echo " β
$c" || echo " β οΈ $c NΓO encontrado"; \
|
| 76 |
done; \
|
| 77 |
+
echo "--- hwaccels ---"; \
|
| 78 |
+
ffmpeg -hwaccels 2>/dev/null | grep -qE "cuda" && echo " β
cuda hwaccel" || echo " β οΈ cuda hwaccel NΓO encontrado"; \
|
| 79 |
+
ffmpeg -hwaccels 2>/dev/null | grep -qE "mpeg2_nvdec" && echo " β
mpeg2_nvdec" || echo " β οΈ mpeg2_nvdec NΓO encontrado"; \
|
| 80 |
echo "--- filtros VOB ---"; \
|
| 81 |
for f in bwdif scale fps mpdecimate loudnorm overlay; do \
|
| 82 |
ffmpeg -filters 2>/dev/null | grep -qE "\\s${f}\\s" && echo " β
$f" || echo " β οΈ $f NΓO encontrado"; \
|
app.py
CHANGED
|
@@ -12,19 +12,8 @@ from PIL import Image, ImageDraw
|
|
| 12 |
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 13 |
|
| 14 |
def gpu_disponivel():
|
| 15 |
-
"""Verifica se
|
| 16 |
-
# 1) nvidia-smi precisa existir e rodar
|
| 17 |
r = subprocess.run(["nvidia-smi"], capture_output=True, text=True)
|
| 18 |
-
if r.returncode != 0:
|
| 19 |
-
return False
|
| 20 |
-
# 2) Testa se h264_nvenc consegue abrir sessΓ£o de encoding
|
| 21 |
-
# Faz um encode de 1 frame de cor sΓ³lida β se falhar, NVENC nΓ£o funciona.
|
| 22 |
-
r = subprocess.run([
|
| 23 |
-
"ffmpeg", "-hide_banner", "-loglevel", "error", "-y",
|
| 24 |
-
"-f", "lavfi", "-i", "color=c=black:s=64x64:d=0.04",
|
| 25 |
-
"-c:v", "h264_nvenc", "-frames:v", "1",
|
| 26 |
-
"-f", "null", "-"
|
| 27 |
-
], capture_output=True, text=True)
|
| 28 |
return r.returncode == 0
|
| 29 |
|
| 30 |
|
|
@@ -275,8 +264,15 @@ def reencode_video(
|
|
| 275 |
gpu_fallback = quer_gpu and not use_gpu
|
| 276 |
tem_logo = logo_file is not None
|
| 277 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 278 |
full_gpu_pipeline = (
|
| 279 |
use_gpu and
|
|
|
|
| 280 |
not remover_duplicados and
|
| 281 |
resolucao == "Original" and
|
| 282 |
not tem_logo
|
|
@@ -466,113 +462,8 @@ def reencode_video(
|
|
| 466 |
|
| 467 |
if process.returncode != 0:
|
| 468 |
err = process.stderr.read()
|
| 469 |
-
|
| 470 |
-
|
| 471 |
-
if use_gpu and ("nvenc" in err.lower() or "cuda" in err.lower()
|
| 472 |
-
or "OpenEncodeSessionEx" in err
|
| 473 |
-
or "no capable devices" in err.lower()):
|
| 474 |
-
yield None, (
|
| 475 |
-
"β οΈ NVENC falhou β repetindo com CPU...\n"
|
| 476 |
-
f"(motivo: {err[-200:]})"
|
| 477 |
-
)
|
| 478 |
-
use_gpu = False
|
| 479 |
-
gpu_fallback = True
|
| 480 |
-
full_gpu_pipeline = False
|
| 481 |
-
|
| 482 |
-
# ReconstrΓ³i o comando sem GPU
|
| 483 |
-
cmd_fallback = ["ffmpeg", "-hide_banner", "-loglevel", "error",
|
| 484 |
-
"-progress", "pipe:1", "-y"]
|
| 485 |
-
if precisa_probe_extra:
|
| 486 |
-
cmd_fallback += ["-analyzeduration", "100M", "-probesize", "100M",
|
| 487 |
-
"-fflags", "+genpts+discardcorrupt"]
|
| 488 |
-
cmd_fallback += ["-i", input_path]
|
| 489 |
-
if tem_logo:
|
| 490 |
-
cmd_fallback += ["-i", logo_tmp]
|
| 491 |
-
|
| 492 |
-
# Filtros (sempre CPU agora)
|
| 493 |
-
vf_fb = list(vf_parts) # jΓ‘ inclui bwdif para VOB, fps, etc.
|
| 494 |
-
if tem_logo:
|
| 495 |
-
overlay_pos = calcular_posicao_logo(
|
| 496 |
-
logo_posicao, logo_margem, logo_offset_x, logo_offset_y)
|
| 497 |
-
if vf_fb:
|
| 498 |
-
fc = (
|
| 499 |
-
f"[0:v]{','.join(vf_fb)}[base];"
|
| 500 |
-
f"[1:v]scale={logo_width_px}:-1[logo];"
|
| 501 |
-
f"[base][logo]overlay={overlay_pos}:format=auto[vout]"
|
| 502 |
-
)
|
| 503 |
-
else:
|
| 504 |
-
fc = (
|
| 505 |
-
f"[1:v]scale={logo_width_px}:-1[logo];"
|
| 506 |
-
f"[0:v][logo]overlay={overlay_pos}:format=auto[vout]"
|
| 507 |
-
)
|
| 508 |
-
cmd_fallback += ["-filter_complex", fc, "-map", "[vout]"]
|
| 509 |
-
if has_audio:
|
| 510 |
-
cmd_fallback += ["-map", "0:a"]
|
| 511 |
-
else:
|
| 512 |
-
if vf_fb:
|
| 513 |
-
cmd_fallback += ["-vf", ",".join(vf_fb)]
|
| 514 |
-
|
| 515 |
-
if remover_duplicados and not tem_logo:
|
| 516 |
-
cmd_fallback += ["-fps_mode", "vfr"]
|
| 517 |
-
|
| 518 |
-
# Codec de vΓdeo CPU
|
| 519 |
-
if "x264" in modo:
|
| 520 |
-
cmd_fallback += ["-c:v", "libx264", "-profile:v", "high",
|
| 521 |
-
"-preset", "slow", "-crf", str(crf_valor),
|
| 522 |
-
"-pix_fmt", "yuv420p"]
|
| 523 |
-
else:
|
| 524 |
-
cmd_fallback += ["-c:v", "libx265", "-preset", "slow",
|
| 525 |
-
"-crf", str(crf_valor), "-pix_fmt", "yuv420p",
|
| 526 |
-
"-x265-params",
|
| 527 |
-
"sao=0:rd=6:psy-rd=1.0:psy-rdoq=2.0:rskip=1"]
|
| 528 |
-
|
| 529 |
-
# Γudio (reaproveita o que jΓ‘ foi calculado)
|
| 530 |
-
if has_audio:
|
| 531 |
-
if normalizar_audio and af:
|
| 532 |
-
cmd_fallback += ["-af", af]
|
| 533 |
-
cmd_fallback += montar_args_audio(
|
| 534 |
-
audio_codec_label, audio_bitrate, audio_sample_rate, int(audio_canais))
|
| 535 |
-
else:
|
| 536 |
-
cmd_fallback += ["-an"]
|
| 537 |
-
|
| 538 |
-
cmd_fallback += ["-movflags", "+faststart"]
|
| 539 |
-
if has_audio:
|
| 540 |
-
cmd_fallback += [
|
| 541 |
-
"-metadata:s:a:0", "language=por",
|
| 542 |
-
"-metadata:s:a:0", "comment=Re-encoded by Super Re-Encoder",
|
| 543 |
-
]
|
| 544 |
-
cmd_fallback.append(output_path)
|
| 545 |
-
|
| 546 |
-
yield None, "β³ Codificando com CPU (fallback)..."
|
| 547 |
-
process2 = subprocess.Popen(
|
| 548 |
-
cmd_fallback, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
|
| 549 |
-
current_sec = 0.0
|
| 550 |
-
while True:
|
| 551 |
-
line = process2.stdout.readline()
|
| 552 |
-
if not line and process2.poll() is not None:
|
| 553 |
-
break
|
| 554 |
-
if line:
|
| 555 |
-
line = line.strip()
|
| 556 |
-
if line.startswith("out_time_ms="):
|
| 557 |
-
val = line.split('=')[1]
|
| 558 |
-
if val != 'N/A':
|
| 559 |
-
current_sec = int(val) / 1000000
|
| 560 |
-
if total_duration > 0:
|
| 561 |
-
pct = min(100, int(current_sec / total_duration * 100))
|
| 562 |
-
yield None, (
|
| 563 |
-
f"β³ CPU fallback... {pct}% "
|
| 564 |
-
f"({current_sec:.1f}s de {total_duration:.1f}s)"
|
| 565 |
-
)
|
| 566 |
-
else:
|
| 567 |
-
yield None, f"β³ CPU fallback... {current_sec:.1f}s processados"
|
| 568 |
-
|
| 569 |
-
if process2.returncode != 0:
|
| 570 |
-
err2 = process2.stderr.read()
|
| 571 |
-
yield None, f"β Erro no FFmpeg (CPU fallback):\n{err2[-1500:]}"
|
| 572 |
-
return
|
| 573 |
-
else:
|
| 574 |
-
yield None, f"β Erro no FFmpeg:\n{err[-1500:]}"
|
| 575 |
-
return
|
| 576 |
|
| 577 |
# ββ RelatΓ³rio final βββββββββββββββββββββββββββββββββββββββββ
|
| 578 |
orig_mb = os.path.getsize(input_path) / (1024 * 1024)
|
|
|
|
| 12 |
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 13 |
|
| 14 |
def gpu_disponivel():
|
| 15 |
+
"""Verifica se a GPU NVIDIA estΓ‘ acessΓvel via nvidia-smi."""
|
|
|
|
| 16 |
r = subprocess.run(["nvidia-smi"], capture_output=True, text=True)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 17 |
return r.returncode == 0
|
| 18 |
|
| 19 |
|
|
|
|
| 264 |
gpu_fallback = quer_gpu and not use_gpu
|
| 265 |
tem_logo = logo_file is not None
|
| 266 |
|
| 267 |
+
# VOB / MPG sΓ£o entrelaΓ§ados e exigem bwdif (filtro CPU).
|
| 268 |
+
# full_gpu_pipeline (= decode+encode na VRAM) Γ© incompatΓvel com filtros
|
| 269 |
+
# CPU porque exigiria transferΓͺncia GPUβCPUβGPU. Para esses formatos,
|
| 270 |
+
# o melhor Γ© decode CPU + bwdif CPU + encode NVENC na GPU.
|
| 271 |
+
formato_sem_full_gpu = ext in {".vob", ".mpg", ".mpeg"}
|
| 272 |
+
|
| 273 |
full_gpu_pipeline = (
|
| 274 |
use_gpu and
|
| 275 |
+
not formato_sem_full_gpu and
|
| 276 |
not remover_duplicados and
|
| 277 |
resolucao == "Original" and
|
| 278 |
not tem_logo
|
|
|
|
| 462 |
|
| 463 |
if process.returncode != 0:
|
| 464 |
err = process.stderr.read()
|
| 465 |
+
yield None, f"β Erro no FFmpeg:\n{err[-1500:]}"
|
| 466 |
+
return
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 467 |
|
| 468 |
# ββ RelatΓ³rio final βββββββββββββββββββββββββββββββββββββββββ
|
| 469 |
orig_mb = os.path.getsize(input_path) / (1024 * 1024)
|