Politrees commited on
Commit
2bbb6e9
Β·
verified Β·
1 Parent(s): 5c944b0

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +289 -35
app.py CHANGED
@@ -1,33 +1,206 @@
1
  import os
 
2
  import uuid
3
  import zipfile
 
4
  import gradio as gr
5
  from tqdm import tqdm
6
  from datetime import datetime
7
  from pydub import AudioSegment
8
  from moviepy import VideoFileClip
 
9
 
10
-
11
- audio_formats = ["mp3", "wav", "flac", "ogg", "aac", "m4a", "aiff", "wma", "opus"]
12
- video_formats = ["mp4", "webm", "mkv", "avi", "mov", "flv"]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
 
14
 
15
  # ---------- AUDIO PROCESSING ----------
16
- def convert_audio(input_files, output_format, session_id, merge_files, gap_duration):
17
- """Converts or merges a list of audio files."""
18
  output_files = []
19
  merged_audio = AudioSegment.silent(duration=0)
20
-
21
  os.makedirs(session_id, exist_ok=True)
22
 
 
 
 
 
 
 
 
23
  for input_file in tqdm(input_files, desc="Converting audio files"):
24
  file_path = input_file if isinstance(input_file, str) else input_file.name
25
  audio = AudioSegment.from_file(file_path)
26
 
27
  base_name = os.path.splitext(os.path.basename(file_path))[0]
28
- output_filename = f"{base_name}.{output_format}"
29
  output_path = os.path.join(session_id, output_filename)
30
- audio.export(output_path, format=output_format)
 
31
 
32
  if merge_files:
33
  merged_audio += audio + AudioSegment.silent(duration=gap_duration)
