上傳一刻相冊,有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()
?