gnosticdev commited on
Commit
c94aadc
verified
1 Parent(s): 9ea2009

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +200 -39
app.py CHANGED
@@ -1,57 +1,218 @@
1
- def process_input_with_resume(text, txt_file, mp3_file, selected_voice, rate, pitch, keywords):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
  try:
3
- # Determinamos el texto a usar
4
- if text.strip():
5
- final_text = text
6
- elif txt_file is not None:
7
- final_text = txt_file.decode("utf-8")
8
- else:
9
- raise ValueError("No text input provided")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
 
11
- # Dividimos el texto en segmentos
12
- segments = split_text_into_segments(final_text, max_segment_length=30)
13
- video_clips = []
14
 
15
- for i, segment in enumerate(segments):
16
- segment_file = os.path.join(output_folder, f"segment_{i}.mp4")
17
- if os.path.exists(segment_file): # Verifica si el segmento ya existe
18
- print(f"Segmento {i} ya procesado, saltando...")
19
- video_clips.append(VideoFileClip(segment_file))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
  continue
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
 
22
- print(f"Procesando segmento {i + 1}/{len(segments)}...")
23
- audio_segment = asyncio.run(text_to_speech(segment, selected_voice, rate, pitch))
24
- if not audio_segment:
25
- raise ValueError(f"Failed to generate audio for segment {i + 1}")
26
 
27
- video_clip = concatenate_pixabay_videos(keywords, num_videos_per_keyword=1)
28
- if not video_clip:
29
- raise ValueError(f"Failed to generate video for segment {i + 1}")
30
-
31
- music_clip = adjust_background_music(video_clip.duration, mp3_file.name) if mp3_file else None
32
- video_segment_path = combine_audio_video(audio_segment, video_clip, music_clip)
 
 
 
 
 
 
 
33
 
34
- # Guardamos el segmento
35
- os.rename(video_segment_path, segment_file)
36
- video_clips.append(VideoFileClip(segment_file))
37
 
38
- # Subimos el segmento a Google Drive (opcional)
39
- upload_to_google_drive(segment_file, folder_id=FOLDER_ID)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40
 
41
- # Combinamos todos los segmentos en un solo video
42
- final_video_clip = concatenate_videoclips(video_clips, method="compose")
43
- final_video_path = os.path.join(output_folder, f"final_video_{int(time.time())}.mp4")
44
- final_video_clip.write_videofile(final_video_path, codec="libx264", audio_codec="aac", fps=24)
45
 
46
- # Subimos el video final a Google Drive
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47
  video_id = upload_to_google_drive(final_video_path, folder_id=FOLDER_ID)
48
  if video_id:
49
  print(f"Video subido a Google Drive con ID: {video_id}")
50
  else:
51
  print("Error subiendo el video a Google Drive")
52
-
53
  return final_video_path
54
-
55
  except Exception as e:
56
  print(f"Error durante el procesamiento: {e}")
57
- return None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ from tts_module import get_voices, text_to_speech
3
+ from pixabay_api import search_pixabay
4
+ from moviepy.editor import (
5
+ AudioFileClip, VideoFileClip, CompositeAudioClip,
6
+ concatenate_audioclips, concatenate_videoclips, vfx, CompositeVideoClip,
7
+ ColorClip
8
+ )
9
+ import asyncio
10
+ import os
11
+ import json
12
+ import time
13
+ import requests
14
+ import tempfile
15
+ import re
16
+ import random
17
+ from google_drive_upload import authenticate_google_drive, upload_to_google_drive
18
+
19
+ # Crear el archivo de credenciales de servicio desde los secretos
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
+ # Define la carpeta de salida
26
+ output_folder = "outputs"
27
+ os.makedirs(output_folder, exist_ok=True)
28
+
29
+ # ID de la carpeta de destino en Google Drive (ajusta esto con tu ID real)
30
+ FOLDER_ID = "TU_ID_DE_CARPETA" # Reemplaza con tu ID de carpeta de Google Drive
31
+
32
+ def resize_and_blur_video(clip, target_aspect_ratio=16/9):
33
+ """Redimensiona y aplica desenfoque al fondo del video para mantener el aspecto 16:9."""
34
  try:
