mirror of
https://github.com/stashapp/stash.git
synced 2025-12-18 21:04:37 +03:00
@@ -118,7 +118,7 @@ const PerformerModal: React.FC<IPerformerModalProps> = ({
|
|||||||
const performerData: GQL.PerformerCreateInput = {
|
const performerData: GQL.PerformerCreateInput = {
|
||||||
name: performer.name ?? "",
|
name: performer.name ?? "",
|
||||||
aliases: performer.aliases,
|
aliases: performer.aliases,
|
||||||
gender: stringToGender(performer.gender ?? undefined),
|
gender: stringToGender(performer.gender ?? undefined, true),
|
||||||
birthdate: performer.birthdate,
|
birthdate: performer.birthdate,
|
||||||
ethnicity: performer.ethnicity,
|
ethnicity: performer.ethnicity,
|
||||||
eye_color: performer.eye_color,
|
eye_color: performer.eye_color,
|
||||||
@@ -162,7 +162,7 @@ const PerformerModal: React.FC<IPerformerModalProps> = ({
|
|||||||
|
|
||||||
// handle exclusions
|
// handle exclusions
|
||||||
Object.keys(performerData).forEach((k) => {
|
Object.keys(performerData).forEach((k) => {
|
||||||
if (excludedPerformerFields.includes(k) || excluded[k]) {
|
if (excluded[k]) {
|
||||||
(performerData as Record<string, unknown>)[k] = undefined;
|
(performerData as Record<string, unknown>)[k] = undefined;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ import { ConfigurationContext } from "src/hooks/Config";
|
|||||||
import StashSearchResult from "./StashSearchResult";
|
import StashSearchResult from "./StashSearchResult";
|
||||||
import PerformerConfig from "./Config";
|
import PerformerConfig from "./Config";
|
||||||
import { LOCAL_FORAGE_KEY, ITaggerConfig, initialConfig } from "../constants";
|
import { LOCAL_FORAGE_KEY, ITaggerConfig, initialConfig } from "../constants";
|
||||||
import { IStashBoxPerformer, selectPerformers } from "../utils";
|
|
||||||
import PerformerModal from "../PerformerModal";
|
import PerformerModal from "../PerformerModal";
|
||||||
import { useUpdatePerformer } from "../queries";
|
import { useUpdatePerformer } from "../queries";
|
||||||
|
|
||||||
@@ -51,7 +50,7 @@ const PerformerTaggerList: React.FC<IPerformerTaggerListProps> = ({
|
|||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [searchResults, setSearchResults] = useState<
|
const [searchResults, setSearchResults] = useState<
|
||||||
Record<string, IStashBoxPerformer[]>
|
Record<string, GQL.ScrapedPerformerDataFragment[]>
|
||||||
>({});
|
>({});
|
||||||
const [searchErrors, setSearchErrors] = useState<
|
const [searchErrors, setSearchErrors] = useState<
|
||||||
Record<string, string | undefined>
|
Record<string, string | undefined>
|
||||||
@@ -87,13 +86,13 @@ const PerformerTaggerList: React.FC<IPerformerTaggerListProps> = ({
|
|||||||
>({});
|
>({});
|
||||||
const [loadingUpdate, setLoadingUpdate] = useState<string | undefined>();
|
const [loadingUpdate, setLoadingUpdate] = useState<string | undefined>();
|
||||||
const [modalPerformer, setModalPerformer] = useState<
|
const [modalPerformer, setModalPerformer] = useState<
|
||||||
IStashBoxPerformer | undefined
|
GQL.ScrapedPerformerDataFragment | undefined
|
||||||
>();
|
>();
|
||||||
|
|
||||||
const doBoxSearch = (performerID: string, searchVal: string) => {
|
const doBoxSearch = (performerID: string, searchVal: string) => {
|
||||||
stashBoxPerformerQuery(searchVal, selectedEndpoint.index)
|
stashBoxPerformerQuery(searchVal, selectedEndpoint.index)
|
||||||
.then((queryData) => {
|
.then((queryData) => {
|
||||||
const s = selectPerformers(queryData.data?.scrapeSinglePerformer ?? []);
|
const s = queryData.data?.scrapeSinglePerformer ?? [];
|
||||||
setSearchResults({
|
setSearchResults({
|
||||||
...searchResults,
|
...searchResults,
|
||||||
[performerID]: s,
|
[performerID]: s,
|
||||||
@@ -130,13 +129,11 @@ const PerformerTaggerList: React.FC<IPerformerTaggerListProps> = ({
|
|||||||
});
|
});
|
||||||
stashBoxPerformerQuery(stashID, endpointIndex)
|
stashBoxPerformerQuery(stashID, endpointIndex)
|
||||||
.then((queryData) => {
|
.then((queryData) => {
|
||||||
const data = selectPerformers(
|
const data = queryData.data?.scrapeSinglePerformer ?? [];
|
||||||
queryData.data?.scrapeSinglePerformer ?? []
|
|
||||||
);
|
|
||||||
if (data.length > 0) {
|
if (data.length > 0) {
|
||||||
setModalPerformer({
|
setModalPerformer({
|
||||||
...data[0],
|
...data[0],
|
||||||
id: performerID,
|
stored_id: performerID,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -168,20 +165,20 @@ const PerformerTaggerList: React.FC<IPerformerTaggerListProps> = ({
|
|||||||
const updatePerformer = useUpdatePerformer();
|
const updatePerformer = useUpdatePerformer();
|
||||||
|
|
||||||
const handlePerformerUpdate = async (input: GQL.PerformerCreateInput) => {
|
const handlePerformerUpdate = async (input: GQL.PerformerCreateInput) => {
|
||||||
const performerData = modalPerformer;
|
|
||||||
setModalPerformer(undefined);
|
setModalPerformer(undefined);
|
||||||
if (performerData?.id) {
|
const performerID = modalPerformer?.stored_id;
|
||||||
|
if (performerID) {
|
||||||
const updateData: GQL.PerformerUpdateInput = {
|
const updateData: GQL.PerformerUpdateInput = {
|
||||||
id: performerData.id,
|
|
||||||
...input,
|
...input,
|
||||||
|
id: performerID,
|
||||||
};
|
};
|
||||||
|
|
||||||
const res = await updatePerformer(updateData);
|
const res = await updatePerformer(updateData);
|
||||||
if (!res.data?.performerUpdate)
|
if (!res.data?.performerUpdate)
|
||||||
setError({
|
setError({
|
||||||
...error,
|
...error,
|
||||||
[performerData.id]: {
|
[performerID]: {
|
||||||
message: `Failed to save performer "${performerData.name}"`,
|
message: `Failed to save performer "${modalPerformer?.name}"`,
|
||||||
details:
|
details:
|
||||||
res?.errors?.[0].message ===
|
res?.errors?.[0].message ===
|
||||||
"UNIQUE constraint failed: performers.checksum"
|
"UNIQUE constraint failed: performers.checksum"
|
||||||
|
|||||||
@@ -2,13 +2,12 @@ import React, { useState } from "react";
|
|||||||
import { Button } from "react-bootstrap";
|
import { Button } from "react-bootstrap";
|
||||||
|
|
||||||
import * as GQL from "src/core/generated-graphql";
|
import * as GQL from "src/core/generated-graphql";
|
||||||
import { IStashBoxPerformer } from "../utils";
|
|
||||||
import { useUpdatePerformer } from "../queries";
|
import { useUpdatePerformer } from "../queries";
|
||||||
import PerformerModal from "../PerformerModal";
|
import PerformerModal from "../PerformerModal";
|
||||||
|
|
||||||
interface IStashSearchResultProps {
|
interface IStashSearchResultProps {
|
||||||
performer: GQL.SlimPerformerDataFragment;
|
performer: GQL.SlimPerformerDataFragment;
|
||||||
stashboxPerformers: IStashBoxPerformer[];
|
stashboxPerformers: GQL.ScrapedPerformerDataFragment[];
|
||||||
endpoint: string;
|
endpoint: string;
|
||||||
onPerformerTagged: (
|
onPerformerTagged: (
|
||||||
performer: Pick<GQL.SlimPerformerDataFragment, "id"> &
|
performer: Pick<GQL.SlimPerformerDataFragment, "id"> &
|
||||||
@@ -25,7 +24,7 @@ const StashSearchResult: React.FC<IStashSearchResultProps> = ({
|
|||||||
endpoint,
|
endpoint,
|
||||||
}) => {
|
}) => {
|
||||||
const [modalPerformer, setModalPerformer] = useState<
|
const [modalPerformer, setModalPerformer] = useState<
|
||||||
IStashBoxPerformer | undefined
|
GQL.ScrapedPerformerDataFragment | undefined
|
||||||
>();
|
>();
|
||||||
const [saveState, setSaveState] = useState<string>("");
|
const [saveState, setSaveState] = useState<string>("");
|
||||||
const [error, setError] = useState<{ message?: string; details?: string }>(
|
const [error, setError] = useState<{ message?: string; details?: string }>(
|
||||||
@@ -35,41 +34,38 @@ const StashSearchResult: React.FC<IStashSearchResultProps> = ({
|
|||||||
const updatePerformer = useUpdatePerformer();
|
const updatePerformer = useUpdatePerformer();
|
||||||
|
|
||||||
const handleSave = async (input: GQL.PerformerCreateInput) => {
|
const handleSave = async (input: GQL.PerformerCreateInput) => {
|
||||||
const performerData = modalPerformer;
|
setError({});
|
||||||
if (performerData?.id) {
|
setSaveState("Saving performer");
|
||||||
setError({});
|
setModalPerformer(undefined);
|
||||||
setSaveState("Saving performer");
|
|
||||||
setModalPerformer(undefined);
|
|
||||||
|
|
||||||
const updateData: GQL.PerformerUpdateInput = {
|
const updateData: GQL.PerformerUpdateInput = {
|
||||||
id: performerData.id,
|
...input,
|
||||||
...input,
|
id: performer.id,
|
||||||
};
|
};
|
||||||
|
|
||||||
const res = await updatePerformer(updateData);
|
const res = await updatePerformer(updateData);
|
||||||
|
|
||||||
if (!res?.data?.performerUpdate)
|
if (!res?.data?.performerUpdate)
|
||||||
setError({
|
setError({
|
||||||
message: `Failed to save performer "${performer.name}"`,
|
message: `Failed to save performer "${performer.name}"`,
|
||||||
details:
|
details:
|
||||||
res?.errors?.[0].message ===
|
res?.errors?.[0].message ===
|
||||||
"UNIQUE constraint failed: performers.checksum"
|
"UNIQUE constraint failed: performers.checksum"
|
||||||
? "Name already exists"
|
? "Name already exists"
|
||||||
: res?.errors?.[0].message,
|
: res?.errors?.[0].message,
|
||||||
});
|
});
|
||||||
else onPerformerTagged(performer);
|
else onPerformerTagged(performer);
|
||||||
setSaveState("");
|
setSaveState("");
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const performers = stashboxPerformers.map((p) => (
|
const performers = stashboxPerformers.map((p) => (
|
||||||
<Button
|
<Button
|
||||||
className="PerformerTagger-performer-search-item minimal col-6"
|
className="PerformerTagger-performer-search-item minimal col-6"
|
||||||
variant="link"
|
variant="link"
|
||||||
key={p.stash_id}
|
key={p.remote_site_id}
|
||||||
onClick={() => setModalPerformer(p)}
|
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>
|
<span>{p.name}</span>
|
||||||
</Button>
|
</Button>
|
||||||
));
|
));
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import * as GQL from "src/core/generated-graphql";
|
import * as GQL from "src/core/generated-graphql";
|
||||||
import { getCountryByISO } from "src/utils/country";
|
|
||||||
import { ParseMode } from "./constants";
|
import { ParseMode } from "./constants";
|
||||||
|
|
||||||
const months = [
|
const months = [
|
||||||
@@ -112,14 +111,6 @@ export function prepareQueryString(
|
|||||||
return s.replace(/\./g, " ");
|
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) => {
|
export const parsePath = (filePath: string) => {
|
||||||
const path = filePath.toLowerCase();
|
const path = filePath.toLowerCase();
|
||||||
const isWin = /^([a-z]:|\\\\)/.test(path);
|
const isWin = /^([a-z]:|\\\\)/.test(path);
|
||||||
@@ -140,229 +131,3 @@ export const parsePath = (filePath: string) => {
|
|||||||
|
|
||||||
return { paths, file, ext };
|
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