APIvideo / app.py
AlexandreScriptsMT's picture
Update app.py
aca6f46 verified
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
@app.post("/gerar-video")
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)