Spaces:
Sleeping
Sleeping
import gradio as gr | |
import ffmpeg | |
import os | |
import tempfile | |
import re | |
import shutil | |
def sanitize_filename(name): | |
name = os.path.splitext(os.path.basename(name))[0] | |
return re.sub(r'[^一-龥\w\-_.]', '_', name) | |
def compress_video(file, resize_mode, resize_value, mute, | |
audio_codec, audio_bitrate, audio_rate, audio_channels, | |
video_codec, video_bitrate, framerate, pix_fmt): | |
if not file: | |
return None, "ファイルが選択されていません" | |
input_path = file.name | |
safe_name = sanitize_filename(input_path) | |
SAVE_DIR = os.path.join(os.getcwd(), "compressed_videos") | |
os.makedirs(SAVE_DIR, exist_ok=True) | |
output_path = os.path.join(SAVE_DIR, f"{safe_name}_compressed.mp4") | |
try: | |
probe = ffmpeg.probe(input_path) | |
video_stream = next((s for s in probe['streams'] if s['codec_type'] == 'video'), None) | |
if not video_stream: | |
return None, "映像ストリームが見つかりません" | |
width = int(video_stream['width']) | |
height = int(video_stream['height']) | |
# リサイズ設定 | |
resize_args = {} | |
if resize_mode == "パーセンテージ": | |
try: | |
percent = float(resize_value.strip('%')) / 100.0 | |
assert 0 < percent <= 1 | |
new_width = int(width * percent) | |
new_height = int(height * percent) | |
resize_args = {'vf': f'scale={new_width}:{new_height}'} | |
except Exception: | |
return None, "リサイズ値が無効です。例: 80%" | |
elif resize_mode == "ピクセル指定": | |
match = re.match(r"(\d+)[xX](\d+)", resize_value) | |
if not match: | |
return None, "リサイズ値が無効です。例: 1280x720" | |
new_width, new_height = map(int, match.groups()) | |
resize_args = {'vf': f'scale={new_width}:{new_height}'} | |
# 音声設定 | |
if mute: | |
audio_args = {'an': True} | |
else: | |
audio_args = {} | |
if audio_codec: audio_args['c:a'] = audio_codec | |
if audio_bitrate: audio_args['b:a'] = audio_bitrate | |
if audio_rate: audio_args['ar'] = audio_rate | |
if audio_channels: audio_args['ac'] = audio_channels | |
# 映像設定 | |
video_args = { | |
'c:v': video_codec, | |
'crf': 18, | |
'preset': 'slow', | |
'r': framerate, | |
'pix_fmt': pix_fmt | |
} | |
if video_bitrate: | |
video_args['b:v'] = video_bitrate | |
stream = ffmpeg.input(input_path) | |
stream = ffmpeg.output( | |
stream, | |
output_path, | |
movflags='+faststart', | |
**resize_args, | |
**audio_args, | |
**video_args | |
) | |
ffmpeg.run(stream, overwrite_output=True) | |
return (output_path, None) if os.path.exists(output_path) else (None, "圧縮に失敗しました") | |
except Exception as e: | |
return None, f"エラーが発生しました: {str(e)}" | |
with gr.Blocks() as demo: | |
gr.Markdown("## 🎬 高画質を維持した動画圧縮ツール (カスタム設定付き)") | |
with gr.Row(): | |
with gr.Column(): | |
video_input = gr.File(label="動画ファイルを選択", file_types=[".mp4", ".mov", ".mkv", ".avi"]) | |
resize_mode = gr.Radio(choices=["パーセンテージ", "ピクセル指定"], value="パーセンテージ", label="リサイズ方法") | |
resize_value = gr.Textbox(label="リサイズ値(例: 80% または 1280x720)", value="100%") | |
mute = gr.Checkbox(label="音声を消す(ミュート)", value=False) | |
gr.Markdown("### 音声設定") | |
audio_codec = gr.Textbox(label="音声コーデック (-c:a)", value="aac") | |
audio_bitrate = gr.Textbox(label="音声ビットレート (-b:a)", value="128k") | |
audio_rate = gr.Textbox(label="サンプリングレート (-ar)", value="44100") | |
audio_channels = gr.Textbox(label="チャンネル数 (-ac)", value="2") | |
gr.Markdown("### 映像設定") | |
video_codec = gr.Textbox(label="映像コーデック (-c:v)", value="libx264") | |
video_bitrate = gr.Textbox(label="映像ビットレート (-b:v、省略可)", value="") | |
framerate = gr.Textbox(label="フレームレート (-r)", value="30") | |
pix_fmt = gr.Textbox(label="ピクセルフォーマット (-pix_fmt)", value="yuv420p") | |
compress_btn = gr.Button("圧縮開始") | |
with gr.Column(): | |
result_file = gr.File(label="圧縮された動画ファイル(ダウンロード)") | |
error_output = gr.Textbox(label="エラーメッセージ", lines=2) | |
compress_btn.click( | |
compress_video, | |
inputs=[video_input, resize_mode, resize_value, mute, | |
audio_codec, audio_bitrate, audio_rate, audio_channels, | |
video_codec, video_bitrate, framerate, pix_fmt], | |
outputs=[result_file, error_output] | |
) | |
if __name__ == "__main__": | |
demo.launch() | |