soiz1's picture
Update app.py
ea25738 verified
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()