35
+ w, h = clip.size
36
+ current_aspect_ratio = w / h
37
+ if abs(current_aspect_ratio - target_aspect_ratio) < 0.1:
38
+ return clip
39
+ if current_aspect_ratio < target_aspect_ratio: # Video vertical
40
+ target_w = int(h * target_aspect_ratio)
41
+ target_h = h
42
+ background = clip.resize(width=target_w)
43
+ try:
44
+ background = background.fx(vfx.blur, sigma=50)
45
+ except Exception as e:
46
+ print(f"Error al aplicar blur: {e}")
47
+ foreground = clip.resize(height=target_h)
48
+ x_center = (target_w - foreground.w) / 2
49
+ return CompositeVideoClip(
50
+ [background, foreground.set_position((x_center, 0))],
51
+ size=(target_w, target_h)
52
+ )
53
+ else: # Video horizontal
54
+ return clip.resize(width=int(h * target_aspect_ratio), height=h)
55
+ except Exception as e:
56
+ print(f"Error en resize_and_blur_video: {e}")
57
+ return clip
58
 
 
 
 
59
 
60
+ def concatenate_pixabay_videos(keywords, num_videos_per_keyword=1):
61
+ """Concatena videos de Pixabay basados en palabras clave."""
62
+ keyword_list = [keyword.strip() for keyword in keywords.split(",") if keyword.strip()]
63
+ if not keyword_list:
64
+ keyword_list = ["nature"] # Palabra clave por defecto
65
+ video_clips = []
66
+ for keyword in keyword_list:
67
+ try:
68
+ print(f"Buscando videos para la palabra clave '{keyword}'...")
69
+ links = search_pixabay(keyword, num_results=num_videos_per_keyword)
70
+ if not links:
71
+ print(f"No se encontraron videos para '{keyword}', probando con 'nature'")
72
+ links = search_pixabay("nature", num_results=num_videos_per_keyword)
73
+ if not links:
74
+ continue
75
+ link = links[0]
76
+ video_response = requests.get(link)
77
+ if video_response.status_code != 200:
78
+ print(f"Error al descargar video desde {link}")
79
  continue
80
+ with tempfile.NamedTemporaryFile(delete=False, suffix=".mp4") as tmp_video:
81
+ tmp_video.write(video_response.content)
82
+ clip = VideoFileClip(tmp_video.name)
83
+ processed_clip = resize_and_blur_video(clip)
84
+ video_clips.append(processed_clip)
85
+ os.unlink(tmp_video.name) # Limpiamos el archivo temporal
86
+ except Exception as e:
87
+ print(f"Error procesando palabra clave '{keyword}': {e}")
88
+ continue
89
+ if not video_clips:
90
+ # Si no hay videos, creamos un clip negro de 5 segundos
91
+ return ColorClip(size=(1920, 1080), color=[0, 0, 0], duration=5)
92
+ random.shuffle(video_clips)
93
+ return concatenate_videoclips(video_clips, method="compose")
94
 
 
 
 
 
95
 
96
+ def adjust_background_music(video_duration, music_file):
97
+ """Ajusta la m煤sica de fondo para que coincida con la duraci贸n del video."""
98
+ try:
99
+ music = AudioFileClip(music_file)
100
+ if music.duration < video_duration:
101
+ repetitions = int(video_duration / music.duration) + 1
102
+ music_clips = [music] * repetitions
103
+ music = concatenate_audioclips(music_clips)
104
+ music = music.subclip(0, video_duration)
105
+ return music.volumex(0.2)
106
+ except Exception as e:
107
+ print(f"Error ajustando m煤sica: {e}")
108
+ return None
109
 
 
 
 
110
 
111
+ def combine_audio_video(audio_file, video_clip, music_clip=None):
112
+ """Combina el audio y el video en un archivo final."""
113
+ try:
114
+ audio_clip = AudioFileClip(audio_file)
115
+ total_duration = audio_clip.duration + 2 # A帽adimos 2 segundos extra
116
+
117
+ # Aseguramos que el video tenga la duraci贸n correcta
118
+ video_clip = video_clip.loop(duration=total_duration)
119
+ video_clip = video_clip.set_duration(total_duration).fadeout(2)
120
+
121
+ # Combinamos el audio principal
122
+ final_clip = video_clip.set_audio(audio_clip)
123
+ # A帽adimos la m煤sica de fondo si existe
124
+ if music_clip:
125
+ music_clip = music_clip.set_duration(total_duration).audio_fadeout(2)
126
+ final_clip = final_clip.set_audio(CompositeAudioClip([audio_clip, music_clip]))
127
+ # Generamos el nombre del archivo y la ruta
128
+ output_filename = f"final_video_{int(time.time())}.mp4"
129
+ output_path = os.path.join(output_folder, output_filename)
130
+
131
+ # Guardamos el video
132
+ final_clip.write_videofile(output_path, codec="libx264", audio_codec="aac", fps=24)
133
+
134
+ # Limpiamos los clips
135
+ final_clip.close()
136
+ video_clip.close()
137
+ audio_clip.close()
138
+ if music_clip:
139
+ music_clip.close()
140
+
141
+ return output_path
142
+ except Exception as e:
143
+ print(f"Error combinando audio y video: {e}")
144
+ if 'final_clip' in locals():
145
+ final_clip.close()
146
+ return None
147
 
 
 
 
 
