From a08d2e258a7346af70e4328101faff27d4e65878 Mon Sep 17 00:00:00 2001 From: Gykes <24581046+Gykes@users.noreply.github.com> Date: Wed, 12 Nov 2025 15:14:04 -0800 Subject: [PATCH] Feature: Add Various Scraper Fields (#6249) * Support aliases in stashbox studio query --------- Co-authored-by: WithoutPants <53250216+WithoutPants@users.noreply.github.com> --- graphql/schema/types/scraper.graphql | 4 ++++ graphql/stash-box/query.graphql | 1 + pkg/models/model_scraped_item.go | 22 ++++++++++++++++++ pkg/stashbox/graphql/generated_client.go | 23 +++++++++++++++---- pkg/stashbox/studio.go | 4 ++++ ui/v2.5/graphql/data/scrapers.graphql | 20 ++++++++++++++++ .../Shared/ScrapeDialog/ScrapedObjectsRow.tsx | 7 ++++-- .../components/Tagger/scenes/StudioModal.tsx | 13 +++++++++++ .../src/docs/en/Manual/ScraperDevelopment.md | 19 ++++++++++++++- 9 files changed, 105 insertions(+), 8 deletions(-) diff --git a/graphql/schema/types/scraper.graphql b/graphql/schema/types/scraper.graphql index a8e1fccb0..84b8b5c85 100644 --- a/graphql/schema/types/scraper.graphql +++ b/graphql/schema/types/scraper.graphql @@ -59,6 +59,10 @@ type ScrapedStudio { urls: [String!] parent: ScrapedStudio image: String + details: String + "Aliases must be comma-delimited to be parsed correctly" + aliases: String + tags: [ScrapedTag!] remote_site_id: String } diff --git a/graphql/stash-box/query.graphql b/graphql/stash-box/query.graphql index f7528e728..4fa023070 100644 --- a/graphql/stash-box/query.graphql +++ b/graphql/stash-box/query.graphql @@ -13,6 +13,7 @@ fragment ImageFragment on Image { fragment StudioFragment on Studio { name id + aliases urls { ...URLFragment } diff --git a/pkg/models/model_scraped_item.go b/pkg/models/model_scraped_item.go index a06463134..dc400ce4e 100644 --- a/pkg/models/model_scraped_item.go +++ b/pkg/models/model_scraped_item.go @@ -19,6 +19,9 @@ type ScrapedStudio struct { Parent *ScrapedStudio `json:"parent"` Image *string `json:"image"` Images []string `json:"images"` + Details *string `json:"details"` + Aliases *string `json:"aliases"` + Tags []*ScrapedTag `json:"tags"` RemoteSiteID *string `json:"remote_site_id"` } @@ -55,6 +58,14 @@ func (s *ScrapedStudio) ToStudio(endpoint string, excluded map[string]bool) *Stu } } + if s.Details != nil && !excluded["details"] { + ret.Details = *s.Details + } + + if s.Aliases != nil && !excluded["aliases"] { + ret.Aliases = NewRelatedStrings(stringslice.FromString(*s.Aliases, ",")) + } + if s.Parent != nil && s.Parent.StoredID != nil && !excluded["parent"] && !excluded["parent_studio"] { parentId, _ := strconv.Atoi(*s.Parent.StoredID) ret.ParentID = &parentId @@ -108,6 +119,17 @@ func (s *ScrapedStudio) ToPartial(id string, endpoint string, excluded map[strin } } + if s.Details != nil && !excluded["details"] { + ret.Details = NewOptionalString(*s.Details) + } + + if s.Aliases != nil && !excluded["aliases"] { + ret.Aliases = &UpdateStrings{ + Values: stringslice.FromString(*s.Aliases, ","), + Mode: RelationshipUpdateModeSet, + } + } + if s.Parent != nil && !excluded["parent"] { if s.Parent.StoredID != nil { parentID, _ := strconv.Atoi(*s.Parent.StoredID) diff --git a/pkg/stashbox/graphql/generated_client.go b/pkg/stashbox/graphql/generated_client.go index f0224900f..90553b14a 100644 --- a/pkg/stashbox/graphql/generated_client.go +++ b/pkg/stashbox/graphql/generated_client.go @@ -82,11 +82,12 @@ func (t *ImageFragment) GetHeight() int { } type StudioFragment struct { - Name string "json:\"name\" graphql:\"name\"" - ID string "json:\"id\" graphql:\"id\"" - Urls []*URLFragment "json:\"urls\" graphql:\"urls\"" - Parent *StudioFragment_Parent "json:\"parent,omitempty\" graphql:\"parent\"" - Images []*ImageFragment "json:\"images\" graphql:\"images\"" + Name string "json:\"name\" graphql:\"name\"" + ID string "json:\"id\" graphql:\"id\"" + Aliases []string "json:\"aliases\" graphql:\"aliases\"" + Urls []*URLFragment "json:\"urls\" graphql:\"urls\"" + Parent *StudioFragment_Parent "json:\"parent,omitempty\" graphql:\"parent\"" + Images []*ImageFragment "json:\"images\" graphql:\"images\"" } func (t *StudioFragment) GetName() string { @@ -101,6 +102,12 @@ func (t *StudioFragment) GetID() string { } return t.ID } +func (t *StudioFragment) GetAliases() []string { + if t == nil { + t = &StudioFragment{} + } + return t.Aliases +} func (t *StudioFragment) GetUrls() []*URLFragment { if t == nil { t = &StudioFragment{} @@ -845,6 +852,7 @@ fragment ImageFragment on Image { fragment StudioFragment on Studio { name id + aliases urls { ... URLFragment } @@ -980,6 +988,7 @@ fragment ImageFragment on Image { fragment StudioFragment on Studio { name id + aliases urls { ... URLFragment } @@ -1115,6 +1124,7 @@ fragment ImageFragment on Image { fragment StudioFragment on Studio { name id + aliases urls { ... URLFragment } @@ -1250,6 +1260,7 @@ fragment ImageFragment on Image { fragment StudioFragment on Studio { name id + aliases urls { ... URLFragment } @@ -1543,6 +1554,7 @@ fragment ImageFragment on Image { fragment StudioFragment on Studio { name id + aliases urls { ... URLFragment } @@ -1641,6 +1653,7 @@ const FindStudioDocument = `query FindStudio ($id: ID, $name: String) { fragment StudioFragment on Studio { name id + aliases urls { ... URLFragment } diff --git a/pkg/stashbox/studio.go b/pkg/stashbox/studio.go index a0e9a6ea6..8934972f2 100644 --- a/pkg/stashbox/studio.go +++ b/pkg/stashbox/studio.go @@ -2,6 +2,7 @@ package stashbox import ( "context" + "strings" "github.com/google/uuid" "github.com/stashapp/stash/pkg/models" @@ -63,8 +64,11 @@ func studioFragmentToScrapedStudio(s graphql.StudioFragment) *models.ScrapedStud images = append(images, image.URL) } + aliases := strings.Join(s.Aliases, ", ") + st := &models.ScrapedStudio{ Name: s.Name, + Aliases: &aliases, Images: images, RemoteSiteID: &s.ID, } diff --git a/ui/v2.5/graphql/data/scrapers.graphql b/ui/v2.5/graphql/data/scrapers.graphql index 8150c1ba7..25c1036d8 100644 --- a/ui/v2.5/graphql/data/scrapers.graphql +++ b/ui/v2.5/graphql/data/scrapers.graphql @@ -7,9 +7,19 @@ fragment ScrapedStudioData on ScrapedStudio { name urls image + details + aliases + tags { + ...ScrapedSceneTagData + } remote_site_id } image + details + aliases + tags { + ...ScrapedSceneTagData + } remote_site_id } @@ -129,9 +139,19 @@ fragment ScrapedSceneStudioData on ScrapedStudio { name urls image + details + aliases + tags { + ...ScrapedSceneTagData + } remote_site_id } image + details + aliases + tags { + ...ScrapedSceneTagData + } remote_site_id } diff --git a/ui/v2.5/src/components/Shared/ScrapeDialog/ScrapedObjectsRow.tsx b/ui/v2.5/src/components/Shared/ScrapeDialog/ScrapedObjectsRow.tsx index 3d7bbe4ad..f3cff8d4e 100644 --- a/ui/v2.5/src/components/Shared/ScrapeDialog/ScrapedObjectsRow.tsx +++ b/ui/v2.5/src/components/Shared/ScrapeDialog/ScrapedObjectsRow.tsx @@ -41,7 +41,9 @@ export const ScrapedStudioRow: React.FC = ({ const value = resultValue ? [resultValue] : []; const selectValue = value.map((p) => { - const aliases: string[] = []; + const aliases: string[] = p.aliases + ? p.aliases.split(",").map((a) => a.trim()) + : []; return { id: p.stored_id ?? "", name: p.name ?? "", @@ -55,10 +57,11 @@ export const ScrapedStudioRow: React.FC = ({ isDisabled={!isNew} onSelect={(items) => { if (onChangeFn) { - const { id, ...data } = items[0]; + const { id, aliases, ...data } = items[0]; onChangeFn({ ...data, stored_id: id, + aliases: aliases?.join(", "), }); } }} diff --git a/ui/v2.5/src/components/Tagger/scenes/StudioModal.tsx b/ui/v2.5/src/components/Tagger/scenes/StudioModal.tsx index 1242adbc5..e01c40881 100644 --- a/ui/v2.5/src/components/Tagger/scenes/StudioModal.tsx +++ b/ui/v2.5/src/components/Tagger/scenes/StudioModal.tsx @@ -142,6 +142,9 @@ const StudioDetails: React.FC = ({
{maybeRenderField("name", studio.name, !isNew)} {maybeRenderURLListField("urls", studio.urls)} + {maybeRenderField("details", studio.details)} + {maybeRenderField("aliases", studio.aliases)} + {maybeRenderField("tags", studio.tags?.map((t) => t.name).join(", "))} {maybeRenderField("parent_studio", studio.parent?.name, false)} {maybeRenderStashBoxLink()}
@@ -232,6 +235,11 @@ const StudioModal: React.FC = ({ urls: studio.urls, image: studio.image, parent_id: studio.parent?.stored_id, + details: studio.details, + aliases: studio.aliases?.split(",").map((a) => a.trim()), + tag_ids: studio.tags?.map((t) => t.stored_id).filter((id) => id) as + | string[] + | undefined, }; // stashid handling code @@ -261,6 +269,11 @@ const StudioModal: React.FC = ({ name: studio.parent?.name, urls: studio.parent?.urls, image: studio.parent?.image, + details: studio.parent?.details, + aliases: studio.parent?.aliases?.split(",").map((a) => a.trim()), + tag_ids: studio.parent?.tags + ?.map((t) => t.stored_id) + .filter((id) => id) as string[] | undefined, }; // stashid handling code diff --git a/ui/v2.5/src/docs/en/Manual/ScraperDevelopment.md b/ui/v2.5/src/docs/en/Manual/ScraperDevelopment.md index bd87d71ab..1f52028f8 100644 --- a/ui/v2.5/src/docs/en/Manual/ScraperDevelopment.md +++ b/ui/v2.5/src/docs/en/Manual/ScraperDevelopment.md @@ -739,7 +739,11 @@ xPathScrapers: URL: $performer/@href Studio: Name: $studio - URL: $studio/@href + URL: $studio/@href + Details: //div[@class="studioDescription"] + Aliases: //div[@class="studioAliases"]/span + Tags: + Name: //div[@class="studioTags"]/a ``` See also [#333](https://github.com/stashapp/stash/pull/333) for more examples. @@ -822,6 +826,11 @@ jsonScrapers: Name: data.performers.#.name Studio: Name: data.site.name + URL: data.site.url + Details: data.site.description + Aliases: data.site.aliases + Tags: + Name: data.site.tags.#.name Tags: Name: data.tags.#.tag @@ -839,6 +848,11 @@ jsonScrapers: Name: $data.performers.#.name Studio: Name: $data.site.name + URL: $data.site.url + Details: $data.site.description + Aliases: $data.site.aliases + Tags: + Name: $data.site.tags.#.name Tags: Name: $data.tags.#.tag driver: @@ -955,7 +969,10 @@ URLs ### Studio ``` +Aliases +Details Name +Tags (see Tag fields) URL ```