音频/视频提取器:Python和moviepy实现

在这篇博客中,我们将深入探讨一个使用Python和wxPython构建的音频/视频提取器应用程序。这个应用程序允许用户从视频文件中提取音频,或者从音频文件中截取特定时间段。让我们逐步分析这个程序的功能和实现。

C:\pythoncode\new\MP3towav.py

全部代码

python 复制代码
import wx
import os
import subprocess
from datetime import datetime
import json
from moviepy.editor import VideoFileClip, AudioFileClip

class AudioVideoExtractor(wx.Frame):
    def __init__(self):
        super().__init__(parent=None, title='Audio/Video Extractor')
        self.panel = wx.Panel(self)
        self.create_widgets()
        self.load_settings()

    def create_widgets(self):
        # File selection
        self.file_picker = wx.FilePickerCtrl(self.panel, message="Choose an audio or video file")
        
        # Time range
        self.start_time = wx.TextCtrl(self.panel, value="00:00:00")
        self.end_time = wx.TextCtrl(self.panel, value="00:00:00")
        
        # Output format
        self.formats = ['mp3', 'wav', 'aac']
        self.format_choice = wx.Choice(self.panel, choices=self.formats)
        
        # Output directory
        self.dir_picker = wx.DirPickerCtrl(self.panel, message="Choose output directory")
        
        # Export button
        self.export_btn = wx.Button(self.panel, label="Export")
        self.export_btn.Bind(wx.EVT_BUTTON, self.on_export)
        
        # Open button
        self.open_btn = wx.Button(self.panel, label="Open in PotPlayer")
        self.open_btn.Bind(wx.EVT_BUTTON, self.on_open)
        
        # Layout
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(wx.StaticText(self.panel, label="Select Audio or Video File:"), 0, wx.ALL, 5)
        sizer.Add(self.file_picker, 0, wx.EXPAND|wx.ALL, 5)
        sizer.Add(wx.StaticText(self.panel, label="Start Time (HH:MM:SS):"), 0, wx.ALL, 5)
        sizer.Add(self.start_time, 0, wx.EXPAND|wx.ALL, 5)
        sizer.Add(wx.StaticText(self.panel, label="End Time (HH:MM:SS):"), 0, wx.ALL, 5)
        sizer.Add(self.end_time, 0, wx.EXPAND|wx.ALL, 5)
        sizer.Add(wx.StaticText(self.panel, label="Output Format:"), 0, wx.ALL, 5)
        sizer.Add(self.format_choice, 0, wx.EXPAND|wx.ALL, 5)
        sizer.Add(wx.StaticText(self.panel, label="Output Directory:"), 0, wx.ALL, 5)
        sizer.Add(self.dir_picker, 0, wx.EXPAND|wx.ALL, 5)
        sizer.Add(self.export_btn, 0, wx.ALL|wx.CENTER, 5)
        sizer.Add(self.open_btn, 0, wx.ALL|wx.CENTER, 5)
        
        self.panel.SetSizer(sizer)
        
    def on_export(self, event):
        input_path = self.file_picker.GetPath()
        start_time = self.start_time.GetValue()
        end_time = self.end_time.GetValue()
        output_format = self.formats[self.format_choice.GetSelection()]
        output_dir = self.dir_picker.GetPath()
        
        if not all([input_path, start_time, end_time, output_format, output_dir]):
            wx.MessageBox("Please fill in all fields", "Error", wx.OK | wx.ICON_ERROR)
            return
        
        try:
            # Check if the input file is video or audio
            file_extension = os.path.splitext(input_path)[1].lower()
            if file_extension in ['.mp4', '.avi', '.mov', '.flv']:  # Add more video extensions if needed
                clip = VideoFileClip(input_path).audio
            elif file_extension in ['.mp3', '.wav', '.aac', '.flac']:  # Add more audio extensions if needed
                clip = AudioFileClip(input_path)
            else:
                raise ValueError("Unsupported file format")

            start = self.time_to_seconds(start_time)
            end = self.time_to_seconds(end_time)
            
            clip = clip.subclip(start, end)
            
            timestamp = datetime.now().strftime("%Y%m%d")
            counter = 1
            while True:
                output_filename = f"{timestamp}_{counter:03d}.{output_format}"
                output_path = os.path.join(output_dir, output_filename)
                if not os.path.exists(output_path):
                    break
                counter += 1
            
            clip.write_audiofile(output_path)
            clip.close()
            
            self.last_exported_file = output_path
            wx.MessageBox(f"Audio exported to {output_path}", "Success", wx.OK | wx.ICON_INFORMATION)
            
            self.save_settings()
        except Exception as e:
            wx.MessageBox(f"Error: {str(e)}", "Error", wx.OK | wx.ICON_ERROR)
    
    def on_open(self, event):
        if hasattr(self, 'last_exported_file') and os.path.exists(self.last_exported_file):
            try:
                subprocess.Popen(['C:\\Program Files\\DAUM\\PotPlayer\\PotPlayerMini64.exe', self.last_exported_file])
            except Exception as e:
                wx.MessageBox(f"Error opening file: {str(e)}", "Error", wx.OK | wx.ICON_ERROR)
        else:
            wx.MessageBox("No file has been exported yet", "Error", wx.OK | wx.ICON_ERROR)
    
    def time_to_seconds(self, time_str):
        h, m, s = map(int, time_str.split(':'))
        return h * 3600 + m * 60 + s
    
    def save_settings(self):
        settings = {
            'last_file': self.file_picker.GetPath(),
            'start_time': self.start_time.GetValue(),
            'end_time': self.end_time.GetValue(),
            'format': self.format_choice.GetSelection(),
            'output_dir': self.dir_picker.GetPath()
        }
        with open('settings.json', 'w') as f:
            json.dump(settings, f)
    
    def load_settings(self):
        if os.path.exists('settings.json'):
            with open('settings.json', 'r') as f:
                settings = json.load(f)
            self.file_picker.SetPath(settings.get('last_file', ''))
            self.start_time.SetValue(settings.get('start_time', '00:00:00'))
            self.end_time.SetValue(settings.get('end_time', '00:00:00'))
            self.format_choice.SetSelection(settings.get('format', 0))
            self.dir_picker.SetPath(settings.get('output_dir', ''))

