gnosticdev commited on
Commit
d5d45b2
verified
1 Parent(s): c2f9b00

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +141 -162
app.py CHANGED
@@ -18,6 +18,11 @@ from io import BytesIO
18
  from PIL import Image
19
  import numpy as np
20
 
 
 
 
 
 
21
  # Define output and temp directories
22
  output_folder = "outputs"
23
  temp_dir = "temp_files"
@@ -25,7 +30,18 @@ os.makedirs(output_folder, exist_ok=True)
25
  os.makedirs(temp_dir, exist_ok=True)
26
 
27
  # Google Drive folder ID
28
- FOLDER_ID = "12S6adpanAXjf71pKKGRRPqpzbJa5XEh3" # Replace with your folder ID
 
 
 
 
 
 
 
 
 
 
 
29
 
30
  def cleanup_temp_files():
31
  """Delete all temporary files from the temp_files folder."""
@@ -38,60 +54,147 @@ def cleanup_temp_files():
38
  print(f"Error deleting {file_path}: {e}")
39
 
40
  def search_google_images(query, num_images=1):
41
- """Search for images using Google Custom Search API."""
42
  try:
43
  api_key = os.getenv('GOOGLE_API_KEY')
44
  cse_id = os.getenv('GOOGLE_CSE_ID')
 
45
 
46
- # Imprimir informaci贸n de depuraci贸n (los ... son para no mostrar la key completa)
47
  print(f"Buscando im谩genes para: {query}")
48
- print(f"CSE ID configurado: {cse_id}")
49
- print(f"API Key configurada: {api_key[:5]}...{api_key[-5:] if api_key else ''}")
50
-
51
- service = build("customsearch", "v1", developerKey=api_key)
52
 
53
- # Realizar la b煤squeda
54
- result = service.cse().list(
55
- q=query,
56
- cx=cse_id,
57
- searchType="image",
58
- num=num_images,
59
- safe='off' # Permitir todos los resultados
60
- ).execute()
61
 
62
- # Verificar si hay resultados
63
- if 'items' in result:
64
- image_urls = [item['link'] for item in result.get('items', [])]
65
- print(f"Encontradas {len(image_urls)} im谩genes")
66
- return image_urls
67
- else:
68
- print("No se encontraron im谩genes")
69
- return []
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
70
 
71
  except Exception as e:
72
- print(f"Error detallado en la b煤squeda de im谩genes: {str(e)}")
73
  return []
74
 
75
- def download_image(url):
76
- """Download an image from a URL and return a PIL Image object."""
77
  try:
78
- response = requests.get(url)
79
- return Image.open(BytesIO(response.content))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
80
  except Exception as e:
81
- print(f"Error downloading image: {e}")
82
  return None
83
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
84
  def create_animated_clip(image, duration=5, zoom_factor=1.1):
85
  """Create an animated clip from a still image with a zoom effect."""
86
  img_array = np.array(image)
87
  img_clip = ImageClip(img_array).set_duration(duration)
 
 
 
 
 
88
  return img_clip.resize(lambda t: 1 + (zoom_factor - 1) * t / duration)
89
 
90
  def concatenate_google_images(keywords, clip_duration=5, num_images_per_keyword=1):
91
  """Concatenate Google Images based on keywords."""
92
  keyword_list = [keyword.strip() for keyword in keywords.split(",") if keyword.strip()]
93
  if not keyword_list:
94
- keyword_list = ["nature"] # Default keyword
 
95
  video_clips = []
96
  for keyword in keyword_list:
97
  try:
