В этой статье мы рассмотрим, как создать простой графический интерфейс (GUI) для редактирования видео с использованием Python и FFmpeg. Мы реализуем функционал для вырезания определенных фрагментов из видео и их объединения. Также рассмотрим, как автоматически подбирать размер окна интерфейса, чтобы все элементы были полностью видны.
Что такое FFmpeg?
FFmpeg — это мощный инструмент для обработки видео и аудио. С его помощью можно вырезать фрагменты видео, объединять их, изменять кодек и многое другое. В нашем примере мы будем использовать FFmpeg для вырезания и объединения видеофрагментов.
Что мы создадим?
Мы создадим приложение с графическим интерфейсом, которое позволит:
- Выбрать входное и выходное видео.
- Добавить временные интервалы (сегменты) для вырезания.
- Обработать видео, вырезав указанные фрагменты и объединив их.
Шаг 1: Установка необходимых инструментов
Перед началом работы убедитесь, что у вас установлены:
- Python (рекомендуется версия 3.7 или выше).
- Библиотека
tkinter
(входит в стандартную поставку Python). - FFmpeg (установите его через терминал или командную строку).
Установите FFmpeg, если он еще не установлен:
- Windows: Скачайте и установите FFmpeg с официального сайта.
- Linux: Используйте пакетный менеджер, например,
sudo apt install ffmpeg
. - macOS: Используйте Homebrew:
brew install ffmpeg
.
Шаг 2: Создание графического интерфейса
Мы будем использовать библиотеку tkinter
для создания графического интерфейса. Вот основной код приложения:
import tkinter as tk
from tkinter import filedialog, messagebox
import subprocess
def cut_and_concat_video(input_video, output_video, segments):
"""
Вырезает определенные фрагменты из видео и объединяет их.
"""
try:
temp_files = []
for i, (start, end) in enumerate(segments):
temp_file = f"temp_{i}.mp4"
temp_files.append(temp_file)
command = [
"ffmpeg",
"-i", input_video,
"-ss", start,
"-to", end,
"-c", "copy",
temp_file
]
subprocess.run(command, check=True)
with open("concat_list.txt", "w") as f:
for temp_file in temp_files:
f.write(f"file '{temp_file}'\n")
concat_command = [
"ffmpeg",
"-f", "concat",
"-safe", "0",
"-i", "concat_list.txt",
"-c", "copy",
output_video
]
subprocess.run(concat_command, check=True)
for temp_file in temp_files:
subprocess.run(["rm", temp_file], check=True)
subprocess.run(["rm", "concat_list.txt"], check=True)
messagebox.showinfo("Успех", "Видео успешно обработано!")
except Exception as e:
messagebox.showerror("Ошибка", f"Произошла ошибка: {e}")
def add_segment():
"""Добавляет новый сегмент в список."""
start = start_entry.get()
end = end_entry.get()
if start and end:
segments_listbox.insert(tk.END, f"{start} - {end}")
start_entry.delete(0, tk.END)
end_entry.delete(0, tk.END)
else:
messagebox.showwarning("Предупреждение", "Заполните оба поля (начало и конец).")
def process_video():
"""Обрабатывает видео на основе выбранных сегментов."""
input_video = input_video_entry.get()
output_video = output_video_entry.get()
segments = []
for item in segments_listbox.get(0, tk.END):
start, end = item.split(" - ")
segments.append((start, end))
if not input_video or not output_video or not segments:
messagebox.showwarning("Предупреждение", "Заполните все поля и добавьте сегменты.")
return
cut_and_concat_video(input_video, output_video, segments)
def select_input_video():
"""Выбирает входное видео."""
file_path = filedialog.askopenfilename(filetypes=[])
if file_path:
input_video_entry.delete(0, tk.END)
input_video_entry.insert(0, file_path)
def select_output_video():
"""Выбирает выходное видео."""
file_path = filedialog.asksaveasfilename(defaultextension=".mp4", filetypes=[])
if file_path:
output_video_entry.delete(0, tk.END)
output_video_entry.insert(0, file_path)
# Создаем графический интерфейс
root = tk.Tk()
root.title("Редактор видео с FFmpeg")
# Входное видео
input_frame = tk.Frame(root)
input_frame.pack(fill=tk.X, padx=10, pady=5)
tk.Label(input_frame, text="Входное видео:").pack(side=tk.LEFT)
input_video_entry = tk.Entry(input_frame, width=50)
input_video_entry.pack(side=tk.LEFT, padx=(10, 0))
select_input_button = tk.Button(input_frame, text="Выбрать", command=select_input_video)
select_input_button.pack(side=tk.LEFT, padx=(10, 0))
# Выходное видео
output_frame = tk.Frame(root)
output_frame.pack(fill=tk.X, padx=10, pady=5)
tk.Label(output_frame, text="Выходное видео:").pack(side=tk.LEFT)
output_video_entry = tk.Entry(output_frame, width=50)
output_video_entry.pack(side=tk.LEFT, padx=(10, 0))
select_output_button = tk.Button(output_frame, text="Выбрать", command=select_output_video)
select_output_button.pack(side=tk.LEFT, padx=(10, 0))
# Добавление сегментов
segments_frame = tk.Frame(root)
segments_frame.pack(fill=tk.X, padx=10, pady=5)
tk.Label(segments_frame, text="Сегменты:").pack(side=tk.LEFT)
start_entry = tk.Entry(segments_frame, width=10)
start_entry.pack(side=tk.LEFT, padx=(10, 0))
tk.Label(segments_frame, text="до").pack(side=tk.LEFT, padx=(10, 0))
end_entry = tk.Entry(segments_frame, width=10)
end_entry.pack(side=tk.LEFT, padx=(10, 0))
add_segment_button = tk.Button(segments_frame, text="Добавить", command=add_segment)
add_segment_button.pack(side=tk.LEFT, padx=(10, 0))
# Список сегментов
segments_listbox = tk.Listbox(root, width=60, height=10)
segments_listbox.pack(padx=10, pady=10)
# Кнопка обработки
process_button = tk.Button(root, text="Обработать видео", command=process_video)
process_button.pack(pady=10)
# Автоматически подбираем размер окна
root.update_idletasks()
root.geometry(f"{root.winfo_width()}x{root.winfo_height()}")
# Запуск приложения
root.mainloop()
Шаг 3: Как работает приложение
- Выбор видео:
- Нажмите кнопку “Выбрать” рядом с полем “Входное видео”, чтобы выбрать файл для обработки.
- Нажмите кнопку “Выбрать” рядом с полем “Выходное видео”, чтобы указать, куда сохранить результат.
- Добавление сегментов:
- Введите время начала и конца фрагмента в формате “чч:мм:сс”.
- Нажмите кнопку “Добавить”, чтобы добавить сегмент в список.
- Обработка видео:
- После добавления всех сегментов нажмите кнопку “Обработать видео”.
- Программа вырежет указанные фрагменты и объединит их в одно видео.
Автоматический подбор размера окна
Чтобы окно интерфейса автоматически подстраивалось под содержимое, мы используем следующие строки кода:
root.update_idletasks()
root.geometry(f"{root.winfo_width()}x{root.winfo_height()}")
Этот код обновляет размеры окна перед его отображением, чтобы все элементы интерфейса были полностью видны.