Optionally perform advanced search during bulk migration
This commit is contained in:
@@ -61,6 +61,7 @@ export type MetadataMigrationSettings = {
|
|||||||
export interface MigrationBulkSearchSettings {
|
export interface MigrationBulkSearchSettings {
|
||||||
selectHighestChapterNumberSource: boolean;
|
selectHighestChapterNumberSource: boolean;
|
||||||
ignoreOutdatedMatches: boolean;
|
ignoreOutdatedMatches: boolean;
|
||||||
|
performAdvancedSearch: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum MigrationPhase {
|
export enum MigrationPhase {
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ import { MANGA_MIGRATION_FIELDS } from '@/lib/graphql/manga/MangaFragments.ts';
|
|||||||
import { ZustandUtil } from '@/lib/zustand/ZustandUtil.ts';
|
import { ZustandUtil } from '@/lib/zustand/ZustandUtil.ts';
|
||||||
import { getErrorMessage } from '@/lib/HelperFunctions.ts';
|
import { getErrorMessage } from '@/lib/HelperFunctions.ts';
|
||||||
import isEqual from 'lodash/fp/isEqual';
|
import isEqual from 'lodash/fp/isEqual';
|
||||||
|
import uniqBy from 'lodash/fp/uniqBy';
|
||||||
|
|
||||||
const RESUMABLE_PHASES: readonly MigrationPhase[] = [MigrationPhase.SEARCHING, MigrationPhase.MIGRATING];
|
const RESUMABLE_PHASES: readonly MigrationPhase[] = [MigrationPhase.SEARCHING, MigrationPhase.MIGRATING];
|
||||||
|
|
||||||
@@ -663,66 +664,91 @@ export class MigrationManager {
|
|||||||
mangaTitle: string,
|
mangaTitle: string,
|
||||||
sourceId: SourceIdInfo['id'],
|
sourceId: SourceIdInfo['id'],
|
||||||
signal: AbortSignal,
|
signal: AbortSignal,
|
||||||
{ selectHighestChapterNumberSource }: MigrationBulkSearchSettings,
|
{ selectHighestChapterNumberSource, performAdvancedSearch }: MigrationBulkSearchSettings,
|
||||||
): Promise<MangaMigrationFieldsFragment[]> {
|
): Promise<MangaMigrationFieldsFragment[]> {
|
||||||
if (signal.aborted) {
|
if (signal.aborted) {
|
||||||
throw new Error(signal.reason);
|
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) {
|
if (signal.aborted) {
|
||||||
throw new Error(signal.reason);
|
throw new Error(signal.reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!selectHighestChapterNumberSource && MigrationManager.hasHigherSourcePriorityMatch(mangaId, sourceId)) {
|
return (async () => {
|
||||||
throw new Error('Entry already has a selected match from a higher priority source');
|
try {
|
||||||
}
|
const updatedMatch = await requestManager.refreshManga(match.id, {
|
||||||
|
awaitRefetchQueries: true,
|
||||||
|
}).response;
|
||||||
|
|
||||||
const searchResponse = await requestManager.graphQLClient.client.mutate<
|
return updatedMatch.data?.fetchManga?.manga ?? match;
|
||||||
GetMigrationSourceMangasFetchMutation,
|
} catch (e) {
|
||||||
GetMigrationSourceMangasFetchMutationVariables
|
// ignore
|
||||||
>({
|
|
||||||
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 (async () => {
|
return match;
|
||||||
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;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const updatedMatches = await Promise.all(matchUpdatePromises);
|
||||||
|
|
||||||
|
return updatedMatches;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async searchForManga(
|
private static async searchForManga(
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ export const MigrationBulkSearchOptionsDialog = ({
|
|||||||
|
|
||||||
const [selectHighestChapterNumberSource, setSelectHighestChapterNumberSource] = useState(false);
|
const [selectHighestChapterNumberSource, setSelectHighestChapterNumberSource] = useState(false);
|
||||||
const [ignoreOutdatedMatches, setIgnoreOutdatesMatches] = useState(false);
|
const [ignoreOutdatedMatches, setIgnoreOutdatesMatches] = useState(false);
|
||||||
|
const [performAdvancedSearch, setPerformAdvancedSearch] = useState(false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={isVisible} fullWidth onClose={onDismiss} onTransitionExited={onExitComplete}>
|
<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`}
|
{t`These options are slow and dangerous and may lead to restrictions from sources`}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Stack>
|
</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
|
<CheckboxInput
|
||||||
label={
|
label={
|
||||||
<Stack
|
<Stack
|
||||||
@@ -107,6 +129,7 @@ export const MigrationBulkSearchOptionsDialog = ({
|
|||||||
onSubmit({
|
onSubmit({
|
||||||
selectHighestChapterNumberSource,
|
selectHighestChapterNumberSource,
|
||||||
ignoreOutdatedMatches,
|
ignoreOutdatedMatches,
|
||||||
|
performAdvancedSearch,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -380,6 +380,10 @@ msgstr "Added manga to library!"
|
|||||||
msgid "Advanced"
|
msgid "Advanced"
|
||||||
msgstr "Advanced"
|
msgstr "Advanced"
|
||||||
|
|
||||||
|
#: src/features/migration/components/MigrationBulkSearchOptionsDialog.tsx
|
||||||
|
msgid "Advanced search mode"
|
||||||
|
msgstr "Advanced search mode"
|
||||||
|
|
||||||
#: src/features/settings/Settings.constants.ts
|
#: 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"
|
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"
|
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"
|
msgid "Bottom"
|
||||||
msgstr "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
|
#: src/features/reader/settings/behaviour/ReaderBehaviourSettings.tsx
|
||||||
msgid "Briefly show current mode when reader is opened or mode got changed"
|
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"
|
msgstr "Briefly show current mode when reader is opened or mode got changed"
|
||||||
|
|||||||
Reference in New Issue
Block a user