waloneai commited on
Commit
15ab247
·
verified ·
1 Parent(s): ed4d9bb

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +63 -101
app.py CHANGED
@@ -58,14 +58,12 @@ allowed_medias = [
58
  ".3gpp",
59
  ]
60
 
61
-
62
  def get_files_infos(files):
63
  results = []
64
  for file in files:
65
  file_path = Path(file.name)
66
  info = {}
67
  info["size"] = os.path.getsize(file_path)
68
- # Sanitize filename by replacing spaces with underscores
69
  info["name"] = file_path.name.replace(" ", "_")
70
  file_extension = file_path.suffix
71
 
@@ -84,39 +82,21 @@ def get_files_infos(files):
84
  info["duration"] = audio.duration
85
  info["audio_channels"] = audio.nchannels
86
  audio.close()
87
- elif file_extension in (
88
- ".png",
89
- ".jpg",
90
- ".jpeg",
91
- ".tiff",
92
- ".bmp",
93
- ".gif",
94
- ".svg",
95
- ):
96
  info["type"] = "image"
97
  img = Image.open(file.name)
98
  info["dimensions"] = "{}x{}".format(img.size[0], img.size[1])
99
  results.append(info)
100
  return results
101
 
102
-
103
  def get_completion(prompt, files_info, top_p, temperature, model_choice):
104
- # Create table header
105
  files_info_string = "| Type | Name | Dimensions | Duration | Audio Channels |\n"
106
  files_info_string += "|------|------|------------|-----------|--------|\n"
107
 
108
- # Add each file as a table row
109
  for file_info in files_info:
110
  dimensions = file_info.get("dimensions", "-")
111
- duration = (
112
- f"{file_info.get('duration', '-')}s" if "duration" in file_info else "-"
113
- )
114
- audio = (
115
- f"{file_info.get('audio_channels', '-')} channels"
116
- if "audio_channels" in file_info
117
- else "-"
118
- )
119
-
120
  files_info_string += f"| {file_info['type']} | {file_info['name']} | {dimensions} | {duration} | {audio} |\n"
121
 
122
  messages = [
@@ -161,7 +141,6 @@ YOUR FFMPEG COMMAND:
161
  },
162
  ]
163
  try:
164
- # Print the complete prompt
165
  print("\n=== COMPLETE PROMPT ===")
166
  for msg in messages:
167
  print(f"\n[{msg['role'].upper()}]:")
@@ -184,11 +163,8 @@ YOUR FFMPEG COMMAND:
184
  max_tokens=2048,
185
  )
186
  content = completion.choices[0].message.content
187
- # Extract command from code block if present
188
  if "```" in content:
189
- # Find content between ```sh or ```bash and the next ```
190
  import re
191
-
192
  command = re.search(r"```(?:sh|bash)?\n(.*?)\n```", content, re.DOTALL)
193
  if command:
194
  command = command.group(1).strip()
@@ -197,32 +173,20 @@ YOUR FFMPEG COMMAND:
197
  else:
198
  command = content.replace("\n", "")
199
 
200
- # remove output.mp4 with the actual output file path
201
  command = command.replace("output.mp4", "")
202
-
203
  return command
204
  except Exception as e:
205
  raise Exception("API Error")
206
 
207
-
208
- def update(
209
- files,
210
- prompt,
211
- top_p=1,
212
- temperature=1,
213
- model_choice="Qwen/Qwen2.5-Coder-32B-Instruct",
214
- ):
215
  if prompt == "":
216
  raise gr.Error("Please enter a prompt.")
217
 
218
  files_info = get_files_infos(files)
219
- # disable this if you're running the app locally or on your own server
220
  for file_info in files_info:
221
  if file_info["type"] == "video":
222
  if file_info["duration"] > 120:
223
- raise gr.Error(
224
- "Please make sure all videos are less than 2 minute long."
225
- )
226
  if file_info["size"] > 100000000:
227
  raise gr.Error("Please make sure all files are less than 100MB in size.")
228
 
