Spaces:
Runtime error
Runtime error
| 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() | |