if __name__ == '__main__':
    app = wx.App()
    frame = AudioVideoExtractor()
    frame.Show()
    app.MainLoop()

1. 概述

这个应用程序的主要功能包括:

  • 从视频文件中提取音频
  • 从音频文件中截取特定时间段
  • 设置输出音频格式
  • 自定义输出文件名和路径
  • 使用PotPlayer播放导出的音频文件
  • 保存和加载用户设置

2. 导入必要的库

python 复制代码
import wx
import os
import subprocess
from datetime import datetime
import json
from moviepy.editor import VideoFileClip, AudioFileClip

这些库为我们提供了以下功能:

  • wx: 用于创建图形用户界面
  • os: 用于文件和路径操作
  • subprocess: 用于启动外部程序(PotPlayer)
  • datetime: 用于生成时间戳
  • json: 用于保存和加载设置
  • moviepy.editor: 用于处理音频和视频文件

3. 主应用程序类

python 复制代码
class AudioVideoExtractor(wx.Frame):
    def __init__(self):
        super().__init__(parent=None, title='Audio/Video Extractor')
        self.panel = wx.Panel(self)
        self.create_widgets()
        self.load_settings()

这个类继承自wx.Frame,是我们应用程序的主窗口。在初始化方法中,我们创建了一个面板,调用方法来创建UI组件,并加载之前保存的设置。

4. 创建UI组件

python 复制代码
def create_widgets(self):
    # 文件选择器
    self.file_picker = wx.FilePickerCtrl(self.panel, message="Choose an audio or video file")
    
    # 时间范围输入
    self.start_time = wx.TextCtrl(self.panel, value="00:00:00")
    self.end_time = wx.TextCtrl(self.panel, value="00:00:00")
    
    # 输出格式选择
    self.formats = ['mp3', 'wav', 'aac']
    self.format_choice = wx.Choice(self.panel, choices=self.formats)
    
    # 输出目录选择
    self.dir_picker = wx.DirPickerCtrl(self.panel, message="Choose output directory")
    
    # 导出按钮
    self.export_btn = wx.Button(self.panel, label="Export")
    self.export_btn.Bind(wx.EVT_BUTTON, self.on_export)
    
    # 打开按钮
    self.open_btn = wx.Button(self.panel, label="Open in PotPlayer")
    self.open_btn.Bind(wx.EVT_BUTTON, self.on_open)
    
    # 布局设置
    # ...

这个方法创建了所有的UI组件,包括文件选择器、时间输入框、格式选择下拉框、目录选择器和按钮。它还设置了组件的布局。

5. 导出功能

