Files
MediaOrganizer/docs/adr/0007-category-partitions-musicvideos.md
T
Shuery c1ed1795c2
CI / lint + test + build (push) Canceled after 0s
docs: 重写 README、新增项目报告与开发工具配置
- README 现代化重写:徽章区、特性一览、统一 GitHub 提示块格式、修正版本号与 AI 默认值
- 新增 docs/PROJECT_REPORT.md 学术项目报告(含许可证合规分析与死链修复记录)
- 收录 23 份 ADR 架构决策记录与 CONTEXT.md 领域术语表
- 新增开发配置:.editorconfig / .shellcheckrc / .markdownlint-cli2.jsonc
- .gitignore 补充 mo_map/、检查报告、编辑器临时文件
- 新增 config.example.json 配置模板(不含真实密钥)
2026-08-14 22:41:40 +08:00

42 lines
5.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 配置按类目分区、特典类别外置与 Music Videos 类目
用户需求:四个配置文件(special_maps / special_keywords / skip_directories / season_offsets)"不仅需要支持当前的,还需要支持其它所有类目";特典类别判定表完全外置;并实现 Music Videos(音乐视频)类目。三项合为 v9.6。
## 配置按类目分区
四个文件支持**两种形态**:全局单表(旧版,行为不变)与类目分区(`{"tv": {...}, "musicvideo": [...]}`)。分区判定 `is_category_partitioned`:顶层全部键 ∈ `MO_CATEGORY_KEYS`(movie/tv/music/musicvideo/video/audio/default/global)→ 分区形态;混合形态警告并按全局处理(避免类目键被当数据键)。
查询链统一:**类目/流程分区 → default 分区 → 全局表 → 内置默认**。关键决策:
1. **分区键语义因文件而异**:特典映射/词表用类目键(tv/musicvideo);跳过目录用**流程键**(video/music)——目录语义判断发生在类目确定之前(正是用词表辅助判定),无法按条目类目取值;`find_show_*`/`count_main_videos`/`detect_special` 查 video 流程分区。
2. **skip_directories 分区 = 完整语义**:分区形态**不叠加内置默认**——否则 music 流程会命中 video 侧内置词(如 "sps"),分区失去意义;通用词放 default 分区。旧全局形态保持"数组内容优先、空数组回退内置"。
3. **special_keywords 分区 = 叠加语义**:tv 分区词 + 内置默认兜底(词表是积累型,AI 持续学习)。
4. **AI 写回自动适配**:`learn_skip_dirs`/`learn_special_keywords` 检测分区形态,写 `video`/`tv` 分区(jq `.video[...]`/`.tv[...]` 路径),全局形态写顶层;内存表同步更新分区表。
5. **分区引用展开**:special_maps 分区内字符串引用同分区优先,其次全局表(`resolve_special_value_cat`)。
**考虑过的方案**:分区键用前缀(`category.tv` 等,丑且破坏旧格式兼容);分区判定用"至少一个类目键"(混合形态静默错位);skip_directories 保持全局 + 仅扩展词表(无法按流程区分,music 流程误命中 video 词)。
## 特典类别判定表外置
`special_category_tag` 原硬编码 24 项类别(Menu/CM/PV/...)→ 外置 `mo_config/special_categories.json`:**数组保序 = 优先级**(`[{"match": "nced", "tag": "NCED"}]`)。查询链:用户表(按序子串)→ 内置默认表(保留在 `special_category_tag` 作最后兜底)→ "Special"。缺失时交互创建(`SPECIAL_CATEGORIES_TEMPLATE` 常量),自动化跳过(内置兜底)。
**考虑过的方案**:对象形态(JSON 对象无顺序,判定优先级丢失——"mini anime" 需先于 "anime");并入 special_keywords(语义不同:词表判定"是否特典",类别表判定"S00 标签",且词表被 AI 写回,类别表是用户静态维护)。
## Music Videos 类目
识别信号 = musicvideo 判定词(special_keywords 的 `musicvideo` 分区 → default → 内置 `MUSICVIDEO_WORDS` 13 词:mv/music video/live/concert/performance/演唱会/音乐视频/音乐录影带/现场 等)。**不回退 tv 特典词表**("sp" 子串命中 "SPs" 目录 = 稳定误判,正是开发中发现的 flakiness 根因之一)。
1. **目录信号(强信号)**:目录名**精确匹配**(归一化去空格小写整名相等)musicvideo 词 → 整目录音乐视频(即使文件名含季集标记)。精确匹配避免 "Muv-Luv"/"tmp.xxx" 含词误判——开发中实测 mktemp 临时目录名(tmp.XXXXXXXX)约 3% 概率含 cm/sp/pv/mv 两字符词导致测试偶发失败,精确匹配彻底根除。
2. **文件名信号**:方括号标记子串命中 musicvideo 词。双命中歧义(标记同时在特典词表,如 [MV]/[PV])用 "**歌手 - 歌名**" 模式消解:文件名含 ` - ` → 音乐视频(歌手分隔是音乐视频命名典型结构);不含 → 保持特典路径("动画名 [MV]" 剧集 MV 特典不被误判)。两侧词表可配置完全控制。
3. **命名**:`MusicVideos/{artist}/{title}.{ext}`(`NAMING_MUSICVIDEO` 模板,Jellyfin 官方结构)。artist 链:元数据 ALBUMARTIST → ARTIST → 父目录名解析("歌手 - 歌名"取首段;无分隔符整名;源根直属不解析)→ FOLDER_UNKNOWN。
4. **不进 AI**:本地规则无网络请求,识别失败(artist 兜底 Unknown)恒成功,不消耗 AI 轮次;unknown 才交 AI(保持 AI 类型域 movie/tv 不变,零 prompt 改动)。
5. **排除在特典路径外**:musicvideo 判定置于 parse 的年份提取之后、TV/特典规则之前;`[PV]`/`[CM]` 不在 musicvideo 词表 → 特典路径不受影响。
**考虑过的方案**:目录信号子串匹配(flakiness 实证否决);musicvideo 词表回退 tv 特典词表(SPs 误判);双命中一律 musicvideo(剧集 MV 特典误判);Music Videos 走 AI 判断(需扩展 prompt 类型域,且本地规则已覆盖多数文件——CONTEXT 挂账"多数文件规则补齐"正是此意)。
## 配套
- 新文件:`src/lib/config/maps.sh` 扩展(分区加载/查询 + 特典类别表),`src/lib/main.sh`(MUSICVIDEO_WORDS_TEMPLATE/SPECIAL_CATEGORIES_TEMPLATE 常量、分区表全局变量),`src/lib/media/filename.sh`(detect_musicvideo),`src/lib/media/identify.sh`(identify_musicvideo),`src/lib/media/ai_resolve.sh`(fallback musicvideo 分支),`src/lib/pipeline/process.sh`(分发),`src/lib/integrate/ai.sh`(写回分区感知)。
- 测试:`tests/cases/media/musicvideo.sh`(7 例)、`tests/cases/config/categories.sh`(5 例)、`tests/cases/config/partition.sh`(6 例);全量 107 通过。
- 版本 9.6(main.sh SCRIPT_VERSION + bashly.yml)。