AZLABS commited on
Commit
dffab80
·
verified ·
1 Parent(s): 5b09ba9

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +149 -62
app.py CHANGED
@@ -1,3 +1,4 @@
 
1
  import os
2
  import json
3
  import urllib.request
@@ -11,101 +12,117 @@ import uuid
11
  import time
12
  import gradio as gr
13
 
14
-
15
- # Configure logging
16
- log_dir = os.getenv('LOG_DIRECTORY', './')
17
- LOGGER_FILE_PATH = os.path.join(str(log_dir), 'utils.log')
18
 
19
  logging.basicConfig(
20
  filename=LOGGER_FILE_PATH,
21
- filemode='a',
22
- format='[%(asctime)s] [%(levelname)s] [%(filename)s] [%(lineno)s:%(funcName)s()] %(message)s',
23
- datefmt='%Y-%b-%d %H:%M:%S'
24
  )
25
- LOGGER = logging.getLogger(__name__)
26
 
27
- log_level_env = os.getenv('LOG_LEVEL', 'INFO')
28
- log_level_dict = {
29
  'DEBUG': logging.DEBUG,
30
  'INFO': logging.INFO,
31
  'WARNING': logging.WARNING,
32
  'ERROR': logging.ERROR,
33
  'CRITICAL': logging.CRITICAL
34
  }
 
35
  if log_level_env in log_level_dict:
36
  log_level = log_level_dict[log_level_env]
37
  else:
38
  log_level = log_level_dict['INFO']
39
- LOGGER.setLevel(log_level)
40
 
41
 
42
  class Text2Video:
43
- """A class to generate videos from text prompts."""
 
 
44
 
45
  def __init__(self) -> None:
46
  """
47
  Initialize the Text2Video class.
48
  """
49
- self.herc = Hercai("") # Replace with your Hercai API key if you have one
 
 
50
 
51
  def get_image(self, img_prompt: str) -> str:
52
  """
53
- Generate an image based on the provided text prompt, ensuring the text is in speech bubbles.
 
54
  Args:
55
- img_prompt (str): Text prompt for generating the image.
 
56
  Returns:
57
- str: URL of the generated image.
58
  """
 
59
  try:
60
- LOGGER.info(f"Generating image with prompt: {img_prompt}")
61
- # Generate image using Hercai
62
- # Modified prompt to include comic-style elements
63
- modified_prompt = f"A comic book style image with speech bubbles containing the following text: '{img_prompt}'. Include vibrant colors and onomatopoeia where appropriate."
 
64
 
 
65
  image_result = self.herc.draw_image(model="simurg", prompt=modified_prompt, negative_prompt="Dark and gloomy")
 
66
  image_url = image_result["url"]
67
 
 
68
  LOGGER.info(f"Image generated successfully: {image_url}")
69
  return image_url
70
 
71
  except Exception as e:
72
  # Log any errors encountered during image generation
73
- LOGGER.error(f"Error generating image: {e}")
74
  return ""
75
 
76
-
77
  def download_img_from_url(self, image_url: str, image_path: str) -> str:
78
  """
79
- Download an image from a URL.
 
80
  Args:
81
- image_url (str): URL of the image to download.
82
- image_path (str): Path to save the downloaded image.
 
83
  Returns:
84
- str: Path of the downloaded image.
85
  """
 
86
  try:
87
- LOGGER.info(f"Downloading image from: {image_url}")
88
- # Download the image from the provided URL and save it to the specified path
89
  urllib.request.urlretrieve(image_url, image_path)
 
90
  LOGGER.info(f"Image downloaded and saved to: {image_path}")
91
  return image_path
92
 
93
  except Exception as e:
94
  # Log any errors encountered during image download
95
- LOGGER.error(f"Error downloading image from URL: {e}")
96
  return ""
97
 
98
  def text_to_audio(self, img_prompt: str, audio_path: str) -> str:
99
  """
100
- Convert text to speech and save it as an audio file.
 
101
  Args:
102
- img_prompt (str): Text to convert to speech.
103
- audio_path (str): Path to save the audio file.
 
104
  Returns:
105
- str: Path of the saved audio file.
106
  """
 
107
  try:
108
- LOGGER.info(f"Converting text to audio: {img_prompt}")
109
  language = 'en'
110
 
111
  # Create a gTTS object to convert text to speech
@@ -113,117 +130,187 @@ class Text2Video:
113
 
114
  # Save the audio file at the specified path
115
  myobj.save(audio_path)
 
116
  LOGGER.info(f"Audio saved to: {audio_path}")
117
  return audio_path
118
  except Exception as e:
119
  # Log any errors encountered during text-to-audio conversion
120
- LOGGER.error(f"Error converting text to audio: {e}")
121
  return ""
122
 
123
  def get_images_and_audio(self, list_prompts: list) -> tuple:
124
  """
125
- Generate images and corresponding audio files from a list of prompts.
 
126
  Args:
127
- list_prompts (list): List of text prompts.
 
128
  Returns:
129
- tuple: A tuple containing lists of image paths and audio paths.
130
  """
131
- img_list = []
132
- audio_paths = []
 
133
  for img_prompt in list_prompts:
 
134
  try:
135
- LOGGER.info(f"Processing prompt: {img_prompt}")
136
  unique_id = uuid.uuid4().hex
 
 
137
  image_path = f"{img_prompt[:9]}_{unique_id}.png"
 
 
138
  img_url = self.get_image(img_prompt)
 
 
139
  image = self.download_img_from_url(img_url, image_path)
 
 
140
  img_list.append(image)
 
 
141
  audio_path = f"{img_prompt[:9]}_{unique_id}.mp3"
 
 
142
  audio = self.text_to_audio(img_prompt, audio_path)
 
 
143
  audio_paths.append(audio)
 
144
  except Exception as e:
145
- LOGGER.error(f"Error processing prompt: {img_prompt}, {e}")
 
146
 
 
 
147
  return img_list, audio_paths
148
 
149
  def create_video_from_images_and_audio(self, image_files: list, audio_files: list, output_path: str) -> None:
150
  """
151
- Create a video from images and corresponding audio files.
 
152
  Args:
153
- image_files (list): List of image files.
154
- audio_files (list): List of audio files.
155
- output_path (str): Path to save the output video file.
156
  """
 
157
  try:
158
- LOGGER.info(f"Creating video from images and audio, output path: {output_path}")
159
  if len(image_files) != len(audio_files):
 
160
  LOGGER.error("Error: Number of images doesn't match the number of audio files.")
161
  return
162
 
 
163
  video_clips = []
 
 
164
  for image_file, audio_file in zip(image_files, audio_files):
165
- LOGGER.info(f"Processing image: {image_file} and audio: {audio_file}")
 
 
166
  frame = cv2.imread(image_file)
 
 
167
  audio_clip = mp.AudioFileClip(audio_file)
 
 
168
  video_clip = mp.ImageClip(image_file).set_duration(audio_clip.duration)
 
 
169
  video_clip = video_clip.set_audio(audio_clip)
 
 
170
  video_clips.append(video_clip)
171
 
 
172
  final_clip = mp.concatenate_videoclips(video_clips)
 
 
173
  final_clip.write_videofile(output_path, codec='libx264', fps=24)
174
- LOGGER.info("Video created successfully.")
 
175
 
176
  except Exception as e:
177
  # Log any errors encountered during video creation
178
  LOGGER.error(f"Error creating video: {e}")
179
 
 
180
  def generate_video(self, text: str) -> str:
181
  """
182
- Generate a video from a list of text prompts.
 
183
  Args:
184
- text (str): Text prompts separated by double commas.
 
185
  Returns:
186
- str: Path to the generated video.
187
  """
 
188
  try:
189
- LOGGER.info(f"Generating video from text: {text}")
190
  list_prompts = [sentence.strip() for sentence in text.split(",,") if sentence.strip()]
 
191
 
 
192
  output_path = "output_video.mp4"
193
 
 
194
  img_list, audio_paths = self.get_images_and_audio(list_prompts)
 
 
195
  self.create_video_from_images_and_audio(img_list, audio_paths, output_path)
 
196
  LOGGER.info(f"Video generated successfully: {output_path}")
197
  return output_path
 
198
  except Exception as e:
199
- LOGGER.error(f"Error generating video: {e}")
 
200
  return ""
201
 
 
202
  def gradio_interface(self):
 
 
 
 
203
  with gr.Blocks(css="style.css", theme='abidlabs/dracula_revamped') as demo:
204
- example_txt = """once upon a time there was a village. It was a nice place to live, except for one thing. people did not like to share.,, One day a visitor came to town.
205
- 'Hello. Does anybody have food to share?' He asked. 'No', said everyone.,,
206
- That's okay', said the visitor. 'I will make stone soup for everyone'.Then he took a stone and dropped it into a giant pot,,"""
207
-
208
  gr.HTML("""
209
- <center><h1 style="color:#fff">Comics Video Generator</h1></center>""")
210
 
 
211
  with gr.Row(elem_id="col-container"):
