APIvideo / app.py
AlexandreScriptsMT's picture
Update app.py
4da758a verified
raw
history blame
3.94 kB
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
# --- MUDANÇA CRÍTICA AQUI (Padrão MoviePy 2.0+) ---
# Não existe mais 'moviepy.editor'. Importamos dos módulos específicos.
from moviepy.video.io.ImageClip import ImageClip
from moviepy.audio.io.AudioFileClip import AudioFileClip
from moviepy.video.compositing.CompositeVideoClip import CompositeVideoClip
from moviepy.video.VideoClip import TextClip
# Nota: Em algumas versões v2, o TextClip pode estar em moviepy.video.tools.subtitles
# ou exigir configuração explicita de fonte.
# Inicializa a API
app = FastAPI()
class VideoRequest(BaseModel):
image_url: str
audio_url: str
def download_file(url, filename):
response = requests.get(url, stream=True)
if response.status_code == 200:
with open(filename, 'wb') as f:
for chunk in response.iter_content(1024):
f.write(chunk)
else:
raise Exception(f"Erro ao baixar {url}")
def criar_video_logica(imagem_path, audio_path, output_path):
print("Carregando modelo Whisper...")
model = WhisperModel("tiny", device="cpu", compute_type="int8")
segments, _ = model.transcribe(audio_path, language="pt")
text_clips = []
# Configurações de fonte (Ajustadas para v2)
# Na v2, as vezes é necessário apontar o caminho da fonte se o ImageMagick não achar
font_conf = {
"font_size": 30, # Mudou de fontsize para font_size em algumas builds v2
"color": 'white',
"font": 'Arial', # Garanta que essa fonte exista no Linux ou use "DejaVu-Sans"
"stroke_color": 'black',
"stroke_width": 2,
"method": 'caption',
"size": (800, None)
}
print("Gerando legendas...")
for segment in segments:
# TextClip na v2 pode ter assinatura diferente dependendo da sub-versão (alpha/beta/stable)
# Este é o padrão mais seguro:
txt_clip = TextClip(text=segment.text.strip(), **font_conf)
txt_clip = txt_clip.with_start(segment.start).with_duration(segment.end - segment.start)
# 'set_position' mudou para 'with_position' em muitas partes da v2 para encadear métodos
txt_clip = txt_clip.with_position(('center', 'bottom'))
text_clips.append(txt_clip)
print("Processando Áudio e Imagem...")
audio_clip = AudioFileClip(audio_path)
# set_duration -> with_duration / set_audio -> with_audio
image_clip = ImageClip(imagem_path).with_duration(audio_clip.duration).with_audio(audio_clip)
final = CompositeVideoClip([image_clip] + text_clips)
print("Renderizando vídeo...")
# write_videofile continua similar, mas 'preset' e 'threads' são geridos pelo ffmpeg
final.write_videofile(output_path, fps=10, codec="libx264", audio_codec="aac", preset="ultrafast", threads=2)
return output_path
@app.post("/gerar-video")
async def gerar_video_endpoint(request: VideoRequest):
try:
temp_img = "temp_image.jpg" # Ajustado para jpg pois o Gemini manda jpg
temp_audio = "temp_audio.mp3"
output_video = "video_final.mp4"
print(f"Baixando: {request.image_url}")
download_file(request.image_url, temp_img)
download_file(request.audio_url, temp_audio)
# Se for uma lista de imagens (lógica nova), precisa ajustar aqui.
# Mantendo simples para teste:
criar_video_logica(temp_img, temp_audio, output_video)
return FileResponse(output_video, media_type="video/mp4", filename="video_editado.mp4")
except Exception as e:
print(f"Erro: {e}")
# Retorna o erro na resposta para você ver no Apps Script
raise HTTPException(status_code=500, detail=str(e))
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=7860)