gnosticdev commited on
Commit
865ab31
·
verified ·
1 Parent(s): 0b2cd2e

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +55 -52
app.py CHANGED
@@ -3,8 +3,7 @@ from tts_module import get_voices, text_to_speech
3
  from pexels_api import search_pexels
4
  from moviepy.editor import (
5
  AudioFileClip, VideoFileClip, CompositeAudioClip,
6
- concatenate_audioclips, concatenate_videoclips,
7
- vfx, CompositeVideoClip
8
  )
9
  import asyncio
10
  import os
@@ -14,6 +13,7 @@ from googleapiclient.discovery import build
14
  from googleapiclient.http import MediaFileUpload
15
  import tempfile
16
  import re
 
17
 
18
  # Define la carpeta de salida temporal
19
  output_folder = "outputs"
@@ -22,85 +22,85 @@ os.makedirs(output_folder, exist_ok=True)
22
  def clean_text_for_search(text):
23
  """Limpia el texto para hacer búsquedas válidas en Pexels"""
24
  # Eliminar caracteres especiales y limitar longitud
25
- text = re.sub(r'[^\w\s]', ' ', text)
26
- words = text.split()
27
- # Tomar solo las primeras 3-4 palabras significativas
28
- return ' '.join(words[:4])
29
 
30
  def resize_and_blur_video(clip, target_aspect_ratio=16/9):
31
  """
32
- Redimensiona y aplica desenfoque al fondo del video para mantener el aspecto 16:9
33
- mientras preserva la calidad del contenido original.
34
  """
35
  try:
36
- # Obtener dimensiones originales
37
  w, h = clip.size
38
  current_aspect_ratio = w / h
39
-
40
  print(f"Procesando video: {w}x{h}, ratio: {current_aspect_ratio}")
41
  if abs(current_aspect_ratio - target_aspect_ratio) < 0.1:
42
  return clip
43
-
44
- # Calcular nuevas dimensiones
45
  if current_aspect_ratio < target_aspect_ratio: # Video vertical
46
  target_w = int(h * target_aspect_ratio)
47
  target_h = h
48
-
49
- # Crear versión desenfocada para el fondo
50
  background = clip.resize(width=target_w)
51
  try:
52
- # Intentar con blur más intenso
53
  background = background.fx(vfx.blur, sigma=50)
54
  except Exception as e:
55
  print(f"Error al aplicar blur: {e}")
56
- # Si falla el blur, usar solo resize
57
- background = background.resize(width=target_w)
58
-
59
- # Escalar video original
60
  foreground = clip.resize(height=target_h)
61
  x_center = (target_w - foreground.w) / 2
62
-
63
- # Combinar capas
64
  return CompositeVideoClip(
65
  [background, foreground.set_position((x_center, 0))],
66
  size=(target_w, target_h)
67
  )
68
  else: # Video horizontal
69
  return clip.resize(width=int(h * target_aspect_ratio), height=h)
70
-
71
  except Exception as e:
72
  print(f"Error en resize_and_blur_video: {e}")
73
- # En caso de error, devolver el clip original
74
  return clip
75
 
76
- def concatenate_pexels_videos(text, num_videos=5):
77
- sentences = [sentence.strip() for sentence in text.split(".") if sentence.strip()]
 
 
 
 
 
 
 
78
  video_clips = []
79
-
80
- for sentence in sentences[:num_videos]: # Limitar número de videos
81
  try:
82
- # Limpiar y preparar el texto para la búsqueda
83
- search_text = clean_text_for_search(sentence)
84
- print(f"Buscando videos para: {search_text}")
85
-
86
- links = search_pexels(search_text, num_results=1)
87
  if not links:
 
 
 
 
 
 
 
88
  continue
89
-
90
- video_response = requests.get(links[0])
91
  with tempfile.NamedTemporaryFile(delete=False, suffix=".mp4") as tmp_video:
92
  tmp_video.write(video_response.content)
93
  clip = VideoFileClip(tmp_video.name)
94
  processed_clip = resize_and_blur_video(clip)
95
  video_clips.append(processed_clip)
96
-
97
  except Exception as e:
98
- print(f"Error procesando sentencia '{sentence}': {e}")
99
  continue
100
-
101
  if not video_clips:
