mirror of
https://github.com/stashapp/stash.git
synced 2025-12-18 04:44:37 +03:00
@@ -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;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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,41 +34,38 @@ 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);
|
||||
setError({});
|
||||
setSaveState("Saving performer");
|
||||
setModalPerformer(undefined);
|
||||
|
||||
const updateData: GQL.PerformerUpdateInput = {
|
||||
id: performerData.id,
|
||||
...input,
|
||||
};
|
||||
const updateData: GQL.PerformerUpdateInput = {
|
||||
...input,
|
||||
id: performer.id,
|
||||
};
|
||||
|
||||
const res = await updatePerformer(updateData);
|
||||
const res = await updatePerformer(updateData);
|
||||
|
||||
if (!res?.data?.performerUpdate)
|
||||
setError({
|
||||
message: `Failed to save performer "${performer.name}"`,
|
||||
details:
|
||||
res?.errors?.[0].message ===
|
||||
"UNIQUE constraint failed: performers.checksum"
|
||||
? "Name already exists"
|
||||
: res?.errors?.[0].message,
|
||||
});
|
||||
else onPerformerTagged(performer);
|
||||
setSaveState("");
|
||||
}
|
||||
if (!res?.data?.performerUpdate)
|
||||
setError({
|
||||
message: `Failed to save performer "${performer.name}"`,
|
||||
details:
|
||||
res?.errors?.[0].message ===
|
||||
"UNIQUE constraint failed: performers.checksum"
|
||||
? "Name already exists"
|
||||
: res?.errors?.[0].message,
|
||||
});
|
||||
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>
|
||||
));
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user