From 4190386c29886e8aa257ddae1faa3a1e85bac53c Mon Sep 17 00:00:00 2001 From: Infinite Date: Mon, 21 Dec 2020 23:46:29 +0000 Subject: [PATCH 1/4] Tagger fixes --- .../src/components/Changelog/versions/v050.md | 1 + ui/v2.5/src/components/Shared/styles.scss | 2 +- ui/v2.5/src/components/Tagger/Config.tsx | 80 +++---------------- .../components/Tagger/StashSearchResult.tsx | 17 ++-- ui/v2.5/src/components/Tagger/Tagger.tsx | 36 +++++---- ui/v2.5/src/components/Tagger/constants.ts | 42 ++++++++++ ui/v2.5/src/hooks/LocalForage.ts | 18 +++-- ui/v2.5/src/hooks/index.ts | 2 +- 8 files changed, 98 insertions(+), 100 deletions(-) create mode 100644 ui/v2.5/src/components/Tagger/constants.ts diff --git a/ui/v2.5/src/components/Changelog/versions/v050.md b/ui/v2.5/src/components/Changelog/versions/v050.md index f79c0a8723a..d01bf181411 100644 --- a/ui/v2.5/src/components/Changelog/versions/v050.md +++ b/ui/v2.5/src/components/Changelog/versions/v050.md @@ -20,3 +20,4 @@ ### 🐛 Bug fixes * Corrected file sizes on 32bit platforms * Fixed login redirect to remember the current page. +* Fixed scene tagger config saving diff --git a/ui/v2.5/src/components/Shared/styles.scss b/ui/v2.5/src/components/Shared/styles.scss index 02bacdef5a4..2e1efc0ffcc 100644 --- a/ui/v2.5/src/components/Shared/styles.scss +++ b/ui/v2.5/src/components/Shared/styles.scss @@ -16,7 +16,7 @@ } &.inline { - display: inline-block; + display: inline; height: auto; margin-left: 0.5rem; } diff --git a/ui/v2.5/src/components/Tagger/Config.tsx b/ui/v2.5/src/components/Tagger/Config.tsx index f07c08d1ff9..f2ff4be6246 100644 --- a/ui/v2.5/src/components/Tagger/Config.tsx +++ b/ui/v2.5/src/components/Tagger/Config.tsx @@ -1,4 +1,4 @@ -import React, { Dispatch, useEffect, useState } from "react"; +import React, { Dispatch, useRef } from "react"; import { Badge, Button, @@ -8,51 +8,9 @@ import { InputGroup, } from "react-bootstrap"; import { Icon } from "src/components/Shared"; -import localForage from "localforage"; - import { useConfiguration } from "src/core/StashService"; -const DEFAULT_BLACKLIST = [ - "\\sXXX\\s", - "1080p", - "720p", - "2160p", - "KTR", - "RARBG", - "\\scom\\s", - "\\[", - "\\]", -]; - -export const initialConfig: ITaggerConfig = { - blacklist: DEFAULT_BLACKLIST, - showMales: false, - mode: "auto", - setCoverImage: true, - setTags: false, - tagOperation: "merge", - fingerprintQueue: {}, -}; - -export type ParseMode = "auto" | "filename" | "dir" | "path" | "metadata"; -const ModeDesc = { - auto: "Uses metadata if present, or filename", - metadata: "Only uses metadata", - filename: "Only uses filename", - dir: "Only uses parent directory of video file", - path: "Uses entire file path", -}; - -export interface ITaggerConfig { - blacklist: string[]; - showMales: boolean; - mode: ParseMode; - setCoverImage: boolean; - setTags: boolean; - tagOperation: string; - selectedEndpoint?: string; - fingerprintQueue: Record; -} +import { ITaggerConfig, ParseMode, ModeDesc } from './constants'; interface IConfigProps { show: boolean; @@ -62,26 +20,7 @@ interface IConfigProps { const Config: React.FC = ({ show, config, setConfig }) => { const stashConfig = useConfiguration(); - const [blacklistInput, setBlacklistInput] = useState(""); - - useEffect(() => { - localForage.getItem("tagger").then((data) => { - setConfig({ - blacklist: data?.blacklist ?? DEFAULT_BLACKLIST, - showMales: data?.showMales ?? false, - mode: data?.mode ?? "auto", - setCoverImage: data?.setCoverImage ?? true, - setTags: data?.setTags ?? false, - tagOperation: data?.tagOperation ?? "merge", - selectedEndpoint: data?.selectedEndpoint, - fingerprintQueue: data?.fingerprintQueue ?? {}, - }); - }); - }, [setConfig]); - - useEffect(() => { - localForage.setItem("tagger", config); - }, [config]); + const blacklistRef = useRef(null); const handleInstanceSelect = (e: React.ChangeEvent) => { const selectedEndpoint = e.currentTarget.value; @@ -102,11 +41,15 @@ const Config: React.FC = ({ show, config, setConfig }) => { }; const handleBlacklistAddition = () => { + if (!blacklistRef.current) + return; + + const input = blacklistRef.current.value; setConfig({ ...config, - blacklist: [...config.blacklist, blacklistInput], + blacklist: [...config.blacklist, input], }); - setBlacklistInput(""); + blacklistRef.current.value = ''; }; const stashBoxes = stashConfig.data?.configuration.general.stashBoxes ?? []; @@ -206,10 +149,7 @@ const Config: React.FC = ({ show, config, setConfig }) => { ) => - setBlacklistInput(e.currentTarget.value) - } + ref={blacklistRef} /> diff --git a/ui/v2.5/src/components/Tagger/StashSearchResult.tsx b/ui/v2.5/src/components/Tagger/StashSearchResult.tsx index 7803db37a73..022d6229a71 100755 --- a/ui/v2.5/src/components/Tagger/StashSearchResult.tsx +++ b/ui/v2.5/src/components/Tagger/StashSearchResult.tsx @@ -104,8 +104,16 @@ const StashSearchResult: React.FC = ({ const updatePerformerStashID = useUpdatePerformerStashID(); const updateStudioStashID = useUpdateStudioStashID(); const [updateScene] = GQL.useSceneUpdateMutation({ - onError: (errors) => errors, + onError: (e) => { + const message = e.message === "invalid JPEG format: short Huffman data" ? + "Failed to save scene due to corrupted cover image" : "Failed to save scene"; + setError({ + message, + details: e.message, + }); + } }); + const { data: allTags } = GQL.useAllTagsForFilterQuery(); const setPerformer = ( @@ -302,12 +310,7 @@ const StashSearchResult: React.FC = ({ }, }); - if (!sceneUpdateResult?.data?.sceneUpdate) { - setError({ - message: "Failed to save scene", - details: sceneUpdateResult?.errors?.[0].message, - }); - } else if (sceneUpdateResult.data?.sceneUpdate) + if (sceneUpdateResult?.data?.sceneUpdate) setScene(sceneUpdateResult.data.sceneUpdate); queueFingerprintSubmission(stashScene.id, endpoint); diff --git a/ui/v2.5/src/components/Tagger/Tagger.tsx b/ui/v2.5/src/components/Tagger/Tagger.tsx index fd9354d1664..74da5d59b7c 100755 --- a/ui/v2.5/src/components/Tagger/Tagger.tsx +++ b/ui/v2.5/src/components/Tagger/Tagger.tsx @@ -3,6 +3,7 @@ import { Button, Card, Form, InputGroup } from "react-bootstrap"; import { Link } from "react-router-dom"; import { HashLink } from "react-router-hash-link"; import { ScenePreview } from "src/components/Scenes/SceneCard"; +import { useLocalForage } from "src/hooks"; import * as GQL from "src/core/generated-graphql"; import { LoadingIndicator, TruncatedText } from "src/components/Shared"; @@ -14,7 +15,8 @@ import { import { Manual } from "src/components/Help/Manual"; import StashSearchResult from "./StashSearchResult"; -import Config, { ITaggerConfig, initialConfig, ParseMode } from "./Config"; +import Config from "./Config"; +import { LOCAL_FORAGE_KEY, ITaggerConfig, ParseMode } from './constants'; import { parsePath, selectScenes, @@ -23,6 +25,14 @@ import { } from "./utils"; const dateRegex = /\.(\d\d)\.(\d\d)\.(\d\d)\./; +const parseDate = (input: string): string => { + const date = s.match(dateRegex); + if (date) { + s = s.replace(date[0], ` 20${date[1]}-${date[2]}-${date[3]} `); + } + return input; +} + function prepareQueryString( scene: Partial, paths: string[], @@ -55,12 +65,8 @@ function prepareQueryString( blacklist.forEach((b) => { s = s.replace(new RegExp(b, "i"), ""); }); - const date = s.match(dateRegex); - s = s.replace(/-/g, " "); - if (date) { - s = s.replace(date[0], ` 20${date[1]}-${date[2]}-${date[3]} `); - } - return s.replace(/\./g, " "); + s = parseDate(s); + return s.replace(/[-\.]/g, " "); } interface ITaggerListProps { @@ -248,6 +254,9 @@ const TaggerList: React.FC = ({ } else if (!isTagged && !hasStashIDs) { mainContent = ( + + Query + = ({ return (
-
- Scene -
-
- Query -
{fingerprintError}
{fingerprintQueue.length > 0 && ( @@ -460,10 +463,13 @@ interface ITaggerProps { export const Tagger: React.FC = ({ scenes }) => { const stashConfig = useConfiguration(); - const [config, setConfig] = useState(initialConfig); + const [{ data: config }, setConfig] = useLocalForage(LOCAL_FORAGE_KEY); const [showConfig, setShowConfig] = useState(false); const [showManual, setShowManual] = useState(false); + if (!config) + return ; + const savedEndpointIndex = stashConfig.data?.configuration.general.stashBoxes.findIndex( (s) => s.endpoint === config.selectedEndpoint @@ -503,7 +509,7 @@ export const Tagger: React.FC = ({ scenes }) => { onClose={() => setShowManual(false)} defaultActiveTab="Tagger.md" /> -
+
{selectedEndpointIndex !== -1 && selectedEndpoint ? ( <>
diff --git a/ui/v2.5/src/components/Tagger/constants.ts b/ui/v2.5/src/components/Tagger/constants.ts new file mode 100644 index 00000000000..d065f986745 --- /dev/null +++ b/ui/v2.5/src/components/Tagger/constants.ts @@ -0,0 +1,42 @@ +export const LOCAL_FORAGE_KEY = "tagger"; +export const DEFAULT_BLACKLIST = [ + "\\sXXX\\s", + "1080p", + "720p", + "2160p", + "KTR", + "RARBG", + "\\scom\\s", + "\\[", + "\\]", +]; + +export const initialConfig: ITaggerConfig = { + blacklist: DEFAULT_BLACKLIST, + showMales: false, + mode: "auto", + setCoverImage: true, + setTags: false, + tagOperation: "merge", + fingerprintQueue: {}, +}; + +export type ParseMode = "auto" | "filename" | "dir" | "path" | "metadata"; +export const ModeDesc = { + auto: "Uses metadata if present, or filename", + metadata: "Only uses metadata", + filename: "Only uses filename", + dir: "Only uses parent directory of video file", + path: "Uses entire file path", +}; + +export interface ITaggerConfig { + blacklist: string[]; + showMales: boolean; + mode: ParseMode; + setCoverImage: boolean; + setTags: boolean; + tagOperation: string; + selectedEndpoint?: string; + fingerprintQueue: Record; +} diff --git a/ui/v2.5/src/hooks/LocalForage.ts b/ui/v2.5/src/hooks/LocalForage.ts index 43763e2add2..d5b77f8062d 100644 --- a/ui/v2.5/src/hooks/LocalForage.ts +++ b/ui/v2.5/src/hooks/LocalForage.ts @@ -27,8 +27,9 @@ interface ILocalForage { const Loading: Record = {}; const Cache: Record = {}; -function useLocalForage( - key: string +export function useLocalForage( + key: string, + defaultValue: T = {} as T ): [ILocalForage, Dispatch>] { const [error, setError] = React.useState(null); const [data, setData] = React.useState(Cache[key] as T); @@ -43,27 +44,32 @@ function useLocalForage( setData(parsed); Cache[key] = parsed; } else { - setData({} as T); - Cache[key] = {}; + setData(defaultValue); + Cache[key] = defaultValue; } setError(null); } catch (err) { setError(err); + Cache[key] = defaultValue; } finally { Loading[key] = false; setLoading(false); } } + if (!loading && !Cache[key]) { Loading[key] = true; setLoading(true); runAsync(); } - }, [loading, data, key]); + }, [loading, key, defaultValue]); useEffect(() => { if (!_.isEqual(Cache[key], data)) { - Cache[key] = _.merge(Cache[key], data); + Cache[key] = _.merge({ + ...Cache[key], + ...data + }); localForage.setItem(key, JSON.stringify(Cache[key])); } }); diff --git a/ui/v2.5/src/hooks/index.ts b/ui/v2.5/src/hooks/index.ts index 15ddf6fea65..0da7a0ea817 100644 --- a/ui/v2.5/src/hooks/index.ts +++ b/ui/v2.5/src/hooks/index.ts @@ -1,5 +1,5 @@ export { default as useToast } from "./Toast"; -export { useInterfaceLocalForage, useChangelogStorage } from "./LocalForage"; +export { useInterfaceLocalForage, useChangelogStorage, useLocalForage } from "./LocalForage"; export { useScenesList, useSceneMarkersList, From c164e07211f05a77d9fd5eb762f83f9cf2a2a1f6 Mon Sep 17 00:00:00 2001 From: Infinite Date: Tue, 22 Dec 2020 00:33:01 +0000 Subject: [PATCH 2/4] WIP --- ui/v2.5/src/components/Tagger/Tagger.tsx | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/ui/v2.5/src/components/Tagger/Tagger.tsx b/ui/v2.5/src/components/Tagger/Tagger.tsx index 74da5d59b7c..9538724d0dc 100755 --- a/ui/v2.5/src/components/Tagger/Tagger.tsx +++ b/ui/v2.5/src/components/Tagger/Tagger.tsx @@ -24,13 +24,19 @@ import { sortScenesByDuration, } from "./utils"; -const dateRegex = /\.(\d\d)\.(\d\d)\.(\d\d)\./; +const simpleDateRegex = /\.(\d\d)\.(\d\d)\.(\d\d)\./; +const isoDateRegex = /\d{4}[-.]\d{2}[-.]\d{2}/; const parseDate = (input: string): string => { - const date = s.match(dateRegex); - if (date) { - s = s.replace(date[0], ` 20${date[1]}-${date[2]}-${date[3]} `); + let output = input; + const simpleDate = output.match(simpleDateRegex); + if (simpleDate) { + output = output.replace(simpleDate[0], ` 20${simpleDate[1]}-${simpleDate[2]}-${simpleDate[3]} `); } - return input; + const isoDate = output.match(isoDateRegex); + if (isoDate) { + output = output.replace(isoDate[0], ` 20${isoDate[1]}-${isoDate[2]}-${isoDate[3]} `); + } + return output; } function prepareQueryString( @@ -66,7 +72,7 @@ function prepareQueryString( s = s.replace(new RegExp(b, "i"), ""); }); s = parseDate(s); - return s.replace(/[-\.]/g, " "); + return s.replace(/[-.]/g, " "); } interface ITaggerListProps { From a33de18a9bfb835b0cd1be1c7dcb47271f3c63c9 Mon Sep 17 00:00:00 2001 From: Infinite Date: Tue, 22 Dec 2020 23:29:03 +0000 Subject: [PATCH 3/4] Finish fixes --- ui/v2.5/src/components/Tagger/Config.tsx | 12 +-- .../components/Tagger/StashSearchResult.tsx | 8 +- ui/v2.5/src/components/Tagger/Tagger.tsx | 96 ++++++++++++++++--- ui/v2.5/src/hooks/LocalForage.ts | 2 +- ui/v2.5/src/hooks/index.ts | 6 +- 5 files changed, 96 insertions(+), 28 deletions(-) diff --git a/ui/v2.5/src/components/Tagger/Config.tsx b/ui/v2.5/src/components/Tagger/Config.tsx index f2ff4be6246..f320567af03 100644 --- a/ui/v2.5/src/components/Tagger/Config.tsx +++ b/ui/v2.5/src/components/Tagger/Config.tsx @@ -10,7 +10,7 @@ import { import { Icon } from "src/components/Shared"; import { useConfiguration } from "src/core/StashService"; -import { ITaggerConfig, ParseMode, ModeDesc } from './constants'; +import { ITaggerConfig, ParseMode, ModeDesc } from "./constants"; interface IConfigProps { show: boolean; @@ -41,15 +41,14 @@ const Config: React.FC = ({ show, config, setConfig }) => { }; const handleBlacklistAddition = () => { - if (!blacklistRef.current) - return; + if (!blacklistRef.current) return; const input = blacklistRef.current.value; setConfig({ ...config, blacklist: [...config.blacklist, input], }); - blacklistRef.current.value = ''; + blacklistRef.current.value = ""; }; const stashBoxes = stashConfig.data?.configuration.general.stashBoxes ?? []; @@ -147,10 +146,7 @@ const Config: React.FC = ({ show, config, setConfig }) => {
Blacklist
- + diff --git a/ui/v2.5/src/components/Tagger/StashSearchResult.tsx b/ui/v2.5/src/components/Tagger/StashSearchResult.tsx index 022d6229a71..62aa9a27771 100755 --- a/ui/v2.5/src/components/Tagger/StashSearchResult.tsx +++ b/ui/v2.5/src/components/Tagger/StashSearchResult.tsx @@ -105,13 +105,15 @@ const StashSearchResult: React.FC = ({ const updateStudioStashID = useUpdateStudioStashID(); const [updateScene] = GQL.useSceneUpdateMutation({ onError: (e) => { - const message = e.message === "invalid JPEG format: short Huffman data" ? - "Failed to save scene due to corrupted cover image" : "Failed to save scene"; + const message = + e.message === "invalid JPEG format: short Huffman data" + ? "Failed to save scene due to corrupted cover image" + : "Failed to save scene"; setError({ message, details: e.message, }); - } + }, }); const { data: allTags } = GQL.useAllTagsForFilterQuery(); diff --git a/ui/v2.5/src/components/Tagger/Tagger.tsx b/ui/v2.5/src/components/Tagger/Tagger.tsx index 9538724d0dc..2fdfd5d8f11 100755 --- a/ui/v2.5/src/components/Tagger/Tagger.tsx +++ b/ui/v2.5/src/components/Tagger/Tagger.tsx @@ -16,7 +16,12 @@ import { Manual } from "src/components/Help/Manual"; import StashSearchResult from "./StashSearchResult"; import Config from "./Config"; -import { LOCAL_FORAGE_KEY, ITaggerConfig, ParseMode } from './constants'; +import { + LOCAL_FORAGE_KEY, + ITaggerConfig, + ParseMode, + initialConfig, +} from "./constants"; import { parsePath, selectScenes, @@ -24,20 +29,78 @@ import { sortScenesByDuration, } from "./utils"; -const simpleDateRegex = /\.(\d\d)\.(\d\d)\.(\d\d)\./; -const isoDateRegex = /\d{4}[-.]\d{2}[-.]\d{2}/; +const months = [ + "jan", + "feb", + "mar", + "apr", + "may", + "jun", + "jul", + "aug", + "sep", + "oct", + "nov", + "dec", +]; + +const ddmmyyRegex = /\.(\d\d)\.(\d\d)\.(\d\d)\./; +const yyyymmddRegex = /(\d{4})[-.](\d{2})[-.](\d{2})/; +const mmddyyRegex = /(\d{2})[-.](\d{2})[-.](\d{4})/; +const ddMMyyRegex = new RegExp( + `(\\d{1,2}).(${months.join("|")})\\.?.(\\d{4})`, + "i" +); +const MMddyyRegex = new RegExp( + `(${months.join("|")})\\.?.(\\d{1,2}),?.(\\d{4})`, + "i" +); const parseDate = (input: string): string => { let output = input; - const simpleDate = output.match(simpleDateRegex); - if (simpleDate) { - output = output.replace(simpleDate[0], ` 20${simpleDate[1]}-${simpleDate[2]}-${simpleDate[3]} `); + const ddmmyy = output.match(ddmmyyRegex); + if (ddmmyy) { + output = output.replace( + ddmmyy[0], + ` 20${ddmmyy[1]}-${ddmmyy[2]}-${ddmmyy[3]} ` + ); } - const isoDate = output.match(isoDateRegex); - if (isoDate) { - output = output.replace(isoDate[0], ` 20${isoDate[1]}-${isoDate[2]}-${isoDate[3]} `); + const mmddyy = output.match(mmddyyRegex); + if (mmddyy) { + output = output.replace( + mmddyy[0], + ` 20${mmddyy[1]}-${mmddyy[2]}-${mmddyy[3]} ` + ); } - return output; -} + const ddMMyy = output.match(ddMMyyRegex); + if (ddMMyy) { + const month = (months.indexOf(ddMMyy[2].toLowerCase()) + 1) + .toString() + .padStart(2, "0"); + output = output.replace( + ddMMyy[0], + ` ${ddMMyy[3]}-${month}-${ddMMyy[1].padStart(2, "0")} ` + ); + } + const MMddyy = output.match(MMddyyRegex); + if (MMddyy) { + const month = (months.indexOf(MMddyy[1].toLowerCase()) + 1) + .toString() + .padStart(2, "0"); + output = output.replace( + MMddyy[0], + ` ${MMddyy[3]}-${month}-${MMddyy[2].padStart(2, "0")} ` + ); + } + + const yyyymmdd = output.search(yyyymmddRegex); + if (yyyymmdd !== -1) + return ( + output.slice(0, yyyymmdd).replace(/-/g, " ") + + output.slice(yyyymmdd, yyyymmdd + 10) + + output.slice(yyyymmdd + 10).replace(/-/g, " ") + ); + return output.replace(/-/g, " "); +}; function prepareQueryString( scene: Partial, @@ -61,6 +124,7 @@ function prepareQueryString( return str; } let s = ""; + if (mode === "auto" || mode === "filename") { s = filename; } else if (mode === "path") { @@ -72,7 +136,7 @@ function prepareQueryString( s = s.replace(new RegExp(b, "i"), ""); }); s = parseDate(s); - return s.replace(/[-.]/g, " "); + return s.replace(/\./g, " "); } interface ITaggerListProps { @@ -469,12 +533,14 @@ interface ITaggerProps { export const Tagger: React.FC = ({ scenes }) => { const stashConfig = useConfiguration(); - const [{ data: config }, setConfig] = useLocalForage(LOCAL_FORAGE_KEY); + const [{ data: config }, setConfig] = useLocalForage( + LOCAL_FORAGE_KEY, + initialConfig + ); const [showConfig, setShowConfig] = useState(false); const [showManual, setShowManual] = useState(false); - if (!config) - return ; + if (!config) return ; const savedEndpointIndex = stashConfig.data?.configuration.general.stashBoxes.findIndex( diff --git a/ui/v2.5/src/hooks/LocalForage.ts b/ui/v2.5/src/hooks/LocalForage.ts index d5b77f8062d..6f0340a5807 100644 --- a/ui/v2.5/src/hooks/LocalForage.ts +++ b/ui/v2.5/src/hooks/LocalForage.ts @@ -68,7 +68,7 @@ export function useLocalForage( if (!_.isEqual(Cache[key], data)) { Cache[key] = _.merge({ ...Cache[key], - ...data + ...data, }); localForage.setItem(key, JSON.stringify(Cache[key])); } diff --git a/ui/v2.5/src/hooks/index.ts b/ui/v2.5/src/hooks/index.ts index 0da7a0ea817..55f5ddeba40 100644 --- a/ui/v2.5/src/hooks/index.ts +++ b/ui/v2.5/src/hooks/index.ts @@ -1,5 +1,9 @@ export { default as useToast } from "./Toast"; -export { useInterfaceLocalForage, useChangelogStorage, useLocalForage } from "./LocalForage"; +export { + useInterfaceLocalForage, + useChangelogStorage, + useLocalForage, +} from "./LocalForage"; export { useScenesList, useSceneMarkersList, From 7761879dcfd661f312647b3f774e2e0a8dfcb125 Mon Sep 17 00:00:00 2001 From: Infinite Date: Thu, 24 Dec 2020 10:32:41 +0000 Subject: [PATCH 4/4] Validate stash-box endpoint pattern --- pkg/manager/config/config.go | 8 ++++++++ ui/v2.5/src/components/Settings/StashBoxConfiguration.tsx | 4 ++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/pkg/manager/config/config.go b/pkg/manager/config/config.go index 1a6fe405a07..622500becfd 100644 --- a/pkg/manager/config/config.go +++ b/pkg/manager/config/config.go @@ -7,6 +7,7 @@ import ( "errors" "io/ioutil" "path/filepath" + "regexp" "github.com/spf13/viper" @@ -427,11 +428,18 @@ func ValidateCredentials(username string, password string) bool { func ValidateStashBoxes(boxes []*models.StashBoxInput) error { isMulti := len(boxes) > 1 + re, err := regexp.Compile("^http.*graphql$") + if err != nil { + return errors.New("Failure to generate regular expression") + } + for _, box := range boxes { if box.APIKey == "" { return errors.New("Stash-box API Key cannot be blank") } else if box.Endpoint == "" { return errors.New("Stash-box Endpoint cannot be blank") + } else if !re.Match([]byte(box.Endpoint)) { + return errors.New("Stash-box Endpoint is invalid") } else if isMulti && box.Name == "" { return errors.New("Stash-box Name cannot be blank") } diff --git a/ui/v2.5/src/components/Settings/StashBoxConfiguration.tsx b/ui/v2.5/src/components/Settings/StashBoxConfiguration.tsx index 32ee4c3f2b0..54cd96468d9 100644 --- a/ui/v2.5/src/components/Settings/StashBoxConfiguration.tsx +++ b/ui/v2.5/src/components/Settings/StashBoxConfiguration.tsx @@ -41,7 +41,7 @@ const Instance: React.FC = ({ value={instance?.endpoint} isValid={(instance?.endpoint?.length ?? 0) > 0} onInput={(e: React.ChangeEvent) => - handleInput("endpoint", e.currentTarget.value) + handleInput("endpoint", e.currentTarget.value.trim()) } /> = ({ value={instance?.api_key} isValid={(instance?.api_key?.length ?? 0) > 0} onInput={(e: React.ChangeEvent) => - handleInput("api_key", e.currentTarget.value) + handleInput("api_key", e.currentTarget.value.trim()) } />