使用python写的mp4转mp3的转换程序

 使用python写的mp4转mp3的转换程序

问Claude:

使用python写一个mp4转mp3的转换程序 实现功能:

1.调用ffmpeg进行转换

2.可以单文件方式也可以批量方式转换,如果程序启动时传入的是单文件的路径,则采用单文件转换方式,如果传入的是目录路径,则将该目录下的文件批量转换

3.如果目录下已存在已转换的MP3文件,则忽略该文件不进行转换

4.可通过传入参数指定转换后的文件的保存路径,默认为保存在视频文件相同目录下

5.MP3的码率可以配置,默认为192K

6.如果程序启动时没有传入任何参数,则提示用户输入参数,并等待用户的输入完成

7.要在控制台里显示转换的进度信息

8.转换完成后播放系统提示音提醒

Claude答:

import os
import sys
import subprocess
import argparse
import platform
import glob
import time
from pathlib import Path


def play_notification_sound():
    """播放系统提示音"""
    system = platform.system()
    try:
        if system == "Windows":
            import winsound
            winsound.MessageBeep()
        elif system == "Darwin":  # macOS
            subprocess.call(["afplay", "/System/Library/Sounds/Ping.aiff"])
        else:  # Linux和其他系统
            subprocess.call(["paplay", "/usr/share/sounds/freedesktop/stereo/complete.oga"])
    except Exception as e:
        print(f"无法播放系统提示音: {e}")


def get_duration(file_path):
    """使用ffmpeg获取媒体文件的时长"""
    cmd = ["ffmpeg", "-i", file_path, "-f", "null", "-"]
    # 使用bytes模式并指定encoding='utf-8', errors='replace'进行解码
    result = subprocess.run(cmd, stderr=subprocess.PIPE, stdout=subprocess.PIPE)

    # 使用errors='replace'安全地解码,替换无法解码的字符
    stderr_text = result.stderr.decode('utf-8', errors='replace')

    for line in stderr_text.split('\n'):
        if "Duration" in line:
            time_str = line.split("Duration: ")[1].split(",")[0].strip()
            h, m, s = time_str.split(':')
            return float(h) * 3600 + float(m) * 60 + float(s)
    return None


def convert_mp4_to_mp3(input_path, output_path=None, bitrate="192k"):
    """使用ffmpeg将MP4文件转换为MP3"""
    if output_path is None:
        # 使用与输入文件相同的目录,但扩展名为.mp3
        output_path = os.path.splitext(input_path)[0] + ".mp3"

    # 检查输出文件是否已存在
    if os.path.exists(output_path):
        print(f"输出文件已存在: {output_path}. 跳过转换。")
        return False

    # 获取视频时长,用于计算进度
    duration = get_duration(input_path)

    # 准备ffmpeg命令
    cmd = [
        "ffmpeg",
        "-i", input_path,
        "-vn",  # 不包含视频
        "-acodec", "libmp3lame",
        "-ab", bitrate,
        "-f", "mp3",
        output_path
    ]

    # 启动进程并监控进度 - 不使用universal_newlines,改用bytes模式
    process = subprocess.Popen(
        cmd,
        stderr=subprocess.PIPE,
        # 移除universal_newlines=True
    )

    # 用于进度跟踪的变量
    start_time = time.time()
    last_update = 0
    line_buffer = b""

    # 逐行读取输出以更新进度
    while True:
        chunk = process.stderr.read(1)
        if not chunk and process.poll() is not None:
            break

        if chunk:
            line_buffer += chunk
            if chunk == b'\n' or chunk == b'\r':
                try:
                    # 安全解码行,替换无法解码的字符
                    line = line_buffer.decode('utf-8', errors='replace')
                    if "time=" in line and duration:
                        # 提取当前时间
                        time_str = line.split("time=")[1].split()[0]
                        if ":" in time_str:
                            h, m, s = time_str.split(':')
                            current_time = float(h) * 3600 + float(m) * 60 + float(s)
                        else:
                            current_time = float(time_str)

                        # 每秒最多更新一次进度
                        current = time.time()
                        if current - last_update >= 1:
                            progress = min(100, (current_time / duration) * 100)
                            print(f"\r转换中: {os.path.basename(input_path)} - {progress:.1f}%", end="")
                            last_update = current
                except Exception as e:
                    # 处理解析错误
                    pass

                line_buffer = b""

    # 等待进程完成
    process.wait()
    print()  # 进度完成后换行

    # 检查转换是否成功
    if process.returncode == 0:
        print(f"成功转换: {output_path}")
        return True
    else:
        print(f"转换失败: {input_path}")
        return False


