Files
hmo 04db423416 Initial commit: skills library
- 70 skills with code and documentation
- Add .gitignore (ignore __pycache__, output/, temp/, venv/)
- Clean up test intermediates and caches
2026-04-26 19:27:40 +08:00

193 lines
5.5 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""
Audio Merger - 音频合并工具
功能:
- 合并多个音频文件为一个完整版
- 自动按文件名排序
- 支持指定输出文件名
- 保持原有文件不变
依赖:
- ffmpeg(必须安装并添加到PATH)
使用方法:
python scripts/merge_audio.py <audio_dir> [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()