当前位置: 首页 > news >正文

video-audio-extractor【源码版】

软件介绍

前几天在网上看见有人分享了一个源码,大概就是py调用的ffmpeg来制作的。

这一次我带来源码版(需要py环境才可以运行),开箱即用版本(直接即可运行)

在这里插入图片描述

今天分享的源码版

软件安装

先安装Python和vscode

python

vscode

vscode的安装

三、源码

源码

import customtkinter as ctk
import subprocess
import os
import sys
import tkinter as tk
from tkinter import filedialog, messagebox
import threading
import webbrowser
import queue
from pathlib import Pathclass VideoAudioExtractor(ctk.CTk):def __init__(self):super().__init__()self.title("视频音频提取工具@阿幸")self.geometry("700x750")  # 增加窗口宽度和高度self.configure(fg_color="#f0f0f0")self.resizable(True, True)ctk.set_appearance_mode("Light")ctk.set_default_color_theme("blue")self.current_files = []  # 存储多个文件self.save_directory = os.path.expanduser("~\\Desktop")self.use_gpu = tk.BooleanVar(value=False)self.keep_folder_structure = tk.BooleanVar(value=True)  # 新增:保持文件夹结构选项self.create_widgets()self.supported_formats = ["mp4", "mkv", "avi", "mov", "wmv","flv", "webm", "mpeg", "mpg", "3gp"]self.ffmpeg_path = self.find_ffmpeg()if not self.ffmpeg_path:messagebox.showerror("错误", "未找到FFmpeg可执行文件!请确保ffmpeg.exe在同一目录下。")self.extract_button.configure(state=tk.DISABLED)# 新增:用于批量处理的队列和计数器self.processing_queue = queue.Queue()self.total_files = 0self.processed_files = 0def find_ffmpeg(self):"""查找当前目录下的FFmpeg可执行文件"""base_dir = os.path.dirname(sys.executable) if getattr(sys, 'frozen', False) else os.path.dirname(os.path.abspath(__file__))ffmpeg_exe = os.path.join(base_dir, "ffmpeg.exe")if os.path.exists(ffmpeg_exe):return ffmpeg_exeffmpeg_exe = os.path.join(base_dir, "ffmpeg", "ffmpeg.exe")if os.path.exists(ffmpeg_exe):return ffmpeg_exereturn Nonedef create_widgets(self):"""创建GUI界面组件"""# 创建主框架,使用pack布局main_frame = ctk.CTkFrame(self, fg_color="#f0f0f0")main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)# 标题标签title_frame = ctk.CTkFrame(main_frame, fg_color="#2196F3", corner_radius=5)title_frame.pack(fill=tk.X, pady=(0, 10))title_label = ctk.CTkLabel(title_frame, text="视频音频提取工具",font=("Microsoft YaHei", 20, "bold"),text_color="#ffffff")title_label.pack(pady=10)# 文件名显示 - 改为可滚动的文本框file_frame = ctk.CTkFrame(main_frame, fg_color="#e0e0e0")file_frame.pack(fill=tk.X, pady=(3, 3))# 修改 height 和 width 参数来调整文本框大小self.file_text = tk.Text(file_frame, wrap=tk.WORD, height=3, width=3,  # 调整这两个参数的值bg="#e0e0e0", fg="#555555", font=("Microsoft YaHei", 16, "bold"))self.file_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=3, pady=3)scrollbar = ctk.CTkScrollbar(file_frame, command=self.file_text.yview)scrollbar.pack(side=tk.RIGHT, fill=tk.Y, pady=2)self.file_text.configure(yscrollcommand=scrollbar.set)self.file_text.insert(tk.END, "未选择文件")self.file_text.config(state=tk.DISABLED)# 保存目录选择save_frame = ctk.CTkFrame(main_frame, fg_color="#e0e0e0")save_frame.pack(fill=tk.X, pady=(0, 10))save_label = ctk.CTkLabel(save_frame, text="保存位置:",font=("Microsoft YaHei", 12), text_color="#333333")save_label.pack(side=tk.LEFT, padx=10, pady=10)self.save_dir_label = ctk.CTkLabel(save_frame, text=self.save_directory,font=("Microsoft YaHei", 10), text_color="#555555",wraplength=400, justify=tk.LEFT)self.save_dir_label.pack(side=tk.LEFT, padx=5, pady=10)browse_button = ctk.CTkButton(save_frame, text="浏览",font=("Microsoft YaHei", 16, "bold"),  # 添加 "bold" 使字体加粗command=self.browse_save_directory,width=60, fg_color="#2196F3", hover_color="#1976D2")browse_button.pack(side=tk.RIGHT, padx=10, pady=10)# 新增:保持文件夹结构选项structure_frame = ctk.CTkFrame(main_frame, fg_color="#e0e0e0")structure_frame.pack(fill=tk.X, pady=(0, 10))self.structure_checkbox = ctk.CTkCheckBox(structure_frame,text="保持文件夹结构(适用于批量处理)",variable=self.keep_folder_structure,font=("Microsoft YaHei", 16, "bold"), text_color="#333333")self.structure_checkbox.pack(side=tk.LEFT, padx=10, pady=10)# 输出格式选择format_frame = ctk.CTkFrame(main_frame, fg_color="#e0e0e0")format_frame.pack(fill=tk.X, pady=(0, 10))format_label = ctk.CTkLabel(format_frame, text="输出音频格式:",font=("Microsoft YaHei", 12), text_color="#333333")format_label.pack(side=tk.LEFT, padx=10, pady=10)self.format_var = ctk.StringVar(value="mp3")format_options_frame = ctk.CTkFrame(format_frame, fg_color="#e0e0e0")format_options_frame.pack(side=tk.LEFT, fill=tk.X, expand=True)self.mp3_radio = ctk.CTkRadioButton(format_options_frame, text="MP3 (通用格式)",variable=self.format_var, value="mp3",font=("Microsoft YaHei", 10), text_color="#333333")self.mp3_radio.pack(side=tk.LEFT, padx=5, pady=10)self.wav_radio = ctk.CTkRadioButton(format_options_frame, text="WAV (无损格式)",variable=self.format_var, value="wav",font=("Microsoft YaHei", 10), text_color="#333333")self.wav_radio.pack(side=tk.LEFT, padx=5, pady=10)self.aac_radio = ctk.CTkRadioButton(format_options_frame, text="AAC (高质量)",variable=self.format_var, value="aac",font=("Microsoft YaHei", 10), text_color="#333333")self.aac_radio.pack(side=tk.LEFT, padx=5, pady=10)# GPU加速选项gpu_frame = ctk.CTkFrame(main_frame, fg_color="#e0e0e0")gpu_frame.pack(fill=tk.X, pady=(0, 10))gpu_label = ctk.CTkLabel(gpu_frame, text="硬件加速:",font=("Microsoft YaHei", 12), text_color="#333333")gpu_label.pack(side=tk.LEFT, padx=10, pady=10)self.gpu_checkbox = ctk.CTkCheckBox(gpu_frame, text="启用GPU加速 (NVIDIA)",variable=self.use_gpu,font=("Microsoft YaHei", 10), text_color="#333333")self.gpu_checkbox.pack(side=tk.LEFT, padx=5, pady=10)# 按钮框架 - 重新设计布局button_frame = ctk.CTkFrame(main_frame, fg_color="#f0f0f0")button_frame.pack(fill=tk.X, pady=(0, 10))# 左侧两个按钮框架left_buttons_frame = ctk.CTkFrame(button_frame, fg_color="#f0f0f0")left_buttons_frame.pack(side=tk.LEFT, fill=tk.X, expand=True)self.select_files_button = ctk.CTkButton(left_buttons_frame, text="选择文件",font=("Microsoft YaHei", 16, "bold"),command=self.select_files,fg_color="#2196F3",hover_color="#1976D2",width=150)self.select_files_button.pack(side=tk.LEFT, padx=(0, 5), pady=10)self.select_folder_button = ctk.CTkButton(left_buttons_frame, text="选择文件夹",font=("Microsoft YaHei", 16, "bold"),command=self.select_folder,fg_color="#2196F3",hover_color="#1976D2",width=150)self.select_folder_button.pack(side=tk.LEFT, padx=(0, 5), pady=10)# 右侧两个按钮框架right_buttons_frame = ctk.CTkFrame(button_frame, fg_color="#f0f0f0")right_buttons_frame.pack(side=tk.RIGHT, fill=tk.X, expand=True)self.extract_button = ctk.CTkButton(right_buttons_frame, text="提取音频",font=("Microsoft YaHei", 16, "bold"),command=self.extract_audio,state=tk.DISABLED,fg_color="#2196F3",hover_color="#1976D2",text_color="white",width=150)self.extract_button.pack(side=tk.RIGHT, padx=(0, 5), pady=10)self.promo_button = ctk.CTkButton(right_buttons_frame, text="关于阿幸",font=("Microsoft YaHei", 16, "bold"),command=lambda: webbrowser.open("https://a-xing.top/"),fg_color="#2196F3",hover_color="#1976D2",text_color="white",width=150)self.promo_button.pack(side=tk.RIGHT, padx=(0, 5), pady=10)# 进度条显示progress_frame = ctk.CTkFrame(main_frame, fg_color="#f0f0f0")progress_frame.pack(fill=tk.X, pady=(0, 10))progress_info_frame = ctk.CTkFrame(progress_frame, fg_color="#f0f0f0")progress_info_frame.pack(fill=tk.X)progress_text = ctk.CTkLabel(progress_info_frame, text="处理进度:",font=("Microsoft YaHei", 12), text_color="#333333")progress_text.pack(side=tk.LEFT, padx=(0, 10), pady=5)self.progress_label = ctk.CTkLabel(progress_info_frame, text="0/0",font=("Microsoft YaHei", 12), text_color="#555555")self.progress_label.pack(side=tk.LEFT, pady=5)self.progress_var = tk.DoubleVar()self.progress_bar = ctk.CTkProgressBar(progress_frame, variable=self.progress_var,width=600, height=10, corner_radius=5,progress_color="#2196F3")self.progress_bar.pack(fill=tk.X, pady=(0, 5))self.progress_bar.set(0)# 状态标签self.status_label = ctk.CTkLabel(main_frame, text="等待转换",font=("Microsoft YaHei", 16, "bold"), text_color="#555555")self.status_label.pack(pady=(0, 5))  # 减小与底部信息的间距# 底部信息(移到状态标签下方)footer_label = ctk.CTkLabel(main_frame, text="支持格式: MP4, MKV, AVI, MOV, WMV, FLV, WEBM, MPEG, MPG, 3GP",font=("Microsoft YaHei", 16, "bold"), text_color="#777777")footer_label.pack(fill=tk.X, pady=(0, 10))  # 调整间距def browse_save_directory(self):"""浏览并选择保存目录"""directory = filedialog.askdirectory(initialdir=self.save_directory)if directory:self.save_directory = directoryself.save_dir_label.configure(text=directory)def select_files(self):"""选择多个文件"""file_paths = filedialog.askopenfilenames(title="选择视频文件",filetypes=[("视频文件", "*.mp4;*.mkv;*.avi;*.mov;*.wmv;*.flv;*.webm;*.mpeg;*.mpg;*.3gp")])if file_paths:self.process_files(file_paths)def select_folder(self):"""选择文件夹"""folder_path = filedialog.askdirectory(title="选择视频文件夹")if folder_path:# 获取文件夹中所有支持的视频文件video_files = []for root, _, files in os.walk(folder_path):for file in files:ext = os.path.splitext(file)[1][1:].lower()if ext in self.supported_formats:video_files.append(os.path.join(root, file))if video_files:self.process_files(video_files)else:messagebox.showinfo("提示", "所选文件夹中未找到支持的视频文件")def process_files(self, file_paths):"""处理选择的多个文件"""valid_files = []for file_path in file_paths:file_ext = os.path.splitext(file_path)[1][1:].lower()if file_ext in self.supported_formats:valid_files.append(file_path)else:print(f"忽略不支持的文件: {file_path}")if not valid_files:messagebox.showerror("错误", "未选择有效的视频文件")returnself.current_files = valid_files# 更新文件列表显示self.file_text.config(state=tk.NORMAL)self.file_text.delete(1.0, tk.END)if len(valid_files) == 1:self.file_text.insert(tk.END, f"已选择: {os.path.basename(valid_files[0])}")else:self.file_text.insert(tk.END, f"已选择 {len(valid_files)} 个文件\n")for file in valid_files[:5]:  # 只显示前5个文件self.file_text.insert(tk.END, f"- {os.path.basename(file)}\n")if len(valid_files) > 5:self.file_text.insert(tk.END, f"... 等 {len(valid_files)} 个文件")self.file_text.config(state=tk.DISABLED)self.extract_button.configure(state=tk.NORMAL)self.status_label.configure(text="就绪")self.progress_bar.set(0)self.progress_label.configure(text="0/0")def extract_audio(self):"""提取音频"""if not self.current_files or not self.ffmpeg_path:returnself.extract_button.configure(state=tk.DISABLED)self.promo_button.configure(state=tk.DISABLED)# 初始化处理队列self.processing_queue.queue.clear()self.total_files = len(self.current_files)self.processed_files = 0self.progress_var.set(0)self.progress_label.configure(text=f"0/{self.total_files}")# 将所有文件添加到处理队列for file in self.current_files:self.processing_queue.put(file)# 启动处理线程threading.Thread(target=self.process_queue, daemon=True).start()def process_queue(self):"""处理队列中的文件"""output_format = self.format_var.get()while not self.processing_queue.empty():file_path = self.processing_queue.get()# 构建输出文件路径if self.total_files == 1 or not self.keep_folder_structure.get():# 单个文件或不保持文件夹结构时,直接输出到保存目录base_name = os.path.basename(os.path.splitext(file_path)[0])output_file = os.path.join(self.save_directory, f"{base_name}.{output_format}")else:# 批量处理且保持文件夹结构relative_path = os.path.relpath(os.path.dirname(file_path),os.path.dirname(self.current_files[0]))target_dir = os.path.join(self.save_directory, relative_path)os.makedirs(target_dir, exist_ok=True)base_name = os.path.basename(os.path.splitext(file_path)[0])output_file = os.path.join(target_dir, f"{base_name}.{output_format}")# 更新状态self.status_label.configure(text=f"正在处理: {os.path.basename(file_path)}")# 执行提取success = self.run_ffmpeg(file_path, output_file, output_format)# 更新进度self.processed_files += 1progress = self.processed_files / self.total_filesself.progress_var.set(progress)self.progress_label.configure(text=f"{self.processed_files}/{self.total_files}")# 处理完成的回调if success:self.after(0, lambda f=os.path.basename(output_file):self.status_label.configure(text=f"已完成: {f}"))# 全部处理完成self.after(0, self.processing_complete)def run_ffmpeg(self, input_file, output_file, output_format):"""运行FFmpeg命令提取音频"""try:# 设置FFmpeg参数cmd = [self.ffmpeg_path]# 添加GPU加速选项if self.use_gpu.get():cmd.extend(["-hwaccel", "cuda", "-hwaccel_output_format", "cuda"])cmd.extend(["-i", input_file, "-vn"])# 设置音频编码参数if output_format == "mp3":cmd.extend(["-acodec", "libmp3lame", "-b:a", "192k"])elif output_format == "wav":cmd.extend(["-acodec", "pcm_s16le"])else:  # aaccmd.extend(["-acodec", "aac", "-b:a", "128k", "-strict", "experimental"])# 覆盖已存在文件cmd.extend(["-y", output_file])# 使用二进制模式读取输出并处理编码process = subprocess.Popen(cmd,stdout=subprocess.PIPE,stderr=subprocess.STDOUT,bufsize=4090)output = []for line_bytes in process.stdout:try:line = line_bytes.decode('utf-8').strip()except UnicodeDecodeError:try:line = line_bytes.decode('gbk').strip()except UnicodeDecodeError:line = line_bytes.decode('utf-8', errors='replace').strip()output.append(line)process.wait()if process.returncode == 0:return Trueelse:error_msg = "\n".join(output) if output else f"未知错误,错误码: {process.returncode}"print(f"处理失败: {input_file}\n{error_msg}")return Falseexcept Exception as e:print(f"处理文件 {input_file} 时发生异常: {str(e)}")return Falsedef processing_complete(self):"""所有文件处理完成后的回调"""self.status_label.configure(text=f"全部处理完成!共处理 {self.total_files} 个文件")# 创建自定义弹窗popup = ctk.CTkToplevel(self)  # 使用 customtkinter 的顶层窗口popup.title("阿幸提示:处理完成")popup.geometry("400x200")  # 设置弹窗大小popup.resizable(True, True)  # 允许调整大小popup.configure(fg_color="#f0f0f0")  # 设置弹窗背景颜色,与主界面一致# 创建标签显示信息label = ctk.CTkLabel(popup, text=f"已成功处理 {self.total_files} 个文件", font=("Microsoft YaHei", 20, "bold"), text_color="#333333",bg_color="transparent")label.pack(pady=20)# 创建按钮框架button_frame = ctk.CTkFrame(popup, fg_color="transparent")button_frame.pack(fill=tk.X, padx=20, pady=10)# 创建访问按钮def open_website():webbrowser.open("https://a-xing.top/")visit_button = ctk.CTkButton(button_frame, text="访问阿幸主页", command=open_website, font=("Microsoft YaHei", 16, "bold"),fg_color="#2196F3",hover_color="#1976D2",width=150)visit_button.pack(side=tk.LEFT, padx=(0, 10), pady=10)# 创建关闭按钮close_button = ctk.CTkButton(button_frame, text="关闭", command=popup.destroy, font=("Microsoft YaHei", 16, "bold"),fg_color="#2196F3",hover_color="#1976D2",width=150)close_button.pack(side=tk.RIGHT, padx=(10, 0), pady=10)self.extract_button.configure(state=tk.NORMAL if self.current_files else tk.DISABLED)self.promo_button.configure(state=tk.NORMAL)if __name__ == "__main__":app = VideoAudioExtractor()app.mainloop()    

