Fix performer tagger (#1874)

* Fix performer tagger
This commit is contained in:
InfiniteTF
2021-10-25 01:49:37 +02:00
committed by GitHub
parent cb52eb798d
commit db1e5c63d0
4 changed files with 35 additions and 277 deletions

View File

@@ -118,7 +118,7 @@ const PerformerModal: React.FC<IPerformerModalProps> = ({
const performerData: GQL.PerformerCreateInput = {
name: performer.name ?? "",
aliases: performer.aliases,
gender: stringToGender(performer.gender ?? undefined),
gender: stringToGender(performer.gender ?? undefined, true),
birthdate: performer.birthdate,
ethnicity: performer.ethnicity,
eye_color: performer.eye_color,
@@ -162,7 +162,7 @@ const PerformerModal: React.FC<IPerformerModalProps> = ({
// handle exclusions
Object.keys(performerData).forEach((k) => {
if (excludedPerformerFields.includes(k) || excluded[k]) {
if (excluded[k]) {
(performerData as Record<string, unknown>)[k] = undefined;
}
});

View File

@@ -18,7 +18,6 @@ import { ConfigurationContext } from "src/hooks/Config";
import StashSearchResult from "./StashSearchResult";
import PerformerConfig from "./Config";
import { LOCAL_FORAGE_KEY, ITaggerConfig, initialConfig } from "../constants";
import { IStashBoxPerformer, selectPerformers } from "../utils";
import PerformerModal from "../PerformerModal";
import { useUpdatePerformer } from "../queries";
@@ -51,7 +50,7 @@ const PerformerTaggerList: React.FC<IPerformerTaggerListProps> = ({
const intl = useIntl();
const [loading, setLoading] = useState(false);
const [searchResults, setSearchResults] = useState<
Record<string, IStashBoxPerformer[]>
Record<string, GQL.ScrapedPerformerDataFragment[]>
>({});
const [searchErrors, setSearchErrors] = useState<
Record<string, string | undefined>
@@ -87,13 +86,13 @@ const PerformerTaggerList: React.FC<IPerformerTaggerListProps> = ({
>({});
const [loadingUpdate, setLoadingUpdate] = useState<string | undefined>();
const [modalPerformer, setModalPerformer] = useState<
IStashBoxPerformer | undefined
GQL.ScrapedPerformerDataFragment | undefined
>();
const doBoxSearch = (performerID: string, searchVal: string) => {
stashBoxPerformerQuery(searchVal, selectedEndpoint.index)
.then((queryData) => {
const s = selectPerformers(queryData.data?.scrapeSinglePerformer ?? []);
const s = queryData.data?.scrapeSinglePerformer ?? [];
setSearchResults({
...searchResults,
[performerID]: s,
@@ -130,13 +129,11 @@ const PerformerTaggerList: React.FC<IPerformerTaggerListProps> = ({
});
stashBoxPerformerQuery(stashID, endpointIndex)
.then((queryData) => {
const data = selectPerformers(
queryData.data?.scrapeSinglePerformer ?? []
);
const data = queryData.data?.scrapeSinglePerformer ?? [];
if (data.length > 0) {
setModalPerformer({
...data[0],
id: performerID,
stored_id: performerID,
});
}
})
@@ -168,20 +165,20 @@ const PerformerTaggerList: React.FC<IPerformerTaggerListProps> = ({
const updatePerformer = useUpdatePerformer();
const handlePerformerUpdate = async (input: GQL.PerformerCreateInput) => {
const performerData = modalPerformer;
setModalPerformer(undefined);
if (performerData?.id) {
const performerID = modalPerformer?.stored_id;
if (performerID) {
const updateData: GQL.PerformerUpdateInput = {
id: performerData.id,
...input,
id: performerID,
};
const res = await updatePerformer(updateData);
if (!res.data?.performerUpdate)
setError({
...error,
[performerData.id]: {
message: `Failed to save performer "${performerData.name}"`,
[performerID]: {
message: `Failed to save performer "${modalPerformer?.name}"`,
details:
res?.errors?.[0].message ===
"UNIQUE constraint failed: performers.checksum"

View File

@@ -2,13 +2,12 @@ import React, { useState } from "react";
import { Button } from "react-bootstrap";
import * as GQL from "src/core/generated-graphql";
import { IStashBoxPerformer } from "../utils";
import { useUpdatePerformer } from "../queries";
import PerformerModal from "../PerformerModal";
interface IStashSearchResultProps {
performer: GQL.SlimPerformerDataFragment;
stashboxPerformers: IStashBoxPerformer[];
stashboxPerformers: GQL.ScrapedPerformerDataFragment[];
endpoint: string;
onPerformerTagged: (
performer: Pick<GQL.SlimPerformerDataFragment, "id"> &
@@ -25,7 +24,7 @@ const StashSearchResult: React.FC<IStashSearchResultProps> = ({
endpoint,
}) => {
const [modalPerformer, setModalPerformer] = useState<
IStashBoxPerformer | undefined
GQL.ScrapedPerformerDataFragment | undefined
>();
const [saveState, setSaveState] = useState<string>("");
const [error, setError] = useState<{ message?: string; details?: string }>(
@@ -35,15 +34,13 @@ const StashSearchResult: React.FC<IStashSearchResultProps> = ({
const updatePerformer = useUpdatePerformer();
const handleSave = async (input: GQL.PerformerCreateInput) => {
const performerData = modalPerformer;
if (performerData?.id) {
setError({});
setSaveState("Saving performer");
setModalPerformer(undefined);
const updateData: GQL.PerformerUpdateInput = {
id: performerData.id,
...input,
id: performer.id,
};
const res = await updatePerformer(updateData);
@@ -59,17 +56,16 @@ const StashSearchResult: React.FC<IStashSearchResultProps> = ({
});
else onPerformerTagged(performer);
setSaveState("");
}
};
const performers = stashboxPerformers.map((p) => (
<Button
className="PerformerTagger-performer-search-item minimal col-6"
variant="link"
key={p.stash_id}
key={p.remote_site_id}
onClick={() => setModalPerformer(p)}
>
<img src={p.images[0]} alt="" className="PerformerTagger-thumb" />
<img src={(p.images ?? [])[0]} alt="" className="PerformerTagger-thumb" />
<span>{p.name}</span>
</Button>
));

View File

@@ -1,5 +1,4 @@
import * as GQL from "src/core/generated-graphql";
import { getCountryByISO } from "src/utils/country";
import { ParseMode } from "./constants";
const months = [
@@ -112,14 +111,6 @@ export function prepareQueryString(
return s.replace(/\./g, " ");
}
const toTitleCase = (phrase: string) => {
return phrase
.toLowerCase()
.split(" ")
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
.join(" ");
};
export const parsePath = (filePath: string) => {
const path = filePath.toLowerCase();
const isWin = /^([a-z]:|\\\\)/.test(path);
@@ -140,229 +131,3 @@ export const parsePath = (filePath: string) => {
return { paths, file, ext };
};
export interface IStashBoxFingerprint {
hash: string;
algorithm: string;
duration: number;
}
export interface IStashBoxPerformer {
id?: string;
stash_id: string;
name: string;
gender?: GQL.GenderEnum;
url?: string;
twitter?: string;
instagram?: string;
birthdate?: string;
ethnicity?: string;
country?: string;
eye_color?: string;
height?: string;
measurements?: string;
fake_tits?: string;
career_length?: string;
tattoos?: string;
piercings?: string;
aliases?: string;
images: string[];
details?: string;
death_date?: string;
hair_color?: string;
weight?: string;
}
export interface IStashBoxTag {
id?: string;
name: string;
}
export interface IStashBoxStudio {
id?: string;
stash_id: string;
name: string;
url?: string;
image?: string;
}
export interface IStashBoxScene {
stash_id: string;
title: string;
date: string;
duration: number;
details?: string;
url?: string;
studio: IStashBoxStudio;
images: string[];
tags: IStashBoxTag[];
performers: IStashBoxPerformer[];
fingerprints: IStashBoxFingerprint[];
}
const selectStudio = (studio: GQL.ScrapedStudio): IStashBoxStudio => ({
id: studio?.stored_id ?? undefined,
stash_id: studio.remote_site_id!,
name: studio.name,
url: studio.url ?? undefined,
});
const selectFingerprints = (
scene: GQL.ScrapedScene | null
): IStashBoxFingerprint[] => scene?.fingerprints ?? [];
const selectTags = (tags: GQL.ScrapedTag[]): IStashBoxTag[] =>
tags.map((t) => ({
id: t.stored_id ?? undefined,
name: t.name ?? "",
}));
export const selectPerformers = (
performers: GQL.ScrapedPerformer[]
): IStashBoxPerformer[] =>
performers.map((p) => ({
id: p.stored_id ?? undefined,
stash_id: p.remote_site_id!,
name: p.name ?? "",
gender: (p.gender ?? GQL.GenderEnum.Female) as GQL.GenderEnum,
url: p.url ?? undefined,
twitter: p.twitter ?? undefined,
instagram: p.instagram ?? undefined,
birthdate: p.birthdate ?? undefined,
ethnicity: p.ethnicity ? toTitleCase(p.ethnicity) : undefined,
country: getCountryByISO(p.country) ?? undefined,
eye_color: p.eye_color ? toTitleCase(p.eye_color) : undefined,
height: p.height ?? undefined,
measurements: p.measurements ?? undefined,
fake_tits: p.fake_tits ? toTitleCase(p.fake_tits) : undefined,
career_length: p.career_length ?? undefined,
tattoos: p.tattoos ? toTitleCase(p.tattoos) : undefined,
piercings: p.piercings ? toTitleCase(p.piercings) : undefined,
aliases: p.aliases ?? undefined,
images: p.images ?? [],
details: p.details ?? undefined,
death_date: p.death_date ?? undefined,
hair_color: p.hair_color ?? undefined,
}));
export const selectScenes = (
scenes?: (GQL.ScrapedScene | null)[]
): IStashBoxScene[] => {
const result = (scenes ?? [])
.filter((s) => s !== null)
.map(
(s) =>
({
stash_id: s?.remote_site_id!,
title: s?.title ?? "",
date: s?.date ?? "",
duration: s?.duration ?? 0,
details: s?.details,
url: s?.url,
images: s?.image ? [s.image] : [],
studio: selectStudio(s?.studio!),
fingerprints: selectFingerprints(s),
performers: selectPerformers(s?.performers ?? []),
tags: selectTags(s?.tags ?? []),
} as IStashBoxScene)
);
return result;
};
export const sortScenesByDuration = (
scenes: IStashBoxScene[],
targetDuration?: number
) =>
scenes.sort((a, b) => {
if (!targetDuration) return 0;
const aDur = [
a.duration,
...a.fingerprints.map((f) => f.duration),
].map((d) => Math.abs(d - targetDuration));
const bDur = [
b.duration,
...b.fingerprints.map((f) => f.duration),
].map((d) => Math.abs(d - targetDuration));
if (aDur.length > 0 && bDur.length === 0) return -1;
if (aDur.length === 0 && bDur.length > 0) return 1;
const aMatches = aDur.filter((match) => match <= 5);
const bMatches = bDur.filter((match) => match <= 5);
if (aMatches.length > 0 || bMatches.length > 0) {
if (aMatches.length > bMatches.length) return -1;
if (aMatches.length < bMatches.length) return 1;
return 0;
}
const aDiff = Math.min(...aDur);
const bDiff = Math.min(...bDur);
if (aDiff < bDiff) return -1;
if (aDiff > bDiff) return 1;
return 0;
});
export const filterPerformer = (
performer: IStashBoxPerformer,
excludedFields: string[]
) => {
const {
name,
aliases,
gender,
birthdate,
ethnicity,
country,
eye_color,
height,
measurements,
fake_tits,
career_length,
tattoos,
piercings,
} = performer;
return {
name: !excludedFields.includes("name") && name ? name : undefined,
aliases:
!excludedFields.includes("aliases") && aliases ? aliases : undefined,
gender: !excludedFields.includes("gender") && gender ? gender : undefined,
birthdate:
!excludedFields.includes("birthdate") && birthdate
? birthdate
: undefined,
ethnicity:
!excludedFields.includes("ethnicity") && ethnicity
? ethnicity
: undefined,
country:
!excludedFields.includes("country") && country ? country : undefined,
eye_color:
!excludedFields.includes("eye_color") && eye_color
? eye_color
: undefined,
height: !excludedFields.includes("height") && height ? height : undefined,
measurements:
!excludedFields.includes("measurements") && measurements
? measurements
: undefined,
fake_tits:
!excludedFields.includes("fake_tits") && fake_tits
? fake_tits
: undefined,
career_length:
!excludedFields.includes("career_length") && career_length
? career_length
: undefined,
tattoos:
!excludedFields.includes("tattoos") && tattoos ? tattoos : undefined,
piercings:
!excludedFields.includes("piercings") && piercings
? piercings
: undefined,
};
};