File size: 3,818 Bytes
9b7097e
c537a4f
9b7097e
374c72e
9b7097e
374c72e
9b7097e
 
c537a4f
9b7097e
c537a4f
9b7097e
 
c537a4f
9b7097e
 
6d66777
9b7097e
 
 
 
6d66777
9b7097e
 
 
6d66777
9b7097e
c537a4f
9b7097e
 
 
 
6d66777
9b7097e
 
6d66777
9b7097e
 
 
 
 
 
 
 
 
6d66777
9b7097e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9a06bc2
9b7097e
 
 
 
 
 
6d66777
9b7097e
374c72e
c537a4f
9b7097e
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
import os
import requests
import asyncio
import tempfile
import logging
from datetime import datetime

import gradio as gr
from moviepy.editor import VideoFileClip, concatenate_videoclips, AudioFileClip, CompositeAudioClip
import edge_tts

from transformers import pipeline
import torch

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

# --- CONFIG ---
PEXELS_API_KEY = os.getenv("PEXELS_API_KEY")
# Modelo de texto en español
MODEL_NAME = "DeepESP/gpt2-spanish"

# Inicializar generador de texto
device = 0 if torch.cuda.is_available() else -1
generator = pipeline("text-generation", model=MODEL_NAME, device=device)

async def listar_voces():
    try:
        return await edge_tts.list_voices()
    except Exception as e:
        logger.error(f"Error obteniendo voces: {e}")
        return [{'ShortName': 'es-ES-ElviraNeural', 'Name': 'Elvira', 'Gender': 'Female'}]

VOCES = asyncio.run(listar_voces())
VOICE_NAMES = [f"{v['Name']} ({v['Gender']})" for v in VOCES]

def generar_guion(prompt):
    prompt_text = f"Escribe un guion profesional para un vídeo de YouTube sobre '{prompt}':\n"
    try:
        out = generator(prompt_text, max_new_tokens=300, temperature=0.7, truncate=True, num_return_sequences=1)
        texto = out[0]['generated_text']
        return texto
    except Exception as e:
        logger.error(f"Error generando guion: {e}")
        return f"Error generando guion: {e}"

def buscar_videos(prompt, max_videos=3):
    try:
        url = f"https://api.pexels.com/videos/search?query={prompt}&per_page={max_videos}"
        resp = requests.get(url, headers={"Authorization": PEXELS_API_KEY}, timeout=10)
        return resp.json().get("videos", [])
    except Exception as e:
        logger.error(f"Error en Pexels: {e}")
        return []

async def crear_video(prompt, voz_index, musica_path=None):
    texto = generar_guion(prompt)
    voz_short = VOCES[voz_index]['ShortName']
    audio_file = "tts.mp3"
    await edge_tts.Communicate(texto, voz_short).save(audio_file)
    tts_audio = AudioFileClip(audio_file)
    dur = tts_audio.duration

    # Música opcional
    if musica_path:
        music = AudioFileClip(musica_path).volumex(0.2).subclip(0, dur)
        audio_comp = CompositeAudioClip([music, tts_audio])
    else:
        audio_comp = tts_audio

    videos = buscar_videos(prompt)
    if not videos:
        return None

    clips = []
    for v in videos:
        url = v['video_files'][0]['link']
        r = requests.get(url, stream=True, timeout=15)
        tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".mp4")
        [tmp.write(c) for c in r.iter_content(1024*1024)]
        tmp.close()
        clip = VideoFileClip(tmp.name).subclip(0, min(dur/len(videos), v['duration']))
        clips.append(clip)

    final = concatenate_videoclips(clips).set_audio(audio_comp)
    out_name = f"video_{datetime.now().strftime('%Y%m%d_%H%M%S')}.mp4"
    final.write_videofile(out_name, codec="libx264", audio_codec="aac", fps=24)

    # Cleanup
    tts_audio.close()
    for c in clips:
        c.close()
        os.remove(c.filename)
    os.remove(audio_file)

    return out_name

def run_crear(prompt, voz_name, muz_file):
    try:
        idx = VOICE_NAMES.index(voz_name)
    except:
        idx = 0
    return asyncio.run(crear_video(prompt, idx, muz_file.name if muz_file else None))

with gr.Blocks() as app:
    tema = gr.Textbox(label="Tema para guion y video")
    voz = gr.Dropdown(choices=VOICE_NAMES, value=VOICE_NAMES[0], label="Voz TTS")
    muz = gr.File(label="Música fondo opcional (mp3/wav)", file_types=[".mp3", ".wav"])
    btn = gr.Button("Crear video")
    vid_out = gr.Video(label="Video generado")

    btn.click(fn=run_crear, inputs=[tema, voz, muz], outputs=vid_out)

if __name__ == "__main__":
    app.launch()