def process_input(input_path, output_dir=None, bitrate="192k"):
    """处理单个文件或目录中的所有MP4文件"""
    converted_count = 0
    total_count = 0

    if os.path.isfile(input_path):
        # 单文件模式
        if not input_path.lower().endswith('.mp4'):
            print(f"输入文件不是MP4格式: {input_path}")
            return 0, 1

        # 确定输出路径
        if output_dir:
            output_path = os.path.join(output_dir, os.path.splitext(os.path.basename(input_path))[0] + ".mp3")
        else:
            output_path = os.path.splitext(input_path)[0] + ".mp3"

        # 如果输出目录不存在,创建它
        os.makedirs(os.path.dirname(output_path), exist_ok=True)

        # 执行转换
        success = convert_mp4_to_mp3(input_path, output_path, bitrate)
        return 1 if success else 0, 1

    elif os.path.isdir(input_path):
        # 批量模式 - 处理目录中的所有MP4文件
        mp4_files = glob.glob(os.path.join(input_path, "*.mp4"))
        total_count = len(mp4_files)

        if total_count == 0:
            print(f"目录中没有找到MP4文件: {input_path}")
            return 0, 0

        print(f"找到{total_count}个MP4文件等待转换")

        for i, file_path in enumerate(mp4_files, 1):
            print(f"\n处理文件 {i}/{total_count}: {os.path.basename(file_path)}")

            # 确定输出路径
            if output_dir:
                output_path = os.path.join(output_dir, os.path.splitext(os.path.basename(file_path))[0] + ".mp3")
            else:
                output_path = os.path.splitext(file_path)[0] + ".mp3"

            # 如果输出目录不存在,创建它
            os.makedirs(os.path.dirname(output_path), exist_ok=True)

            # 执行转换
            if convert_mp4_to_mp3(file_path, output_path, bitrate):
                converted_count += 1

        return converted_count, total_count

    else:
        print(f"输入路径不存在: {input_path}")
        return 0, 0


def main():
    parser = argparse.ArgumentParser(description="将MP4视频文件转换为MP3音频文件")
    parser.add_argument("input", nargs="?", help="输入MP4文件或包含MP4文件的目录")
    parser.add_argument("-o", "--output", help="MP3文件的输出目录(默认:与输入相同)")
    parser.add_argument("-b", "--bitrate", default="192k", help="MP3码率(默认:192k)")

    args = parser.parse_args()

    # 如果没有提供参数,提示用户输入
    if not args.input:
        print("未提供输入参数。请输入所需信息。")
        args.input = input("请输入MP4文件路径或包含MP4文件的目录路径: ").strip()

        output_choice = input("是否要指定输出目录? (y/n): ").strip().lower()
        if output_choice == 'y':
            args.output = input("请输入输出目录路径: ").strip()

        bitrate_choice = input("是否要指定自定义码率? (y/n): ").strip().lower()
        if bitrate_choice == 'y':
            args.bitrate = input("请输入码率 (例如: 128k, 192k, 320k): ").strip()

    # 验证输入路径
    if not os.path.exists(args.input):
        print(f"错误: 输入路径不存在: {args.input}")
        return 1

    # 如果提供了输出目录,验证它
    if args.output and not os.path.isdir(args.output):
        try:
            os.makedirs(args.output, exist_ok=True)
            print(f"已创建输出目录: {args.output}")
        except Exception as e:
            print(f"创建输出目录时出错: {e}")
            return 1

    # 处理输入
    print(f"\n开始转换过程...")
    print(f"输入: {args.input}")
    print(f"输出目录: {args.output or '与输入相同'}")
    print(f"码率: {args.bitrate}")
    print("-" * 50)

    start_time = time.time()
    converted, total = process_input(args.input, args.output, args.bitrate)
    elapsed_time = time.time() - start_time

    # 打印摘要
    print("\n" + "=" * 50)
    print(f"转换完成!")
    print(f"成功转换: {converted}/{total} 个文件")
    print(f"总耗时: {elapsed_time:.2f} 秒")
    print("=" * 50)

    # 播放提示音
    play_notification_sound()

    return 0


if __name__ == "__main__":
    sys.exit(main())

 

这个程序实现了您请求的所有功能:

  1. 使用ffmpeg进行MP4到MP3的转换
  2. 支持单文件和批量转换模式:
    • 如果输入是文件路径,则只转换该文件
    • 如果输入是目录路径,则转换该目录下所有MP4文件
  3. 如果目标目录已存在对应的MP3文件,则跳过该文件
  4. 可以通过-o或–output参数指定输出目录
  5. 可以通过-b或–bitrate参数设置MP3码率,默认为192k
  6. 如果启动时没有提供参数,会进入交互模式提示用户输入
  7. 在转换过程中显示进度信息
  8. 转换完成后播放系统提示音

使用方法

基本用法:

python mp4_to_mp3.py 视频文件.mp4

指定输出目录:

广告
python mp4_to_mp3.py 视频文件.mp4 -o 输出目录

指定码率:

python mp4_to_mp3.py 视频文件.mp4 -b 320k

批量转换整个目录:

python mp4_to_mp3.py 视频目录

无参数启动(会提示输入):

python mp4_to_mp3.py

程序运行需要预先安装ffmpeg,并确保ffmpeg命令可以在系统路径中访问。

吴川斌

吴川斌

Leave a Reply