From 30a4f135173c1ec498bd91b7ba41f2c66c46b831 Mon Sep 17 00:00:00 2001 From: Gykes <24581046+Gykes@users.noreply.github.com> Date: Sun, 21 Dec 2025 20:32:17 -0800 Subject: [PATCH 01/17] migration --- pkg/sqlite/migrations/76_performer_career_dates.up.sql | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 pkg/sqlite/migrations/76_performer_career_dates.up.sql diff --git a/pkg/sqlite/migrations/76_performer_career_dates.up.sql b/pkg/sqlite/migrations/76_performer_career_dates.up.sql new file mode 100644 index 0000000000..681e0a9e4e --- /dev/null +++ b/pkg/sqlite/migrations/76_performer_career_dates.up.sql @@ -0,0 +1,2 @@ +ALTER TABLE `performers` ADD COLUMN `career_start` integer; +ALTER TABLE `performers` ADD COLUMN `career_end` integer; From 163f59e2da4c372c44b02da9ca88e065c3f051d1 Mon Sep 17 00:00:00 2001 From: Gykes <24581046+Gykes@users.noreply.github.com> Date: Sun, 21 Dec 2025 20:34:30 -0800 Subject: [PATCH 02/17] schema --- graphql/schema/types/performer.graphql | 8 ++++++++ pkg/models/model_performer.go | 4 ++++ 2 files changed, 12 insertions(+) diff --git a/graphql/schema/types/performer.graphql b/graphql/schema/types/performer.graphql index fbb67ce8f0..38382f097f 100644 --- a/graphql/schema/types/performer.graphql +++ b/graphql/schema/types/performer.graphql @@ -31,6 +31,8 @@ type Performer { penis_length: Float circumcised: CircumisedEnum career_length: String + career_start: Int + career_end: Int tattoos: String piercings: String alias_list: [String!]! @@ -78,6 +80,8 @@ input PerformerCreateInput { penis_length: Float circumcised: CircumisedEnum career_length: String + career_start: Int + career_end: Int tattoos: String piercings: String alias_list: [String!] @@ -116,6 +120,8 @@ input PerformerUpdateInput { penis_length: Float circumcised: CircumisedEnum career_length: String + career_start: Int + career_end: Int tattoos: String piercings: String alias_list: [String!] @@ -159,6 +165,8 @@ input BulkPerformerUpdateInput { penis_length: Float circumcised: CircumisedEnum career_length: String + career_start: Int + career_end: Int tattoos: String piercings: String alias_list: BulkUpdateStrings diff --git a/pkg/models/model_performer.go b/pkg/models/model_performer.go index 566dcae1ef..29fb837971 100644 --- a/pkg/models/model_performer.go +++ b/pkg/models/model_performer.go @@ -20,6 +20,8 @@ type Performer struct { PenisLength *float64 `json:"penis_length"` Circumcised *CircumisedEnum `json:"circumcised"` CareerLength string `json:"career_length"` + CareerStart *int `json:"career_start"` + CareerEnd *int `json:"career_end"` Tattoos string `json:"tattoos"` Piercings string `json:"piercings"` Favorite bool `json:"favorite"` @@ -76,6 +78,8 @@ type PerformerPartial struct { PenisLength OptionalFloat64 Circumcised OptionalString CareerLength OptionalString + CareerStart OptionalInt + CareerEnd OptionalInt Tattoos OptionalString Piercings OptionalString Favorite OptionalBool From 7ef0d5b8998eedd0e552b7a2b19ecd4d01aab93e Mon Sep 17 00:00:00 2001 From: Gykes <24581046+Gykes@users.noreply.github.com> Date: Sun, 21 Dec 2025 20:36:41 -0800 Subject: [PATCH 03/17] sqlite --- pkg/sqlite/performer.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/pkg/sqlite/performer.go b/pkg/sqlite/performer.go index bf6b780b25..cedc06dc8d 100644 --- a/pkg/sqlite/performer.go +++ b/pkg/sqlite/performer.go @@ -45,6 +45,8 @@ type performerRow struct { PenisLength null.Float `db:"penis_length"` Circumcised zero.String `db:"circumcised"` CareerLength zero.String `db:"career_length"` + CareerStart null.Int `db:"career_start"` + CareerEnd null.Int `db:"career_end"` Tattoos zero.String `db:"tattoos"` Piercings zero.String `db:"piercings"` Favorite bool `db:"favorite"` @@ -83,6 +85,8 @@ func (r *performerRow) fromPerformer(o models.Performer) { r.Circumcised = zero.StringFrom(o.Circumcised.String()) } r.CareerLength = zero.StringFrom(o.CareerLength) + r.CareerStart = intFromPtr(o.CareerStart) + r.CareerEnd = intFromPtr(o.CareerEnd) r.Tattoos = zero.StringFrom(o.Tattoos) r.Piercings = zero.StringFrom(o.Piercings) r.Favorite = o.Favorite @@ -111,6 +115,8 @@ func (r *performerRow) resolve() *models.Performer { FakeTits: r.FakeTits.String, PenisLength: nullFloatPtr(r.PenisLength), CareerLength: r.CareerLength.String, + CareerStart: nullIntPtr(r.CareerStart), + CareerEnd: nullIntPtr(r.CareerEnd), Tattoos: r.Tattoos.String, Piercings: r.Piercings.String, Favorite: r.Favorite, @@ -156,6 +162,8 @@ func (r *performerRowRecord) fromPartial(o models.PerformerPartial) { r.setNullFloat64("penis_length", o.PenisLength) r.setNullString("circumcised", o.Circumcised) r.setNullString("career_length", o.CareerLength) + r.setNullInt("career_start", o.CareerStart) + r.setNullInt("career_end", o.CareerEnd) r.setNullString("tattoos", o.Tattoos) r.setNullString("piercings", o.Piercings) r.setBool("favorite", o.Favorite) @@ -755,6 +763,8 @@ func (qb *PerformerStore) sortByScenesDuration(direction string) string { var performerSortOptions = sortOptions{ "birthdate", "career_length", + "career_start", + "career_end", "created_at", "galleries_count", "height", From 638f34e6f58cb300220bd18c69b27031606f79c2 Mon Sep 17 00:00:00 2001 From: Gykes <24581046+Gykes@users.noreply.github.com> Date: Sun, 21 Dec 2025 20:39:28 -0800 Subject: [PATCH 04/17] api --- internal/api/resolver_mutation_performer.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/internal/api/resolver_mutation_performer.go b/internal/api/resolver_mutation_performer.go index c54e3ca93b..05c949c9f0 100644 --- a/internal/api/resolver_mutation_performer.go +++ b/internal/api/resolver_mutation_performer.go @@ -50,6 +50,8 @@ func (r *mutationResolver) PerformerCreate(ctx context.Context, input models.Per newPerformer.PenisLength = input.PenisLength newPerformer.Circumcised = input.Circumcised newPerformer.CareerLength = translator.string(input.CareerLength) + newPerformer.CareerStart = input.CareerStart + newPerformer.CareerEnd = input.CareerEnd newPerformer.Tattoos = translator.string(input.Tattoos) newPerformer.Piercings = translator.string(input.Piercings) newPerformer.Favorite = translator.bool(input.Favorite) @@ -250,6 +252,8 @@ func (r *mutationResolver) PerformerUpdate(ctx context.Context, input models.Per updatedPerformer.PenisLength = translator.optionalFloat64(input.PenisLength, "penis_length") updatedPerformer.Circumcised = translator.optionalString((*string)(input.Circumcised), "circumcised") updatedPerformer.CareerLength = translator.optionalString(input.CareerLength, "career_length") + updatedPerformer.CareerStart = translator.optionalInt(input.CareerStart, "career_start") + updatedPerformer.CareerEnd = translator.optionalInt(input.CareerEnd, "career_end") updatedPerformer.Tattoos = translator.optionalString(input.Tattoos, "tattoos") updatedPerformer.Piercings = translator.optionalString(input.Piercings, "piercings") updatedPerformer.Favorite = translator.optionalBool(input.Favorite, "favorite") @@ -367,6 +371,8 @@ func (r *mutationResolver) BulkPerformerUpdate(ctx context.Context, input BulkPe updatedPerformer.PenisLength = translator.optionalFloat64(input.PenisLength, "penis_length") updatedPerformer.Circumcised = translator.optionalString((*string)(input.Circumcised), "circumcised") updatedPerformer.CareerLength = translator.optionalString(input.CareerLength, "career_length") + updatedPerformer.CareerStart = translator.optionalInt(input.CareerStart, "career_start") + updatedPerformer.CareerEnd = translator.optionalInt(input.CareerEnd, "career_end") updatedPerformer.Tattoos = translator.optionalString(input.Tattoos, "tattoos") updatedPerformer.Piercings = translator.optionalString(input.Piercings, "piercings") From fde4fd6508354dc5c5ccd572219d72cad3b4ada7 Mon Sep 17 00:00:00 2001 From: Gykes <24581046+Gykes@users.noreply.github.com> Date: Sun, 21 Dec 2025 20:40:21 -0800 Subject: [PATCH 05/17] scraper update --- pkg/scraper/performer.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkg/scraper/performer.go b/pkg/scraper/performer.go index 98e9317620..e052404530 100644 --- a/pkg/scraper/performer.go +++ b/pkg/scraper/performer.go @@ -20,6 +20,8 @@ type ScrapedPerformerInput struct { PenisLength *string `json:"penis_length"` Circumcised *string `json:"circumcised"` CareerLength *string `json:"career_length"` + CareerStart *string `json:"career_start"` + CareerEnd *string `json:"career_end"` Tattoos *string `json:"tattoos"` Piercings *string `json:"piercings"` Aliases *string `json:"aliases"` From 61fea75b0fdfdba3f7835401fdbe903d73dcdce8 Mon Sep 17 00:00:00 2001 From: Gykes <24581046+Gykes@users.noreply.github.com> Date: Sun, 21 Dec 2025 20:42:25 -0800 Subject: [PATCH 06/17] Performer Edit Panel --- .../PerformerDetails/PerformerEditPanel.tsx | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/ui/v2.5/src/components/Performers/PerformerDetails/PerformerEditPanel.tsx b/ui/v2.5/src/components/Performers/PerformerDetails/PerformerEditPanel.tsx index f2d825e07f..c28b062217 100644 --- a/ui/v2.5/src/components/Performers/PerformerDetails/PerformerEditPanel.tsx +++ b/ui/v2.5/src/components/Performers/PerformerDetails/PerformerEditPanel.tsx @@ -124,6 +124,8 @@ export const PerformerEditPanel: React.FC = ({ tattoos: yup.string().ensure(), piercings: yup.string().ensure(), career_length: yup.string().ensure(), + career_start: yupInputNumber().positive().nullable().defined(), + career_end: yupInputNumber().positive().nullable().defined(), urls: yupUniqueStringList(intl), details: yup.string().ensure(), tag_ids: yup.array(yup.string().required()).defined(), @@ -153,6 +155,8 @@ export const PerformerEditPanel: React.FC = ({ tattoos: performer.tattoos ?? "", piercings: performer.piercings ?? "", career_length: performer.career_length ?? "", + career_start: performer.career_start ?? null, + career_end: performer.career_end ?? null, urls: performer.urls ?? [], details: performer.details ?? "", tag_ids: (performer.tags ?? []).map((t) => t.id), @@ -256,6 +260,12 @@ export const PerformerEditPanel: React.FC = ({ if (state.career_length) { formik.setFieldValue("career_length", state.career_length); } + if (state.career_start) { + formik.setFieldValue("career_start", parseInt(state.career_start, 10)); + } + if (state.career_end) { + formik.setFieldValue("career_end", parseInt(state.career_end, 10)); + } if (state.tattoos) { formik.setFieldValue("tattoos", state.tattoos); } @@ -719,6 +729,8 @@ export const PerformerEditPanel: React.FC = ({ {renderInputField("piercings", "textarea")} {renderInputField("career_length")} + {renderInputField("career_start", "number")} + {renderInputField("career_end", "number")} {renderURLListField("urls", onScrapePerformerURL, urlScrapable)} From 9b87ca8d449986dc917b433e68fe917994c3559f Mon Sep 17 00:00:00 2001 From: Gykes <24581046+Gykes@users.noreply.github.com> Date: Sun, 21 Dec 2025 20:43:27 -0800 Subject: [PATCH 07/17] performer graphql --- ui/v2.5/graphql/data/performer.graphql | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ui/v2.5/graphql/data/performer.graphql b/ui/v2.5/graphql/data/performer.graphql index 035c8abc72..4a197790a4 100644 --- a/ui/v2.5/graphql/data/performer.graphql +++ b/ui/v2.5/graphql/data/performer.graphql @@ -14,6 +14,8 @@ fragment PerformerData on Performer { penis_length circumcised career_length + career_start + career_end tattoos piercings alias_list From bd9b9e593edcbc0b7b4fcb5727640cf691425d0f Mon Sep 17 00:00:00 2001 From: Gykes <24581046+Gykes@users.noreply.github.com> Date: Sun, 21 Dec 2025 20:44:14 -0800 Subject: [PATCH 08/17] localization --- ui/v2.5/src/locales/en-GB.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ui/v2.5/src/locales/en-GB.json b/ui/v2.5/src/locales/en-GB.json index 21d3f6a241..66b109ad8d 100644 --- a/ui/v2.5/src/locales/en-GB.json +++ b/ui/v2.5/src/locales/en-GB.json @@ -174,7 +174,9 @@ "filesystem": "Filesystem" }, "captions": "Captions", + "career_end": "Career End", "career_length": "Career Length", + "career_start": "Career Start", "chapters": "Chapters", "circumcised": "Circumcised", "circumcised_types": { From 15a46dab244c6f1f34701f567f3dc972c06a6d0e Mon Sep 17 00:00:00 2001 From: Gykes <24581046+Gykes@users.noreply.github.com> Date: Sun, 21 Dec 2025 20:54:00 -0800 Subject: [PATCH 09/17] PerformerCreateInputs update --- pkg/models/performer.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkg/models/performer.go b/pkg/models/performer.go index 239d8347fc..5cf50fb527 100644 --- a/pkg/models/performer.go +++ b/pkg/models/performer.go @@ -222,6 +222,8 @@ type PerformerCreateInput struct { PenisLength *float64 `json:"penis_length"` Circumcised *CircumisedEnum `json:"circumcised"` CareerLength *string `json:"career_length"` + CareerStart *int `json:"career_start"` + CareerEnd *int `json:"career_end"` Tattoos *string `json:"tattoos"` Piercings *string `json:"piercings"` Aliases *string `json:"aliases"` @@ -261,6 +263,8 @@ type PerformerUpdateInput struct { PenisLength *float64 `json:"penis_length"` Circumcised *CircumisedEnum `json:"circumcised"` CareerLength *string `json:"career_length"` + CareerStart *int `json:"career_start"` + CareerEnd *int `json:"career_end"` Tattoos *string `json:"tattoos"` Piercings *string `json:"piercings"` Aliases *string `json:"aliases"` From 1b9012c6713677dc188b3c39159f19e4b2b4a18e Mon Sep 17 00:00:00 2001 From: Gykes <24581046+Gykes@users.noreply.github.com> Date: Sun, 21 Dec 2025 21:23:05 -0800 Subject: [PATCH 10/17] schema plus migration updates --- pkg/sqlite/database.go | 2 +- pkg/sqlite/migrations/76_performer_career_dates.up.sql | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/sqlite/database.go b/pkg/sqlite/database.go index 0ea3d71700..a87f6706fc 100644 --- a/pkg/sqlite/database.go +++ b/pkg/sqlite/database.go @@ -34,7 +34,7 @@ const ( cacheSizeEnv = "STASH_SQLITE_CACHE_SIZE" ) -var appSchemaVersion uint = 75 +var appSchemaVersion uint = 76 //go:embed migrations/*.sql var migrationsBox embed.FS diff --git a/pkg/sqlite/migrations/76_performer_career_dates.up.sql b/pkg/sqlite/migrations/76_performer_career_dates.up.sql index 681e0a9e4e..006d9fae79 100644 --- a/pkg/sqlite/migrations/76_performer_career_dates.up.sql +++ b/pkg/sqlite/migrations/76_performer_career_dates.up.sql @@ -1,2 +1,2 @@ -ALTER TABLE `performers` ADD COLUMN `career_start` integer; -ALTER TABLE `performers` ADD COLUMN `career_end` integer; +ALTER TABLE "performers" ADD COLUMN "career_start" integer; +ALTER TABLE "performers" ADD COLUMN "career_end" integer; From 720e0cf90fac32fafca3549eda6625f2c378d63a Mon Sep 17 00:00:00 2001 From: Gykes <24581046+Gykes@users.noreply.github.com> Date: Sun, 21 Dec 2025 21:53:01 -0800 Subject: [PATCH 11/17] update career length --- internal/api/resolver_model_performer.go | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/internal/api/resolver_model_performer.go b/internal/api/resolver_model_performer.go index 94da629323..0a00eefb56 100644 --- a/internal/api/resolver_model_performer.go +++ b/internal/api/resolver_model_performer.go @@ -2,6 +2,7 @@ package api import ( "context" + "fmt" "strconv" "github.com/stashapp/stash/internal/api/loaders" @@ -109,6 +110,24 @@ func (r *performerResolver) HeightCm(ctx context.Context, obj *models.Performer) return obj.Height, nil } +func (r *performerResolver) CareerLength(ctx context.Context, obj *models.Performer) (*string, error) { + if obj.CareerStart == nil && obj.CareerEnd == nil { + return nil, nil + } + + var ret string + switch { + case obj.CareerEnd == nil: + ret = fmt.Sprintf("%d -", *obj.CareerStart) + case obj.CareerStart == nil: + ret = fmt.Sprintf("- %d", *obj.CareerEnd) + default: + ret = fmt.Sprintf("%d - %d", *obj.CareerStart, *obj.CareerEnd) + } + + return &ret, nil +} + func (r *performerResolver) Birthdate(ctx context.Context, obj *models.Performer) (*string, error) { if obj.Birthdate != nil { ret := obj.Birthdate.String() From 027d047c31836836a417c4833b578b019a66ff55 Mon Sep 17 00:00:00 2001 From: Gykes <24581046+Gykes@users.noreply.github.com> Date: Sun, 21 Dec 2025 21:57:26 -0800 Subject: [PATCH 12/17] depreciation --- graphql/schema/types/performer.graphql | 8 ++++---- .../Performers/PerformerDetails/PerformerEditPanel.tsx | 6 ------ 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/graphql/schema/types/performer.graphql b/graphql/schema/types/performer.graphql index 38382f097f..015529b223 100644 --- a/graphql/schema/types/performer.graphql +++ b/graphql/schema/types/performer.graphql @@ -30,7 +30,7 @@ type Performer { fake_tits: String penis_length: Float circumcised: CircumisedEnum - career_length: String + career_length: String @deprecated(reason: "Use career_start and career_end") career_start: Int career_end: Int tattoos: String @@ -79,7 +79,7 @@ input PerformerCreateInput { fake_tits: String penis_length: Float circumcised: CircumisedEnum - career_length: String + career_length: String @deprecated(reason: "Use career_start and career_end") career_start: Int career_end: Int tattoos: String @@ -119,7 +119,7 @@ input PerformerUpdateInput { fake_tits: String penis_length: Float circumcised: CircumisedEnum - career_length: String + career_length: String @deprecated(reason: "Use career_start and career_end") career_start: Int career_end: Int tattoos: String @@ -164,7 +164,7 @@ input BulkPerformerUpdateInput { fake_tits: String penis_length: Float circumcised: CircumisedEnum - career_length: String + career_length: String @deprecated(reason: "Use career_start and career_end") career_start: Int career_end: Int tattoos: String diff --git a/ui/v2.5/src/components/Performers/PerformerDetails/PerformerEditPanel.tsx b/ui/v2.5/src/components/Performers/PerformerDetails/PerformerEditPanel.tsx index c28b062217..b29d427527 100644 --- a/ui/v2.5/src/components/Performers/PerformerDetails/PerformerEditPanel.tsx +++ b/ui/v2.5/src/components/Performers/PerformerDetails/PerformerEditPanel.tsx @@ -123,7 +123,6 @@ export const PerformerEditPanel: React.FC = ({ circumcised: yupInputEnum(GQL.CircumisedEnum).nullable().defined(), tattoos: yup.string().ensure(), piercings: yup.string().ensure(), - career_length: yup.string().ensure(), career_start: yupInputNumber().positive().nullable().defined(), career_end: yupInputNumber().positive().nullable().defined(), urls: yupUniqueStringList(intl), @@ -154,7 +153,6 @@ export const PerformerEditPanel: React.FC = ({ circumcised: performer.circumcised ?? null, tattoos: performer.tattoos ?? "", piercings: performer.piercings ?? "", - career_length: performer.career_length ?? "", career_start: performer.career_start ?? null, career_end: performer.career_end ?? null, urls: performer.urls ?? [], @@ -257,9 +255,6 @@ export const PerformerEditPanel: React.FC = ({ if (state.fake_tits) { formik.setFieldValue("fake_tits", state.fake_tits); } - if (state.career_length) { - formik.setFieldValue("career_length", state.career_length); - } if (state.career_start) { formik.setFieldValue("career_start", parseInt(state.career_start, 10)); } @@ -728,7 +723,6 @@ export const PerformerEditPanel: React.FC = ({ {renderInputField("tattoos", "textarea")} {renderInputField("piercings", "textarea")} - {renderInputField("career_length")} {renderInputField("career_start", "number")} {renderInputField("career_end", "number")} From 602fc96d8a758a32dca3de95eba4111d0ba53abd Mon Sep 17 00:00:00 2001 From: Gykes <24581046+Gykes@users.noreply.github.com> Date: Sun, 21 Dec 2025 22:00:08 -0800 Subject: [PATCH 13/17] scrapers + stashboxes --- .../schema/types/scraped-performer.graphql | 8 ++++++-- pkg/models/model_scraped_item.go | 14 +++++++++++++ pkg/stashbox/performer.go | 20 ++++++++++++++++++- 3 files changed, 39 insertions(+), 3 deletions(-) diff --git a/graphql/schema/types/scraped-performer.graphql b/graphql/schema/types/scraped-performer.graphql index 487c89516d..799b5cd6e7 100644 --- a/graphql/schema/types/scraped-performer.graphql +++ b/graphql/schema/types/scraped-performer.graphql @@ -18,7 +18,9 @@ type ScrapedPerformer { fake_tits: String penis_length: String circumcised: String - career_length: String + career_length: String @deprecated(reason: "Use career_start and career_end") + career_start: String + career_end: String tattoos: String piercings: String # aliases must be comma-delimited to be parsed correctly @@ -54,7 +56,9 @@ input ScrapedPerformerInput { fake_tits: String penis_length: String circumcised: String - career_length: String + career_length: String @deprecated(reason: "Use career_start and career_end") + career_start: String + career_end: String tattoos: String piercings: String aliases: String diff --git a/pkg/models/model_scraped_item.go b/pkg/models/model_scraped_item.go index 4254a98769..212aac5773 100644 --- a/pkg/models/model_scraped_item.go +++ b/pkg/models/model_scraped_item.go @@ -177,6 +177,8 @@ type ScrapedPerformer struct { PenisLength *string `json:"penis_length"` Circumcised *string `json:"circumcised"` CareerLength *string `json:"career_length"` + CareerStart *string `json:"career_start"` + CareerEnd *string `json:"career_end"` Tattoos *string `json:"tattoos"` Piercings *string `json:"piercings"` Aliases *string `json:"aliases"` @@ -222,6 +224,18 @@ func (p *ScrapedPerformer) ToPerformer(endpoint string, excluded map[string]bool if p.CareerLength != nil && !excluded["career_length"] { ret.CareerLength = *p.CareerLength } + if p.CareerStart != nil && !excluded["career_start"] { + cs, err := strconv.Atoi(*p.CareerStart) + if err == nil { + ret.CareerStart = &cs + } + } + if p.CareerEnd != nil && !excluded["career_end"] { + ce, err := strconv.Atoi(*p.CareerEnd) + if err == nil { + ret.CareerEnd = &ce + } + } if p.Country != nil && !excluded["country"] { ret.Country = *p.Country } diff --git a/pkg/stashbox/performer.go b/pkg/stashbox/performer.go index 38824eba14..9144a20f78 100644 --- a/pkg/stashbox/performer.go +++ b/pkg/stashbox/performer.go @@ -231,6 +231,16 @@ func performerFragmentToScrapedPerformer(p graphql.PerformerFragment) *models.Sc sp.Height = &hs } + if p.CareerStartYear != nil { + cs := strconv.Itoa(*p.CareerStartYear) + sp.CareerStart = &cs + } + + if p.CareerEndYear != nil { + ce := strconv.Itoa(*p.CareerEndYear) + sp.CareerEnd = &ce + } + if p.BirthDate != nil { sp.Birthdate = padFuzzyDate(p.BirthDate) } @@ -388,7 +398,15 @@ func (c Client) SubmitPerformerDraft(ctx context.Context, performer *models.Perf aliases := strings.Join(performer.Aliases.List(), ",") draft.Aliases = &aliases } - if performer.CareerLength != "" { + // Use CareerStart and CareerEnd directly if available + if performer.CareerStart != nil { + draft.CareerStartYear = performer.CareerStart + } + if performer.CareerEnd != nil { + draft.CareerEndYear = performer.CareerEnd + } + // Fall back to parsing CareerLength for backwards compatibility + if draft.CareerStartYear == nil && draft.CareerEndYear == nil && performer.CareerLength != "" { var career = strings.Split(performer.CareerLength, "-") if i, err := strconv.Atoi(strings.TrimSpace(career[0])); err == nil { draft.CareerStartYear = &i From b2404d1a9de074fa36103caf9f508471bb041135 Mon Sep 17 00:00:00 2001 From: Gykes <24581046+Gykes@users.noreply.github.com> Date: Sun, 21 Dec 2025 22:07:51 -0800 Subject: [PATCH 14/17] backwards compat --- gqlgen.yml | 4 ++++ internal/api/resolver_model_performer.go | 27 ++++++++++++++---------- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/gqlgen.yml b/gqlgen.yml index b949d44dcd..4a3d73d519 100644 --- a/gqlgen.yml +++ b/gqlgen.yml @@ -140,4 +140,8 @@ models: fields: plugins: resolver: true + Performer: + fields: + career_length: + resolver: true diff --git a/internal/api/resolver_model_performer.go b/internal/api/resolver_model_performer.go index 0a00eefb56..19db6736c9 100644 --- a/internal/api/resolver_model_performer.go +++ b/internal/api/resolver_model_performer.go @@ -111,21 +111,26 @@ func (r *performerResolver) HeightCm(ctx context.Context, obj *models.Performer) } func (r *performerResolver) CareerLength(ctx context.Context, obj *models.Performer) (*string, error) { - if obj.CareerStart == nil && obj.CareerEnd == nil { - return nil, nil + // Compute from CareerStart and CareerEnd if available + if obj.CareerStart != nil || obj.CareerEnd != nil { + var ret string + switch { + case obj.CareerEnd == nil: + ret = fmt.Sprintf("%d -", *obj.CareerStart) + case obj.CareerStart == nil: + ret = fmt.Sprintf("- %d", *obj.CareerEnd) + default: + ret = fmt.Sprintf("%d - %d", *obj.CareerStart, *obj.CareerEnd) + } + return &ret, nil } - var ret string - switch { - case obj.CareerEnd == nil: - ret = fmt.Sprintf("%d -", *obj.CareerStart) - case obj.CareerStart == nil: - ret = fmt.Sprintf("- %d", *obj.CareerEnd) - default: - ret = fmt.Sprintf("%d - %d", *obj.CareerStart, *obj.CareerEnd) + // Fall back to stored CareerLength for backwards compatibility + if obj.CareerLength != "" { + return &obj.CareerLength, nil } - return &ret, nil + return nil, nil } func (r *performerResolver) Birthdate(ctx context.Context, obj *models.Performer) (*string, error) { From cffd8fcdb1e7a81b13e9e29f56b46afb94315084 Mon Sep 17 00:00:00 2001 From: Gykes <24581046+Gykes@users.noreply.github.com> Date: Sun, 21 Dec 2025 22:20:47 -0800 Subject: [PATCH 15/17] scraper modal --- ui/v2.5/graphql/data/scrapers.graphql | 4 +++ .../PerformerScrapeDialog.tsx | 28 +++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/ui/v2.5/graphql/data/scrapers.graphql b/ui/v2.5/graphql/data/scrapers.graphql index 4a0f588a43..5afa0b545e 100644 --- a/ui/v2.5/graphql/data/scrapers.graphql +++ b/ui/v2.5/graphql/data/scrapers.graphql @@ -39,6 +39,8 @@ fragment ScrapedPerformerData on ScrapedPerformer { penis_length circumcised career_length + career_start + career_end tattoos piercings aliases @@ -69,6 +71,8 @@ fragment ScrapedScenePerformerData on ScrapedPerformer { penis_length circumcised career_length + career_start + career_end tattoos piercings aliases diff --git a/ui/v2.5/src/components/Performers/PerformerDetails/PerformerScrapeDialog.tsx b/ui/v2.5/src/components/Performers/PerformerDetails/PerformerScrapeDialog.tsx index b3ec4bff6b..16644866ad 100644 --- a/ui/v2.5/src/components/Performers/PerformerDetails/PerformerScrapeDialog.tsx +++ b/ui/v2.5/src/components/Performers/PerformerDetails/PerformerScrapeDialog.tsx @@ -278,6 +278,18 @@ export const PerformerScrapeDialog: React.FC = ( props.scraped.career_length ) ); + const [careerStart, setCareerStart] = useState>( + new ScrapeResult( + props.performer.career_start?.toString(), + props.scraped.career_start + ) + ); + const [careerEnd, setCareerEnd] = useState>( + new ScrapeResult( + props.performer.career_end?.toString(), + props.scraped.career_end + ) + ); const [tattoos, setTattoos] = useState>( new ScrapeResult(props.performer.tattoos, props.scraped.tattoos) ); @@ -348,6 +360,8 @@ export const PerformerScrapeDialog: React.FC = ( penisLength, circumcised, careerLength, + careerStart, + careerEnd, tattoos, piercings, urls, @@ -380,6 +394,8 @@ export const PerformerScrapeDialog: React.FC = ( measurements: measurements.getNewValue(), fake_tits: fakeTits.getNewValue(), career_length: careerLength.getNewValue(), + career_start: careerStart.getNewValue(), + career_end: careerEnd.getNewValue(), tattoos: tattoos.getNewValue(), piercings: piercings.getNewValue(), urls: urls.getNewValue(), @@ -499,6 +515,18 @@ export const PerformerScrapeDialog: React.FC = ( result={careerLength} onChange={(value) => setCareerLength(value)} /> + setCareerStart(value)} + /> + setCareerEnd(value)} + /> Date: Sun, 21 Dec 2025 22:22:26 -0800 Subject: [PATCH 16/17] depreciate careerLength in scrapers --- .../PerformerDetails/PerformerScrapeDialog.tsx | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/ui/v2.5/src/components/Performers/PerformerDetails/PerformerScrapeDialog.tsx b/ui/v2.5/src/components/Performers/PerformerDetails/PerformerScrapeDialog.tsx index 16644866ad..ac9c5409bc 100644 --- a/ui/v2.5/src/components/Performers/PerformerDetails/PerformerScrapeDialog.tsx +++ b/ui/v2.5/src/components/Performers/PerformerDetails/PerformerScrapeDialog.tsx @@ -272,12 +272,6 @@ export const PerformerScrapeDialog: React.FC = ( const [fakeTits, setFakeTits] = useState>( new ScrapeResult(props.performer.fake_tits, props.scraped.fake_tits) ); - const [careerLength, setCareerLength] = useState>( - new ScrapeResult( - props.performer.career_length, - props.scraped.career_length - ) - ); const [careerStart, setCareerStart] = useState>( new ScrapeResult( props.performer.career_start?.toString(), @@ -359,7 +353,6 @@ export const PerformerScrapeDialog: React.FC = ( fakeTits, penisLength, circumcised, - careerLength, careerStart, careerEnd, tattoos, @@ -393,7 +386,6 @@ export const PerformerScrapeDialog: React.FC = ( height: height.getNewValue(), measurements: measurements.getNewValue(), fake_tits: fakeTits.getNewValue(), - career_length: careerLength.getNewValue(), career_start: careerStart.getNewValue(), career_end: careerEnd.getNewValue(), tattoos: tattoos.getNewValue(), @@ -509,12 +501,6 @@ export const PerformerScrapeDialog: React.FC = ( result={fakeTits} onChange={(value) => setFakeTits(value)} /> - setCareerLength(value)} - /> Date: Sun, 21 Dec 2025 22:25:37 -0800 Subject: [PATCH 17/17] add to stashbox --- ui/v2.5/src/components/Tagger/PerformerModal.tsx | 10 ++++++++-- ui/v2.5/src/components/Tagger/constants.ts | 3 ++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/ui/v2.5/src/components/Tagger/PerformerModal.tsx b/ui/v2.5/src/components/Tagger/PerformerModal.tsx index 79f80708af..64485818df 100755 --- a/ui/v2.5/src/components/Tagger/PerformerModal.tsx +++ b/ui/v2.5/src/components/Tagger/PerformerModal.tsx @@ -240,7 +240,12 @@ const PerformerModal: React.FC = ({ height_cm: Number.parseFloat(performer.height ?? "") ?? undefined, measurements: performer.measurements, fake_tits: performer.fake_tits, - career_length: performer.career_length, + career_start: performer.career_start + ? Number.parseInt(performer.career_start, 10) + : undefined, + career_end: performer.career_end + ? Number.parseInt(performer.career_end, 10) + : undefined, tattoos: performer.tattoos, piercings: performer.piercings, urls: performer.urls, @@ -326,7 +331,8 @@ const PerformerModal: React.FC = ({ {maybeRenderField("measurements", performer.measurements)} {performer?.gender !== GQL.GenderEnum.Male && maybeRenderField("fake_tits", performer.fake_tits)} - {maybeRenderField("career_length", performer.career_length)} + {maybeRenderField("career_start", performer.career_start)} + {maybeRenderField("career_end", performer.career_end)} {maybeRenderField("tattoos", performer.tattoos, false)} {maybeRenderField("piercings", performer.piercings, false)} {maybeRenderField("weight", performer.weight, false)} diff --git a/ui/v2.5/src/components/Tagger/constants.ts b/ui/v2.5/src/components/Tagger/constants.ts index d499062aa6..d59a6d3d53 100644 --- a/ui/v2.5/src/components/Tagger/constants.ts +++ b/ui/v2.5/src/components/Tagger/constants.ts @@ -75,7 +75,8 @@ export const PERFORMER_FIELDS = [ "fake_tits", "tattoos", "piercings", - "career_length", + "career_start", + "career_end", "urls", "details", ];