mirror of
https://github.com/stashapp/stash.git
synced 2025-12-06 16:34:02 +01:00
Handle equality when scraping scene performers (#4094)
This commit is contained in:
parent
50c4ac98af
commit
cf3301c8bc
4 changed files with 63 additions and 15 deletions
|
|
@ -7,7 +7,10 @@ import {
|
||||||
ScrapedTextAreaRow,
|
ScrapedTextAreaRow,
|
||||||
} from "src/components/Shared/ScrapeDialog/ScrapeDialog";
|
} from "src/components/Shared/ScrapeDialog/ScrapeDialog";
|
||||||
import clone from "lodash-es/clone";
|
import clone from "lodash-es/clone";
|
||||||
import { ScrapeResult } from "src/components/Shared/ScrapeDialog/scrapeResult";
|
import {
|
||||||
|
ObjectListScrapeResult,
|
||||||
|
ScrapeResult,
|
||||||
|
} from "src/components/Shared/ScrapeDialog/scrapeResult";
|
||||||
import {
|
import {
|
||||||
ScrapedPerformersRow,
|
ScrapedPerformersRow,
|
||||||
ScrapedStudioRow,
|
ScrapedStudioRow,
|
||||||
|
|
@ -97,9 +100,9 @@ export const GalleryScrapeDialog: React.FC<IGalleryScrapeDialogProps> = (
|
||||||
}
|
}
|
||||||
|
|
||||||
const [performers, setPerformers] = useState<
|
const [performers, setPerformers] = useState<
|
||||||
ScrapeResult<GQL.ScrapedPerformer[]>
|
ObjectListScrapeResult<GQL.ScrapedPerformer>
|
||||||
>(
|
>(
|
||||||
new ScrapeResult<GQL.ScrapedPerformer[]>(
|
new ObjectListScrapeResult<GQL.ScrapedPerformer>(
|
||||||
sortStoredIdObjects(
|
sortStoredIdObjects(
|
||||||
props.galleryPerformers.map((p) => ({
|
props.galleryPerformers.map((p) => ({
|
||||||
stored_id: p.id,
|
stored_id: p.id,
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,10 @@ import { useIntl } from "react-intl";
|
||||||
import { uniq } from "lodash-es";
|
import { uniq } from "lodash-es";
|
||||||
import { Performer } from "src/components/Performers/PerformerSelect";
|
import { Performer } from "src/components/Performers/PerformerSelect";
|
||||||
import { IHasStoredID, sortStoredIdObjects } from "src/utils/data";
|
import { IHasStoredID, sortStoredIdObjects } from "src/utils/data";
|
||||||
import { ScrapeResult } from "src/components/Shared/ScrapeDialog/scrapeResult";
|
import {
|
||||||
|
ObjectListScrapeResult,
|
||||||
|
ScrapeResult,
|
||||||
|
} from "src/components/Shared/ScrapeDialog/scrapeResult";
|
||||||
import {
|
import {
|
||||||
ScrapedMoviesRow,
|
ScrapedMoviesRow,
|
||||||
ScrapedPerformersRow,
|
ScrapedPerformersRow,
|
||||||
|
|
@ -117,9 +120,9 @@ export const SceneScrapeDialog: React.FC<ISceneScrapeDialogProps> = ({
|
||||||
}
|
}
|
||||||
|
|
||||||
const [performers, setPerformers] = useState<
|
const [performers, setPerformers] = useState<
|
||||||
ScrapeResult<GQL.ScrapedPerformer[]>
|
ObjectListScrapeResult<GQL.ScrapedPerformer>
|
||||||
>(
|
>(
|
||||||
new ScrapeResult<GQL.ScrapedPerformer[]>(
|
new ObjectListScrapeResult<GQL.ScrapedPerformer>(
|
||||||
sortStoredIdObjects(
|
sortStoredIdObjects(
|
||||||
scenePerformers.map((p) => ({
|
scenePerformers.map((p) => ({
|
||||||
stored_id: p.id,
|
stored_id: p.id,
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ import { RatingSystem } from "src/components/Shared/Rating/RatingSystem";
|
||||||
import { ModalComponent } from "../Shared/Modal";
|
import { ModalComponent } from "../Shared/Modal";
|
||||||
import { IHasStoredID, sortStoredIdObjects } from "src/utils/data";
|
import { IHasStoredID, sortStoredIdObjects } from "src/utils/data";
|
||||||
import {
|
import {
|
||||||
|
ObjectListScrapeResult,
|
||||||
ScrapeResult,
|
ScrapeResult,
|
||||||
ZeroableScrapeResult,
|
ZeroableScrapeResult,
|
||||||
hasScrapedValues,
|
hasScrapedValues,
|
||||||
|
|
@ -118,9 +119,9 @@ const SceneMergeDetails: React.FC<ISceneMergeDetailsProps> = ({
|
||||||
}
|
}
|
||||||
|
|
||||||
const [performers, setPerformers] = useState<
|
const [performers, setPerformers] = useState<
|
||||||
ScrapeResult<GQL.ScrapedPerformer[]>
|
ObjectListScrapeResult<GQL.ScrapedPerformer>
|
||||||
>(
|
>(
|
||||||
new ScrapeResult<GQL.ScrapedPerformer[]>(
|
new ObjectListScrapeResult<GQL.ScrapedPerformer>(
|
||||||
sortStoredIdObjects(dest.performers.map(idToStoredID))
|
sortStoredIdObjects(dest.performers.map(idToStoredID))
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
@ -203,8 +204,8 @@ const SceneMergeDetails: React.FC<ISceneMergeDetailsProps> = ({
|
||||||
);
|
);
|
||||||
|
|
||||||
setPerformers(
|
setPerformers(
|
||||||
new ScrapeResult(
|
new ObjectListScrapeResult<GQL.ScrapedPerformer>(
|
||||||
dest.performers.map(idToStoredID),
|
sortStoredIdObjects(dest.performers.map(idToStoredID)),
|
||||||
uniqIDStoredIDs(all.map((s) => s.performers.map(idToStoredID)).flat())
|
uniqIDStoredIDs(all.map((s) => s.performers.map(idToStoredID)).flat())
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,30 @@
|
||||||
import isEqual from "lodash-es/isEqual";
|
import lodashIsEqual from "lodash-es/isEqual";
|
||||||
import clone from "lodash-es/clone";
|
import clone from "lodash-es/clone";
|
||||||
|
import { IHasStoredID } from "src/utils/data";
|
||||||
|
|
||||||
export class ScrapeResult<T> {
|
export class ScrapeResult<T> {
|
||||||
public newValue?: T;
|
public newValue?: T;
|
||||||
public originalValue?: T;
|
public originalValue?: T;
|
||||||
public scraped: boolean = false;
|
public scraped: boolean = false;
|
||||||
public useNewValue: boolean = false;
|
public useNewValue: boolean = false;
|
||||||
|
private isEqual: (
|
||||||
|
v1: T | undefined | null,
|
||||||
|
v2: T | undefined | null
|
||||||
|
) => boolean;
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
originalValue?: T | null,
|
originalValue?: T | null,
|
||||||
newValue?: T | null,
|
newValue?: T | null,
|
||||||
useNewValue?: boolean
|
useNewValue?: boolean,
|
||||||
|
isEqual: (
|
||||||
|
v1: T | undefined | null,
|
||||||
|
v2: T | undefined | null
|
||||||
|
) => boolean = lodashIsEqual
|
||||||
) {
|
) {
|
||||||
this.originalValue = originalValue ?? undefined;
|
this.originalValue = originalValue ?? undefined;
|
||||||
this.newValue = newValue ?? undefined;
|
this.newValue = newValue ?? undefined;
|
||||||
|
this.isEqual = isEqual;
|
||||||
|
|
||||||
// NOTE: this means that zero values are treated as null
|
// NOTE: this means that zero values are treated as null
|
||||||
// this is incorrect for numbers and booleans, but correct for strings
|
// this is incorrect for numbers and booleans, but correct for strings
|
||||||
const hasNewValue = !!this.newValue;
|
const hasNewValue = !!this.newValue;
|
||||||
|
|
@ -32,7 +43,7 @@ export class ScrapeResult<T> {
|
||||||
const ret = clone(this);
|
const ret = clone(this);
|
||||||
|
|
||||||
ret.newValue = value;
|
ret.newValue = value;
|
||||||
ret.useNewValue = !isEqual(ret.newValue, ret.originalValue);
|
ret.useNewValue = !this.isEqual(ret.newValue, ret.originalValue);
|
||||||
|
|
||||||
// #2691 - if we're setting the value, assume it should be treated as
|
// #2691 - if we're setting the value, assume it should be treated as
|
||||||
// scraped
|
// scraped
|
||||||
|
|
@ -53,9 +64,13 @@ export class ZeroableScrapeResult<T> extends ScrapeResult<T> {
|
||||||
public constructor(
|
public constructor(
|
||||||
originalValue?: T | null,
|
originalValue?: T | null,
|
||||||
newValue?: T | null,
|
newValue?: T | null,
|
||||||
useNewValue?: boolean
|
useNewValue?: boolean,
|
||||||
|
isEqual: (
|
||||||
|
v1: T | undefined | null,
|
||||||
|
v2: T | undefined | null
|
||||||
|
) => boolean = lodashIsEqual
|
||||||
) {
|
) {
|
||||||
super(originalValue, newValue, useNewValue);
|
super(originalValue, newValue, useNewValue, isEqual);
|
||||||
|
|
||||||
const hasNewValue = this.newValue !== undefined;
|
const hasNewValue = this.newValue !== undefined;
|
||||||
|
|
||||||
|
|
@ -65,6 +80,32 @@ export class ZeroableScrapeResult<T> extends ScrapeResult<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function storedIDsEqual<T extends IHasStoredID>(
|
||||||
|
o1: T[] | undefined | null,
|
||||||
|
o2: T[] | undefined | null
|
||||||
|
) {
|
||||||
|
return (
|
||||||
|
!!o1 &&
|
||||||
|
!!o2 &&
|
||||||
|
o1.length === o2.length &&
|
||||||
|
o1.every((o) => {
|
||||||
|
return o2.find((oo) => o.stored_id === oo.stored_id);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ObjectListScrapeResult<
|
||||||
|
T extends IHasStoredID
|
||||||
|
> extends ScrapeResult<T[]> {
|
||||||
|
public constructor(
|
||||||
|
originalValue?: T[] | null,
|
||||||
|
newValue?: T[] | null,
|
||||||
|
useNewValue?: boolean
|
||||||
|
) {
|
||||||
|
super(originalValue, newValue, useNewValue, storedIDsEqual);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
export function hasScrapedValues(values: ScrapeResult<any>[]) {
|
export function hasScrapedValues(values: ScrapeResult<any>[]) {
|
||||||
return values.some((r) => r.scraped);
|
return values.some((r) => r.scraped);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue