gnosticdev commited on
Commit
fb7a4e5
·
verified ·
1 Parent(s): b0c9ed1

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +244 -0
app.py ADDED
@@ -0,0 +1,244 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ from tts_module import get_voices, text_to_speech
3
+ from moviepy.editor import (
4
+ AudioFileClip, ImageClip, CompositeAudioClip,
5
+ concatenate_audioclips, concatenate_videoclips, vfx, CompositeVideoClip,
6
+ ColorClip
7
+ )
8
+ import asyncio
9
+ import os
10
+ import json
11
+ import time
12
+ import requests
13
+ import random
14
+ from googleapiclient.discovery import build
15
+ from google.oauth2 import service_account
16
+ from googleapiclient.http import MediaFileUpload
17
+ 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"
24
+ 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."""
32
+ for filename in os.listdir(temp_dir):
33
+ file_path = os.path.join(temp_dir, filename)
34
+ try:
35
+ if os.path.isfile(file_path):
36
+ os.remove(file_path)
37
+ except Exception as e:
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
+ service = build("customsearch", "v1", developerKey=os.getenv('GOOGLE_API_KEY'))
44
+ result = service.cse().list(q=query, cx=os.getenv('GOOGLE_CSE_ID'), searchType="image", num=num_images).execute()
45
+ return [item['link'] for item in result.get('items', [])]
46
+ except Exception as e:
47
+ print(f"Error searching Google Images: {e}")
48
+ return []
49
+
50
+ def download_image(url):
51
+ """Download an image from a URL and return a PIL Image object."""
52
+ try:
53
+ response = requests.get(url)
54
+ return Image.open(BytesIO(response.content))
55
+ except Exception as e:
56
+ print(f"Error downloading image: {e}")
57
+ return None
58
+
59
+ def create_animated_clip(image, duration=5, zoom_factor=1.1):
60
+ """Create an animated clip from a still image with a zoom effect."""
61
+ img_array = np.array(image)
62
+ img_clip = ImageClip(img_array).set_duration(duration)
63
+ return img_clip.resize(lambda t: 1 + (zoom_factor - 1) * t / duration)
64
+
65
+ def concatenate_google_images(keywords, clip_duration=5, num_images_per_keyword=1):
66
+ """Concatenate Google Images based on keywords."""
67
+ keyword_list = [keyword.strip() for keyword in keywords.split(",") if keyword.strip()]
68
+ if not keyword_list:
69
+ keyword_list = ["nature"] # Default keyword
70
+ video_clips = []
71
+ for keyword in keyword_list:
72
+ try:
73
+ print(f"Searching images for keyword '{keyword}'...")
74
+ image_urls = search_google_images(keyword, num_images=num_images_per_keyword)
75
+ for url in image_urls:
76
+ image = download_image(url)
77
+ if image:
78
+ clip = create_animated_clip(image, duration=clip_duration)
79
+ video_clips.append(clip)
80
+ except Exception as e:
81
+ print(f"Error processing keyword '{keyword}': {e}")
82
+ continue
83
+ if not video_clips:
84
+ # If no images, create a black clip
85
+ return ColorClip(size=(1920, 1080), color=[0, 0, 0], duration=5)
86
+ random.shuffle(video_clips)
87
+ return concatenate_videoclips(video_clips, method="compose")
88
+
89
+ def adjust_background_music(video_duration, music_file):
90
+ """Adjust background music to match video duration."""
91
+ try:
92
+ music = AudioFileClip(music_file)
93
+ if music.duration < video_duration:
94
+ repetitions = int(video_duration / music.duration) + 1
95
+ music_clips = [music] * repetitions
96
+ music = concatenate_audioclips(music_clips)
97
+ music = music.subclip(0, video_duration)
98
+ return music.volumex(0.2)
99
+ except Exception as e:
100
+ print(f"Error adjusting music: {e}")
101
+ return None
102
+
103
+ def combine_audio_video(audio_file, video_clip, music_clip=None):
104
+ """Combine audio and video into a final file."""
105
+ try:
106
+ audio_clip = AudioFileClip(audio_file)
107
+ total_duration = audio_clip.duration + 2 # Add 2 extra seconds
108
+
109
+ # Ensure video has correct duration
110
+ video_clip = video_clip.loop(duration=total_duration)
111
+ video_clip = video_clip.set_duration(total_duration).fadeout(2)
112
+
113
+ # Combine main audio
114
+ final_clip = video_clip.set_audio(audio_clip)
115
+ # Add background music if it exists
116
+ if music_clip:
117
+ music_clip = music_clip.set_duration(total_duration).audio_fadeout(2)
118
+ final_clip = final_clip.set_audio(CompositeAudioClip([audio_clip, music_clip]))
119
+
120
+ # Generate filename and path
121
+ output_filename = f"final_video_{int(time.time())}.mp4"
122
+ output_path = os.path.join(output_folder, output_filename)
123
+
124
+ # Save video
125
+ final_clip.write_videofile(output_path, codec="libx264", audio_codec="aac", fps=24)
126
+
127
+ # Clean up clips
128
+ final_clip.close()
129
+ video_clip.close()
130
+ audio_clip.close()
131
+ if music_clip:
132
+ music_clip.close()
133
+
134
+ return output_path
135
+ except Exception as e:
136
+ print(f"Error combining audio and video: {e}")
137
+ if 'final_clip' in locals():
138
+ final_clip.close()
139
+ return None
140
+
141
+ def upload_to_google_drive(file_path, folder_id):
142
+ """Upload a file to Google Drive and return the public link."""
143
+ try:
144
+ creds = service_account.Credentials.from_service_account_info(
145
+ json.loads(os.getenv('GOOGLE_SERVICE_ACCOUNT')),
146
+ scopes=['https://www.googleapis.com/auth/drive']
147
+ )
148
+ service = build('drive', 'v3', credentials=creds)
149
+
150
+ file_metadata = {
151
+ 'name': os.path.basename(file_path),
152
+ 'parents': [folder_id]
153
+ }
154
+ media = MediaFileUpload(file_path, resumable=True)
155
+ file = service.files().create(body=file_metadata, media_body=media, fields='id').execute()
156
+
157
+ permission = {
158
+ 'type': 'anyone',
159
+ 'role': 'reader'
160
+ }
161
+ service.permissions().create(fileId=file['id'], body=permission).execute()
162
+
163
+ file_id = file['id']
164
+ download_link = f"https://drive.google.com/uc?export=download&id={file_id}"
165
+ return download_link
166
+ except Exception as e:
167
+ print(f"Error uploading to Google Drive: {e}")
168
+ return None
169
+
170
+ def process_input(text, txt_file, mp3_file, selected_voice, rate, pitch, keywords):
171
+ """Process user input and generate the final video."""
172
+ try:
173
+ # Determine the text to use
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
+
181
+ # Generate audio
182
+ audio_file = asyncio.run(text_to_speech(final_text, selected_voice, rate, pitch))
183
+ if not audio_file:
184
+ raise ValueError("Failed to generate audio")
185
+
186
+ # Generate video from Google Images
187
+ video_clip = concatenate_google_images(keywords, clip_duration=5, num_images_per_keyword=1)
188
+ if not video_clip:
189
+ raise ValueError("Failed to generate video")
190
+
191
+ # Process background music if it exists
192
+ music_clip = None
193
+ if mp3_file is not None:
194
+ music_clip = adjust_background_music(video_clip.duration, mp3_file.name)
195
+
196
+ # Combine everything
197
+ final_video_path = combine_audio_video(audio_file, video_clip, music_clip)
198
+ if not final_video_path:
199
+ raise ValueError("Failed to combine audio and video")
200
+
201
+ # Upload to Google Drive and get the link
202
+ download_link = upload_to_google_drive(final_video_path, folder_id=FOLDER_ID)
203
+ if download_link:
204
+ print(f"Video uploaded to Google Drive. Download link: {download_link}")
205
+ return f"[Download video]({download_link})"
206
+ else:
207
+ raise ValueError("Error uploading video to Google Drive")
208
+ except Exception as e:
209
+ print(f"Error during processing: {e}")
210
+ return None
211
+ finally:
212
+ cleanup_temp_files() # Clean up temporary files when finished
213
+
214
+ # Gradio interface
215
+ with gr.Blocks() as demo:
216
+ gr.Markdown("# Text-to-Video Generator")
217
+ with gr.Row():
218
+ with gr.Column():
219
+ text_input = gr.Textbox(label="Write your text here", lines=5)
220
+ txt_file_input = gr.File(label="Or upload a .txt file", file_types=[".txt"])
221
+ mp3_file_input = gr.File(label="Upload background music (.mp3)", file_types=[".mp3"])
222
+ keyword_input = gr.Textbox(
223
+ label="Enter keywords separated by commas (e.g., universe, galaxy, forest, cat)",
224
+ 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"
225
+ )
226
+ voices = asyncio.run(get_voices())
227
+ voice_dropdown = gr.Dropdown(choices=list(voices.keys()), label="Select Voice")
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("") # Display download link
232
+
233
+ btn = gr.Button("Generate Video")
234
+ btn.click(
235
+ process_input,
236
+ inputs=[text_input, txt_file_input, mp3_file_input, voice_dropdown, rate_slider, pitch_slider, keyword_input],
237
+ outputs=output_link
238
+ )
239
+
240
+ # Read the port assigned by Hugging Face
241
+ port = int(os.getenv("PORT", 7860))
242
+
243
+ # Launch the application
244
+ demo.launch(server_name="0.0.0.0", server_port=port, share=True, show_error=True)