|
import gradio as gr |
|
import os |
|
import shutil |
|
import PIL |
|
from PIL import Image |
|
import py7zr |
|
import tarfile |
|
import zipfile |
|
import ffmpeg |
|
from pathlib import Path |
|
import tempfile |
|
from PIL import ImageDraw |
|
def generate_thumbnail(input_video, time_position=0): |
|
if input_video is None: |
|
return None |
|
|
|
temp_dir = tempfile.mkdtemp() |
|
output_path = os.path.join(temp_dir, "thumbnail.jpg") |
|
|
|
stream = ffmpeg.input(input_video.name, ss=time_position) |
|
stream = ffmpeg.output(stream, output_path, vframes=1) |
|
ffmpeg.run(stream, overwrite_output=True) |
|
|
|
return output_path |
|
|
|
def extract_audio(input_video, output_format="mp3"): |
|
if input_video is None: |
|
return None |
|
|
|
temp_dir = tempfile.mkdtemp() |
|
output_path = os.path.join(temp_dir, f"extracted_audio.{output_format}") |
|
|
|
stream = ffmpeg.input(input_video.name) |
|
stream = ffmpeg.output(stream, output_path, acodec='libmp3lame') |
|
ffmpeg.run(stream, overwrite_output=True) |
|
|
|
return output_path |
|
|
|
def trim_video(input_video, start_time, end_time): |
|
if input_video is None: |
|
return None |
|
|
|
temp_dir = tempfile.mkdtemp() |
|
output_path = os.path.join(temp_dir, "trimmed_video.mp4") |
|
|
|
stream = ffmpeg.input(input_video.name, ss=start_time, t=end_time-start_time) |
|
stream = ffmpeg.output(stream, output_path, acodec='copy', vcodec='copy') |
|
ffmpeg.run(stream, overwrite_output=True) |
|
|
|
return output_path |
|
|
|
def crop_image(input_image, x1, y1, x2, y2): |
|
if input_image is None: |
|
return None |
|
|
|
temp_dir = tempfile.mkdtemp() |
|
img = Image.open(input_image.name) |
|
cropped = img.crop((x1, y1, x2, y2)) |
|
|
|
output_path = os.path.join(temp_dir, f"cropped_image.{input_image.name.split('.')[-1]}") |
|
cropped.save(output_path) |
|
|
|
return output_path |
|
|
|
def add_watermark(input_image, watermark_text, position="bottom-right"): |
|
if input_image is None: |
|
return None |
|
|
|
temp_dir = tempfile.mkdtemp() |
|
img = Image.open(input_image.name) |
|
draw = ImageDraw.Draw(img) |
|
|
|
|
|
font_size = int(min(img.width, img.height) * 0.05) |
|
text_width = len(watermark_text) * font_size * 0.6 |
|
text_height = font_size |
|
|
|
|
|
positions = { |
|
"top-left": (10, 10), |
|
"top-right": (img.width - text_width - 10, 10), |
|
"bottom-left": (10, img.height - text_height - 10), |
|
"bottom-right": (img.width - text_width - 10, img.height - text_height - 10) |
|
} |
|
|
|
x, y = positions.get(position, positions["bottom-right"]) |
|
draw.text((x, y), watermark_text, fill=(255, 255, 255, 128)) |
|
|
|
output_path = os.path.join(temp_dir, f"watermarked_image.{input_image.name.split('.')[-1]}") |
|
img.save(output_path) |
|
|
|
return output_path |
|
|
|
def convert_image(input_image, output_format, compression_level): |
|
if input_image is None: |
|
return None |
|
|
|
temp_dir = tempfile.mkdtemp() |
|
output_path = os.path.join(temp_dir, f"converted_image.{output_format.lower()}") |
|
|
|
img = Image.open(input_image.name) |
|
|
|
if output_format.lower() == 'jpeg' and img.mode == 'RGBA': |
|
background = Image.new('RGB', img.size, (255, 255, 255)) |
|
background.paste(img, mask=img.split()[3]) |
|
img = background |
|
|
|
|
|
quality = max(1, min(95, int(95 - (compression_level * 0.94)))) |
|
|
|
if output_format.lower() in ['jpeg', 'webp']: |
|
img.save(output_path, quality=quality, optimize=True) |
|
elif output_format.lower() == 'png': |
|
img.save(output_path, optimize=True, compression_level=int(compression_level/10)) |
|
else: |
|
img.save(output_path) |
|
|
|
return output_path |
|
|
|
def compress_video(input_video, compression_level, output_format="mp4"): |
|
if input_video is None: |
|
return None |
|
|
|
temp_dir = tempfile.mkdtemp() |
|
output_path = os.path.join(temp_dir, f"compressed_video.{output_format}") |
|
|
|
|
|
crf = int(51 * (compression_level / 100)) |
|
|
|
stream = ffmpeg.input(input_video.name) |
|
stream = ffmpeg.output(stream, output_path, |
|
vcodec='libx265', |
|
crf=crf, |
|
acodec='aac') |
|
ffmpeg.run(stream, overwrite_output=True) |
|
|
|
return output_path |
|
|
|
def compress_files(input_files, compression_format, compression_level): |
|
if not input_files: |
|
return None |
|
|
|
temp_dir = tempfile.mkdtemp() |
|
timestamp = Path(input_files[0].name).stem |
|
|
|
if compression_format == "zip": |
|
output_path = os.path.join(temp_dir, f"compressed_{timestamp}.zip") |
|
compression = zipfile.ZIP_DEFLATED |
|
compresslevel = int(9 * compression_level / 100) |
|
with zipfile.ZipFile(output_path, 'w', compression=compression, compresslevel=compresslevel) as zipf: |
|
for file in input_files: |
|
zipf.write(file.name, os.path.basename(file.name)) |
|
|
|
if compression_format == "7z": |
|
output_path = os.path.join(temp_dir, f"compressed_{timestamp}.7z") |
|
|
|
compression_filters = [ |
|
{'id': py7zr.FILTER_LZMA2, 'preset': int(9 * compression_level / 100)} |
|
] |
|
with py7zr.SevenZipFile(output_path, 'w', filters=compression_filters) as szf: |
|
for file in input_files: |
|
szf.write(file.name, os.path.basename(file.name)) |
|
|
|
elif compression_format == "tar.gz": |
|
output_path = os.path.join(temp_dir, f"compressed_{timestamp}.tar.gz") |
|
compresslevel = int(9 * compression_level / 100) |
|
with tarfile.open(output_path, "w:gz", compresslevel=compresslevel) as tar: |
|
for file in input_files: |
|
tar.add(file.name, arcname=os.path.basename(file.name)) |
|
|
|
elif compression_format == "tar.xz": |
|
output_path = os.path.join(temp_dir, f"compressed_{timestamp}.tar.xz") |
|
preset = int(9 * compression_level / 100) |
|
with tarfile.open(output_path, "w:xz", preset=preset) as tar: |
|
for file in input_files: |
|
tar.add(file.name, arcname=os.path.basename(file.name)) |
|
|
|
return output_path |
|
|
|
def resize_image(input_image, width, height, maintain_aspect): |
|
if input_image is None: |
|
return None |
|
|
|
temp_dir = tempfile.mkdtemp() |
|
img = Image.open(input_image.name) |
|
|
|
if maintain_aspect: |
|
|
|
aspect_ratio = img.width / img.height |
|
if width: |
|
height = int(width / aspect_ratio) |
|
else: |
|
width = int(height * aspect_ratio) |
|
|
|
|
|
width = width if width else img.width |
|
height = height if height else img.height |
|
|
|
resized_img = img.resize((width, height), Image.Resampling.LANCZOS) |
|
output_path = os.path.join(temp_dir, f"resized_image.{input_image.name.split('.')[-1]}") |
|
resized_img.save(output_path) |
|
|
|
return output_path |
|
|
|
|
|
|
|
with gr.Blocks(title="Media Toolbox") as app: |
|
gr.Markdown("""# Universal Media Toolbox (For all Your Ai Media Needs ✨✨) |
|
#### Note: Higher compression values will significantly reduce file quality and size. |
|
#### Recommended compression levels: |
|
#### - Images: 20-40 for good balance between quality and size |
|
#### - Videos: 30-50 for reasonable quality |
|
#### - Files: 50-70 for general use |
|
#### Only use maximum compression (90-100) when file size is critical and quality loss is acceptable |
|
""") |
|
|
|
with gr.Tab("Image Conversion"): |
|
with gr.Row(): |
|
with gr.Column(): |
|
image_input = gr.File(label="Upload Image") |
|
image_format = gr.Dropdown( |
|
choices=["PNG", "JPEG", "WEBP", "GIF", "BMP"], |
|
value="PNG", |
|
label="Convert to Format" |
|
) |
|
image_compression = gr.Slider( |
|
minimum=0, |
|
maximum=100, |
|
value=20, |
|
label="Compression Level (0: Highest Quality, 100: Maximum Compression)" |
|
) |
|
image_convert_btn = gr.Button("Convert Image") |
|
with gr.Column(): |
|
image_output = gr.File(label="Converted Image") |
|
|
|
with gr.Tab("Image Resize"): |
|
with gr.Row(): |
|
with gr.Column(): |
|
resize_input = gr.File(label="Upload Image") |
|
with gr.Row(): |
|
width = gr.Number(label="Width (pixels)", value=800) |
|
height = gr.Number(label="Height (pixels)", value=600) |
|
maintain_aspect = gr.Checkbox(label="Maintain Aspect Ratio", value=True) |
|
resize_btn = gr.Button("Resize Image") |
|
with gr.Column(): |
|
resize_output = gr.File(label="Resized Image") |
|
|
|
with gr.Tab("Image Crop"): |
|
with gr.Row(): |
|
with gr.Column(): |
|
crop_input = gr.File(label="Upload Image") |
|
with gr.Row(): |
|
x1 = gr.Number(label="Left", value=0) |
|
y1 = gr.Number(label="Top", value=0) |
|
x2 = gr.Number(label="Right", value=100) |
|
y2 = gr.Number(label="Bottom", value=100) |
|
crop_btn = gr.Button("Crop Image") |
|
with gr.Column(): |
|
crop_output = gr.File(label="Cropped Image") |
|
|
|
with gr.Tab("Image Watermark"): |
|
with gr.Row(): |
|
with gr.Column(): |
|
watermark_input = gr.File(label="Upload Image") |
|
watermark_text = gr.Textbox(label="Watermark Text") |
|
position = gr.Dropdown( |
|
choices=["top-left", "top-right", "bottom-left", "bottom-right"], |
|
value="bottom-right", |
|
label="Position" |
|
) |
|
watermark_btn = gr.Button("Add Watermark") |
|
with gr.Column(): |
|
watermark_output = gr.File(label="Watermarked Image") |
|
|
|
with gr.Tab("Video Compression"): |
|
with gr.Row(): |
|
with gr.Column(): |
|
video_input = gr.File(label="Upload Video") |
|
compression_level = gr.Slider( |
|
minimum=0, |
|
maximum=100, |
|
value=50, |
|
label="Compression Level (0: Highest Quality, 100: Highest Compression)" |
|
) |
|
video_compress_btn = gr.Button("Compress Video") |
|
with gr.Column(): |
|
video_output = gr.File(label="Compressed Video") |
|
|
|
with gr.Tab("Video Trim"): |
|
with gr.Row(): |
|
with gr.Column(): |
|
trim_input = gr.File(label="Upload Video") |
|
with gr.Row(): |
|
start_time = gr.Number(label="Start Time (seconds)", value=0) |
|
end_time = gr.Number(label="End Time (seconds)", value=10) |
|
trim_btn = gr.Button("Trim Video") |
|
with gr.Column(): |
|
trim_output = gr.File(label="Trimmed Video") |
|
|
|
with gr.Tab("Audio Extraction"): |
|
with gr.Row(): |
|
with gr.Column(): |
|
audio_input = gr.File(label="Upload Video") |
|
audio_format = gr.Dropdown( |
|
choices=["mp3", "wav", "aac"], |
|
value="mp3", |
|
label="Audio Format" |
|
) |
|
extract_btn = gr.Button("Extract Audio") |
|
with gr.Column(): |
|
audio_output = gr.File(label="Extracted Audio") |
|
|
|
with gr.Tab("Video Thumbnail"): |
|
with gr.Row(): |
|
with gr.Column(): |
|
thumbnail_input = gr.File(label="Upload Video") |
|
time_pos = gr.Number(label="Time Position (seconds)", value=0) |
|
thumbnail_btn = gr.Button("Generate Thumbnail") |
|
with gr.Column(): |
|
thumbnail_output = gr.File(label="Video Thumbnail") |
|
|
|
with gr.Tab("File Compression"): |
|
with gr.Row(): |
|
with gr.Column(): |
|
files_input = gr.File(label="Upload Files", file_count="multiple") |
|
compression_format = gr.Dropdown( |
|
choices=["zip", "7z", "tar.gz", "tar.xz"], |
|
value="zip", |
|
label="Compression Format" |
|
) |
|
file_compression = gr.Slider( |
|
minimum=0, |
|
maximum=100, |
|
value=50, |
|
label="Compression Level (0: Fastest/Largest, 100: Slowest/Smallest)" |
|
) |
|
files_compress_btn = gr.Button("Compress Files") |
|
with gr.Column(): |
|
files_output = gr.File(label="Compressed Files") |
|
|
|
|
|
image_convert_btn.click(convert_image, |
|
inputs=[image_input, image_format, image_compression], |
|
outputs=image_output) |
|
|
|
resize_btn.click(resize_image, |
|
inputs=[resize_input, width, height, maintain_aspect], |
|
outputs=resize_output) |
|
|
|
crop_btn.click(crop_image, |
|
inputs=[crop_input, x1, y1, x2, y2], |
|
outputs=crop_output) |
|
|
|
watermark_btn.click(add_watermark, |
|
inputs=[watermark_input, watermark_text, position], |
|
outputs=watermark_output) |
|
|
|
video_compress_btn.click(compress_video, |
|
inputs=[video_input, compression_level], |
|
outputs=video_output) |
|
|
|
trim_btn.click(trim_video, |
|
inputs=[trim_input, start_time, end_time], |
|
outputs=trim_output) |
|
|
|
extract_btn.click(extract_audio, |
|
inputs=[audio_input, audio_format], |
|
outputs=audio_output) |
|
|
|
thumbnail_btn.click(generate_thumbnail, |
|
inputs=[thumbnail_input, time_pos], |
|
outputs=thumbnail_output) |
|
|
|
files_compress_btn.click(compress_files, |
|
inputs=[files_input, compression_format, file_compression], |
|
outputs=files_output) |
|
|
|
|
|
app.launch(ssr_mode=False,server_port=7860,) |
|
|