@@ -230,25 +194,18 @@ def update(
230
  while attempts < 2:
231
  print("ATTEMPT", attempts)
232
  try:
233
- command_string = get_completion(
234
- prompt, files_info, top_p, temperature, model_choice
235
- )
236
- print(
237
- f"""///PROMTP {prompt} \n\n/// START OF COMMAND ///:\n\n{command_string}\n\n/// END OF COMMAND ///\n\n"""
238
- )
239
 
240
- # split command string into list of arguments
241
  args = shlex.split(command_string)
242
  if args[0] != "ffmpeg":
243
  raise Exception("Command does not start with ffmpeg")
244
  temp_dir = tempfile.mkdtemp()
245
- # copy files to temp dir with sanitized names
246
  for file in files:
247
  file_path = Path(file.name)
248
  sanitized_name = file_path.name.replace(" ", "_")
249
  shutil.copy(file_path, Path(temp_dir) / sanitized_name)
250
 
251
- # test if ffmpeg command is valid dry run
252
  ffmpg_dry_run = subprocess.run(
253
  args + ["-f", "null", "-"],
254
  stderr=subprocess.PIPE,
@@ -260,16 +217,12 @@ def update(
260
  else:
261
  print("Command is not valid. Error output:")
262
  print(ffmpg_dry_run.stderr)
263
- raise Exception(
264
- "FFMPEG generated command is not valid. Please try something else."
265
- )
266
 
267
  output_file_name = f"output_{uuid.uuid4()}.mp4"
268
  output_file_path = str((Path(temp_dir) / output_file_name).resolve())
269
  final_command = args + ["-y", output_file_path]
270
- print(
271
- f"\n=== EXECUTING FFMPEG COMMAND ===\nffmpeg {' '.join(final_command[1:])}\n"
272
- )
273
  subprocess.run(final_command, cwd=temp_dir)
274
  generated_command = f"### Generated Command\n```bash\nffmpeg {' '.join(args[1:])} -y output.mp4\n```"
275
  return output_file_path, gr.update(value=generated_command)
@@ -279,22 +232,28 @@ def update(
279
  print("FROM UPDATE", e)
280
  raise gr.Error(e)
281
 
282
-
283
- # Custom login system
284
  def check_password(password):
285
  return password == PASSWORD
286
 
 
 
 
 
 
 
 
 
 
 
 
287
 
288
- # Gradio app with password protection
289
- with gr.Blocks() as demo:
290
- # Login interface (visible by default)
291
  with gr.Row(visible=True) as login_interface:
292
  with gr.Column():
293
  password_input = gr.Textbox(label="Enter Password", type="password")
294
  login_button = gr.Button("Login")
295
  login_status = gr.Textbox(label="Login Status", interactive=False)
296
 
297
- # Main app interface (hidden by default)
298
  with gr.Row(visible=False) as main_interface:
299
  gr.Markdown(
300
  """
@@ -303,45 +262,43 @@ with gr.Blocks() as demo:
303
  """,
304
  elem_id="header",
305
  )
306
- with gr.Row():
307
- with gr.Column():
308
- user_files = gr.File(
309
- file_count="multiple",
310
- label="Media files",
311
- file_types=allowed_medias,
 
 
 
 
 
 
 
 
 
 
312
  )
313
- user_prompt = gr.Textbox(
314
- placeholder="eg: Remove the 3 first seconds of the video",
315
- label="Instructions",
 
 
 
 
316
  )
317
- btn = gr.Button("Run")
318
- with gr.Accordion("Parameters", open=False):
319
- model_choice = gr.Radio(
320
- choices=list(MODELS.keys()),
321
- value=list(MODELS.keys())[0],
322
- label="Model",
323
- )
324
- top_p = gr.Slider(
325
- minimum=-0,
326
- maximum=1.0,
327
- value=0.7,
328
- step=0.05,
329
- interactive=True,
330
- label="Top-p (nucleus sampling)",
331
- )
332
- temperature = gr.Slider(
333
- minimum=-0,
334
- maximum=5.0,
335
- value=0.1,
336
- step=0.1,
337
- interactive=True,
338
- label="Temperature",
339
- )
340
- with gr.Column():
341
- generated_video = gr.Video(
342
- interactive=False, label="Generated Video", include_audio=True
343
  )
344
- generated_command = gr.Markdown()
 
 
 
345
 
346
  btn.click(
347
  fn=update,
@@ -396,9 +353,15 @@ with gr.Blocks() as demo:
396
  cache_examples=False,
397
  )
398
 
399
-
 
 
 
 
 
 
 
400
 
401
- # Show/hide main interface based on login status
402
  def toggle_interface(password):
403
  if check_password(password):
404
  return gr.update(visible=False), gr.update(visible=True), "Login successful!"
@@ -411,6 +374,5 @@ with gr.Blocks() as demo:
411
  outputs=[login_interface, main_interface, login_status],
412
  )
413
 
414
- # Launch the app with embedding enabled
415
  demo.queue(default_concurrency_limit=200)
416
  demo.launch(show_api=False, ssr_mode=False, share=True, inline=True)
 
58
  ".3gpp",
59
  ]
60
 
 
61
  def get_files_infos(files):
62
  results = []
63
  for file in files:
64
  file_path = Path(file.name)
65
  info = {}
66
  info["size"] = os.path.getsize(file_path)
 
67
  info["name"] = file_path.name.replace(" ", "_")
68
  file_extension = file_path.suffix
69
 
 
82
  info["duration"] = audio.duration
83
  info["audio_channels"] = audio.nchannels
84
  audio.close()
85
+ elif file_extension in (".png", ".jpg", ".jpeg", ".tiff", ".bmp", ".gif", ".svg"):
 
 
 
 
 
 
 
 
86
  info["type"] = "image"
87
  img = Image.open(file.name)
88
  info["dimensions"] = "{}x{}".format(img.size[0], img.size[1])
89
  results.append(info)
90
  return results
91
 
 
92
  def get_completion(prompt, files_info, top_p, temperature, model_choice):
 
93
  files_info_string = "| Type | Name | Dimensions | Duration | Audio Channels |\n"
94
  files_info_string += "|------|------|------------|-----------|--------|\n"
95
 
 
96
  for file_info in files_info:
97
  dimensions = file_info.get("dimensions", "-")
98
+ duration = f"{file_info.get('duration', '-')}s" if "duration" in file_info else "-"
99
+ audio = f"{file_info.get('audio_channels', '-')} channels" if "audio_channels" in file_info else "-"
 
 
 
 
 
 
 
100
  files_info_string += f"| {file_info['type']} | {file_info['name']} | {dimensions} | {duration} | {audio} |\n"
101
 
102
  messages = [
 
141
  },
142
  ]
143
  try:
 
144
  print("\n=== COMPLETE PROMPT ===")
145
  for msg in messages:
146
  print(f"\n[{msg['role'].upper()}]:")
 
163
  max_tokens=2048,
164
  )