@@ -102,139 +205,18 @@ def concatenate_google_images(keywords, clip_duration=5, num_images_per_keyword=
102
  if image:
103
  clip = create_animated_clip(image, duration=clip_duration)
104
  video_clips.append(clip)
 
105
  except Exception as e:
106
  print(f"Error processing keyword '{keyword}': {e}")
107
  continue
 
108
  if not video_clips:
109
- # If no images, create a black clip
110
- return ColorClip(size=(1920, 1080), color=[0, 0, 0], duration=5)
111
  random.shuffle(video_clips)
112
  return concatenate_videoclips(video_clips, method="compose")
113
 
114
- def adjust_background_music(video_duration, music_file):
115
- """Adjust background music to match video duration."""
116
- try:
117
- music = AudioFileClip(music_file)
118
- if music.duration < video_duration:
119
- repetitions = int(video_duration / music.duration) + 1
120
- music_clips = [music] * repetitions
121
- music = concatenate_audioclips(music_clips)
122
- music = music.subclip(0, video_duration)
123
- return music.volumex(0.2)
124
- except Exception as e:
125
- print(f"Error adjusting music: {e}")
126
- return None
127
-
128
- def combine_audio_video(audio_file, video_clip, music_clip=None):
129
- """Combine audio and video into a final file."""
130
- try:
131
- audio_clip = AudioFileClip(audio_file)
132
- total_duration = audio_clip.duration + 2 # Add 2 extra seconds
133
-
134
- # Ensure video has correct duration
135
- video_clip = video_clip.loop(duration=total_duration)
136
- video_clip = video_clip.set_duration(total_duration).fadeout(2)
137
-
138
- # Combine main audio
139
- final_clip = video_clip.set_audio(audio_clip)
140
- # Add background music if it exists
141
- if music_clip:
142
- music_clip = music_clip.set_duration(total_duration).audio_fadeout(2)
143
- final_clip = final_clip.set_audio(CompositeAudioClip([audio_clip, music_clip]))
144
-
145
- # Generate filename and path
146
- output_filename = f"final_video_{int(time.time())}.mp4"
147
- output_path = os.path.join(output_folder, output_filename)
148
-
149
- # Save video
150
- final_clip.write_videofile(output_path, codec="libx264", audio_codec="aac", fps=24)
151
-
152
- # Clean up clips
153
- final_clip.close()
154
- video_clip.close()
155
- audio_clip.close()
156
- if music_clip:
157
- music_clip.close()
158
-
159
- return output_path
160
- except Exception as e:
161
- print(f"Error combining audio and video: {e}")
162
- if 'final_clip' in locals():
163
- final_clip.close()
164
- return None
165
-
166
- def upload_to_google_drive(file_path, folder_id):
167
- """Upload a file to Google Drive and return the public link."""
168
- try:
169
- creds = service_account.Credentials.from_service_account_info(
170
- json.loads(os.getenv('GOOGLE_SERVICE_ACCOUNT')),
171
- scopes=['https://www.googleapis.com/auth/drive']
172
- )
173
- service = build('drive', 'v3', credentials=creds)
174
-
175
- file_metadata = {
176
- 'name': os.path.basename(file_path),
177
- 'parents': [folder_id]
178
- }
179
- media = MediaFileUpload(file_path, resumable=True)
180
- file = service.files().create(body=file_metadata, media_body=media, fields='id').execute()
181
-
182
- permission = {
183
- 'type': 'anyone',
184
- 'role': 'reader'
185
- }
186
- service.permissions().create(fileId=file['id'], body=permission).execute()
187
-
188
- file_id = file['id']
189
- download_link = f"https://drive.google.com/uc?export=download&id={file_id}"
190
- return download_link
191
- except Exception as e:
192
- print(f"Error uploading to Google Drive: {e}")
193
- return None
194
-
195
- def process_input(text, txt_file, mp3_file, selected_voice, rate, pitch, keywords):
196
- """Process user input and generate the final video."""
197
- try:
198
- # Determine the text to use
199
- if text.strip():
200
- final_text = text
201
- elif txt_file is not None:
202
- final_text = txt_file.decode("utf-8")
203
- else:
204
- raise ValueError("No text input provided")
205
-
206
- # Generate audio
207
- audio_file = asyncio.run(text_to_speech(final_text, selected_voice, rate, pitch))
208
- if not audio_file:
209
- raise ValueError("Failed to generate audio")
210
-
211
- # Generate video from Google Images
212
- video_clip = concatenate_google_images(keywords, clip_duration=5, num_images_per_keyword=1)
213
- if not video_clip:
214
- raise ValueError("Failed to generate video")
215
-
216
- # Process background music if it exists
217
- music_clip = None
218
- if mp3_file is not None:
219
- music_clip = adjust_background_music(video_clip.duration, mp3_file.name)
220
-
221
- # Combine everything
222
- final_video_path = combine_audio_video(audio_file, video_clip, music_clip)
223
- if not final_video_path:
224
- raise ValueError("Failed to combine audio and video")
225
-
226
- # Upload to Google Drive and get the link
227
- download_link = upload_to_google_drive(final_video_path, folder_id=FOLDER_ID)
228
- if download_link:
229
- print(f"Video uploaded to Google Drive. Download link: {download_link}")
230
- return f"[Download video]({download_link})"
231
- else:
232
- raise ValueError("Error uploading video to Google Drive")
233
- except Exception as e:
234
- print(f"Error during processing: {e}")
235
- return None
236
- finally:
237
- cleanup_temp_files() # Clean up temporary files when finished
238
 
239
  # Gradio interface
240
  with gr.Blocks() as demo:
@@ -245,15 +227,15 @@ with gr.Blocks() as demo:
245
  txt_file_input = gr.File(label="Or upload a .txt file", file_types=[".txt"])
246
  mp3_file_input = gr.File(label="Upload background music (.mp3)", file_types=[".mp3"])
247
  keyword_input = gr.Textbox(
248
- label="Enter keywords separated by commas (e.g., universe, galaxy, forest, cat)",
249
- 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"
250
  )
251
  voices = asyncio.run(get_voices())
252
  voice_dropdown = gr.Dropdown(choices=list(voices.keys()), label="Select Voice")
253
  rate_slider = gr.Slider(minimum=-50, maximum=50, value=0, label="Speech Rate Adjustment (%)", step=1)
254
  pitch_slider = gr.Slider(minimum=-20, maximum=20, value=0, label="Pitch Adjustment (Hz)", step=1)
255
  with gr.Column():
256
- output_link = gr.Markdown("") # Display download link
257
 
258
  btn = gr.Button("Generate Video")
259
  btn.click(
@@ -262,8 +244,5 @@ with gr.Blocks() as demo:
262
  outputs=output_link
263
  )
264
 
265
- # Read the port assigned by Hugging Face
266
  port = int(os.getenv("PORT", 7860))
267
-
268
- # Launch the application
269
  demo.launch(server_name="0.0.0.0", server_port=port, share=True, show_error=True)
 
18
  from PIL import Image
19
  import numpy as np
20
 
21
+ # Configuraci贸n de dimensiones m铆nimas para las im谩genes
22
+ MIN_WIDTH = 1920
23
+ MIN_HEIGHT = 1080
24
+ TARGET_ASPECT_RATIO = 16/9
25
+
26
  # Define output and temp directories
27
  output_folder = "outputs"
28
  temp_dir = "temp_files"
 
30
  os.makedirs(temp_dir, exist_ok=True)
31
 
32
  # Google Drive folder ID
33
+ FOLDER_ID = "12S6adpanAXjf71pKKGRRPqpzbJa5XEh3"
34
+
35
+ def load_proxies(proxy_file="proxys.txt"):
36
+ """Load proxies from a text file."""
37
+ try:
38
+ with open(proxy_file, 'r') as f:
39
+ proxies = [line.strip() for line in f if line.strip()]
40
+ print(f"Loaded {len(proxies)} proxies from file")
41
+ return [{"http": f"http://{proxy}", "https": f"http://{proxy}"} for proxy in proxies]
42
+ except Exception as e:
43
+ print(f"Error loading proxies: {e}")
44
+ return []
45
 
46
  def cleanup_temp_files():
47
  """Delete all temporary files from the temp_files folder."""
 
54
  print(f"Error deleting {file_path}: {e}")
55
 
56
  def search_google_images(query, num_images=1):
57
+ """Search for images using Google Custom Search API with size requirements."""
58
  try:
59
  api_key = os.getenv('GOOGLE_API_KEY')
60
  cse_id = os.getenv('GOOGLE_CSE_ID')
61
+ proxies = load_proxies()
62
 
 
63
  print(f"Buscando im谩genes para: {query}")
 
 
 
 
64
 
65
+ if not proxies:
66
+ print("No proxies available, trying without proxy")
67
+ proxies = [None]
 
 
 
 
 
68
 
69
+ for proxy in proxies:
70
+ try:
71
+ service = build("customsearch", "v1", developerKey=api_key)
72
+
73
+ if proxy:
74
+ session = requests.Session()
75
+ session.proxies = proxy
76
+ service._http.http = session
77
+ print(f"Trying with proxy: {proxy['http']}")
78
+
79
+ # Especificar par谩metros de b煤squeda para im谩genes grandes
80
+ result = service.cse().list(
81
+ q=query,
82
+ cx=cse_id,
83
+ searchType="image",
84
+ num=num_images * 3, # Buscar m谩s im谩genes para filtrar por tama帽o
85
+ safe='off',
86
+ imgSize='huge', # Preferir im谩genes grandes
87
+ imgType='photo', # Preferir fotos sobre dibujos
88
+ rights='cc_publicdomain|cc_attribute|cc_sharealike', # Im谩genes con licencia apropiada
89
+ ).execute()
90
+
91
+ if 'items' in result:
92
+ image_urls = []
93
+ for item in result.get('items', []):
94
+ if 'image' in item:
95
+ width = int(item['image'].get('width', 0))
96
+ height = int(item['image'].get('height', 0))
97
+ if width >= MIN_WIDTH and height >= MIN_HEIGHT:
98
+ image_urls.append(item['link'])
99
+ if len(image_urls) >= num_images:
100
+ break
101
+
102
+ print(f"Encontradas {len(image_urls)} im谩genes de tama帽o adecuado")
103
+ return image_urls
104
+
105
+ time.sleep(1)
106
+
107
+ except Exception as e:
108
+ print(f"Error con proxy {proxy}: {str(e)}")
109
+ continue
110
+
111
+ print("No se encontraron im谩genes despu茅s de probar todos los proxies")
112
+ return []
113
 
114
  except Exception as e:
115
+ print(f"Error general en la b煤squeda de im谩genes: {str(e)}")
116
  return []
117
 
118
+ def process_image(image):
119
+ """Process and resize image to meet minimum requirements."""
120
  try:
121
+ # Obtener dimensiones actuales
122
+ width, height = image.size
123
+
124
+ # Calcular relaci贸n de aspecto actual
125
+ current_ratio = width / height
126
+
127
+ # Determinar nuevas dimensiones manteniendo relaci贸n 16:9
128
+ if current_ratio > TARGET_ASPECT_RATIO:
129
+ # Imagen m谩s ancha que 16:9
130
+ new_width = max(MIN_WIDTH, width)
131
+ new_height = int(new_width / TARGET_ASPECT_RATIO)
132
+ else:
133
+ # Imagen m谩s alta que 16:9
134
+ new_height = max(MIN_HEIGHT, height)
135
+ new_width = int(new_height * TARGET_ASPECT_RATIO)
136
+
137
+ # Redimensionar imagen
138
+ image = image.resize((new_width, new_height), Image.Resampling.LANCZOS)
139
+
140
+ # Crear fondo negro del tama帽o objetivo
141
+ background = Image.new('RGB', (MAX(new_width, MIN_WIDTH), MAX(new_height, MIN_HEIGHT)), 'black')
142
+
143
+ # Centrar imagen en el fondo
144
+ offset = ((background.width - image.width) // 2,
145
+ (background.height - image.height) // 2)
146
+ background.paste(image, offset)
147
+
148
+ return background
149
+
150
  except Exception as e:
151
+ print(f"Error processing image: {e}")
152
  return None
153
 
154
+ def download_image(url):
155
+ """Download and process image from URL using proxies."""
156
+ proxies = load_proxies()
157
+ if not proxies:
158
+ proxies = [None]
159
+
160
+ for proxy in proxies:
161
+ try:
162
+ response = requests.get(url, proxies=proxy, timeout=10)
163
+ image = Image.open(BytesIO(response.content))
164
+
165
+ # Convertir a RGB si es necesario
166
+ if image.mode in ('RGBA', 'LA') or (image.mode == 'P' and 'transparency' in image.info):
167
+ background = Image.new('RGB', image.size, (0, 0, 0))
168
+ background.paste(image, mask=image.split()[-1])
169
+ image = background
170
+
171
+ # Procesar imagen para cumplir requisitos de tama帽o
172
+ processed_image = process_image(image)
173
+ if processed_image:
174
+ return processed_image
175
+
176
+ except Exception as e:
177
+ print(f"Error downloading image with proxy {proxy}: {e}")
178
+ continue
179
+ return None
180
+
181
  def create_animated_clip(image, duration=5, zoom_factor=1.1):
182
  """Create an animated clip from a still image with a zoom effect."""
183
  img_array = np.array(image)
184
  img_clip = ImageClip(img_array).set_duration(duration)
185
+
186
+ # Asegurar que el clip tenga el tama帽o correcto
187
+ if img_clip.size[0] < MIN_WIDTH or img_clip.size[1] < MIN_HEIGHT:
188
+ img_clip = img_clip.resize(width=MIN_WIDTH, height=MIN_HEIGHT)
189
+
190
  return img_clip.resize(lambda t: 1 + (zoom_factor - 1) * t / duration)
191
 
192
  def concatenate_google_images(keywords, clip_duration=5, num_images_per_keyword=1):
193
  """Concatenate Google Images based on keywords."""
194
  keyword_list = [keyword.strip() for keyword in keywords.split(",") if keyword.strip()]
195
  if not keyword_list:
196
+ keyword_list = ["nature"]
197
+
198
  video_clips = []
199
  for keyword in keyword_list:
200
  try:
 
205
  if image:
206
  clip = create_animated_clip(image, duration=clip_duration)
207
  video_clips.append(clip)
208
+ time.sleep(1)
209
  except Exception as e:
210
  print(f"Error processing keyword '{keyword}': {e}")
211
  continue
212
+
213
  if not video_clips:
214
+ return ColorClip(size=(MIN_WIDTH, MIN_HEIGHT), color=[0, 0, 0], duration=5)
215
+
216
  random.shuffle(video_clips)
217
  return concatenate_videoclips(video_clips, method="compose")
218
 
219
+ [... Resto del c贸digo se mantiene igual ...]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
220
 
221
  # Gradio interface
222
  with gr.Blocks() as demo:
 
227
  txt_file_input = gr.File(label="Or upload a .txt file", file_types=[".txt"])
228
  mp3_file_input = gr.File(label="Upload background music (.mp3)", file_types=[".mp3"])
229
  keyword_input = gr.Textbox(
230
+ label="Enter keywords separated by commas",
231
+ value="fear, religion, god, demons, aliens, possession, galaxy, mysterious"
232
  )
233
  voices = asyncio.run(get_voices())
234
  voice_dropdown = gr.Dropdown(choices=list(voices.keys()), label="Select Voice")
235
  rate_slider = gr.Slider(minimum=-50, maximum=50, value=0, label="Speech Rate Adjustment (%)", step=1)
236
  pitch_slider = gr.Slider(minimum=-20, maximum=20, value=0, label="Pitch Adjustment (Hz)", step=1)
237
  with gr.Column():
238
+ output_link = gr.Markdown("")
239
 
240
  btn = gr.Button("Generate Video")
241
  btn.click(
 
244
  outputs=output_link
245
  )
246
 
 
247
  port = int(os.getenv("PORT", 7860))
 
 
248
  demo.launch(server_name="0.0.0.0", server_port=port, share=True, show_error=True)