mirror of
https://github.com/stashapp/stash.git
synced 2025-12-18 12:54:38 +03:00
Fix outstanding tagger issues (#912)
* Fix potential image errors * Fix issue preventing favoriting of tagged performers * Add error handling in case of network issues * Show individual search errors * Unset scene results if query fails * Don't abort scene submission if scene id isn't found
This commit is contained in:
@@ -106,7 +106,13 @@ export const Performer: React.FC = () => {
|
||||
try {
|
||||
if (!isNew) {
|
||||
await updatePerformer({
|
||||
variables: performerInput as GQL.PerformerUpdateInput,
|
||||
variables: {
|
||||
...performerInput,
|
||||
stash_ids: (performerInput?.stash_ids ?? []).map((s) => ({
|
||||
endpoint: s.endpoint,
|
||||
stash_id: s.stash_id,
|
||||
})),
|
||||
} as GQL.PerformerUpdateInput,
|
||||
});
|
||||
if (performerInput.image) {
|
||||
// Refetch image to bust browser cache
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useCallback, useState } from "react";
|
||||
import React, { useState, useReducer } from "react";
|
||||
import cx from "classnames";
|
||||
import { Button } from "react-bootstrap";
|
||||
import { uniq } from "lodash";
|
||||
@@ -64,6 +64,16 @@ interface IStashSearchResultProps {
|
||||
queueFingerprintSubmission: (sceneId: string, endpoint: string) => void;
|
||||
}
|
||||
|
||||
interface IPerformerReducerAction {
|
||||
id: string;
|
||||
data: PerformerOperation;
|
||||
}
|
||||
|
||||
const performerReducer = (
|
||||
state: Record<string, PerformerOperation>,
|
||||
action: IPerformerReducerAction
|
||||
) => ({ ...state, [action.id]: action.data });
|
||||
|
||||
const StashSearchResult: React.FC<IStashSearchResultProps> = ({
|
||||
scene,
|
||||
stashScene,
|
||||
@@ -78,9 +88,7 @@ const StashSearchResult: React.FC<IStashSearchResultProps> = ({
|
||||
queueFingerprintSubmission,
|
||||
}) => {
|
||||
const [studio, setStudio] = useState<StudioOperation>();
|
||||
const [performers, setPerformers] = useState<
|
||||
Record<string, PerformerOperation>
|
||||
>({});
|
||||
const [performers, dispatch] = useReducer(performerReducer, {});
|
||||
const [saveState, setSaveState] = useState<string>("");
|
||||
const [error, setError] = useState<{ message?: string; details?: string }>(
|
||||
{}
|
||||
@@ -96,11 +104,10 @@ const StashSearchResult: React.FC<IStashSearchResultProps> = ({
|
||||
});
|
||||
const { data: allTags } = GQL.useAllTagsForFilterQuery();
|
||||
|
||||
const setPerformer = useCallback(
|
||||
(performerData: PerformerOperation, performerID: string) =>
|
||||
setPerformers({ ...performers, [performerID]: performerData }),
|
||||
[performers]
|
||||
);
|
||||
const setPerformer = (
|
||||
performerData: PerformerOperation,
|
||||
performerID: string
|
||||
) => dispatch({ id: performerID, data: performerData });
|
||||
|
||||
const handleSave = async () => {
|
||||
setError({});
|
||||
@@ -232,7 +239,8 @@ const StashSearchResult: React.FC<IStashSearchResultProps> = ({
|
||||
});
|
||||
if (img.status === 200) {
|
||||
const blob = await img.blob();
|
||||
imgData = await blobToBase64(blob);
|
||||
// Sanity check on image size since bad images will fail
|
||||
if (blob.size > 10000) imgData = await blobToBase64(blob);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -76,12 +76,16 @@ const TaggerList: React.FC<ITaggerListProps> = ({
|
||||
queueFingerprintSubmission,
|
||||
clearSubmissionQueue,
|
||||
}) => {
|
||||
const [fingerprintError, setFingerprintError] = useState("");
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [queryString, setQueryString] = useState<Record<string, string>>({});
|
||||
|
||||
const [searchResults, setSearchResults] = useState<
|
||||
Record<string, IStashBoxScene[]>
|
||||
>({});
|
||||
const [searchErrors, setSearchErrors] = useState<
|
||||
Record<string, string | undefined>
|
||||
>({});
|
||||
const [selectedResult, setSelectedResult] = useState<
|
||||
Record<string, number>
|
||||
>();
|
||||
@@ -96,14 +100,29 @@ const TaggerList: React.FC<ITaggerListProps> = ({
|
||||
config.fingerprintQueue[selectedEndpoint.endpoint] ?? [];
|
||||
|
||||
const doBoxSearch = (sceneID: string, searchVal: string) => {
|
||||
stashBoxQuery(searchVal, selectedEndpoint.index).then((queryData) => {
|
||||
const s = selectScenes(queryData.data?.queryStashBoxScene);
|
||||
setSearchResults({
|
||||
...searchResults,
|
||||
[sceneID]: s,
|
||||
stashBoxQuery(searchVal, selectedEndpoint.index)
|
||||
.then((queryData) => {
|
||||
const s = selectScenes(queryData.data?.queryStashBoxScene);
|
||||
setSearchResults({
|
||||
...searchResults,
|
||||
[sceneID]: s,
|
||||
});
|
||||
setSearchErrors({
|
||||
...searchErrors,
|
||||
[sceneID]: undefined,
|
||||
});
|
||||
setLoading(false);
|
||||
})
|
||||
.catch(() => {
|
||||
setLoading(false);
|
||||
// Destructure to remove existing result
|
||||
const { [sceneID]: unassign, ...results } = searchResults;
|
||||
setSearchResults(results);
|
||||
setSearchErrors({
|
||||
...searchErrors,
|
||||
[sceneID]: "Network Error",
|
||||
});
|
||||
});
|
||||
setLoading(false);
|
||||
});
|
||||
|
||||
setLoading(true);
|
||||
};
|
||||
@@ -113,9 +132,13 @@ const TaggerList: React.FC<ITaggerListProps> = ({
|
||||
{ loading: submittingFingerprints },
|
||||
] = GQL.useSubmitStashBoxFingerprintsMutation({
|
||||
onCompleted: (result) => {
|
||||
setFingerprintError("");
|
||||
if (result.submitStashBoxFingerprints)
|
||||
clearSubmissionQueue(selectedEndpoint.endpoint);
|
||||
},
|
||||
onError: () => {
|
||||
setFingerprintError("Network Error");
|
||||
},
|
||||
});
|
||||
|
||||
const handleFingerprintSubmission = () => {
|
||||
@@ -141,20 +164,36 @@ const TaggerList: React.FC<ITaggerListProps> = ({
|
||||
const newFingerprints = { ...fingerprints };
|
||||
|
||||
const sceneIDs = scenes
|
||||
.filter(
|
||||
(s) => fingerprints[s.id] === undefined && s.stash_ids.length === 0
|
||||
)
|
||||
.filter((s) => s.stash_ids.length === 0)
|
||||
.map((s) => s.id);
|
||||
|
||||
const results = await stashBoxBatchQuery(sceneIDs, selectedEndpoint.index);
|
||||
const results = await stashBoxBatchQuery(
|
||||
sceneIDs,
|
||||
selectedEndpoint.index
|
||||
).catch(() => {
|
||||
setLoadingFingerprints(false);
|
||||
setFingerprintError("Network Error");
|
||||
});
|
||||
|
||||
if (!results) return;
|
||||
|
||||
// clear search errors
|
||||
setSearchErrors({});
|
||||
|
||||
selectScenes(results.data?.queryStashBoxScene).forEach((scene) => {
|
||||
scene.fingerprints?.forEach((f) => {
|
||||
newFingerprints[f.hash] = scene;
|
||||
});
|
||||
});
|
||||
|
||||
// Null any ids that are still undefined since it means they weren't found
|
||||
sceneIDs.forEach((id) => {
|
||||
newFingerprints[id] = newFingerprints[id] ?? null;
|
||||
});
|
||||
|
||||
setFingerprints(newFingerprints);
|
||||
setLoadingFingerprints(false);
|
||||
setFingerprintError("");
|
||||
};
|
||||
|
||||
const canFingerprintSearch = () =>
|
||||
@@ -164,7 +203,10 @@ const TaggerList: React.FC<ITaggerListProps> = ({
|
||||
|
||||
const getFingerprintCount = () => {
|
||||
const count = scenes.filter(
|
||||
(s) => s.stash_ids.length === 0 && fingerprints[s.id]
|
||||
(s) =>
|
||||
s.stash_ids.length === 0 &&
|
||||
((s.checksum && fingerprints[s.checksum]) ||
|
||||
(s.oshash && fingerprints[s.oshash]))
|
||||
).length;
|
||||
return `${count > 0 ? count : "No"} new fingerprint matches found`;
|
||||
};
|
||||
@@ -246,11 +288,13 @@ const TaggerList: React.FC<ITaggerListProps> = ({
|
||||
}
|
||||
|
||||
let searchResult;
|
||||
if (searchResults[scene.id]?.length === 0)
|
||||
if (searchErrors[scene.id]) {
|
||||
searchResult = (
|
||||
<div className="text-danger font-weight-bold">No results found.</div>
|
||||
<div className="text-danger font-weight-bold">
|
||||
{searchErrors[scene.id]}
|
||||
</div>
|
||||
);
|
||||
else if (fingerprintMatch && !isTagged && !hasStashIDs) {
|
||||
} else if (fingerprintMatch && !isTagged && !hasStashIDs) {
|
||||
searchResult = (
|
||||
<StashSearchResult
|
||||
showMales={config.showMales}
|
||||
@@ -266,10 +310,17 @@ const TaggerList: React.FC<ITaggerListProps> = ({
|
||||
queueFingerprintSubmission={queueFingerprintSubmission}
|
||||
/>
|
||||
);
|
||||
} else if (searchResults[scene.id] && !isTagged && !fingerprintMatch) {
|
||||
} else if (
|
||||
searchResults[scene.id]?.length > 0 &&
|
||||
!isTagged &&
|
||||
!fingerprintMatch
|
||||
) {
|
||||
searchResult = (
|
||||
<ul className="pl-0 mt-4">
|
||||
{sortScenesByDuration(searchResults[scene.id]).map(
|
||||
{sortScenesByDuration(
|
||||
searchResults[scene.id],
|
||||
scene.file.duration ?? undefined
|
||||
).map(
|
||||
(sceneResult, i) =>
|
||||
sceneResult && (
|
||||
<StashSearchResult
|
||||
@@ -295,6 +346,10 @@ const TaggerList: React.FC<ITaggerListProps> = ({
|
||||
)}
|
||||
</ul>
|
||||
);
|
||||
} else if (searchResults[scene.id]?.length === 0) {
|
||||
searchResult = (
|
||||
<div className="text-danger font-weight-bold">No results found.</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -320,14 +375,15 @@ const TaggerList: React.FC<ITaggerListProps> = ({
|
||||
|
||||
return (
|
||||
<Card className="tagger-table">
|
||||
<div className="tagger-table-header row mb-4">
|
||||
<div className="tagger-table-header row flex-nowrap mb-4 align-items-center">
|
||||
<div className="col-md-6">
|
||||
<b>Path</b>
|
||||
</div>
|
||||
<div className="col-md-2">
|
||||
<b>Query</b>
|
||||
</div>
|
||||
<div className="ml-auto mr-2">
|
||||
<b className="ml-auto mr-2 text-danger">{fingerprintError}</b>
|
||||
<div className="mr-2">
|
||||
{fingerprintQueue.length > 0 && (
|
||||
<Button
|
||||
onClick={handleFingerprintSubmission}
|
||||
|
||||
@@ -159,9 +159,9 @@ export const sortScenesByDuration = (
|
||||
) =>
|
||||
scenes.sort((a, b) => {
|
||||
const adur =
|
||||
a?.duration ?? a?.fingerprints.map((f) => f.duration)?.[0] ?? null;
|
||||
a?.duration || (a?.fingerprints.map((f) => f.duration)?.[0] ?? null);
|
||||
const bdur =
|
||||
b?.duration ?? b?.fingerprints.map((f) => f.duration)?.[0] ?? null;
|
||||
b?.duration || (b?.fingerprints.map((f) => f.duration)?.[0] ?? null);
|
||||
if (!adur && !bdur) return 0;
|
||||
if (adur && !bdur) return -1;
|
||||
if (!adur && bdur) return 1;
|
||||
|
||||
Reference in New Issue
Block a user