212
- input_text = gr.Textbox(label="Comics Text", placeholder="Enter the comics by double comma separated")
 
213
 
 
214
  with gr.Row(elem_id="col-container"):
215
  button = gr.Button("Generate Video")
216
 
 
217
  with gr.Row(elem_id="col-container"):
218
  output = gr.PlayableVideo()
219
 
 
220
  with gr.Row(elem_id="col-container"):
 
 
 
221
  example = gr.Examples([example_txt], input_text)
222
 
 
223
  button.click(self.generate_video, [input_text], output)
 
 
 
224
  demo.launch(debug=True)
225
 
226
 
227
  if __name__ == "__main__":
228
- text2video = Text2Video()
229
- text2video.gradio_interface()
 
 
1
+
2
  import os
3
  import json
4
  import urllib.request
 
12
  import time
13
  import gradio as gr
14
 
15
+ # Configure detailed logging
16
+ log_dir = os.getenv('LOG_DIRECTORY', './') # Get log directory from environment variable, default to current directory
17
+ LOGGER_FILE_PATH = os.path.join(str(log_dir), 'utils.log') # Construct the full path to the log file
 
18
 
19
  logging.basicConfig(
20
  filename=LOGGER_FILE_PATH,
21
+ filemode='a', # Append to the log file
22
+ format='[%(asctime)s] [%(levelname)s] [%(filename)s] [%(lineno)s:%(funcName)s()] %(message)s', # Log format
23
+ datefmt='%Y-%b-%d %H:%M:%S' # Date and time format
24
  )
25
+ LOGGER = logging.getLogger(__name__) # Get the logger instance
26
 
27
+ log_level_env = os.getenv('LOG_LEVEL', 'INFO') # Get log level from environment variable, default to INFO
28
+ log_level_dict = { # Dictionary mapping log level names to their corresponding numerical values
29
  'DEBUG': logging.DEBUG,
30
  'INFO': logging.INFO,
31
  'WARNING': logging.WARNING,
32
  'ERROR': logging.ERROR,
33
  'CRITICAL': logging.CRITICAL
34
  }
35
+ # Set the log level based on the environment variable or default to INFO
36
  if log_level_env in log_level_dict:
37
  log_level = log_level_dict[log_level_env]
38
  else:
39
  log_level = log_level_dict['INFO']
40
+ LOGGER.setLevel(log_level) # Set the log level for the logger instance
41
 
42
 
43
  class Text2Video:
44
+ """
45
+ A class to generate videos from text prompts, with detailed logging and a user-friendly interface.
46
+ """
47
 
48
  def __init__(self) -> None:
49
  """
50
  Initialize the Text2Video class.
51
  """
52
+ LOGGER.info("Initializing Text2Video class")
53
+ self.herc = Hercai("") # Replace "" with your actual Hercai API key if you have one
54
+ LOGGER.info("Hercai initialized successfully")
55
 
56
  def get_image(self, img_prompt: str) -> str:
57
  """
58
+ Generate an image from a text prompt using Hercai, with detailed logging and comic book styling.
59
+
60
  Args:
61
+ img_prompt (str): The text prompt to generate the image from.
62
+
63
  Returns:
64
+ str: The URL of the generated image. Returns an empty string if an error occurred.
65
  """
66
+ LOGGER.info(f"Generating image for prompt: {img_prompt}")
67
  try:
68
+ # Create a comic book style prompt
69
+ modified_prompt = f"Generate a comic book style image with speech bubbles containing the following text: '{img_prompt}'. " \
70
+ f"Include elements like vibrant colors, onomatopoeia, and exaggerated expressions to enhance the comic book aesthetic."
71
+ # Log the modified prompt
72
+ LOGGER.info(f"Modified prompt for Hercai: {modified_prompt}")
73
 
74
+ # Generate the image using Hercai
75
  image_result = self.herc.draw_image(model="simurg", prompt=modified_prompt, negative_prompt="Dark and gloomy")
76
+ # Extract the image URL from the result
77
  image_url = image_result["url"]
78
 
79
+ # Log the generated image URL
80
  LOGGER.info(f"Image generated successfully: {image_url}")
81
  return image_url
82
 
83
  except Exception as e:
84
  # Log any errors encountered during image generation
85
+ LOGGER.error(f"Error generating image for prompt '{img_prompt}': {e}")
86
  return ""
87
 
 
88
  def download_img_from_url(self, image_url: str, image_path: str) -> str:
89
  """
90
+ Download an image from a URL to a local file path.
91
+
92
  Args:
93
+ image_url (str): The URL of the image to download.
94
+ image_path (str): The local file path to save the downloaded image.
95
+
96
  Returns:
97
+ str: The local file path of the downloaded image. Returns an empty string if an error occurred.
98
  """
