update merged performer upon batch update (#5664)

* update merged performer upon batch update
* Handle aliases and name for merged performer
* Refactor merge performer code

Log when merging performers
---------
Co-authored-by: WithoutPants <53250216+WithoutPants@users.noreply.github.com>
This commit is contained in:
fume8866
2025-02-26 22:23:48 -05:00
committed by GitHub
parent 661e9eba51
commit 4d447c3340
5 changed files with 92 additions and 17 deletions

View File

@@ -49,6 +49,8 @@ fragment PerformerFragment on Performer {
aliases aliases
gender gender
merged_ids merged_ids
deleted
merged_into_id
urls { urls {
...URLFragment ...URLFragment
} }

View File

@@ -9,6 +9,7 @@ import (
"github.com/stashapp/stash/pkg/models" "github.com/stashapp/stash/pkg/models"
"github.com/stashapp/stash/pkg/performer" "github.com/stashapp/stash/pkg/performer"
"github.com/stashapp/stash/pkg/scraper/stashbox" "github.com/stashapp/stash/pkg/scraper/stashbox"
"github.com/stashapp/stash/pkg/sliceutil"
"github.com/stashapp/stash/pkg/studio" "github.com/stashapp/stash/pkg/studio"
) )
@@ -119,6 +120,18 @@ func (t *StashBoxBatchTagTask) findStashBoxPerformer(ctx context.Context) (*mode
} }
if remoteID != "" { if remoteID != "" {
performer, err = client.FindStashBoxPerformerByID(ctx, remoteID) performer, err = client.FindStashBoxPerformerByID(ctx, remoteID)
if performer != nil && performer.RemoteMergedIntoId != nil {
mergedPerformer, err := t.handleMergedPerformer(ctx, performer, client)
if err != nil {
return nil, err
}
if mergedPerformer != nil {
logger.Infof("Performer id %s merged into %s, updating local performer", remoteID, *performer.RemoteMergedIntoId)
performer = mergedPerformer
}
}
} }
} else { } else {
var name string var name string
@@ -133,6 +146,21 @@ func (t *StashBoxBatchTagTask) findStashBoxPerformer(ctx context.Context) (*mode
return performer, err return performer, err
} }
func (t *StashBoxBatchTagTask) handleMergedPerformer(ctx context.Context, performer *models.ScrapedPerformer, client *stashbox.Client) (mergedPerformer *models.ScrapedPerformer, err error) {
mergedPerformer, err = client.FindStashBoxPerformerByID(ctx, *performer.RemoteMergedIntoId)
if err != nil {
return nil, fmt.Errorf("loading merged performer %s from stashbox", *performer.RemoteMergedIntoId)
}
if mergedPerformer.StoredID != nil && *mergedPerformer.StoredID != *performer.StoredID {
logger.Warnf("Performer %s merged into %s, but both exist locally, not merging", *performer.StoredID, *mergedPerformer.StoredID)
return nil, nil
}
mergedPerformer.StoredID = performer.StoredID
return mergedPerformer, nil
}
func (t *StashBoxBatchTagTask) processMatchedPerformer(ctx context.Context, p *models.ScrapedPerformer, excluded map[string]bool) { func (t *StashBoxBatchTagTask) processMatchedPerformer(ctx context.Context, p *models.ScrapedPerformer, excluded map[string]bool) {
// Refreshing an existing performer // Refreshing an existing performer
if t.performer != nil { if t.performer != nil {
@@ -156,6 +184,19 @@ func (t *StashBoxBatchTagTask) processMatchedPerformer(ctx context.Context, p *m
partial := p.ToPartial(t.box.Endpoint, excluded, existingStashIDs) partial := p.ToPartial(t.box.Endpoint, excluded, existingStashIDs)
// if we're setting the performer's aliases, and not the name, then filter out the name
// from the aliases to avoid duplicates
// add the name to the aliases if it's not already there
if partial.Aliases != nil && !partial.Name.Set {
partial.Aliases.Values = sliceutil.Filter(partial.Aliases.Values, func(s string) bool {
return s != t.performer.Name
})
if p.Name != nil && t.performer.Name != *p.Name {
partial.Aliases.Values = sliceutil.AppendUnique(partial.Aliases.Values, *p.Name)
}
}
if err := performer.ValidateUpdate(ctx, t.performer.ID, partial, qb); err != nil { if err := performer.ValidateUpdate(ctx, t.performer.ID, partial, qb); err != nil {
return err return err
} }

View File

@@ -128,13 +128,15 @@ type ScrapedPerformer struct {
Aliases *string `json:"aliases"` Aliases *string `json:"aliases"`
Tags []*ScrapedTag `json:"tags"` Tags []*ScrapedTag `json:"tags"`
// This should be a base64 encoded data URL // This should be a base64 encoded data URL
Image *string `json:"image"` // deprecated: use Images Image *string `json:"image"` // deprecated: use Images
Images []string `json:"images"` Images []string `json:"images"`
Details *string `json:"details"` Details *string `json:"details"`
DeathDate *string `json:"death_date"` DeathDate *string `json:"death_date"`
HairColor *string `json:"hair_color"` HairColor *string `json:"hair_color"`
Weight *string `json:"weight"` Weight *string `json:"weight"`
RemoteSiteID *string `json:"remote_site_id"` RemoteSiteID *string `json:"remote_site_id"`
RemoteDeleted bool `json:"remote_deleted"`
RemoteMergedIntoId *string `json:"remote_merged_into_id"`
} }
func (ScrapedPerformer) IsScrapedContent() {} func (ScrapedPerformer) IsScrapedContent() {}

View File

@@ -196,6 +196,8 @@ type PerformerFragment struct {
Aliases []string "json:\"aliases\" graphql:\"aliases\"" Aliases []string "json:\"aliases\" graphql:\"aliases\""
Gender *GenderEnum "json:\"gender,omitempty\" graphql:\"gender\"" Gender *GenderEnum "json:\"gender,omitempty\" graphql:\"gender\""
MergedIds []string "json:\"merged_ids\" graphql:\"merged_ids\"" MergedIds []string "json:\"merged_ids\" graphql:\"merged_ids\""
Deleted bool "json:\"deleted\" graphql:\"deleted\""
MergedIntoID *string "json:\"merged_into_id,omitempty\" graphql:\"merged_into_id\""
Urls []*URLFragment "json:\"urls\" graphql:\"urls\"" Urls []*URLFragment "json:\"urls\" graphql:\"urls\""
Images []*ImageFragment "json:\"images\" graphql:\"images\"" Images []*ImageFragment "json:\"images\" graphql:\"images\""
BirthDate *string "json:\"birth_date,omitempty\" graphql:\"birth_date\"" BirthDate *string "json:\"birth_date,omitempty\" graphql:\"birth_date\""
@@ -249,6 +251,18 @@ func (t *PerformerFragment) GetMergedIds() []string {
} }
return t.MergedIds return t.MergedIds
} }
func (t *PerformerFragment) GetDeleted() bool {
if t == nil {
t = &PerformerFragment{}
}
return t.Deleted
}
func (t *PerformerFragment) GetMergedIntoID() *string {
if t == nil {
t = &PerformerFragment{}
}
return t.MergedIntoID
}
func (t *PerformerFragment) GetUrls() []*URLFragment { func (t *PerformerFragment) GetUrls() []*URLFragment {
if t == nil { if t == nil {
t = &PerformerFragment{} t = &PerformerFragment{}
@@ -860,6 +874,8 @@ fragment PerformerFragment on Performer {
aliases aliases
gender gender
merged_ids merged_ids
deleted
merged_into_id
urls { urls {
... URLFragment ... URLFragment
} }
@@ -993,6 +1009,8 @@ fragment PerformerFragment on Performer {
aliases aliases
gender gender
merged_ids merged_ids
deleted
merged_into_id
urls { urls {
... URLFragment ... URLFragment
} }
@@ -1126,6 +1144,8 @@ fragment PerformerFragment on Performer {
aliases aliases
gender gender
merged_ids merged_ids
deleted
merged_into_id
urls { urls {
... URLFragment ... URLFragment
} }
@@ -1259,6 +1279,8 @@ fragment PerformerFragment on Performer {
aliases aliases
gender gender
merged_ids merged_ids
deleted
merged_into_id
urls { urls {
... URLFragment ... URLFragment
} }
@@ -1331,6 +1353,8 @@ fragment PerformerFragment on Performer {
aliases aliases
gender gender
merged_ids merged_ids
deleted
merged_into_id
urls { urls {
... URLFragment ... URLFragment
} }
@@ -1408,6 +1432,8 @@ fragment PerformerFragment on Performer {
aliases aliases
gender gender
merged_ids merged_ids
deleted
merged_into_id
urls { urls {
... URLFragment ... URLFragment
} }
@@ -1546,6 +1572,8 @@ fragment PerformerFragment on Performer {
aliases aliases
gender gender
merged_ids merged_ids
deleted
merged_into_id
urls { urls {
... URLFragment ... URLFragment
} }

View File

@@ -297,16 +297,18 @@ func performerFragmentToScrapedPerformer(p graphql.PerformerFragment) *models.Sc
} }
sp := &models.ScrapedPerformer{ sp := &models.ScrapedPerformer{
Name: &p.Name, Name: &p.Name,
Disambiguation: p.Disambiguation, Disambiguation: p.Disambiguation,
Country: p.Country, Country: p.Country,
Measurements: formatMeasurements(*p.Measurements), Measurements: formatMeasurements(*p.Measurements),
CareerLength: formatCareerLength(p.CareerStartYear, p.CareerEndYear), CareerLength: formatCareerLength(p.CareerStartYear, p.CareerEndYear),
Tattoos: formatBodyModifications(p.Tattoos), Tattoos: formatBodyModifications(p.Tattoos),
Piercings: formatBodyModifications(p.Piercings), Piercings: formatBodyModifications(p.Piercings),
Twitter: findURL(p.Urls, "TWITTER"), Twitter: findURL(p.Urls, "TWITTER"),
RemoteSiteID: &p.ID, RemoteSiteID: &p.ID,
Images: images, RemoteDeleted: p.Deleted,
RemoteMergedIntoId: p.MergedIntoID,
Images: images,
// TODO - tags not currently supported // TODO - tags not currently supported
// graphql schema change to accommodate this. Leave off for now. // graphql schema change to accommodate this. Leave off for now.
} }