Python进行批量剪辑视频片头片尾

 更新时间:2025年01月09日 10:37:51   作者:黑客白泽  
这篇文章主要为大家详细介绍了如何使用Python进行批量剪辑视频片头片尾功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下

1.简介

批量剪辑片头片尾的软件,让你的视频创作事半功倍,视频剪辑处理完成后,用户可以在指定文件夹中查看已经剪切完片头片尾的视频‌。这些工具不仅适用于个人用户进行日常的视频编辑工作,也适合视频制作团队在项目制作中提高工作效率。

功能:

加载和保存配置:读取和保存配置文件。

获取视频时长:使用ffprobe获取视频的总时长。

视频剪辑:使用ffmpeg截取视频片段,支持批量处理。

图形用户界面:通过tkinter选择输入文件和输出目录,设置开始和结束时间。

语音提示:在剪辑完成后进行语音提示。

2.运行效果

3.相关源码

import os
import subprocess
import threading
import tkinter as tk
from tkinter import filedialog
from tkinter import messagebox
from tkinter import ttk
import json
from datetime import datetime, timedelta
import pyttsx3
 
INTERNAL_FOLDER = os.path.join(os.path.dirname(__file__), '_internal')
CONFIG_FILE = os.path.join(INTERNAL_FOLDER, 'config.json')
FFMPEG_FOLDER = os.path.join(INTERNAL_FOLDER, 'ffmpeg_folder')
COMPLETION_FILE = os.path.join(INTERNAL_FOLDER, 'completed.txt')
ICON_PATH = os.path.join(INTERNAL_FOLDER, 'moviecamera.ico')  # 使用_internal文件夹中的图标
 
def ensure_internal_dir_exists():
    if not os.path.exists(INTERNAL_FOLDER):
        os.makedirs(INTERNAL_FOLDER)
 
def load_config():
    if os.path.exists(CONFIG_FILE):
        with open(CONFIG_FILE, 'r', encoding='utf-8') as f:
            return json.load(f)
    return {}
 
def save_config(config):
    ensure_internal_dir_exists()
    with open(CONFIG_FILE, 'w', encoding='utf-8') as f:
        json.dump(config, f)
 
def format_time(hours, minutes, seconds, milliseconds):
    return f"{int(hours):02}:{int(minutes):02}:{int(seconds):02}.{int(milliseconds):03}"
 
def get_video_duration(input_path):
    ffprobe_path = os.path.join(FFMPEG_FOLDER, 'ffprobe.exe')
    if not os.path.exists(ffprobe_path):
        messagebox.showerror("错误", "找不到 ffprobe 可执行文件")
        return 0
    result = subprocess.run(
        [ffprobe_path, "-v", "error", "-show_entries", "format=duration", "-of", "default=noprint_wrappers=1:nokey=1", input_path],
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
        universal_newlines=True
    )
    try:
        return float(result.stdout)
    except ValueError:
        print("FFprobe输出:", result.stdout)  # 打印ffprobe标准输出以便调试
        print("FFprobe错误:", result.stderr)  # 打印ffprobe错误输出以便调试
        messagebox.showerror("错误", "无法获取视频时长,请检查输入文件")
        return 0
 
def trim_video(input_path, output_path, start_time, end_time):
    ffmpeg_path = os.path.join(FFMPEG_FOLDER, 'ffmpeg.exe')
    if not os.path.exists(ffmpeg_path):
        messagebox.showerror("错误", "找不到 ffmpeg 可执行文件")
        return
 
    if start_time == "00:00:00.000" and end_time == "00:00:00.000":
        messagebox.showerror("错误", "请至少选择一个开始时间或结束时间")
        return
 
    duration = get_video_duration(input_path)
    if duration == 0:
        return
 
    start_time_seconds = timedelta(
        hours=int(start_time.split(":")[0]),
        minutes=int(start_time.split(":")[1]),
        seconds=int(start_time.split(":")[2].split(".")[0]),
        milliseconds=int(start_time.split(":")[2].split(".")[1])
    ).total_seconds()
 
    end_time_seconds = timedelta(
        hours=int(end_time.split(":")[0]),
        minutes=int(end_time.split(":")[1]),
        seconds=int(end_time.split(":")[2].split(".")[0]),
        milliseconds=int(end_time.split(":")[2].split(".")[1])
    ).total_seconds()
 
    trim_duration = duration - start_time_seconds - end_time_seconds
    if trim_duration <= 0:
        messagebox.showerror("错误", "剪辑后的持续时间小于等于0")
        return
 
    cmd = [
        ffmpeg_path,
        '-ss', start_time,
        '-i', input_path,
        '-t', str(trim_duration),
        '-vcodec', 'copy',
        '-acodec', 'copy',
        output_path,
        '-y'
    ]
 
    print("执行FFmpeg命令:", " ".join(cmd))  # 打印命令以便调试
 
    startupinfo = subprocess.STARTUPINFO()
    startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
 
    result = subprocess.run(cmd, startupinfo=startupinfo, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True, encoding='utf-8')
    print("FFmpeg输出:", result.stdout)  # 打印FFmpeg标准输出
    print("FFmpeg错误:", result.stderr)  # 打印FFmpeg错误输出
 
    if result.returncode != 0:
        messagebox.showerror("错误", f"FFmpeg执行失败: {result.stderr}")
 
def batch_trim_videos(input_files, output_dir, start_time, end_time):
    if not os.path.exists(output_dir):
        os.makedirs(output_dir)
 
    for input_file in input_files:
        output_file = os.path.join(output_dir, os.path.basename(input_file))
        trim_video(input_file, output_file, start_time, end_time)
     
    with open(COMPLETION_FILE, 'w', encoding='utf-8') as f:
        f.write("老弟已经完成了,你开心吗")
    print("视频剪辑完成标志已写入文件")
 
def select_input_files():
    files = filedialog.askopenfilenames(filetypes=[("MP4 files", "*.mp4")])
    if files:
        entry_input_files.delete(0, tk.END)
        entry_input_files.insert(0, f"已选择 {len(files)} 个文件")
        entry_input_files.files = files
 
def select_output_directory():
    directory = filedialog.askdirectory()
    if directory:
        entry_output_directory.config(state='normal')
        entry_output_directory.delete(0, tk.END)
        entry_output_directory.insert(0, directory)
        entry_output_directory.config(state='disabled', disabledbackground='#d9d9d9', disabledforeground='#000000')
        label_status.config(text="输出目录已选择。请选择开始时间和结束时间。")
        config['output_directory'] = directory
        save_config(config)
 
def validate_time_format(time_str):
    try:
        datetime.strptime(time_str, '%H:%M:%S.%f')
        return True
    except ValueError:
        return False
 
def start_trimming():
    input_files = getattr(entry_input_files, 'files', [])
    output_directory = entry_output_directory.get()
    start_time = format_time(var_start_hours.get(), var_start_minutes.get(), var_start_seconds.get(), var_start_milliseconds.get())
    end_time = format_time(var_end_hours.get(), var_end_minutes.get(), var_end_seconds.get(), var_end_milliseconds.get())
 
    # 验证时间格式
    if not validate_time_format(start_time) or not validate_time_format(end_time):
        messagebox.showerror("错误", "时间格式不正确")
        return
 
    if start_time == "00:00:00.000" and end_time == "00:00:00.000":
        messagebox.showerror("错误", "请至少选择一个开始时间或结束时间")
        return
 
    if not input_files:
        messagebox.showwarning("警告", "请选择输入文件!")
        return
    if not output_directory:
        messagebox.showwarning("警告", "请选择输出目录!")
        return
 
    threading.Thread(target=batch_trim_videos, args=(input_files, output_directory, start_time, end_time)).start()
    threading.Thread(target=monitor_completion).start()
 
def monitor_completion():
    while not os.path.exists(COMPLETION_FILE):
        pass
    with open(COMPLETION_FILE, 'r', encoding='utf-8') as f:
        message = f.read()
    print("检测到完成标志文件,内容:", message)
    speak(message)
 
def speak(text):
    print("初始化语音引擎")
    engine = pyttsx3.init()
    print("语音引擎初始化成功")
    engine.say(text)
    print("语音引擎开始说话")
    engine.runAndWait()
    print("语音引擎说话结束")
 
def show_about():
    about_window = tk.Toplevel(root)
    about_window.title("关于")
    about_window.geometry("400x300")
    about_window.resizable(False, False)
     
    text = tk.Text(about_window, wrap='word', height=15, width=50)
    text.insert(tk.END, "n这是一个用来截取视频片段的小工具。\n\n功能特性:\n"
                        "1. 支持批量视频截取\n"
                        "2. 支持自定义开始时间和结束时间\n"
                        "3. 使用 FFmpeg 进行视频处理\n"
                        "4. 提供简单易用的图形用户界面\n\n"
                        "感谢使用本工具!(是无损快剪哈)")
    text.config(state='disabled')
     
    scrollbar = tk.Scrollbar(about_window, command=text.yview)
    text.config(yscrollcommand=scrollbar.set)
     
    text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
    scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
 
config = load_config()
 
root = tk.Tk()
root.title("视频截取工具")
root.geometry("510x250")
root.resizable(False, False)
root.iconbitmap(ICON_PATH)  # 设置窗口图标
 
# 创建菜单栏
menu_bar = tk.Menu(root)
root.config(menu=menu_bar)
 
# 创建菜单
menu = tk.Menu(menu_bar, tearoff=0)
menu_bar.add_cascade(label="菜单", menu=menu)
menu.add_command(label="关于", command=show_about)
 
tk.Label(root, text="选择输入文件:").grid(row=0, column=0, padx=5, pady=5, sticky='e')
entry_input_files = tk.Entry(root, width=50)
entry_input_files.grid(row=0, column=1, padx=5, pady=5, columnspan=4, sticky='w')
entry_input_files.files = []
tk.Button(root, text="浏览", command=select_input_files).grid(row=0, column=5, padx=5, pady=5, sticky='w')
 
tk.Label(root, text="选择输出目录:").grid(row=1, column=0, padx=5, pady=5, sticky='e')
entry_output_directory = tk.Entry(root, width=50)
entry_output_directory.grid(row=1, column=1, padx=5, pady=5, columnspan=4, sticky='w')
tk.Button(root, text="浏览", command=select_output_directory).grid(row=1, column=5, padx=5, pady=5, sticky='w')
 
if 'output_directory' in config:
    entry_output_directory.insert(0, config['output_directory'])
    entry_output_directory.config(state='disabled', disabledbackground='#d9d9d9', disabledforeground='#000000')
    label_status = tk.Label(root, text="输出目录已选择。请选择开始时间和结束时间。")
else:
    label_status = tk.Label(root, text="请选择输出目录。")
 
label_status.grid(row=4, column=0, columnspan=6, pady=10)
 
hours = [f"{i:02}" for i in range(24)]
minutes_seconds = [f"{i:02}" for i in range(60)]
milliseconds = [f"{i:03}" for i in range(1000)]
 
var_start_hours = tk.StringVar(value="00")
var_start_minutes = tk.StringVar(value="00")
var_start_seconds = tk.StringVar(value="00")
var_start_milliseconds = tk.StringVar(value="000")
 
var_end_hours = tk.StringVar(value="00")
var_end_minutes = tk.StringVar(value="00")
var_end_seconds = tk.StringVar(value="00")
var_end_milliseconds = tk.StringVar(value="000")
 
def create_time_frame(root, var_hours, var_minutes, var_seconds, var_milliseconds):
    frame = tk.Frame(root)
    ttk.Combobox(frame, textvariable=var_hours, values=hours, width=3).pack(side='left')
    tk.Label(frame, text="时").pack(side='left', padx=3)
    ttk.Combobox(frame, textvariable=var_minutes, values=minutes_seconds, width=3).pack(side='left')
    tk.Label(frame, text="分").pack(side='left', padx=3)
    ttk.Combobox(frame, textvariable=var_seconds, values=minutes_seconds, width=3).pack(side='left')
    tk.Label(frame, text="秒").pack(side='left', padx=3)
    ttk.Combobox(frame, textvariable=var_milliseconds, values=milliseconds, width=4).pack(side='left')
    tk.Label(frame, text="毫秒").pack(side='left', padx=3)
    return frame
 
tk.Label(root, text="开始时间:").grid(row=2, column=0, padx=5, pady=5, sticky='e')
start_time_frame = create_time_frame(root, var_start_hours, var_start_minutes, var_start_seconds, var_start_milliseconds)
start_time_frame.grid(row=2, column=1, columnspan=5, padx=5, pady=5, sticky='w')
 
tk.Label(root, text="结束时间:").grid(row=3, column=0, padx=5, pady=5, sticky='e')
end_time_frame = create_time_frame(root, var_end_hours, var_end_minutes, var_end_seconds, var_end_milliseconds)
end_time_frame.grid(row=3, column=1, columnspan=5, padx=5, pady=5, sticky='w')
 
tk.Button(root, text="开始截取", command=start_trimming).grid(row=5, column=0, columnspan=6, pady=10, sticky='ew')
 
root.mainloop()

到此这篇关于Python进行批量剪辑视频片头片尾的文章就介绍到这了,更多相关Python剪辑视频内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 详析Python面向对象中的继承

    详析Python面向对象中的继承

    这篇文章主要详析Python面向对象中的继承,类继承作为python的三大特性之一,在我们学习python的时候是必不可少的。使用类继承,能够大大减少重复代码的编写,下文详细内容需要的小伙伴可以参考一下
    2022-03-03
  • Python NLTK库全面解析及代码示例(NLP核心库)

    Python NLTK库全面解析及代码示例(NLP核心库)

    NLTK是Python中用于自然语言处理(NLP)的核心库,提供了丰富的文本处理工具、算法和语料库,下面通过实例代码场景分析给大家详细介绍Python NLTK库全面解析,感兴趣的朋友一起看看吧
    2025-06-06
  • LyScript实现Hook隐藏调试器的方法详解

    LyScript实现Hook隐藏调试器的方法详解

    LyScript 插件集成的内置API函数可灵活的实现绕过各类反调试保护机制。本文将运用LyScript实现绕过大多数通用调试机制,实现隐藏调试器的目的,需要的可以参考一下
    2022-09-09
  • 如何使用Python创建json文件

    如何使用Python创建json文件

    众所周知JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,这篇文章主要给大家介绍了关于如何使用Python创建json文件的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2023-06-06
  • python通过colorama模块在控制台输出彩色文字的方法

    python通过colorama模块在控制台输出彩色文字的方法

    这篇文章主要介绍了python通过colorama模块在控制台输出彩色文字的方法,实例分析了colorama模块的功能及相关使用技巧,具有一定参考借鉴价值,需要的朋友可以参考下
    2015-03-03
  • python3中关于excel追加写入格式被覆盖问题(实例代码)

    python3中关于excel追加写入格式被覆盖问题(实例代码)

    这篇文章主要介绍了python3中关于excel追加写入格式被覆盖问题,本文通过实例代码给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-01-01
  • python数据处理 根据颜色对图片进行分类的方法

    python数据处理 根据颜色对图片进行分类的方法

    今天小编就为大家分享一篇python数据处理 根据颜色对图片进行分类的方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-12-12
  • Python3中的2to3转换工具使用示例

    Python3中的2to3转换工具使用示例

    这篇文章主要介绍了Python3中的2to3转换工具使用示例,本文详细讲解了使用的步骤,并总结了一些使用注意事项,需要的朋友可以参考下
    2015-06-06
  • Python中实现WebSocket的示例详解

    Python中实现WebSocket的示例详解

    WebSocket 是一种计算机通信协议,它为客户端和服务器之间的双向通信提供了一个全双工的通道,本篇文章将详解 Python 中如何实现 WebSocket,感兴趣的可以了解下
    2025-03-03
  • Python学习之路安装pycharm的教程详解

    Python学习之路安装pycharm的教程详解

    pycharm 是一款功能强大的 Python 编辑器,具有跨平台性。这篇文章主要介绍了Python学习之路安装pycharm的教程,本文分步骤通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-06-06

最新评论