File size: 5,220 Bytes
43fcbe8
 
 
 
 
 
 
 
 
a483f49
43fcbe8
 
 
a483f49
 
 
 
43fcbe8
 
 
a483f49
43fcbe8
 
 
 
 
 
 
a483f49
43fcbe8
 
 
 
a483f49
 
 
 
43fcbe8
 
 
 
 
 
a483f49
 
43fcbe8
a483f49
43fcbe8
 
a483f49
43fcbe8
 
 
 
 
 
 
 
a483f49
 
43fcbe8
a483f49
 
43fcbe8
a483f49
 
 
 
 
 
 
43fcbe8
 
 
a483f49
 
 
 
 
43fcbe8
a483f49
 
 
43fcbe8
 
 
 
 
 
 
a483f49
43fcbe8
 
a483f49
43fcbe8
 
 
a483f49
43fcbe8
 
a483f49
 
 
 
 
 
43fcbe8
 
a483f49
 
43fcbe8
a483f49
 
43fcbe8
a483f49
 
 
 
 
 
 
 
43fcbe8
a483f49
 
 
 
 
 
 
 
 
 
 
43fcbe8
 
 
 
 
 
 
a483f49
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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
import os
import requests
import edge_tts
import gradio as gr
from moviepy.editor import *
from moviepy.video.fx.all import resize, scroll
from PIL import Image
import io
import asyncio
import json

# 1. Descargar imágenes/videos de stock (Pexels)
def get_stock_media(query, is_video=False):
    API_KEY = os.getenv("PEXELS_API_KEY")  # Configurar en HF Secrets
    if not API_KEY:
        raise ValueError("¡Falta la API Key de Pexels! Añádela en los Secrets de Hugging Face.")
    
    url = f"https://api.pexels.com/v1/{'videos' if is_video else 'photos'}/search?query={query}&per_page=1"
    headers = {"Authorization": API_KEY}
    response = requests.get(url, headers=headers).json()
    
    if is_video:
        video_url = response["videos"][0]["video_files"][0]["link"]
        return requests.get(video_url).content
    else:
        image_url = response["photos"][0]["src"]["large"]
        return Image.open(io.BytesIO(requests.get(image_url).content))

# 2. Generar voz con Edge TTS
async def generate_voice(text, voice="es-ES-AlvaroNeural", output_path="voice.mp3"):
    communicate = edge_tts.Communicate(text=text, voice=voice)
    await communicate.save(output_path)

# 3. Añadir música de fondo (20% volumen, loop automático)
def add_background_music(audio_clip, music_path=None, volume=0.2):
    if not music_path:
        return audio_clip
    music = AudioFileClip(music_path).volumex(volume)
    if music.duration < audio_clip.duration:
        music = music.loop(duration=audio_clip.duration)
    return CompositeAudioClip([audio_clip, music.set_start(0)])

# 4. Efectos de movimiento/zoom para imágenes
def apply_effects(clip, zoom_factor=1.05, effect_duration=2):
    return clip.resize(zoom_factor).set_position('center').fx(scroll, h=50, w=50)

# 5. Subtítulos dinámicos (aparecen progresivamente)
def generate_subtitles(text, duration, fontsize=30, color="white", stroke_color="black"):
    words = text.split()
    word_duration = duration / max(len(words), 1)  # Evitar división por cero
    clips = []
    for i, word in enumerate(words):
        txt_clip = TextClip(
            " ".join(words[:i+1]),
            fontsize=fontsize,
            color=color,
            stroke_color=stroke_color,
            font="Arial-Bold",
            size=(None, None),
            method="caption"
        ).set_start(i * word_duration).set_duration(word_duration)
        clips.append(txt_clip.set_position(("center", "bottom")))
    return concatenate_videoclips(clips)

# 6. Función principal
async def generate_video(script_json, voice_model, music_file=None):
    try:
        script = json.loads(script_json)
    except json.JSONDecodeError:
        raise gr.Error("¡Formato de script inválido! Usa JSON como en el ejemplo.")
    
    clips = []
    for i, scene in enumerate(script):
        img = get_stock_media(scene["prompt"])
        img_path = f"scene_{i}.jpg"
        img.save(img_path)
        
        voice_path = f"voice_{i}.mp3"
        await generate_voice(scene["text"], voice_model, voice_path)
        
        audio = AudioFileClip(voice_path)
        clip = ImageClip(img_path).set_duration(audio.duration)
        clip = apply_effects(clip)
        
        subtitles = generate_subtitles(scene["text"], audio.duration)
        final_clip = CompositeVideoClip([clip, subtitles]).set_audio(audio)
        clips.append(final_clip)
    
    final_video = concatenate_videoclips(clips)
    if music_file:
        final_video.audio = add_background_music(final_video.audio, music_file)
    
    output_path = "final_video.mp4"
    final_video.write_videofile(output_path, fps=24, codec="libx264", threads=4)
    return output_path

# 7. Interfaz Gradio
def ui(script_json, voice_model, music_file=None):
    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)
    try:
        output_video = loop.run_until_complete(generate_video(script_json, voice_model, music_file))
    except Exception as e:
        raise gr.Error(f"Error: {str(e)}")
    finally:
        loop.close()
    return output_video

# Voces Edge TTS (puedes añadir más)
voices = list(edge_tts.list_voices())

with gr.Blocks(title="Generador de Videos con IA") as demo:
    gr.Markdown("## 🎥 Generador de Videos con IA (Gratis)")
    with gr.Row():
        script_input = gr.Textbox(
            label="Script (JSON)",
            placeholder='[{"prompt": "paisaje", "text": "Texto aquí..."}]',
            lines=5
        )
    with gr.Row():
        voice_dropdown = gr.Dropdown(choices=voices, label="Voz", value="es-ES-AlvaroNeural")
        music_upload = gr.File(label="Música de fondo (opcional)", type="filepath")
    generate_btn = gr.Button("Generar Video")
    output_video = gr.Video(label="Resultado", format="mp4")
    
    gr.Examples(
        examples=[[
            '[{"prompt": "ciudad futurista", "text": "Bienvenidos al futuro."}]',
            "es-ES-AlvaroNeural",
            None
        ]],
        inputs=[script_input, voice_dropdown, music_upload],
        outputs=output_video
    )
    
    generate_btn.click(
        fn=ui,
        inputs=[script_input, voice_dropdown, music_upload],
        outputs=output_video,
    )

demo.launch(debug=True)