148
 
149
+ def process_input(text, txt_file, mp3_file, selected_voice, rate, pitch, keywords):
150
+ """Procesa la entrada del usuario y genera el video final."""
151
+ try:
152
+ # Determinamos el texto a usar
153
+ if text.strip():
154
+ final_text = text
155
+ elif txt_file is not None:
156
+ final_text = txt_file.decode("utf-8")
157
+ else:
158
+ raise ValueError("No text input provided")
159
+ # Generamos el audio
160
+ audio_file = asyncio.run(text_to_speech(final_text, selected_voice, rate, pitch))
161
+ if not audio_file:
162
+ raise ValueError("Failed to generate audio")
163
+ # Generamos el video
164
+ video_clip = concatenate_pixabay_videos(keywords, num_videos_per_keyword=1)
165
+ if not video_clip:
166
+ raise ValueError("Failed to generate video")
167
+ # Procesamos la m煤sica de fondo si existe
168
+ music_clip = None
169
+ if mp3_file is not None:
170
+ music_clip = adjust_background_music(video_clip.duration, mp3_file.name)
171
+ # Combinamos todo
172
+ final_video_path = combine_audio_video(audio_file, video_clip, music_clip)
173
+ if not final_video_path:
174
+ raise ValueError("Failed to combine audio and video")
175
+ # Subimos a Google Drive
176
  video_id = upload_to_google_drive(final_video_path, folder_id=FOLDER_ID)
177
  if video_id:
178
  print(f"Video subido a Google Drive con ID: {video_id}")
179
  else:
180
  print("Error subiendo el video a Google Drive")
181
+
182
  return final_video_path
 
183
  except Exception as e:
184
  print(f"Error durante el procesamiento: {e}")
185
+ return None
186
+
187
+
188
+ # Interfaz Gradio
189
+ with gr.Blocks() as demo:
190
+ gr.Markdown("# Text-to-Video Generator")
191
+ with gr.Row():
192
+ with gr.Column():
193
+ text_input = gr.Textbox(label="Write your text here", lines=5)
194
+ txt_file_input = gr.File(label="Or upload a .txt file", file_types=[".txt"])
195
+ mp3_file_input = gr.File(label="Upload background music (.mp3)", file_types=[".mp3"])
196
+ keyword_input = gr.Textbox(
197
+ label="Enter keywords separated by commas (e.g., universe, galaxy, forest, cat)",
198
+ value="nature"
199
+ )
200
+ voices = asyncio.run(get_voices())
201
+ voice_dropdown = gr.Dropdown(choices=list(voices.keys()), label="Select Voice")
202
+ rate_slider = gr.Slider(minimum=-50, maximum=50, value=0, label="Speech Rate Adjustment (%)", step=1)
203
+ pitch_slider = gr.Slider(minimum=-20, maximum=20, value=0, label="Pitch Adjustment (Hz)", step=1)
204
+ with gr.Column():
205
+ output_video = gr.File(label="Generated Video") # Usamos gr.File en lugar de gr.Video
206
+
207
+ btn = gr.Button("Generate Video")
208
+ btn.click(
209
+ process_input,
210
+ inputs=[text_input, txt_file_input, mp3_file_input, voice_dropdown, rate_slider, pitch_slider, keyword_input],
211
+ outputs=output_video
212
+ )
213
+
214
+ # Leer el puerto asignado por Hugging Face
215
+ port = int(os.getenv("PORT", 7860))
216
+
217
+ # Lanzar la aplicaci贸n
218
+ demo.launch(server_name="0.0.0.0", server_port=port, share=True, show_error=True, enable_queue=True)