gnosticdev commited on
Commit
59b95fb
verified
1 Parent(s): 5284746

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +10 -59
app.py CHANGED
@@ -17,23 +17,19 @@ from google.oauth2 import service_account
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,43 +39,37 @@ def cleanup_temp_files():
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,14 +83,12 @@ def download_video(link):
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:
103
- print(f"Buscando videos para la palabra clave '{keyword}'...")
104
  links = search_pixabay(keyword, num_results=num_videos_per_keyword)
105
  if not links:
106
  print(f"No se encontraron videos para '{keyword}', probando con 'nature'")
@@ -112,18 +100,16 @@ def concatenate_pixabay_videos(keywords, num_videos_per_keyword=1):
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,67 +123,45 @@ def adjust_background_music(video_duration, music_file):
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()
166
  if music_clip:
167
  music_clip.close()
168
-
169
  return output_path
170
  except Exception as e:
171
  print(f"Error combinando audio y video: {e}")
172
- if 'final_clip' in locals():
173
- final_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]
189
  }
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,35 +170,27 @@ def upload_to_google_drive(file_path, folder_id):
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}")
238
  return f"[Descargar video]({download_link})"
239
  else:
240
  raise ValueError("Error subiendo el video a Google Drive")
@@ -242,9 +198,8 @@ def process_input(text, txt_file, mp3_file, selected_voice, rate, pitch, keyword
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():
@@ -253,16 +208,15 @@ with gr.Blocks() as demo:
253
  txt_file_input = gr.File(label="Or upload a .txt file", file_types=[".txt"])
254
  mp3_file_input = gr.File(label="Upload background music (.mp3)", file_types=[".mp3"])
255
  keyword_input = gr.Textbox(
256
- label="Enter keywords separated by commas (e.g., universe, galaxy, forest, cat)",
257
- value="fear, religion, god, demons, aliens, possession, galaxy, mysterious, dystopian, astral, warfare, space, space, galaxy, moon, fear, astral, god, evil, mystery, cosmos, stars, paranormal, inexplicable, hidden, enigma, unknown, unusual, intriguing, curious, strange, supernatural, esoteric, arcane, occultism, supernatural, mystery, phenomenon, rare, unusual, enigmatic, sinister, gloomy, dark, shadowy, macabre, eerie, chilling, cursed, fantastic, unreal, unknown, mysterious, enigmatic, inexplicable, unusual, strange, unusual, arcane, esoteric, hidden, shadowy, dark, gloomy, sinister, macabre, eerie, chilling, cursed, fantastic, unreal, paranormal, supernatural, occultism, phenomenon, rare, intriguing, curious"
258
  )
259
  voices = asyncio.run(get_voices())
260
  voice_dropdown = gr.Dropdown(choices=list(voices.keys()), label="Select Voice")
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(
268
  process_input,
@@ -270,8 +224,5 @@ with gr.Blocks() as demo:
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)
 
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
  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
  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:
 
92
  links = search_pixabay(keyword, num_results=num_videos_per_keyword)
93
  if not links:
94
  print(f"No se encontraron videos para '{keyword}', probando con 'nature'")
 
100
  clip = VideoFileClip(temp_video_path)
101
  processed_clip = resize_and_blur_video(clip)
102
  video_clips.append(processed_clip)
103
+ os.remove(temp_video_path)
104
  except Exception as e:
105
  print(f"Error procesando palabra clave '{keyword}': {e}")
106
  continue
107
  if not video_clips:
 
108
  return ColorClip(size=(1920, 1080), color=[0, 0, 0], duration=5)
109
  random.shuffle(video_clips)
110
  return concatenate_videoclips(video_clips, method="compose")
111
 
112
  def adjust_background_music(video_duration, music_file):
 
113
  try:
114
  music = AudioFileClip(music_file)
115
  if music.duration < video_duration:
 
123
  return None
124
 
125
  def combine_audio_video(audio_file, video_clip, music_clip=None):
 
126
  try:
127
  audio_clip = AudioFileClip(audio_file)
128
+ total_duration = audio_clip.duration + 2
 
 
129
  video_clip = video_clip.loop(duration=total_duration)
130
  video_clip = video_clip.set_duration(total_duration).fadeout(2)
 
 
131
  final_clip = video_clip.set_audio(audio_clip)
 
132
  if music_clip:
133
  music_clip = music_clip.set_duration(total_duration).audio_fadeout(2)
134
  final_clip = final_clip.set_audio(CompositeAudioClip([audio_clip, music_clip]))
 
135
  output_filename = f"final_video_{int(time.time())}.mp4"
136
  output_path = os.path.join(output_folder, output_filename)
 
 
137
  final_clip.write_videofile(output_path, codec="libx264", audio_codec="aac", fps=24)
 
 
138
  final_clip.close()
139
  video_clip.close()
140
  audio_clip.close()
141
  if music_clip:
142
  music_clip.close()
 
143
  return output_path
144
  except Exception as e:
145
  print(f"Error combinando audio y video: {e}")
 
 
146
  return None
147
 
148
  def upload_to_google_drive(file_path, folder_id):
 
149
  try:
 
150
  creds = service_account.Credentials.from_service_account_file(
151
  'service-account.json', scopes=['https://www.googleapis.com/auth/drive']
152
  )
153
  service = build('drive', 'v3', credentials=creds)
 
 
154
  file_metadata = {
155
  'name': os.path.basename(file_path),
156
  'parents': [folder_id]
157
  }
158
  media = MediaFileUpload(file_path, resumable=True)
159
  file = service.files().create(body=file_metadata, media_body=media, fields='id').execute()
 
 
160
  permission = {
161
  'type': 'anyone',
162
  'role': 'reader'
163
  }
164
  service.permissions().create(fileId=file['id'], body=permission).execute()
 
 
165
  file_id = file['id']
166
  download_link = f"https://drive.google.com/uc?export=download&id={file_id}"
167
  return download_link
 
170
  return None
171
 
172
  def process_input(text, txt_file, mp3_file, selected_voice, rate, pitch, keywords):
 
173
  try:
 
174
  if text.strip():
175
  final_text = text
176
  elif txt_file is not None:
177
  final_text = txt_file.decode("utf-8")
178
  else:
179
  raise ValueError("No text input provided")
 
180
  audio_file = asyncio.run(text_to_speech(final_text, selected_voice, rate, pitch))
181
  if not audio_file:
182
  raise ValueError("Failed to generate audio")
 
183
  video_clip = concatenate_pixabay_videos(keywords, num_videos_per_keyword=1)
184
  if not video_clip:
185
  raise ValueError("Failed to generate video")
 
186
  music_clip = None
187
  if mp3_file is not None:
188
  music_clip = adjust_background_music(video_clip.duration, mp3_file.name)
 
189
  final_video_path = combine_audio_video(audio_file, video_clip, music_clip)
190
  if not final_video_path:
191
  raise ValueError("Failed to combine audio and video")
 
192
  download_link = upload_to_google_drive(final_video_path, folder_id=FOLDER_ID)
193
  if download_link:
 
194
  return f"[Descargar video]({download_link})"
195
  else:
196
  raise ValueError("Error subiendo el video a Google Drive")
 
198
  print(f"Error durante el procesamiento: {e}")
199
  return None
200
  finally:
201
+ cleanup_temp_files()
202
 
 
203
  with gr.Blocks() as demo:
204
  gr.Markdown("# Text-to-Video Generator")
205
  with gr.Row():
 
208
  txt_file_input = gr.File(label="Or upload a .txt file", file_types=[".txt"])
209
  mp3_file_input = gr.File(label="Upload background music (.mp3)", file_types=[".mp3"])
210
  keyword_input = gr.Textbox(
211
+ label="Enter keywords separated by commas",
212
+ value="fear,religion,god,demons,aliens,possession"
213
  )
214
  voices = asyncio.run(get_voices())
215
  voice_dropdown = gr.Dropdown(choices=list(voices.keys()), label="Select Voice")
216
  rate_slider = gr.Slider(minimum=-50, maximum=50, value=0, label="Speech Rate Adjustment (%)", step=1)
217
  pitch_slider = gr.Slider(minimum=-20, maximum=20, value=0, label="Pitch Adjustment (Hz)", step=1)
218
  with gr.Column():
219
+ output_link = gr.Markdown("")
 
220
  btn = gr.Button("Generate Video")
221
  btn.click(
222
  process_input,
 
224
  outputs=output_link
225
  )
226
 
 
227
  port = int(os.getenv("PORT", 7860))
 
 
228
  demo.launch(server_name="0.0.0.0", server_port=port, share=True, show_error=True)