Python 批量剪片头片尾

脚本实现了以下功能:

  • 加载和保存配置:读取和保存配置文件。
  • 获取视频时长:使用ffprobe获取视频的总时长。
  • 视频剪辑:使用ffmpeg截取视频片段,支持批量处理。
  • 图形用户界面:通过tkinter选择输入文件和输出目录,设置开始和结束时间。
  • 语音提示:在剪辑完成后进行语音提示
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()

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Python可以使用GDAL库来实现批量栅格数据和shp文件。GDAL(Geospatial Data Abstraction Library)是一个开源的地理空间数据处理库,可以用于处理各种栅格和矢量数据格式。 以下是使用Python批量栅格数据和shp文件的步骤: 1. 导入必要的库 ```python from osgeo import gdal, ogr ``` 2. 定义批量函数 ```python def batch_clip(raster_path, shapefile_path, output_path): # 打开栅格数据 raster_dataset = gdal.Open(raster_path) # 打开shp文件 shapefile_dataset = ogr.Open(shapefile_path) # 获取栅格数据的范围 raster_extent = raster_dataset.GetGeoTransform() # 获取shp文件中的要素 shapefile_layer = shapefile_dataset.GetLayer() for feature in shapefile_layer: # 获取要素的外包框 feature_geometry = feature.GetGeometryRef() feature_extent = feature_geometry.GetEnvelope() # 裁栅格数据 gdal.Warp(output_path + feature.GetField('id') + '.tif', raster_dataset, outputBounds=feature_extent) ``` 3. 调用批量函数 ```python raster_path = 'input_raster.tif' shapefile_path = 'input_shapefile.shp' output_path = 'output/' batch_clip(raster_path, shapefile_path, output_path) ``` 这将会将栅格数据按照shp文件中每个要素的外包框进行裁,并将裁后的栅格数据保存到指定的输出路径。 需要注意的是,这里假设栅格数据和shp文件的投影是一致的,如果投影不一致,需要先进行投影转换。同时也可以根据不同的需求对裁的方法进行调整,比如指定裁使用的插值方法、裁后的栅格分辨率等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

数据服务生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值