@@ -35,16 +208,43 @@ def convert_audio(input_files, output_format, session_id, merge_files, gap_durat
35
  output_files.append(output_path)
36
 
37
  if merge_files:
38
- merged_output_path = os.path.join(session_id, f"merged_output.{output_format}")
39
- merged_audio.export(merged_output_path, format=output_format)
40
  return [merged_output_path]
41
 
42
  return output_files
43
 
44
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
45
  # ---------- ZIP CREATION ----------
46
  def create_zip(files_to_zip, session_id):
47
- """Creates a ZIP archive from a list of files."""
48
  zip_filename = f"{session_id}.zip"
49
  with zipfile.ZipFile(zip_filename, 'w') as zipf:
50
  for file in tqdm(files_to_zip, desc="Creating ZIP archive"):
@@ -53,16 +253,34 @@ def create_zip(files_to_zip, session_id):
53
 
54
 
55
  # ---------- AUDIO HANDLER ----------
56
- def process_audio_files(files, output_format, merge_files, gap_duration, progress=gr.Progress(track_tqdm=True)):
57
- """Main handler function for audio processing."""
58
  if not files:
59
  raise gr.Error("Please upload at least one audio file!")
60
 
61
  session_id = datetime.now().strftime("%Y-%m-%d_%H-%M-%S") + "_" + str(uuid.uuid4())[:8]
62
  print(f"\nStarting audio session: {session_id}")
63
- print(f"Files to convert: {len(files)} to format {output_format}")
 
 
 
 
 
 
 
 
 
64
 
65
- output_files = convert_audio(files, output_format, session_id, merge_files, gap_duration)
 
 
 
 
 
 
 
 
 
 
66
 
67
  if len(output_files) > 1:
68
  print("Creating ZIP archive...")
@@ -73,42 +291,49 @@ def process_audio_files(files, output_format, merge_files, gap_duration, progres
73
 
74
 
75
  # ---------- VIDEO HANDLER ----------
76
- def process_video(input_video, conversion_type, output_format, progress=gr.Progress(track_tqdm=True)):
77
- """Converts a video to another format or extracts its audio."""
78
  if not input_video:
79
- raise gr.Error("Please upload a video file first!")
80
 
81
  session_id = datetime.now().strftime("%Y-%m-%d_%H-%M-%S") + "_" + str(uuid.uuid4())[:8]
82
  os.makedirs(session_id, exist_ok=True)
83
 
84
  input_path = input_video if isinstance(input_video, str) else input_video.name
85
  base_name = os.path.splitext(os.path.basename(input_path))[0]
86
- output_filename = f"{base_name}_converted.{output_format}"
87
  output_path = os.path.join(session_id, output_filename)
88
 
89
  print(f"\nStarting video session: {session_id}")
90
- print(f"Conversion type: {conversion_type}, Output format: {output_format}")
91
 
92
  try:
93
  clip = VideoFileClip(input_path)
94
 
95
  if conversion_type == "Video to Video":
96
- print("Converting video to video...")
97
- clip.write_videofile(output_path, codec='libx264', audio_codec='aac', logger=None)
98
 
99
  elif conversion_type == "Video to Audio":
100
- print("Extracting audio from video...")
101
  if clip.audio is None:
102
- raise gr.Error("The uploaded video file does not contain an audio track.")
103
  audio_clip = clip.audio
104
- audio_clip.write_audiofile(output_path, logger=None)
 
 
 
 
 
 
 
 
 
105
  audio_clip.close()
106
 
107
  clip.close()
108
 
109
  except Exception as e:
110
  print(f"An error occurred: {e}")
111
- raise gr.Error(f"An error occurred during processing: {e}")
112
 
113
  print("Video processing complete!")
114
  return output_path
@@ -116,27 +341,35 @@ def process_video(input_video, conversion_type, output_format, progress=gr.Progr
116
 
117
  # ---------- FORMAT CHOICES ----------
118
  def update_format_choices(conversion_type):
119
- """Dynamically updates the format dropdown based on the conversion type."""
120
  if conversion_type == "Video to Video":
121
- return gr.Dropdown(choices=video_formats, value="mp4", label="Output Video Format")
 
 
122
  else:
123
- return gr.Dropdown(choices=audio_formats, value="mp3", label="Output Audio Format")
 
 
124
 
125
 
126
  # ---------- UI ----------
 
 
 
 
127
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
128
  gr.HTML("<center><h1>Universal Media Converter 🎧🎬</h1></center>")
129
 
130
  with gr.Tabs():
131
  # AUDIO TAB
132
  with gr.TabItem("🎢 Audio Converter"):
133
- gr.HTML("<center>Upload one or more audio files and select the output format. You can also merge all files into a single track.</center>")
134
 
135
  with gr.Row():
136
  with gr.Column(scale=2):
137
  audio_file_input = gr.Files(label="Upload Audio Files", file_types=["audio"])
138
  with gr.Column(scale=1):
139
- audio_format_choice = gr.Dropdown(choices=audio_formats, label="Output Format", value="mp3")
 
140
  merge_files_checkbox = gr.Checkbox(label="Merge all files into one")
141
  gap_slider = gr.Slider(minimum=0, maximum=5000, step=100, value=500, label="Gap between files (ms)")
142
 
@@ -149,9 +382,29 @@ with gr.Blocks(theme=gr.themes.Soft()) as demo:
149
  outputs=audio_output_file
150
  )
151
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
152
  # VIDEO TAB
153
  with gr.TabItem("🎬 Video Converter"):
154
- gr.HTML("<center>Upload a video file, then choose whether to convert it to another video format or to extract its audio track.</center>")
155
 
156
  with gr.Row():
157
  with gr.Column(scale=2):
@@ -162,10 +415,11 @@ with gr.Blocks(theme=gr.themes.Soft()) as demo:
162
  label="Conversion Type",
163
  value="Video to Video"
164
  )
 
165
  video_format_dropdown = gr.Dropdown(
166
- choices=video_formats,
167
- label="Output Video Format",
168
- value="mp4"
169
  )
170
 
171
  video_submit_button = gr.Button("πŸš€ Convert", variant="primary")
 
1
  import os
2
+ import re
3
  import uuid
4
  import zipfile
5
+ import subprocess
6
  import gradio as gr
7
  from tqdm import tqdm
8
  from datetime import datetime
9
  from pydub import AudioSegment
10
  from moviepy import VideoFileClip
11
+ from PIL import Image
12
 
13
+ # ----------------------- FFmpeg utils -----------------------
14
+ def _run_ffmpeg(args):
15
+ try:
16
+ res = subprocess.run(["ffmpeg", *args], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True)
17
+ return res.stdout
18
+ except Exception:
19
+ return None
20
+
21
+ def ffmpeg_writable_formats():
22
+ """
23
+ Returns a set of FFmpeg format names (including aliases) available for writing (E flag).
24
+ Example: {'mp4', 'mov', 'm4a', '3gp', 'matroska', 'webm', ...}
25
+ """
26
+ out = _run_ffmpeg(["-hide_banner", "-v", "error", "-formats"])
27
+ if not out:
28
+ return set()
29
+ fmts = set()
30
+ for line in out.splitlines():
31
+ # lines look like: " DE matroska,webm Matroska / WebM"
32
+ if re.match(r"^\s*[D\s]*E\s+", line):
33
+ m = re.search(r"^\s*[D\s]*E\s+([^\s]+)", line)
34
+ if not m:
35
+ continue
36
+ names = m.group(1)
37
+ for name in names.split(","):
38
+ fmts.add(name.strip())
39
+ return fmts
40
+
41
+ def ffmpeg_audio_encoders():
42
+ """
43
+ Returns a set of available audio encoders, e.g. {'aac','libmp3lame','libopus',...}
44
+ """
45
+ out = _run_ffmpeg(["-hide_banner", "-v", "error", "-encoders"])
46
+ if not out:
47
+ return set()
48
+ enc = set()
49
+ for line in out.splitlines():
50
+ # lines look like: " A..... libmp3lame MP3 (MPEG audio layer 3) (codec mp3)"
51
+ m = re.match(r"^\s*A\S*\s+([^\s]+)", line)
52
+ if m:
53
+ enc.add(m.group(1).strip())
54
+ return enc
55
+
56
+ # Extension -> FFmpeg container mapping (curated, common ones)
57
+ AUDIO_EXT_TO_FFMPEG_FORMAT = {
58
+ "mp3": "mp3",
59
+ "wav": "wav",
60
+ "w64": "w64",
61
+ "flac": "flac",
62
+ "ogg": "ogg",
63
+ "oga": "ogg",
64
+ "opus": "ogg", # ogg container; needs libopus
65
+ "spx": "ogg", # ogg container; needs libspeex
66
+ "aac": "adts",
67
+ "m4a": "mp4",
68
+ "m4b": "mp4",
69
+ "m4r": "mp4",
70
+ "ac3": "ac3",
71
+ "aiff": "aiff",
72
+ "aif": "aiff",
73
+ "aifc": "aiff",
74
+ "caf": "caf",
75
+ "au": "au",
76
+ "amr": "amr",
77
+ "dts": "dts",
78
+ "mp2": "mp2",
79
+ "wma": "asf",
80
+ "wv": "wv",
81
+ "mka": "matroska",
82
+ }
83
+
84
+ # Some extensions require specific encoders
85
+ AUDIO_REQUIRED_CODECS = {
86
+ "mp3": ["libmp3lame"],
87
+ "opus": ["libopus"],
88
+ "spx": ["libspeex"],
89
+ # others rely on FFmpeg defaults
90
+ }
91
+
92
+ VIDEO_EXT_TO_FFMPEG_FORMAT = {
93
+ "mp4": "mp4",
94
+ "m4v": "mp4",
95
+ "mov": "mov",
96
+ "avi": "avi",
97
+ "mkv": "matroska",
98
+ "webm": "webm",
99
+ "flv": "flv",
100
+ "ogv": "ogg",
101
+ "mpeg": "mpeg",
102
+ "mpg": "mpeg",
103
+ "ts": "mpegts",
104
+ "m2ts": "mpegts",
105
+ "mxf": "mxf",
106
+ "3gp": "3gp",
107
+ "3g2": "3g2",
108
+ "asf": "asf",
109
+ "wmv": "asf",
110
+ "vob": "vob",
111
+ }
112
+
113
+ def available_audio_extensions():
114
+ writable = ffmpeg_writable_formats()
115
+ encoders = ffmpeg_audio_encoders()
116
+ exts = []
117
+ for ext, ffmt in AUDIO_EXT_TO_FFMPEG_FORMAT.items():
118
+ if ffmt not in writable:
119
+ continue
120
+ req = AUDIO_REQUIRED_CODECS.get(ext)
121
+ if req and not any(r in encoders for r in req):
122
+ continue
123
+ exts.append(ext)
124
+ # fallback if ffmpeg is missing or query failed
125
+ if not exts:
126
+ exts = ["mp3", "wav", "flac", "ogg", "aac", "m4a", "aiff", "wma", "opus"]
127
+ return sorted(set(exts))
128
+
129
+ def available_video_extensions():
130
+ writable = ffmpeg_writable_formats()
131
+ exts = [ext for ext, ffmt in VIDEO_EXT_TO_FFMPEG_FORMAT.items() if ffmt in writable]
132
+ if not exts:
133
+ exts = ["mp4", "mkv", "avi", "mov", "webm", "flv", "mpeg", "mpg", "ts"]
134
+ return sorted(set(exts))
135
+
136
+ # ----------------------- Pillow utils (images) -----------------------
137
+ def available_image_extensions():
138
+ # All registered extensions Pillow knows how to save (best-effort)
139
+ ext2fmt = Image.registered_extensions() # {".jpg":"JPEG", ...}
140
+ save_ok = set(getattr(Image, "SAVE", {}).keys()) or set()
141
+ if not save_ok:
142
+ # If SAVE registry is unavailable, assume registered formats are savable
143
+ save_ok = set(ext2fmt.values())
144
+ exts = []
145
+ for ext, fmt in ext2fmt.items():
146
+ if fmt in save_ok:
147
+ e = ext.lstrip(".").lower()
148
+ exts.append(e)
149
+ if not exts:
150
+ exts = ["png", "jpg", "jpeg", "webp", "bmp", "tiff", "gif", "ico", "ppm", "pgm", "pbm", "pnm", "tga", "xbm", "xpm", "pdf", "eps"]
151
+ return sorted(set(exts))
152
+
153
+ def pil_format_for_ext(ext):
154
+ ext = ext.lower().strip(".")
155
+ for k, v in Image.registered_extensions().items():
156
+ if k.lstrip(".").lower() == ext:
157
+ return v
158
+ fallback = {
159
+ "jpg": "JPEG",
160
+ "jpeg": "JPEG",
161
+ "png": "PNG",
162
+ "webp": "WEBP",
163
+ "bmp": "BMP",
164
+ "tiff": "TIFF",
165
+ "tif": "TIFF",
166
+ "gif": "GIF",
167
+ "ico": "ICO",
168
+ "ppm": "PPM",
169
+ "pgm": "PPM",
170
+ "pbm": "PPM",
171
+ "pnm": "PPM",
172
+ "tga": "TGA",
173
+ "xbm": "XBM",
174
+ "xpm": "XPM",
175
+ "pdf": "PDF",
176
+ "eps": "EPS",
177
+ }
178
+ return fallback.get(ext, None)
179
 
180
 
181
  # ---------- AUDIO PROCESSING ----------
182
+ def convert_audio(input_files, output_ext, session_id, merge_files, gap_duration):
183
+ """Convert/merge audio into the selected format (by extension)."""
184
  output_files = []
185
  merged_audio = AudioSegment.silent(duration=0)
 
186
  os.makedirs(session_id, exist_ok=True)
187
 
188
+ ff_format = AUDIO_EXT_TO_FFMPEG_FORMAT.get(output_ext, output_ext)
189
+ codec = None
190
+ if output_ext == "opus":
191
+ codec = "libopus"
192
+ elif output_ext == "spx":
193
+ codec = "libspeex"
194
+
195
  for input_file in tqdm(input_files, desc="Converting audio files"):
196
  file_path = input_file if isinstance(input_file, str) else input_file.name
197
  audio = AudioSegment.from_file(file_path)
198
 
199
  base_name = os.path.splitext(os.path.basename(file_path))[0]
200
+ output_filename = f"{base_name}.{output_ext}"
201
  output_path = os.path.join(session_id, output_filename)
202
+
203
+ audio.export(output_path, format=ff_format, codec=codec)
204
 
205
  if merge_files:
206
  merged_audio += audio + AudioSegment.silent(duration=gap_duration)
 
208
  output_files.append(output_path)
209
 
210
  if merge_files:
211
+ merged_output_path = os.path.join(session_id, f"merged_output.{output_ext}")
212
+ merged_audio.export(merged_output_path, format=ff_format, codec=codec)
213
  return [merged_output_path]
214
 
215
  return output_files
216
 
217
 
218
+ # ---------- IMAGE PROCESSING ----------
219
+ def convert_images(input_files, output_ext, session_id):
220
+ """Simple image format conversion."""
221
+ os.makedirs(session_id, exist_ok=True)
222
+ output_files = []
223
+
224
+ pil_fmt = pil_format_for_ext(output_ext)
225
+ if not pil_fmt:
226
+ raise gr.Error(f"Pillow cannot save to format: {output_ext}")
227
+
228
+ for input_file in tqdm(input_files, desc="Converting images"):
229
+ file_path = input_file if isinstance(input_file, str) else input_file.name
230
+ base_name = os.path.splitext(os.path.basename(file_path))[0]
231
+ output_filename = f"{base_name}.{output_ext}"
232
+ output_path = os.path.join(session_id, output_filename)
233
+
234
+ with Image.open(file_path) as img:
235
+ img.load()
236
+ # For JPEG ensure RGB mode
237
+ if pil_fmt.upper() == "JPEG":
238
+ img = img.convert("RGB")
239
+ img.save(output_path, format=pil_fmt)
240
+
241
+ output_files.append(output_path)
242
+
243
+ return output_files
244
+
245
+
246
  # ---------- ZIP CREATION ----------
247
  def create_zip(files_to_zip, session_id):
 
248
  zip_filename = f"{session_id}.zip"
249
  with zipfile.ZipFile(zip_filename, 'w') as zipf:
250
  for file in tqdm(files_to_zip, desc="Creating ZIP archive"):
 
253
 
254
 
255
  # ---------- AUDIO HANDLER ----------
256
+ def process_audio_files(files, output_ext, merge_files, gap_duration, progress=gr.Progress(track_tqdm=True)):
 
257
  if not files:
258
  raise gr.Error("Please upload at least one audio file!")
259
 
260
  session_id = datetime.now().strftime("%Y-%m-%d_%H-%M-%S") + "_" + str(uuid.uuid4())[:8]
261
  print(f"\nStarting audio session: {session_id}")
262
+ print(f"Files to convert: {len(files)} to .{output_ext}")
263
+
264
+ output_files = convert_audio(files, output_ext, session_id, merge_files, gap_duration)
265
+
266
+ if len(output_files) > 1:
267
+ print("Creating ZIP archive...")
268
+ zip_filename = create_zip(output_files, session_id)
269
+ return zip_filename
270
+
271
+ return output_files[0]
272
 
273
+
274
+ # ---------- IMAGE HANDLER ----------
275
+ def process_image_files(files, output_ext, progress=gr.Progress(track_tqdm=True)):
276
+ if not files:
277
+ raise gr.Error("Please upload at least one image!")
278
+
279
+ session_id = datetime.now().strftime("%Y-%m-%d_%H-%M-%S") + "_" + str(uuid.uuid4())[:8]
280
+ print(f"\nStarting image session: {session_id}")
281
+ print(f"Files to convert: {len(files)} to .{output_ext}")
282
+
283
+ output_files = convert_images(files, output_ext, session_id)
284
 
285
  if len(output_files) > 1:
286
  print("Creating ZIP archive...")
 
291
 
292
 
293
  # ---------- VIDEO HANDLER ----------
294
+ def process_video(input_video, conversion_type, output_ext, progress=gr.Progress(track_tqdm=True)):
 
295
  if not input_video:
296
+ raise gr.Error("Please upload a video file!")
297
 
298
  session_id = datetime.now().strftime("%Y-%m-%d_%H-%M-%S") + "_" + str(uuid.uuid4())[:8]
299
  os.makedirs(session_id, exist_ok=True)
300
 
301
  input_path = input_video if isinstance(input_video, str) else input_video.name
302
  base_name = os.path.splitext(os.path.basename(input_path))[0]
303
+ output_filename = f"{base_name}_converted.{output_ext}"
304
  output_path = os.path.join(session_id, output_filename)
305
 
306
  print(f"\nStarting video session: {session_id}")
307
+ print(f"Conversion type: {conversion_type}, Output: .{output_ext}")
308
 
309
  try:
310
  clip = VideoFileClip(input_path)
311
 
312
  if conversion_type == "Video to Video":
313
+ # Let MoviePy use its defaults; for some containers (e.g. webm) this may require specific codecs
314
+ clip.write_videofile(output_path, logger=None)
315
 
316
  elif conversion_type == "Video to Audio":
 
317
  if clip.audio is None:
318
+ raise gr.Error("The uploaded video does not contain an audio track.")
319
  audio_clip = clip.audio
320
+
321
+ audio_codec = None
322
+ if output_ext == "opus":
323
+ if "libopus" in ffmpeg_audio_encoders():
324
+ audio_codec = "libopus"
325
+ elif output_ext == "spx":
326
+ if "libspeex" in ffmpeg_audio_encoders():
327
+ audio_codec = "libspeex"
328
+
329
+ audio_clip.write_audiofile(output_path, logger=None, codec=audio_codec)
330
  audio_clip.close()
331
 
332
  clip.close()
333
 
334
  except Exception as e:
335
  print(f"An error occurred: {e}")
336
+ raise gr.Error(f"Processing error: {e}")
337
 
338
  print("Video processing complete!")
339
  return output_path
 
341
 
342
  # ---------- FORMAT CHOICES ----------
343
  def update_format_choices(conversion_type):
 
344
  if conversion_type == "Video to Video":
345
+ vf = available_video_extensions()
346
+ value = "mp4" if "mp4" in vf else (vf[0] if vf else None)
347
+ return gr.Dropdown(choices=vf, value=value, label="Output Video Format")
348
  else:
349
+ af = available_audio_extensions()
350
+ value = "mp3" if "mp3" in af else (af[0] if af else None)
351
+ return gr.Dropdown(choices=af, value=value, label="Output Audio Format")
352
 
353
 
354
  # ---------- UI ----------
355
+ AUDIO_FORMATS = available_audio_extensions()
356
+ VIDEO_FORMATS = available_video_extensions()
357
+ IMAGE_FORMATS = available_image_extensions()
358
+
359
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
360
  gr.HTML("<center><h1>Universal Media Converter 🎧🎬</h1></center>")
361
 
362
  with gr.Tabs():
363
  # AUDIO TAB
364
  with gr.TabItem("🎢 Audio Converter"):
365
+ gr.HTML("<center>Upload one or more audio files and choose the output format. You can also merge files into a single track (optional).</center>")
366
 
367
  with gr.Row():
368
  with gr.Column(scale=2):
369
  audio_file_input = gr.Files(label="Upload Audio Files", file_types=["audio"])
370
  with gr.Column(scale=1):
371
+ default_audio = "mp3" if "mp3" in AUDIO_FORMATS else (AUDIO_FORMATS[0] if AUDIO_FORMATS else None)
372
+ audio_format_choice = gr.Dropdown(choices=AUDIO_FORMATS, label="Output Format", value=default_audio)
373
  merge_files_checkbox = gr.Checkbox(label="Merge all files into one")
374
  gap_slider = gr.Slider(minimum=0, maximum=5000, step=100, value=500, label="Gap between files (ms)")
375
 
 
382
  outputs=audio_output_file
383
  )