102
- raise Exception("No se pudieron obtener videos válidos")
103
-
 
 
 
104
  return concatenate_videoclips(video_clips, method="compose")
105
 
106
  def adjust_background_music(video_duration, music_file):
@@ -126,7 +126,7 @@ def combine_audio_video(audio_file, video_clip, music_clip=None):
126
  video_clip = video_clip.loop(duration=total_duration)
127
  video_clip = video_clip.set_duration(total_duration).fadeout(5)
128
  final_clip = video_clip.set_audio(audio_clip)
129
-
130
  if music_clip:
131
  if music_clip.duration < total_duration:
132
  repetitions = int(total_duration / music_clip.duration) + 1
@@ -136,16 +136,17 @@ def combine_audio_video(audio_file, video_clip, music_clip=None):
136
  music_clip = music_clip.subclip(0, total_duration)
137
  music_clip = music_clip.audio_fadeout(5)
138
  final_clip = final_clip.set_audio(CompositeAudioClip([audio_clip, music_clip]))
139
-
140
  output_filename = f"final_video_{int(time.time())}.mp4"
141
  output_path = os.path.join(output_folder, output_filename)
142
  final_clip.write_videofile(output_path, codec="libx264", audio_codec="aac", fps=24)
143
  return output_path
 
144
  except Exception as e:
145
  print(f"Error combinando audio y video: {e}")
146
  return None
147
 
148
- def process_input(text, txt_file, mp3_file, selected_voice, rate, pitch):
149
  try:
150
  if text.strip():
151
  final_text = text
@@ -153,29 +154,30 @@ def process_input(text, txt_file, mp3_file, selected_voice, rate, pitch):
153
  final_text = txt_file.decode("utf-8")
154
  else:
155
  return "No input provided"
156
-
157
  voices = asyncio.run(get_voices())
158
  if selected_voice not in voices:
159
  return f"La voz '{selected_voice}' no es válida. Por favor, seleccione una de las siguientes voces: {', '.join(voices.keys())}"
160
-
161
  try:
162
  audio_file = asyncio.run(text_to_speech(final_text, selected_voice, rate, pitch))
163
  except Exception as e:
164
  return f"Error generando audio: {e}"
165
-
166
  try:
167
- video_clip = concatenate_pexels_videos(final_text, num_videos=5)
168
  except Exception as e:
169
  return f"Error concatenando videos: {e}"
170
-
171
  if mp3_file is not None:
172
  music_clip = adjust_background_music(video_clip.duration, mp3_file.name)
173
  else:
174
  music_clip = None
175
-
176
  final_video_path = combine_audio_video(audio_file, video_clip, music_clip)
177
  upload_to_google_drive(final_video_path)
178
  return final_video_path
 
179
  except Exception as e:
180
  return f"Error durante el procesamiento: {e}"
181
 
@@ -185,7 +187,7 @@ def upload_to_google_drive(file_path):
185
  if not api_key:
186
  print("Error: GOOGLE_API_KEY no está definida en las variables de entorno.")
187
  return None
188
-
189
  service = build("drive", "v3", developerKey=api_key)
190
  file_metadata = {"name": os.path.basename(file_path)}
191
  media = MediaFileUpload(file_path, resumable=True)
@@ -204,21 +206,22 @@ with gr.Blocks() as demo:
204
  text_input = gr.Textbox(label="Write your text here", lines=5)
205
  txt_file_input = gr.File(label="Or upload a .txt file", file_types=[".txt"])
206
  mp3_file_input = gr.File(label="Upload background music (.mp3)", file_types=[".mp3"])
 
207
  voices = asyncio.run(get_voices())
208
  voice_dropdown = gr.Dropdown(choices=list(voices.keys()), label="Select Voice")
209
  rate_slider = gr.Slider(minimum=-50, maximum=50, value=0, label="Speech Rate Adjustment (%)", step=1)
210
  pitch_slider = gr.Slider(minimum=-20, maximum=20, value=0, label="Pitch Adjustment (Hz)", step=1)
211
  with gr.Column():
212
  output_video = gr.File(label="Download Generated Video")
213
-
214
  btn = gr.Button("Generate Video")
215
  btn.click(
216
  process_input,
217
- inputs=[text_input, txt_file_input, mp3_file_input, voice_dropdown, rate_slider, pitch_slider],
218
  outputs=output_video
219
  )
