Spaces:
Runtime error
Runtime error
File size: 4,649 Bytes
137cb2f ee827e5 137cb2f ee827e5 e69008b 137cb2f f64427e ee827e5 f64427e 137cb2f ee827e5 f64427e 137cb2f ee827e5 137cb2f f64427e ee827e5 137cb2f ee827e5 f64427e ee827e5 f64427e ee827e5 137cb2f ee827e5 52618fe f64427e ee827e5 f64427e ee827e5 f64427e ee827e5 f64427e ee827e5 f64427e ee827e5 f64427e ee827e5 f64427e ee827e5 f64427e ee827e5 d652a79 f64427e d652a79 f64427e ee827e5 f9c3950 f64427e ee827e5 f64427e ee827e5 f64427e 137cb2f 52618fe 137cb2f 5ac40a1 f64427e | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 | import gradio as gr
import json
import base64
import tempfile
import os
import wave
from moviepy import *
from moviepy.video.fx import FadeIn, FadeOut
# Configurações de áudio padrão
SAMPLE_RATE = 24000
NUM_CHANNELS = 1
SAMPWIDTH = 2
def pcm_to_wav(pcm_base64, output_path):
"""Converte PCM Base64 para WAV."""
try:
pcm_bytes = base64.b64decode(pcm_base64)
with wave.open(output_path, 'wb') as wav_file:
wav_file.setnchannels(NUM_CHANNELS)
wav_file.setsampwidth(SAMPWIDTH)
wav_file.setframerate(SAMPLE_RATE)
wav_file.writeframes(pcm_bytes)
return True
except Exception as e:
print(f"Erro Audio: {e}")
return False
def base64_to_image(image_base64, output_path):
"""Salva imagem Base64 em arquivo."""
try:
with open(output_path, "wb") as f:
f.write(base64.b64decode(image_base64))
return True
except Exception as e:
print(f"Erro Imagem: {e}")
return False
def generate_video(project_json):
print("--- INICIANDO RENDERIZAÇÃO (V2 - BLOCKS) ---")
# Criar pasta temporária para processamento
temp_dir_obj = tempfile.TemporaryDirectory()
temp_dir = temp_dir_obj.name
try:
# Tratamento do input JSON
if isinstance(project_json, str):
try:
data = json.loads(project_json)
except:
raise gr.Error("Erro: O texto fornecido não é um JSON válido.")
else:
data = project_json
scenes_data = data.get("scenes", [])
if not scenes_data:
raise gr.Error("O JSON não contém a lista de cenas ('scenes').")
clips = []
scenes_data.sort(key=lambda x: x.get("id", 0))
for i, scene in enumerate(scenes_data):
print(f"Processando cena {i}...")
img_path = os.path.join(temp_dir, f"scene_{i}.jpg")
audio_path = os.path.join(temp_dir, f"scene_{i}.wav")
img_b64 = scene.get("image_data_base64")
audio_b64 = scene.get("audio_data_base64")
if not img_b64 or not audio_b64:
print(f"Pulando cena {i}: Falta audio ou imagem.")
continue
if not base64_to_image(img_b64, img_path): continue
if not pcm_to_wav(audio_b64, audio_path): continue
try:
# --- SINTAXE MOVIEPY 2.0 ---
audio_clip = AudioFileClip(audio_path)
# Definir duração da imagem = audio + 0.1s
duration = audio_clip.duration + 0.1
video_clip = ImageClip(img_path).with_duration(duration)
# Juntar Audio
video_clip = video_clip.with_audio(audio_clip)
# Aplicar Fade In
video_clip = video_clip.with_effects([FadeIn(duration=0.5)])
clips.append(video_clip)
except Exception as e_clip:
print(f"Erro na montagem do clipe {i}: {e_clip}")
if not clips:
raise gr.Error("Nenhum clipe foi gerado. Verifique os dados Base64.")
print(f"Concatenando {len(clips)} cenas...")
final_video = concatenate_videoclips(clips, method="compose")
output_path = os.path.join(temp_dir, "output_final.mp4")
final_video.write_videofile(
output_path,
fps=24,
codec="libx264",
audio_codec="aac",
preset="ultrafast",
logger=None
)
print("Vídeo gerado com sucesso!")
return output_path
except Exception as e:
error_msg = f"Erro fatal no backend: {str(e)}"
print(error_msg)
raise gr.Error(error_msg)
# --- AQUI ESTA A MUDANÇA ---
# Usamos Blocks para ter controle total sobre o nome da API
with gr.Blocks(title="Renderizador AI V2") as demo:
gr.Markdown("# Renderizador AI Backend")
with gr.Row():
input_component = gr.JSON(label="JSON do Projeto")
output_component = gr.Video(label="Vídeo Final")
btn = gr.Button("Renderizar", variant="primary")
# O parametro api_name="predict" ABAIXO é o segredo.
# Ele obriga o Gradio a expor a rota /predict que seu App procura.
btn.click(
fn=generate_video,
inputs=input_component,
outputs=output_component,
api_name="predict"
)
if __name__ == "__main__":
demo.launch()
|