Featured image of post Linux 音乐库自动化分类整理全记录:从混乱目录到多歌单归档

Linux 音乐库自动化分类整理全记录:从混乱目录到多歌单归档

用 Python + Shell 把上千个混乱的音乐文件按 DJ、儿歌、经典老歌、纯音乐等类别自动归档,保留扩展名,支持歌词配对、误分类修正与后续维护。

字数: 5357

Linux 音乐库自动化分类整理全记录:从混乱目录到多歌单归档

环境:CentOS / RHEL 类系统 路径:/usr/local/music_player/music Python:python3(虚拟环境 venvmusic3.12) 目标:把上千个 .mp3、.flac、.m4a、.lrc 文件,按 DJ、儿歌、经典老歌、纯音乐、其他分类、余哥专属、跑步、书珩专属、众音归档等目录自动归档,并保留扩展名。


一、背景与目标

音乐目录一开始非常混乱:

text

/usr/local/music_player/music/
├── 100首早教儿歌连续播放来啦!....mp3
├── 【15首经典英文儿歌】....lrc
├── 2026抖音热歌榜长期霸榜歌曲.mp3
├── Alan Walker – Faded.mp3
├── Beyond – 海阔天空.mp3
├── DJ – Hello friend.mp3
├── 儿歌多多 - 小燕子.mp3
├── 纯音乐 - 太极.mp3
├── 经典老歌、流行歌曲、DJ、儿歌、纯音乐混杂在一起
└── ...

文件类型包括:

  • 歌曲:.mp3、.flac、.m4a
  • 歌词:.lrc
  • 其他:default.png、music_player.db

目标分类目录:

DJ
儿歌
经典老歌
其他分类
纯音乐
余哥专属
跑步
书珩拾声重组
余哥拾声重组
书珩专属
众音归档

分类原则:保留扩展名,例如 Beyond – 海阔天空.mp3 和 Beyond – 海阔天空.lrc 会分别归档。


二、创建分类目录

先进入音乐根目录:

cd /usr/local/music_player/music

创建所有目标目录:

mkdir -p DJ 儿歌 经典老歌 其他分类 纯音乐 余哥专属 跑步 书珩拾声重组 余哥拾声重组 书珩专属 众音归档

确认目录:

ls -ld DJ 儿歌 经典老歌 其他分类 纯音乐 余哥专属 跑步 书珩拾声重组 余哥拾声重组 书珩专属 众音归档

三、编写自动分类脚本

创建 classify_music.py:

cd /usr/local/music_player/music
cat > classify_music.py <<'PY'
import os, re

SRC = "/usr/local/music_player/music"
OUT = os.path.join(SRC, "分类清单")
os.makedirs(OUT, exist_ok=True)

EXTS = (".mp3", ".flac", ".m4a", ".lrc")

YG_FILE = os.path.join(OUT, "余哥专属名单.txt")
if not os.path.exists(YG_FILE):
    with open(YG_FILE, "w", encoding="utf-8") as f:
        f.write("# 每行写一个完整文件名,保留扩展名,例如:\n")
        f.write("# 某某歌曲.mp3\n")
        f.write("# 某某歌曲.lrc\n")

YG = set()
with open(YG_FILE, encoding="utf-8") as f:
    for line in f:
        line = line.strip()
        if line and not line.startswith("#"):
            YG.add(line)