220
 
221
- # Leer el puerto asignado por Hugging Face o usar 7860 como valor predeterminado
222
  port = int(os.getenv("PORT", 7860))
223
 
224
  # Lanzar la aplicación
 
3
  from pexels_api import search_pexels
4
  from moviepy.editor import (
5
  AudioFileClip, VideoFileClip, CompositeAudioClip,
6
+ concatenate_audioclips, concatenate_videoclips, vfx, CompositeVideoClip
 
7
  )
8
  import asyncio
9
  import os
 
13
  from googleapiclient.http import MediaFileUpload
14
  import tempfile
15
  import re
16
+ import random
17
 
18
  # Define la carpeta de salida temporal
19
  output_folder = "outputs"
 
22
  def clean_text_for_search(text):
23
  """Limpia el texto para hacer búsquedas válidas en Pexels"""
24
  # Eliminar caracteres especiales y limitar longitud
25
+ text = re.sub(r'[^\w\s]', '', text).strip()
26
+ return text
 
 
27
 
28
  def resize_and_blur_video(clip, target_aspect_ratio=16/9):
29
  """
30
+ Redimensiona y aplica desenfoque al fondo del video para mantener el aspecto 16:9.
 
31
  """
32
  try:
 
33
  w, h = clip.size
34
  current_aspect_ratio = w / h
35
+
36
  print(f"Procesando video: {w}x{h}, ratio: {current_aspect_ratio}")
37
  if abs(current_aspect_ratio - target_aspect_ratio) < 0.1:
38
  return clip
39
+
 
40
  if current_aspect_ratio < target_aspect_ratio: # Video vertical
41
  target_w = int(h * target_aspect_ratio)
42
  target_h = h
43
+
 
44
  background = clip.resize(width=target_w)
45
  try:
 
46
  background = background.fx(vfx.blur, sigma=50)
47
  except Exception as e:
48
  print(f"Error al aplicar blur: {e}")
49
+
 
 
 
50
  foreground = clip.resize(height=target_h)
51
  x_center = (target_w - foreground.w) / 2
 
 
52
  return CompositeVideoClip(
53
  [background, foreground.set_position((x_center, 0))],
54
  size=(target_w, target_h)
55
  )
56
  else: # Video horizontal
57
  return clip.resize(width=int(h * target_aspect_ratio), height=h)
58
+
59
  except Exception as e:
60
  print(f"Error en resize_and_blur_video: {e}")
 
61
  return clip
62
 
63
+ def concatenate_pexels_videos(keywords, num_videos_per_keyword=1):
64
+ """
65
+ Concatena videos de Pexels basados en palabras clave proporcionadas por el usuario.
66
+ :param keywords: Palabras clave separadas por comas (ejemplo: "universo, galaxia, bosque, gato").
67
+ """
68
+ keyword_list = [keyword.strip() for keyword in keywords.split(",") if keyword.strip()]
69
+ if not keyword_list:
70
+ raise Exception("No se proporcionaron palabras clave válidas.")
71
+
72
  video_clips = []
73
+
74
+ for keyword in keyword_list:
75
  try:
76
+ print(f"Buscando videos para la palabra clave '{keyword}'...")
77
+ links = search_pexels(keyword, num_results=num_videos_per_keyword)
 
 
 
78
  if not links:
79
+ print(f"No se encontraron videos para la palabra clave '{keyword}'.")
80
+ continue
81
+
82
+ link = links[0] # Usamos solo el primer video encontrado
83
+ video_response = requests.get(link)
84
+ if video_response.status_code != 200:
85
+ print(f"Error al descargar video desde {link}: Código de estado {video_response.status_code}")
86
  continue
87
+
 
88
  with tempfile.NamedTemporaryFile(delete=False, suffix=".mp4") as tmp_video:
89
  tmp_video.write(video_response.content)
90
  clip = VideoFileClip(tmp_video.name)
91
  processed_clip = resize_and_blur_video(clip)
92
  video_clips.append(processed_clip)
93
+
94
  except Exception as e:
95
+ print(f"Error procesando palabra clave '{keyword}': {e}")
96
  continue
97
+
98
  if not video_clips:
