diff --git a/README.md b/README.md index 8d53f5c..456164c 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,285 @@ -# Anisong-Organizer +# Anisong Organizer -一个简单易用的自动化整理程序:扫描音乐文件夹,自动识别每首歌的出处并分类整理——动漫歌按系列排列,非动漫歌按歌手地区与成立时间排列,歌词与封面同步归类 \ No newline at end of file +一个简单易用的自动化整理程序:扫描音乐文件夹,自动识别每首歌的出处并分类整理——动漫歌按系列排列,非动漫歌按歌手地区与成立时间排列,歌词与封面同步归类。 + +- ✅ 零配置:工具包文件夹复制到任意音乐文件夹即可运行 +- ✅ 零依赖:纯 Python 标准库(联网识别日文曲名时推荐安装可选的 pykakasi;ncm 解密已装 numpy 时自动加速) +- ✅ 先预览后执行:默认只出预览,确认后才执行,绝不静默修改 +- ✅ 幂等:重复运行不会重复处理,也不会破坏已整理好的结构 +- ✅ 只动文件名:不修改音乐文件内容和内置标签 +- ✅ 六分类输出:Anisong / 日韩 / 中国港澳台 / 中国大陆 / 欧美 / 其他(未被识别),没有内容的分类不创建文件夹 +- ✅ 网易云 .ncm 一键解密:解密为 flac/mp3 后进入同一流水线,原 .ncm 保留到「加密原件」夹 +- ✅ 歌词/封面同步:歌词与音乐同编号入「歌词」夹;封面按专辑名入「封面」夹 +- ✅ 浏览器交互界面:`--web` 启动本地网页界面,可视化调整分类与顺序 + +--- + +## 快速上手 + +0. **(可选)便携版无需安装 Python**:工具包已内置官方 Python 嵌入式运行时(`python\` 目录,约 11MB)。双击 `启动网页版.bat` 直接打开浏览器整理界面;命令行用户双击 `运行.bat`。本机已装 Python 时两套入口同样可用。 + + > 📦 **源码版注意**:从 git 仓库克隆的源码**不含**内置运行时(`python\` 目录)。需本机已装 Python 3.8+,或到 python.org 下载 [Windows embeddable package (64-bit)](https://www.python.org/downloads/windows/) 解压为 `python\` 目录,即可恢复免安装便携。 + +1. **放入工具包**:把本文件夹(`Anisong Organizer`)整个复制进你的音乐文件夹,例如: + + ``` + D:\音乐\我的收藏\ + ├── Anisong Organizer\ ← 本工具包 + ├── 未闻花名 - 茅野爱衣.flac + ├── Sexyback - Justin Timberlake,Timbaland.ncm + └── ...(你的音乐文件) + ``` + +2. **双击运行**:双击 `运行.bat`(或执行 `python anime_sorter.py`)。程序自动处理**上级文件夹**里的音乐。 + +3. **看导览**:程序打印完整的整理预览(旧名 → 分类 → 新位置),并生成 `导览.txt`(也是撤销对照表)。确认后输入 `y` 执行。 + +4. **想要图形界面**:执行 `python anime_sorter.py --web`,浏览器里拖拽调整分类、点击应用。 + +5. **以后新增歌曲**:把新歌丢进音乐文件夹,再运行一次即可。新歌编号自动续接,识别结果缓存到 `识别缓存.json` / `artist_map.json`,下次离线也能用。 + +## 整理结果示例 + +``` +D:\音乐\我的收藏\ +├── Anisong\ ← 识别到动漫的歌(按系列首播年份排序) +│ ├── 歌曲\01_疾風の如く.flac +│ ├── 加密原件\02_プランA.ncm +│ ├── 歌词\01_疾風の如く.lrc +│ └── 封面\プランA.jpg +├── 日韩\ ← 非动漫、歌手地区为日韩(按歌手成立年份排序) +│ ├── 歌曲\01_Power.flac +│ ├── 加密原件\01_Power.ncm +│ └── 歌词\01_Power.lrc +├── 中国港澳台\ +│ └── 歌曲\01_海阔天空.mp3 +├── 中国大陆\ +│ └── 歌曲\01_平凡之路.mp3 +├── 欧美\ +│ └── 歌曲\01_Sexyback.flac +├── 其他(未被识别)\ ← 兜底:查不到出处的歌 +│ └── 歌曲\01_未知之歌.mp3 +└── Anisong Organizer\ +``` + +(没有内容的分类不会创建文件夹。) + +## 工作原理 + +``` +音乐文件夹(.flac/.mp3/.ncm/.lrc…) + │ + ├─ ⓪ 解密与自愈 ─────── .ncm 解密为 flac/mp3(幂等);「加密原件」夹中 + │ 缺失解密音频的 .ncm 自动恢复;封面提取待分类后入夹 + │ + ├─ ① 提取曲名与歌手 ── FLAC 读内置标签(TITLE/ARTIST) + │ 其他格式或无标签时回退解析文件名「曲名 - 歌手」 + │ + ├─ ② 曲名规范化 ─────── 简中混入修正(谜→謎 风→風)、全角空格、 + │ 去注释尾巴(feat. XXX / Director's Edit. 等) + │ + ├─ ③ 分类判定 ──────── 1) 本地映射表 anime_map.json(含历史联网缓存) + │ 2) 四动漫源**并行**联网查询,按曲名语言调整优先级: + │ 假名→anison 优先 / 汉字→萌娘优先 / 英文→animethemes 优先 + │ 命中 → Anisong;源连续失败 2 次自动熔断(本次运行停用) + │ 3) MusicBrainz 查歌手地区 → 日韩/中国港澳台/中国大陆/欧美 + │ 4) 都查不到 → 其他(未被识别) + │ + ├─ ④ 排序 ──────────── Anisong: 系列按首播年份(老→新),系列内按 OP/ED 顺序 + │ 日韩/港澳台/大陆/欧美: 按歌手分组(同歌手相邻), + │ 歌手按成立年份(未知置后),组内按曲名 + │ 其他: 按 (歌手, 曲名) + │ + ├─ ⑤ 编号 ──────────── 每分类独立编号(01,02,03…),续接该分类现有最大号; + │ 歌词与加密原件与歌曲永远同号 + │ + ├─ ⑥ 文件名清理 ────── Windows 禁用字符转全角、尾随点转全角、重名加 (2) + │ + └─ ⑦ 预览确认后执行 ── 移动到分类文件夹(导览.txt 含完整绝对路径对照,可回退); + 联网结果写入 识别缓存.json(每次联网运行重新生成) / artist_map.json 缓存 +``` + +## 文件说明(分层结构) + +``` +Anisong Organizer\ +├── 启动网页版.bat / 运行.bat ← 入口层(双击即用) +├── app\ ← 程序层 +├── data\ ← 数据层(用户维护/缓存/导览) +├── python\ ← 运行时层(内置 Python,免安装) +└── README.md +``` + +| 文件 | 作用 | +|---|---| +| `app\anime_sorter.py` | 主程序(命令行入口 + 全部流水线逻辑) | +| `app\ncm_decrypt.py` | 网易云 .ncm 解密模块(纯标准库,numpy 可选加速;可单独 `import` 使用) | +| `app\web_server.py` | `--web` 模式的本地服务(127.0.0.1 随机端口;关闭网页即自动退出) | +| `app\web_ui.html` | Web 界面页面(纯静态无 CDN,全离线可用) | +| `data\anime_map.json` | 本地曲目→动漫系列映射表(**用户维护**:种子数据 + ✎ 手动标注;不再自动追加联网结果) | +| `data\识别缓存.json` | 联网识别结果缓存(**每次联网运行自动重新生成**,不累积历史,可安全删除) | +| `data\artist_map.json` | MusicBrainz 歌手地区/成立时间缓存(自动生成,可手工编辑/预填,离线生效) | +| `data\设置.json` | 数据源开关 + 分类/输出/解密等全部选项 | +| `data\导览.txt` | 每次运行自动生成的整理导览(含完整移动对照,可用于撤销;Web 界面有「📖 导览」按钮直接查看) | +| `运行.bat` | 双击运行入口(命令行界面;自动优先使用内置 `python\` 运行时) | +| `启动网页版.bat` | 双击启动浏览器整理界面(**关闭网页即自动退出进程**;仅出错时窗口停留) | +| `python\` | 内置的官方 Python 嵌入式运行时(便携版核心,免安装 Python) | +| `README.md` | 本说明文档 | + +## 设置.json + +```json +{ + "sources": { + "animethemes": {"enabled": true}, + "anison": {"enabled": true}, + "wikipedia": {"enabled": true}, + "moegirl": {"enabled": true} + }, + "musicbrainz": { + "enabled": true, + "rate_limit": 1.05, + "user_agent": "AnisongOrganizer/1.0 ( contact )", + "max_failures": 3 + }, + "decrypt": {"enabled": true, "save_cover": true}, + "output": "", + "categories": { + "anisong": "Anisong", + "jp_kr": "日韩", + "hk_mo_tw": "中国港澳台", + "cn_mainland": "中国大陆", + "western": "欧美", + "other": "其他(未被识别)" + }, + "folders": {"songs": "歌曲", "originals": "加密原件", "lyrics": "歌词", "covers": "封面"} +} +``` + +- `sources`:各动漫识别源的启停(anison / wikipedia 需要加速器/代理) +- `musicbrainz`:歌手地区分类源(需要加速器/代理;`user_agent` 必填自定义格式,默认 UA 会被官方封 IP;连续失败 `max_failures` 次后本次运行自动禁用,剩余歌曲归「其他」) +- `decrypt`:ncm 解密与封面提取开关 +- `output`:分类输出根目录,**空 = 被处理文件夹本身**;可填其他盘路径(自动跨盘移动) +- `categories` / `folders`:六分类与四子文件夹的名称,均可自定义 + +## 数据源(联网识别) + +| 顺序 | 数据源 | 擅长 | 需要加速器/代理 | +|---|---|---|---| +| 1 | AnimeThemes.moe | 英文/罗马字曲名,自带年份与季度;日文曲名自动转罗马字后再查 | 否 | +| 2 | anison.info | 日文曲名,专门的动漫歌曲数据库(结果行直接含动漫名与 OP/ED 类型) | **是** | +| 3 | 日语维基百科 | 从歌曲条目的「テレビアニメ『X』のOP/ED」句式精确提取动漫 | **是** | +| 4 | 萌娘百科 | 中日文曲名,国内直连 | 否 | +| 5 | MusicBrainz | 歌手地区/成立年份(非动漫曲分类排序用),官方限速 1 次/秒,结果本地缓存 | **是** | + +连不上的源不会卡死程序:运行结束统一汇总状态并提示(「✗ 数据源 anison 连接失败 2 次 —— 该源需要加速器/代理,请确认已开启后重试」)。MusicBrainz 查询失败时歌曲自动归入「其他(未被识别)」。Web 界面顶部有同样的数据源状态面板。 + +**动漫歌曲独特适配**(v4.1): +- **语言感知优先级**:假名标题(日文曲名)以日文曲库 anison 优先、纯汉字以中日文覆盖的萌娘百科优先、纯英文/罗马字以 AnimeThemes 优先——各取所长 +- **并行查询**:启用源同时查询,按优先级取首个命中(耗时 = 最慢的源而非各源之和) +- **源熔断**:某源连续失败 2 次后本次运行自动停用,不再每首歌白等超时(无梯子实测 2 首歌 81.8s → 36s,歌越多收益越大) + +## 命令行参数 + +```bash +python anime_sorter.py # 预览模式(默认),确认后执行 +python anime_sorter.py --apply # 跳过确认直接执行 +python anime_sorter.py --offline # 禁用联网,只查本地映射表/缓存 +python anime_sorter.py --sources=animethemes,moegirl # 本次只用指定源 +python anime_sorter.py D:\某文件夹 # 处理指定文件夹(缺省为工具包上级文件夹) +python anime_sorter.py D:\下载目录 --recursive # 递归整理子目录(网易云下载结构),执行后清理空目录 +python anime_sorter.py --web # 启动浏览器交互界面 +``` + +## Web 界面 + +`python anime_sorter.py --web` 启动本地服务并自动打开浏览器(仅监听 127.0.0.1,数据不出本机): + +- 表单输入:**来源文件夹**(如网易云下载目录)+ **输出目录**(留空=来源文件夹)+ **递归整理子目录**(下载目录勾选,执行后自动清理源目录空壳);默认联网识别(**用户维护的 anime_map.json 始终第一优先**) +- 顶部实时显示**数据源状态**(正常/失败/熔断/需代理) +- 进度栏下方实时罗列**已识别的歌曲**(Anisong 标注所属动漫系列与首播年份,其他类标注乐队组建年份) +- 每首歌的**所属动漫系列以蓝色下拉框直接展示**(与分类下拉同款样式):正常显示当前匹配系列,展开后第一行是「+ 新建系列…」(**点击即出现输入框可直接打字**,选现存系列则立即生效并带出首播年份);标注**应用后自动写入 anime_map.json**。也可改分类(下拉)、调整顺序(↑↓) +- **勾选多行批量选择**后:可整体**拖拽**到其他分类或指定位置(也支持单选拖拽,拖动完成自动取消勾选);或通过批量工具栏一键移动 +- 「应用全部移动」执行(执行前有确认弹窗);**应用后计划回到可编辑状态,可继续调整并再次应用**(反复整理直到满意) +- 工具栏的「**📖 导览**」按钮:弹窗直接展示整理导览(完整移动对照/撤销日志),可复制或下载 `导览.txt` +- 可中途「停止」;执行结果统计展示 + +## 一键整理(网易云下载目录) + +把网易云下载目录(`歌手\专辑\xxx.ncm` 嵌套结构)一键整理到指定路径: + +```bash +python anime_sorter.py D:\网易云下载 --recursive --apply +# 输出目录: 设置.json 的 output(留空=下载目录本身);执行后自动清理空目录 +``` + +或 Web 界面:来源填下载目录、输出填目标路径、勾选「递归整理子目录」→ 扫描 → 应用。 +递归扫描自动跳过分类目录与工具包文件夹;子目录中的 .ncm 原位解密后统一移入输出目录分类。 + +**网易云下载设置全部适配**: + +| 设置项 | 适配方式 | +|---|---| +| 命名「歌曲名」 | .ncm 用内嵌元数据补全歌手;普通文件无歌手信息时归「其他」,可 ✎ 手动标注 | +| 命名「歌手-歌曲名」 | 文件名双向解析,自动识别歌手在前还是曲名在前(优先取能命中映射表的方向) | +| 命名「歌曲名-歌手」 | 默认解析 ✓ | +| 保存「不分文件夹」 | 顶层扫描 ✓ | +| 保存「按歌手」/「按歌手\专辑」 | 递归模式自动包含 ✓ | +| VIP 曲目(VipSongsDownload 子文件夹) | 递归模式自动包含;非递归时检测到会提示勾选递归 | + +## 常见问题与调试 + +### 歌曲被归入「其他(未被识别)」 + +可能原因:本地表没有且动漫源查不到、歌手不在 MusicBrainz 缓存、MusicBrainz 连不上(需加速器)、或文件名/标签里没有歌手信息。处理办法: + +1. 在 Web 界面手动调整分类; +2. 或打开 `anime_map.json`(动漫歌)/ `artist_map.json`(非动漫歌)仿照已有条目手工添加; +3. 重新运行。 + +### 完全没网 / 联网超时 + +- 程序对每个接口都设了超时,网络不可用时自动跳过联网、只查本地表与缓存,不会卡死或崩溃。 +- 想完全离线运行:加 `--offline`(本地映射与缓存仍生效)。 + +### 日文曲名识别率低 + +联网识别日文曲名依赖 `pykakasi` 做罗马字转写:`pip install pykakasi`。未安装时程序仍可运行,只是日文曲名的联网命中率会明显下降。 + +### 重复运行会怎样? + +安全幂等。整理完成的文件都在分类文件夹里,顶层没有文件时运行直接显示「无需处理」;新增文件按分类现有最大号续接编号;「加密原件」夹中缺失解密音频的 .ncm 会自动重新解密恢复。 + +### 改错了想撤销? + +`导览.txt` 里每行都是 `目标完整路径 ← 原始完整路径` 的移动对照,按它手工移回即可(Web 界面点「📖 导览」按钮直接查看/复制/下载)。 + +### 旧版本(v3.1)整理过的文件夹 + +自动迁移:顶层平铺编号的文件与旧「歌词」文件夹里的 .lrc 会重新归类进新结构(编号变为分类内编号,属预期);旧顶层「封面」文件夹无法可靠对应分类,保持原位并在运行时提示。 + +## 已知限制与设计取舍 + +- **联网源覆盖**:animethemes.moe 主要收录 OP/ED 主题曲;纯 OST/插入曲或冷门歌曲可能查不到 → 归「其他」,属预期兜底而非故障。 +- **识别不是 100% 准确**:动漫识别采用「高置信才采用」策略;地区分类依赖 MusicBrainz 数据;预览确认与 Web 手动调整就是最后一道人工保险。 +- **系列归属的合并粒度**:同世界观多季动画默认归为一个系列(如「とあるシリーズ」含魔禁+超炮)。想拆开就在 `anime_map.json` 里手动分成多个系列。 +- **歌手名不写入文件名**:最终格式为 `编号_曲名`,歌手信息保留在文件内置标签中(本程序不修改标签)。 +- **不写标签**:音乐文件本身只改名移动;如需写入 ALBUM=系列名 等标签,可在此基础上扩展。 +- **只扫描文件夹第一层**:不递归处理子文件夹(分类文件夹内部不参与重复整理)。 +- **.ncm 解密后原件保留**:解密是「另存新文件」而非替换,磁盘占用会翻倍;确认无误后可自行删除「加密原件」夹中的 .ncm。 + +## 依赖 + +- **无需安装任何环境**:工具包内置官方 Python 3.12 嵌入式运行时(`python\` 目录);本机已装 Python 3.8+ 时也可直接用(启动脚本自动优先内置运行时)。源码版不含该运行时,获取方式见「快速上手」第 0 步 +- 零第三方强制依赖(纯标准库) +- 可选:pykakasi(提升日文曲名联网识别率)、numpy(ncm 解密加速,无则自动用纯 Python 路径) + +## 测试情况 + +- **ncm 解密**:4 个真实网易云 .ncm 全部解密成功,输出与开源参考实现(ncmdump-py)**sha256 字节级一致**;numpy 与纯标准库两条路径均验证通过;AES 模块通过 NIST 标准测试向量 +- **六分类(v4)**:本地映射/动漫源/MusicBrainz 缓存三类判定路径分别验证;日韩/港澳台/大陆/欧美/其他归属正确;地区分类按歌手成立年份排序正确 +- **结构与幂等(v4)**:分类内编号独立、歌词/加密原件同号、空分类不创建文件夹、旧 v3.1 结构自动迁移、复跑「无需处理」且文件零改动(快照对比) +- **增量与自愈(v4)**:新增文件编号续接;删除解密产物后从「加密原件」自动恢复 +- **Web 界面(v4)**:扫描/计划/改分类/调顺序/应用/停止/参数校验/源状态面板 20 项 API 测试全部通过(自动化) +- **源策略(v4.1)**:熔断机制(连续失败 2 次停用)、并行查询、语言感知优先级;无梯子计时实测 81.8s → 36s;MusicBrainz 失败计数与提示验证通过 +- 兼容性:映射表/设置文件兼容带 BOM 的 UTF-8(Windows 记事本编辑不会导致崩溃) diff --git a/启动网页版.bat b/启动网页版.bat new file mode 100644 index 0000000..d5a5e09 --- /dev/null +++ b/启动网页版.bat @@ -0,0 +1,8 @@ +@echo off +chcp 65001 >nul +cd /d "%~dp0" +if exist "python\python.exe" (set PY=python\python.exe) else (set PY=python) +echo Starting Anisong Organizer web UI... +echo (Close the browser page to exit.) +%PY% app\anime_sorter.py --web +if errorlevel 1 pause diff --git a/命令行运行.bat b/命令行运行.bat new file mode 100644 index 0000000..dd38af5 --- /dev/null +++ b/命令行运行.bat @@ -0,0 +1,6 @@ +@echo off +chcp 65001 >nul +cd /d "%~dp0" +if exist "python\python.exe" (set PY=python\python.exe) else (set PY=python) +%PY% app\anime_sorter.py %* +pause