RULES = [
    ("DJ", re.compile(
        r"DJ|Dj|dj|Remix|remix|Mix|mix|电音|越南鼓|车载|劲嗨|嗨曲|Phonk|PHONK|"
        r"EDM|舞曲|慢摇|8D|3D环绕|环绕|重低音|0\.8x|Extended|Club|Radio Edit",
        re.I
    )),
    ("儿歌", re.compile(
        r"儿歌|儿童|宝宝|早教|童声|幼儿园|贝乐虎|儿歌多多|三三儿歌|小跳蛙|小星星|"
        r"两只老虎|小燕子|小白兔|鲨鱼宝宝|小兔子乖乖|拔萝卜|数鸭子|粉刷匠|"
        r"蜗牛与黄鹂鸟|鲁冰花|虫儿飞|新年好|白龙马|猪猪侠|喜羊羊|加油鸭|"
        r"生日快乐歌|小小姑娘|小螺号|大风车|捉泥鳅|正义的警车|一只哈巴狗|"
        r"三字经|声律启蒙|道德经|古诗儿歌|国学|故事|绘本|睡前|哄睡|催眠曲|"
        r"羊水声|嘘声|摇篮曲|童谣|童话|小鸭子|小手拍拍|刷牙歌|快乐起床|"
        r"爱我你就抱抱我|猴哥|超级便便|鲨鱼Shark|问月|明天会更好|萱草花",
        re.I
    )),
    ("纯音乐", re.compile(
        r"纯音乐|纯音|钢琴|PIANO|Piano|钢琴版|轻音乐|伴奏|器乐|古琴|吉他|二胡|"
        r"琵琶|笛|萧|萨克斯|小提琴|大提琴|纯享版|演奏|Music for|Relax|冥想|"
        r"白噪音|ASMR|颂钵|流水声|雨声|海边|湖畔|睡眠|放松|解压|治愈|氛围|"
        r"环境音|Lullaby|River Flows|Canon|卡农|天空之城|Summer|菊次郎|"
        r"故乡的原风景|Nightingale|Annie|Childhood|Snowdreams|Sounds Of Silence|"
        r"Wishes|Eutopia|Near Light|One Step Ahead|On the Nature|Water Copy|"
        r"With an Orchid|风居住的街道|雨的印记|安静的午后|散落的星星|浮光|仙境|"
        r"太极|故乡的小路|梦的地方|瞬间的永恒|逍遥游|花之舞|青空|所念皆星河|"
        r"和煦的糖果风|风之谷|Windy Hill",
        re.I
    )),
    ("经典老歌", re.compile(
        r"Beyond|BEYOND|光辉岁月|海阔天空|真的爱你|张国荣|张学友|刘德华|邓丽君|"
        r"陈百强|陈慧娴|谭咏麟|周华健|李宗盛|罗大佑|费玉清|叶蒨文|叶丽仪|"
        r"上海滩|千千阕歌|吻别|一起走过的日子|来生缘|月亮代表我的心|漫步人生路|"
        r"一剪梅|梦驼铃|朋友|当年情|风继续吹|讲不出再见|爱在深秋|水中花|"
        r"一生不变|一生何求|祝福你|财神到|恭喜发财|拜新年|好运来|新年快乐|"
        r"春节|过年|铁血丹心|潇洒走一回|新不了情|哭砂|梦醒时分|后来|十年|"
        r"童话|勇气|红豆|约定",
        re.I
    )),
]

CATS = ["DJ", "纯音乐", "儿歌", "其他分类", "经典老歌", "余哥专属"]

for c in CATS:
    with open(os.path.join(OUT, c + ".txt"), "w", encoding="utf-8"):
        pass

all_file = open(os.path.join(OUT, "全部歌曲歌词名.txt"), "w", encoding="utf-8")

for name in sorted(os.listdir(SRC)):
    path = os.path.join(SRC, name)
    if not os.path.isfile(path):
        continue
    if not name.lower().endswith(EXTS):
        continue

    all_file.write(name + "\n")

    if name in YG:
        cat = "余哥专属"
    else:
        cat = "其他分类"
        for c, pat in RULES:
            if pat.search(name):
                cat = c
                break

    with open(os.path.join(OUT, cat + ".txt"), "a", encoding="utf-8") as f:
        f.write(name + "\n")

all_file.close()

print("分类完成,清单目录:", OUT)
for c in CATS:
    p = os.path.join(OUT, c + ".txt")
    n = sum(1 for _ in open(p, encoding="utf-8"))
    print(f"{c}: {n} 个")
PY

运行:

python3 classify_music.py

输出示例:

分类完成,清单目录: /usr/local/music_player/music/分类清单
DJ: 261 个
纯音乐: 135 个
儿歌: 135 个
其他分类: 684 个
经典老歌: 98 个
余哥专属: 0 个

此时 分类清单/ 下会生成:

DJ.txt
纯音乐.txt
儿歌.txt
其他分类.txt
经典老歌.txt
余哥专属.txt
全部歌曲歌词名.txt
余哥专属名单.txt

查看某个清单:

cat 分类清单/DJ.txt
cat 分类清单/纯音乐.txt
cat 分类清单/儿歌.txt

四、根据清单移动文件(第一版)

最初可以写一个简单的移动脚本:

cd /usr/local/music_player/music

