Spaces:
Sleeping
Sleeping
| import os | |
| import requests | |
| import uvicorn | |
| from fastapi import FastAPI, HTTPException | |
| from fastapi.responses import FileResponse | |
| from pydantic import BaseModel | |
| from faster_whisper import WhisperModel | |
| # --- A IMPORTAÇÃO CLÁSSICA E SEGURA --- | |
| # Na versão 1.0.3, este módulo contém tudo que precisamos | |
| from moviepy.editor import ImageClip, AudioFileClip, TextClip, CompositeVideoClip, concatenate_videoclips | |
| app = FastAPI() | |
| # Modelo de dados | |
| class VideoRequest(BaseModel): | |
| audio_url: str | |
| imagens: list[str] # Aceita lista de imagens agora | |
| duracao_estimada: int = 60 | |
| # --- Funções Auxiliares --- | |
| def download_file(url, filename): | |
| try: | |
| response = requests.get(url, stream=True) | |
| response.raise_for_status() # Garante que deu 200 OK | |
| with open(filename, 'wb') as f: | |
| for chunk in response.iter_content(chunk_size=8192): | |
| f.write(chunk) | |
| except Exception as e: | |
| print(f"Erro ao baixar {url}: {e}") | |
| raise e | |
| def criar_video_v1(lista_imagens_paths, audio_path, output_path): | |
| print("1. Carregando Áudio...") | |
| audio_clip = AudioFileClip(audio_path) | |
| duracao_total = audio_clip.duration | |
| # Cálculos de tempo | |
| qtd_imgs = len(lista_imagens_paths) | |
| if qtd_imgs == 0: raise Exception("Nenhuma imagem fornecida") | |
| tempo_por_imagem = duracao_total / qtd_imgs | |
| print(f"2. Montando Timeline ({qtd_imgs} imagens, {tempo_por_imagem:.2f}s cada)...") | |
| clips_visuais = [] | |
| for img_path in lista_imagens_paths: | |
| # Configuração Versão 1.0.3 (usando set_) | |
| clip = ImageClip(img_path).set_duration(tempo_por_imagem) | |
| # Opcional: Resize para garantir que caiba (se necessário) | |
| # clip = clip.resize(height=1080) | |
| clips_visuais.append(clip) | |
| # Junta as imagens em sequência | |
| video_sem_audio = concatenate_videoclips(clips_visuais, method="compose") | |
| # Junta com o áudio | |
| video_final = video_sem_audio.set_audio(audio_clip) | |
| # --- GERAÇÃO DE LEGENDAS (Opcional - Pode comentar se der erro de ImageMagick) --- | |
| # Para ativar legendas, descomente abaixo. Se der erro de "convert", deixe comentado por enquanto. | |
| # print("3. Gerando Legendas (Whisper)...") | |
| # model = WhisperModel("tiny", device="cpu", compute_type="int8") | |
| # segments, _ = model.transcribe(audio_path, language="pt") | |
| # legendas_clips = [] | |
| # for segment in segments: | |
| # txt = TextClip(segment.text, fontsize=24, color='white', font='DejaVu-Sans-Bold', stroke_color='black', stroke_width=1, size=(800, None), method='caption') | |
| # txt = txt.set_start(segment.start).set_duration(segment.end - segment.start).set_position(('center', 'bottom')) | |
| # legendas_clips.append(txt) | |
| # video_final = CompositeVideoClip([video_final] + legendas_clips) | |
| # --------------------------------------------------------------------------------- | |
| print("4. Renderizando Arquivo Final...") | |
| # fps=10 é leve e rápido para imagens estáticas | |
| video_final.write_videofile( | |
| output_path, | |
| fps=10, | |
| codec="libx264", | |
| audio_codec="aac", | |
| preset="ultrafast", | |
| threads=2 | |
| ) | |
| # Limpeza de memória (importante no Hugging Face) | |
| audio_clip.close() | |
| video_final.close() | |
| return output_path | |
| async def gerar_video_endpoint(request: VideoRequest): | |
| try: | |
| # Nomes temporários | |
| temp_audio = "temp_audio.mp3" | |
| lista_imgs_locais = [] | |
| output_video = "video_final.mp4" | |
| # 1. Baixar Audio | |
| print(f"Baixando áudio...") | |
| download_file(request.audio_url, temp_audio) | |
| # 2. Baixar Imagens (Loop) | |
| print(f"Baixando {len(request.imagens)} imagens...") | |
| for i, url in enumerate(request.imagens): | |
| nome_img = f"temp_img_{i}.jpg" | |
| download_file(url, nome_img) | |
| lista_imgs_locais.append(nome_img) | |
| # 3. Processar | |
| criar_video_v1(lista_imgs_locais, temp_audio, output_video) | |
| # 4. Retornar | |
| return FileResponse(output_video, media_type="video/mp4", filename="video_renderizado.mp4") | |
| except Exception as e: | |
| print(f"ERRO CRÍTICO: {str(e)}") | |
| raise HTTPException(status_code=500, detail=str(e)) | |
| if __name__ == "__main__": | |
| uvicorn.run(app, host="0.0.0.0", port=7860) |