Spaces:
Running
Running
import gradio as gr | |
import subprocess | |
import os | |
#import spaces | |
def get_file_size(file_path): | |
"""Get file size in a human-readable format""" | |
if not file_path or not os.path.exists(file_path): | |
return "N/A" | |
size_bytes = os.path.getsize(file_path) | |
# Convert to appropriate unit | |
for unit in ['B', 'KB', 'MB', 'GB']: | |
if size_bytes < 1024 or unit == 'GB': | |
return f"{size_bytes:.2f} {unit}" | |
size_bytes /= 1024 | |
#@spaces.GPU(duration=120) | |
def compress_video(video_file): | |
"""Compresses the uploaded video using FFmpeg and returns the output video path and file sizes.""" | |
if video_file is None: | |
gr.Info("No video uploaded") | |
return None, "N/A", "N/A", 0 | |
try: | |
# Get the input file path and size | |
input_path = video_file | |
input_size = get_file_size(input_path) | |
# Create output filename - in the same directory as input | |
base_dir = os.path.dirname(input_path) | |
filename = os.path.basename(input_path) | |
name, ext = os.path.splitext(filename) | |
output_path = os.path.join(base_dir, f"{name}_compressed{ext}") | |
# Execute ffmpeg command | |
command = [ | |
"ffmpeg", | |
#"-hwaccel", | |
#"cuda", | |
"-i", input_path, | |
"-vcodec", "libx264", | |
"-crf", "28", | |
"-vf", "pad=ceil(iw/2)*2:ceil(ih/2)*2", | |
"-y", | |
output_path | |
] | |
# Show processing notification | |
gr.Info("Processing video...") | |
# Run the command | |
process = subprocess.Popen( | |
command, | |
stdout=subprocess.PIPE, | |
stderr=subprocess.PIPE | |
) | |
stdout, stderr = process.communicate() | |
if process.returncode != 0: | |
error_message = stderr.decode() | |
gr.Info(f"Error: FFmpeg operation failed. Error message: {error_message}") | |
return None, input_size, "N/A", 0 | |
# Calculate output size and savings | |
output_size = get_file_size(output_path) | |
# Calculate compression percentage | |
input_bytes = os.path.getsize(input_path) | |
output_bytes = os.path.getsize(output_path) | |
if input_bytes > 0: | |
compression_percent = (1 - (output_bytes / input_bytes)) * 100 | |
else: | |
compression_percent = 0 | |
# Show success notification | |
gr.Info(f"Video compressed successfully! Saved {compression_percent:.1f}% of original size") | |
# Return the processed video file and size information | |
return output_path, input_size, output_size, round(compression_percent, 1) | |
except Exception as e: | |
gr.Info(f"An error occurred: {str(e)}") | |
return None, get_file_size(video_file) if video_file else "N/A", "N/A", 0 | |
with gr.Blocks(theme=gr.themes.Soft()) as app: | |
gr.Markdown("# Video Compression with FFmpeg") | |
gr.Markdown(""" | |
- Uses H.264 codec | |
- CRF 28 (higher values = more compression, lower quality) | |
- Pads dimensions to ensure they're even numbers (required by some codecs) | |
Thanks for [this article](https://huggingface.co/posts/luigi12345/403914274386316) for providing the formula | |
""") | |
with gr.Row(): | |
with gr.Column(): | |
input_video = gr.Video(label="Upload Video") | |
input_size_display = gr.Textbox(label="Input File Size", interactive=False) | |
compress_btn = gr.Button("Compress Video", variant="primary") | |
with gr.Column(): | |
output_video = gr.Video(label="Compressed Video") | |
output_size_display = gr.Textbox(label="Output File Size", interactive=False) | |
compression_ratio = gr.Number(label="Space Saved (%)", interactive=False) | |
compress_btn.click( | |
fn=compress_video, | |
inputs=[input_video], | |
outputs=[output_video, input_size_display, output_size_display, compression_ratio] | |
) | |
# Also update input size when video is uploaded | |
input_video.change( | |
fn=lambda video: get_file_size(video) if video else "N/A", | |
inputs=[input_video], | |
outputs=[input_size_display] | |
) | |
app.launch(debug=True, show_error=True) | |