Optionally perform advanced search during bulk migration

This commit is contained in:
schroda
2026-05-08 15:41:58 +02:00
parent 3d514d357a
commit 55a620c63a
4 changed files with 105 additions and 47 deletions

View File

@@ -61,6 +61,7 @@ export type MetadataMigrationSettings = {
export interface MigrationBulkSearchSettings {
selectHighestChapterNumberSource: boolean;
ignoreOutdatedMatches: boolean;
performAdvancedSearch: boolean;
}
export enum MigrationPhase {

View File

@@ -56,6 +56,7 @@ import { MANGA_MIGRATION_FIELDS } from '@/lib/graphql/manga/MangaFragments.ts';
import { ZustandUtil } from '@/lib/zustand/ZustandUtil.ts';
import { getErrorMessage } from '@/lib/HelperFunctions.ts';
import isEqual from 'lodash/fp/isEqual';
import uniqBy from 'lodash/fp/uniqBy';
const RESUMABLE_PHASES: readonly MigrationPhase[] = [MigrationPhase.SEARCHING, MigrationPhase.MIGRATING];
@@ -663,66 +664,91 @@ export class MigrationManager {
mangaTitle: string,
sourceId: SourceIdInfo['id'],
signal: AbortSignal,
{ selectHighestChapterNumberSource }: MigrationBulkSearchSettings,
{ selectHighestChapterNumberSource, performAdvancedSearch }: MigrationBulkSearchSettings,
): Promise<MangaMigrationFieldsFragment[]> {
if (signal.aborted) {
throw new Error(signal.reason);
}
return MigrationManager.getOrCreateSourceQueue(sourceId)(async () => {
if (signal.aborted) {
throw new Error(signal.reason);
}
if (!selectHighestChapterNumberSource && MigrationManager.hasHigherSourcePriorityMatch(mangaId, sourceId)) {
throw new Error('Entry already has a selected match from a higher priority source');
}
const searchQueries = performAdvancedSearch
? [
mangaTitle,
...enhancedCleanup(mangaTitle)
.split(' ')
.filter((query) => query.length >= 3),
]
: [mangaTitle];
const searchRequests = searchQueries.map((query) =>
MigrationManager.getOrCreateSourceQueue(sourceId)(() =>
requestManager.graphQLClient.client.mutate<
GetMigrationSourceMangasFetchMutation,
GetMigrationSourceMangasFetchMutationVariables
>({
mutation: GET_MIGRATION_SOURCE_MANGAS_FETCH,
variables: {
input: {
source: sourceId,
query,
page: 1,
type: FetchSourceMangaType.Search,
},
},
context: { fetchOptions: { signal } },
}),
),
);
const searchResponses = await Promise.allSettled(searchRequests);
const successfulSearchResponses = searchResponses
.filter((response) => response.status === 'fulfilled')
.map((response) => response.value);
if (!successfulSearchResponses.length) {
const failureReasons = searchResponses.map((response) => (response as PromiseRejectedResult).reason);
throw new Error(`Search failed due to:${failureReasons.join('\n\n')}`);
}
const searchResults = successfulSearchResponses.flatMap(
(response) => response?.data?.fetchSourceManga?.mangas ?? [],
);
const uniqueSearchResults = uniqBy('id', searchResults);
const matches = uniqueSearchResults.filter(
(searchMatch) => enhancedCleanup(searchMatch.title) === enhancedCleanup(mangaTitle),
);
const matchUpdatePromises = matches.map(async (match) => {
if (signal.aborted) {
throw new Error(signal.reason);
}
if (!selectHighestChapterNumberSource && MigrationManager.hasHigherSourcePriorityMatch(mangaId, sourceId)) {
throw new Error('Entry already has a selected match from a higher priority source');
}
return (async () => {
try {
const updatedMatch = await requestManager.refreshManga(match.id, {
awaitRefetchQueries: true,
}).response;
const searchResponse = await requestManager.graphQLClient.client.mutate<
GetMigrationSourceMangasFetchMutation,
GetMigrationSourceMangasFetchMutationVariables
>({
mutation: GET_MIGRATION_SOURCE_MANGAS_FETCH,
variables: {
input: {
source: sourceId,
query: mangaTitle,
page: 1,
type: FetchSourceMangaType.Search,
},
},
context: { fetchOptions: { signal } },
});
const searchMatches = searchResponse?.data?.fetchSourceManga?.mangas ?? [];
const matches = searchMatches.filter(
(searchMatch) => enhancedCleanup(searchMatch.title) === enhancedCleanup(mangaTitle),
);
const matchUpdatePromises = matches.map(async (match) => {
if (signal.aborted) {
throw new Error(signal.reason);
return updatedMatch.data?.fetchManga?.manga ?? match;
} catch (e) {
// ignore
}
return (async () => {
try {
const updatedMatch = await requestManager.refreshManga(match.id, {
awaitRefetchQueries: true,
}).response;
return updatedMatch.data?.fetchManga?.manga ?? match;
} catch (e) {
// ignore
}
return match;
})();
});
const updatedMatches = await Promise.all(matchUpdatePromises);
return updatedMatches;
return match;
})();
});
const updatedMatches = await Promise.all(matchUpdatePromises);
return updatedMatches;
}
private static async searchForManga(

View File

@@ -30,6 +30,7 @@ export const MigrationBulkSearchOptionsDialog = ({
const [selectHighestChapterNumberSource, setSelectHighestChapterNumberSource] = useState(false);
const [ignoreOutdatedMatches, setIgnoreOutdatesMatches] = useState(false);
const [performAdvancedSearch, setPerformAdvancedSearch] = useState(false);
return (
<Dialog open={isVisible} fullWidth onClose={onDismiss} onTransitionExited={onExitComplete}>
@@ -77,6 +78,27 @@ export const MigrationBulkSearchOptionsDialog = ({
{t`These options are slow and dangerous and may lead to restrictions from sources`}
</Typography>
</Stack>
<CheckboxInput
label={
<Stack
sx={{
// Padding comes from the MUI Checkbox component
pt: '9px',
}}
>
<Typography>{t`Advanced search mode`}</Typography>
<Typography
variant="body2"
color="textSecondary"
>{t`Breaks down the title into keywords for a wider search`}</Typography>
</Stack>
}
sx={{
alignItems: 'start',
}}
checked={performAdvancedSearch}
onChange={(_, checked) => setPerformAdvancedSearch(checked)}
/>
<CheckboxInput
label={
<Stack
@@ -107,6 +129,7 @@ export const MigrationBulkSearchOptionsDialog = ({
onSubmit({
selectHighestChapterNumberSource,
ignoreOutdatedMatches,
performAdvancedSearch,
})
}
>

View File

@@ -380,6 +380,10 @@ msgstr "Added manga to library!"
msgid "Advanced"
msgstr "Advanced"
#: src/features/migration/components/MigrationBulkSearchOptionsDialog.tsx
msgid "Advanced search mode"
msgstr "Advanced search mode"
#: src/features/settings/Settings.constants.ts
msgid "After changing this setting go to the \"About\" page and check for a webUI update and install it"
msgstr "After changing this setting go to the \"About\" page and check for a webUI update and install it"
@@ -601,6 +605,10 @@ msgstr "Both"
msgid "Bottom"
msgstr "Bottom"
#: src/features/migration/components/MigrationBulkSearchOptionsDialog.tsx
msgid "Breaks down the title into keywords for a wider search"
msgstr "Breaks down the title into keywords for a wider search"
#: src/features/reader/settings/behaviour/ReaderBehaviourSettings.tsx
msgid "Briefly show current mode when reader is opened or mode got changed"
msgstr "Briefly show current mode when reader is opened or mode got changed"