File size: 6,839 Bytes
43fcbe8
c9d2e08
43fcbe8
 
c9d2e08
 
 
43fcbe8
c9d2e08
 
 
43fcbe8
c9d2e08
57db4ae
43fcbe8
c9d2e08
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57db4ae
c9d2e08
 
 
 
 
57db4ae
c9d2e08
 
 
 
 
 
43fcbe8
c9d2e08
 
 
 
 
 
 
 
43fcbe8
c9d2e08
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43fcbe8
 
c9d2e08
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43fcbe8
c9d2e08
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43fcbe8
57db4ae
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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
import os
import random
import requests
import gradio as gr
from moviepy.editor import VideoFileClip, concatenate_videoclips, AudioFileClip, TextClip, CompositeVideoClip
from moviepy.audio.fx.all import audio_loop
import edge_tts
import asyncio
from datetime import datetime
from pathlib import Path
from transformers import pipeline

# Pexels API key from Hugging Face Space environment variable
PEXELS_API_KEY = os.getenv("PEXELS_API_KEY")

# Ensure asyncio works with Gradio
def run_async(coro):
    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)
    result = loop.run_until_complete(coro)
    loop.close()
    return result

# Load local text generation model (adjust based on Hugging Face Spaces resources)
try:
    generator = pipeline("text-generation", model="mistralai/Mistral-7B-Instruct-v0.2", device="cpu")
except Exception as e:
    print(f"Error loading model: {e}")
    generator = None

# Fetch videos from Pexels
def fetch_pexels_videos(query, num_videos=5):
    headers = {"Authorization": PEXELS_API_KEY}
    url = f"https://api.pexels.com/videos/search?query={query}&per_page={num_videos}"
    response = requests.get(url, headers=headers)
    if response.status_code == 200:
        return [video["video_files"][0]["link"] for video in response.json()["videos"]]
    return []

# Generate script using local model or custom text
def generate_script(prompt, custom_text=None):
    if custom_text:
        return custom_text.strip()
    if not prompt:
        return "Error: Debes proporcionar un prompt o un guion personalizado."
    
    # Generate script with local model
    if generator:
        input_text = f"Genera un guion para un video sobre '{prompt}'. Crea una lista numerada con descripciones breves (máximo 20 palabras por ítem) para un top 10 relacionado con el tema."
        try:
            result = generator(input_text, max_length=300, num_return_sequences=1, do_sample=True)[0]['generated_text']
            return result.strip()
        except Exception as e:
            print(f"Error generating script: {e}")
    
    # Fallback mock response if model fails
    if "recetas" in prompt.lower():
        return """
        1. Tacos al pastor: Jugosa carne marinada con piña.
        2. Lasagna: Capas de pasta, carne y queso fundido.
        3. Sushi: Arroz y pescado fresco en rollos delicados.
        4. Pizza casera: Masa crujiente con tus ingredientes favoritos.
        5. Paella: Arroz con mariscos y azafrán.
        6. Ceviche: Pescado fresco marinado en limón.
        7. Ramen: Caldo rico con fideos y cerdo.
        8. Tiramisú: Postre cremoso con café y mascarpone.
        9. Enchiladas: Tortillas rellenas con salsa picante.
        10. Curry: Especias intensas con carne o vegetales.
        """
    return f"Top 10 sobre {prompt}: No se pudo generar un guion específico."

# Generate voice using Edge TTS
async def generate_voice(text, output_file="output.mp3"):
    communicate = edge_tts.Communicate(text, voice="es-MX-DaliaNeural")
    await communicate.save(output_file)
    return output_file

# Generate subtitles
def generate_subtitles(text, duration, fps=24):
    words = text.split()
    avg_duration = duration / len(words)
    subtitles = []
    for i, word in enumerate(words):
        start_time = i * avg_duration
        end_time = (i + 1) * avg_duration
        subtitle = TextClip(word, fontsize=30, color='white', bg_color='black', size=(1280, 100))
        subtitle = subtitle.set_position(('center', 'bottom')).set_duration(avg_duration).set_fps(fps)
        subtitles.append(subtitle)
    return subtitles

# Download and trim video
def download_and_trim_video(url, duration, output_path):
    response = requests.get(url, stream=True)
    with open("temp_video.mp4", "wb") as f:
        for chunk in response.iter_content(chunk_size=1024):
            f.write(chunk)
    clip = VideoFileClip("temp_video.mp4").subclip(0, min(duration, VideoFileClip("temp_video.mp4").duration))
    clip.write_videofile(output_path, codec="libx264", audio_codec="aac")
    clip.close()
    os.remove("temp_video.mp4")
    return output_path

# Main video creation function
def create_video(prompt, custom_text, music_file):
    output_dir = "output_videos"
    os.makedirs(output_dir, exist_ok=True)
    timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
    output_video = f"{output_dir}/video_{timestamp}.mp4"

    # Generate or use provided script
    script = generate_script(prompt, custom_text)
    if "Error" in script:
        return script

    # Generate voice
    voice_file = "temp_audio.mp3"
    run_async(generate_voice(script, voice_file))
    audio = AudioFileClip(voice_file)
    video_duration = audio.duration

    # Fetch Pexels videos
    query = prompt.split()[0] if prompt else "generic"
    video_urls = fetch_pexels_videos(query, num_videos=5)
    if not video_urls:
        return "Error: No se encontraron videos en Pexels."

    # Download and trim videos
    clips = []
    for i, url in enumerate(video_urls):
        clip_path = f"temp_clip_{i}.mp4"
        download_and_trim_video(url, video_duration / len(video_urls), clip_path)
        clips.append(VideoFileClip(clip_path))

    # Concatenate video clips
    final_clip = concatenate_videoclips(clips, method="compose").set_duration(video_duration)

    # Add looped music
    if music_file:
        music = AudioFileClip(music_file.name)
        music = audio_loop(music, duration=video_duration)
        final_audio = final_clip.set_audio(music.set_duration(video_duration))
    else:
        final_audio = final_clip.set_audio(audio)

    # Add subtitles
    subtitles = generate_subtitles(script, video_duration)
    final_clip = CompositeVideoClip([final_clip] + subtitles)

    # Write final video
    final_clip.write_videofile(output_video, codec="libx264", audio_codec="aac", fps=24)
    
    # Clean up
    for clip in clips:
        clip.close()
    audio.close()
    if music_file:
        music.close()
    final_clip.close()
    os.remove(voice_file)
    for i in range(len(video_urls)):
        os.remove(f"temp_clip_{i}.mp4")

    return output_video

# Gradio interface
with gr.Blocks() as demo:
    gr.Markdown("# Generador de Videos")
    gr.Markdown("Ingresa un prompt (ej., 'Top 10 recetas más ricas') o un guion personalizado. Sube música opcional (MP3).")
    prompt = gr.Textbox(label="Prompt", placeholder="Ejemplo: Top 10 recetas más ricas")
    custom_text = gr.Textbox(label="Guion Personalizado", placeholder="Ingresa tu propio guion aquí (opcional)")
    music_file = gr.File(label="Subir Música (MP3, opcional)", file_types=[".mp3"])
    submit = gr.Button("Generar Video")
    output = gr.Video(label="Video Generado")
    submit.click(fn=create_video, inputs=[prompt, custom_text, music_file], outputs=output)

demo.launch()