怎么用脚本整理音乐库

wen 实用脚本 2

本文目录导读:

怎么用脚本整理音乐库

  1. 基础整理脚本
  2. 高级功能版本
  3. 使用方法
  4. 注意事项

我来教你用脚本整理音乐库,这里提供一个Python脚本方案,能自动处理常见问题:

基础整理脚本

#!/usr/bin/env python3
import os
import re
import shutil
import json
from pathlib import Path
import mutagen
from mutagen.easyid3 import EasyID3
from mutagen.mp3 import MP3
from mutagen.flac import FLAC
class MusicOrganizer:
    def __init__(self, music_dir):
        self.music_dir = Path(music_dir)
        self.organized_dir = self.music_dir / "已整理"
    def get_audio_metadata(self, file_path):
        """读取音频文件元数据"""
        try:
            ext = file_path.suffix.lower()
            if ext == '.mp3':
                audio = EasyID3(file_path)
                return {
                    'artist': audio.get('artist', [''])[0],
                    'album': audio.get('album', [''])[0],
                    'title': audio.get('title', [''])[0],
                    'tracknumber': audio.get('tracknumber', [''])[0]
                }
            elif ext == '.flac':
                audio = FLAC(file_path)
                return {
                    'artist': audio.get('artist', [''])[0],
                    'album': audio.get('album', [''])[0],
                    'title': audio.get('title', [''])[0],
                    'tracknumber': audio.get('tracknumber', [''])[0]
                }
        except Exception as e:
            print(f"读取 {file_path} 元数据失败: {e}")
        return None
    def sanitize_filename(self, name):
        """清理文件名中的非法字符"""
        # 替换不合法字符
        illegal_chars = r'[<>:"/\\|?*]'
        return re.sub(illegal_chars, '_', name).strip()
    def organize_by_artist_album(self):
        """按 艺术家/专辑 结构整理"""
        if not self.music_dir.exists():
            print("音乐目录不存在!")
            return
        # 创建整理目录
        self.organized_dir.mkdir(exist_ok=True)
        # 遍历所有音频文件
        audio_exts = {'.mp3', '.flac', '.wav', '.m4a', '.ogg'}
        moved_files = 0
        for file_path in self.music_dir.rglob('*'):
            if file_path.suffix.lower() not in audio_exts:
                continue
            metadata = self.get_audio_metadata(file_path)
            # 如果没有元数据,使用文件名推测
            if metadata is None or not metadata['artist']:
                artist = '未知艺术家'
                album = '未知专辑'
            else:
                artist = self.sanitize_filename(metadata['artist'])
                album = self.sanitize_filename(metadata['album'] or '未知专辑')
            # 构建目标路径
            target_dir = self.organized_dir / artist / album
            target_dir.mkdir(parents=True, exist_ok=True)
            # 重命名文件(添加序号)
            filename = file_path.name
            if metadata and metadata['tracknumber']:
                try:
                    track_num = int(re.search(r'\d+', metadata['tracknumber']).group())
                    filename = f"{track_num:02d} - {self.sanitize_filename(metadata['title'] or file_path.stem)}{file_path.suffix}"
                except:
                    pass
            # 处理重名
            target_file = target_dir / filename
            if target_file.exists():
                base = target_file.stem
                target_file = target_dir / f"{base}_1{target_file.suffix}"
            # 移动文件
            try:
                shutil.move(str(file_path), str(target_file))
                moved_files += 1
                print(f"✓ {file_path.name} → {artist}/{album}/{filename}")
            except Exception as e:
                print(f"✗ 移动 {file_path} 失败: {e}")
        print(f"\n完成!共整理 {moved_files} 个文件")
    def fix_missing_tags(self):
        """智能修复文件名中的元数据"""
        # 从文件名提取信息: 艺术家 - 专辑 - 曲目号 歌曲名.mp3
        pattern = r'(.+?)\s*-\s*(.+?)\s*-\s*(\d+)\s*(.+)'
        for file_path in self.music_dir.rglob('*.mp3'):
            match = re.match(pattern, file_path.stem)
            if match and not self.get_audio_metadata(file_path):
                artist, album, track, title = match.groups()
                try:
                    audio = EasyID3(file_path)
                    audio['artist'] = artist.strip()
                    audio['album'] = album.strip()
                    audio['title'] = title.strip()
                    audio['tracknumber'] = track.strip()
                    audio.save()
                    print(f"修复标签: {file_path.name}")
                except Exception as e:
                    print(f"修复失败 {file_path.name}: {e}")
    def generate_playlist(self):
        """生成通过播放列表"""
        # 按流派分类
        genres = {}
        for file_path in self.organized_dir.rglob('*.mp3'):
            try:
                audio = EasyID3(file_path)
                genre = audio.get('genre', ['未知'])[0]
                if genre not in genres:
                    genres[genre] = []
                genres[genre].append(str(file_path))
            except:
                pass
        # 生成M3U播放列表
        playlist_dir = self.music_dir / '播放列表'
        playlist_dir.mkdir(exist_ok=True)
        for genre, files in genres.items():
            playlist_file = playlist_dir / f"{genre}.m3u"
            with open(playlist_file, 'w', encoding='utf-8') as f:
                f.write("#EXTM3U\n")
                for file_path in files:
                    f.write(f"#EXTINF:{Path(file_path).stat().st_size // 1000},{Path(file_path).stem}\n")
                    f.write(f"{file_path}\n")
            print(f"生成播放列表: {genre}.m3u ({len(files)}首)")