165
  content = completion.choices[0].message.content
 
166
  if "```" in content:
 
167
  import re
 
168
  command = re.search(r"```(?:sh|bash)?\n(.*?)\n```", content, re.DOTALL)
169
  if command:
170
  command = command.group(1).strip()
 
173
  else:
174
  command = content.replace("\n", "")
175
 
 
176
  command = command.replace("output.mp4", "")
 
177
  return command
178
  except Exception as e:
179
  raise Exception("API Error")
180
 
181
+ def update(files, prompt, top_p=1, temperature=1, model_choice="Qwen/Qwen2.5-Coder-32B-Instruct"):
 
 
 
 
 
 
 
182
  if prompt == "":
183
  raise gr.Error("Please enter a prompt.")
184
 
185
  files_info = get_files_infos(files)
 
186
  for file_info in files_info:
187
  if file_info["type"] == "video":
188
  if file_info["duration"] > 120:
189
+ raise gr.Error("Please make sure all videos are less than 2 minute long.")
 
 
190
  if file_info["size"] > 100000000:
191
  raise gr.Error("Please make sure all files are less than 100MB in size.")
192
 
 
194
  while attempts < 2:
195
  print("ATTEMPT", attempts)
196
  try:
197
+ command_string = get_completion(prompt, files_info, top_p, temperature, model_choice)
198
+ print(f"""///PROMTP {prompt} \n\n/// START OF COMMAND ///:\n\n{command_string}\n\n/// END OF COMMAND ///\n\n""")
 
 
 
 
199
 
 
200
  args = shlex.split(command_string)
201
  if args[0] != "ffmpeg":
202
  raise Exception("Command does not start with ffmpeg")
203
  temp_dir = tempfile.mkdtemp()
 
204
  for file in files:
205
  file_path = Path(file.name)
206
  sanitized_name = file_path.name.replace(" ", "_")
