fix: 完善跨季匹配与平台过滤逻辑,修复备用匹配状态及集标题解析 (#392)
* fix: 跨季扩展阻塞与备用匹配 isMatched 状态修复 checkEpisodeSatisfied 指定平台无匹配数据时返回 false: 指定目标平台(如 qiyi)但搜索结果均不匹配该平台时,不再误判为满足。 新增 anyPlatformHadData 标记,无匹配平台时返回 false 以触发跨季扩展。 备用匹配成功后设置 isMatched: fallbackMatchAniAndEp 成功匹配剧集后 isMatched 未正确置为 true, 导致客户端收到匹配结果但标记为未匹配。 * fix: checkEpisodeSatisfied 优选平台无匹配时回退到不区分平台重查而非直接返回false 当 PLATFORM_ORDER 指定的平台与搜索结果源不匹配时(例如 PLATFORM_ORDER=qiyi 但搜索结果来自 renren), anyPlatformHadData 为 false,原先直接返回 false 会错误触发跨季扩展(S5~S10)和 spillover 匹配, 造成大量无谓 API 请求。 修复:anyPlatformHadData 为 false 且有 targetPlatform 时,以 targetPlatform=null 递归调用自身, 回退到 _any_ 模式重新查验全部可用数据,再依据集数是否满足决定是否触发扩展。 这同时正确覆盖两种场景: - 当前季集数不足(如 S1E31 仅 11 集)→ 正确触发扩展 - 当前季集数充足但平台不匹配(如 S4E3 在 renren 有 24 集且含 E3)→ 正确抑制扩展 * fix: matchAniAndEp与findCrossSeasonEpisodeMap中平台不匹配源时回退到无平台过滤 当PLATFORM_ORDER与搜索结果源不匹配时(例如PLATFORM_ORDER=renren但搜到iqiyi结果): 1. matchAniAndEp: findEpisodeByNumber因平台过滤丢弃所有集数返回null后, 新增回退:检查候选源是否匹配指定平台,不匹配则以platform=null重试。 这避免了bestRes为空而错误触发findCrossSeasonEpisodeMap spillover。 2. findCrossSeasonEpisodeMap: 平台不匹配时以currentSeason++ + continue 替代break,允许跨季映射在后续匹配季中继续查找而非直接退出。 保护措施:isPreferredAnime的原有回退逻辑保留作为最后一层兜底。 * fix: extractEpisodeNumberFromTitle 支持第X话和第X回格式 此前仅识别 /第(\d+)集/,导致 dandan 等源使用第31话格式的集标题 无法被提取集号,进而 findEpisodeByNumber 对所有分季 dandan 条目返回 null。 跨季扩展虽然正确拉取了 S3 dandan 条目的 E31(第31话 决裂),但 extractEpisodeNumberFromTitle 无法提取数字 31,导致匹配被跳过, 最终落在能识别第31集的 bahamut 不分季条目上。 修复将正则扩展为 /第(\d+)[集话回]/,同时覆盖: - 集:标准中文格式(如第31集) - 话:日式动画格式(如第31话) - 回:旧式分回格式(如第12回) 不匹配 期/章/卷,避免错配综艺/漫画/OVA分卷格式。 * fix: titleMatches 对无季号标题放宽季数守卫,避免不分季长剧被全部拦截 titleMatches 在 querySeason>1 时原先对无季号标题作 (null||1)!=querySeason 拦截,导致 iqiyi 搜索航海王 S2E62 时全部 16 个结果因标题中无季号被过滤, searchData 为空,匹配链路崩溃。 修复:仅当源标题明确含季号且与目标季不一致时才拦截,无季号的不分季长剧 交由上游源处理器(如 iqiyi 季数预过滤)决定是否保留。 影响分析: - 不分季长剧(querySeason>1):修复前被拦截 → 修复后通过 → 正确 - 分季剧S1标题(querySeason=2):修复前拦截 → 修复后仍拦截 → 不变 - 分季剧S2标题(querySeason=2):修复前通过 → 修复后仍通过 → 不变 - 分季剧S3标题(querySeason=2):修复前拦截 → 修复后仍拦截 → 不变 - querySeason=1或null:不受此分支影响,原有行为完全保持
This commit is contained in:
1 parent
f4d6e5781b
commit
d4d4c9fffa
2 files changed
+28
-7
No files matched your search
@@ -252,6 +252,7 @@ function checkEpisodeSatisfied(animesList, querySeason, queryEpisode, requestAni
|
||||
}
|
||||
|
||||
let allSatisfied = true;
|
||||
let anyPlatformHadData = false;
|
||||
|
||||
for (const tPlat of targetPlatforms) {
|
||||
let isEpisodeSatisfied = false;
|
||||
@@ -296,8 +297,9 @@ function checkEpisodeSatisfied(animesList, querySeason, queryEpisode, requestAni
|
||||
}
|
||||
|
||||
if (!platformHasData) {
|
||||
continue;
|
||||
continue;
|
||||
}
|
||||
anyPlatformHadData = true;
|
||||
|
||||
if (!isEpisodeSatisfied) {
|
||||
let totalValidEpisodes = 0;
|
||||
@@ -311,6 +313,11 @@ function checkEpisodeSatisfied(animesList, querySeason, queryEpisode, requestAni
|
||||
}
|
||||
}
|
||||
|
||||
// 优先平台无匹配数据时回退到不区分平台重新检查全部可用数据
|
||||
if (!anyPlatformHadData && targetPlatform) {
|
||||
return checkEpisodeSatisfied(animesList, querySeason, queryEpisode, requestAnimeDetailsMap, null);
|
||||
}
|
||||
|
||||
return allSatisfied;
|
||||
}
|
||||
|
||||
@@ -1220,7 +1227,8 @@ function findCrossSeasonEpisodeMap(searchData, title, year, season, episode, pla
|
||||
|
||||
if (absoluteMatch) {
|
||||
if (platform && getPlatformMatchScore(extractEpisodeTitle(absoluteMatch.episodeTitle), platform) === 0) {
|
||||
break;
|
||||
currentSeason++;
|
||||
continue;
|
||||
}
|
||||
log("info", `[system] [spillover] 跨季溢出查找命中 (按绝对标题数字) -> 所在季:S${currentSeason} 集标题:${absoluteMatch.episodeTitle}`);
|
||||
bestRes = {
|
||||
@@ -1234,7 +1242,8 @@ function findCrossSeasonEpisodeMap(searchData, title, year, season, episode, pla
|
||||
if (currentTargetEpisode <= allEps.length) {
|
||||
const targetEp = allEps[currentTargetEpisode - 1];
|
||||
if (platform && getPlatformMatchScore(extractEpisodeTitle(targetEp.episodeTitle), platform) === 0) {
|
||||
break;
|
||||
currentSeason++;
|
||||
continue;
|
||||
}
|
||||
log("info", `[system] [spillover] 跨季溢出查找命中 (按相对排位计算) -> 所在季:S${currentSeason} 集标题:${targetEp.episodeTitle}`);
|
||||
bestRes = {
|
||||
@@ -1359,6 +1368,14 @@ async function matchAniAndEp(season, episode, year, searchData, title, req, plat
|
||||
// 匹配集数
|
||||
matchedEpisode = findEpisodeByNumber(filteredEpisodes, episode, targetEpisode, platform);
|
||||
|
||||
// 当指定平台与候选动画源不匹配导致过滤后无匹配时,回退到不区分平台提取集数
|
||||
if (!matchedEpisode && platform) {
|
||||
const actualAnimePlatform = extractPlatformFromTitle(anime.animeTitle) || anime.source;
|
||||
if (getPlatformMatchScore(actualAnimePlatform, platform) === 0) {
|
||||
matchedEpisode = findEpisodeByNumber(filteredEpisodes, episode, targetEpisode, null);
|
||||
}
|
||||
}
|
||||
|
||||
// 如果当前是用户的优选偏好,但由于平台配置限制导致未命中目标平台,则放宽条件无视平台限制提取集数
|
||||
if (!matchedEpisode && isPreferredAnime) {
|
||||
log("info", `[system] [match] 优选剧集未命中目标平台 ${platform},放宽条件提取集数`);
|
||||
@@ -1745,6 +1762,9 @@ export async function matchAnime(url, req, clientIp) {
|
||||
resAnime = __ret.resAnime;
|
||||
// 跨季集数顺延映射的结果不更新 lastSearch,防止无关番剧污染 lastSelectMap 偏好记录
|
||||
spilloverMatched = __ret.isSpillover;
|
||||
if (resAnime) {
|
||||
resData["isMatched"] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -320,8 +320,9 @@ export function titleMatches(title, query, parsedSeason = null) {
|
||||
const titleSeason = getExplicitSeasonNumber(titleText);
|
||||
|
||||
if (querySeason > 1) {
|
||||
// 搜索指定续作(>1)时,标题必须明确包含该季度标识
|
||||
if ((titleSeason || 1) !== querySeason) return false;
|
||||
// 搜索指定续作(>1)时,仅当源标题明确包含季号时才校验季号一致性
|
||||
// 源标题无季号的不分季长剧不应被此规则拦截,交由上游源处理器决定
|
||||
if (titleSeason !== null && titleSeason !== querySeason) return false;
|
||||
} else if (querySeason === 1) {
|
||||
// 搜索第1季时,拦截明确标明为其他季度(如第2季、第3季)的结果
|
||||
if (titleSeason !== null && titleSeason !== 1) return false;
|
||||
@@ -452,8 +453,8 @@ export function extractSeasonNumberFromAnimeTitle(animeTitle) {
|
||||
export function extractEpisodeNumberFromTitle(episodeTitle) {
|
||||
if (!episodeTitle) return null;
|
||||
|
||||
// 匹配格式:第1集、第01集、第10集等
|
||||
const chineseMatch = episodeTitle.match(/第(\d+)集/);
|
||||
// 匹配格式:第1集、第01集、第1话、第1回等
|
||||
const chineseMatch = episodeTitle.match(/第(\d+)[集话回]/);
|
||||
if (chineseMatch) {
|
||||
return parseInt(chineseMatch[1], 10);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user