for list in 分类清单/*.txt; do
    [ -f "$list" ] || continue

    cat_name=$(basename "$list" .txt)
    mkdir -p "$cat_name"

    echo "正在处理: $cat_name ..."

    while IFS= read -r f; do
        [ -z "$f" ] && continue
        [[ "$f" == \#* ]] && continue

        if [ -f "$f" ]; then
            mv -n -- "$f" "$cat_name/"
        else
            echo "  未找到: $f"
        fi
    done < "$list"
done

问题:如果同一个文件出现在多个清单里,先处理的清单会把它移走,后面的清单就会提示“未找到”。 所以后面需要更智能的 reorganize_music.py。


五、编写智能重组脚本

创建 reorganize_music.py:

cd /usr/local/music_player/music
cat > reorganize_music.py <<'PY'
import os
import shutil

BASE = "/usr/local/music_player/music"

CATS = [
    "余哥专属", "书珩专属", "跑步", "书珩拾声重组", "余哥拾声重组",
    "众音归档", "DJ", "儿歌", "经典老歌", "纯音乐", "其他分类"
]

PRIORITY = [
    "余哥专属", "书珩专属", "跑步", "书珩拾声重组", "余哥拾声重组",
    "众音归档", "经典老歌", "DJ", "儿歌", "纯音乐", "其他分类"
]

DRY_RUN = True

def find_file(name):
    search_dirs = [BASE] + [os.path.join(BASE, c) for c in CATS]
    for d in search_dirs:
        p = os.path.join(d, name)
        if os.path.isfile(p):
            return p
    return None

def move_to_cat(cat):
    list_path = os.path.join(BASE, "分类清单", f"{cat}.txt")
    if not os.path.isfile(list_path):
        print(f"跳过 {cat}: 没有清单")
        return

    target_dir = os.path.join(BASE, cat)
    os.makedirs(target_dir, exist_ok=True)

    moved = 0
    notfound = []

    with open(list_path, encoding="utf-8") as f:
        for line in f:
            name = line.strip()
            if not name or name.startswith("#"):
                continue

            target_path = os.path.join(target_dir, name)

            if os.path.isfile(target_path):
                continue

            src = find_file(name)

            if src and os.path.dirname(src) != target_dir:
                print(f"[{cat}] {src}  ->  {target_path}")
                if not DRY_RUN:
                    shutil.move(src, target_path)
                moved += 1
            elif not src:
                notfound.append(name)

    print(f"\n{cat}: 移动 {moved} 个,未找到 {len(notfound)} 个")
    if notfound:
        for n in notfound[:10]:
            print(f"  未找到: {n}")
        if len(notfound) > 10:
            print(f"  ... 还有 {len(notfound) - 10} 个")

if __name__ == "__main__":
    for cat in PRIORITY:
        move_to_cat(cat)
    print("\n完成")
PY

先预览:

python3 reorganize_music.py

确认无误后,把:

DRY_RUN = True

改为:

DRY_RUN = False

再次运行:

python3 reorganize_music.py

执行日志会显示类似:

[余哥专属] /usr/local/music_player/music/池柚 - No photo of you left to survey.flac  ->  /usr/local/music_player/music/余哥专属/...
...
余哥专属: 移动 266 个,未找到 0 个
书珩专属: 移动 8 个,未找到 0 个
跑步: 移动 24 个,未找到 0 个
书珩拾声重组: 移动 23 个,未找到 0 个
余哥拾声重组: 移动 3 个,未找到 0 个
众音归档: 移动 247 个,未找到 0 个
儿歌: 移动 1 个,未找到 0 个
其他分类: 移动 1 个,未找到 0 个
经典老歌: 移动 0 个,未找到 0 个
DJ: 移动 0 个,未找到 0 个
纯音乐: 移动 0 个,未找到 0 个
完成

六、统计与检查

查看各目录文件数量:

cd /usr/local/music_player/music

for d in DJ 儿歌 经典老歌 其他分类 纯音乐 余哥专属 跑步 书珩拾声重组 余哥拾声重组 书珩专属 众音归档; do
    echo -n "$d: "
    ls -1 "$d" 2>/dev/null | wc -l
done

示例结果:

DJ: 261
儿歌: 112
经典老歌: 116
其他分类: 74
纯音乐: 179
余哥专属: 266
跑步: 24
书珩拾声重组: 23
余哥拾声重组: 3
书珩专属: 8
众音归档: 247

检查根目录是否还有残留:

find . -maxdepth 1 -type f \( -iname '*.mp3' -o -iname '*.flac' -o -iname '*.m4a' -o -iname '*.lrc' \) -printf '%f\n' | sort

检查是否有文件漏在分类目录之外:

find . -maxdepth 2 -type f \( -iname '*.mp3' -o -iname '*.flac' -o -iname '*.m4a' -o -iname '*.lrc' \) \
  -printf '%p\n' | grep -v -E '/(DJ|儿歌|经典老歌|其他分类|纯音乐|余哥专属|跑步|书珩拾声重组|余哥拾声重组|书珩专属|众音归档)/' | sort

没有输出,说明归档完成。


七、手动微调误分类

自动规则难免有误,比如:

纯音乐里混入的人声歌曲

Céline Dion - My Heart Will Go On
Queen – We Will Rock You
Michael Learns To Rock – Take Me To Your Heart
全球顶级史诗神曲《Victory》
曲木老表 - 清风上南枝 (合唱氛围版)
琵琶行-奇然&沈谧仁
青丝 (纯享版)-时光胶囊

批量移到 众音归档:

cd /usr/local/music_player/music

for f in \
  "纯音乐/Céline Dion - My Heart Will Go On.mp3" \
  "纯音乐/Céline Dion - My Heart Will Go On.lrc" \
  "纯音乐/Queen – We Will Rock You.mp3" \
  "纯音乐/Queen – We Will Rock You.lrc" \
  "纯音乐/Michael Learns To Rock – Take Me To Your Heart.mp3" \
  "纯音乐/Michael Learns To Rock – Take Me To Your Heart.lrc" \
  "纯音乐/全球顶级史诗神曲《Victory》,气势磅礴的空灵仙音,太震撼了!.mp3" \
  "纯音乐/全球顶级史诗神曲《Victory》,气势磅礴的空灵仙音,太震撼了!.lrc" \
  "纯音乐/曲木老表 - 清风上南枝 (合唱氛围版).flac" \
  "纯音乐/曲木老表 - 清风上南枝 (合唱氛围版).lrc" \
  "纯音乐/琵琶行-奇然&沈谧仁.mp3" \
  "纯音乐/琵琶行-奇然&沈谧仁.lrc" \
  "纯音乐/青丝 (纯享版)-时光胶囊.flac" \
  "纯音乐/青丝 (纯享版)-时光胶囊.lrc"
do
  [ -f "$f" ] && mv -n "$f" 众音归档/
done

儿歌里混入的流行歌曲

光良 – 童话
光阴的故事-罗大佑
我把故事酿成酒-侯泽润

批量移到 众音归档:

cd /usr/local/music_player/music

for f in \
  "儿歌/光良 – 童话.mp3" \
  "儿歌/光良 – 童话.lrc" \
  "儿歌/光阴的故事-罗大佑.flac" \
  "儿歌/光阴的故事-罗大佑.lrc" \
  "儿歌/我把故事酿成酒-侯泽润.mp3" \
  "儿歌/我把故事酿成酒-侯泽润.lrc"
do
  [ -f "$f" ] && mv -n "$f" 众音归档/
done

八、日常维护命令

重新分类(新增歌曲后):

cd /usr/local/music_player/music
python3 classify_music.py
python3 reorganize_music.py

查看某目录内容

ls -1 DJ/
ls -1 纯音乐/
ls -1 儿歌/

只看歌曲文件,不看 .lrc:

grep -E '\.(mp3|flac|m4a)$' 分类清单/DJ.txt
grep -E '\.(mp3|flac|m4a)$' 分类清单/纯音乐.txt
grep -E '\.(mp3|flac|m4a)$' 分类清单/儿歌.txt

统计歌曲数量(不含歌词):

for d in DJ 儿歌 经典老歌 其他分类 纯音乐 余哥专属 跑步 书珩拾声重组 余哥拾声重组 书珩专属 众音归档; do
    echo -n "$d: "
    find "$d" -maxdepth 1 -type f \( -iname '*.mp3' -o -iname '*.flac' -o -iname '*.m4a' \) | wc -l
done

九、注意事项

  1. 先预览再执行:reorganize_music.py 里的 DRY_RUN = True 先跑一遍,确认移动列表无误后再改成 False。
  2. 不要覆盖文件:移动时使用 mv -n,目标存在同名文件时跳过。
  3. 目录名含空格或特殊字符:命令中要加引号,如 "书珩专属"。
  4. 分类规则可调:classify_music.py 中的 RULES 是按关键词匹配,可按需增删。
  5. 余哥专属名单:分类清单/余哥专属名单.txt 可手动填写完整文件名,一行一个,保留扩展名。
  6. 保留分类清单:分类清单/ 下的 .txt 是分类依据,建议长期保留。
  7. 备份重要数据:大规模 mv 前建议先备份,至少先 DRY_RUN。

十、总结

通过两个 Python 脚本:

  • classify_music.py:扫描根目录,按关键词生成分类清单;
  • reorganize_music.py:按优先级从所有目录查找文件,移动到目标目录;

再配合几条 Shell 命令做统计、检查和微调,就能把上千个混乱的音乐文件自动归档到:

DJ、儿歌、经典老歌、其他分类、纯音乐、
余哥专属、跑步、书珩拾声重组、余哥拾声重组、
书珩专属、众音归档

整个过程保留了扩展名,支持 .mp3、.flac、.m4a、.lrc,并且可以重复运行,适合长期维护个人音乐库。

发布了 13 篇文章 | 共 43.6K 字 | 总访客数: Loading | 总访问量: Loading
Hugo theme Stack designed by Jimmy | 黔ICP备2025049100号-1