99
+ LOGGER.info(f"Downloading image from URL: {image_url}")
100
  try:
101
+ # Download the image from the URL and save it to the specified path
 
102
  urllib.request.urlretrieve(image_url, image_path)
103
+
104
  LOGGER.info(f"Image downloaded and saved to: {image_path}")
105
  return image_path
106
 
107
  except Exception as e:
108
  # Log any errors encountered during image download
109
+ LOGGER.error(f"Error downloading image from URL '{image_url}': {e}")
110
  return ""
111
 
112
  def text_to_audio(self, img_prompt: str, audio_path: str) -> str:
113
  """
114
+ Convert text to speech using gTTS and save it as an audio file.
115
+
116
  Args:
117
+ img_prompt (str): The text to convert to speech.
118
+ audio_path (str): The local file path to save the generated audio file.
119
+
120
  Returns:
121
+ str: The local file path of the saved audio file. Returns an empty string if an error occurred.
122
  """
123
+ LOGGER.info(f"Converting text to audio: {img_prompt}")
124
  try:
125
+ # Set the language for speech synthesis (English in this case)
126
  language = 'en'
127
 
128
  # Create a gTTS object to convert text to speech
 
130
 
131
  # Save the audio file at the specified path
132
  myobj.save(audio_path)
133
+
134
  LOGGER.info(f"Audio saved to: {audio_path}")
135
  return audio_path
136
  except Exception as e:
137
  # Log any errors encountered during text-to-audio conversion
138
+ LOGGER.error(f"Error converting text '{img_prompt}' to audio: {e}")
139
  return ""
140
 
141
  def get_images_and_audio(self, list_prompts: list) -> tuple:
142
  """
143
+ Generate images and corresponding audio files for a list of text prompts.
144
+
145
  Args:
146
+ list_prompts (list): A list of text prompts.
147
+
148
  Returns:
149
+ tuple: A tuple containing two lists: image paths and audio paths.
150
  """
151
+ LOGGER.info("Generating images and audio for prompts")
152
+ img_list = [] # List to store image paths
153
+ audio_paths = [] # List to store audio paths
154
  for img_prompt in list_prompts:
155
+ LOGGER.info(f"Processing prompt: {img_prompt}")
156
  try:
157
+ # Generate a unique identifier for the image and audio files
158
  unique_id = uuid.uuid4().hex
159
+
160
+ # Construct the image path using the unique identifier
161
  image_path = f"{img_prompt[:9]}_{unique_id}.png"
162
+
163
+ # Generate the image URL using Hercai
164
  img_url = self.get_image(img_prompt)
165
+
166
+ # Download the image from the generated URL
167
  image = self.download_img_from_url(img_url, image_path)
168
+
169
+ # Add the image path to the list
170
  img_list.append(image)
171
+
172
+ # Construct the audio path using the unique identifier
173
  audio_path = f"{img_prompt[:9]}_{unique_id}.mp3"
174
+
175
+ # Convert the text to audio and save it
176
  audio = self.text_to_audio(img_prompt, audio_path)
177
+
178
+ # Add the audio path to the list
179
  audio_paths.append(audio)
180
+
181
  except Exception as e:
182
+ # Log any errors encountered during the process
183
+ LOGGER.error(f"Error processing prompt '{img_prompt}': {e}")
184
 
185
+ # Return the lists of image paths and audio paths
186
+ LOGGER.info("Images and audio generated successfully")
187
  return img_list, audio_paths
188
 
189
  def create_video_from_images_and_audio(self, image_files: list, audio_files: list, output_path: str) -> None:
190
  """
191
+ Generate a video from a list of image files and corresponding audio files.
192
+
193
  Args:
194
+ image_files (list): A list of local file paths to image files.
195
+ audio_files (list): A list of local file paths to audio files.
196
+ output_path (str): The local file path where the generated video will be saved.
197
  """
198
+ LOGGER.info("Creating video from images and audio")
199
  try:
200
+ # Check if the number of images and audio files match
201
  if len(image_files) != len(audio_files):
202
+ # Log an error if the number of image files and audio files don't match
203
  LOGGER.error("Error: Number of images doesn't match the number of audio files.")
204
  return
205
 
206
+ # Create an empty list to store video clips
207
  video_clips = []
208
+
209
+ # Loop through each image file and corresponding audio file
210
  for image_file, audio_file in zip(image_files, audio_files):