99
+ raise Exception("No se pudieron obtener videos válidos.")
100
+
101
+ # Aleatorizar el orden de los clips si es necesario
102
+ random.shuffle(video_clips)
103
+
104
  return concatenate_videoclips(video_clips, method="compose")
105
 
106
  def adjust_background_music(video_duration, music_file):
 
126
  video_clip = video_clip.loop(duration=total_duration)
127
  video_clip = video_clip.set_duration(total_duration).fadeout(5)
128
  final_clip = video_clip.set_audio(audio_clip)
129
+
130
  if music_clip:
131
  if music_clip.duration < total_duration:
132
  repetitions = int(total_duration / music_clip.duration) + 1
 
136
  music_clip = music_clip.subclip(0, total_duration)
137
  music_clip = music_clip.audio_fadeout(5)
138
  final_clip = final_clip.set_audio(CompositeAudioClip([audio_clip, music_clip]))
139
+
140
  output_filename = f"final_video_{int(time.time())}.mp4"
141
  output_path = os.path.join(output_folder, output_filename)
142
  final_clip.write_videofile(output_path, codec="libx264", audio_codec="aac", fps=24)
143
  return output_path
144
+
145
  except Exception as e:
146
  print(f"Error combinando audio y video: {e}")
147
  return None
148
 
149
+ def process_input(text, txt_file, mp3_file, selected_voice, rate, pitch, keywords):
150
  try:
151
  if text.strip():
152
  final_text = text
 
154
  final_text = txt_file.decode("utf-8")
155
  else:
156
  return "No input provided"
157
+
158
  voices = asyncio.run(get_voices())
159
  if selected_voice not in voices:
160
  return f"La voz '{selected_voice}' no es válida. Por favor, seleccione una de las siguientes voces: {', '.join(voices.keys())}"
161
+
162
  try:
163
  audio_file = asyncio.run(text_to_speech(final_text, selected_voice, rate, pitch))
164
  except Exception as e:
165
  return f"Error generando audio: {e}"
166
+
167
  try:
168
+ video_clip = concatenate_pexels_videos(keywords, num_videos_per_keyword=1)
169
  except Exception as e:
170
  return f"Error concatenando videos: {e}"
171
+
172
  if mp3_file is not None:
173
  music_clip = adjust_background_music(video_clip.duration, mp3_file.name)
174
  else:
175
  music_clip = None
176
+
177
  final_video_path = combine_audio_video(audio_file, video_clip, music_clip)
178
  upload_to_google_drive(final_video_path)
179
  return final_video_path
180
+
181
  except Exception as e:
182
  return f"Error durante el procesamiento: {e}"
183
 
 
187
  if not api_key:
188
  print("Error: GOOGLE_API_KEY no está definida en las variables de entorno.")
189
  return None
190
+
191
  service = build("drive", "v3", developerKey=api_key)
192
  file_metadata = {"name": os.path.basename(file_path)}
193
  media = MediaFileUpload(file_path, resumable=True)
 
206
  text_input = gr.Textbox(label="Write your text here", lines=5)
207
  txt_file_input = gr.File(label="Or upload a .txt file", file_types=[".txt"])
208
  mp3_file_input = gr.File(label="Upload background music (.mp3)", file_types=[".mp3"])
209
+ keyword_input = gr.Textbox(label="Enter keywords separated by commas (e.g., universe, galaxy, forest, cat)")
210
  voices = asyncio.run(get_voices())
211
  voice_dropdown = gr.Dropdown(choices=list(voices.keys()), label="Select Voice")
212
  rate_slider = gr.Slider(minimum=-50, maximum=50, value=0, label="Speech Rate Adjustment (%)", step=1)
213
  pitch_slider = gr.Slider(minimum=-20, maximum=20, value=0, label="Pitch Adjustment (Hz)", step=1)
214
  with gr.Column():
215
  output_video = gr.File(label="Download Generated Video")
216
+
217
  btn = gr.Button("Generate Video")
218
  btn.click(
219
  process_input,
220
+ inputs=[text_input, txt_file_input, mp3_file_input, voice_dropdown, rate_slider, pitch_slider, keyword_input],
221
  outputs=output_video
222
  )
223
 
224
+ # Leer el puerto asignado por Hugging Face
225
  port = int(os.getenv("PORT", 7860))
226
 
227
  # Lanzar la aplicación