Spaces:
Paused
Paused
Upload app.py
Browse files
app.py
CHANGED
|
@@ -37,22 +37,15 @@ def contar_frames(path):
|
|
| 37 |
# Logo β preparaΓ§Γ£o, posiΓ§Γ£o e preview ao vivo
|
| 38 |
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 39 |
|
| 40 |
-
def preparar_logo(logo_path,
|
| 41 |
"""
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
"""
|
| 47 |
img = Image.open(logo_path).convert("RGBA")
|
| 48 |
|
| 49 |
-
# Redimensiona proporcionalmente Γ largura alvo
|
| 50 |
-
alvo_w = max(1, int(vid_w * tamanho_pct / 100))
|
| 51 |
-
ratio = alvo_w / img.width
|
| 52 |
-
alvo_h = max(1, int(img.height * ratio))
|
| 53 |
-
img = img.resize((alvo_w, alvo_h), Image.LANCZOS)
|
| 54 |
-
|
| 55 |
-
# Escala o canal alpha pela opacidade escolhida
|
| 56 |
if opacidade_pct < 100:
|
| 57 |
r, g, b, a = img.split()
|
| 58 |
fator = opacidade_pct / 100.0
|
|
@@ -61,7 +54,7 @@ def preparar_logo(logo_path, vid_w, tamanho_pct, opacidade_pct):
|
|
| 61 |
|
| 62 |
temp_path = "/tmp/logo_overlay.png"
|
| 63 |
img.save(temp_path, "PNG")
|
| 64 |
-
return temp_path
|
| 65 |
|
| 66 |
|
| 67 |
def calcular_posicao_logo(posicao, margem=20, offset_x=0, offset_y=0):
|
|
@@ -251,11 +244,11 @@ def reencode_video(
|
|
| 251 |
|
| 252 |
# ββ Prepara a logo (Pillow) βββββββββββββββββββββββββββββββββ
|
| 253 |
logo_tmp = None
|
|
|
|
| 254 |
if tem_logo:
|
| 255 |
try:
|
| 256 |
-
logo_tmp
|
| 257 |
-
|
| 258 |
-
)
|
| 259 |
except Exception as e:
|
| 260 |
yield None, f"β Erro ao processar logo: {e}"
|
| 261 |
return
|
|
@@ -288,29 +281,25 @@ def reencode_video(
|
|
| 288 |
if tem_logo:
|
| 289 |
cmd += ["-i", logo_tmp]
|
| 290 |
|
| 291 |
-
# ββ filter_complex (
|
| 292 |
if tem_logo:
|
| 293 |
overlay_pos = calcular_posicao_logo(
|
| 294 |
logo_posicao, logo_margem, logo_offset_x, logo_offset_y
|
| 295 |
)
|
| 296 |
-
#
|
| 297 |
-
#
|
| 298 |
-
#
|
| 299 |
-
#
|
| 300 |
-
# yuv420p para o encoder. Sem premultiply β straight alpha puro.
|
| 301 |
if vf_parts:
|
| 302 |
fc = (
|
| 303 |
-
f"[0:v]{','.join(vf_parts)}
|
| 304 |
-
f"[1:v]
|
| 305 |
-
f"[base][logo]overlay={overlay_pos}[
|
| 306 |
-
f"[composed]format=yuv420p[vout]"
|
| 307 |
)
|
| 308 |
else:
|
| 309 |
fc = (
|
| 310 |
-
f"[
|
| 311 |
-
f"[
|
| 312 |
-
f"[base][logo]overlay={overlay_pos}[composed];"
|
| 313 |
-
f"[composed]format=yuv420p[vout]"
|
| 314 |
)
|
| 315 |
cmd += ["-filter_complex", fc, "-map", "[vout]"]
|
| 316 |
if has_audio:
|
|
|
|
| 37 |
# Logo β preparaΓ§Γ£o, posiΓ§Γ£o e preview ao vivo
|
| 38 |
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 39 |
|
| 40 |
+
def preparar_logo(logo_path, opacidade_pct):
|
| 41 |
"""
|
| 42 |
+
SOMENTE aplica a opacidade (escala o canal alpha) via Pillow.
|
| 43 |
+
O redimensionamento Γ© feito pelo prΓ³prio FFmpeg com scale filter β
|
| 44 |
+
exatamente como o LegendadorBrasileiroWhisperX faz. Assim evitamos
|
| 45 |
+
re-saves desnecessΓ‘rios da PNG e preservamos as bordas originais.
|
| 46 |
"""
|
| 47 |
img = Image.open(logo_path).convert("RGBA")
|
| 48 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 49 |
if opacidade_pct < 100:
|
| 50 |
r, g, b, a = img.split()
|
| 51 |
fator = opacidade_pct / 100.0
|
|
|
|
| 54 |
|
| 55 |
temp_path = "/tmp/logo_overlay.png"
|
| 56 |
img.save(temp_path, "PNG")
|
| 57 |
+
return temp_path
|
| 58 |
|
| 59 |
|
| 60 |
def calcular_posicao_logo(posicao, margem=20, offset_x=0, offset_y=0):
|
|
|
|
| 244 |
|
| 245 |
# ββ Prepara a logo (Pillow) βββββββββββββββββββββββββββββββββ
|
| 246 |
logo_tmp = None
|
| 247 |
+
logo_width_px = 0
|
| 248 |
if tem_logo:
|
| 249 |
try:
|
| 250 |
+
logo_tmp = preparar_logo(logo_file, logo_opacidade)
|
| 251 |
+
logo_width_px = max(1, int(vid_w * logo_tamanho / 100))
|
|
|
|
| 252 |
except Exception as e:
|
| 253 |
yield None, f"β Erro ao processar logo: {e}"
|
| 254 |
return
|
|
|
|
| 281 |
if tem_logo:
|
| 282 |
cmd += ["-i", logo_tmp]
|
| 283 |
|
| 284 |
+
# ββ filter_complex (IDΓNTICO ao LegendadorBrasileiroWhisperX) βββ
|
| 285 |
if tem_logo:
|
| 286 |
overlay_pos = calcular_posicao_logo(
|
| 287 |
logo_posicao, logo_margem, logo_offset_x, logo_offset_y
|
| 288 |
)
|
| 289 |
+
# Regra de ouro: NΓO forΓ§ar nenhum format= na cadeia. O overlay
|
| 290 |
+
# do FFmpeg jΓ‘ lida com alpha PNG corretamente. ConversΓ΅es
|
| 291 |
+
# explΓcitas yuv420pβrgba causam mismatch de matriz de cor
|
| 292 |
+
# (BT.601/BT.709) e introduzem halo verde nas bordas.
|
|
|
|
| 293 |
if vf_parts:
|
| 294 |
fc = (
|
| 295 |
+
f"[0:v]{','.join(vf_parts)}[base];"
|
| 296 |
+
f"[1:v]scale={logo_width_px}:-1[logo];"
|
| 297 |
+
f"[base][logo]overlay={overlay_pos}[vout]"
|
|
|
|
| 298 |
)
|
| 299 |
else:
|
| 300 |
fc = (
|
| 301 |
+
f"[1:v]scale={logo_width_px}:-1[logo];"
|
| 302 |
+
f"[0:v][logo]overlay={overlay_pos}[vout]"
|
|
|
|
|
|
|
| 303 |
)
|
| 304 |
cmd += ["-filter_complex", fc, "-map", "[vout]"]
|
| 305 |
if has_audio:
|