gnosticdev commited on
Commit
5284746
verified
1 Parent(s): 4cf7ad9

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +43 -8
app.py CHANGED
@@ -17,19 +17,23 @@ from google.oauth2 import service_account
17
  from googleapiclient.http import MediaFileUpload
18
  from io import BytesIO
19
 
 
20
  service_account_info = json.loads(os.getenv('GOOGLE_SERVICE_ACCOUNT', '{}'))
21
  if service_account_info:
22
  with open('service-account.json', 'w') as f:
23
  json.dump(service_account_info, f)
24
 
 
25
  output_folder = "outputs"
26
  temp_dir = "temp_files"
27
  os.makedirs(output_folder, exist_ok=True)
28
  os.makedirs(temp_dir, exist_ok=True)
29
 
30
- FOLDER_ID = "12S6adpanAXjf71pKKGRRPqpzbJa5XEh3"
 
31
 
32
  def cleanup_temp_files():
 
33
  for filename in os.listdir(temp_dir):
34
  file_path = os.path.join(temp_dir, filename)
35
  try:
@@ -39,37 +43,43 @@ def cleanup_temp_files():
39
  print(f"Error deleting {file_path}: {e}")
40
 
41
  def resize_and_blur_video(clip, target_width=1920, target_height=1080):
 
42
  try:
43
  w, h = clip.size
44
  current_aspect_ratio = w / h
45
  target_aspect_ratio = target_width / target_height
46
 
47
  if abs(current_aspect_ratio - target_aspect_ratio) < 0.1:
 
48
  return clip.resize((target_width, target_height))
49
 
 
50
  background = ColorClip(size=(target_width, target_height), color=[0, 0, 0]).set_duration(clip.duration)
51
  try:
52
  background = background.fx(vfx.blur, sigma=50)
53
  except Exception as e:
54
  print(f"Error al aplicar blur: {e}")
55
 
56
- if current_aspect_ratio < target_aspect_ratio:
 
57
  new_height = target_height
58
  new_width = int(new_height * current_aspect_ratio)
59
  x_center = (target_width - new_width) / 2
60
  resized_clip = clip.resize(width=new_width).set_position((x_center, 0))
61
- else:
62
  new_width = target_width
63
  new_height = int(new_width / current_aspect_ratio)
64
  y_center = (target_height - new_height) / 2
65
  resized_clip = clip.resize(height=new_height).set_position((0, y_center))
66
 
 
67
  return CompositeVideoClip([background, resized_clip], size=(target_width, target_height))
68
  except Exception as e:
69
  print(f"Error en resize_and_blur_video: {e}")
70
  return clip
71
 
72
  def download_video(link):
 
73
  try:
74
  video_response = requests.get(link)
75
  if video_response.status_code != 200:
@@ -83,9 +93,10 @@ def download_video(link):
83
  return None
84
 
85
  def concatenate_pixabay_videos(keywords, num_videos_per_keyword=1):
 
86
  keyword_list = [keyword.strip() for keyword in keywords.split(",") if keyword.strip()]
87
  if not keyword_list:
88
- keyword_list = ["nature"]
89
  video_clips = []
90
  for keyword in keyword_list:
91
  try:
@@ -101,16 +112,18 @@ def concatenate_pixabay_videos(keywords, num_videos_per_keyword=1):
101
  clip = VideoFileClip(temp_video_path)
102
  processed_clip = resize_and_blur_video(clip)
103
  video_clips.append(processed_clip)
104
- os.remove(temp_video_path)
105
  except Exception as e:
106
  print(f"Error procesando palabra clave '{keyword}': {e}")
107
  continue
108
  if not video_clips:
 
109
  return ColorClip(size=(1920, 1080), color=[0, 0, 0], duration=5)
110
  random.shuffle(video_clips)
111
  return concatenate_videoclips(video_clips, method="compose")
112
 
113
  def adjust_background_music(video_duration, music_file):
 
114
  try:
115
  music = AudioFileClip(music_file)
116
  if music.duration < video_duration:
@@ -124,22 +137,29 @@ def adjust_background_music(video_duration, music_file):
124
  return None
125
 
126
  def combine_audio_video(audio_file, video_clip, music_clip=None):
 
127
  try:
128
  audio_clip = AudioFileClip(audio_file)
129
- total_duration = audio_clip.duration + 2
130
 
 
131
  video_clip = video_clip.loop(duration=total_duration)
132
  video_clip = video_clip.set_duration(total_duration).fadeout(2)
133
 
 
134
  final_clip = video_clip.set_audio(audio_clip)
 
135
  if music_clip:
136
  music_clip = music_clip.set_duration(total_duration).audio_fadeout(2)
