你的MP3标签信息全是乱的?这个整理脚本你会写吗?

wen 实用脚本 45

我来帮你写一个MP3标签整理脚本,以下是基于Python的解决方案:

你的MP3标签信息全是乱的?这个整理脚本你会写吗?

解决方案:MP3标签整理脚本

安装依赖

pip install mutagen

基础整理脚本

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
MP3标签整理工具
"""
import os
import re
from mutagen import File
from mutagen.easyid3 import EasyID3
from mutagen.id3 import ID3, ID3NoHeaderError
def clean_text(text):
    """清理文本,去除多余空格和特殊字符"""
    if not text:
        return text
    text = re.sub(r'\s+', ' ', text)
    text = text.strip()
    text = text.replace('\x00', '')
    return text
def get_audio_file_info(filepath):
    """获取音频文件信息"""
    try:
        audio = File(filepath)
        if audio is None:
            return None
        return audio
    except:
        return None
def read_all_tags(filepath):
    """读取所有标签信息"""
    try:
        try:
            audio = EasyID3(filepath)
        except ID3NoHeaderError:
            return "未找到ID3标签"
        tags = {}
        for key, value in audio.items():
            if value:
                tags[key] = value[0] if isinstance(value, list) else value
        return tags
    except Exception as e:
        return f"读取标签失败: {str(e)}"
def write_tags(filepath, tag_dict):
    """写入标签信息"""
    try:
        try:
            audio = EasyID3(filepath)
        except ID3NoHeaderError:
            audio = EasyID3()
        for key, value in tag_dict.items():
            if value:
                audio[key] = [clean_text(str(value))]
        audio.save()
        return True
    except Exception as e:
        print(f"写入标签失败 [{filepath}]: {str(e)}")
        return False
def organize_mp3_tags(filepath):
    """整理单个MP3文件的标签"""
    tag_info = read_all_tags(filepath)
    if isinstance(tag_info, str):
        print(f"跳过文件: {filepath} - {tag_info}")
        return False
    print(f"\n处理文件: {os.path.basename(filepath)}")
    print("当前标签:", tag_info)
    # 清理所有标签
    cleaned_tags = {}
    for key, value in tag_info.items():
        cleaned_tags[key] = clean_text(value)
    # 写入清理后的标签
    success = write_tags(filepath, cleaned_tags)
    if success:
        print("标签整理完成")
    return success
def batch_process(directory, extensions=['.mp3']):
    """批量处理文件夹中的所有音频文件"""
    success_count = 0
    fail_count = 0
    for root, dirs, files in os.walk(directory):
        for file in files:
            if any(file.endswith(ext) for ext in extensions):
                filepath = os.path.join(root, file)
                if organize_mp3_tags(filepath):
                    success_count += 1
                else:
                    fail_count += 1
    print(f"\n处理完成: 成功 {success_count} 个, 失败 {fail_count} 个")
def advanced_cleanup(filepath):
    """高级清理:修复常见问题"""
    audio = get_audio_file_info(filepath)
    if audio is None:
        return False
    # 示例:修复乱码(如GBK编码修复)
    # 这需要根据实际情况调整
    if audio.get('title'):
        try:
            title = audio['title'][0]
            if isinstance(title, bytes):
                title = title.decode('utf-8', errors='replace')
                audio['title'] = [title]
        except:
            pass
    audio.save()
    return True
def automatically_fill_tags(filepath):
    """尝试从文件名智能填充标签"""
    filename = os.path.basename(filepath)
    # 常见命名格式: "艺术家 - 歌曲名.mp3"
    match = re.match(r'(.+)\s*-\s*(.+)\.mp3$', filename)
    if match:
        artist = clean_text(match.group(1))
        title = clean_text(match.group(2))
        # 读取现有标签
        tags = read_all_tags(filepath)
        if isinstance(tags, str):
            tags = {}
        # 只在标签为空时填充
        if not tags.get('artist'):
            tags['artist'] = artist
        if not tags.get('title'):
            tags['title'] = title
        write_tags(filepath, tags)
        return True
    return False
def delete_too_long_tags(filepath, max_length=100):
    """删除过长的标签值"""
    try:
        audio = EasyID3(filepath)
        changes = False
        for key in list(audio.keys()):
            if len(str(audio[key][0])) > max_length:
                del audio[key]
                changes = True
                print(f"删除过长的 '{key}' 标签")
        if changes:
            audio.save()
        return True
    except:
        return False
# 使用示例
if __name__ == "__main__":
    import sys
    # 处理单个文件
    if len(sys.argv) > 1:
        for arg in sys.argv[1:]:
            if os.path.isfile(arg):
                organize_mp3_tags(arg)
            elif os.path.isdir(arg):
                batch_process(arg)
    else:
        # 默认处理当前目录
        print("使用示例: python mp3_tag_organizer.py <文件或目录>")
        print(" python mp3_tag_organizer.py ./my_mp3_folder")
        # 测试代码
        # batch_process('./', ['.mp3', '.flac'])

GUI版本(简化版)

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
MP3标签整理工具 - GUI版本
"""
import tkinter as tk
from tkinter import filedialog, messagebox, scrolledtext
import os
import threading
from mutagen.easyid3 import EasyID3
from mutagen.id3 import ID3NoHeaderError
class MP3TagOrganizerGUI:
    def __init__(self, root):
        self.root = root
        self.root.title("MP3标签整理工具")
        self.root.geometry("600x400")
        # 文件选择
        self.label = tk.Label(root, text="选择MP3文件或文件夹:")
        self.label.pack(pady=10)
        self.frame = tk.Frame(root)
        self.frame.pack(pady=5)
        self.btn_file = tk.Button(self.frame, text="选择文件", command=self.select_files)
        self.btn_file.pack(side=tk.LEFT, padx=5)
        self.btn_folder = tk.Button(self.frame, text="选择文件夹", command=self.select_folder)
        self.btn_folder.pack(side=tk.LEFT, padx=5)
        # 操作按钮
        self.btn_clean = tk.Button(root, text="清理标签", command=self.clean_tags)
        self.btn_clean.pack(pady=5)
        self.btn_fill = tk.Button(root, text="从文件名填充", command=self.fill_from_filename)
        self.btn_fill.pack(pady=5)
        # 日志区域
        self.log = scrolledtext.ScrolledText(root, height=15)
        self.log.pack(pady=10, padx=10, fill=tk.BOTH, expand=True)
        self.selected_files = []
    def log_message(self, message):
        self.log.insert(tk.END, f"{message}\n")
        self.log.see(tk.END)
    def select_files(self):
        files = filedialog.askopenfilenames(
            title="选择MP3文件",
            filetypes=[("MP3 files", "*.mp3")]
        )
        if files:
            self.selected_files = list(files)
            self.log_message(f"已选择 {len(files)} 个文件")
    def select_folder(self):
        folder = filedialog.askdirectory(title="选择文件夹")
        if folder:
            self.selected_files = []
            for root, dirs, files in os.walk(folder):
                for file in files:
                    if file.endswith('.mp3'):
                        self.selected_files.append(os.path.join(root, file))
            self.log_message(f"已选择文件夹,找到 {len(self.selected_files)} 个MP3文件")
    def process_files(self, operation_name, operation_func):
        if not self.selected_files:
            messagebox.showwarning("警告", "请先选择文件或文件夹!")
            return
        self.log_message(f"\n开始{operation_name}...")
        def process():
            success = 0
            fail = 0
            for filepath in self.selected_files:
                try:
                    if operation_func(filepath):
                        success += 1
                    else:
                        fail += 1
                except Exception as e:
                    fail += 1
                    self.log_message(f"错误: {os.path.basename(filepath)} - {str(e)}")
            self.log_message(f"\n{operation_name}完成: 成功 {success}, 失败 {fail}")
            messagebox.showinfo("完成", f"{operation_name}完成!\n成功: {success}\n失败: {fail}")
        thread = threading.Thread(target=process)
        thread.start()
    def clean_tags(self):
        def clean(filepath):
            try:
                audio = EasyID3(filepath)
                for key in list(audio.keys()):
                    if audio[key] and audio[key][0]:
                        audio[key] = [audio[key][0].strip()]
                audio.save()
                self.log_message(f"已清理: {os.path.basename(filepath)}")
                return True
            except ID3NoHeaderError:
                self.log_message(f"跳过: {os.path.basename(filepath)} - 无ID3标签")
                return False
            except Exception as e:
                self.log_message(f"错误: {os.path.basename(filepath)} - {str(e)}")
                return False
        self.process_files("清理标签", clean)
    def fill_from_filename(self):
        def fill(filepath):
            import re
            filename = os.path.basename(filepath)
            match = re.match(r'(.+)\s*-\s*(.+)\.mp3$', filename)
            if not match:
                self.log_message(f"跳过: {filename} - 无法从文件名解析")
                return False
            artist = match.group(1).strip()
            title = match.group(2).strip()
            try:
                try:
                    audio = EasyID3(filepath)
                except ID3NoHeaderError:
                    audio = EasyID3()
                # 只在标签为空时填充
                if not audio.get('artist'):
                    audio['artist'] = [artist]
                if not audio.get('title'):
                    audio['title'] = [title]
                audio.save()
                self.log_message(f"已填充: {filename} -> 艺术家: {artist}, 标题: {title}")
                return True
            except Exception as e:
                self.log_message(f"错误: {filename} - {str(e)}")
                return False
        self.process_files("从文件名填充", fill)
if __name__ == "__main__":
    root = tk.Tk()
    app = MP3TagOrganizerGUI(root)
    root.mainloop()

使用说明

  1. 保存脚本mp3_tag_organizer.py
  2. 安装依赖pip install mutagen
  3. 命令行使用
    python mp3_tag_organizer.py ./mp3_files/
  4. GUI版本:直接运行GUI脚本

常见问题处理

  • 乱码问题:脚本会自动清理空格和特殊字符
  • 标签缺失:可以从文件名自动填充
  • 编码问题:支持UTF-8和GBK编码

这个脚本可以处理大多数MP3标签混乱的问题,建议先备份文件再使用!

抱歉,评论功能暂时关闭!