gnosticdev commited on
Commit
a483f49
·
verified ·
1 Parent(s): 77470ba

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +67 -38
app.py CHANGED
@@ -3,19 +3,22 @@ import requests
3
  import edge_tts
4
  import gradio as gr
5
  from moviepy.editor import *
6
- from moviepy.editor import AudioFileClip, CompositeVideoClip, concatenate_videoclips, ImageClip, TextClip
7
  from moviepy.video.fx.all import resize, scroll
8
  from PIL import Image
9
- import numpy as np
10
  import io
11
  import asyncio
 
12
 
13
  # 1. Descargar imágenes/videos de stock (Pexels)
14
  def get_stock_media(query, is_video=False):
15
- API_KEY = os.getenv("PEXELS_API_KEY") # Configura esto en HF Secrets
 
 
 
16
  url = f"https://api.pexels.com/v1/{'videos' if is_video else 'photos'}/search?query={query}&per_page=1"
17
  headers = {"Authorization": API_KEY}
18
  response = requests.get(url, headers=headers).json()
 
19
  if is_video:
20
  video_url = response["videos"][0]["video_files"][0]["link"]
21
  return requests.get(video_url).content
@@ -23,26 +26,28 @@ def get_stock_media(query, is_video=False):
23
  image_url = response["photos"][0]["src"]["large"]
24
  return Image.open(io.BytesIO(requests.get(image_url).content))
25
 
26
- # 2. Generar voz con Edge TTS (todos los modelos)
27
  async def generate_voice(text, voice="es-ES-AlvaroNeural", output_path="voice.mp3"):
28
  communicate = edge_tts.Communicate(text=text, voice=voice)
29
  await communicate.save(output_path)
30
 
31
- # 3. Añadir música de fondo (en loop si es necesario)
32
- def add_background_music(audio_clip, music_path="background.mp3", volume=0.2):
 
 
33
  music = AudioFileClip(music_path).volumex(volume)
34
  if music.duration < audio_clip.duration:
35
  music = music.loop(duration=audio_clip.duration)
36
  return CompositeAudioClip([audio_clip, music.set_start(0)])
37
 
38
  # 4. Efectos de movimiento/zoom para imágenes
39
- def apply_effects(clip, zoom_factor=1.1, effect_duration=2):
40
- return clip.resize(zoom_factor).set_position('center').fx(vfx.scroll, h=100, w=100)
41
 
42
- # 5. Crear subtítulos dinámicos (karaoke-style)
43
  def generate_subtitles(text, duration, fontsize=30, color="white", stroke_color="black"):
44
  words = text.split()
45
- word_duration = duration / len(words)
46
  clips = []
47
  for i, word in enumerate(words):
