File size: 4,380 Bytes
9583a19
 
 
 
 
 
 
4da758a
aca6f46
 
 
9583a19
 
 
aca6f46
9583a19
 
aca6f46
 
 
 
9583a19
 
aca6f46
 
 
9583a19
aca6f46
9583a19
aca6f46
 
 
9583a19
aca6f46
 
 
 
4da758a
aca6f46
 
 
 
 
 
 
 
 
 
 
 
 
 
4da758a
aca6f46
 
4da758a
aca6f46
 
9583a19
aca6f46
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9583a19
 
 
 
 
 
aca6f46
9583a19
aca6f46
9583a19
 
aca6f46
 
9583a19
 
aca6f46
 
 
 
 
 
 
 
 
9583a19
aca6f46
 
9583a19
 
aca6f46
9583a19
 
 
 
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
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)