win10(三)視頻剪裁

上傳一刻相冊,有30M大小限制。這個軟件能免費剪裁視頻而且支持手機的H.265格式,這個格式目前連potplayer都支持不好。但是配合FFmpeg可以檢測并且能按大小(或時間)剪裁,并上傳到一刻相冊上播放。

下載FFmpeg的方法:打開網站

Download FFmpeg

然后選擇Windows由gyan.dev構建,再將下載的壓縮包解壓為文件夾,將文件夾放到自己想放的位置,并在環境變量里添加其中bin文件夾的位置。

例如解壓為ffmpeg文件夾放到C盤表面后,在環境變量Path里增加C:\ffmpeg\bin即可。

以下是批量剪裁python代碼:

# coding=utf-8
import tkinter as tk
from tkinter import ttk, filedialog, messagebox
import subprocess
import os
import threading
import queue
import time
import json

class VideoSplitterApp:
? ? def __init__(self, root):
? ? ? ? self.root = root
? ? ? ? self.root.title("視頻分割工具")
? ? ? ? self.root.geometry("500x400")
? ? ? ? self.root.configure(bg='light yellow') ?# 設置背景為黃色
? ? ? ??
? ? ? ? self.video_paths = []
? ? ? ? self.split_method = tk.StringVar()
? ? ? ? self.split_method.set("size") ?# 默認按大小分割
? ? ? ? self.split_threads = []
? ? ? ? self.output_dir = None
? ? ? ? self.current_process = None
? ? ? ? self.is_cancelled = False
? ? ? ? self.progress_value = tk.DoubleVar()
? ? ? ? self.current_video_index = 0
? ? ? ? self.progress_queue = queue.Queue()
? ? ? ??
? ? ? ? # 設置字體
? ? ? ? self.font_style = ("FangSong", 12)
? ? ? ? self.button_font_style = ("FangSong", 12, "bold")
? ? ? ??
? ? ? ? # 糖果色按鈕顏色
? ? ? ? self.candy_colors = ['#FFB6C1', '#87CEFA', '#98FB98', '#DDA0DD', '#FFD700']
? ? ? ??
? ? ? ? self.create_widgets()
? ? ? ??
? ? ? ? # 定期檢查進度隊列
? ? ? ? self.check_progress_queue()
? ? ? ??
? ? ? ? # 檢查FFmpeg是否可用
? ? ? ? self.check_ffmpeg_available()
? ? ? ??
? ? def check_ffmpeg_available(self):
? ? ? ? """檢查系統是否安裝了FFmpeg"""
? ? ? ? try:
? ? ? ? ? ? result = subprocess.run(['ffmpeg', '-version'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
? ? ? ? ? ? if result.returncode != 0:
? ? ? ? ? ? ? ? messagebox.showerror("錯誤", "未找到FFmpeg。請確保已正確安裝FFmpeg并將其添加到系統PATH中。")
? ? ? ? except FileNotFoundError:
? ? ? ? ? ? messagebox.showerror("錯誤", "未找到FFmpeg。請確保已正確安裝FFmpeg并將其添加到系統PATH中。")
? ? ? ??
? ? def create_widgets(self):
? ? ? ? # 文件選擇部分
? ? ? ? self.file_frame = tk.Frame(self.root, bg='light yellow')
? ? ? ? self.file_frame.pack(pady=10, fill=tk.X, padx=10)
? ? ? ??
? ? ? ? self.select_button = tk.Button(self.file_frame, text="選擇視頻文件",?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? command=self.select_video,?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? bg=self.candy_colors[0],
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? font=self.button_font_style,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? relief=tk.RAISED, bd=2)
? ? ? ? self.select_button.pack(side=tk.LEFT, padx=5)
? ? ? ??
? ? ? ? self.file_label = tk.Label(self.file_frame, text="未選擇文件",?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? wraplength=300, bg='light yellow',?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? font=self.font_style)
? ? ? ? self.file_label.pack(side=tk.LEFT, padx=5, fill=tk.X, expand=True)
? ? ? ??
? ? ? ? # 分割方式選擇
? ? ? ? self.method_frame = tk.Frame(self.root, bg='light yellow')
? ? ? ? self.method_frame.pack(pady=10, fill=tk.X, padx=10)
? ? ? ??
? ? ? ? self.time_radio = tk.Radiobutton(self.method_frame, text="按時間分割",?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? variable=self.split_method, value="time",?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? command=self.toggle_input,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? bg='light yellow', font=self.font_style)
? ? ? ? self.time_radio.pack(side=tk.LEFT, padx=5)
? ? ? ??
? ? ? ? self.size_radio = tk.Radiobutton(self.method_frame, text="按大小分割",?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? variable=self.split_method, value="size",?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? command=self.toggle_input,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? bg='light yellow', font=self.font_style)
? ? ? ? self.size_radio.pack(side=tk.LEFT, padx=5)
? ? ? ??
? ? ? ? # 輸入框
? ? ? ? self.input_frame = tk.Frame(self.root, bg='light yellow')
? ? ? ? self.input_frame.pack(pady=10, fill=tk.X, padx=10)
? ? ? ??
? ? ? ? self.time_label = tk.Label(self.input_frame, text="分割時間(秒):",
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? bg='light yellow', font=self.font_style)
? ? ? ? self.time_label.pack(side=tk.LEFT, padx=5)
? ? ? ??
? ? ? ? self.time_entry = tk.Entry(self.input_frame, width=10, font=self.font_style)
? ? ? ? self.time_entry.pack(side=tk.LEFT, padx=5)
? ? ? ??
? ? ? ? self.size_label = tk.Label(self.input_frame, text="分割大小(MB):",
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? bg='light yellow', font=self.font_style)
? ? ? ? self.size_label.pack(side=tk.LEFT, padx=5)
? ? ? ??
? ? ? ? self.size_entry = tk.Entry(self.input_frame, width=10, font=self.font_style)
? ? ? ? self.size_entry.insert(0, "28") ?# 默認大小20MB
? ? ? ? self.size_entry.pack(side=tk.LEFT, padx=5)
? ? ? ??
? ? ? ? self.toggle_input() ?# 初始化時禁用不需要的輸入框
? ? ? ??
? ? ? ? # 輸出目錄選擇
? ? ? ? self.output_frame = tk.Frame(self.root, bg='light yellow')
? ? ? ? self.output_frame.pack(pady=5, fill=tk.X, padx=10)
? ? ? ??
? ? ? ? self.output_button = tk.Button(self.output_frame,?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?text="選擇輸出文件夾",
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?command=self.select_output_dir,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?bg=self.candy_colors[1],
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?font=self.button_font_style,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?relief=tk.RAISED, bd=2)
? ? ? ? self.output_button.pack(side=tk.LEFT, padx=5)
? ? ? ??
? ? ? ? self.output_label = tk.Label(self.output_frame,?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?text="默認在原目錄創建split_output",
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?wraplength=300, bg='light yellow',?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?font=self.font_style)
? ? ? ? self.output_label.pack(side=tk.LEFT, padx=5, fill=tk.X, expand=True)
? ? ? ??
? ? ? ? # 進度條
? ? ? ? self.progress_frame = tk.Frame(self.root, bg='light yellow')
? ? ? ? self.progress_frame.pack(pady=10, fill=tk.X, padx=10)
? ? ? ??
? ? ? ? self.progress_label = tk.Label(self.progress_frame, text="準備就緒",
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? bg='light yellow', font=self.font_style)
? ? ? ? self.progress_label.pack(fill=tk.X)
? ? ? ??
? ? ? ? self.progress_bar = ttk.Progressbar(self.progress_frame,?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?variable=self.progress_value,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?maximum=100)
? ? ? ? self.progress_bar.pack(fill=tk.X, pady=5)
? ? ? ??
? ? ? ? # 操作按鈕
? ? ? ? self.button_frame = tk.Frame(self.root, bg='light yellow')
? ? ? ? self.button_frame.pack(pady=10, fill=tk.X, padx=10)
? ? ? ??
? ? ? ? self.start_button = tk.Button(self.button_frame, text="開始分割",?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?command=self.start_split,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?bg=self.candy_colors[2],
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?font=self.button_font_style,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?relief=tk.RAISED, bd=2)
? ? ? ? self.start_button.pack(side=tk.LEFT, padx=5)
? ? ? ??
? ? ? ? self.cancel_button = tk.Button(self.button_frame, text="取消",?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? command=self.cancel_split,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? bg=self.candy_colors[3],
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? font=self.button_font_style,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? relief=tk.RAISED, bd=2)
? ? ? ? self.cancel_button.pack(side=tk.LEFT, padx=5)
? ??
? ? def check_progress_queue(self):
? ? ? ? """定期檢查進度隊列并更新UI"""
? ? ? ? try:
? ? ? ? ? ? while True:
? ? ? ? ? ? ? ? message = self.progress_queue.get_nowait()
? ? ? ? ? ? ? ? if message.startswith("PROGRESS:"):
? ? ? ? ? ? ? ? ? ? progress = float(message.split(":")[1])
? ? ? ? ? ? ? ? ? ? self.progress_value.set(progress)
? ? ? ? ? ? ? ? else:
? ? ? ? ? ? ? ? ? ? self.progress_label.config(text=message)
? ? ? ? except queue.Empty:
? ? ? ? ? ? pass
? ? ? ? finally:
? ? ? ? ? ? # 每100毫秒檢查一次隊列
? ? ? ? ? ? self.root.after(100, self.check_progress_queue)
? ??
? ? def select_video(self):
? ? ? ? file_paths = filedialog.askopenfilenames(
? ? ? ? ? ? title="選擇視頻文件",
? ? ? ? ? ? filetypes=[("視頻文件", "*.mp4 *.avi *.mkv *.mov *.flv *.wmv *.webm")]
? ? ? ? )
? ? ? ? if file_paths:
? ? ? ? ? ? self.video_paths = list(file_paths)
? ? ? ? ? ? if len(file_paths) == 1:
? ? ? ? ? ? ? ? self.file_label.config(text=os.path.basename(file_paths[0]))
? ? ? ? ? ? else:
? ? ? ? ? ? ? ? self.file_label.config(text=f"已選擇 {len(file_paths)} 個文件")
? ??
? ? def toggle_input(self):
? ? ? ? if self.split_method.get() == "time":
? ? ? ? ? ? self.time_entry.config(state="normal")
? ? ? ? ? ? self.size_entry.config(state="disabled")
? ? ? ? else:
? ? ? ? ? ? self.size_entry.config(state="normal")
? ? ? ? ? ? self.time_entry.config(state="disabled")
? ??
? ? def validate_input(self):
? ? ? ? if not self.video_paths:
? ? ? ? ? ? messagebox.showerror("錯誤", "請選擇視頻文件!")
? ? ? ? ? ? return False
? ? ? ? ? ??
? ? ? ? # 驗證每個文件的時長
? ? ? ? for path in self.video_paths:
? ? ? ? ? ? if not os.path.exists(path):
? ? ? ? ? ? ? ? messagebox.showerror("錯誤", f"文件不存在:{path}")
? ? ? ? ? ? ? ? return False
? ? ? ? ? ? ? ??
? ? ? ? ? ? if self.split_method.get() == "time":
? ? ? ? ? ? ? ? try:
? ? ? ? ? ? ? ? ? ? interval = float(self.time_entry.get())
? ? ? ? ? ? ? ? ? ? if interval <= 0:
? ? ? ? ? ? ? ? ? ? ? ? raise ValueError
? ? ? ? ? ? ? ? ? ? duration = self.get_video_duration(path)
? ? ? ? ? ? ? ? ? ? if duration == 0:
? ? ? ? ? ? ? ? ? ? ? ? return False
? ? ? ? ? ? ? ? ? ? if interval > duration:
? ? ? ? ? ? ? ? ? ? ? ? messagebox.showwarning("警告",?
? ? ? ? ? ? ? ? ? ? ? ? ? ? f"分割時間大于視頻時長({duration:.2f}秒),將只生成一個片段")
? ? ? ? ? ? ? ? except ValueError:
? ? ? ? ? ? ? ? ? ? messagebox.showerror("錯誤", "請輸入有效的分割時間!")
? ? ? ? ? ? ? ? ? ? return False
? ? ? ? ? ? else:
? ? ? ? ? ? ? ? try:
? ? ? ? ? ? ? ? ? ? size = int(self.size_entry.get())
? ? ? ? ? ? ? ? ? ? if size <= 0:
? ? ? ? ? ? ? ? ? ? ? ? raise ValueError
? ? ? ? ? ? ? ? except ValueError:
? ? ? ? ? ? ? ? ? ? messagebox.showerror("錯誤", "請輸入有效的分割大小!")
? ? ? ? ? ? ? ? ? ? return False
? ? ? ? ? ??
? ? ? ? return True
? ??
? ? def get_video_duration(self, input_video):
? ? ? ? """獲取視頻時長,支持H.265編碼"""
? ? ? ? try:
? ? ? ? ? ? # 使用JSON格式輸出,更容易解析
? ? ? ? ? ? cmd = [
? ? ? ? ? ? ? ? 'ffprobe', '-v', 'quiet',
? ? ? ? ? ? ? ? '-print_format', 'json',
? ? ? ? ? ? ? ? '-show_format',
? ? ? ? ? ? ? ? '-show_streams',
? ? ? ? ? ? ? ? input_video
? ? ? ? ? ? ]
? ? ? ? ? ??
? ? ? ? ? ? result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
? ? ? ? ? ??
? ? ? ? ? ? if result.returncode != 0:
? ? ? ? ? ? ? ? # 如果標準方法失敗,嘗試使用更兼容的方法
? ? ? ? ? ? ? ? return self.get_video_duration_fallback(input_video)
? ? ? ? ? ??
? ? ? ? ? ? # 解析JSON輸出
? ? ? ? ? ? data = json.loads(result.stdout)
? ? ? ? ? ??
? ? ? ? ? ? # 首先嘗試從format獲取時長
? ? ? ? ? ? if 'format' in data and 'duration' in data['format']:
? ? ? ? ? ? ? ? return float(data['format']['duration'])
? ? ? ? ? ??
? ? ? ? ? ? # 然后嘗試從視頻流獲取時長
? ? ? ? ? ? if 'streams' in data:
? ? ? ? ? ? ? ? for stream in data['streams']:
? ? ? ? ? ? ? ? ? ? if stream['codec_type'] == 'video' and 'duration' in stream:
? ? ? ? ? ? ? ? ? ? ? ? return float(stream['duration'])
? ? ? ? ? ??
? ? ? ? ? ? # 如果以上方法都失敗,使用備用方法
? ? ? ? ? ? return self.get_video_duration_fallback(input_video)
? ? ? ? ? ??
? ? ? ? except Exception as e:
? ? ? ? ? ? print(f"獲取視頻時長錯誤: {e}")
? ? ? ? ? ? return self.get_video_duration_fallback(input_video)
? ??
? ? def get_video_duration_fallback(self, input_video):
? ? ? ? """備用方法獲取視頻時長"""
? ? ? ? try:
? ? ? ? ? ? # 嘗試使用不同的參數獲取視頻時長
? ? ? ? ? ? cmd = [
? ? ? ? ? ? ? ? 'ffprobe', '-v', 'error',
? ? ? ? ? ? ? ? '-show_entries', 'format=duration',
? ? ? ? ? ? ? ? '-of', 'default=noprint_wrappers=1:nokey=1',
? ? ? ? ? ? ? ? input_video
? ? ? ? ? ? ]
? ? ? ? ? ??
? ? ? ? ? ? result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
? ? ? ? ? ??
? ? ? ? ? ? if result.returncode == 0:
? ? ? ? ? ? ? ? return float(result.stdout)
? ? ? ? ? ??
? ? ? ? ? ? # 如果仍然失敗,嘗試使用更兼容的方法
? ? ? ? ? ? cmd = [
? ? ? ? ? ? ? ? 'ffprobe', '-i', input_video,
? ? ? ? ? ? ? ? '-show_entries', 'format=duration',
? ? ? ? ? ? ? ? '-v', 'quiet',
? ? ? ? ? ? ? ? '-of', 'csv=p=0'
? ? ? ? ? ? ]
? ? ? ? ? ??
? ? ? ? ? ? result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
? ? ? ? ? ??
? ? ? ? ? ? if result.returncode == 0:
? ? ? ? ? ? ? ? return float(result.stdout)
? ? ? ? ? ??
? ? ? ? ? ? # 如果所有方法都失敗,顯示錯誤信息
? ? ? ? ? ? error_msg = f"無法獲取視頻時長: {input_video}\n"
? ? ? ? ? ? error_msg += "可能的原因:\n"
? ? ? ? ? ? error_msg += "1. 視頻文件損壞\n"
? ? ? ? ? ? error_msg += "2. 不支持的視頻編碼格式(如H.265)\n"
? ? ? ? ? ? error_msg += "3. FFmpeg版本過舊\n"
? ? ? ? ? ? error_msg += "請嘗試更新FFmpeg到最新版本"
? ? ? ? ? ??
? ? ? ? ? ? self.progress_queue.put(error_msg)
? ? ? ? ? ? return 0
? ? ? ? ? ??
? ? ? ? except Exception as e:
? ? ? ? ? ? error_msg = f"獲取視頻時長錯誤: {str(e)}"
? ? ? ? ? ? self.progress_queue.put(error_msg)
? ? ? ? ? ? return 0
? ??
? ? def split_video(self, input_video, interval_sec=0, size_mb=0):
? ? ? ? if self.is_cancelled:
? ? ? ? ? ? return
? ? ? ? ? ??
? ? ? ? # 更新進度標簽
? ? ? ? self.progress_queue.put(f"處理中: {os.path.basename(input_video)}")
? ? ? ??
? ? ? ? # 確定輸出目錄
? ? ? ? if self.output_dir:
? ? ? ? ? ? output_dir = self.output_dir
? ? ? ? else:
? ? ? ? ? ? original_dir = os.path.dirname(input_video)
? ? ? ? ? ? output_dir = os.path.join(original_dir, "split_output")
? ? ? ? os.makedirs(output_dir, exist_ok=True)
? ? ? ??
? ? ? ? # 生成輸出路徑
? ? ? ? base_name = os.path.basename(input_video)
? ? ? ? base, ext = os.path.splitext(base_name)
? ? ? ? output_template = os.path.join(output_dir, f"{base}_%03d{ext}")
? ? ? ??
? ? ? ? # 獲取視頻時長
? ? ? ? duration = self.get_video_duration(input_video)
? ? ? ? if duration == 0:
? ? ? ? ? ? self.progress_queue.put(f"錯誤: 無法獲取 {os.path.basename(input_video)} 的時長")
? ? ? ? ? ? return
? ? ? ??
? ? ? ? if interval_sec:
? ? ? ? ? ? # 按時間分割
? ? ? ? ? ? cmd = [
? ? ? ? ? ? ? ? 'ffmpeg', '-i', input_video, '-c', 'copy',
? ? ? ? ? ? ? ? '-f', 'segment', '-segment_time', str(interval_sec),
? ? ? ? ? ? ? ? '-reset_timestamps', '1', '-y', output_template
? ? ? ? ? ? ]
? ? ? ? else:
? ? ? ? ? ? # 按大小分割
? ? ? ? ? ? file_size = os.path.getsize(input_video)
? ? ? ? ? ? # 計算目標比特率 (bps)
? ? ? ? ? ? target_size_bits = size_mb * 1024 * 1024 * 8
? ? ? ? ? ? # 計算分割時間
? ? ? ? ? ? segment_time = (target_size_bits * duration) / (file_size * 8)
? ? ? ? ? ??
? ? ? ? ? ? cmd = [
? ? ? ? ? ? ? ? 'ffmpeg', '-i', input_video, '-c', 'copy',
? ? ? ? ? ? ? ? '-f', 'segment', '-segment_time', str(segment_time),
? ? ? ? ? ? ? ? '-reset_timestamps', '1', '-y', output_template
? ? ? ? ? ? ]
? ? ? ??
? ? ? ? try:
? ? ? ? ? ? # 啟動進程
? ? ? ? ? ? process = subprocess.Popen(
? ? ? ? ? ? ? ? cmd,?
? ? ? ? ? ? ? ? stdout=subprocess.PIPE,?
? ? ? ? ? ? ? ? stderr=subprocess.STDOUT,
? ? ? ? ? ? ? ? universal_newlines=True,
? ? ? ? ? ? ? ? bufsize=1
? ? ? ? ? ? )
? ? ? ? ? ??
? ? ? ? ? ? # 讀取輸出并更新進度
? ? ? ? ? ? for line in process.stdout:
? ? ? ? ? ? ? ? if self.is_cancelled:
? ? ? ? ? ? ? ? ? ? process.terminate()
? ? ? ? ? ? ? ? ? ? break
? ? ? ? ? ? ? ? ? ??
? ? ? ? ? ? ? ? # 解析ffmpeg輸出以獲取進度
? ? ? ? ? ? ? ? if "time=" in line:
? ? ? ? ? ? ? ? ? ? time_pos = line.find("time=")
? ? ? ? ? ? ? ? ? ? if time_pos != -1:
? ? ? ? ? ? ? ? ? ? ? ? time_str = line[time_pos+5:].split()[0]
? ? ? ? ? ? ? ? ? ? ? ? try:
? ? ? ? ? ? ? ? ? ? ? ? ? ? # 將時間轉換為秒
? ? ? ? ? ? ? ? ? ? ? ? ? ? h, m, s = time_str.split(':')
? ? ? ? ? ? ? ? ? ? ? ? ? ? current_time = int(h)*3600 + int(m)*60 + float(s)
? ? ? ? ? ? ? ? ? ? ? ? ? ??
? ? ? ? ? ? ? ? ? ? ? ? ? ? # 計算進度百分比
? ? ? ? ? ? ? ? ? ? ? ? ? ? if duration > 0:
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? progress = (current_time / duration) * 100
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? self.progress_queue.put(f"PROGRESS:{progress}")
? ? ? ? ? ? ? ? ? ? ? ? except:
? ? ? ? ? ? ? ? ? ? ? ? ? ? pass
? ? ? ? ? ??
? ? ? ? ? ? # 等待進程完成
? ? ? ? ? ? process.wait()
? ? ? ? ? ??
? ? ? ? ? ? if process.returncode == 0 and not self.is_cancelled:
? ? ? ? ? ? ? ? self.progress_queue.put(f"完成: {os.path.basename(input_video)}")
? ? ? ? ? ? elif self.is_cancelled:
? ? ? ? ? ? ? ? self.progress_queue.put("已取消")
? ? ? ? ? ? else:
? ? ? ? ? ? ? ? self.progress_queue.put(f"錯誤: {os.path.basename(input_video)}")
? ? ? ? ? ? ? ??
? ? ? ? except Exception as e:
? ? ? ? ? ? self.progress_queue.put(f"錯誤: {str(e)}")
? ??
? ? def start_split(self):
? ? ? ? if not self.validate_input():
? ? ? ? ? ? return
? ? ? ??
? ? ? ? self.is_cancelled = False
? ? ? ? self.current_video_index = 0
? ? ? ? self.progress_value.set(0)
? ? ? ? self.progress_queue.put("開始處理...")
? ? ? ??
? ? ? ? # 禁用開始按鈕,防止重復點擊
? ? ? ? self.start_button.config(state=tk.DISABLED)
? ? ? ??
? ? ? ? # 啟動一個線程來處理所有視頻
? ? ? ? worker_thread = threading.Thread(target=self.process_all_videos)
? ? ? ? worker_thread.daemon = True ?# 設置為守護線程,主程序退出時自動結束
? ? ? ? worker_thread.start()
? ??
? ? def process_all_videos(self):
? ? ? ? """處理所有視頻的線程函數"""
? ? ? ? for i, video_path in enumerate(self.video_paths):
? ? ? ? ? ? if self.is_cancelled:
? ? ? ? ? ? ? ? break
? ? ? ? ? ? ? ??
? ? ? ? ? ? self.current_video_index = i
? ? ? ? ? ??
? ? ? ? ? ? if self.split_method.get() == "time":
? ? ? ? ? ? ? ? interval = float(self.time_entry.get())
? ? ? ? ? ? ? ? self.split_video(video_path, interval)
? ? ? ? ? ? else:
? ? ? ? ? ? ? ? size = int(self.size_entry.get())
? ? ? ? ? ? ? ? self.split_video(video_path, 0, size)
? ? ? ??
? ? ? ? if not self.is_cancelled:
? ? ? ? ? ? self.progress_queue.put("所有任務完成!")
? ? ? ? ? ? # 在主線程中顯示消息框
? ? ? ? ? ? self.root.after(0, lambda: messagebox.showinfo("完成", "所有視頻分割完成!"))
? ? ? ??
? ? ? ? # 重新啟用開始按鈕
? ? ? ? self.root.after(0, lambda: self.start_button.config(state=tk.NORMAL))
? ??
? ? def select_output_dir(self):
? ? ? ? """選擇輸出目錄"""
? ? ? ? dir_path = filedialog.askdirectory(title="選擇輸出文件夾")
? ? ? ? if dir_path:
? ? ? ? ? ? self.output_dir = dir_path
? ? ? ? ? ? self.output_label.config(text=f"輸出目錄: {dir_path}")
? ??
? ? def cancel_split(self):
? ? ? ? self.is_cancelled = True
? ? ? ? if self.current_process:
? ? ? ? ? ? self.current_process.terminate()
? ? ? ? self.progress_queue.put("取消中...")
? ? ? ??
? ? ? ? # 重新啟用開始按鈕
? ? ? ? self.start_button.config(state=tk.NORMAL)
? ? ? ??
def main():
? ? root = tk.Tk()
? ? app = VideoSplitterApp(root)
? ? root.mainloop()

if __name__ == "__main__":
? ? main()
?

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/95855.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/95855.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/95855.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Linux--線程

Linux線程概念 1 什么是線程 ? 在?個程序?的?個執?路線就叫做線程&#xff08;thread&#xff09;。更準確的定義是&#xff1a;線程是“?個進程內部 的控制序列” ? ?切進程?少都有?個執?線程 ? 線程在進程內部運?&#xff0c;本質是在進程地址空間內運? ? 在L…

【C++】C++11的包裝器:function與bind簡介

各位大佬好&#xff0c;我是落羽&#xff01;一個堅持不斷學習進步的學生。 如果您覺得我的文章還不錯&#xff0c;歡迎多多互三分享交流&#xff0c;一起學習進步&#xff01; 也歡迎關注我的blog主頁: 落羽的落羽 文章目錄一、function1. 概念2. 用法二、bind1. 概念2. 用法…

MySQL高級特性詳解

MySQL高級特性詳解 一、自關聯查詢 概念 自關聯查詢是指一個表與它自己進行連接的查詢。通常用于處理具有層級關系或遞歸結構的數據。 應用場景 員工與上級關系分類的父子關系地區的層級關系 示例 -- 創建員工表 CREATE TABLE employees (emp_id INT PRIMARY KEY,emp_name VARC…

深度學習——調整學習率

學習率調整方法詳解在深度學習訓練過程中&#xff0c;學習率&#xff08;Learning Rate, LR&#xff09; 是影響模型收斂速度和效果的關鍵超參數。學習率過大可能導致訓練不穩定、震蕩甚至無法收斂&#xff1b;學習率過小又會導致收斂過慢甚至陷入局部最優。因此&#xff0c;如…

Java分頁 Element—UI

前端代碼 <div class"block"><span class"demonstration">頁數較少時的效果</span><el-paginationlayout"prev, pager, next":total"50"></el-pagination> </div>參考Element-UI total:0, form: …

html中列表和表格的使用

列表一般來說只有一列一列的進行使用&#xff0c;是一維的列表分為三種列表形式<!-- 列表標簽ul-li:無序列表&#xff0c;必須用 <ul> 當 “容器”&#xff08;代表 “無序列表”&#xff09;&#xff0c;每個條目用 <li> 包起來&#xff08;代表 “列表項”&am…

大學信息查詢平臺:一個現代化的React教育項目

一 項目簡介大學信息查詢平臺是一個基于React Vite Tailwind CSS構建的現代化Web應用&#xff0c;專門用于查詢中國各大高校的詳細信息。該項目不僅功能實用&#xff0c;更在用戶體驗和界面設計上做到了極致。二 核心功能2.1. 智能大學搜索// 搜索功能核心代碼 const searchU…

代碼隨想錄算法訓練營第六天 - 哈希表2 || 454.四數相加II / 383.贖金信 / 15.三數之和 / 18.四數之和

代碼隨想錄算法訓練營第六天 - 哈希表2 || 454.四數相加II / 383.贖金信 / 15.三數之和 / 18.四數之和454.四數相加II解題思路383.贖金信自己解答&#xff1a;代碼隨想錄講解暴力做法哈希表15.三數之和雙指針優化改進18.四數之和自己的解答系統講解454.四數相加II 文檔講解&…

FPGA實現流水式排序算法

該算法采用雙調排序算法&#xff0c;是一種可流水的遞推算法&#xff0c;且算法的消耗時長可算&#xff0c;具體細節參考視頻&#xff1a; https://www.bilibili.com/video/BV1S3thzWEnh/?spm_id_from333.1387.homepage.video_card.click&vd_source69fb997b62efa60ae1add…

平衡車 -- MPU6050

&#x1f308;個人主頁&#xff1a;羽晨同學 &#x1f4ab;個人格言:“成為自己未來的主人~” 傳感器原理 此外&#xff0c;用陀螺儀獲取x,y,z軸的加速度。 初始化 我們現在對MPU6050進行初始化&#xff0c;MPU6050通過I2C總線與單片機進行通信&#xff0c;通過的是PB8和PB…

在電路浪涌測試中,TVS(瞬態電壓抑制二極管)的防護效果確實會受到陪測設備中去耦網絡(Decoupling Network,DN)的顯著影響

在電路浪涌測試中&#xff0c;TVS&#xff08;瞬態電壓抑制二極管&#xff09;的防護效果確實會受到陪測設備中去耦網絡&#xff08;Decoupling Network&#xff0c;DN&#xff09;的顯著影響&#xff0c;這一現象與浪涌能量的傳遞路徑、阻抗匹配及信號完整性密切相關。結合 AD…

Redis之分布式鎖與緩存設計

1、分布式鎖 1.1、超賣問題/*** 存在庫存超賣的不安全問題*/private void deductStock() {int stockTotal Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));if (stockTotal > 0) { // 這里存在多個線程、進程同時判斷通過&#xff0c;然后超買…

靜態住宅IP的特點

穩定性高&#xff1a;與動態IP地址相比&#xff0c;靜態住宅IP不會不定時變更&#xff0c;能確保業務在網絡環境中的一致性和連貫性&#xff0c;適合需要長期維持同一身份的場景&#xff0c;如跨境電商業務等3。安全性強&#xff1a;由于其住宅屬性&#xff0c;看起來更像是正常…

Linux 編譯 Android 版 QGroundControl 軟件并運行到手機上

Linux 編譯 Android 版 QGroundControl 軟件并運行到手機上環境說明操作步驟一、參考上一篇文章在電腦端把環境搭建好二、配置 Qt Creator 的 Android 環境環境說明 電腦系統 Ubuntu 22.04 qgroundcontrol master 分支 Qt 6.8.3 操作步驟 一、參考上一篇文章在電腦端把環境搭…

Python 2025:量化金融與智能交易的新紀元

當Python遇見金融大數據&#xff0c;算法交易正迎來前所未有的技術變革在2025年的技術浪潮中&#xff0c;Python已經從一個"膠水語言"蛻變為金融科技領域的核心驅動力。根據GitHub 2025年度報告&#xff0c;Python在量化金融項目中的使用率增長了217%&#xff0c;在對…

[論文閱讀] 人工智能 + 軟件工程 | TDD痛點破解:LLM自動生成測試骨架靠譜嗎?靜態分析+專家評審給出答案

TDD痛點破解&#xff1a;LLM自動生成測試骨架靠譜嗎&#xff1f;靜態分析專家評審給出答案 論文信息項目詳情論文原標題Evaluation of Large Language Models for Generating RSpec Test Skeletons in Ruby on Rails論文鏈接https://arxiv.org/pdf/2509.04644一段話總結 該研究…

開源PSS解析器1

本章介紹另一個開源PSS解析工具zuspec&#xff1a; zuspec 提供了一組用于處理 actions relationship level 的工具 &#xff08;ARL&#xff09; 模型&#xff0c;主要是使用 Accellera 便攜式測試和刺激 &#xff08;PSS&#xff09; 語言描述的模型。ARL 模型用于為數字設計…

26考研——內存管理_內存管理策略(3)

408答疑 文章目錄一、內存管理策略1、內存管理的基本原理和要求1.1、相關概念1.2、邏輯地址與物理地址1.3、程序的鏈接與裝入1.4、進程的內存映像1.5、內存保護1.6、內存共享1.7、內存分配與回收1.8、在存儲管理中涉及到兩個問題2、連續分配管理方式2.1、相關概念2.2、單一連續…

Python爬蟲實戰:研究Event Handling機制,構建在線教育平臺的課程數據采集和分析系統

1. 引言 1.1 研究背景與意義 在大數據時代,互聯網作為全球最大的信息載體,蘊含著海量有價值的數據。這些數據涵蓋了商業交易、用戶行為、社會趨勢等多個領域,對企業決策、學術研究和社會管理具有重要參考價值。如何高效、準確地獲取這些數據并進行深度分析,成為當前數據科…

docker 安裝 redis 并設置 volumes 并修改 修改密碼(四)

設置新密碼: 127.0.0.1:6379> CONFIG SET requirepass newpassword OK驗證新密碼: 127.0.0.1:6379> AUTH newpassword OK更新配置文件: 編輯主機的配置文件/data/redis/conf/redis.conf,將requirepass的值修改為新密碼: requirepass newpassword重啟容器以使配置…