Kenmei manga to match.
Optionaltoken: stringOptional authentication token.
Optional search service configuration overrides.
OptionalprogressCallback: (current: number, total: number, currentTitle?: string) => voidOptional callback for progress updates (current, total, currentTitle).
OptionalshouldCancel: () => booleanOptional function to check for cancellation request.
OptionalabortSignal: AbortSignalOptional abort signal to cancel the operation.
Promise resolving to array of MangaMatchResult objects.
export async function batchMatchManga(
mangaList: KenmeiManga[],
token?: string,
config: Partial<SearchServiceConfig> = {},
progressCallback?: (
current: number,
total: number,
currentTitle?: string,
) => void,
shouldCancel?: () => boolean,
abortSignal?: AbortSignal,
): Promise<MangaMatchResult[]> {
return withGroupAsync(
`[MangaSearchService] Batch Match (${mangaList.length} manga)`,
async () => {
// Ensure we have the latest cache data
syncWithClientCache();
const searchConfig = { ...DEFAULT_SEARCH_CONFIG, ...config };
// Create a set to track which manga have been reported in the progress
const reportedIndices = new Set<number>();
// Declare cache variables in outer scope for access in catch block
// Update progress with deduplication
const updateProgress = (index: number, title?: string) => {
if (progressCallback && !reportedIndices.has(index)) {
reportedIndices.add(index);
progressCallback(reportedIndices.size, mangaList.length, title);
}
};
// Simplified cancellation check
const checkCancellation = () => {
checkCancellationState(abortSignal, shouldCancel, "Batch matching");
};
let storage: CachedResultsStorage | undefined;
try {
console.info(
`[MangaSearchService] 🚀 Starting batch matching for ${mangaList.length} manga entries`,
);
storage = {
cachedResults: {},
cachedComickSources: {},
cachedMangaDexSources: {},
cachedFallbackIndices: new Set<number>(),
};
// Categorize manga based on cache status
const result = categorizeMangaForBatching(
mangaList,
searchConfig,
updateProgress,
);
storage.cachedResults = result.cachedResults;
storage.cachedComickSources = result.cachedComickSources;
storage.cachedMangaDexSources = result.cachedMangaDexSources;
const { uncachedManga, knownMangaIds } = result;
console.debug(
`[MangaSearchService] 🔍 Categorization: ${Object.keys(storage.cachedResults).length} cached, ${uncachedManga.length} uncached, ${knownMangaIds.length} known IDs`,
);
// Check for cancellation
checkCancellation();
// Process manga with known IDs first
await processKnownMangaIds(
{ knownMangaIds, mangaList, uncachedManga },
{ searchConfig, token },
{ shouldCancel, abortSignal },
{ updateProgress },
storage,
);
// Check for cancellation
checkCancellation();
// Process uncached manga using batched GraphQL queries with automatic rate limit handling
await processMangaWithRateLimit(
{ uncachedManga, mangaList, reportedIndices },
{ token, searchConfig },
{ abortSignal, checkCancellation },
{ updateProgress },
storage,
);
// Check for cancellation after the batch completes
checkCancellation();
console.debug(
"[MangaSearchService] 🔍 Compiling final match results...",
);
// Compile final results
const finalResults = await compileMatchResults(
mangaList,
storage,
checkCancellation,
updateProgress,
searchConfig.shouldUseWorkers,
);
console.info(
`[MangaSearchService] ✅ Batch matching complete: ${finalResults.length} results`,
);
return finalResults;
} catch (error) {
console.error(
"[MangaSearchService] ❌ Error in batch matching process:",
error,
);
// If we got here due to cancellation, return whatever partial results we have
if (error instanceof CancelledError) {
console.info(
`[MangaSearchService] Cancellation detected, returning partial results`,
);
// Log cancellation as info message, not an exception
import("@sentry/electron/renderer")
.then((Sentry) => {
Sentry.captureMessage("Batch matching cancelled by user", "info");
})
.catch(() => {
// Silently ignore Sentry import errors
});
// Return partial results we've gathered so far
return handleCancellationResults(
mangaList,
storage?.cachedResults ?? {},
);
}
// For non-cancellation errors, capture to Sentry
captureError(ErrorType.UNKNOWN, "Batch manga matching failed", error, {
mangaListLength: mangaList.length,
searchConfig: {
confidenceThreshold: config.matchConfig?.confidenceThreshold,
},
stage: "batch_match",
});
// Otherwise rethrow the error
throw error;
}
},
);
}
Matches multiple Kenmei manga entries efficiently using batch operations.
Uses cached results, batch ID fetching, and sequential searches for uncached entries. Supports cancellation with partial results return and progress tracking.