gnosticdev commited on
Commit
4d60f7e
verified
1 Parent(s): 86dad84

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +106 -106
app.py CHANGED
@@ -1,7 +1,11 @@
1
  import gradio as gr
2
  from tts_module import get_voices, text_to_speech
3
  from pexels_api import search_pexels
4
- from moviepy.editor import AudioFileClip, VideoFileClip, CompositeAudioClip, concatenate_audioclips, concatenate_videoclips
 
 
 
 
5
  import asyncio
6
  import os
7
  import time
@@ -9,17 +13,12 @@ import requests
9
  from googleapiclient.discovery import build
10
  from googleapiclient.http import MediaFileUpload
11
  import tempfile
12
- import PIL
13
-
14
- # Parche para compatibilidad con Pillow >= 9.0.0
15
- if not hasattr(PIL.Image, "ANTIALIAS"):
16
- PIL.Image.ANTIALIAS = PIL.Image.Resampling.LANCZOS
17
 
18
  # Define la carpeta de salida temporal
19
  output_folder = "outputs"
20
  os.makedirs(output_folder, exist_ok=True)
21
 
22
- # Sube el archivo al Google Drive usando una variable de entorno para la API Key
23
  def upload_to_google_drive(file_path):
24
  try:
25
  api_key = os.getenv("GOOGLE_API_KEY")
@@ -37,103 +36,106 @@ def upload_to_google_drive(file_path):
37
  print(f"Error al subir archivo a Google Drive: {e}")
38
  return None
39
 
40
- # Ajustar m煤sica de fondo
41
  def adjust_background_music(video_duration, music_file):
42
- try:
43
- music = AudioFileClip(music_file)
44
- if music.duration < video_duration:
45
- repetitions = int(video_duration / music.duration) + 1
46
- music_clips = [music] * repetitions
47
- music = concatenate_audioclips(music_clips)
48
- if music.duration > video_duration:
49
- music = music.subclip(0, video_duration)
50
- music = music.volumex(0.2)
51
- return music
52
- except Exception as e:
53
- print(f"Error al ajustar m煤sica de fondo: {e}")
54
- return None
55
-
56
- # Concatenar m煤ltiples videos de Pexels (manteniendo funcionalidad original)
57
- def concatenate_pexels_videos(text, num_videos=5, target_width=1920, target_height=1080):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58
  sentences = [sentence.strip() for sentence in text.split(".") if sentence.strip()]
59
- video_clips = []
60
-
61
  for sentence in sentences:
62
  try:
63
- # Buscar videos en Pexels (funcionalidad original)
64
  links = search_pexels(sentence, num_results=num_videos)
65
- if not links:
66
- print(f"No se encontraron videos para la frase '{sentence}'.")
67
- continue
68
-
69
- link = links[0] # Usamos solo el primer video encontrado
70
- print(f"Enlace encontrado para '{sentence}': {link}")
71
-
72
- # Descargar el video desde Pexels (funcionalidad original)
73
- video_response = requests.get(link)
74
- if video_response.status_code != 200:
75
- print(f"Error al descargar video desde {link}: C贸digo de estado {video_response.status_code}")
76
- continue
77
-
78
- # Crear un archivo temporal para guardar el video (funcionalidad original)
79
- with tempfile.NamedTemporaryFile(delete=False, suffix=".mp4") as tmp_video:
80
- tmp_video.write(video_response.content)
81
- tmp_path = tmp_video.name
82
-
83
- # Procesar el video descargado (mejoras aqu铆)
84
- clip = VideoFileClip(tmp_path)
85
-
86
- # Redimensionar manteniendo la proporci贸n original
87
- if clip.size[0] != target_width or clip.size[1] != target_height:
88
- clip_resized = clip.resize(width=target_width)
89
- canvas = ColorClip(size=(target_width, target_height), color=(0, 0, 0), duration=clip_resized.duration)
90
- clip_centered = CompositeVideoClip([clip_resized.set_position("center")], size=(target_width, target_height))
91
- video_clips.append(clip_centered)
92
- else:
93
- video_clips.append(clip)
94
-
95
- # Eliminar el archivo temporal despu茅s de procesarlo
96
- os.remove(tmp_path)
97
-
98
  except Exception as e:
99
- print(f"Error al procesar video para la frase '{sentence}': {e}")
100
  continue
101
-
102
- if not video_clips:
103
- raise Exception("No se pudieron procesar videos para el texto proporcionado.")
 
 
 
 
 
 
 
 
 
 
 
104
  final_clip = concatenate_videoclips(video_clips, method="compose")
105
  return final_clip
106
 
107
- # Combinar audio, video y m煤sica
108
  def combine_audio_video(audio_file, video_clip, music_clip=None):