# 使用示例
if __name__ == "__main__":
    # 使用方法:
    # python music_organizer.py /path/to/music
    import sys
    if len(sys.argv) < 2:
        print("用法: python music_organizer.py <音乐文件夹路径>")
        sys.exit(1)
    organizer = MusicOrganizer(sys.argv[1])
    print("选择操作方式:")
    print("1. 按艺术家/专辑整理")
    print("2. 修复缺失的标签")
    print("3. 生成播放列表")
    print("4. 全部执行")
    choice = input("输入选择 (1-4): ")
    if choice in ['1', '4']:
        organizer.organize_by_artist_album()
    if choice in ['2', '4']:
        organizer.fix_missing_tags()
    if choice in ['3', '4']:
        organizer.generate_playlist()

高级功能版本

如果你想处理更复杂的情况,这里有个增强版:

# 增强功能
def advanced_functions(self):
    """高级整理功能"""
    # 1. 去重功能
    def find_duplicates(self):
        """根据文件大小和MD5查找重复音乐"""
        import hashlib
        seen_files = {}
        duplicates = []
        for file_path in self.music_dir.rglob('*'):
            if file_path.suffix.lower() not in ['.mp3', '.flac', '.wav']:
                continue
            # 计算文件哈希
            file_hash = hashlib.md5(file_path.read_bytes()).hexdigest()
            if file_hash in seen_files:
                duplicates.append((seen_files[file_hash], file_path))
            else:
                seen_files[file_hash] = file_path
        return duplicates
    # 2. 批量重命名
    def batch_rename(self):
        """批量重命名文件,统一格式"""
        for file_path in self.music_dir.rglob('*'):
            if file_path.suffix.lower() not in ['.mp3', '.flac']:
                continue
            # 智能识别格式并规范化
            #  01-song.mp3 → 01_song.mp3
            #       artist-album-track.mp3 → 艺术家-专辑-01 歌曲名.mp3
            pass  # 实现你的重命名逻辑
    # 3. 封面管理
    def manage_covers(self):
        """下载和嵌入封面"""
        import requests
        cover_dir = self.music_dir / 'covers'
        cover_dir.mkdir(exist_ok=True)
        # 对没有封面的专辑下载封面
        # 可以使用 iTunes API 或 Last.fm API
        pass
    # 4. 格式转换
    def convert_formats(self):
        """批量格式转换(如flac转mp3)"""
        import subprocess
        for flac_file in self.music_dir.rglob('*.flac'):
            mp3_file = flac_file.with_suffix('.mp3')
            # 使用 ffmpeg 转换
            subprocess.run([
                'ffmpeg', '-i', str(flac_file), 
                '-codec:a', 'libmp3lame', 
                '-qscale:a', '2', 
                str(mp3_file)
            ])

使用方法

  1. 安装依赖

    pip install mutagen
  2. 基础使用

    python music_organizer.py ~/Music
  3. 高级使用(可添加到crontab定期执行):

    # 每周日晚上整理
    0 20 * * 0 python /path/to/music_organizer.py ~/Music

注意事项

  1. 备份原始文件:整理前务必备份,脚本会移动文件
  2. 测试运行:先在小目录测试,确认无误后再处理完整音乐库
  3. 保留原文件:修改脚本中的shutil.moveshutil.copy即可保留原文件
  4. 处理特殊情况:歌手"Various Artists"、日本艺名、多艺术家合作等特殊场景需要额外处理

这个脚本基本能满足大部分整理需求,如果需要修改特定行为,告诉我你的具体需求,我可以提供定制版本。

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