Optionally ignore outdated matches during bulk migration

This commit is contained in:
schroda
2026-05-08 15:19:26 +02:00
parent 67bb2248c4
commit 3d514d357a
7 changed files with 87 additions and 3 deletions

View File

@@ -52,6 +52,7 @@ export const ENTRY_STATUS_TRANSLATION: Record<MigrationEntryStatus, MessageDescr
[MigrationEntryStatus.SEARCH_COMPLETE]: msg`Match found`,
[MigrationEntryStatus.SEARCH_FAILED]: msg`Search failed`,
[MigrationEntryStatus.NO_MATCH]: msg`No match found`,
[MigrationEntryStatus.OUTDATED]: msg`Only outdated matches found`,
[MigrationEntryStatus.MIGRATING]: msg`Migrating…`,
[MigrationEntryStatus.MIGRATION_COMPLETE]: msg`Successfully migrated`,
[MigrationEntryStatus.MIGRATION_FAILED]: msg`Migration failed`,
@@ -62,6 +63,7 @@ export const MIGRATE_SEARCH_ENTRY_GROUPS = [
MigrationEntryStatus.SEARCHING,
MigrationEntryStatus.SEARCH_FAILED,
MigrationEntryStatus.NO_MATCH,
MigrationEntryStatus.OUTDATED,
MigrationEntryStatus.SEARCH_COMPLETE,
] as const satisfies readonly MigrationEntryStatus[];
@@ -72,6 +74,7 @@ export const MIGRATE_SEARCH_ENTRY_GROUP_EXPAND_DEFAULT_STATE: Record<
[MigrationEntryStatus.SEARCHING]: true,
[MigrationEntryStatus.SEARCH_FAILED]: false,
[MigrationEntryStatus.NO_MATCH]: false,
[MigrationEntryStatus.OUTDATED]: false,
[MigrationEntryStatus.SEARCH_COMPLETE]: false,
};
@@ -79,6 +82,7 @@ export const MIGRATE_EXECUTE_ENTRY_GROUPS = [
MigrationEntryStatus.MIGRATING,
MigrationEntryStatus.MIGRATION_FAILED,
MigrationEntryStatus.NO_MATCH,
MigrationEntryStatus.OUTDATED,
MigrationEntryStatus.EXCLUDED,
MigrationEntryStatus.MIGRATION_COMPLETE,
] as const satisfies readonly MigrationEntryStatus[];
@@ -90,6 +94,7 @@ export const MIGRATE_EXECUTE_ENTRY_GROUP_EXPAND_DEFAULT_STATE: Record<
[MigrationEntryStatus.MIGRATING]: true,
[MigrationEntryStatus.MIGRATION_FAILED]: false,
[MigrationEntryStatus.NO_MATCH]: false,
[MigrationEntryStatus.OUTDATED]: false,
[MigrationEntryStatus.EXCLUDED]: false,
[MigrationEntryStatus.MIGRATION_COMPLETE]: false,
};

View File