109
- try:
110
- audio_clip = AudioFileClip(audio_file)
111
- total_duration = audio_clip.duration + 5
112
- if video_clip.duration < total_duration:
113
- video_clip = video_clip.loop(duration=total_duration)
114
- video_clip = video_clip.set_duration(total_duration).fadeout(5)
115
- final_clip = video_clip.set_audio(audio_clip)
116
-
117
- if music_clip:
118
- if music_clip.duration < total_duration:
119
- repetitions = int(total_duration / music_clip.duration) + 1
120
- music_clips = [music_clip] * repetitions
121
- music_clip = concatenate_audioclips(music_clips)
122
- if music_clip.duration > total_duration:
123
- music_clip = music_clip.subclip(0, total_duration)
124
- music_clip = music_clip.audio_fadeout(5)
125
- final_clip = final_clip.set_audio(CompositeAudioClip([audio_clip, music_clip]))
126
-
127
- output_filename = f"final_video_{int(time.time())}.mp4"
128
- output_path = os.path.join(output_folder, output_filename)
129
- final_clip.write_videofile(output_path, codec="libx264", audio_codec="aac", fps=24)
130
- return output_path
131
 
132
- except Exception as e:
133
- print(f"Error al combinar audio, video y m煤sica: {e}")
134
- return None
135
-
136
- # Funci贸n principal
137
  def process_input(text, txt_file, mp3_file, selected_voice, rate, pitch):
138
  try:
139
  if text.strip():
@@ -141,35 +143,33 @@ def process_input(text, txt_file, mp3_file, selected_voice, rate, pitch):
141
  elif txt_file is not None:
142
  final_text = txt_file.decode("utf-8")
143
  else:
144
- return None
145
 
146
  voices = asyncio.run(get_voices())
147
  if selected_voice not in voices:
148
- return None
149
 
150
- # Generar audio con TTS
151
- audio_file = asyncio.run(text_to_speech(final_text, selected_voice, rate, pitch))
 
 
152
 
153
- # Concatenar videos de Pexels
154
- video_clip = concatenate_pexels_videos(final_text, num_videos=5)
 
 
155
 
156
- # Ajustar m煤sica de fondo
157
  if mp3_file is not None:
158
  music_clip = adjust_background_music(video_clip.duration, mp3_file.name)
159
  else:
160
  music_clip = None
161
 
162
- # Combinar audio, video y m煤sica
163
  final_video_path = combine_audio_video(audio_file, video_clip, music_clip)
164
-
165
- # Subir el video final a Google Drive
166
  upload_to_google_drive(final_video_path)
167
-
168
  return final_video_path
169
 
170
  except Exception as e:
171
- print(f"Error durante el procesamiento: {e}")
172
- return None
173
 
174
  # Interfaz Gradio
175
  with gr.Blocks() as demo:
@@ -193,7 +193,7 @@ with gr.Blocks() as demo:
193
  outputs=output_video
194
  )
195
 
196
- # Leer el puerto asignado por Hugging Face
197
  port = int(os.getenv("PORT", 7860))
198
 
199
  # Lanzar la aplicaci贸n
 
1
  import gradio as gr
2
  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
11
  import time
 
13
  from googleapiclient.discovery import build
14
  from googleapiclient.http import MediaFileUpload
15
  import tempfile
16
+ import numpy as np
 
 
 
 
17
 
18
  # Define la carpeta de salida temporal
19
  output_folder = "outputs"
20
  os.makedirs(output_folder, exist_ok=True)
21
 
 
22
  def upload_to_google_drive(file_path):
23
  try:
24
  api_key = os.getenv("GOOGLE_API_KEY")
 
36
  print(f"Error al subir archivo a Google Drive: {e}")
37
  return None
38
 
 
39
  def adjust_background_music(video_duration, music_file):
40
+ music = AudioFileClip(music_file)
41
+ if music.duration < video_duration:
42
+ repetitions = int(video_duration / music.duration) + 1
43
+ music_clips = [music] * repetitions
44
+ music = concatenate_audioclips(music_clips)
45
+ if music.duration > video_duration:
46
+ music = music.subclip(0, video_duration)
47
+ music = music.volumex(0.2)
48
+ return music
49
+
50
+ def resize_and_blur_video(clip, target_aspect_ratio=16/9):
51
+ """
52
+ Redimensiona y aplica desenfoque al fondo del video para mantener el aspecto 16:9
53
+ mientras preserva la calidad del contenido original.
54
+ """
55
+ # Obtener dimensiones originales
56
+ w, h = clip.size
57
+ current_aspect_ratio = w / h
58
+
59
+ if abs(current_aspect_ratio - target_aspect_ratio) < 0.1:
60
+ return clip
61
+
62
+ # Calcular nuevas dimensiones manteniendo la altura
63
+ target_w = int(h * target_aspect_ratio)
64
+ target_h = h
65
+
66
+ if current_aspect_ratio < target_aspect_ratio: # Video vertical
67
+ # Crear versi贸n desenfocada y escalada para el fondo
68
+ background = (clip
69
+ .resize(width=target_w) # Escalar al ancho objetivo
70
+ .fx(vfx.blur, sigma=15) # Aplicar desenfoque gaussiano
71
+ )
72
+
73
+ # Escalar el video original manteniendo su aspecto
74
+ foreground = clip.resize(height=target_h)
75
+
76
+ # Centrar el video original sobre el fondo desenfocado
77
+ x_center = (target_w - foreground.w) / 2
78
+
79
+ # Combinar las capas
80
+ final = CompositeVideoClip(
81
+ [background,
82
+ foreground.set_position((x_center, 0))],
83
+ size=(target_w, target_h)
84
+ )
85
+
86
+ return final
87
+ else: # Video horizontal
88
+ return clip.resize(width=target_w, height=target_h)
89
+
90
+ def concatenate_pexels_videos(text, num_videos=5):
91
  sentences = [sentence.strip() for sentence in text.split(".") if sentence.strip()]