384
 
385
+ # IMAGE TAB
386
+ with gr.TabItem("πŸ–ΌοΈ Image Converter"):
387
+ gr.HTML("<center>Upload one or more images and choose the output format.</center>")
388
+
389
+ with gr.Row():
390
+ with gr.Column(scale=2):
391
+ image_file_input = gr.Files(label="Upload Image Files", file_types=["image"])
392
+ with gr.Column(scale=1):
393
+ default_image = "png" if "png" in IMAGE_FORMATS else (IMAGE_FORMATS[0] if IMAGE_FORMATS else None)
394
+ image_format_choice = gr.Dropdown(choices=IMAGE_FORMATS, label="Output Image Format", value=default_image)
395
+
396
+ image_submit_button = gr.Button("πŸš€ Convert", variant="primary")
397
+ image_output_file = gr.File(label="Download Result")
398
+
399
+ image_submit_button.click(
400
+ fn=process_image_files,
401
+ inputs=[image_file_input, image_format_choice],
402
+ outputs=image_output_file
403
+ )
404
+
405
  # VIDEO TAB
406
  with gr.TabItem("🎬 Video Converter"):
407
+ gr.HTML("<center>Upload a video, then choose to convert it to another video format or to extract its audio.</center>")
408
 
409
  with gr.Row():
410
  with gr.Column(scale=2):
 
415
  label="Conversion Type",
416
  value="Video to Video"
417
  )
418
+ default_video = "mp4" if "mp4" in VIDEO_FORMATS else (VIDEO_FORMATS[0] if VIDEO_FORMATS else None)
419
  video_format_dropdown = gr.Dropdown(
420
+ choices=VIDEO_FORMATS,
421
+ label="Output Video/Audio Format",
422
+ value=default_video
423
  )
424
 
425
  video_submit_button = gr.Button("πŸš€ Convert", variant="primary")