自己写的, 基于FFMPEG以及Python.
用来将绝大多数能够遇得到的视频格式无脑的转成AAC, 存入一个新的文件夹, 输出时保留文件名
同时处理十个文件.
#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
import os
import sys
import subprocess
from concurrent.futures import ThreadPoolExecutor, as_completed
from pathlib import Path
# 配置
VIDEO_EXTS = ('.mp4', '.mkv', '.avi', '.mov', '.wmv', '.flv', '.webm', '.ts')
OUTPUT_DIR = "audio_extract"
MAX_WORKERS = 10 # 并行线程数
FFMPEG_CMD = "ffmpeg"
def find_videos():
"""查找当前目录下所有视频文件"""
videos = []
for f in os.listdir('.'):
if f.lower().endswith(VIDEO_EXTS):
videos.append(f)
return videos
def should_skip(output_path):
"""
判断是否应该跳过该文件
- 文件不存在 → 不跳过(需要转换)
- 文件存在但大小为0 → 不跳过(覆盖)
- 文件存在且大小>0 → 跳过
"""
if not os.path.exists(output_path):
return False, "新文件"
file_size = os.path.getsize(output_path)
if file_size == 0:
# 空文件,删除后重新转换
os.remove(output_path)
return False, "空文件(覆盖)"
return True, f"已存在 ({file_size / 1024:.1f} KB)"
def extract_audio(video_path):
"""提取单个视频的音频"""
filename = Path(video_path).stem
output_path = os.path.join(OUTPUT_DIR, f"{filename}.m4a")
# 检查是否需要跳过
skip, reason = should_skip(output_path)
if skip:
return ("skip", filename, reason)
cmd = [
FFMPEG_CMD,
'-hide_banner',
'-loglevel', 'quiet',
'-i', video_path,
'-vn',
'-c:a', 'aac',
'-b:a', '256k',
'-ar', '44100',
'-y',
output_path
]
try:
result = subprocess.run(cmd, capture_output=True, text=True)
if result.returncode == 0:
# 验证输出文件确实生成且非空
if os.path.exists(output_path) and os.path.getsize(output_path) > 0:
return ("success", filename, None)
else:
return ("fail", filename, "输出文件为空")
else:
return ("fail", filename, result.stderr[:100])
except Exception as e:
return ("fail", filename, str(e))
def main():
print("=" * 42)
print(" 视频批量转 AAC 256k")
print("=" * 42)
print()
# 检查ffmpeg
try:
subprocess.run([FFMPEG_CMD, '-version'], capture_output=True, check=True)
except (subprocess.CalledProcessError, FileNotFoundError):
print("错误:未找到 FFmpeg,请确保已安装并添加到PATH")
input("按回车键退出...")
sys.exit(1)
# 创建输出目录
os.makedirs(OUTPUT_DIR, exist_ok=True)
# 查找视频
videos = find_videos()
total = len(videos)
if total == 0:
print("未找到视频文件!")
input("按回车键退出...")
return
print(f"发现 {total} 个视频文件")
print(f"输出目录: {OUTPUT_DIR}")
print(f"并行线程: {MAX_WORKERS}")
print("-" * 42)
# 多线程并行处理
stats = {"success": 0, "skip": 0, "fail": 0}
failed_tasks = []
with ThreadPoolExecutor(max_workers=MAX_WORKERS) as executor:
future_to_video = {
executor.submit(extract_audio, v): v for v in videos
}
for future in as_completed(future_to_video):
status, filename, info = future.result()
if status == "success":
print(f"[OK] {filename}.m4a")
stats["success"] += 1
elif status == "skip":
print(f"[SKIP] {filename}.m4a ({info})")
stats["skip"] += 1
else:
print(f"[FAIL] {filename} - {info}")
stats["fail"] += 1
failed_tasks.append((filename, info))
print("-" * 42)
print(f"结果: 成功 {stats['success']} | 跳过 {stats['skip']} | 失败 {stats['fail']} | 总计 {total}")
if failed_tasks:
print(f"\n失败详情:")
for name, err in failed_tasks:
print(f" - {name}: {err}")
print()
input("按回车键退出...")
if __name__ == "__main__":
main()