92
+ video_links = []
 
93
  for sentence in sentences:
94
  try:
 
95
  links = search_pexels(sentence, num_results=num_videos)
96
+ if links:
97
+ video_links.append(links[0])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
98
  except Exception as e:
99
+ print(f"Error al buscar video para la frase '{sentence}': {e}")
100
  continue
101
+
102
+ if not video_links:
103
+ raise Exception("No se encontraron videos relevantes para el texto proporcionado.")
104
+
105
+ video_clips = []
106
+ for link in video_links:
107
+ video_response = requests.get(link)
108
+ with tempfile.NamedTemporaryFile(delete=False, suffix=".mp4") as tmp_video:
109
+ tmp_video.write(video_response.content)
110
+ # Cargar y procesar cada clip
111
+ clip = VideoFileClip(tmp_video.name)
112
+ processed_clip = resize_and_blur_video(clip)
113
+ video_clips.append(processed_clip)
114
+
115
  final_clip = concatenate_videoclips(video_clips, method="compose")
116
  return final_clip
117
 
 
118
  def combine_audio_video(audio_file, video_clip, music_clip=None):
119
+ audio_clip = AudioFileClip(audio_file)
120
+ total_duration = audio_clip.duration + 5
121
+ if video_clip.duration < total_duration:
122
+ video_clip = video_clip.loop(duration=total_duration)
123
+ video_clip = video_clip.set_duration(total_duration).fadeout(5)
124
+ final_clip = video_clip.set_audio(audio_clip)
125
+ if music_clip:
126
+ if music_clip.duration < total_duration:
127
+ repetitions = int(total_duration / music_clip.duration) + 1
128
+ music_clips = [music_clip] * repetitions
129
+ music_clip = concatenate_audioclips(music_clips)
130
+ if music_clip.duration > total_duration:
131
+ music_clip = music_clip.subclip(0, total_duration)
132
+ music_clip = music_clip.audio_fadeout(5)
133
+ final_clip = final_clip.set_audio(CompositeAudioClip([audio_clip, music_clip]))
134
+ output_filename = f"final_video_{int(time.time())}.mp4"
135
+ output_path = os.path.join(output_folder, output_filename)
136
+ final_clip.write_videofile(output_path, codec="libx264", audio_codec="aac", fps=24)
137
+ return output_path
 
 
 
138
 
 
 
 
 
 
139
  def process_input(text, txt_file, mp3_file, selected_voice, rate, pitch):
140
  try:
141
  if text.strip():
 
143
  elif txt_file is not None:
144
  final_text = txt_file.decode("utf-8")
145
  else:
146
+ return "No input provided"
147
 
148
  voices = asyncio.run(get_voices())
149
  if selected_voice not in voices:
150
+ return f"La voz '{selected_voice}' no es v谩lida. Por favor, seleccione una de las siguientes voces: {', '.join(voices.keys())}"
151
 
152
+ try:
153
+ audio_file = asyncio.run(text_to_speech(final_text, selected_voice, rate, pitch))
154
+ except Exception as e:
155
+ return f"Error con la voz seleccionada: {e}"
156
 
157
+ try:
158
+ video_clip = concatenate_pexels_videos(final_text, num_videos=5)
159
+ except Exception as e:
160
+ return f"Error al buscar videos en Pexels: {e}"
161
 
 
162
  if mp3_file is not None:
163
  music_clip = adjust_background_music(video_clip.duration, mp3_file.name)
164
  else:
165
  music_clip = None
166
 
 
167
  final_video_path = combine_audio_video(audio_file, video_clip, music_clip)
 
 
168
  upload_to_google_drive(final_video_path)
 
169
  return final_video_path
170
 
171
  except Exception as e:
172
+ return f"Error durante el procesamiento: {e}"
 
173
 
174
  # Interfaz Gradio
175
  with gr.Blocks() as demo:
 
193
  outputs=output_video
194
  )
195
 
196
+ # Leer el puerto asignado por Hugging Face o usar 7860 como valor predeterminado
197
  port = int(os.getenv("PORT", 7860))
198
 
199
  # Lanzar la aplicaci贸n