跳转至

基于 FFmpeg 的 OGG 转 MP3 批量转换方法


🛠️ 1. 环境准备(Windows)

访问 FFmpeg 官网: 🔗 ffmpeg.org/download.html → 点击 Windows → 跳转到 gyan.dev 下载并解压。

💡 参数说明:

  • ffmpeg/ffprobe: 外部命令行工具,脚本中已指定绝对路径 C:\ffmpeg\bin\
  • -qscale:a 2: 表示高质量 MP3(动态比特率 VBR,范围 0–9,数值越低音质越好)。
  • -y: 自动覆盖同名的目标文件。
  • subprocess.run(): 让 Python 自动调用系统命令行执行转换。

💻 2. Python 转换脚本源码

import os
import subprocess
import json
from pathlib import Path

# 配置 FFmpeg 和 ffprobe 的实际路径
ffmpeg_path = r"C:\ffmpeg\bin\ffmpeg.exe"
ffprobe_path = r"C:\ffmpeg\bin\ffprobe.exe"
download_path = str(Path.home() / "Downloads")

# 用于记录已生成的 MP3 文件名,防止重名冲突
used_names = set()

def get_all_metadata_tags(filepath):
    """使用 ffprobe 提取音频文件所有的元数据标签(Tags)"""
    try:
        result = subprocess.run([
            ffprobe_path, "-v", "quiet",
            "-print_format", "json",
            "-show_format",
            "-show_streams",
            filepath
        ], capture_output=True, text=True, encoding='utf-8', check=True)

        if not result.stdout:
            print(f"⚠️ ffprobe 没有输出: {filepath}")
            return {}

        info = json.loads(result.stdout)
        tags = {}

        # 1. 读取文件格式级别的标签
        format_tags = info.get("format", {}).get("tags", {})
        for k, v in format_tags.items():
            tags[k.upper()] = v

        # 2. 读取音频流级别的标签
        streams = info.get("streams", [])
        for stream in streams:
            stream_tags = stream.get("tags", {})
            for k, v in stream_tags.items():
                tags[k.upper()] = v

        return tags
    except subprocess.CalledProcessError:
        print(f"⚠️ ffprobe 调用失败: {filepath}")
        return {}
    except json.JSONDecodeError:
        print(f"⚠️ JSON 解析失败: {filepath}")
        return {}

# 遍历下载文件夹中的所有 .ogg 文件
for root, dirs, files in os.walk(download_path):
    for file in files:
        if file.lower().endswith(".ogg"):
            ogg_path = os.path.join(root, file)

            print(f"\n[调试] 正在解析: {ogg_path}")
            tags = get_all_metadata_tags(ogg_path)
            print(f"元数据内容: {tags}")

            # 命名策略:优先使用 标题 - 艺术家
            title = tags.get("TITLE")
            artist = tags.get("ARTIST")

            if title and artist:
                name = f"{title} - {artist}"
            elif title:
                name = title
            elif artist:
                name = artist
            else:
                name = os.path.splitext(file)[0]  # 无标签则使用原文件名

            # 过滤掉 Windows 不支持的特殊字符,只保留字母、数字、空格和部分符号
            safe_name = "".join(c for c in name if c.isalnum() or c in " _-").strip()
            if not safe_name:
                safe_name = "output"

            # 防止文件名重复的递增计数器
            mp3_filename = safe_name + ".mp3"
            counter = 1
            while mp3_filename.lower() in used_names or os.path.exists(os.path.join(root, mp3_filename)):
                mp3_filename = f"{safe_name}_{counter}.mp3"
                counter += 1

            used_names.add(mp3_filename.lower())
            mp3_path = os.path.join(root, mp3_filename)

            # 调用 FFmpeg 执行核心转换逻辑
            try:
                subprocess.run([
                    ffmpeg_path, "-y", "-i", ogg_path,
                    "-codec:a", "libmp3lame", "-qscale:a", "2", mp3_path
                ], check=True)
                print(f"✅ 转换成功:{mp3_path}")
            except subprocess.CalledProcessError:
                print(f"❌ 转换失败:{ogg_path}")

💡 使用须知

本脚本仅用于个人本地音频文件的格式转换与多媒体库元数据(Metadata)整理,请勿用于任何侵犯第三方版权的非法用途。