mirror of
https://github.com/stashapp/stash.git
synced 2025-12-10 02:15:30 +01:00
tags + some refactor
This commit is contained in:
parent
de0a03f73e
commit
ae40827e78
15 changed files with 292 additions and 56 deletions
|
|
@ -165,6 +165,12 @@ type Query {
|
|||
input: ScrapeSingleStudioInput!
|
||||
): [ScrapedStudio!]!
|
||||
|
||||
"Scrape for a single tag"
|
||||
scrapeSingleTag(
|
||||
source: ScraperSourceInput!
|
||||
input: ScrapeSingleTagInput!
|
||||
): [ScrapedTag!]!
|
||||
|
||||
"Scrape for a single performer"
|
||||
scrapeSinglePerformer(
|
||||
source: ScraperSourceInput!
|
||||
|
|
|
|||
|
|
@ -198,6 +198,13 @@ input ScrapeSingleStudioInput {
|
|||
query: String
|
||||
}
|
||||
|
||||
input ScrapeSingleTagInput {
|
||||
"""
|
||||
Query can be either a name or a Stash ID
|
||||
"""
|
||||
query: String
|
||||
}
|
||||
|
||||
input ScrapeSinglePerformerInput {
|
||||
"Instructs to query by string"
|
||||
query: String
|
||||
|
|
|
|||
|
|
@ -170,6 +170,12 @@ query FindStudio($id: ID, $name: String) {
|
|||
}
|
||||
}
|
||||
|
||||
query FindTag($id: ID, $name: String) {
|
||||
findTag(id: $id, name: $name) {
|
||||
...TagFragment
|
||||
}
|
||||
}
|
||||
|
||||
mutation SubmitFingerprint($input: FingerprintSubmission!) {
|
||||
submitFingerprint(input: $input)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -353,6 +353,45 @@ func (r *queryResolver) ScrapeSingleStudio(ctx context.Context, source scraper.S
|
|||
return nil, errors.New("stash_box_index must be set")
|
||||
}
|
||||
|
||||
func (r *queryResolver) ScrapeSingleTag(ctx context.Context, source scraper.Source, input ScrapeSingleTagInput) ([]*models.ScrapedTag, error) {
|
||||
if source.StashBoxIndex != nil || source.StashBoxEndpoint != nil {
|
||||
b, err := resolveStashBox(source.StashBoxIndex, source.StashBoxEndpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
client := r.newStashBoxClient(*b)
|
||||
|
||||
var ret []*models.ScrapedTag
|
||||
out, err := client.FindTag(ctx, *input.Query)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if out != nil {
|
||||
ret = append(ret, out)
|
||||
}
|
||||
|
||||
if len(ret) > 0 {
|
||||
if err := r.withReadTxn(ctx, func(ctx context.Context) error {
|
||||
for _, tag := range ret {
|
||||
if err := match.ScrapedTag(ctx, r.repository.Tag, tag, b.Endpoint); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return nil, errors.New("stash_box_index must be set")
|
||||
}
|
||||
|
||||
func (r *queryResolver) ScrapeSinglePerformer(ctx context.Context, source scraper.Source, input ScrapeSinglePerformerInput) ([]*models.ScrapedPerformer, error) {
|
||||
var ret []*models.ScrapedPerformer
|
||||
switch {
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ type StashBoxGraphQLClient interface {
|
|||
FindPerformerByID(ctx context.Context, id string, interceptors ...clientv2.RequestInterceptor) (*FindPerformerByID, error)
|
||||
FindSceneByID(ctx context.Context, id string, interceptors ...clientv2.RequestInterceptor) (*FindSceneByID, error)
|
||||
FindStudio(ctx context.Context, id *string, name *string, interceptors ...clientv2.RequestInterceptor) (*FindStudio, error)
|
||||
FindTag(ctx context.Context, id *string, name *string, interceptors ...clientv2.RequestInterceptor) (*FindTag, error)
|
||||
SubmitFingerprint(ctx context.Context, input FingerprintSubmission, interceptors ...clientv2.RequestInterceptor) (*SubmitFingerprint, error)
|
||||
Me(ctx context.Context, interceptors ...clientv2.RequestInterceptor) (*Me, error)
|
||||
SubmitSceneDraft(ctx context.Context, input SceneDraftInput, interceptors ...clientv2.RequestInterceptor) (*SubmitSceneDraft, error)
|
||||
|
|
@ -763,6 +764,17 @@ func (t *FindStudio) GetFindStudio() *StudioFragment {
|
|||
return t.FindStudio
|
||||
}
|
||||
|
||||
type FindTag struct {
|
||||
FindTag *TagFragment "json:\"findTag,omitempty\" graphql:\"findTag\""
|
||||
}
|
||||
|
||||
func (t *FindTag) GetFindTag() *TagFragment {
|
||||
if t == nil {
|
||||
t = &FindTag{}
|
||||
}
|
||||
return t.FindTag
|
||||
}
|
||||
|
||||
type SubmitFingerprint struct {
|
||||
SubmitFingerprint bool "json:\"submitFingerprint\" graphql:\"submitFingerprint\""
|
||||
}
|
||||
|
|
@ -1695,6 +1707,35 @@ func (c *Client) FindStudio(ctx context.Context, id *string, name *string, inter
|
|||
return &res, nil
|
||||
}
|
||||
|
||||
const FindTagDocument = `query FindTag ($id: ID, $name: String) {
|
||||
findTag(id: $id, name: $name) {
|
||||
... TagFragment
|
||||
}
|
||||
}
|
||||
fragment TagFragment on Tag {
|
||||
name
|
||||
id
|
||||
}
|
||||
`
|
||||
|
||||
func (c *Client) FindTag(ctx context.Context, id *string, name *string, interceptors ...clientv2.RequestInterceptor) (*FindTag, error) {
|
||||
vars := map[string]any{
|
||||
"id": id,
|
||||
"name": name,
|
||||
}
|
||||
|
||||
var res FindTag
|
||||
if err := c.Client.Post(ctx, "FindTag", FindTagDocument, &res, vars, interceptors...); err != nil {
|
||||
if c.Client.ParseDataWhenErrors {
|
||||
return &res, err
|
||||
}
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
const SubmitFingerprintDocument = `mutation SubmitFingerprint ($input: FingerprintSubmission!) {
|
||||
submitFingerprint(input: $input)
|
||||
}
|
||||
|
|
@ -1796,6 +1837,7 @@ var DocumentOperationNames = map[string]string{
|
|||
FindPerformerByIDDocument: "FindPerformerByID",
|
||||
FindSceneByIDDocument: "FindSceneByID",
|
||||
FindStudioDocument: "FindStudio",
|
||||
FindTagDocument: "FindTag",
|
||||
SubmitFingerprintDocument: "SubmitFingerprint",
|
||||
MeDocument: "Me",
|
||||
SubmitSceneDraftDocument: "SubmitSceneDraft",
|
||||
|
|
|
|||
36
pkg/stashbox/tag.go
Normal file
36
pkg/stashbox/tag.go
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
package stashbox
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/stashapp/stash/pkg/models"
|
||||
)
|
||||
|
||||
func (c Client) FindTag(ctx context.Context, query string) (*models.ScrapedTag, error) {
|
||||
var id *string
|
||||
var name *string
|
||||
|
||||
_, err := uuid.Parse(query)
|
||||
if err == nil {
|
||||
// Confirmed the user passed in a Stash ID
|
||||
id = &query
|
||||
} else {
|
||||
// Otherwise assume they're searching on a name
|
||||
name = &query
|
||||
}
|
||||
|
||||
tag, err := c.client.FindTag(ctx, id, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if tag.FindTag == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return &models.ScrapedTag{
|
||||
Name: tag.FindTag.Name,
|
||||
RemoteSiteID: &tag.FindTag.ID,
|
||||
}, nil
|
||||
}
|
||||
|
|
@ -62,6 +62,15 @@ query ScrapeSingleStudio(
|
|||
}
|
||||
}
|
||||
|
||||
query ScrapeSingleTag(
|
||||
$source: ScraperSourceInput!
|
||||
$input: ScrapeSingleTagInput!
|
||||
) {
|
||||
scrapeSingleTag(source: $source, input: $input) {
|
||||
...ScrapedSceneTagData
|
||||
}
|
||||
}
|
||||
|
||||
query ScrapeSinglePerformer(
|
||||
$source: ScraperSourceInput!
|
||||
$input: ScrapeSinglePerformerInput!
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import { ImageInput } from "src/components/Shared/ImageInput";
|
|||
import { LoadingIndicator } from "src/components/Shared/LoadingIndicator";
|
||||
import { CountrySelect } from "src/components/Shared/CountrySelect";
|
||||
import ImageUtils from "src/utils/image";
|
||||
import { getStashIDs } from "src/utils/stashIds";
|
||||
import { addUpdateStashID, getStashIDs } from "src/utils/stashIds";
|
||||
import { stashboxDisplayName } from "src/utils/stashbox";
|
||||
import { useToast } from "src/hooks/Toast";
|
||||
import { Prompt } from "react-router-dom";
|
||||
|
|
@ -574,23 +574,10 @@ export const PerformerEditPanel: React.FC<IPerformerDetails> = ({
|
|||
|
||||
function onStashIDSelected(item?: GQL.StashIdInput) {
|
||||
if (!item) return;
|
||||
|
||||
// Check if StashID with this endpoint already exists
|
||||
const existingIndex = formik.values.stash_ids.findIndex(
|
||||
(s) => s.endpoint === item.endpoint
|
||||
formik.setFieldValue(
|
||||
"stash_ids",
|
||||
addUpdateStashID(formik.values.stash_ids, item)
|
||||
);
|
||||
|
||||
let newStashIDs;
|
||||
if (existingIndex >= 0) {
|
||||
// Replace existing StashID
|
||||
newStashIDs = [...formik.values.stash_ids];
|
||||
newStashIDs[existingIndex] = item;
|
||||
} else {
|
||||
// Add new StashID
|
||||
newStashIDs = [...formik.values.stash_ids, item];
|
||||
}
|
||||
|
||||
formik.setFieldValue("stash_ids", newStashIDs);
|
||||
}
|
||||
|
||||
function renderButtons(classNames: string) {
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import { LoadingIndicator } from "src/components/Shared/LoadingIndicator";
|
|||
import { ImageInput } from "src/components/Shared/ImageInput";
|
||||
import { useToast } from "src/hooks/Toast";
|
||||
import ImageUtils from "src/utils/image";
|
||||
import { getStashIDs } from "src/utils/stashIds";
|
||||
import { addUpdateStashID, getStashIDs } from "src/utils/stashIds";
|
||||
import { useFormik } from "formik";
|
||||
import { Prompt } from "react-router-dom";
|
||||
import { useConfigurationContext } from "src/hooks/Config";
|
||||
|
|
@ -552,23 +552,10 @@ export const SceneEditPanel: React.FC<IProps> = ({
|
|||
|
||||
function onStashIDSelected(item?: GQL.StashIdInput) {
|
||||
if (!item) return;
|
||||
|
||||
// Check if StashID with this endpoint already exists
|
||||
const existingIndex = formik.values.stash_ids.findIndex(
|
||||
(s) => s.endpoint === item.endpoint
|
||||
formik.setFieldValue(
|
||||
"stash_ids",
|
||||
addUpdateStashID(formik.values.stash_ids, item)
|
||||
);
|
||||
|
||||
let newStashIDs;
|
||||
if (existingIndex >= 0) {
|
||||
// Replace existing StashID
|
||||
newStashIDs = [...formik.values.stash_ids];
|
||||
newStashIDs[existingIndex] = item;
|
||||
} else {
|
||||
// Add new StashID
|
||||
newStashIDs = [...formik.values.stash_ids, item];
|
||||
}
|
||||
|
||||
formik.setFieldValue("stash_ids", newStashIDs);
|
||||
}
|
||||
|
||||
const image = useMemo(() => {
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import {
|
|||
stashBoxPerformerQuery,
|
||||
stashBoxSceneQuery,
|
||||
stashBoxStudioQuery,
|
||||
stashBoxTagQuery,
|
||||
} from "src/core/StashService";
|
||||
import { useToast } from "src/hooks/Toast";
|
||||
import { stringToGender } from "src/utils/gender";
|
||||
|
|
@ -22,9 +23,10 @@ import { stringToGender } from "src/utils/gender";
|
|||
type SearchResultItem =
|
||||
| GQL.ScrapedPerformerDataFragment
|
||||
| GQL.ScrapedSceneDataFragment
|
||||
| GQL.ScrapedStudioDataFragment;
|
||||
| GQL.ScrapedStudioDataFragment
|
||||
| GQL.ScrapedSceneTagDataFragment;
|
||||
|
||||
export type StashBoxEntityType = "performer" | "scene" | "studio";
|
||||
export type StashBoxEntityType = "performer" | "scene" | "studio" | "tag";
|
||||
|
||||
interface IProps {
|
||||
entityType: StashBoxEntityType;
|
||||
|
|
@ -232,6 +234,27 @@ export const StudioSearchResult: React.FC<IStudioResultProps> = ({
|
|||
);
|
||||
};
|
||||
|
||||
// Tag Result Component
|
||||
interface ITagResultProps {
|
||||
tag: GQL.ScrapedSceneTagDataFragment;
|
||||
}
|
||||
|
||||
export const TagSearchResult: React.FC<ITagResultProps> = ({ tag }) => {
|
||||
return (
|
||||
<div className="mt-3 search-item" style={{ cursor: "pointer" }}>
|
||||
<div className="tag-result">
|
||||
<Row>
|
||||
<div className="col flex-column">
|
||||
<h4 className="tag-name">
|
||||
<span>{tag.name}</span>
|
||||
</h4>
|
||||
</div>
|
||||
</Row>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// Helper to get entity type display name for i18n
|
||||
function getEntityTypeDisplayName(entityType: StashBoxEntityType): string {
|
||||
switch (entityType) {
|
||||
|
|
@ -241,6 +264,8 @@ function getEntityTypeDisplayName(entityType: StashBoxEntityType): string {
|
|||
return "Scene";
|
||||
case "studio":
|
||||
return "Studio";
|
||||
case "tag":
|
||||
return "Tag";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -253,6 +278,8 @@ function getFoundMessageId(entityType: StashBoxEntityType): string {
|
|||
return "dialogs.scenes_found";
|
||||
case "studio":
|
||||
return "dialogs.studios_found";
|
||||
case "tag":
|
||||
return "dialogs.tags_found";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -318,6 +345,14 @@ export const StashBoxIDSearchModal: React.FC<IProps> = ({
|
|||
setResults(queryData.data?.scrapeSingleStudio ?? []);
|
||||
break;
|
||||
}
|
||||
case "tag": {
|
||||
const queryData = await stashBoxTagQuery(
|
||||
query,
|
||||
selectedStashBox.endpoint
|
||||
);
|
||||
setResults(queryData.data?.scrapeSingleTag ?? []);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
Toast.error(error);
|
||||
|
|
@ -357,6 +392,10 @@ export const StashBoxIDSearchModal: React.FC<IProps> = ({
|
|||
return (
|
||||
<StudioSearchResult studio={item as GQL.ScrapedStudioDataFragment} />
|
||||
);
|
||||
case "tag":
|
||||
return (
|
||||
<TagSearchResult tag={item as GQL.ScrapedSceneTagDataFragment} />
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import { DetailsEditNavbar } from "src/components/Shared/DetailsEditNavbar";
|
|||
import { Button, Form } from "react-bootstrap";
|
||||
import { faPlus } from "@fortawesome/free-solid-svg-icons";
|
||||
import ImageUtils from "src/utils/image";
|
||||
import { getStashIDs } from "src/utils/stashIds";
|
||||
import { addUpdateStashID, getStashIDs } from "src/utils/stashIds";
|
||||
import { useFormik } from "formik";
|
||||
import { Prompt } from "react-router-dom";
|
||||
import isEqual from "lodash-es/isEqual";
|
||||
|
|
@ -153,20 +153,10 @@ export const StudioEditPanel: React.FC<IStudioEditPanel> = ({
|
|||
|
||||
function onStashIDSelected(item?: GQL.StashIdInput) {
|
||||
if (!item) return;
|
||||
|
||||
const existingIndex = formik.values.stash_ids.findIndex(
|
||||
(s) => s.endpoint === item.endpoint
|
||||
formik.setFieldValue(
|
||||
"stash_ids",
|
||||
addUpdateStashID(formik.values.stash_ids, item)
|
||||
);
|
||||
|
||||
let newStashIDs;
|
||||
if (existingIndex >= 0) {
|
||||
newStashIDs = [...formik.values.stash_ids];
|
||||
newStashIDs[existingIndex] = item;
|
||||
} else {
|
||||
newStashIDs = [...formik.values.stash_ids, item];
|
||||
}
|
||||
|
||||
formik.setFieldValue("stash_ids", newStashIDs);
|
||||
}
|
||||
|
||||
const {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,8 @@ import { FormattedMessage, useIntl } from "react-intl";
|
|||
import * as GQL from "src/core/generated-graphql";
|
||||
import * as yup from "yup";
|
||||
import { DetailsEditNavbar } from "src/components/Shared/DetailsEditNavbar";
|
||||
import { Form } from "react-bootstrap";
|
||||
import { Button, Form } from "react-bootstrap";
|
||||
import { faPlus } from "@fortawesome/free-solid-svg-icons";
|
||||
import ImageUtils from "src/utils/image";
|
||||
import { useFormik } from "formik";
|
||||
import { Prompt } from "react-router-dom";
|
||||
|
|
@ -11,11 +12,14 @@ import Mousetrap from "mousetrap";
|
|||
import { LoadingIndicator } from "src/components/Shared/LoadingIndicator";
|
||||
import isEqual from "lodash-es/isEqual";
|
||||
import { useToast } from "src/hooks/Toast";
|
||||
import { useConfigurationContext } from "src/hooks/Config";
|
||||
import { handleUnsavedChanges } from "src/utils/navigation";
|
||||
import { formikUtils } from "src/utils/form";
|
||||
import { yupFormikValidate, yupUniqueAliases } from "src/utils/yup";
|
||||
import { getStashIDs } from "src/utils/stashIds";
|
||||
import { addUpdateStashID, getStashIDs } from "src/utils/stashIds";
|
||||
import { Tag, TagSelect } from "../TagSelect";
|
||||
import { Icon } from "src/components/Shared/Icon";
|
||||
import StashBoxIDSearchModal from "src/components/Shared/StashBoxIDSearchModal";
|
||||
|
||||
interface ITagEditPanel {
|
||||
tag: Partial<GQL.TagDataFragment>;
|
||||
|
|
@ -36,9 +40,13 @@ export const TagEditPanel: React.FC<ITagEditPanel> = ({
|
|||
}) => {
|
||||
const intl = useIntl();
|
||||
const Toast = useToast();
|
||||
const { configuration: stashConfig } = useConfigurationContext();
|
||||
|
||||
const isNew = tag.id === undefined;
|
||||
|
||||
// Editing state
|
||||
const [isStashIDSearchOpen, setIsStashIDSearchOpen] = useState(false);
|
||||
|
||||
// Network state
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
|
|
@ -143,6 +151,14 @@ export const TagEditPanel: React.FC<ITagEditPanel> = ({
|
|||
ImageUtils.onImageChange(event, onImageLoad);
|
||||
}
|
||||
|
||||
function onStashIDSelected(item?: GQL.StashIdInput) {
|
||||
if (!item) return;
|
||||
formik.setFieldValue(
|
||||
"stash_ids",
|
||||
addUpdateStashID(formik.values.stash_ids, item)
|
||||
);
|
||||
}
|
||||
|
||||
const {
|
||||
renderField,
|
||||
renderInputField,
|
||||
|
|
@ -186,8 +202,23 @@ export const TagEditPanel: React.FC<ITagEditPanel> = ({
|
|||
|
||||
// TODO: CSS class
|
||||
return (
|
||||
<div>
|
||||
{isNew && (
|
||||
<>
|
||||
{isStashIDSearchOpen && (
|
||||
<StashBoxIDSearchModal
|
||||
entityType="tag"
|
||||
stashBoxes={stashConfig?.general.stashBoxes ?? []}
|
||||
excludedStashBoxEndpoints={formik.values.stash_ids.map(
|
||||
(s) => s.endpoint
|
||||
)}
|
||||
onSelectItem={(item) => {
|
||||
onStashIDSelected(item);
|
||||
setIsStashIDSearchOpen(false);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
<div>
|
||||
{isNew && (
|
||||
<h2>
|
||||
<FormattedMessage
|
||||
id="actions.add_entity"
|
||||
|
|
@ -215,7 +246,21 @@ export const TagEditPanel: React.FC<ITagEditPanel> = ({
|
|||
{renderInputField("description", "textarea")}
|
||||
{renderParentTagsField()}
|
||||
{renderSubTagsField()}
|
||||
{renderStashIDsField("stash_ids", "tags")}
|
||||
{renderStashIDsField(
|
||||
"stash_ids",
|
||||
"tags",
|
||||
"stash_ids",
|
||||
undefined,
|
||||
<Button
|
||||
variant="success"
|
||||
className="mr-2 py-0"
|
||||
onClick={() => setIsStashIDSearchOpen(true)}
|
||||
disabled={!stashConfig?.general.stashBoxes?.length}
|
||||
title={intl.formatMessage({ id: "actions.add_stash_id" })}
|
||||
>
|
||||
<Icon icon={faPlus} />
|
||||
</Button>
|
||||
)}
|
||||
<hr />
|
||||
{renderInputField("ignore_auto_tag", "checkbox")}
|
||||
</Form>
|
||||
|
|
@ -234,6 +279,7 @@ export const TagEditPanel: React.FC<ITagEditPanel> = ({
|
|||
onDelete={onDelete}
|
||||
acceptSVG
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2329,6 +2329,23 @@ export const stashBoxSceneQuery = (query: string, stashBoxEndpoint: string) =>
|
|||
}
|
||||
);
|
||||
|
||||
export const stashBoxTagQuery = (
|
||||
query: string | null,
|
||||
stashBoxEndpoint: string
|
||||
) =>
|
||||
client.query<GQL.ScrapeSingleTagQuery, GQL.ScrapeSingleTagQueryVariables>({
|
||||
query: GQL.ScrapeSingleTagDocument,
|
||||
variables: {
|
||||
source: {
|
||||
stash_box_endpoint: stashBoxEndpoint,
|
||||
},
|
||||
input: {
|
||||
query: query,
|
||||
},
|
||||
},
|
||||
fetchPolicy: "network-only",
|
||||
});
|
||||
|
||||
export const mutateStashBoxBatchPerformerTag = (
|
||||
input: GQL.StashBoxBatchTagInput
|
||||
) =>
|
||||
|
|
|
|||
|
|
@ -1015,6 +1015,7 @@
|
|||
},
|
||||
"scenes_found": "{count} scenes found",
|
||||
"studios_found": "{count} studios found",
|
||||
"tags_found": "{count} tags found",
|
||||
"scrape_entity_query": "{entity_type} Scrape Query",
|
||||
"scrape_entity_title": "{entity_type} Scrape Results",
|
||||
"scrape_results_existing": "Existing",
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import * as GQL from "src/core/generated-graphql";
|
||||
|
||||
export const getStashIDs = (
|
||||
ids?: { stash_id: string; endpoint: string; updated_at: string }[]
|
||||
) =>
|
||||
|
|
@ -32,3 +34,25 @@ export const separateNamesAndStashIds = (
|
|||
|
||||
return { names, stashIds };
|
||||
};
|
||||
|
||||
/**
|
||||
* Utility to add or update a StashID in an array.
|
||||
* If a StashID with the same endpoint exists, it will be replaced.
|
||||
* Otherwise, the new StashID will be appended.
|
||||
*/
|
||||
export const addUpdateStashID = (
|
||||
existingStashIDs: GQL.StashIdInput[],
|
||||
newItem: GQL.StashIdInput
|
||||
): GQL.StashIdInput[] => {
|
||||
const existingIndex = existingStashIDs.findIndex(
|
||||
(s) => s.endpoint === newItem.endpoint
|
||||
);
|
||||
|
||||
if (existingIndex >= 0) {
|
||||
const newStashIDs = [...existingStashIDs];
|
||||
newStashIDs[existingIndex] = newItem;
|
||||
return newStashIDs;
|
||||
}
|
||||
|
||||
return [...existingStashIDs, newItem];
|
||||
};
|
||||
Loading…
Reference in a new issue