From 5e984a60a572bd762584405d5d97310334f9bc5b Mon Sep 17 00:00:00 2001 From: Elegy233 <82034747+wan0ge@users.noreply.github.com> Date: Sat, 8 Aug 2026 21:32:43 +0800 Subject: [PATCH] =?UTF-8?q?fix(dandan):=20=E4=BC=98=E5=8C=96=E8=AF=A6?= =?UTF-8?q?=E6=83=85=E8=AF=B7=E6=B1=82=E6=95=B0=EF=BC=8C=E5=8E=9F=E5=90=8D?= =?UTF-8?q?=E6=90=9C=E7=B4=A2=E6=B7=BB=E5=8A=A0=E4=BD=8E=E9=97=A8=E6=A7=9B?= =?UTF-8?q?=E9=A2=84=E8=BF=87=E6=BB=A4=E9=81=BF=E5=85=8D=E5=87=BA=E7=8E=B0?= =?UTF-8?q?=E5=AE=8C=E5=85=A8=E6=97=A0=E5=85=B3=E7=BB=93=E6=9E=9C=20(#419)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(dandan): 优化详情请求数,搜索返回前用低阈值titleMatches预过滤 search()返回前用titleMatches(t, keyword, season, false, 0.5)对原始搜索 结果做初筛,剔除明显无关条目避免浪费getEpisodes详情请求。0.5阈值仅挡 明显无关项,翻译差异结果放行交由handleAnimes含别名0.8最终过滤。 TMDB abort从原始搜索返回时推迟到预过滤后:保留结果则abort,初筛清空则 不abort以触发内部日语原名兜底。执行顺序由Promise.all改为先await原始再 决定是否await TMDB,确保abort在TMDB完成前生效。 * feat(dandan): 关联作品恢复预过滤误剔除的原始搜索结果条目 search()预过滤后在结果数组上记录原始animeIds,handleAnimes处理 关联作品时检查其animeId是否在原始搜索结果中被误过滤(如翻译差异 导致低阈值titleMatches未命中),自动补回至sourceAnimes与处理队列。 仅在未指定目标季时触发,避免干扰明确指定季度的场景。 * fix: 修复titleMatches相似度策略无法返回true的回归bug commit 3345ed19添加年份兜底时将末尾return simMatch改为return false, 导致simMatch为true时跳过年份检查块后直接返回false。相似度匹配自7月14日 起在所有调用路径中静默失效,影响dandan预过滤及含别名0.8最终过滤。 预过滤titleMatches调用传入forceNonStrict=true,确保严格匹配模式下 仍使用包含+相似度双策略判断。 * perf(dandan): 日语原名搜索添加预过滤,常规预过滤阀门调整为0.4 常规搜索预过滤threshold由0.5调至0.4,更宽松以减少翻译差异导致的 误过滤。TMDB日语原名搜索结果新增threshold 0.19的低门阀预过滤, 剔除明显无关条目(如「正能量企鹅」等仅包含个别匹配字的噪声)。 --- danmu_api/sources/dandan.js | 68 +++++++++++++++++++++++++++------- danmu_api/utils/common-util.js | 2 +- 2 files changed, 56 insertions(+), 14 deletions(-) diff --git a/danmu_api/sources/dandan.js b/danmu_api/sources/dandan.js index fbfb288..83c3ae9 100644 --- a/danmu_api/sources/dandan.js +++ b/danmu_api/sources/dandan.js @@ -85,9 +85,6 @@ export default class DandanSource extends BaseSource { log("info", "[dandan] 原始搜索成功,但未返回任何结果 (source: original)"); return { success: false, source: 'original' }; } - - // 原始搜索有结果,中断 TMDB 流程 - tmdbAbortController.abort(); const animes = resp.data.animes; log("info", `[dandan] dandanSearchresp (original): ${JSON.stringify(animes)}`); log("info", `[dandan] 返回 ${animes.length} 条结果 (source: original)`); @@ -165,20 +162,35 @@ export default class DandanSource extends BaseSource { } })(); - // 等待两个搜索任务同时完成,优先采用原始搜索结果 - const [originalResult, tmdbResult] = await Promise.all([ - originalSearchPromise, - tmdbSearchPromise - ]); - - // 优先返回原始搜索结果 + // 搜索结果预过滤:先拿到原始结果再决定是否等待TMDB兜底 + const originalResult = await originalSearchPromise; if (originalResult.success) { - return originalResult.data; + const resolvedSeason = getExplicitSeasonNumber(keyword); + const preFiltered = originalResult.data.filter(anime => { + if (anime.isTmdbSource) return true; + const t = anime.animeTitle || anime.title || ''; + return titleMatches(t, keyword, resolvedSeason, true, 0.8); + }); + if (preFiltered.length > 0) { + tmdbAbortController.abort(); + // 记录原始搜索结果的全部animeId,供handleAnimes关联作品恢复误过滤条目使用 + preFiltered._originalAnimeIds = originalResult.data.map(a => a.animeId); + return preFiltered; + } + // 初筛清空原始结果时不abort,等待TMDB兜底 } - // 原始搜索无结果,返回 TMDB 搜索结果 + // 原始搜索无结果或被初筛清空,等待并返回TMDB搜索结果 + const tmdbResult = await tmdbSearchPromise; + + // 原始搜索无结果,对TMDB日语原名结果做最终预过滤 if (tmdbResult.success) { - return tmdbResult.data; + const resolvedSeason = getExplicitSeasonNumber(keyword); + const tmdbFiltered = tmdbResult.data.filter(anime => { + const t = anime.animeTitle || anime.title || ''; + return titleMatches(t, keyword, resolvedSeason, true, 0.19); + }); + if (tmdbFiltered.length > 0) return tmdbFiltered; } log("info", `[dandan] 原始搜索和基于TMDB的搜索均未返回任何结果 (当前搜索词: ${keyword})`); @@ -394,6 +406,36 @@ export default class DandanSource extends BaseSource { } } } + + // 关联作品补回预过滤误剔除的原始搜索结果条目(如翻译差异导致误过滤) + if (!isTargetFoundInInitial && Array.isArray(details.relateds)) { + const originalIds = sourceAnimes._originalAnimeIds; + if (Array.isArray(originalIds) && originalIds.length > 0) { + const recoveredIds = new Set(originalIds); + for (const rel of details.relateds) { + if (recoveredIds.has(rel.animeId) && !existingIds.has(rel.animeId)) { + existingIds.add(rel.animeId); + if (!sourceAnimes.some(a => a.animeId === rel.animeId)) { + sourceAnimes.push({ + animeId: rel.animeId, + animeTitle: rel.animeTitle, + title: rel.animeTitle, + imageUrl: rel.imageUrl, + rating: rel.rating || 0, + isRelated: true + }); + } + queue.push({ + animeId: rel.animeId, + animeTitle: rel.animeTitle, + imageUrl: rel.imageUrl, + rating: rel.rating || 0, + isRelated: true // 标记动态挖掘出的条目为相关作品 + }); + } + } + } + } } // 区分初始搜索结果与动态相关作品的结果过滤逻辑 diff --git a/danmu_api/utils/common-util.js b/danmu_api/utils/common-util.js index e1ecb7d..2ac1402 100644 --- a/danmu_api/utils/common-util.js +++ b/danmu_api/utils/common-util.js @@ -368,7 +368,7 @@ export function titleMatches(title, query, parsedSeason = null, forceNonStrict = } } - return false; + return simMatch; } /**