Manga title to process cached results for
Cache key to retrieve results from
OptionalkenmeiManga: KenmeiMangaOptional Kenmei manga for custom rule evaluation
Manga search response with cached results or null if cache invalid
Custom rules are only evaluated when kenmeiManga is provided to enable proper filtering. For general caching without kenmeiManga, custom rules are not evaluated. Skip rules take precedence over accept rules - if a match satisfies both, it is skipped. Accept rule confidence boost is applied immediately within this function for cached results, ensuring consistent confidence values before returning to the caller.
export function processCachedResults(
title: string,
cacheKey: string,
kenmeiManga?: KenmeiManga,
): MangaSearchResponse | null {
if (!isCacheValid(cacheKey)) return null;
console.debug(`[MangaSearchService] Using cache for ${title}`);
let filteredResults = filterOutBlacklistedManga(
mangaCache[cacheKey].manga.filter(
(manga) => manga.format !== "NOVEL" && manga.format !== "LIGHT_NOVEL",
),
);
const matchConfig = getMatchConfig();
// Apply system content filters (one-shots, adult content, custom skip rules)
filteredResults = applySystemContentFilters(
filteredResults,
matchConfig,
kenmeiManga,
title,
);
if (filteredResults.length === 0) {
console.warn(
`[MangaSearchService] ⚠️ Cached results for "${title}" were fully filtered out (blacklisted/filtered). Invalidating cache entry and re-running search`,
);
delete mangaCache[cacheKey];
saveCache();
return null;
}
console.debug(
`[MangaSearchService] ⚖️ Calculating fresh confidence scores for ${filteredResults.length} cached matches`,
);
const cacheEntry = mangaCache[cacheKey];
const comickSourceMap = recordToSourceMap(
cacheEntry?.sourceMetadata?.comickSources,
);
const mangaDexSourceMap = recordToSourceMap(
cacheEntry?.sourceMetadata?.mangaDexSources,
);
const matches = filteredResults.map((manga) => {
let confidence = calculateConfidence(title, manga);
const titleTypePriority = calculateTitleTypePriority(manga, title);
// Apply custom accept rule boost if kenmeiManga provided
if (kenmeiManga) {
const { shouldAccept, matchedRule } = shouldAcceptByCustomRules(
manga,
kenmeiManga,
);
if (shouldAccept && matchedRule) {
// Apply confidence floor boost immediately for cached results
const isExactMatch =
title.toLowerCase() === manga.title?.romaji?.toLowerCase() ||
title.toLowerCase() === manga.title?.english?.toLowerCase();
const minConfidence = isExactMatch
? ACCEPT_RULE_CONFIDENCE_FLOOR_EXACT
: ACCEPT_RULE_CONFIDENCE_FLOOR_REGULAR;
if (confidence < minConfidence) {
console.debug(
`[MangaSearchService] ⭐ Boosting cached result confidence from ${(confidence * 100).toFixed(0)}% to ${(minConfidence * 100).toFixed(0)}% for "${manga.title?.romaji || manga.title?.english}" (custom accept rule: "${matchedRule.description}")`,
);
confidence = minConfidence;
}
}
}
console.debug(
`[MangaSearchService] ⚖️ Cached match confidence for "${manga.title?.english || manga.title?.romaji}": ${(confidence * 100).toFixed(0)}% (priority: ${titleTypePriority})`,
);
return { manga, confidence, titleTypePriority };
});
// Sort by confidence and priority
matches.sort((a, b) => {
if (a.confidence !== b.confidence) {
return b.confidence - a.confidence;
}
return b.titleTypePriority - a.titleTypePriority;
});
const finalMatches = matches.map(({ manga, confidence }) => ({
manga,
confidence,
comickSource: comickSourceMap.get(manga.id),
mangaDexSource: mangaDexSourceMap.get(manga.id),
sourceInfo: getSourceInfo(manga.id, comickSourceMap, mangaDexSourceMap),
}));
return {
matches: finalMatches,
pageInfo: undefined,
};
}
Process cached results with filtering and confidence recalculation.
When
kenmeiMangais provided, applies custom rules in the following order:Without
kenmeiManga, only system filters are applied (backward-compatible mode).