This commit is contained in:
lychang
2026-07-20 04:46:02 +08:00
parent 00af6e8528
commit a6bfe7eda2
15 changed files with 927 additions and 864 deletions

View File

@@ -6,6 +6,7 @@ use std::collections::HashSet;
use lazy_static::lazy_static;
use jieba_rs::Jieba;
use crate::engine::tag_dict;
use crate::engine::tag_extractor;
// 全局 jieba 分词器
lazy_static! {
@@ -695,6 +696,92 @@ pub fn normalize_tag(tag: &str) -> String {
tag.trim().to_lowercase()
}
// ═══════════════════════════════════════════════════════
// 统一标题预处理(用于 parse_album_title 入口)
// ═══════════════════════════════════════════════════════
/// 统一标题预处理:移除照片数后缀、[工作室] 前缀、称呼前缀、编号后缀
pub fn preprocess_title(title: &str) -> String {
let t = title.trim();
if t.is_empty() { return String::new(); }
let mut t = RE_PHOTOS_SUFFIX.replace(t, "").trim().to_string();
t = RE_BRACKET_PREFIX.replace(&t, "").trim().to_string();
for prefix in tag_dict::get_tag_dict().get_person_prefixes() {
if t.starts_with(prefix) {
t = t[prefix.len()..].trim().to_string();
break;
}
}
t = RE_NUMBER_SUFFIX.replace(&t, "").trim().to_string();
t
}
/// 统一相册标题解析入口
///
/// 一次 jieba 分词 → 同时提取 person + tags。
/// tags 通过 tag_extractor 的三步流程(A 词典 / B jieba 名词 / C 正则)提取,
/// 使用同一份 jieba result。
///
/// 返回 ParseResult 可直接用于 Phase 1b 批量写库。
pub fn parse_album_title(album_id: &str, title: &str) -> crate::models::parse::ParseResult {
let mut result = crate::models::parse::ParseResult::new(album_id.to_string());
let title = title.trim();
if title.is_empty() { return result; }
// 1. 预处理
let cleaned = preprocess_title(title);
// 2. cosplay 格式优先
if let Some(caps) = RE_COSPLAY_BY.captures(&cleaned) {
result.person_name = Some(clean_name(caps[3].trim()));
let name = result.person_name.as_deref().unwrap_or("");
// 有限标签提取(仅词典 + 正则,不调 jieba 避免重复)
let tags = tag_extractor::extract_dict_tags(title, &[name.to_lowercase()], &mut std::collections::HashSet::new());
result.tags = tags;
return result;
}
if let Some(caps) = RE_COSPLAY.captures(&cleaned) {
result.person_name = Some(clean_name(caps[1].trim()));
let name = result.person_name.as_deref().unwrap_or("");
let tags = tag_extractor::extract_dict_tags(title, &[name.to_lowercase()], &mut std::collections::HashSet::new());
result.tags = tags;
return result;
}
// 3. 一次 jieba 分词(核心!全流程共用)
let jieba_tags = JIEBA.tag(&cleaned, true);
// 4. 提取人名
let person_raw = extract_person_from_tags(&jieba_tags);
if let Some(name) = &person_raw {
// include 扩展
let expanded = tag_dict::get_tag_dict()
.find_longest_included_match(name)
.filter(|longer| longer.len() > name.len());
let effective_name = expanded.as_deref().unwrap_or(name.as_str());
let (primary, aliases) = split_person_name_with_aliases(effective_name);
let final_name = if primary.is_empty() { effective_name.to_string() } else { primary };
if final_name.chars().count() > 2 {
result.person_name = Some(final_name);
// person_primary 用于别名处理,暂存但不暴露
}
}
// 5. 提取标签(使用同一份 jieba result)
let person_exclude = result.person_name.as_deref();
let raw_tags = tag_extractor::extract_tags_from_title(title, person_exclude, &jieba_tags);
let unique = tag_extractor::unique_tags(raw_tags);
result.tags = unique;
result
}
#[cfg(test)]
mod tests {
use super::*;