#!/usr/bin/env python3 """ Audio Merger - 音频合并工具 功能: - 合并多个音频文件为一个完整版 - 自动按文件名排序 - 支持指定输出文件名 - 保持原有文件不变 依赖: - ffmpeg(必须安装并添加到PATH) 使用方法: python scripts/merge_audio.py [options] 示例: # 合并指定目录所有MP3 python scripts/merge_audio.py ./audio_chapters # 指定输出文件名 python scripts/merge_audio.py ./audio_chapters --output 完整版.mp3 # 指定文件模式(默认*.mp3) python scripts/merge_audio.py ./audio_chapters --pattern "*.mp3" """ import os import argparse import subprocess import sys from pathlib import Path def get_audio_files(audio_dir: str, pattern: str = "*.mp3") -> list: """获取目录中的音频文件并按文件名排序""" audio_path = Path(audio_dir) if not audio_path.exists(): print(f"错误: 目录不存在 {audio_dir}") return [] files = list(audio_path.glob(pattern)) files.sort() # 过滤掉已经存在的完整版文件(避免重复合并) files = [ f for f in files if "完整版" not in f.name and "complete" not in f.name.lower() ] return [str(f) for f in files] def merge_audio_files(audio_files: list, output_file: str, audio_dir: str) -> bool: """使用 ffmpeg 合并音频文件""" if not audio_files: print("错误: 没有找到音频文件") return False print(f"找到 {len(audio_files)} 个音频文件") print("文件列表:") for i, f in enumerate(audio_files[:5], 1): print(f" {i}. {os.path.basename(f)}") if len(audio_files) > 5: print(f" ... 还有 {len(audio_files) - 5} 个文件") print() # 创建 ffmpeg concat 列表文件 concat_file = os.path.join(audio_dir, ".merge_list.txt") try: with open(concat_file, "w", encoding="utf-8") as f: for audio_file in audio_files: # 使用相对路径 rel_path = os.path.relpath(audio_file, audio_dir) f.write(f"file '{rel_path}'\n") print(f"开始合并到: {os.path.basename(output_file)}") # 构建 ffmpeg 命令 cmd = [ "ffmpeg", "-y", # 覆盖已存在的文件 "-f", "concat", "-safe", "0", "-i", concat_file, "-c", "copy", # 直接复制,不重新编码 output_file, ] # 执行合并 result = subprocess.run(cmd, capture_output=True, cwd=audio_dir) if result.returncode == 0: file_size = os.path.getsize(output_file) / (1024 * 1024) print(f"✓ 合并成功!") print(f" 输出文件: {os.path.basename(output_file)}") print(f" 文件大小: {file_size:.1f} MB") print(f" 包含文件: {len(audio_files)} 个") return True else: print(f"✗ 合并失败") error_msg = ( result.stderr.decode("utf-8", errors="ignore") if result.stderr else "未知错误" ) if "ffmpeg" in error_msg.lower() and "not found" in error_msg.lower(): print(" 提示: 未找到 ffmpeg,请先安装 ffmpeg 并添加到 PATH") else: print(f" 错误: {error_msg[:200]}") return False finally: # 清理临时文件 if os.path.exists(concat_file): os.remove(concat_file) def main(): parser = argparse.ArgumentParser( description="音频文件合并工具", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=""" 示例: %(prog)s ./audio_chapters %(prog)s ./audio_chapters --output 完整版.mp3 %(prog)s ./audio_chapters --pattern "chapter_*.mp3" 注意: 需要先安装 ffmpeg: https://ffmpeg.org/download.html """, ) parser.add_argument("audio_dir", help="音频文件所在目录") parser.add_argument( "--output", "-o", help="输出文件名(默认: 00_完整版_合并音频.mp3)" ) parser.add_argument( "--pattern", default="*.mp3", help="文件匹配模式(默认: *.mp3)" ) args = parser.parse_args() # 检查目录 if not os.path.isdir(args.audio_dir): print(f"错误: 目录不存在 {args.audio_dir}") sys.exit(1) # 获取音频文件 audio_files = get_audio_files(args.audio_dir, args.pattern) if not audio_files: print("没有找到音频文件,退出") sys.exit(1) # 确定输出文件名 if args.output: output_file = os.path.join(args.audio_dir, args.output) else: output_file = os.path.join(args.audio_dir, "00_完整版_合并音频.mp3") # 检查输出文件是否已存在 if os.path.exists(output_file): print(f"警告: 输出文件已存在,将被覆盖: {os.path.basename(output_file)}") response = input("是否继续? (y/n): ") if response.lower() != "y": print("已取消") sys.exit(0) print("=" * 60) print("音频合并工具") print("=" * 60) print() # 执行合并 success = merge_audio_files(audio_files, output_file, args.audio_dir) print() print("=" * 60) if success: print("合并完成!") else: print("合并失败!") print("=" * 60) if __name__ == "__main__": main()