python 复制代码
def on_export(self, event):
    # 获取用户输入
    input_path = self.file_picker.GetPath()
    start_time = self.start_time.GetValue()
    end_time = self.end_time.GetValue()
    output_format = self.formats[self.format_choice.GetSelection()]
    output_dir = self.dir_picker.GetPath()
    
    # 检查输入是否完整
    if not all([input_path, start_time, end_time, output_format, output_dir]):
        wx.MessageBox("Please fill in all fields", "Error", wx.OK | wx.ICON_ERROR)
        return
    
    try:
        # 检查输入文件类型
        file_extension = os.path.splitext(input_path)[1].lower()
        if file_extension in ['.mp4', '.avi', '.mov', '.flv']:
            clip = VideoFileClip(input_path).audio
        elif file_extension in ['.mp3', '.wav', '.aac', '.flac']:
            clip = AudioFileClip(input_path)
        else:
            raise ValueError("Unsupported file format")

        # 处理音频
        start = self.time_to_seconds(start_time)
        end = self.time_to_seconds(end_time)
        clip = clip.subclip(start, end)
        
        # 生成输出文件名
        timestamp = datetime.now().strftime("%Y%m%d")
        counter = 1
        while True:
            output_filename = f"{timestamp}_{counter:03d}.{output_format}"
            output_path = os.path.join(output_dir, output_filename)
            if not os.path.exists(output_path):
                break
            counter += 1
        
        # 导出音频
        clip.write_audiofile(output_path)
        clip.close()
        
        self.last_exported_file = output_path
        wx.MessageBox(f"Audio exported to {output_path}", "Success", wx.OK | wx.ICON_INFORMATION)
        
        self.save_settings()
    except Exception as e:
        wx.MessageBox(f"Error: {str(e)}", "Error", wx.OK | wx.ICON_ERROR)

这个方法是程序的核心,它处理音频/视频的导出过程:

  1. 获取用户输入的所有必要信息。
  2. 检查输入文件的类型(音频或视频)。
  3. 根据用户指定的时间范围截取音频。
  4. 生成一个唯一的输出文件名。
  5. 导出音频文件。
  6. 保存用户设置以便下次使用。

6. 在PotPlayer中打开文件

python 复制代码
def on_open(self, event):
    if hasattr(self, 'last_exported_file') and os.path.exists(self.last_exported_file):
        try:
            subprocess.Popen(['potplayer.exe', self.last_exported_file])
        except Exception as e:
            wx.MessageBox(f"Error opening file: {str(e)}", "Error", wx.OK | wx.ICON_ERROR)
    else:
        wx.MessageBox("No file has been exported yet", "Error", wx.OK | wx.ICON_ERROR)

这个方法允许用户直接在PotPlayer中打开最近导出的文件。

7. 设置的保存和加载

python 复制代码
def save_settings(self):
    settings = {
        'last_file': self.file_picker.GetPath(),
        'start_time': self.start_time.GetValue(),
        'end_time': self.end_time.GetValue(),
        'format': self.format_choice.GetSelection(),
        'output_dir': self.dir_picker.GetPath()
    }
    with open('settings.json', 'w') as f:
        json.dump(settings, f)

def load_settings(self):
    if os.path.exists('settings.json'):
        with open('settings.json', 'r') as f:
            settings = json.load(f)
        self.file_picker.SetPath(settings.get('last_file', ''))
        self.start_time.SetValue(settings.get('start_time', '00:00:00'))
        self.end_time.SetValue(settings.get('end_time', '00:00:00'))
        self.format_choice.SetSelection(settings.get('format', 0))
        self.dir_picker.SetPath(settings.get('output_dir', ''))

这些方法允许程序保存用户的设置并在下次启动时加载它们,提高了用户体验。

8. 主程序入口

python 复制代码
if __name__ == '__main__':
    app = wx.App()
    frame = AudioVideoExtractor()
    frame.Show()
    app.MainLoop()

这是程序的入口点,它创建了wxPython应用程序实例,实例化了我们的AudioVideoExtractor类,并启动主事件循环。

运行结果

结论

这个音频/视频提取器是一个功能强大yet易用的工具,展示了如何使用Python和wxPython创建实用的桌面应用程序。它结合了文件I/O、音频/视频处理、GUI编程和设置管理等多个方面,是一个很好的学习案例。

相关推荐
databook1 小时前
Manim实现闪光轨迹特效
后端·python·动效
Juchecar3 小时前
解惑:NumPy 中 ndarray.ndim 到底是什么?
python
用户8356290780513 小时前
Python 删除 Excel 工作表中的空白行列
后端·python
Json_3 小时前
使用python-fastApi框架开发一个学校宿舍管理系统-前后端分离项目
后端·python·fastapi
数据智能老司机9 小时前
精通 Python 设计模式——分布式系统模式
python·设计模式·架构
数据智能老司机10 小时前
精通 Python 设计模式——并发与异步模式
python·设计模式·编程语言
数据智能老司机10 小时前
精通 Python 设计模式——测试模式
python·设计模式·架构
数据智能老司机10 小时前
精通 Python 设计模式——性能模式
python·设计模式·架构
c8i11 小时前
drf初步梳理
python·django
每日AI新事件11 小时前
python的异步函数
python