Python代码-视频帧批处理工具v2025.03.14

import os
import threading
import tkinter as tk
from tkinter import filedialog, messagebox
from moviepy.video.io.VideoFileClip import VideoFileClip
from moviepy.config import change_settings
import customtkinter as ctk
import sys

def get_ffmpeg_path():
    """获取 ffmpeg 的路径"""
    # 获取当前运行的目录
    current_dir = os.path.dirname(sys.executable) if hasattr(sys, '_MEIPASS') else os.path.dirname(__file__)
    ffmpeg_path = os.path.join(current_dir, "ffmpeg.exe")

    # 检查 ffmpeg 文件是否存在
    if not os.path.exists(ffmpeg_path):
        raise FileNotFoundError(f"FFmpeg 文件不存在: {ffmpeg_path}")
    return ffmpeg_path

# 设置 ffmpeg 路径
change_settings({"FFMPEG_BINARY": get_ffmpeg_path()})

class VideoProcessorApp(ctk.CTk):
    def __init__(self):
        super().__init__()
        self.title("视频帧批处理工具v2025.03.14")
        self.geometry("550x800")

        # 初始化变量
        self.selected_files = []
        self.output_folder = ""
        self.suffix = "_processed"

        # 创建 UI
        self.create_widgets()

    def create_widgets(self):
        """创建界面组件"""
        self.video_label = ctk.CTkLabel(self, text="选择视频文件:", font=("Arial", 16))
        self.video_label.pack(pady=10)

        self.video_listbox = ctk.CTkTextbox(self, width=500, height=100, font=("Arial", 12))
        self.video_listbox.pack(pady=10)

        self.select_videos_button = ctk.CTkButton(self, text="添加视频文件", command=self.select_videos)
        self.select_videos_button.pack(pady=10)

        self.output_folder_label = ctk.CTkLabel(self, text="输出文件夹:", font=("Arial", 16))
        self.output_folder_label.pack(pady=10)

        self.output_folder_entry = ctk.CTkEntry(self, width=400, font=("Arial", 12))
        self.output_folder_entry.pack(pady=10)

        self.select_output_folder_button = ctk.CTkButton(self, text="选择文件夹", command=self.select_output_folder)
        self.select_output_folder_button.pack(pady=10)

        self.suffix_label = ctk.CTkLabel(self, text="添加后缀:", font=("Arial", 16))
        self.suffix_label.pack(pady=10)

        self.suffix_entry = ctk.CTkEntry(self, width=200, font=("Arial", 12))
        self.suffix_entry.insert(0, self.suffix)
        self.suffix_entry.pack(pady=10)

        self.progress_bar = ctk.CTkProgressBar(self, width=500)
        self.progress_bar.pack(pady=20)
        self.progress_bar.set(0)

        self.status_label = ctk.CTkLabel(self, text="状态:等待处理", font=("Arial", 14))
        self.status_label.pack(pady=10)

        self.output_textbox = ctk.CTkTextbox(self, width=500, height=100, font=("Arial", 12))
        self.output_textbox.pack(pady=10)

        self.start_button = ctk.CTkButton(self, text="开始处理", command=self.start_processing)
        self.start_button.pack(pady=10)

        self.exit_button = ctk.CTkButton(self, text="退出", command=self.quit)
        self.exit_button.pack(pady=10)

    def select_videos(self):
        file_paths = filedialog.askopenfilenames(
            title="选择视频文件",
            filetypes=[("视频文件", "*.mp4;*.avi;*.mov;*.mkv"), ("所有文件", "*.*")]
        )
        if file_paths:
            self.selected_files = file_paths
            self.video_listbox.delete("1.0", tk.END)
            for file in file_paths:
                self.video_listbox.insert(tk.END, file + "\n")

    def select_output_folder(self):
        folder_path = filedialog.askdirectory(title="选择输出文件夹")
        if folder_path:
            self.output_folder = folder_path
            self.output_folder_entry.delete(0, tk.END)
            self.output_folder_entry.insert(0, folder_path)

    def start_processing(self):
        if not self.selected_files:
            messagebox.showerror("错误", "请先选择视频文件!")
            return
        if not self.output_folder_entry.get():
            messagebox.showerror("错误", "请先选择输出文件夹!")
            return

        self.suffix = self.suffix_entry.get()
        self.output_folder = self.output_folder_entry.get()
        self.output_textbox.delete("1.0", tk.END)

        threading.Thread(target=self.process_videos).start()

    def process_videos(self):
        self.status_label.configure(text="正在处理视频...")
        self.progress_bar.set(0)

        for i, video_path in enumerate(self.selected_files):
            try:
                filename, ext = os.path.splitext(os.path.basename(video_path))
                output_filename = f"{filename}{self.suffix}{ext}"
                output_path = os.path.join(self.output_folder, output_filename)

                self.process_single_video(video_path, output_path)
                self.output_textbox.insert(tk.END, f"已完成: {output_path}\n")
            except Exception as e:
                self.output_textbox.insert(tk.END, f"处理失败: {video_path}\n错误: {str(e)}\n")

            progress = (i + 1) / len(self.selected_files)
            self.progress_bar.set(progress)
            self.status_label.configure(text=f"正在处理: {i + 1}/{len(self.selected_files)}")

        self.status_label.configure(text="所有视频处理完成!")
        self.output_textbox.insert(tk.END, "所有视频处理完成!\n")
        messagebox.showinfo("完成", "所有视频处理完成!")

    def process_single_video(self, input_path, output_path):
        try:
            video = VideoFileClip(input_path)
            self.output_textbox.insert(tk.END, f"成功加载视频: {input_path}\n")
        except Exception as e:
            self.output_textbox.insert(tk.END, f"加载失败: {input_path}\n错误: {str(e)}\n")
            return

        try:
            if video.duration < 2 / video.fps:
                self.output_textbox.insert(tk.END, f"视频时长过短,无法处理: {input_path}\n")
                return

            fps = video.fps
            duration = video.duration
            first_frame_time = 1 / fps
            last_frame_time = duration - (1 / fps)

            self.output_textbox.insert(tk.END, f"正在裁剪视频: {input_path}\n")
            processed_video = video.subclip(first_frame_time, last_frame_time)

            if processed_video is None:
                self.output_textbox.insert(tk.END, f"裁剪后的视频为空: {input_path}\n")
                return

            self.output_textbox.insert(tk.END, f"正在保存视频: {output_path}\n")
            processed_video.write_videofile(output_path, codec="libx264", audio_codec="aac")
            self.output_textbox.insert(tk.END, f"已完成: {output_path}\n")
        except Exception as e:
            self.output_textbox.insert(tk.END, f"处理失败: {input_path}\n错误: {str(e)}\n")


if __name__ == "__main__":
    app = VideoProcessorApp()
    app.mainloop()

标签: none

添加新评论