运行界面

在这里插入图片描述

源码下载

夸克网盘:
https://pan.quark.cn/s/2d4d8e8505ee

http://www.dtcms.com/a/230034.html

相关文章:

  • 从OSI到TCP/IP:网络协议的演变与作用
  • 设计模式-迪米特法则
  • 3D视觉重构工业智造:解码迁移科技如何用“硬核之眼“重塑生产节拍
  • Doris查询Hive数据:实现高效跨数据源分析的实践指南
  • hive 3集成Iceberg 1.7中的Java版本问题
  • Duix.HeyGem:以“离线+开源”重构数字人创作生态
  • 大数据学习(128)-数据分析实例
  • 【网络安全】漏洞分析:阿帕奇漏洞学习
  • 大数据学习(129)-Hive数据分析
  • 【Web应用】若依框架:基础篇14 源码阅读-后端代码分析-课程管理模块前后端代码分析
  • 设计模式杂谈-模板设计模式
  • 容器化实施:Docker容器构建与优化深度剖析
  • (2025)Windows修改JupyterNotebook的字体,使用JetBrains Mono
  • Python爬虫(48)基于Scrapy-Redis与深度强化学习的智能分布式爬虫架构设计与实践
  • 内网穿透:打破网络限制的利器!深入探索和简单实现方案
  • 解决pycharm同一个文件夹下from *** import***仍显示No module named
  • 在VSCode中开发一个uni-app项目
  • python打卡day44@浙大疏锦行
  • 《PyTorch:开启深度学习新世界的魔法之门》
  • [Python] python信号处理绘制信号频谱
  • Transformer核心原理
  • SIFT 算法原理详解
  • 使用 Python 制作 GIF 动图,并打包为 EXE 可执行程序
  • Office文档图片批量导出工具
  • 新德通科技:以创新驱动光通信一体化发展,赋能全球智能互联
  • [yolov11改进系列]基于yolov11使用FasterNet替换backbone用于轻量化网络的python源码+训练源码
  • 欢乐熊大话蓝牙知识14:用 STM32 或 EFR32 实现 BLE 通信模块:从0到蓝牙,你也能搞!
  • Spring AI Alibaba + Nacos 动态 MCP Server 代理方案
  • 数据驱动在线教育平台优化:用数据帮你变成“教书匠+数据控”
  • 6.4 note