211
+ LOGGER.info(f"Processing image: {image_file}, audio: {audio_file}")
212
+
213
+ # Read the image file using OpenCV
214
  frame = cv2.imread(image_file)
215
+
216
+ # Load the audio clip using MoviePy
217
  audio_clip = mp.AudioFileClip(audio_file)
218
+
219
+ # Create a video clip from the image and set its duration to the audio clip's duration
220
  video_clip = mp.ImageClip(image_file).set_duration(audio_clip.duration)
221
+
222
+ # Set the audio for the video clip
223
  video_clip = video_clip.set_audio(audio_clip)
224
+
225
+ # Append the video clip to the list of video clips
226
  video_clips.append(video_clip)
227
 
228
+ # Concatenate all the video clips into a single video clip
229
  final_clip = mp.concatenate_videoclips(video_clips)
230
+
231
+ # Write the final video clip to a file using the specified output path
232
  final_clip.write_videofile(output_path, codec='libx264', fps=24)
233
+
234
+ LOGGER.info(f"Video created successfully at: {output_path}")
235
 
236
  except Exception as e:
237
  # Log any errors encountered during video creation
238
  LOGGER.error(f"Error creating video: {e}")
239
 
240
+
241
  def generate_video(self, text: str) -> str:
242
  """
243
+ Generate a video from a comma-separated string of text prompts
244
+
245
  Args:
246
+ text (str): A comma-separated string of text prompts, where each prompt represents a scene or frame in the video.
247
+
248
  Returns:
249
+ str: The file path of the generated video file. Returns an empty string if an error occurred.
250
  """
251
+ LOGGER.info("Generating video from text")
252
  try:
253
+ # Split the input text into a list of prompts
254
  list_prompts = [sentence.strip() for sentence in text.split(",,") if sentence.strip()]
255
+ LOGGER.info(f"Prompts extracted from text: {list_prompts}")
256
 
257
+ # Define the output path for the generated video
258
  output_path = "output_video.mp4"
259
 
260
+ # Generate images and corresponding audio files for each prompt
261
  img_list, audio_paths = self.get_images_and_audio(list_prompts)
262
+
263
+ # Create the video from the generated images and audio files
264
  self.create_video_from_images_and_audio(img_list, audio_paths, output_path)
265
+
266
  LOGGER.info(f"Video generated successfully: {output_path}")
267
  return output_path
268
+
269
  except Exception as e:
270
+ # Log any errors encountered during video generation
271
+ LOGGER.error(f"Error generating video from text '{text}': {e}")
272
  return ""
273
 
274
+
275
  def gradio_interface(self):
276
+ """
277
+ Creates a user-friendly Gradio interface for the video generation application.
278
+ """
279
+ LOGGER.info("Launching Gradio interface")
280
  with gr.Blocks(css="style.css", theme='abidlabs/dracula_revamped') as demo:
281
+ # Set the title of the application
 
 
 
282
  gr.HTML("""
283
+ <center><h1 style="color:#fff">Comics Video Generator</h1></center>""")
284
 
285
+ # Create a text box for user input, allowing them to enter comic book text
286
  with gr.Row(elem_id="col-container"):
287
+ input_text = gr.Textbox(label="Comics Text",
288
+ placeholder="Enter the comics text, separating scenes with double commas (,,)")
289
 
290
+ # Create a button that triggers the video generation process
291
  with gr.Row(elem_id="col-container"):
292
  button = gr.Button("Generate Video")
293
 
294
+ # Create a component to display the generated video
295
  with gr.Row(elem_id="col-container"):
296
  output = gr.PlayableVideo()
297
 
298
+ # Provide an example to guide users on how to format their input
299
  with gr.Row(elem_id="col-container"):
300
+ example_txt = """Once upon a time there was a village. It was a nice place to live, except for one thing. People did not like to share.,,
301
+ One day a visitor came to town. 'Hello. Does anybody have food to share?' He asked. 'No', said everyone.,,
302
+ 'That's okay', said the visitor. 'I will make stone soup for everyone'. Then he took a stone and dropped it into a giant pot,,"""
303
  example = gr.Examples([example_txt], input_text)
304
 
305
+ # Define the button's click event to call the generate_video function with the user's input
306
  button.click(self.generate_video, [input_text], output)
307
+
308
+ LOGGER.info("Gradio interface launched successfully")
309
+ # Launch the Gradio interface
310
  demo.launch(debug=True)
311
 
312
 
313
  if __name__ == "__main__":
314
+ LOGGER.info("Starting application")
315
+ text2video = Text2Video() # Create an instance of the Text2Video class
316
+ text2video.gradio_interface() # Launch the Gradio interface