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:
InfiniteTF
2020-11-04 22:28:58 +01:00
committed by GitHub
parent 66c7af62f6
commit 9ec762ae9a
9 changed files with 131 additions and 54 deletions

View File

@@ -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

View File

@@ -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);
}
}

View File

@@ -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}

View File

@@ -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;