admin
发布于 2025-11-20 / 8 阅读
0
0

音频转为mp3格式

#!/usr/bin/env python3
"""
基于 FFmpeg 的音频格式转换脚本
将目录下的所有音频文件转换为标准 MP3 格式
直接调用 FFmpeg 命令行工具,不依赖 pydub
"""

import os
import sys
import subprocess
import argparse
from pathlib import Path
from typing import List, Optional

# 支持的输入格式
SUPPORTED_INPUT_FORMATS = [
    '.mp3', '.wav', '.flac', '.m4a', '.aac', '.ogg',
    '.wma', '.aiff', '.au', '.ra', '.m4p', '.m4b', '.m4r'
]

# 默认输出格式
DEFAULT_OUTPUT_FORMAT = 'mp3'

def check_ffmpeg():
    """检查 FFmpeg 是否安装"""
    try:
        result = subprocess.run(['ffmpeg', '-version'],
                              capture_output=True, text=True, shell=True)
        if result.returncode == 0:
            return True
    except:
        pass

    print("错误: 未找到 FFmpeg")
    print("请确保已安装 FFmpeg 并添加到系统 PATH")
    print("下载地址: https://ffmpeg.org/download.html")
    return False

def convert_audio_file(input_path: str, output_path: str, output_format: str = DEFAULT_OUTPUT_FORMAT,
                       bitrate: str = "192k", sample_rate: int = 44100) -> bool:
    """
    使用 FFmpeg 转换单个音频文件

    Args:
        input_path: 输入文件路径
        output_path: 输出文件路径
        output_format: 输出格式
        bitrate: 比特率
        sample_rate: 采样率

    Returns:
        bool: 转换是否成功
    """
    try:
        print(f"正在转换: {Path(input_path).name}")

        # 构建 FFmpeg 命令
        cmd = [
            'ffmpeg', '-y',  # -y 表示覆盖输出文件
            '-i', input_path,  # 输入文件
            '-ar', str(sample_rate),  # 采样率
            '-b:a', bitrate,  # 音频比特率
        ]

        # 根据输出格式添加特定参数
        if output_format.lower() == 'mp3':
            cmd.extend(['-codec:a', 'libmp3lame', '-q:a', '2'])  # 高质量 VBR
        elif output_format.lower() == 'wav':
            cmd.extend(['-codec:a', 'pcm_s16le'])  # PCM 16-bit
        elif output_format.lower() == 'm4a':
            cmd.extend(['-codec:a', 'aac'])
        else:
            cmd.extend(['-codec:a', output_format])

        cmd.append(output_path)

        # 执行转换
        result = subprocess.run(cmd, capture_output=True, text=True, shell=True)

        if result.returncode == 0:
            print(f"✓ 转换完成: {Path(output_path).name}")
            return True
        else:
            print(f"✗ 转换失败 {Path(input_path).name}: {result.stderr}")
            return False

    except Exception as e:
        print(f"✗ 转换失败 {Path(input_path).name}: {str(e)}")
        return False

def find_audio_files(directory: str = '.') -> List[str]:
    """
    查找目录中的所有音频文件(包括子目录)

    Args:
        directory: 要搜索的目录

    Returns:
        List[str]: 音频文件路径列表
    """
    audio_files = []
    dir_path = Path(directory)

    # 使用 glob 递归查找所有音频文件
    for ext in SUPPORTED_INPUT_FORMATS:
        pattern = f"**/*{ext}"
        for file_path in dir_path.glob(pattern):
            if file_path.is_file():
                audio_files.append(str(file_path))

    return sorted(audio_files)

def main():
    parser = argparse.ArgumentParser(description='音频格式转换工具 (基于 FFmpeg)')
    parser.add_argument('--format', '-f', default=DEFAULT_OUTPUT_FORMAT,
                       help=f'输出格式 (默认: {DEFAULT_OUTPUT_FORMAT})')
    parser.add_argument('--bitrate', '-b', default="192k",
                       help='音频比特率 (默认: 192k)')
    parser.add_argument('--sample-rate', '-s', type=int, default=44100,
                       help='采样率 (默认: 44100)')
    parser.add_argument('--output-dir', '-o',
                       help='输出目录 (默认: 当前目录下的 converted 文件夹)')
    parser.add_argument('--input-dir', '-i', default='.',
                       help='输入目录 (默认: 当前目录)')
    parser.add_argument('--overwrite', action='store_true',
                       help='覆盖已存在的文件')
    parser.add_argument('--recursive', '-r', action='store_true',
                       help='递归处理子目录')

    args = parser.parse_args()

    # 检查 FFmpeg
    if not check_ffmpeg():
        sys.exit(1)

    # 查找音频文件
    print(f"在目录中搜索音频文件: {Path(args.input_dir).absolute()}")
    audio_files = find_audio_files(args.input_dir)

    if not audio_files:
        print("未找到任何音频文件")
        print(f"支持的格式: {', '.join(SUPPORTED_INPUT_FORMATS)}")
        return

    print(f"找到 {len(audio_files)} 个音频文件")

    # 显示前几个文件作为示例
    print("\n找到的文件包括:")
    for file in audio_files[:5]:
        print(f"  - {file}")
    if len(audio_files) > 5:
        print(f"  ... 还有 {len(audio_files) - 5} 个文件")

    # 设置输出目录
    if args.output_dir:
        output_dir = Path(args.output_dir)
    else:
        output_dir = Path(args.input_dir) / 'converted'

    output_dir.mkdir(exist_ok=True)
    print(f"\n输出目录: {output_dir.absolute()}")

    # 转换文件
    success_count = 0
    total_count = len(audio_files)

    print(f"\n开始转换 (格式: {args.format}, 比特率: {args.bitrate})\n")

    for input_file in audio_files:
        input_path = Path(input_file)

        # 保持子目录结构
        if args.recursive and input_path.parent != Path(args.input_dir):
            relative_path = input_path.parent.relative_to(args.input_dir)
            output_subdir = output_dir / relative_path
            output_subdir.mkdir(parents=True, exist_ok=True)
        else:
            output_subdir = output_dir

        output_file = output_subdir / f"{input_path.stem}.{args.format}"

        # 检查文件是否已存在
        if output_file.exists() and not args.overwrite:
            print(f"⚠ 跳过已存在的文件: {output_file.relative_to(output_dir)}")
            success_count += 1  # 算作成功
            continue

        if convert_audio_file(input_file, str(output_file), args.format, args.bitrate, args.sample_rate):
            success_count += 1

    # 显示转换结果
    print(f"\n{'='*50}")
    print(f"转换完成! 成功: {success_count}/{total_count}")

    if success_count == total_count:
        print("✅ 所有文件转换成功!")
    else:
        print("⚠️ 部分文件转换失败,请检查错误信息")

    # 显示输出目录大小
    if output_dir.exists():
        total_size = sum(f.stat().st_size for f in output_dir.glob('**/*') if f.is_file())
        size_mb = total_size / (1024 * 1024)
        print(f"\n输出目录大小: {size_mb:.1f} MB")

if __name__ == '__main__':
    main()


评论