48
  txt_clip = TextClip(
@@ -51,58 +56,82 @@ def generate_subtitles(text, duration, fontsize=30, color="white", stroke_color=
51
  color=color,
52
  stroke_color=stroke_color,
53
  font="Arial-Bold",
 
 
54
  ).set_start(i * word_duration).set_duration(word_duration)
55
- clips.append(txt_clip)
56
- return concatenate_videoclips(clips).set_position(("center", "bottom"))
57
 
58
- # 6. Función principal para generar el video
59
- async def generate_video(script, voice_model, music_file=None):
 
 
 
 
 
60
  clips = []
61
  for i, scene in enumerate(script):
62
- # Descargar imagen y generar voz
63
  img = get_stock_media(scene["prompt"])
64
- img.save(f"scene_{i}.jpg")
65
- await generate_voice(scene["text"], voice_model, f"voice_{i}.mp3")
 
 
 
66
 
67
- # Crear clip con efectos
68
- audio = AudioFileClip(f"voice_{i}.mp3")
69
- clip = ImageClip(f"scene_{i}.jpg").set_duration(audio.duration)
70
- clip = apply_effects(clip) # Efecto de zoom/movimiento
71
 
72
- # Subtítulos dinámicos
73
  subtitles = generate_subtitles(scene["text"], audio.duration)
74
  final_clip = CompositeVideoClip([clip, subtitles]).set_audio(audio)
75
  clips.append(final_clip)
76
 
77
- # Unir clips y añadir música
78
  final_video = concatenate_videoclips(clips)
79
  if music_file:
80
- final_video.audio = add_background_music(final_video.audio, music_file.name)
81
 
82
- # Exportar
83
  output_path = "final_video.mp4"
84
- final_video.write_videofile(output_path, fps=24, codec="libx264")
85
  return output_path
86
 
87
  # 7. Interfaz Gradio
88
- def ui(script, voice_model, music_file):
89
  loop = asyncio.new_event_loop()
90
  asyncio.set_event_loop(loop)
91
- output_video = loop.run_until_complete(generate_video(script, voice_model, music_file))
 
 
 
 
 
92
  return output_video
93
 
94
- # Lista de voces Edge TTS (ejemplo)
95
- voices = ["es-ES-AlvaroNeural", "es-MX-DaliaNeural", "en-US-JennyNeural", "fr-FR-HenriNeural"]
96
 
97
- # Diseño de la interfaz
98
- with gr.Blocks() as demo:
99
- gr.Markdown("## 🎬 Generador de Videos con IA (100% Gratis)")
100
  with gr.Row():
101
- script_input = gr.Textbox(label="Script (JSON)", placeholder='[{"prompt": "futuristic city", "text": "Texto aquí..."}]')
102
- voice_dropdown = gr.Dropdown(choices=voices, label="Voz Edge TTS", value="es-ES-AlvaroNeural")
103
- music_upload = gr.File(label="Música de fondo (opcional)", type="file")
 
 
 
 
 
104
  generate_btn = gr.Button("Generar Video")
105
- output_video = gr.Video(label="Video Generado")
 
 
 
 
 
 
 
 
 
 
106
 
107
  generate_btn.click(
108
  fn=ui,
@@ -110,4 +139,4 @@ with gr.Blocks() as demo:
110
  outputs=output_video,
111
  )
112
 
113
- demo.launch()
 
3
  import edge_tts
4
  import gradio as gr
5
  from moviepy.editor import *
 
6
  from moviepy.video.fx.all import resize, scroll
7
  from PIL import Image
 
8
  import io
9
  import asyncio
10
+ import json
11
 
12
  # 1. Descargar imágenes/videos de stock (Pexels)
13
  def get_stock_media(query, is_video=False):
14
+ API_KEY = os.getenv("PEXELS_API_KEY") # Configurar en HF Secrets
15
+ if not API_KEY:
16
+ raise ValueError("¡Falta la API Key de Pexels! Añádela en los Secrets de Hugging Face.")
17
+
18
  url = f"https://api.pexels.com/v1/{'videos' if is_video else 'photos'}/search?query={query}&per_page=1"
19
  headers = {"Authorization": API_KEY}
20
  response = requests.get(url, headers=headers).json()
21
+
22
  if is_video:
23
  video_url = response["videos"][0]["video_files"][0]["link"]
24
  return requests.get(video_url).content
 
26
  image_url = response["photos"][0]["src"]["large"]
27
  return Image.open(io.BytesIO(requests.get(image_url).content))
28
 
29
+ # 2. Generar voz con Edge TTS
30
  async def generate_voice(text, voice="es-ES-AlvaroNeural", output_path="voice.mp3"):
31
  communicate = edge_tts.Communicate(text=text, voice=voice)
32
  await communicate.save(output_path)
33
 
34
+ # 3. Añadir música de fondo (20% volumen, loop automático)
35
+ def add_background_music(audio_clip, music_path=None, volume=0.2):
36
+ if not music_path:
37
+ return audio_clip
38
  music = AudioFileClip(music_path).volumex(volume)
39
  if music.duration < audio_clip.duration:
40
  music = music.loop(duration=audio_clip.duration)
41
  return CompositeAudioClip([audio_clip, music.set_start(0)])
42
 
43
  # 4. Efectos de movimiento/zoom para imágenes
44
+ def apply_effects(clip, zoom_factor=1.05, effect_duration=2):
45
+ return clip.resize(zoom_factor).set_position('center').fx(scroll, h=50, w=50)
46
 
47
+ # 5. Subtítulos dinámicos (aparecen progresivamente)
48
  def generate_subtitles(text, duration, fontsize=30, color="white", stroke_color="black"):
49
  words = text.split()
50
+ word_duration = duration / max(len(words), 1) # Evitar división por cero
51
  clips = []
52
  for i, word in enumerate(words):
53
  txt_clip = TextClip(
 
56
  color=color,
57
  stroke_color=stroke_color,
58
  font="Arial-Bold",
59
+ size=(None, None),
60
+ method="caption"
61
  ).set_start(i * word_duration).set_duration(word_duration)
62
+ clips.append(txt_clip.set_position(("center", "bottom")))
63
+ return concatenate_videoclips(clips)
64
 
65
+ # 6. Función principal
66
+ async def generate_video(script_json, voice_model, music_file=None):
67
+ try:
68
+ script = json.loads(script_json)
69
+ except json.JSONDecodeError:
70
+ raise gr.Error("¡Formato de script inválido! Usa JSON como en el ejemplo.")
71
+
72
  clips = []
73
  for i, scene in enumerate(script):
 
74
  img = get_stock_media(scene["prompt"])
75
+ img_path = f"scene_{i}.jpg"
76
+ img.save(img_path)
77
+
78
+ voice_path = f"voice_{i}.mp3"
79
+ await generate_voice(scene["text"], voice_model, voice_path)
80
 
81
+ audio = AudioFileClip(voice_path)
82
+ clip = ImageClip(img_path).set_duration(audio.duration)
83
+ clip = apply_effects(clip)
 
84
 
 
85
  subtitles = generate_subtitles(scene["text"], audio.duration)
86
  final_clip = CompositeVideoClip([clip, subtitles]).set_audio(audio)
87
  clips.append(final_clip)
88
 
 
89
  final_video = concatenate_videoclips(clips)
90
  if music_file:
91
+ final_video.audio = add_background_music(final_video.audio, music_file)
92
 
 
93
  output_path = "final_video.mp4"
94
+ final_video.write_videofile(output_path, fps=24, codec="libx264", threads=4)
95
  return output_path
96
 
97
  # 7. Interfaz Gradio
98
+ def ui(script_json, voice_model, music_file=None):
99
  loop = asyncio.new_event_loop()
100
  asyncio.set_event_loop(loop)
101
+ try:
102
+ output_video = loop.run_until_complete(generate_video(script_json, voice_model, music_file))
103
+ except Exception as e:
104
+ raise gr.Error(f"Error: {str(e)}")
105
+ finally:
106
+ loop.close()
107
  return output_video
108
 
109
+ # Voces Edge TTS (puedes añadir más)
110
+ voices = list(edge_tts.list_voices())
111
 
112
+ with gr.Blocks(title="Generador de Videos con IA") as demo:
113
+ gr.Markdown("## 🎥 Generador de Videos con IA (Gratis)")
 
114
  with gr.Row():
115
+ script_input = gr.Textbox(
116
+ label="Script (JSON)",
117
+ placeholder='[{"prompt": "paisaje", "text": "Texto aquí..."}]',
118
+ lines=5
119
+ )
120
+ with gr.Row():
121
+ voice_dropdown = gr.Dropdown(choices=voices, label="Voz", value="es-ES-AlvaroNeural")
122
+ music_upload = gr.File(label="Música de fondo (opcional)", type="filepath")
123
  generate_btn = gr.Button("Generar Video")
124
+ output_video = gr.Video(label="Resultado", format="mp4")
125
+
126
+ gr.Examples(
127
+ examples=[[
128
+ '[{"prompt": "ciudad futurista", "text": "Bienvenidos al futuro."}]',
129
+ "es-ES-AlvaroNeural",
130
+ None
131
+ ]],
132
+ inputs=[script_input, voice_dropdown, music_upload],
133
+ outputs=output_video
134
+ )
135
 
136
  generate_btn.click(
137
  fn=ui,
 
139
  outputs=output_video,
140
  )
141
 
142
+ demo.launch(debug=True)