Skip to content

Instantly share code, notes, and snippets.

@krisstibex
Last active May 14, 2025 22:26
Show Gist options
  • Save krisstibex/0bfaf72b63734b89ff89ba791a7c1d7f to your computer and use it in GitHub Desktop.
Save krisstibex/0bfaf72b63734b89ff89ba791a7c1d7f to your computer and use it in GitHub Desktop.
使用FFMPEG的视频处理压缩gui脚本 需要安装FFMPEG
tkinter
tkinterdnd2
subprocess32
import tkinter as tk
from tkinter import filedialog, messagebox
from tkinterdnd2 import DND_FILES, TkinterDnD
import subprocess
import os
from pathlib import Path
def select_file():
file_path = filedialog.askopenfilename(filetypes=[("Video Files", "*.mp4 *.mkv *.mov *.avi")])
input_path_var.set(file_path)
update_output_name()
def handle_drop(data):
if data:
path = data.strip('{}') # Windows 拖拽带花括号
input_path_var.set(path)
update_output_name()
def update_output_name():
input_path = input_path_var.get()
if input_path:
name = Path(input_path).stem
ext = ".mkv" if codec_var.get() == "libsvtav1" else ".mp4"
output_name_var.set(f"compressed_{name}{ext}")
def toggle_crf_mode():
use_crf = use_crf_var.get()
crf_entry.config(state="normal" if use_crf else "disabled")
bitrate_entry.config(state="disabled" if use_crf else "normal")
def toggle_audio_settings():
state = "disabled" if remove_audio_var.get() else "normal"
audio_codec_menu.config(state=state)
audio_bitrate_entry.config(state=state)
def compress():
input_path = input_path_var.get()
if not Path(input_path).is_file():
messagebox.showerror("错误", "无效输入文件路径")
return
output_path = Path.cwd() / output_name_var.get()
cmd = ["ffmpeg", "-y", "-i", input_path]
if resolution_var.get():
cmd += ["-s", resolution_var.get()]
if framerate_var.get():
cmd += ["-r", framerate_var.get()]
cmd += ["-c:v", codec_var.get()]
if use_crf_var.get():
cmd += ["-crf", crf_var.get()]
else:
cmd += ["-b:v", bitrate_var.get()]
cmd += ["-preset", preset_var.get()]
if threads_var.get().isdigit() and int(threads_var.get()) > 0:
cmd += ["-threads", threads_var.get()]
if remove_audio_var.get():
cmd += ["-an"]
else:
cmd += ["-c:a", audio_codec_var.get(), "-b:a", audio_bitrate_var.get()]
if two_pass_var.get():
null_output = "NUL" if os.name == 'nt' else "/dev/null"
try:
subprocess.run(cmd + ["-pass", "1", "-f", "null", null_output], check=True)
subprocess.run(cmd + ["-pass", "2", str(output_path)], check=True)
except subprocess.CalledProcessError:
messagebox.showerror("错误", "两遍压缩失败!")
return
else:
cmd += [str(output_path)]
try:
subprocess.run(cmd, check=True)
except subprocess.CalledProcessError:
messagebox.showerror("错误", "压缩失败,请检查 FFmpeg 是否正确安装")
return
messagebox.showinfo("完成", f"压缩完成:\n{output_path}")
# ----------------- GUI 构建 -----------------
app = TkinterDnD.Tk()
app.title("🎬 FFmpeg 视频压缩器(拖放支持)")
# 输入文件路径(拖放)
input_path_var = tk.StringVar()
tk.Label(app, text="视频文件路径").grid(row=0, column=0, sticky="e")
input_entry = tk.Entry(app, textvariable=input_path_var, width=40)
input_entry.grid(row=0, column=1)
input_entry.drop_target_register(DND_FILES)
input_entry.dnd_bind('<<Drop>>', lambda e: handle_drop(e.data))
tk.Button(app, text="选择文件", command=select_file).grid(row=0, column=2)
# 编码器
codec_var = tk.StringVar(value="libx264")
tk.Label(app, text="视频编码器").grid(row=1, column=0, sticky="e")
tk.OptionMenu(app, codec_var, "libx264", "libx265", "libsvtav1", command=lambda _: update_output_name()).grid(row=1, column=1, sticky="w")
# 分辨率和帧率
resolution_var = tk.StringVar(value="1280x720")
framerate_var = tk.StringVar(value="30")
tk.Label(app, text="分辨率").grid(row=2, column=0, sticky="e")
tk.Entry(app, textvariable=resolution_var).grid(row=2, column=1, sticky="w")
tk.Label(app, text="帧率").grid(row=3, column=0, sticky="e")
tk.Entry(app, textvariable=framerate_var).grid(row=3, column=1, sticky="w")
# CRF / 码率
use_crf_var = tk.BooleanVar(value=True)
tk.Checkbutton(app, text="使用 CRF 模式", variable=use_crf_var, command=toggle_crf_mode).grid(row=4, column=1, sticky="w")
crf_var = tk.StringVar(value="23")
bitrate_var = tk.StringVar(value="2000k")
tk.Label(app, text="CRF").grid(row=5, column=0, sticky="e")
crf_entry = tk.Entry(app, textvariable=crf_var)
crf_entry.grid(row=5, column=1, sticky="w")
tk.Label(app, text="码率").grid(row=6, column=0, sticky="e")
bitrate_entry = tk.Entry(app, textvariable=bitrate_var)
bitrate_entry.grid(row=6, column=1, sticky="w")
# 音频设置
remove_audio_var = tk.BooleanVar(value=False)
tk.Checkbutton(app, text="移除音频", variable=remove_audio_var, command=toggle_audio_settings).grid(row=7, column=1, sticky="w")
audio_codec_var = tk.StringVar(value="aac")
audio_bitrate_var = tk.StringVar(value="128k")
tk.Label(app, text="音频编码器").grid(row=8, column=0, sticky="e")
audio_codec_menu = tk.OptionMenu(app, audio_codec_var, "aac", "libopus", "libmp3lame")
audio_codec_menu.grid(row=8, column=1, sticky="w")
tk.Label(app, text="音频码率").grid(row=9, column=0, sticky="e")
audio_bitrate_entry = tk.Entry(app, textvariable=audio_bitrate_var)
audio_bitrate_entry.grid(row=9, column=1, sticky="w")
# 高级设置
preset_var = tk.StringVar(value="medium")
threads_var = tk.StringVar(value="0")
two_pass_var = tk.BooleanVar(value=False)
tk.Label(app, text="Preset").grid(row=10, column=0, sticky="e")
tk.OptionMenu(app, preset_var, "ultrafast", "fast", "medium", "slow", "veryslow").grid(row=10, column=1, sticky="w")
tk.Label(app, text="线程数").grid(row=11, column=0, sticky="e")
tk.Entry(app, textvariable=threads_var).grid(row=11, column=1, sticky="w")
tk.Checkbutton(app, text="启用两遍压缩", variable=two_pass_var).grid(row=12, column=1, sticky="w")
# 输出文件名(自动)
output_name_var = tk.StringVar(value="compressed_output.mp4")
tk.Label(app, text="输出文件名").grid(row=13, column=0, sticky="e")
tk.Entry(app, textvariable=output_name_var, width=30).grid(row=13, column=1, sticky="w")
# 开始压缩
tk.Button(app, text="开始压缩", command=compress, bg="#4CAF50", fg="white", width=20).grid(row=14, column=1, pady=10)
toggle_crf_mode()
toggle_audio_settings()
app.mainloop()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment