吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 2550|回复: 69
收起左侧

[Python 原创] 批量剪视频片头片尾

  [复制链接]
是谁的大海 发表于 2024-7-12 18:01
本帖最后由 是谁的大海 于 2024-7-13 13:02 编辑

批量剪片头片尾的小软件(ps:不好用的话,就找别的软件哦)
这个脚本实现了以下功能:
  • 加载和保存配置:读取和保存配置文件。
  • 获取视频时长:使用ffprobe获取视频的总时长。
  • 视频剪辑:使用ffmpeg截取视频片段,支持批量处理。
  • 图形用户界面:通过tkinter选择输入文件和输出目录,设置开始和结束时间。
  • 语音提示:在剪辑完成后进行语音提示。
  • 软件截图
  • image.png
  • 来了来了久等了,昨天睡着了
  • 修复了(选择了保存位置被强行固定没办法修改的问题)
  • 脑容量有限不知道还有什么好做的
https://wwl.lanzn.com/b03c9aoc5c
密码:i88q
[Asm] 纯文本查看 复制代码
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功能特性:\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()
image.png

免费评分

参与人数 9吾爱币 +15 热心值 +8 收起 理由
qfqha + 1 + 1 我很赞同!
苏紫方璇 + 7 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
alexsanda + 1 + 1 谢谢@Thanks!
daoye9988 + 1 + 1 谢谢@Thanks!
xinxin99 + 1 + 1 用心讨论,共获提升!
wapj2900958 + 1 + 1 时间略不同,可否倒数多少秒
shimin20089 + 1 我很赞同!
a553366 + 1 + 1 谢谢@Thanks!
lizy169 + 1 + 1 谢谢@Thanks!

查看全部评分

本帖被以下淘专辑推荐:

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

chm1988 发表于 2024-7-12 20:35
坐等链接 感谢
云的彼岸918 发表于 2024-7-17 16:48
楼主,能不能增加个从片头和片尾两头往回数秒的功能?有的视频中间时长不一样,但片头片尾的时间长度是一样的,谢谢!
shen12wang 发表于 2024-7-12 20:27
WJFCYLIB 发表于 2024-7-12 20:35
很有有意思
ruanxiaoqi 发表于 2024-7-12 20:41
这样可以高效完成任务,那能控制的的准确吗
Afyj333 发表于 2024-7-12 20:46
这样就能高效去掉软件强加的片头片尾了,不错的东西,值得等待
yue1993713 发表于 2024-7-12 20:57
这个脚本感觉有点厉害的,可以试试先
shanzhanzhe 发表于 2024-7-12 21:42
这个不错,学习研究一下
头像被屏蔽
xu313 发表于 2024-7-12 21:54
提示: 作者被禁止或删除 内容自动屏蔽
流浪情人 发表于 2024-7-12 22:05
可以选择去除那些广告什么的 挺不错的
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-12-13 02:37

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表