update
This commit is contained in:
@@ -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::*;
|
||||
|
||||
Reference in New Issue
Block a user