SQLite 歌曲入库命名规则
本文用于约束本地媒体文件扫描入库时,如何从文件名中解析歌手、歌名和关键词,并写入 SQLite 索引。
当前文档面向 KTV 点歌场景,重点覆盖 Android 目录扫描入库链路;后续如果 macOS 或桌面端也接入 SQLite,应复用同一套规则。
1. 目标
- 将文件名解析为稳定、可查询的结构化字段。
- 保证合唱歌曲、多关键词歌曲可以被正确索引。
- 对无法完全识别的文件名给出统一降级策略,避免直接漏歌。
2. 标准命名格式
标准文件名格式定义为:
歌手字段-歌曲名-关键词1-关键词2-关键词3....扩展名示例:
周杰伦&费玉清-千里之外-国语-流行音乐.mp4按 - 做一级拆分后,应得到:
- 第 1 段:歌手字段
- 第 2 段:歌曲名
- 第 3 段及以后:关键词字段
对应上面的示例:
- 歌手字段:
周杰伦&费玉清 - 歌曲名:
千里之外 - 关键词:
国语、流行音乐
说明:
- 示例中的
&出现在歌手字段中,表示多歌手合唱,不属于歌曲名的一部分。 - 歌曲名字段整体保留,不再按
&或其他符号二次拆分。
3. 文件名预处理规则
正式解析前,先做以下预处理:
- 去掉文件扩展名,只保留基础文件名。
- 对整串文件名做首尾空白裁剪。
- 按
-拆分为多个片段。 - 对每个片段做
trim(),去掉片段首尾空格。 - 过滤掉关键词区中的空片段。
建议兼容但不改变标准规则的处理:
- 历史文件如果使用了
-、—、–,可以先归一化为-再解析。 - 标准入库命名仍以半角连字符
-为一级分隔符。
3.1 连字符白名单
由于半角连字符 - 同时承担一级分隔符角色,歌手名或歌名中如果合法包含 -,默认规则无法自动区分,必须使用白名单兜底。
白名单数据文件:
docs/sqlite_hyphen_whitelist.yaml
当前建议只维护一类白名单:
artist_names:合法包含-的歌手名
当前提供的初始化数据只作为第一版种子,后续发现新样本时继续追加,不要在代码里写死。
推荐解析顺序:
- 文件名归一化后,先按
-拆分片段。 - 歌手字段优先尝试白名单精确匹配,建议采用“最长命中优先”。
- 歌手字段确定后,从右到左消费
language与tags关键词片段。 - 左侧歌手片段与右侧关键词片段之间的中间剩余片段,重新拼接为歌曲名。
- 歌名统一由“左侧歌手片段与右侧关键词片段消费完成后的中间剩余片段”重组生成。
- 如果歌手白名单没有命中,再回退到默认规则。
3.2 连字符白名单匹配流程
为避免不同实现对“最长命中优先”的理解不一致,白名单匹配流程固定如下。
假设文件名去掉扩展名并归一化后得到:
片段1-片段2-片段3-片段4-...则解析时必须按以下顺序执行:
- 先尝试匹配歌手白名单。
- 歌手白名单匹配完成后,消费掉对应片段。
- 再从剩余片段的右侧开始识别并消费
language与tags。 - 左右两侧消费完成后,中间剩余片段重新拼接为歌曲名。
- 左右两侧消费完成后,中间剩余片段直接重组为歌名。
3.2.1 歌手白名单匹配规则
以全部片段的起始位置为基准,尝试从长到短拼接:
片段1-片段2-片段3片段1-片段2片段1
如果某个拼接结果命中 artist_names,则立即采用该结果作为歌手字段,并停止歌手白名单匹配。
这就是“最长命中优先”的具体含义:
- 优先匹配能覆盖更多片段的候选
- 一旦命中更长候选,不再继续尝试更短候选
如果歌手白名单完全未命中,则回退到默认规则:
- 第 1 段作为歌手字段
3.2.2 歌名生成规则
在当前规则下,不再维护歌名白名单。
歌名的确定方式固定为:
- 先完成歌手白名单匹配。
- 再完成右侧
language/tags片段消费。 - 将左右两侧消费后的中间剩余片段按
-重新拼接为歌名。
也就是说,歌名中的合法连字符 - 会自然保留在中间剩余片段中,不再需要额外的歌名白名单参与切分。
3.2.3 关键词消费规则
关键词消费改为“右侧优先”:
- 所有已被歌手字段消费的片段,不再参与后续解析
- 只允许从当前剩余片段的最右侧开始识别并消费
language/tags - 一旦某个右侧片段既未命中
language、也未命中tags,则停止继续向左消费 - 左右两侧消费完成后,中间剩余片段全部归入歌曲名
也就是说,关键词区不是固定的“原始第 3 段以后”,而是“歌手字段从左消费后、再由右侧连续命中的 language / tags 片段组成的尾部区间”。
3.2.4 示例 1
文件名:
A-Lin-给我一个理由忘记-国语-流行拆分片段:
ALin给我一个理由忘记国语流行
歌手匹配时:
- 先尝试
A-Lin-给我一个理由忘记 - 再尝试
A-Lin - 命中
artist_names
因此:
- 歌手消费片段 1-2
- 从右侧识别出关键词
国语、流行 - 中间剩余片段重组后,歌曲名为
给我一个理由忘记
3.2.5 示例 2
文件名:
A-Lin-Love-Love-Love-国语-流行假设:
A-Lin在artist_names
拆分片段:
ALinLoveLoveLove国语流行
解析过程:
- 歌手白名单命中
A-Lin,消费片段 1-2 - 从右侧消费关键词,识别出
国语、流行 - 中间剩余片段为
Love、Love、Love - 中间剩余片段按
-重组为Love-Love-Love
最终结果:
- 歌手:
A-Lin - 歌曲名:
Love-Love-Love - 关键词:
国语、流行
3.2.6 冲突处理规则
如果同一位置存在多个白名单候选:
- 优先选择消费片段更多的候选
- 如果消费片段数相同,则优先选择白名单文件中靠前的条目
如果歌手白名单命中后,导致剩余片段不足以组成有效歌曲名:
- 该次白名单命中视为无效
- 回退到更短候选继续尝试
- 如果所有候选都失败,则回退到默认规则
3.2.7 默认规则的边界
默认规则只在歌手白名单完全未命中时生效:
- 歌手白名单未命中时,歌手 = 原始第 1 段
- 歌名 = 左侧歌手片段与右侧关键词片段消费完成后的中间剩余片段,按
-重新拼接
不能在歌手白名单已经命中的情况下,再把原始第 2 段固定当作歌名;也不能在右侧关键词已经可识别时,提前把这些片段错误吞进歌名。
示例:
A-Lin-给我一个理由忘记-国语-流行如果 A-Lin 在 artist_names 中,则应解析为:
- 歌手:
A-Lin - 歌曲名:
给我一个理由忘记 - 关键词:
国语、流行
否则会被错误拆成:
- 歌手:
A - 歌曲名:
Lin
因此,白名单是当前规则下的必要前置数据,而不是可选优化。
4. 字段解析规则
在引入连字符白名单和关键词识别后,字段定位不再是“固定第 1 段 / 第 2 段 / 第 3 段以后”的简单规则,而是按“左侧消费歌手、右侧消费关键词、中间归并歌名”的方式执行。
固定解析顺序如下:
- 去掉扩展名并完成文件名归一化。
- 按
-拆分为片段。 - 从左到右优先匹配歌手白名单,采用“最长命中优先”。
- 歌手确定后,从右到左处理关键词片段。
- 关键词匹配前,先对尾部片段执行“尾部噪声后缀清理”。
- 从右到左逐个处理尾部片段;对每个片段先尝试识别
language,未命中再尝试识别tags,只消费尾部连续命中的片段。 - 左侧歌手已消费片段与右侧关键词已消费片段之间的所有剩余片段,重新用
-拼接为歌曲名。
只有在未启用白名单和关键词识别的最简实现里,才可以近似理解为“第 1 段是歌手、第 2 段是歌名、第 3 段以后是关键词”。正式实现必须以“消费片段后的剩余区间”作为字段边界。
4.1 歌手字段
歌手字段始终从片段左侧开始确定。
如果歌手白名单命中,则歌手字段可能消费多个连续片段;如果歌手白名单未命中,则回退为原始第 1 段。
示例:
周杰伦&费玉清歌手字段的处理规则:
- 如果包含
&,表示多歌手合唱。 - 按
&拆分歌手列表。 - 每个歌手名再次做
trim()。 - 过滤空字符串后,保留原始顺序。
示例结果:
artistDisplayName = "周杰伦&费玉清"artistNames = ["周杰伦", "费玉清"]artistCount = 2isChorus = true
补充约束:
- 当前阶段只把
&视为歌手字段中的合唱分隔符。 - 如果歌曲名中本身包含
&,不做歌手拆分,只按歌曲名原文保留。 - 如果歌手字段为空,则视为解析失败,进入异常降级流程。
4.2 歌曲名字段
歌曲名字段不是固定的“第 2 段”,而是“左侧歌手片段消费完成后、右侧关键词片段消费完成后,中间剩余的全部片段”。
示例结果:
title = "千里之外"
处理规则:
- 将中间剩余片段按原顺序保留。
- 用
-将中间剩余片段重新拼接为歌曲名。 - 对最终歌曲名做首尾空白裁剪。
- 不再按
&、/、+等符号继续拆分。
如果歌曲名字段为空,则视为解析失败,进入异常降级流程。
示例:
A-Lin-Love-Love-国语-流行_副本(1).mp4解析过程:
- 歌手白名单命中
A-Lin - 右侧关键词识别得到
国语、流行 - 中间剩余片段为
Love、Love
最终歌曲名应为:
Love-Love
4.3 关键词字段
关键词字段也不是固定的“第 3 段及以后”,而是“在歌手片段已经从左侧消费完成后,从右侧开始尝试识别并消费的片段”。
示例:
国语流行音乐
处理规则:
- 只允许从当前剩余片段的最右侧开始识别。
- 识别前先执行“尾部噪声后缀清理”。
- 命中的关键词按原始出现顺序写入结构化字段。
- 去除空字符串。
- 对完全重复的关键词可去重。
- 逐个与关键词词典匹配。
5. 关键词匹配规则
关键词不直接当作自由文本使用,而是优先映射到结构化字段。
对文件名按 - 拆分后,关键词识别的前提是:
- 左侧歌手字段已经先完成消费
- 语言和标签只允许从当前剩余片段的最右侧开始识别
- 只有右侧连续命中的关键词片段才会被消费
也就是说,语言和标签等信息只能从“歌手已消费完成后,剩余片段的右侧尾部”里识别,不能扫描整个中间区域,更不能反向吞掉真实歌名。
为兼容当前命名,本文继续使用字段名 language,但其语义调整为“多值语言列表”;如果后续新建表结构,也可以命名为 languages。
语种 language 为多值字段;歌曲类型、业务标签、版本标签统一归入 tags 多值字段。
5.0 关键词标准化规则
关键词在进入词典匹配前,必须先做统一标准化。
标准化的目的只有一个:
- 提高词典命中率
标准化不会改变原始入库值的保存方式:
- 标准化结果只用于匹配过程和中间计算,不直接替换原文
- 当前方案不要求将原始关键词片段正式落库
推荐标准化顺序如下:
- 首尾空白裁剪:对关键词执行
trim() - 空白压缩:将连续空白折叠为单个空格
- 全角半角归一化:如
DJ->DJ,Live->Live - 大小写归一化:英文统一转小写后参与匹配
- 繁简体归一化:如
國語->国语,閩南話->闽南话 - 分隔符归一化:将无语义差异的符号归一,如
—、–->- - 标点裁剪:去掉关键词首尾无意义标点,但不破坏关键词正文
推荐示例:
| 原始关键词 | 标准化后 |
|---|---|
國語 | 国语 |
閩南話 | 闽南话 |
DJ | dj |
Live | live |
流行音樂 | 流行音乐 |
补充约束:
- 标准化必须在语言匹配和标签匹配前统一执行
- 词典匹配应基于标准化后的值,而不是原始值
- 词典命中后写入的是“标准分类名”或“标准标签名”,不是标准化后的中间字符串
- 如果标准化后为空字符串,则该关键词视为无效关键词,不参与匹配
当前阶段建议最低支持以下三类归一化能力:
- 繁简体归一
- 全角半角归一
- 英文大小写归一
5.0.1 尾部噪声后缀清理
在关键词标准化之前,还需要先处理“文件复制、导出、系统追加序号”这类无业务语义的尾部噪声。
该规则仅允许作用在“当前最右侧待识别片段”上,不能扫描或修改歌名正文。
推荐清理目标包括但不限于:
_副本_副本(n)副本副本(n)(1)、(2)这类纯序号后缀_copycopy
处理规则:
- 仅对右侧待匹配关键词片段执行。
- 从片段尾部开始移除无意义后缀。
- 清理完成后再执行关键词标准化与词典匹配。
- 如果清理后片段为空,则该片段视为无效关键词。
- 不得删除正文中的真实语义内容,例如
Live、演唱会版、DJ版、伴奏版。
示例:
| 原始片段 | 清理后 |
|---|---|
流行_副本(1) | 流行 |
国语(2) | 国语 |
Live_副本 | Live |
5.1 语言匹配规则
语言字段 language 的识别优先级最高,但该优先级作用于“单个尾部片段的分类顺序”,不是要求必须先把所有右侧片段都尝试成语言。
language 的落库值应为多值列表:
- 单个语种示例:
["国语"] - 多个语种示例:
["国语", "粤语"] - 未命中任何语种时:
["其它"]
如果同一首歌识别出多个语言关键词:
- 允许全部保留
- 去重
- 按文件名中的原始出现顺序保存,而不是按扫描顺序倒序保存
推荐标准分类名如下:
国语粤语闽南语英语日语韩语客语其它
推荐关键词映射表如下:
| 命中关键词 | 标准分类名 |
|---|---|
国语、普通话、华语 | 国语 |
粤语、广东话、白话 | 粤语 |
闽南语、闽南话、台语、福建话 | 闽南语 |
英语、英文 | 英语 |
日语、日文 | 日语 |
韩语、韩文 | 韩语 |
客语、客家话 | 客语 |
处理规则:
- 从当前剩余片段的最右侧开始,逐个向左处理尾部片段。
- 对每个待匹配片段先执行“尾部噪声后缀清理”。
- 再执行“关键词标准化规则”。
- 先按语言词典做“完整词匹配”。
- 如果命中语言,则将对应标准分类名加入
language列表,并消费该片段。 - 如果当前片段未命中语言,不代表停止;还应继续尝试将该片段按
tags分类。 - 如果多个连续尾部片段同时命中不同语言,则全部写入
language列表,去重后按原始出现顺序保存。 - 如果所有尾部连续关键词片段都没有命中语言,则
language = ["其它"]。
示例:
周杰伦-青花瓷-国语-流行音乐歌手字段消费后,右侧尾部待识别片段为:
国语流行音乐
其中:
国语命中语言词典,写入language = ["国语"]流行音乐继续参与后续的标签匹配
再例如:
陈小云-爱情恰恰-闽南话-经典歌手字段消费后,右侧尾部待识别片段为:
闽南话经典
其中:
闽南话归一到language = ["闽南语"]
5.2 标签匹配规则
在右侧尾部片段分类过程中,tags 识别与语言识别共享同一轮从右到左的扫描;只是对每个片段都必须先尝试语言,再尝试 tags。
tags 是多值字段,用于统一承载:
- 歌曲类型:如
流行、经典、摇滚 - 业务标签:如
对唱、合唱、现场版 - 版本标签:如
Live、MV、伴奏版
和语种规则一致,tags 也只从“歌手已消费完成后,剩余片段的右侧尾部”中识别。
推荐标准标签名如下:
流行经典摇滚民谣舞曲DJ情歌儿歌戏曲对唱合唱现场版Live演唱会MV伴奏版原版重制版
推荐关键词映射表如下:
| 命中关键词 | 标准标签名 |
|---|---|
流行、流行音乐、流行歌曲 | 流行 |
经典、经典老歌、怀旧 | 经典 |
摇滚、摇滚乐 | 摇滚 |
民谣、校园民谣 | 民谣 |
舞曲、劲爆、嗨歌 | 舞曲 |
DJ、电音 | DJ |
情歌、抒情 | 情歌 |
儿歌、童谣 | 儿歌 |
戏曲、黄梅戏、京剧、越剧 | 戏曲 |
对唱 | 对唱 |
合唱 | 合唱 |
现场版 | 现场版 |
Live | Live |
演唱会 | 演唱会 |
MV | MV |
伴奏版 | 伴奏版 |
原版 | 原版 |
重制版 | 重制版 |
处理规则:
- 只处理当前剩余片段右侧尾部的连续候选片段。
- 对每个候选片段先执行“尾部噪声后缀清理”,再执行“关键词标准化规则”。
- 对单个片段先尝试匹配语言;只有未命中语言时,才继续尝试匹配
tags。 tags使用“完整词匹配”优先。- 每命中一个
tags候选,就消费一个右侧片段,并继续向左尝试。 - 一旦某个右侧片段既未命中语言、也未命中
tags,就停止继续向左扫描,剩余中间片段保留给歌名。 - 所有命中的标准标签都写入
tags。 tags允许多值,同时需要去重并保留首次命中顺序。- 如果没有任何标签关键词命中,则
tags = []。
推荐伪流程如下:
for 右侧尾部片段 from right to left:
清理尾部噪声
标准化
if 命中 language:
将 language 追加到语言列表
消费该片段
continue
if 命中 tags:
写入 tags
消费该片段
continue
break示例:
张学友-吻别-国语-流行音乐歌手字段消费后,右侧尾部待识别片段为:
国语流行音乐
其中:
国语命中语言词典,写入language = ["国语"]流行音乐命中标签词典,写入tags = ["流行"]
再例如:
凤凰传奇-自由飞翔-国语-流行-对唱歌手字段消费后,右侧尾部待识别片段为:
国语流行对唱
其中:
国语命中语言词典流行命中标签词典对唱命中标签词典
最终结果:
language = ["国语"]tags = ["流行", "对唱"]
关于 对唱/合唱 的特殊约束:
isChorus只根据歌手字段是否包含&判定对唱、合唱只写入tags- 如果出现
对唱/合唱标签,但歌手字段没有&,允许保留标签,同时记录解析告警
6. 推荐落库结果
以文件名:
周杰伦&费玉清-千里之外-国语-流行音乐.mp4为例,推荐的解析结果如下:
| 字段 | 值 |
|---|---|
fileName | 周杰伦&费玉清-千里之外-国语-流行音乐.mp4 |
artistDisplayName | 周杰伦&费玉清 |
artistNames | ["周杰伦", "费玉清"] |
artistCount | 2 |
isChorus | true |
title | 千里之外 |
language | ["国语"] |
tags | ["流行"] |
如果文件名为:
陈雷-欢喜就好-闽南话-经典.mp4则推荐结果为:
| 字段 | 值 |
|---|---|
artistDisplayName | 陈雷 |
artistNames | ["陈雷"] |
title | 欢喜就好 |
language | ["闽南语"] |
tags | ["经典"] |
如果当前 SQLite 表结构暂时还没有拆分歌手关系表,至少应保证下面这些主表字段可被稳定保存:
titleartistsearch_indexfile_namemedia_path
如果后续要支持歌手维度检索、合唱检索、歌手聚合,歌手信息也推荐使用关联表,而不是只依赖主表中的展示字段。
建议补充以下字段或关联表:
artist_display_nameartist_countis_chorussong_artists关联表
6.1 关联表推荐落库方式
由于 language 和 tags 都是多值字段,且后续需要支持“按语言获取列表”“按语言过滤后继续按歌名搜索”等查询,SQLite 落库方案在本文中明确固定为关联表实现,不再推荐也不再讨论 JSON 字符串、逗号分隔字符串或其他非结构化单列文本方案。
推荐使用关联表:
song_artists(song_id, artist_name)song_languages(song_id, language)song_tags(song_id, tag)
推荐原因:
- 可以高效按单个歌手筛选歌曲
- 可以支持合唱歌曲按任一歌手命中
- 可以支持歌手聚合、歌手维度统计
- 可以高效按语言筛选歌曲
- 可以在语言过滤后继续按歌名排序或前缀搜索
- 可以自然支持一首歌多个语言、多个标签
- 可以避免
LIKE '%国语%'这类无法稳定命中索引的查询方式
推荐最小表结构如下:
CREATE TABLE songs (
id INTEGER PRIMARY KEY,
title TEXT NOT NULL,
title_norm TEXT NOT NULL,
artist_display_name TEXT NOT NULL,
file_name TEXT NOT NULL,
media_path TEXT NOT NULL UNIQUE
);
CREATE TABLE song_artists (
song_id INTEGER NOT NULL,
artist_name TEXT NOT NULL,
PRIMARY KEY (song_id, artist_name)
);
CREATE TABLE song_languages (
song_id INTEGER NOT NULL,
language TEXT NOT NULL,
PRIMARY KEY (song_id, language)
);
CREATE TABLE song_tags (
song_id INTEGER NOT NULL,
tag TEXT NOT NULL,
PRIMARY KEY (song_id, tag)
);
CREATE INDEX idx_song_languages_language_song
ON song_languages(language, song_id);
CREATE INDEX idx_song_tags_tag_song
ON song_tags(tag, song_id);
CREATE INDEX idx_song_artists_artist_song
ON song_artists(artist_name, song_id);
CREATE INDEX idx_songs_title_norm
ON songs(title_norm);补充说明:
artist_display_name保留原始展示值,例如周杰伦&费玉清song_artists.artist_name保存拆分后的单个歌手名,例如周杰伦、费玉清PRIMARY KEY (song_id, artist_name)、PRIMARY KEY (song_id, language)和PRIMARY KEY (song_id, tag)可直接防止重复值- 如果未来需要级联删除,可在正式建表时补上外键约束
推荐查询示例:
按单个歌手获取歌曲列表:
SELECT s.*
FROM song_artists sa
JOIN songs s ON s.id = sa.song_id
WHERE sa.artist_name = ?
ORDER BY s.title_norm ASC;按歌手过滤后继续按歌名搜索:
SELECT s.*
FROM song_artists sa
JOIN songs s ON s.id = sa.song_id
WHERE sa.artist_name = ?
AND s.title_norm LIKE ? || '%'
ORDER BY s.title_norm ASC;按单个语言获取歌曲列表:
SELECT s.*
FROM song_languages sl
JOIN songs s ON s.id = sl.song_id
WHERE sl.language = ?
ORDER BY s.title_norm ASC;按语言过滤后继续按歌名搜索:
SELECT s.*
FROM song_languages sl
JOIN songs s ON s.id = sl.song_id
WHERE sl.language = ?
AND s.title_norm LIKE ? || '%'
ORDER BY s.title_norm ASC;查询同时属于多个语言的歌曲:
SELECT s.*
FROM songs s
JOIN song_languages sl ON sl.song_id = s.id
WHERE sl.language IN (?, ?)
GROUP BY s.id
HAVING COUNT(DISTINCT sl.language) = 2
ORDER BY s.title_norm ASC;因此,本文档后续提到的歌手、语言、标签字段,统一理解为:
artist逻辑层:展示值 + 单歌手列表language逻辑层:多值列表tags逻辑层:多值列表- SQLite 落库层:关联表
- 查询层:通过
JOIN而不是字符串模糊匹配实现
补充约束:
songs主表保留artist_display_name作为展示字段songs主表不要求额外保留language、tags冗余列- 单歌手检索的 SQLite 唯一权威来源是
song_artists language的 SQLite 唯一权威来源是song_languagestags的 SQLite 唯一权威来源是song_tags
7. 搜索索引建议
搜索索引建议由以下内容拼接组成:
- 歌曲名
- 歌手展示名
- 每个独立歌手名
- 原始文件名
- 扩展名
- 语种
- 已识别关键词
- 标题拼音/首字母
- 歌手拼音/首字母
这样可以覆盖以下检索场景:
- 搜歌曲名
- 搜单个歌手名
- 搜合唱歌手中的任一歌手
- 搜语种
- 搜标签关键词
- 搜拼音首字母
8. 异常与降级规则
为了避免因为文件名不规范导致歌曲完全丢失,入库时需要定义统一的降级策略。
8.1 无法得到有效歌名
如果按 - 拆分并完成歌手、关键词消费后,无法得到有效歌名:
- 不满足标准格式
- 仍允许入库
title = 去除文件后缀后的完整文件名artist = "未识别歌手"parseStatus = invalid或等价状态字段
8.2 歌手或歌曲名为空
如果歌手字段为空,或者中间剩余片段在重新拼接后为空:
- 视为非标准命名
- 走与上面相同的降级逻辑
8.3 关键词未命中词典
如果关键词没有命中任何词典:
- 该关键词不参与
language或tags落库 - 该关键词可拼入
search_index - 如需排查解析行为,建议仅在开发期日志中输出,不作为正式 SQLite 字段保存
8.4 多个语言关键词同时命中
例如:
歌手A-歌曲B-国语-粤语建议处理方式:
language支持多值保存- 所有命中的语言关键词都写入
language - 去重后按文件名中的原始出现顺序保存
例如:
language = ["国语", "粤语"]
8.5 多个标签关键词同时命中
例如:
歌手A-歌曲B-国语-流行-经典建议处理方式:
language = ["国语"]tags = ["流行", "经典"]
多个标签可以并行保存,不需要再压缩成单值字段。
8.5.1 歌曲类型标签与业务标签同时命中
例如:
歌手A-歌曲B-国语-流行-对唱建议处理方式:
language = ["国语"]tags = ["流行", "对唱"]
两类标签并行保存,互不覆盖。
8.6 多歌手拆分后为空
例如出现:
&&-千里之外-国语则说明歌手字段无有效值,应按异常规则处理。
8.7 解析规则版本与重建
由于语种词典、标签词典和白名单都可能持续演进,建议为每条入库记录保存解析规则版本,例如 parseRuleVersion。
规则变更场景包括但不限于:
- 新增或修改语种关键词映射
- 新增或修改标签关键词映射
- 新增或修改连字符白名单
发生上述变更后,建议触发对应目录的全量重扫或全量重建索引,避免新旧规则混用。
9. 推荐实现顺序
建议按下面顺序落地代码:
- 统一文件名预处理与一级分段。
- 先稳定解析
artist/title。 - 增加
&合唱歌手拆分。 - 建立连字符白名单,先兜底歌手名和歌名中的合法
-。 - 建立关键词词典,将关键词映射到
language、tags等字段。 - 未识别关键词不落库,仅在开发期日志输出,便于排查解析行为。
- 最后再扩展歌手关联表或更细的分类字段。
10. 与当前实现的差异
当前 Android 示例中的 SQLite 入库实现位于:
android/app/src/main/kotlin/com/ktv/player/ktv2_example/MainActivity.kt
当前实现的特点是:
- 只从文件名中解析
artist和title - 只按第一个分隔符拆分
- 尚未处理
&多歌手 - 尚未处理歌手名或歌名中合法包含
-的白名单场景 - 尚未支持“左侧消费歌手、右侧消费关键词、中间重组歌名”的正式规则
- 尚未处理关键词尾部噪声后缀,例如
_副本(1) language统一写死为其它,尚未根据右侧尾部关键词识别- 尚未根据右侧尾部关键词识别
tags - 尚未保存结构化关键词解析结果到关联表
因此,本文档描述的是下一步应实现的目标规则,而不是当前代码已经完整具备的行为。