137
  final_clip = final_clip.set_audio(CompositeAudioClip([audio_clip, music_clip]))
 
138
  output_filename = f"final_video_{int(time.time())}.mp4"
139
  output_path = os.path.join(output_folder, output_filename)
140
 
 
141
  final_clip.write_videofile(output_path, codec="libx264", audio_codec="aac", fps=24)
142
 
 
143
  final_clip.close()
144
  video_clip.close()
145
  audio_clip.close()
@@ -154,12 +174,15 @@ def combine_audio_video(audio_file, video_clip, music_clip=None):
154
  return None
155
 
156
  def upload_to_google_drive(file_path, folder_id):
 
157
  try:
 
158
  creds = service_account.Credentials.from_service_account_file(
159
  'service-account.json', scopes=['https://www.googleapis.com/auth/drive']
160
  )
161
  service = build('drive', 'v3', credentials=creds)
162
 
 
163
  file_metadata = {
164
  'name': os.path.basename(file_path),
165
  'parents': [folder_id]
@@ -167,12 +190,14 @@ def upload_to_google_drive(file_path, folder_id):
167
  media = MediaFileUpload(file_path, resumable=True)
168
  file = service.files().create(body=file_metadata, media_body=media, fields='id').execute()
169
 
 
170
  permission = {
171
  'type': 'anyone',
172
  'role': 'reader'
173
  }
174
  service.permissions().create(fileId=file['id'], body=permission).execute()
175
 
 
176
  file_id = file['id']
177
  download_link = f"https://drive.google.com/uc?export=download&id={file_id}"
178
  return download_link
@@ -181,25 +206,32 @@ def upload_to_google_drive(file_path, folder_id):
181
  return None
182
 
183
  def process_input(text, txt_file, mp3_file, selected_voice, rate, pitch, keywords):
 
184
  try:
 
185
  if text.strip():
186
  final_text = text
187
  elif txt_file is not None:
188
  final_text = txt_file.decode("utf-8")
189
  else:
190
  raise ValueError("No text input provided")
 
191
  audio_file = asyncio.run(text_to_speech(final_text, selected_voice, rate, pitch))
192
  if not audio_file:
193
  raise ValueError("Failed to generate audio")
 
194
  video_clip = concatenate_pixabay_videos(keywords, num_videos_per_keyword=1)
195
  if not video_clip:
196
  raise ValueError("Failed to generate video")
 
197
  music_clip = None
198
  if mp3_file is not None:
199
  music_clip = adjust_background_music(video_clip.duration, mp3_file.name)
 
200
  final_video_path = combine_audio_video(audio_file, video_clip, music_clip)
201
  if not final_video_path:
202
  raise ValueError("Failed to combine audio and video")
 
203
  download_link = upload_to_google_drive(final_video_path, folder_id=FOLDER_ID)
204
  if download_link:
205
  print(f"Video subido a Google Drive. Enlace de descarga: {download_link}")
@@ -210,8 +242,9 @@ def process_input(text, txt_file, mp3_file, selected_voice, rate, pitch, keyword
210
  print(f"Error durante el procesamiento: {e}")
211
  return None
212
  finally:
213
- cleanup_temp_files()
214
 
 
215
  with gr.Blocks() as demo:
216
  gr.Markdown("# Text-to-Video Generator")
217
  with gr.Row():
@@ -228,7 +261,7 @@ with gr.Blocks() as demo:
228
  rate_slider = gr.Slider(minimum=-50, maximum=50, value=0, label="Speech Rate Adjustment (%)", step=1)
229
  pitch_slider = gr.Slider(minimum=-20, maximum=20, value=0, label="Pitch Adjustment (Hz)", step=1)
230
  with gr.Column():
231
- output_link = gr.Markdown("")
232
 
233
  btn = gr.Button("Generate Video")
234
  btn.click(
@@ -237,6 +270,8 @@ with gr.Blocks() as demo:
237
  outputs=output_link
238
  )
239
 
 
240
  port = int(os.getenv("PORT", 7860))
241
 
 
242
  demo.launch(server_name="0.0.0.0", server_port=port, share=True, show_error=True)
 
17
  from googleapiclient.http import MediaFileUpload
18
  from io import BytesIO
19
 
20
+ # Crear el archivo de credenciales de servicio desde los secretos
21
  service_account_info = json.loads(os.getenv('GOOGLE_SERVICE_ACCOUNT', '{}'))
22
  if service_account_info:
23
  with open('service-account.json', 'w') as f:
24
  json.dump(service_account_info, f)
25
 
26
+ # Define la carpeta de salida y la carpeta temporal
27
  output_folder = "outputs"
28
  temp_dir = "temp_files"
29
  os.makedirs(output_folder, exist_ok=True)
30
  os.makedirs(temp_dir, exist_ok=True)
31
 
32
+ # ID de la carpeta de destino en Google Drive
33
+ FOLDER_ID = "12S6adpanAXjf71pKKGRRPqpzbJa5XEh3" # Reemplaza con tu ID de carpeta
34
 
35
  def cleanup_temp_files():
36
+ """Elimina todos los archivos temporales de la carpeta temp_files."""
37
  for filename in os.listdir(temp_dir):
38
  file_path = os.path.join(temp_dir, filename)
39
  try:
 
43
  print(f"Error deleting {file_path}: {e}")
44
 
45
  def resize_and_blur_video(clip, target_width=1920, target_height=1080):
46
+ """Redimensiona el video al tama帽o 1080p (16:9) y aplica desenfoque si es necesario."""
47
  try:
48
  w, h = clip.size
49
  current_aspect_ratio = w / h
50
  target_aspect_ratio = target_width / target_height
51
 
52
  if abs(current_aspect_ratio - target_aspect_ratio) < 0.1:
53
+ # Si la relaci贸n de aspecto ya es cercana a 16:9, solo redimensionamos
54
  return clip.resize((target_width, target_height))
55
 
56
+ # Crear un fondo borroso con las dimensiones objetivo
57
  background = ColorClip(size=(target_width, target_height), color=[0, 0, 0]).set_duration(clip.duration)
58
  try:
59
  background = background.fx(vfx.blur, sigma=50)
60
  except Exception as e:
61
  print(f"Error al aplicar blur: {e}")
62
 
63
+ # Redimensionar el video original para mantener su proporci贸n
64
+ if current_aspect_ratio < target_aspect_ratio: # Video vertical
65
  new_height = target_height
66
  new_width = int(new_height * current_aspect_ratio)
67
  x_center = (target_width - new_width) / 2
68
  resized_clip = clip.resize(width=new_width).set_position((x_center, 0))
69
+ else: # Video horizontal
70
  new_width = target_width
71
  new_height = int(new_width / current_aspect_ratio)
72
  y_center = (target_height - new_height) / 2
73
  resized_clip = clip.resize(height=new_height).set_position((0, y_center))
74
 
75
+ # Combinar el fondo borroso con el video redimensionado
76
  return CompositeVideoClip([background, resized_clip], size=(target_width, target_height))
77
  except Exception as e:
78
  print(f"Error en resize_and_blur_video: {e}")
79
  return clip
80
 
81
  def download_video(link):
82
+ """Descarga un video desde un enlace y lo guarda en la carpeta temporal."""
83
  try:
84
  video_response = requests.get(link)
85
  if video_response.status_code != 200:
 
93
  return None
94
 
95
  def concatenate_pixabay_videos(keywords, num_videos_per_keyword=1):
96
+ """Concatena videos de Pixabay basados en palabras clave."""
97
  keyword_list = [keyword.strip() for keyword in keywords.split(",") if keyword.strip()]
98
  if not keyword_list:
99
+ keyword_list = ["nature"] # Palabra clave por defecto
100
  video_clips = []
101
  for keyword in keyword_list:
102
  try:
 
112
  clip = VideoFileClip(temp_video_path)
113
  processed_clip = resize_and_blur_video(clip)
114
  video_clips.append(processed_clip)
115
+ os.remove(temp_video_path) # Eliminar archivo temporal despu茅s de usarlo
116
  except Exception as e:
117
  print(f"Error procesando palabra clave '{keyword}': {e}")
118
  continue
119
  if not video_clips:
120
+ # Si no hay videos, creamos un clip negro de 5 segundos
121
  return ColorClip(size=(1920, 1080), color=[0, 0, 0], duration=5)
122
  random.shuffle(video_clips)
123
  return concatenate_videoclips(video_clips, method="compose")
124
 
125
  def adjust_background_music(video_duration, music_file):
126
+ """Ajusta la m煤sica de fondo para que coincida con la duraci贸n del video."""
127
  try:
128
  music = AudioFileClip(music_file)
129
  if music.duration < video_duration:
 
137
  return None
138
 
139
  def combine_audio_video(audio_file, video_clip, music_clip=None):
140
+ """Combina el audio y el video en un archivo final."""
141
  try:
142
  audio_clip = AudioFileClip(audio_file)
143
+ total_duration = audio_clip.duration + 2 # A帽adimos 2 segundos extra
144
 
145
+ # Aseguramos que el video tenga la duraci贸n correcta
146
  video_clip = video_clip.loop(duration=total_duration)
147
  video_clip = video_clip.set_duration(total_duration).fadeout(2)
148
 
149
+ # Combinamos el audio principal
150
  final_clip = video_clip.set_audio(audio_clip)
151
+ # A帽adimos la m煤sica de fondo si existe
152
  if music_clip:
153
  music_clip = music_clip.set_duration(total_duration).audio_fadeout(2)
154
  final_clip = final_clip.set_audio(CompositeAudioClip([audio_clip, music_clip]))
155
+ # Generamos el nombre del archivo y la ruta
156
  output_filename = f"final_video_{int(time.time())}.mp4"
157
  output_path = os.path.join(output_folder, output_filename)
158
 
159
+ # Guardamos el video
160
  final_clip.write_videofile(output_path, codec="libx264", audio_codec="aac", fps=24)
161
 
162
+ # Limpiamos los clips
163
  final_clip.close()
164
  video_clip.close()
165
  audio_clip.close()
 
174
  return None
175
 
176
  def upload_to_google_drive(file_path, folder_id):
177
+ """Sube un archivo a Google Drive y devuelve el enlace p煤blico."""
178
  try:
179
+ # Autenticaci贸n
180
  creds = service_account.Credentials.from_service_account_file(
181
  'service-account.json', scopes=['https://www.googleapis.com/auth/drive']
182
  )
183
  service = build('drive', 'v3', credentials=creds)
184
 
185
+ # Subir el archivo
186
  file_metadata = {
187
  'name': os.path.basename(file_path),
188
  'parents': [folder_id]
 
190
  media = MediaFileUpload(file_path, resumable=True)
191
  file = service.files().create(body=file_metadata, media_body=media, fields='id').execute()
192
 
193
+ # Hacer el archivo p煤blico
194
  permission = {
195
  'type': 'anyone',
196
  'role': 'reader'
197
  }
198
  service.permissions().create(fileId=file['id'], body=permission).execute()
199
 
200
+ # Generar el enlace de descarga
201
  file_id = file['id']
202
  download_link = f"https://drive.google.com/uc?export=download&id={file_id}"
203
  return download_link
 
206
  return None
207
 
208
  def process_input(text, txt_file, mp3_file, selected_voice, rate, pitch, keywords):
209
+ """Procesa la entrada del usuario y genera el video final."""
210
  try:
211
+ # Determinamos el texto a usar
212
  if text.strip():
213
  final_text = text
214
  elif txt_file is not None:
215
  final_text = txt_file.decode("utf-8")
216
  else:
217
  raise ValueError("No text input provided")
218
+ # Generamos el audio
219
  audio_file = asyncio.run(text_to_speech(final_text, selected_voice, rate, pitch))
220
  if not audio_file:
221
  raise ValueError("Failed to generate audio")
222
+ # Generamos el video
223
  video_clip = concatenate_pixabay_videos(keywords, num_videos_per_keyword=1)
224
  if not video_clip:
225
  raise ValueError("Failed to generate video")
226
+ # Procesamos la m煤sica de fondo si existe
227
  music_clip = None
228
  if mp3_file is not None:
229
  music_clip = adjust_background_music(video_clip.duration, mp3_file.name)
230
+ # Combinamos todo
231
  final_video_path = combine_audio_video(audio_file, video_clip, music_clip)
232
  if not final_video_path:
233
  raise ValueError("Failed to combine audio and video")
234
+ # Subimos a Google Drive y obtenemos el enlace
235
  download_link = upload_to_google_drive(final_video_path, folder_id=FOLDER_ID)
236
  if download_link:
237
  print(f"Video subido a Google Drive. Enlace de descarga: {download_link}")
 
242
  print(f"Error durante el procesamiento: {e}")
243
  return None
244
  finally:
245
+ cleanup_temp_files() # Limpiar archivos temporales al finalizar
246
 
247
+ # Interfaz Gradio
248
  with gr.Blocks() as demo:
249
  gr.Markdown("# Text-to-Video Generator")
250
  with gr.Row():
 
261
  rate_slider = gr.Slider(minimum=-50, maximum=50, value=0, label="Speech Rate Adjustment (%)", step=1)
262
  pitch_slider = gr.Slider(minimum=-20, maximum=20, value=0, label="Pitch Adjustment (Hz)", step=1)
263
  with gr.Column():
264
+ output_link = gr.Markdown("") # Mostrar el enlace de descarga
265
 
266
  btn = gr.Button("Generate Video")
267
  btn.click(
 
270
  outputs=output_link
271
  )
272
 
273
+ # Leer el puerto asignado por Hugging Face
274
  port = int(os.getenv("PORT", 7860))
275
 
276
+ # Lanzar la aplicaci贸n
277
  demo.launch(server_name="0.0.0.0", server_port=port, share=True, show_error=True)