@@ -60,6 +60,7 @@ export type MetadataMigrationSettings = {
export interface MigrationBulkSearchSettings {
selectHighestChapterNumberSource: boolean;
ignoreOutdatedMatches: boolean;
}
export enum MigrationPhase {
@@ -76,6 +77,7 @@ export enum MigrationEntryStatus {
SEARCHING = 'searching',
SEARCH_COMPLETE = 'search_complete',
SEARCH_FAILED = 'search_failed',
OUTDATED = 'outdated',
NO_MATCH = 'no_match',
MIGRATING = 'migrating',
MIGRATION_COMPLETE = 'migration_complete',

View File

@@ -378,6 +378,7 @@ export class MigrationManager {
...MIGRATE_SEARCH_ENTRY_GROUP_EXPAND_DEFAULT_STATE,
[MigrationEntryStatus.SEARCHING]: false,
[MigrationEntryStatus.NO_MATCH]: !allSearchesFailed && !searchProgress.success,
[MigrationEntryStatus.OUTDATED]: !allSearchesFailed && !searchProgress.success,
[MigrationEntryStatus.SEARCH_FAILED]: allSearchesFailed,
[MigrationEntryStatus.SEARCH_COMPLETE]: !!searchProgress.success,
};
@@ -730,7 +731,7 @@ export class MigrationManager {
mainSignal: AbortSignal,
options: MigrationBulkSearchSettings,
): Promise<void> {
const { selectHighestChapterNumberSource } = options;
const { selectHighestChapterNumberSource, ignoreOutdatedMatches } = options;
const state = MigrationManager.getState();
const entry = state.entries[mangaId];
@@ -837,6 +838,7 @@ export class MigrationManager {
destSourceId,
);
const ignoreOutdatedMatch = ignoreOutdatedMatches && !hasNewerChapter;
const isPreferredSourcePriorityMatch =
hasHigherSourcePriority && !selectHighestChapterNumberSource;
const isPreferredChapterNumberMatch =
@@ -845,7 +847,8 @@ export class MigrationManager {
(hasHigherSourcePriority && hasSameLatestChapterAsSelectedMatch) ||
!draftMatchEntry);
const isPreferredMatch = isPreferredSourcePriorityMatch || isPreferredChapterNumberMatch;
const isPreferredMatch =
!ignoreOutdatedMatch && (isPreferredSourcePriorityMatch || isPreferredChapterNumberMatch);
if (isPreferredMatch) {
draftEntry.selectedMatchMangaId = bestMatch.id;
draftEntry.selectedMatchSourceId = destSourceId;
@@ -873,7 +876,12 @@ export class MigrationManager {
if (draftEntry.searchMatches.length) {
draft.searchProgress.success += 1;
draftEntry.status = MigrationEntryStatus.SEARCH_COMPLETE;
if (draftEntry.selectedMatchMangaId != null) {
draftEntry.status = MigrationEntryStatus.SEARCH_COMPLETE;
} else {
draftEntry.status = MigrationEntryStatus.OUTDATED;
}
} else {
draftEntry.status = MigrationEntryStatus.NO_MATCH;
}

View File

@@ -29,10 +29,34 @@ export const MigrationBulkSearchOptionsDialog = ({
const { t } = useLingui();
const [selectHighestChapterNumberSource, setSelectHighestChapterNumberSource] = useState(false);
const [ignoreOutdatedMatches, setIgnoreOutdatesMatches] = useState(false);
return (
<Dialog open={isVisible} fullWidth onClose={onDismiss} onTransitionExited={onExitComplete}>
<DialogTitle>{t`Search options`}</DialogTitle>
<DialogContent>
<CheckboxInput
label={
<Stack
sx={{
// Padding comes from the MUI Checkbox component
pt: '9px',
}}
>
<Typography>{t`Ignore matches without newer chapters`}</Typography>
<Typography
variant="body2"
color="textSecondary"
>{t`Do not automatically select matches if they are behind the current source. They will still be shown in the found matches`}</Typography>
</Stack>
}
sx={{
alignItems: 'start',
}}
checked={ignoreOutdatedMatches}
onChange={(_, checked) => setIgnoreOutdatesMatches(checked)}
/>
</DialogContent>
<DialogContent dividers>
<Stack
direction="row"
@@ -82,6 +106,7 @@ export const MigrationBulkSearchOptionsDialog = ({
onClick={() =>
onSubmit({
selectHighestChapterNumberSource,
ignoreOutdatedMatches,
})
}
>

View File

@@ -60,6 +60,10 @@ export const MigrationExecute = () => {
() => entryList.filter((entry) => entry.status === MigrationEntryStatus.NO_MATCH),
[entryList],
);
const outdatedEntries = useMemo(
() => entryList.filter((entry) => entry.status === MigrationEntryStatus.OUTDATED),
[entryList],
);
return (
<>
@@ -102,6 +106,15 @@ export const MigrationExecute = () => {
color="warning"
isMigrating
/>
<MigrationEntryGroup
status={MigrationEntryStatus.OUTDATED}
title={plural(outdatedEntries.length, {
one: '1 entry with only outdated matches',
other: '# entries with only outdated matches',
})}
entries={outdatedEntries}
color="warning"
/>
<MigrationEntryGroup
status={MigrationEntryStatus.EXCLUDED}
title={plural(excludedEntries.length, {

View File

@@ -58,6 +58,10 @@ export const MigrationSearch = () => {
() => entryList.filter((entry) => entry.status === MigrationEntryStatus.NO_MATCH),
[entryList],
);
const outdatedEntries = useMemo(
() => entryList.filter((entry) => entry.status === MigrationEntryStatus.OUTDATED),
[entryList],
);
const matchedEntries = useMemo(
() => entryList.filter((entry) => entry.status === MigrationEntryStatus.SEARCH_COMPLETE),
[entryList],
@@ -103,6 +107,15 @@ export const MigrationSearch = () => {
entries={noMatchEntries}
color="warning"
/>
<MigrationEntryGroup
status={MigrationEntryStatus.OUTDATED}
title={plural(outdatedEntries.length, {
one: '1 entry with only outdated matches',
other: '# entries with only outdated matches',
})}
entries={outdatedEntries}
color="warning"
/>
<MigrationEntryGroup
status={MigrationEntryStatus.SEARCH_COMPLETE}
title={plural(matchedEntries.length, {