207
  shutil.copy(file_path, Path(temp_dir) / sanitized_name)
208
 
 
209
  ffmpg_dry_run = subprocess.run(
210
  args + ["-f", "null", "-"],
211
  stderr=subprocess.PIPE,
 
217
  else:
218
  print("Command is not valid. Error output:")
219
  print(ffmpg_dry_run.stderr)
220
+ raise Exception("FFMPEG generated command is not valid. Please try something else.")
 
 
221
 
222
  output_file_name = f"output_{uuid.uuid4()}.mp4"
223
  output_file_path = str((Path(temp_dir) / output_file_name).resolve())
224
  final_command = args + ["-y", output_file_path]
225
+ print(f"\n=== EXECUTING FFMPEG COMMAND ===\nffmpeg {' '.join(final_command[1:])}\n")
 
 
226
  subprocess.run(final_command, cwd=temp_dir)
227
  generated_command = f"### Generated Command\n```bash\nffmpeg {' '.join(args[1:])} -y output.mp4\n```"
228
  return output_file_path, gr.update(value=generated_command)
 
232
  print("FROM UPDATE", e)
233
  raise gr.Error(e)
234
 
 
 
235
  def check_password(password):
236
  return password == PASSWORD
237
 
238
+ # Custom CSS for mobile responsiveness
239
+ custom_css = """
240
+ @media (max-width: 768px) {
241
+ .gradio-container {
242
+ flex-direction: column;
243
+ }
244
+ .gradio-column {
245
+ width: 100% !important;
246
+ }
247
+ }
248
+ """
249
 
250
+ with gr.Blocks(css=custom_css) as demo:
 
 
251
  with gr.Row(visible=True) as login_interface:
252
  with gr.Column():
253
  password_input = gr.Textbox(label="Enter Password", type="password")
254
  login_button = gr.Button("Login")
255
  login_status = gr.Textbox(label="Login Status", interactive=False)
256
 
 
257
  with gr.Row(visible=False) as main_interface:
258
  gr.Markdown(
259
  """
 
262
  """,
263
  elem_id="header",
264
  )
265
+ with gr.Column():
266
+ user_files = gr.File(
267
+ file_count="multiple",
268
+ label="Media files",
269
+ file_types=allowed_medias,
270
+ )
271
+ user_prompt = gr.Textbox(
272
+ placeholder="eg: Remove the 3 first seconds of the video",
273
+ label="Instructions",
274
+ )
275
+ btn = gr.Button("Run")
276
+ with gr.Accordion("Parameters", open=False):
277
+ model_choice = gr.Radio(
278
+ choices=list(MODELS.keys()),
279
+ value=list(MODELS.keys())[0],
280
+ label="Model",
281
  )
282
+ top_p = gr.Slider(
283
+ minimum=-0,
284
+ maximum=1.0,
285
+ value=0.7,
286
+ step=0.05,
287
+ interactive=True,
288
+ label="Top-p (nucleus sampling)",
289
  )
290
+ temperature = gr.Slider(
291
+ minimum=-0,
292
+ maximum=5.0,
293
+ value=0.1,
294
+ step=0.1,
295
+ interactive=True,
296
+ label="Temperature",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
297
  )
298
+ generated_video = gr.Video(
299
+ interactive=False, label="Generated Video", include_audio=True
300
+ )
301
+ generated_command = gr.Markdown()
302
 
303
  btn.click(
304
  fn=update,
 
353
  cache_examples=False,
354
  )
355
 
356
+ with gr.Row():
357
+ gr.Markdown(
358
+ """
359
+ If you have idea to improve this please open a PR:
360
+
361
+ [![Open a Pull Request](https://huggingface.co/datasets/huggingface/badges/raw/main/open-a-pr-lg-light.svg)](https://huggingface.co/spaces/huggingface-projects/video-composer-gpt4/discussions)
362
+ """,
363
+ )
364
 
 
365
  def toggle_interface(password):
366
  if check_password(password):
367
  return gr.update(visible=False), gr.update(visible=True), "Login successful!"
 
374
  outputs=[login_interface, main_interface, login_status],
375
  )
376
 
 
377
  demo.queue(default_concurrency_limit=200)
378
  demo.launch(show_api=False, ssr_mode=False, share=True, inline=True)