Performer refactor (#3057)

* Separate performer model from sqlite model
* Use GenderEnum for gender
This commit is contained in:
WithoutPants 2022-10-31 14:58:01 +11:00 committed by GitHub
parent b1fa933868
commit 270bc317cb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
45 changed files with 1558 additions and 1226 deletions

View file

@ -185,21 +185,6 @@ func (t changesetTranslator) optionalIntFromString(value *string, field string)
return models.NewOptionalInt(vv), nil
}
func (t changesetTranslator) nullBool(value *bool, field string) *sql.NullBool {
if !t.hasField(field) {
return nil
}
ret := &sql.NullBool{}
if value != nil {
ret.Bool = *value
ret.Valid = true
}
return ret
}
func (t changesetTranslator) optionalBool(value *bool, field string) models.OptionalBool {
if !t.hasField(field) {
return models.OptionalBool{}

View file

@ -10,6 +10,7 @@ import (
"github.com/stashapp/stash/internal/static"
"github.com/stashapp/stash/pkg/hash"
"github.com/stashapp/stash/pkg/logger"
"github.com/stashapp/stash/pkg/models"
)
type imageBox struct {
@ -86,7 +87,7 @@ func initialiseCustomImages() {
}
}
func getRandomPerformerImageUsingName(name, gender, customPath string) ([]byte, error) {
func getRandomPerformerImageUsingName(name string, gender models.GenderEnum, customPath string) ([]byte, error) {
var box *imageBox
// If we have a custom path, we should return a new box in the given path.
@ -95,10 +96,10 @@ func getRandomPerformerImageUsingName(name, gender, customPath string) ([]byte,
}
if box == nil {
switch strings.ToUpper(gender) {
case "FEMALE":
switch gender {
case models.GenderEnumFemale:
box = performerBox
case "MALE":
case models.GenderEnumMale:
box = performerBoxMale
default:
box = performerBox

View file

@ -2,7 +2,6 @@ package api
import (
"context"
"time"
"github.com/stashapp/stash/internal/api/urlbuilders"
"github.com/stashapp/stash/pkg/gallery"
@ -10,131 +9,14 @@ import (
"github.com/stashapp/stash/pkg/models"
)
func (r *performerResolver) Name(ctx context.Context, obj *models.Performer) (*string, error) {
if obj.Name.Valid {
return &obj.Name.String, nil
}
return nil, nil
}
func (r *performerResolver) URL(ctx context.Context, obj *models.Performer) (*string, error) {
if obj.URL.Valid {
return &obj.URL.String, nil
}
return nil, nil
}
func (r *performerResolver) Gender(ctx context.Context, obj *models.Performer) (*models.GenderEnum, error) {
var ret models.GenderEnum
if obj.Gender.Valid {
ret = models.GenderEnum(obj.Gender.String)
if ret.IsValid() {
func (r *performerResolver) Birthdate(ctx context.Context, obj *models.Performer) (*string, error) {
if obj.Birthdate != nil {
ret := obj.Birthdate.String()
return &ret, nil
}
}
return nil, nil
}
func (r *performerResolver) Twitter(ctx context.Context, obj *models.Performer) (*string, error) {
if obj.Twitter.Valid {
return &obj.Twitter.String, nil
}
return nil, nil
}
func (r *performerResolver) Instagram(ctx context.Context, obj *models.Performer) (*string, error) {
if obj.Instagram.Valid {
return &obj.Instagram.String, nil
}
return nil, nil
}
func (r *performerResolver) Birthdate(ctx context.Context, obj *models.Performer) (*string, error) {
if obj.Birthdate.Valid {
return &obj.Birthdate.String, nil
}
return nil, nil
}
func (r *performerResolver) Ethnicity(ctx context.Context, obj *models.Performer) (*string, error) {
if obj.Ethnicity.Valid {
return &obj.Ethnicity.String, nil
}
return nil, nil
}
func (r *performerResolver) Country(ctx context.Context, obj *models.Performer) (*string, error) {
if obj.Country.Valid {
return &obj.Country.String, nil
}
return nil, nil
}
func (r *performerResolver) EyeColor(ctx context.Context, obj *models.Performer) (*string, error) {
if obj.EyeColor.Valid {
return &obj.EyeColor.String, nil
}
return nil, nil
}
func (r *performerResolver) Height(ctx context.Context, obj *models.Performer) (*string, error) {
if obj.Height.Valid {
return &obj.Height.String, nil
}
return nil, nil
}
func (r *performerResolver) Measurements(ctx context.Context, obj *models.Performer) (*string, error) {
if obj.Measurements.Valid {
return &obj.Measurements.String, nil
}
return nil, nil
}
func (r *performerResolver) FakeTits(ctx context.Context, obj *models.Performer) (*string, error) {
if obj.FakeTits.Valid {
return &obj.FakeTits.String, nil
}
return nil, nil
}
func (r *performerResolver) CareerLength(ctx context.Context, obj *models.Performer) (*string, error) {
if obj.CareerLength.Valid {
return &obj.CareerLength.String, nil
}
return nil, nil
}
func (r *performerResolver) Tattoos(ctx context.Context, obj *models.Performer) (*string, error) {
if obj.Tattoos.Valid {
return &obj.Tattoos.String, nil
}
return nil, nil
}
func (r *performerResolver) Piercings(ctx context.Context, obj *models.Performer) (*string, error) {
if obj.Piercings.Valid {
return &obj.Piercings.String, nil
}
return nil, nil
}
func (r *performerResolver) Aliases(ctx context.Context, obj *models.Performer) (*string, error) {
if obj.Aliases.Valid {
return &obj.Aliases.String, nil
}
return nil, nil
}
func (r *performerResolver) Favorite(ctx context.Context, obj *models.Performer) (bool, error) {
if obj.Favorite.Valid {
return obj.Favorite.Bool, nil
}
return false, nil
}
func (r *performerResolver) ImagePath(ctx context.Context, obj *models.Performer) (*string, error) {
baseURL, _ := ctx.Value(BaseURLCtxKey).(string)
imagePath := urlbuilders.NewPerformerURLBuilder(baseURL, obj).GetPerformerImageURL()
@ -212,51 +94,14 @@ func (r *performerResolver) StashIds(ctx context.Context, obj *models.Performer)
return stashIDsSliceToPtrSlice(ret), nil
}
func (r *performerResolver) Rating(ctx context.Context, obj *models.Performer) (*int, error) {
if obj.Rating.Valid {
rating := int(obj.Rating.Int64)
return &rating, nil
}
return nil, nil
}
func (r *performerResolver) Details(ctx context.Context, obj *models.Performer) (*string, error) {
if obj.Details.Valid {
return &obj.Details.String, nil
}
return nil, nil
}
func (r *performerResolver) DeathDate(ctx context.Context, obj *models.Performer) (*string, error) {
if obj.DeathDate.Valid {
return &obj.DeathDate.String, nil
if obj.DeathDate != nil {
ret := obj.DeathDate.String()
return &ret, nil
}
return nil, nil
}
func (r *performerResolver) HairColor(ctx context.Context, obj *models.Performer) (*string, error) {
if obj.HairColor.Valid {
return &obj.HairColor.String, nil
}
return nil, nil
}
func (r *performerResolver) Weight(ctx context.Context, obj *models.Performer) (*int, error) {
if obj.Weight.Valid {
weight := int(obj.Weight.Int64)
return &weight, nil
}
return nil, nil
}
func (r *performerResolver) CreatedAt(ctx context.Context, obj *models.Performer) (*time.Time, error) {
return &obj.CreatedAt.Timestamp, nil
}
func (r *performerResolver) UpdatedAt(ctx context.Context, obj *models.Performer) (*time.Time, error) {
return &obj.UpdatedAt.Timestamp, nil
}
func (r *performerResolver) Movies(ctx context.Context, obj *models.Performer) (ret []*models.Movie, err error) {
if err := r.withTxn(ctx, func(ctx context.Context) error {
ret, err = r.repository.Movie.FindByPerformerID(ctx, obj.ID)

View file

@ -2,7 +2,6 @@ package api
import (
"context"
"database/sql"
"fmt"
"strconv"
"time"
@ -54,78 +53,75 @@ func (r *mutationResolver) PerformerCreate(ctx context.Context, input PerformerC
// Populate a new performer from the input
currentTime := time.Now()
newPerformer := models.Performer{
Name: input.Name,
Checksum: checksum,
CreatedAt: models.SQLiteTimestamp{Timestamp: currentTime},
UpdatedAt: models.SQLiteTimestamp{Timestamp: currentTime},
CreatedAt: currentTime,
UpdatedAt: currentTime,
}
newPerformer.Name = sql.NullString{String: input.Name, Valid: true}
if input.URL != nil {
newPerformer.URL = sql.NullString{String: *input.URL, Valid: true}
newPerformer.URL = *input.URL
}
if input.Gender != nil {
newPerformer.Gender = sql.NullString{String: input.Gender.String(), Valid: true}
newPerformer.Gender = *input.Gender
}
if input.Birthdate != nil {
newPerformer.Birthdate = models.SQLiteDate{String: *input.Birthdate, Valid: true}
d := models.NewDate(*input.Birthdate)
newPerformer.Birthdate = &d
}
if input.Ethnicity != nil {
newPerformer.Ethnicity = sql.NullString{String: *input.Ethnicity, Valid: true}
newPerformer.Ethnicity = *input.Ethnicity
}
if input.Country != nil {
newPerformer.Country = sql.NullString{String: *input.Country, Valid: true}
newPerformer.Country = *input.Country
}
if input.EyeColor != nil {
newPerformer.EyeColor = sql.NullString{String: *input.EyeColor, Valid: true}
newPerformer.EyeColor = *input.EyeColor
}
if input.Height != nil {
newPerformer.Height = sql.NullString{String: *input.Height, Valid: true}
newPerformer.Height = *input.Height
}
if input.Measurements != nil {
newPerformer.Measurements = sql.NullString{String: *input.Measurements, Valid: true}
newPerformer.Measurements = *input.Measurements
}
if input.FakeTits != nil {
newPerformer.FakeTits = sql.NullString{String: *input.FakeTits, Valid: true}
newPerformer.FakeTits = *input.FakeTits
}
if input.CareerLength != nil {
newPerformer.CareerLength = sql.NullString{String: *input.CareerLength, Valid: true}
newPerformer.CareerLength = *input.CareerLength
}
if input.Tattoos != nil {
newPerformer.Tattoos = sql.NullString{String: *input.Tattoos, Valid: true}
newPerformer.Tattoos = *input.Tattoos
}
if input.Piercings != nil {
newPerformer.Piercings = sql.NullString{String: *input.Piercings, Valid: true}
newPerformer.Piercings = *input.Piercings
}
if input.Aliases != nil {
newPerformer.Aliases = sql.NullString{String: *input.Aliases, Valid: true}
newPerformer.Aliases = *input.Aliases
}
if input.Twitter != nil {
newPerformer.Twitter = sql.NullString{String: *input.Twitter, Valid: true}
newPerformer.Twitter = *input.Twitter
}
if input.Instagram != nil {
newPerformer.Instagram = sql.NullString{String: *input.Instagram, Valid: true}
newPerformer.Instagram = *input.Instagram
}
if input.Favorite != nil {
newPerformer.Favorite = sql.NullBool{Bool: *input.Favorite, Valid: true}
} else {
newPerformer.Favorite = sql.NullBool{Bool: false, Valid: true}
newPerformer.Favorite = *input.Favorite
}
if input.Rating != nil {
newPerformer.Rating = sql.NullInt64{Int64: int64(*input.Rating), Valid: true}
} else {
newPerformer.Rating = sql.NullInt64{Valid: false}
newPerformer.Rating = input.Rating
}
if input.Details != nil {
newPerformer.Details = sql.NullString{String: *input.Details, Valid: true}
newPerformer.Details = *input.Details
}
if input.DeathDate != nil {
newPerformer.DeathDate = models.SQLiteDate{String: *input.DeathDate, Valid: true}
d := models.NewDate(*input.DeathDate)
newPerformer.DeathDate = &d
}
if input.HairColor != nil {
newPerformer.HairColor = sql.NullString{String: *input.HairColor, Valid: true}
newPerformer.HairColor = *input.HairColor
}
if input.Weight != nil {
weight := int64(*input.Weight)
newPerformer.Weight = sql.NullInt64{Int64: weight, Valid: true}
newPerformer.Weight = input.Weight
}
if input.IgnoreAutoTag != nil {
newPerformer.IgnoreAutoTag = *input.IgnoreAutoTag
@ -138,24 +134,23 @@ func (r *mutationResolver) PerformerCreate(ctx context.Context, input PerformerC
}
// Start the transaction and save the performer
var performer *models.Performer
if err := r.withTxn(ctx, func(ctx context.Context) error {
qb := r.repository.Performer
performer, err = qb.Create(ctx, newPerformer)
err = qb.Create(ctx, &newPerformer)
if err != nil {
return err
}
if len(input.TagIds) > 0 {
if err := r.updatePerformerTags(ctx, performer.ID, input.TagIds); err != nil {
if err := r.updatePerformerTags(ctx, newPerformer.ID, input.TagIds); err != nil {
return err
}
}
// update image table
if len(imageData) > 0 {
if err := qb.UpdateImage(ctx, performer.ID, imageData); err != nil {
if err := qb.UpdateImage(ctx, newPerformer.ID, imageData); err != nil {
return err
}
}
@ -163,7 +158,7 @@ func (r *mutationResolver) PerformerCreate(ctx context.Context, input PerformerC
// Save the stash_ids
if input.StashIds != nil {
stashIDJoins := stashIDPtrSliceToSlice(input.StashIds)
if err := qb.UpdateStashIDs(ctx, performer.ID, stashIDJoins); err != nil {
if err := qb.UpdateStashIDs(ctx, newPerformer.ID, stashIDJoins); err != nil {
return err
}
}
@ -173,17 +168,14 @@ func (r *mutationResolver) PerformerCreate(ctx context.Context, input PerformerC
return nil, err
}
r.hookExecutor.ExecutePostHooks(ctx, performer.ID, plugin.PerformerCreatePost, input, nil)
return r.getPerformer(ctx, performer.ID)
r.hookExecutor.ExecutePostHooks(ctx, newPerformer.ID, plugin.PerformerCreatePost, input, nil)
return r.getPerformer(ctx, newPerformer.ID)
}
func (r *mutationResolver) PerformerUpdate(ctx context.Context, input PerformerUpdateInput) (*models.Performer, error) {
// Populate performer from the input
performerID, _ := strconv.Atoi(input.ID)
updatedPerformer := models.PerformerPartial{
ID: performerID,
UpdatedAt: &models.SQLiteTimestamp{Timestamp: time.Now()},
}
updatedPerformer := models.NewPerformerPartial()
translator := changesetTranslator{
inputMap: getUpdateInputMap(ctx),
@ -203,54 +195,53 @@ func (r *mutationResolver) PerformerUpdate(ctx context.Context, input PerformerU
// generate checksum from performer name rather than image
checksum := md5.FromString(*input.Name)
updatedPerformer.Name = &sql.NullString{String: *input.Name, Valid: true}
updatedPerformer.Checksum = &checksum
updatedPerformer.Name = models.NewOptionalString(*input.Name)
updatedPerformer.Checksum = models.NewOptionalString(checksum)
}
updatedPerformer.URL = translator.nullString(input.URL, "url")
updatedPerformer.URL = translator.optionalString(input.URL, "url")
if translator.hasField("gender") {
if input.Gender != nil {
updatedPerformer.Gender = &sql.NullString{String: input.Gender.String(), Valid: true}
updatedPerformer.Gender = models.NewOptionalString(input.Gender.String())
} else {
updatedPerformer.Gender = &sql.NullString{String: "", Valid: false}
updatedPerformer.Gender = models.NewOptionalStringPtr(nil)
}
}
updatedPerformer.Birthdate = translator.sqliteDate(input.Birthdate, "birthdate")
updatedPerformer.Country = translator.nullString(input.Country, "country")
updatedPerformer.EyeColor = translator.nullString(input.EyeColor, "eye_color")
updatedPerformer.Measurements = translator.nullString(input.Measurements, "measurements")
updatedPerformer.Height = translator.nullString(input.Height, "height")
updatedPerformer.Ethnicity = translator.nullString(input.Ethnicity, "ethnicity")
updatedPerformer.FakeTits = translator.nullString(input.FakeTits, "fake_tits")
updatedPerformer.CareerLength = translator.nullString(input.CareerLength, "career_length")
updatedPerformer.Tattoos = translator.nullString(input.Tattoos, "tattoos")
updatedPerformer.Piercings = translator.nullString(input.Piercings, "piercings")
updatedPerformer.Aliases = translator.nullString(input.Aliases, "aliases")
updatedPerformer.Twitter = translator.nullString(input.Twitter, "twitter")
updatedPerformer.Instagram = translator.nullString(input.Instagram, "instagram")
updatedPerformer.Favorite = translator.nullBool(input.Favorite, "favorite")
updatedPerformer.Rating = translator.nullInt64(input.Rating, "rating")
updatedPerformer.Details = translator.nullString(input.Details, "details")
updatedPerformer.DeathDate = translator.sqliteDate(input.DeathDate, "death_date")
updatedPerformer.HairColor = translator.nullString(input.HairColor, "hair_color")
updatedPerformer.Weight = translator.nullInt64(input.Weight, "weight")
updatedPerformer.IgnoreAutoTag = input.IgnoreAutoTag
updatedPerformer.Birthdate = translator.optionalDate(input.Birthdate, "birthdate")
updatedPerformer.Country = translator.optionalString(input.Country, "country")
updatedPerformer.EyeColor = translator.optionalString(input.EyeColor, "eye_color")
updatedPerformer.Measurements = translator.optionalString(input.Measurements, "measurements")
updatedPerformer.Height = translator.optionalString(input.Height, "height")
updatedPerformer.Ethnicity = translator.optionalString(input.Ethnicity, "ethnicity")
updatedPerformer.FakeTits = translator.optionalString(input.FakeTits, "fake_tits")
updatedPerformer.CareerLength = translator.optionalString(input.CareerLength, "career_length")
updatedPerformer.Tattoos = translator.optionalString(input.Tattoos, "tattoos")
updatedPerformer.Piercings = translator.optionalString(input.Piercings, "piercings")
updatedPerformer.Aliases = translator.optionalString(input.Aliases, "aliases")
updatedPerformer.Twitter = translator.optionalString(input.Twitter, "twitter")
updatedPerformer.Instagram = translator.optionalString(input.Instagram, "instagram")
updatedPerformer.Favorite = translator.optionalBool(input.Favorite, "favorite")
updatedPerformer.Rating = translator.optionalInt(input.Rating, "rating")
updatedPerformer.Details = translator.optionalString(input.Details, "details")
updatedPerformer.DeathDate = translator.optionalDate(input.DeathDate, "death_date")
updatedPerformer.HairColor = translator.optionalString(input.HairColor, "hair_color")
updatedPerformer.Weight = translator.optionalInt(input.Weight, "weight")
updatedPerformer.IgnoreAutoTag = translator.optionalBool(input.IgnoreAutoTag, "ignore_auto_tag")
// Start the transaction and save the p
var p *models.Performer
if err := r.withTxn(ctx, func(ctx context.Context) error {
qb := r.repository.Performer
// need to get existing performer
existing, err := qb.Find(ctx, updatedPerformer.ID)
existing, err := qb.Find(ctx, performerID)
if err != nil {
return err
}
if existing == nil {
return fmt.Errorf("performer with id %d not found", updatedPerformer.ID)
return fmt.Errorf("performer with id %d not found", performerID)
}
if err := performer.ValidateDeathDate(existing, input.Birthdate, input.DeathDate); err != nil {
@ -259,26 +250,26 @@ func (r *mutationResolver) PerformerUpdate(ctx context.Context, input PerformerU
}
}
p, err = qb.Update(ctx, updatedPerformer)
_, err = qb.UpdatePartial(ctx, performerID, updatedPerformer)
if err != nil {
return err
}
// Save the tags
if translator.hasField("tag_ids") {
if err := r.updatePerformerTags(ctx, p.ID, input.TagIds); err != nil {
if err := r.updatePerformerTags(ctx, performerID, input.TagIds); err != nil {
return err
}
}
// update image table
if len(imageData) > 0 {
if err := qb.UpdateImage(ctx, p.ID, imageData); err != nil {
if err := qb.UpdateImage(ctx, performerID, imageData); err != nil {
return err
}
} else if imageIncluded {
// must be unsetting
if err := qb.DestroyImage(ctx, p.ID); err != nil {
if err := qb.DestroyImage(ctx, performerID); err != nil {
return err
}
}
@ -296,8 +287,8 @@ func (r *mutationResolver) PerformerUpdate(ctx context.Context, input PerformerU
return nil, err
}
r.hookExecutor.ExecutePostHooks(ctx, p.ID, plugin.PerformerUpdatePost, input, translator.getFields())
return r.getPerformer(ctx, p.ID)
r.hookExecutor.ExecutePostHooks(ctx, performerID, plugin.PerformerUpdatePost, input, translator.getFields())
return r.getPerformer(ctx, performerID)
}
func (r *mutationResolver) updatePerformerTags(ctx context.Context, performerID int, tagsIDs []string) error {
@ -315,43 +306,39 @@ func (r *mutationResolver) BulkPerformerUpdate(ctx context.Context, input BulkPe
}
// Populate performer from the input
updatedTime := time.Now()
translator := changesetTranslator{
inputMap: getUpdateInputMap(ctx),
}
updatedPerformer := models.PerformerPartial{
UpdatedAt: &models.SQLiteTimestamp{Timestamp: updatedTime},
}
updatedPerformer := models.NewPerformerPartial()
updatedPerformer.URL = translator.nullString(input.URL, "url")
updatedPerformer.Birthdate = translator.sqliteDate(input.Birthdate, "birthdate")
updatedPerformer.Ethnicity = translator.nullString(input.Ethnicity, "ethnicity")
updatedPerformer.Country = translator.nullString(input.Country, "country")
updatedPerformer.EyeColor = translator.nullString(input.EyeColor, "eye_color")
updatedPerformer.Height = translator.nullString(input.Height, "height")
updatedPerformer.Measurements = translator.nullString(input.Measurements, "measurements")
updatedPerformer.FakeTits = translator.nullString(input.FakeTits, "fake_tits")
updatedPerformer.CareerLength = translator.nullString(input.CareerLength, "career_length")
updatedPerformer.Tattoos = translator.nullString(input.Tattoos, "tattoos")
updatedPerformer.Piercings = translator.nullString(input.Piercings, "piercings")
updatedPerformer.Aliases = translator.nullString(input.Aliases, "aliases")
updatedPerformer.Twitter = translator.nullString(input.Twitter, "twitter")
updatedPerformer.Instagram = translator.nullString(input.Instagram, "instagram")
updatedPerformer.Favorite = translator.nullBool(input.Favorite, "favorite")
updatedPerformer.Rating = translator.nullInt64(input.Rating, "rating")
updatedPerformer.Details = translator.nullString(input.Details, "details")
updatedPerformer.DeathDate = translator.sqliteDate(input.DeathDate, "death_date")
updatedPerformer.HairColor = translator.nullString(input.HairColor, "hair_color")
updatedPerformer.Weight = translator.nullInt64(input.Weight, "weight")
updatedPerformer.IgnoreAutoTag = input.IgnoreAutoTag
updatedPerformer.URL = translator.optionalString(input.URL, "url")
updatedPerformer.Birthdate = translator.optionalDate(input.Birthdate, "birthdate")
updatedPerformer.Ethnicity = translator.optionalString(input.Ethnicity, "ethnicity")
updatedPerformer.Country = translator.optionalString(input.Country, "country")
updatedPerformer.EyeColor = translator.optionalString(input.EyeColor, "eye_color")
updatedPerformer.Height = translator.optionalString(input.Height, "height")
updatedPerformer.Measurements = translator.optionalString(input.Measurements, "measurements")
updatedPerformer.FakeTits = translator.optionalString(input.FakeTits, "fake_tits")
updatedPerformer.CareerLength = translator.optionalString(input.CareerLength, "career_length")
updatedPerformer.Tattoos = translator.optionalString(input.Tattoos, "tattoos")
updatedPerformer.Piercings = translator.optionalString(input.Piercings, "piercings")
updatedPerformer.Aliases = translator.optionalString(input.Aliases, "aliases")
updatedPerformer.Twitter = translator.optionalString(input.Twitter, "twitter")
updatedPerformer.Instagram = translator.optionalString(input.Instagram, "instagram")
updatedPerformer.Favorite = translator.optionalBool(input.Favorite, "favorite")
updatedPerformer.Rating = translator.optionalInt(input.Rating, "rating")
updatedPerformer.Details = translator.optionalString(input.Details, "details")
updatedPerformer.DeathDate = translator.optionalDate(input.DeathDate, "death_date")
updatedPerformer.HairColor = translator.optionalString(input.HairColor, "hair_color")
updatedPerformer.Weight = translator.optionalInt(input.Weight, "weight")
updatedPerformer.IgnoreAutoTag = translator.optionalBool(input.IgnoreAutoTag, "ignore_auto_tag")
if translator.hasField("gender") {
if input.Gender != nil {
updatedPerformer.Gender = &sql.NullString{String: input.Gender.String(), Valid: true}
updatedPerformer.Gender = models.NewOptionalString(input.Gender.String())
} else {
updatedPerformer.Gender = &sql.NullString{String: "", Valid: false}
updatedPerformer.Gender = models.NewOptionalStringPtr(nil)
}
}
@ -378,7 +365,7 @@ func (r *mutationResolver) BulkPerformerUpdate(ctx context.Context, input BulkPe
return err
}
performer, err := qb.Update(ctx, updatedPerformer)
performer, err := qb.UpdatePartial(ctx, performerID, updatedPerformer)
if err != nil {
return err
}

View file

@ -54,7 +54,7 @@ func (rs performerRoutes) Image(w http.ResponseWriter, r *http.Request) {
}
if len(image) == 0 || defaultParam == "true" {
image, _ = getRandomPerformerImageUsingName(performer.Name.String, performer.Gender.String, config.GetInstance().GetCustomPerformerImageLocation())
image, _ = getRandomPerformerImageUsingName(performer.Name, performer.Gender, config.GetInstance().GetCustomPerformerImageLocation())
}
if err := utils.ServeImage(image, w, r); err != nil {

View file

@ -1,8 +1,9 @@
package urlbuilders
import (
"github.com/stashapp/stash/pkg/models"
"strconv"
"github.com/stashapp/stash/pkg/models"
)
type PerformerURLBuilder struct {
@ -15,7 +16,7 @@ func NewPerformerURLBuilder(baseURL string, performer *models.Performer) Perform
return PerformerURLBuilder{
BaseURL: baseURL,
PerformerID: strconv.Itoa(performer.ID),
UpdatedAt: strconv.FormatInt(performer.UpdatedAt.Timestamp.Unix(), 10),
UpdatedAt: strconv.FormatInt(performer.UpdatedAt.Unix(), 10),
}
}

View file

@ -22,14 +22,14 @@ func TestGalleryPerformers(t *testing.T) {
const performerID = 2
performer := models.Performer{
ID: performerID,
Name: models.NullString(performerName),
Name: performerName,
}
const reversedPerformerName = "name performer"
const reversedPerformerID = 3
reversedPerformer := models.Performer{
ID: reversedPerformerID,
Name: models.NullString(reversedPerformerName),
Name: reversedPerformerName,
}
testTables := generateTestTable(performerName, galleryExt)

View file

@ -19,14 +19,14 @@ func TestImagePerformers(t *testing.T) {
const performerID = 2
performer := models.Performer{
ID: performerID,
Name: models.NullString(performerName),
Name: performerName,
}
const reversedPerformerName = "name performer"
const reversedPerformerID = 3
reversedPerformer := models.Performer{
ID: reversedPerformerID,
Name: models.NullString(reversedPerformerName),
Name: reversedPerformerName,
}
testTables := generateTestTable(performerName, imageExt)

View file

@ -87,11 +87,10 @@ func createPerformer(ctx context.Context, pqb models.PerformerWriter) error {
// create the performer
performer := models.Performer{
Checksum: testName,
Name: sql.NullString{Valid: true, String: testName},
Favorite: sql.NullBool{Valid: true, Bool: false},
Name: testName,
}
_, err := pqb.Create(ctx, performer)
err := pqb.Create(ctx, &performer)
if err != nil {
return err
}

View file

@ -33,7 +33,7 @@ func getPerformerTagger(p *models.Performer, cache *match.Cache) tagger {
return tagger{
ID: p.ID,
Type: "performer",
Name: p.Name.String,
Name: p.Name,
cache: cache,
}
}

View file

@ -60,7 +60,7 @@ func testPerformerScenes(t *testing.T, performerName, expectedRegex string) {
performer := models.Performer{
ID: performerID,
Name: models.NullString(performerName),
Name: performerName,
}
organized := false
@ -140,7 +140,7 @@ func testPerformerImages(t *testing.T, performerName, expectedRegex string) {
performer := models.Performer{
ID: performerID,
Name: models.NullString(performerName),
Name: performerName,
}
organized := false
@ -221,7 +221,7 @@ func testPerformerGalleries(t *testing.T, performerName, expectedRegex string) {
performer := models.Performer{
ID: performerID,
Name: models.NullString(performerName),
Name: performerName,
}
organized := false

View file

@ -152,14 +152,14 @@ func TestScenePerformers(t *testing.T) {
const performerID = 2
performer := models.Performer{
ID: performerID,
Name: models.NullString(performerName),
Name: performerName,
}
const reversedPerformerName = "name performer"
const reversedPerformerID = 3
reversedPerformer := models.Performer{
ID: reversedPerformerID,
Name: models.NullString(reversedPerformerName),
Name: reversedPerformerName,
}
testTables := generateTestTable(performerName, sceneExt)

View file

@ -58,11 +58,11 @@ func (t *tagger) tagPerformers(ctx context.Context, performerReader match.Perfor
added, err := addFunc(t.ID, p.ID)
if err != nil {
return t.addError("performer", p.Name.String, err)
return t.addError("performer", p.Name, err)
}
if added {
t.addLog("performer", p.Name.String)
t.addLog("performer", p.Name)
}
}

View file

@ -612,7 +612,7 @@ func (me *contentDirectoryService) getPerformers() []interface{} {
}
for _, s := range performers {
objs = append(objs, makeStorageFolder("performers/"+strconv.Itoa(s.ID), s.Name.String, "performers"))
objs = append(objs, makeStorageFolder("performers/"+strconv.Itoa(s.ID), s.Name, "performers"))
}
return nil

View file

@ -2,7 +2,6 @@ package identify
import (
"context"
"database/sql"
"fmt"
"strconv"
"time"
@ -12,7 +11,7 @@ import (
)
type PerformerCreator interface {
Create(ctx context.Context, newPerformer models.Performer) (*models.Performer, error)
Create(ctx context.Context, newPerformer *models.Performer) error
UpdateStashIDs(ctx context.Context, performerID int, stashIDs []models.StashID) error
}
@ -33,13 +32,14 @@ func getPerformerID(ctx context.Context, endpoint string, w PerformerCreator, p
}
func createMissingPerformer(ctx context.Context, endpoint string, w PerformerCreator, p *models.ScrapedPerformer) (*int, error) {
created, err := w.Create(ctx, scrapedToPerformerInput(p))
performerInput := scrapedToPerformerInput(p)
err := w.Create(ctx, &performerInput)
if err != nil {
return nil, fmt.Errorf("error creating performer: %w", err)
}
if endpoint != "" && p.RemoteSiteID != nil {
if err := w.UpdateStashIDs(ctx, created.ID, []models.StashID{
if err := w.UpdateStashIDs(ctx, performerInput.ID, []models.StashID{
{
Endpoint: endpoint,
StashID: *p.RemoteSiteID,
@ -49,65 +49,66 @@ func createMissingPerformer(ctx context.Context, endpoint string, w PerformerCre
}
}
return &created.ID, nil
return &performerInput.ID, nil
}
func scrapedToPerformerInput(performer *models.ScrapedPerformer) models.Performer {
currentTime := time.Now()
ret := models.Performer{
Name: sql.NullString{String: *performer.Name, Valid: true},
Name: *performer.Name,
Checksum: md5.FromString(*performer.Name),
CreatedAt: models.SQLiteTimestamp{Timestamp: currentTime},
UpdatedAt: models.SQLiteTimestamp{Timestamp: currentTime},
Favorite: sql.NullBool{Bool: false, Valid: true},
CreatedAt: currentTime,
UpdatedAt: currentTime,
}
if performer.Birthdate != nil {
ret.Birthdate = models.SQLiteDate{String: *performer.Birthdate, Valid: true}
d := models.NewDate(*performer.Birthdate)
ret.Birthdate = &d
}
if performer.DeathDate != nil {
ret.DeathDate = models.SQLiteDate{String: *performer.DeathDate, Valid: true}
d := models.NewDate(*performer.DeathDate)
ret.DeathDate = &d
}
if performer.Gender != nil {
ret.Gender = sql.NullString{String: *performer.Gender, Valid: true}
ret.Gender = models.GenderEnum(*performer.Gender)
}
if performer.Ethnicity != nil {
ret.Ethnicity = sql.NullString{String: *performer.Ethnicity, Valid: true}
ret.Ethnicity = *performer.Ethnicity
}
if performer.Country != nil {
ret.Country = sql.NullString{String: *performer.Country, Valid: true}
ret.Country = *performer.Country
}
if performer.EyeColor != nil {
ret.EyeColor = sql.NullString{String: *performer.EyeColor, Valid: true}
ret.EyeColor = *performer.EyeColor
}
if performer.HairColor != nil {
ret.HairColor = sql.NullString{String: *performer.HairColor, Valid: true}
ret.HairColor = *performer.HairColor
}
if performer.Height != nil {
ret.Height = sql.NullString{String: *performer.Height, Valid: true}
ret.Height = *performer.Height
}
if performer.Measurements != nil {
ret.Measurements = sql.NullString{String: *performer.Measurements, Valid: true}
ret.Measurements = *performer.Measurements
}
if performer.FakeTits != nil {
ret.FakeTits = sql.NullString{String: *performer.FakeTits, Valid: true}
ret.FakeTits = *performer.FakeTits
}
if performer.CareerLength != nil {
ret.CareerLength = sql.NullString{String: *performer.CareerLength, Valid: true}
ret.CareerLength = *performer.CareerLength
}
if performer.Tattoos != nil {
ret.Tattoos = sql.NullString{String: *performer.Tattoos, Valid: true}
ret.Tattoos = *performer.Tattoos
}
if performer.Piercings != nil {
ret.Piercings = sql.NullString{String: *performer.Piercings, Valid: true}
ret.Piercings = *performer.Piercings
}
if performer.Aliases != nil {
ret.Aliases = sql.NullString{String: *performer.Aliases, Valid: true}
ret.Aliases = *performer.Aliases
}
if performer.Twitter != nil {
ret.Twitter = sql.NullString{String: *performer.Twitter, Valid: true}
ret.Twitter = *performer.Twitter
}
if performer.Instagram != nil {
ret.Instagram = sql.NullString{String: *performer.Instagram, Valid: true}
ret.Instagram = *performer.Instagram
}
return ret

View file

@ -1,11 +1,11 @@
package identify
import (
"database/sql"
"errors"
"reflect"
"strconv"
"testing"
"time"
"github.com/stashapp/stash/pkg/models"
"github.com/stashapp/stash/pkg/models/mocks"
@ -24,9 +24,10 @@ func Test_getPerformerID(t *testing.T) {
name := "name"
mockPerformerReaderWriter := mocks.PerformerReaderWriter{}
mockPerformerReaderWriter.On("Create", testCtx, mock.Anything).Return(&models.Performer{
ID: validStoredID,
}, nil)
mockPerformerReaderWriter.On("Create", testCtx, mock.Anything).Run(func(args mock.Arguments) {
p := args.Get(1).(*models.Performer)
p.ID = validStoredID
}).Return(nil)
type args struct {
endpoint string
@ -132,14 +133,16 @@ func Test_createMissingPerformer(t *testing.T) {
performerID := 1
mockPerformerReaderWriter := mocks.PerformerReaderWriter{}
mockPerformerReaderWriter.On("Create", testCtx, mock.MatchedBy(func(p models.Performer) bool {
return p.Name.String == validName
})).Return(&models.Performer{
ID: performerID,
}, nil)
mockPerformerReaderWriter.On("Create", testCtx, mock.MatchedBy(func(p models.Performer) bool {
return p.Name.String == invalidName
})).Return(nil, errors.New("error creating performer"))
mockPerformerReaderWriter.On("Create", testCtx, mock.MatchedBy(func(p *models.Performer) bool {
return p.Name == validName
})).Run(func(args mock.Arguments) {
p := args.Get(1).(*models.Performer)
p.ID = performerID
}).Return(nil)
mockPerformerReaderWriter.On("Create", testCtx, mock.MatchedBy(func(p *models.Performer) bool {
return p.Name == invalidName
})).Return(errors.New("error creating performer"))
mockPerformerReaderWriter.On("UpdateStashIDs", testCtx, performerID, []models.StashID{
{
@ -241,6 +244,10 @@ func Test_scrapedToPerformerInput(t *testing.T) {
return &ret
}
dateToDatePtr := func(d models.Date) *models.Date {
return &d
}
tests := []struct {
name string
performer *models.ScrapedPerformer
@ -268,34 +275,24 @@ func Test_scrapedToPerformerInput(t *testing.T) {
Instagram: nextVal(),
},
models.Performer{
Name: models.NullString(name),
Name: name,
Checksum: md5,
Favorite: sql.NullBool{
Bool: false,
Valid: true,
},
Birthdate: models.SQLiteDate{
String: *nextVal(),
Valid: true,
},
DeathDate: models.SQLiteDate{
String: *nextVal(),
Valid: true,
},
Gender: models.NullString(*nextVal()),
Ethnicity: models.NullString(*nextVal()),
Country: models.NullString(*nextVal()),
EyeColor: models.NullString(*nextVal()),
HairColor: models.NullString(*nextVal()),
Height: models.NullString(*nextVal()),
Measurements: models.NullString(*nextVal()),
FakeTits: models.NullString(*nextVal()),
CareerLength: models.NullString(*nextVal()),
Tattoos: models.NullString(*nextVal()),
Piercings: models.NullString(*nextVal()),
Aliases: models.NullString(*nextVal()),
Twitter: models.NullString(*nextVal()),
Instagram: models.NullString(*nextVal()),
Birthdate: dateToDatePtr(models.NewDate(*nextVal())),
DeathDate: dateToDatePtr(models.NewDate(*nextVal())),
Gender: models.GenderEnum(*nextVal()),
Ethnicity: *nextVal(),
Country: *nextVal(),
EyeColor: *nextVal(),
HairColor: *nextVal(),
Height: *nextVal(),
Measurements: *nextVal(),
FakeTits: *nextVal(),
CareerLength: *nextVal(),
Tattoos: *nextVal(),
Piercings: *nextVal(),
Aliases: *nextVal(),
Twitter: *nextVal(),
Instagram: *nextVal(),
},
},
{
@ -304,12 +301,8 @@ func Test_scrapedToPerformerInput(t *testing.T) {
Name: &name,
},
models.Performer{
Name: models.NullString(name),
Name: name,
Checksum: md5,
Favorite: sql.NullBool{
Bool: false,
Valid: true,
},
},
},
}
@ -318,7 +311,7 @@ func Test_scrapedToPerformerInput(t *testing.T) {
got := scrapedToPerformerInput(tt.performer)
// clear created/updated dates
got.CreatedAt = models.SQLiteTimestamp{}
got.CreatedAt = time.Time{}
got.UpdatedAt = got.CreatedAt
if !reflect.DeepEqual(got, tt.want) {

View file

@ -176,7 +176,7 @@ func (j *autoTagJob) autoTagPerformers(ctx context.Context, progress *job.Progre
return nil
}); err != nil {
return fmt.Errorf("error auto-tagging performer '%s': %s", performer.Name.String, err.Error())
return fmt.Errorf("error auto-tagging performer '%s': %s", performer.Name, err.Error())
}
progress.Increment()

View file

@ -2,7 +2,6 @@ package manager
import (
"context"
"database/sql"
"fmt"
"time"
@ -31,7 +30,7 @@ func (t *StashBoxPerformerTagTask) Description() string {
if t.name != nil {
name = *t.name
} else if t.performer != nil {
name = t.performer.Name.String
name = t.performer.Name
}
return fmt.Sprintf("Tagging performer %s from stash-box", name)
@ -70,7 +69,7 @@ func (t *StashBoxPerformerTagTask) stashBoxPerformerTag(ctx context.Context) {
if t.name != nil {
name = *t.name
} else {
name = t.performer.Name.String
name = t.performer.Name
}
performer, err = client.FindStashBoxPerformerByName(ctx, name)
}
@ -86,84 +85,64 @@ func (t *StashBoxPerformerTagTask) stashBoxPerformerTag(ctx context.Context) {
}
if performer != nil {
updatedTime := time.Now()
if t.performer != nil {
partial := models.PerformerPartial{
ID: t.performer.ID,
UpdatedAt: &models.SQLiteTimestamp{Timestamp: updatedTime},
}
partial := models.NewPerformerPartial()
if performer.Aliases != nil && !excluded["aliases"] {
value := getNullString(performer.Aliases)
partial.Aliases = &value
partial.Aliases = models.NewOptionalString(*performer.Aliases)
}
if performer.Birthdate != nil && *performer.Birthdate != "" && !excluded["birthdate"] {
value := getDate(performer.Birthdate)
partial.Birthdate = &value
partial.Birthdate = models.NewOptionalDate(*value)
}
if performer.CareerLength != nil && !excluded["career_length"] {
value := getNullString(performer.CareerLength)
partial.CareerLength = &value
partial.CareerLength = models.NewOptionalString(*performer.CareerLength)
}
if performer.Country != nil && !excluded["country"] {
value := getNullString(performer.Country)
partial.Country = &value
partial.Country = models.NewOptionalString(*performer.Country)
}
if performer.Ethnicity != nil && !excluded["ethnicity"] {
value := getNullString(performer.Ethnicity)
partial.Ethnicity = &value
partial.Ethnicity = models.NewOptionalString(*performer.Ethnicity)
}
if performer.EyeColor != nil && !excluded["eye_color"] {
value := getNullString(performer.EyeColor)
partial.EyeColor = &value
partial.EyeColor = models.NewOptionalString(*performer.EyeColor)
}
if performer.FakeTits != nil && !excluded["fake_tits"] {
value := getNullString(performer.FakeTits)
partial.FakeTits = &value
partial.FakeTits = models.NewOptionalString(*performer.FakeTits)
}
if performer.Gender != nil && !excluded["gender"] {
value := getNullString(performer.Gender)
partial.Gender = &value
partial.Gender = models.NewOptionalString(*performer.Gender)
}
if performer.Height != nil && !excluded["height"] {
value := getNullString(performer.Height)
partial.Height = &value
partial.Height = models.NewOptionalString(*performer.Height)
}
if performer.Instagram != nil && !excluded["instagram"] {
value := getNullString(performer.Instagram)
partial.Instagram = &value
partial.Instagram = models.NewOptionalString(*performer.Instagram)
}
if performer.Measurements != nil && !excluded["measurements"] {
value := getNullString(performer.Measurements)
partial.Measurements = &value
partial.Measurements = models.NewOptionalString(*performer.Measurements)
}
if excluded["name"] && performer.Name != nil {
value := sql.NullString{String: *performer.Name, Valid: true}
partial.Name = &value
partial.Name = models.NewOptionalString(*performer.Name)
checksum := md5.FromString(*performer.Name)
partial.Checksum = &checksum
partial.Checksum = models.NewOptionalString(checksum)
}
if performer.Piercings != nil && !excluded["piercings"] {
value := getNullString(performer.Piercings)
partial.Piercings = &value
partial.Piercings = models.NewOptionalString(*performer.Piercings)
}
if performer.Tattoos != nil && !excluded["tattoos"] {
value := getNullString(performer.Tattoos)
partial.Tattoos = &value
partial.Tattoos = models.NewOptionalString(*performer.Tattoos)
}
if performer.Twitter != nil && !excluded["twitter"] {
value := getNullString(performer.Twitter)
partial.Twitter = &value
partial.Twitter = models.NewOptionalString(*performer.Twitter)
}
if performer.URL != nil && !excluded["url"] {
value := getNullString(performer.URL)
partial.URL = &value
partial.URL = models.NewOptionalString(*performer.URL)
}
txnErr := txn.WithTxn(ctx, instance.Repository, func(ctx context.Context) error {
r := instance.Repository
_, err := r.Performer.Update(ctx, partial)
_, err := r.Performer.UpdatePartial(ctx, t.performer.ID, partial)
if !t.refresh {
err = r.Performer.UpdateStashIDs(ctx, t.performer.ID, []models.StashID{
@ -203,35 +182,34 @@ func (t *StashBoxPerformerTagTask) stashBoxPerformerTag(ctx context.Context) {
} else if t.name != nil && performer.Name != nil {
currentTime := time.Now()
newPerformer := models.Performer{
Aliases: getNullString(performer.Aliases),
Aliases: getString(performer.Aliases),
Birthdate: getDate(performer.Birthdate),
CareerLength: getNullString(performer.CareerLength),
CareerLength: getString(performer.CareerLength),
Checksum: md5.FromString(*performer.Name),
Country: getNullString(performer.Country),
CreatedAt: models.SQLiteTimestamp{Timestamp: currentTime},
Ethnicity: getNullString(performer.Ethnicity),
EyeColor: getNullString(performer.EyeColor),
FakeTits: getNullString(performer.FakeTits),
Favorite: sql.NullBool{Bool: false, Valid: true},
Gender: getNullString(performer.Gender),
Height: getNullString(performer.Height),
Instagram: getNullString(performer.Instagram),
Measurements: getNullString(performer.Measurements),
Name: sql.NullString{String: *performer.Name, Valid: true},
Piercings: getNullString(performer.Piercings),
Tattoos: getNullString(performer.Tattoos),
Twitter: getNullString(performer.Twitter),
URL: getNullString(performer.URL),
UpdatedAt: models.SQLiteTimestamp{Timestamp: currentTime},
Country: getString(performer.Country),
CreatedAt: currentTime,
Ethnicity: getString(performer.Ethnicity),
EyeColor: getString(performer.EyeColor),
FakeTits: getString(performer.FakeTits),
Gender: models.GenderEnum(getString(performer.Gender)),
Height: getString(performer.Height),
Instagram: getString(performer.Instagram),
Measurements: getString(performer.Measurements),
Name: *performer.Name,
Piercings: getString(performer.Piercings),
Tattoos: getString(performer.Tattoos),
Twitter: getString(performer.Twitter),
URL: getString(performer.URL),
UpdatedAt: currentTime,
}
err := txn.WithTxn(ctx, instance.Repository, func(ctx context.Context) error {
r := instance.Repository
createdPerformer, err := r.Performer.Create(ctx, newPerformer)
err := r.Performer.Create(ctx, &newPerformer)
if err != nil {
return err
}
err = r.Performer.UpdateStashIDs(ctx, createdPerformer.ID, []models.StashID{
err = r.Performer.UpdateStashIDs(ctx, newPerformer.ID, []models.StashID{
{
Endpoint: t.box.Endpoint,
StashID: *performer.RemoteSiteID,
@ -246,7 +224,7 @@ func (t *StashBoxPerformerTagTask) stashBoxPerformerTag(ctx context.Context) {
if imageErr != nil {
return imageErr
}
err = r.Performer.UpdateImage(ctx, createdPerformer.ID, image)
err = r.Performer.UpdateImage(ctx, newPerformer.ID, image)
}
return err
})
@ -261,24 +239,25 @@ func (t *StashBoxPerformerTagTask) stashBoxPerformerTag(ctx context.Context) {
if t.name != nil {
name = *t.name
} else if t.performer != nil {
name = t.performer.Name.String
name = t.performer.Name
}
logger.Infof("No match found for %s", name)
}
}
func getDate(val *string) models.SQLiteDate {
func getDate(val *string) *models.Date {
if val == nil {
return models.SQLiteDate{Valid: false}
} else {
return models.SQLiteDate{String: *val, Valid: true}
return nil
}
ret := models.NewDate(*val)
return &ret
}
func getNullString(val *string) sql.NullString {
func getString(val *string) string {
if val == nil {
return sql.NullString{Valid: false}
return ""
} else {
return sql.NullString{String: *val, Valid: true}
return *val
}
}

View file

@ -136,10 +136,10 @@ func (i *Importer) populatePerformers(ctx context.Context) error {
var pluckedNames []string
for _, performer := range performers {
if !performer.Name.Valid {
if performer.Name == "" {
continue
}
pluckedNames = append(pluckedNames, performer.Name.String)
pluckedNames = append(pluckedNames, performer.Name)
}
missingPerformers := stringslice.StrFilter(names, func(name string) bool {
@ -176,12 +176,12 @@ func (i *Importer) createPerformers(ctx context.Context, names []string) ([]*mod
for _, name := range names {
newPerformer := *models.NewPerformer(name)
created, err := i.PerformerWriter.Create(ctx, newPerformer)
err := i.PerformerWriter.Create(ctx, &newPerformer)
if err != nil {
return nil, err
}
ret = append(ret, created)
ret = append(ret, &newPerformer)
}
return ret, nil

View file

@ -169,7 +169,7 @@ func TestImporterPreImportWithPerformer(t *testing.T) {
performerReaderWriter.On("FindByNames", testCtx, []string{existingPerformerName}, false).Return([]*models.Performer{
{
ID: existingPerformerID,
Name: models.NullString(existingPerformerName),
Name: existingPerformerName,
},
}, nil).Once()
performerReaderWriter.On("FindByNames", testCtx, []string{existingPerformerErr}, false).Return(nil, errors.New("FindByNames error")).Once()
@ -199,9 +199,10 @@ func TestImporterPreImportWithMissingPerformer(t *testing.T) {
}
performerReaderWriter.On("FindByNames", testCtx, []string{missingPerformerName}, false).Return(nil, nil).Times(3)
performerReaderWriter.On("Create", testCtx, mock.AnythingOfType("models.Performer")).Return(&models.Performer{
ID: existingPerformerID,
}, nil)
performerReaderWriter.On("Create", testCtx, mock.AnythingOfType("*models.Performer")).Run(func(args mock.Arguments) {
performer := args.Get(1).(*models.Performer)
performer.ID = existingPerformerID
}).Return(nil)
err := i.PreImport(testCtx)
assert.NotNil(t, err)
@ -232,7 +233,7 @@ func TestImporterPreImportWithMissingPerformerCreateErr(t *testing.T) {
}
performerReaderWriter.On("FindByNames", testCtx, []string{missingPerformerName}, false).Return(nil, nil).Once()
performerReaderWriter.On("Create", testCtx, mock.AnythingOfType("models.Performer")).Return(nil, errors.New("Create error"))
performerReaderWriter.On("Create", testCtx, mock.AnythingOfType("*models.Performer")).Return(errors.New("Create error"))
err := i.PreImport(testCtx)
assert.NotNil(t, err)

View file

@ -216,10 +216,10 @@ func (i *Importer) populatePerformers(ctx context.Context) error {
var pluckedNames []string
for _, performer := range performers {
if !performer.Name.Valid {
if performer.Name == "" {
continue
}
pluckedNames = append(pluckedNames, performer.Name.String)
pluckedNames = append(pluckedNames, performer.Name)
}
missingPerformers := stringslice.StrFilter(names, func(name string) bool {
@ -256,12 +256,12 @@ func (i *Importer) createPerformers(ctx context.Context, names []string) ([]*mod
for _, name := range names {
newPerformer := *models.NewPerformer(name)
created, err := i.PerformerWriter.Create(ctx, newPerformer)
err := i.PerformerWriter.Create(ctx, &newPerformer)
if err != nil {
return nil, err
}
ret = append(ret, created)
ret = append(ret, &newPerformer)
}
return ret, nil

View file

@ -130,7 +130,7 @@ func TestImporterPreImportWithPerformer(t *testing.T) {
performerReaderWriter.On("FindByNames", testCtx, []string{existingPerformerName}, false).Return([]*models.Performer{
{
ID: existingPerformerID,
Name: models.NullString(existingPerformerName),
Name: existingPerformerName,
},
}, nil).Once()
performerReaderWriter.On("FindByNames", testCtx, []string{existingPerformerErr}, false).Return(nil, errors.New("FindByNames error")).Once()
@ -160,9 +160,10 @@ func TestImporterPreImportWithMissingPerformer(t *testing.T) {
}
performerReaderWriter.On("FindByNames", testCtx, []string{missingPerformerName}, false).Return(nil, nil).Times(3)
performerReaderWriter.On("Create", testCtx, mock.AnythingOfType("models.Performer")).Return(&models.Performer{
ID: existingPerformerID,
}, nil)
performerReaderWriter.On("Create", testCtx, mock.AnythingOfType("*models.Performer")).Run(func(args mock.Arguments) {
performer := args.Get(1).(*models.Performer)
performer.ID = existingPerformerID
}).Return(nil)
err := i.PreImport(testCtx)
assert.NotNil(t, err)
@ -193,7 +194,7 @@ func TestImporterPreImportWithMissingPerformerCreateErr(t *testing.T) {
}
performerReaderWriter.On("FindByNames", testCtx, []string{missingPerformerName}, false).Return(nil, nil).Once()
performerReaderWriter.On("Create", testCtx, mock.AnythingOfType("models.Performer")).Return(nil, errors.New("Create error"))
performerReaderWriter.On("Create", testCtx, mock.AnythingOfType("*models.Performer")).Return(errors.New("Create error"))
err := i.PreImport(testCtx)
assert.NotNil(t, err)

View file

@ -169,7 +169,7 @@ func PathToPerformers(ctx context.Context, path string, reader PerformerAutoTagQ
var ret []*models.Performer
for _, p := range performers {
// TODO - commenting out alias handling until both sides work correctly
if nameMatchesPath(p.Name.String, path) != -1 { // || nameMatchesPath(p.Aliases.String, path) {
if nameMatchesPath(p.Name, path) != -1 { // || nameMatchesPath(p.Aliases.String, path) {
ret = append(ret, p)
}
}

View file

@ -80,26 +80,17 @@ func (_m *PerformerReaderWriter) CountByTagID(ctx context.Context, tagID int) (i
}
// Create provides a mock function with given fields: ctx, newPerformer
func (_m *PerformerReaderWriter) Create(ctx context.Context, newPerformer models.Performer) (*models.Performer, error) {
func (_m *PerformerReaderWriter) Create(ctx context.Context, newPerformer *models.Performer) error {
ret := _m.Called(ctx, newPerformer)
var r0 *models.Performer
if rf, ok := ret.Get(0).(func(context.Context, models.Performer) *models.Performer); ok {
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, *models.Performer) error); ok {
r0 = rf(ctx, newPerformer)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*models.Performer)
}
r0 = ret.Error(0)
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, models.Performer) error); ok {
r1 = rf(ctx, newPerformer)
} else {
r1 = ret.Error(1)
}
return r0, r1
return r0
}
// Destroy provides a mock function with given fields: ctx, id
@ -314,29 +305,6 @@ func (_m *PerformerReaderWriter) FindMany(ctx context.Context, ids []int) ([]*mo
return r0, r1
}
// FindNamesBySceneID provides a mock function with given fields: ctx, sceneID
func (_m *PerformerReaderWriter) FindNamesBySceneID(ctx context.Context, sceneID int) ([]*models.Performer, error) {
ret := _m.Called(ctx, sceneID)
var r0 []*models.Performer
if rf, ok := ret.Get(0).(func(context.Context, int) []*models.Performer); ok {
r0 = rf(ctx, sceneID)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]*models.Performer)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, int) error); ok {
r1 = rf(ctx, sceneID)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// GetImage provides a mock function with given fields: ctx, performerID
func (_m *PerformerReaderWriter) GetImage(ctx context.Context, performerID int) ([]byte, error) {
ret := _m.Called(ctx, performerID)
@ -460,49 +428,17 @@ func (_m *PerformerReaderWriter) QueryForAutoTag(ctx context.Context, words []st
}
// Update provides a mock function with given fields: ctx, updatedPerformer
func (_m *PerformerReaderWriter) Update(ctx context.Context, updatedPerformer models.PerformerPartial) (*models.Performer, error) {
func (_m *PerformerReaderWriter) Update(ctx context.Context, updatedPerformer *models.Performer) error {
ret := _m.Called(ctx, updatedPerformer)
var r0 *models.Performer
if rf, ok := ret.Get(0).(func(context.Context, models.PerformerPartial) *models.Performer); ok {
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, *models.Performer) error); ok {
r0 = rf(ctx, updatedPerformer)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*models.Performer)
}
r0 = ret.Error(0)
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, models.PerformerPartial) error); ok {
r1 = rf(ctx, updatedPerformer)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// UpdateFull provides a mock function with given fields: ctx, updatedPerformer
func (_m *PerformerReaderWriter) UpdateFull(ctx context.Context, updatedPerformer models.Performer) (*models.Performer, error) {
ret := _m.Called(ctx, updatedPerformer)
var r0 *models.Performer
if rf, ok := ret.Get(0).(func(context.Context, models.Performer) *models.Performer); ok {
r0 = rf(ctx, updatedPerformer)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*models.Performer)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, models.Performer) error); ok {
r1 = rf(ctx, updatedPerformer)
} else {
r1 = ret.Error(1)
}
return r0, r1
return r0
}
// UpdateImage provides a mock function with given fields: ctx, performerID, image
@ -519,6 +455,29 @@ func (_m *PerformerReaderWriter) UpdateImage(ctx context.Context, performerID in
return r0
}
// UpdatePartial provides a mock function with given fields: ctx, id, updatedPerformer
func (_m *PerformerReaderWriter) UpdatePartial(ctx context.Context, id int, updatedPerformer models.PerformerPartial) (*models.Performer, error) {
ret := _m.Called(ctx, id, updatedPerformer)
var r0 *models.Performer
if rf, ok := ret.Get(0).(func(context.Context, int, models.PerformerPartial) *models.Performer); ok {
r0 = rf(ctx, id, updatedPerformer)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*models.Performer)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, int, models.PerformerPartial) error); ok {
r1 = rf(ctx, id, updatedPerformer)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// UpdateStashIDs provides a mock function with given fields: ctx, performerID, stashIDs
func (_m *PerformerReaderWriter) UpdateStashIDs(ctx context.Context, performerID int, stashIDs []models.StashID) error {
ret := _m.Called(ctx, performerID, stashIDs)

View file

@ -1,80 +1,87 @@
package models
import (
"database/sql"
"time"
"github.com/stashapp/stash/pkg/hash/md5"
)
type Performer struct {
ID int `db:"id" json:"id"`
Checksum string `db:"checksum" json:"checksum"`
Name sql.NullString `db:"name" json:"name"`
Gender sql.NullString `db:"gender" json:"gender"`
URL sql.NullString `db:"url" json:"url"`
Twitter sql.NullString `db:"twitter" json:"twitter"`
Instagram sql.NullString `db:"instagram" json:"instagram"`
Birthdate SQLiteDate `db:"birthdate" json:"birthdate"`
Ethnicity sql.NullString `db:"ethnicity" json:"ethnicity"`
Country sql.NullString `db:"country" json:"country"`
EyeColor sql.NullString `db:"eye_color" json:"eye_color"`
Height sql.NullString `db:"height" json:"height"`
Measurements sql.NullString `db:"measurements" json:"measurements"`
FakeTits sql.NullString `db:"fake_tits" json:"fake_tits"`
CareerLength sql.NullString `db:"career_length" json:"career_length"`
Tattoos sql.NullString `db:"tattoos" json:"tattoos"`
Piercings sql.NullString `db:"piercings" json:"piercings"`
Aliases sql.NullString `db:"aliases" json:"aliases"`
Favorite sql.NullBool `db:"favorite" json:"favorite"`
CreatedAt SQLiteTimestamp `db:"created_at" json:"created_at"`
UpdatedAt SQLiteTimestamp `db:"updated_at" json:"updated_at"`
Rating sql.NullInt64 `db:"rating" json:"rating"`
Details sql.NullString `db:"details" json:"details"`
DeathDate SQLiteDate `db:"death_date" json:"death_date"`
HairColor sql.NullString `db:"hair_color" json:"hair_color"`
Weight sql.NullInt64 `db:"weight" json:"weight"`
IgnoreAutoTag bool `db:"ignore_auto_tag" json:"ignore_auto_tag"`
ID int `json:"id"`
Checksum string `json:"checksum"`
Name string `json:"name"`
Gender GenderEnum `json:"gender"`
URL string `json:"url"`
Twitter string `json:"twitter"`
Instagram string `json:"instagram"`
Birthdate *Date `json:"birthdate"`
Ethnicity string `json:"ethnicity"`
Country string `json:"country"`
EyeColor string `json:"eye_color"`
Height string `json:"height"`
Measurements string `json:"measurements"`
FakeTits string `json:"fake_tits"`
CareerLength string `json:"career_length"`
Tattoos string `json:"tattoos"`
Piercings string `json:"piercings"`
Aliases string `json:"aliases"`
Favorite bool `json:"favorite"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
Rating *int `json:"rating"`
Details string `json:"details"`
DeathDate *Date `json:"death_date"`
HairColor string `json:"hair_color"`
Weight *int `json:"weight"`
IgnoreAutoTag bool `json:"ignore_auto_tag"`
}
// PerformerPartial represents part of a Performer object. It is used to update
// the database entry.
type PerformerPartial struct {
ID int `db:"id" json:"id"`
Checksum *string `db:"checksum" json:"checksum"`
Name *sql.NullString `db:"name" json:"name"`
Gender *sql.NullString `db:"gender" json:"gender"`
URL *sql.NullString `db:"url" json:"url"`
Twitter *sql.NullString `db:"twitter" json:"twitter"`
Instagram *sql.NullString `db:"instagram" json:"instagram"`
Birthdate *SQLiteDate `db:"birthdate" json:"birthdate"`
Ethnicity *sql.NullString `db:"ethnicity" json:"ethnicity"`
Country *sql.NullString `db:"country" json:"country"`
EyeColor *sql.NullString `db:"eye_color" json:"eye_color"`
Height *sql.NullString `db:"height" json:"height"`
Measurements *sql.NullString `db:"measurements" json:"measurements"`
FakeTits *sql.NullString `db:"fake_tits" json:"fake_tits"`
CareerLength *sql.NullString `db:"career_length" json:"career_length"`
Tattoos *sql.NullString `db:"tattoos" json:"tattoos"`
Piercings *sql.NullString `db:"piercings" json:"piercings"`
Aliases *sql.NullString `db:"aliases" json:"aliases"`
Favorite *sql.NullBool `db:"favorite" json:"favorite"`
CreatedAt *SQLiteTimestamp `db:"created_at" json:"created_at"`
UpdatedAt *SQLiteTimestamp `db:"updated_at" json:"updated_at"`
Rating *sql.NullInt64 `db:"rating" json:"rating"`
Details *sql.NullString `db:"details" json:"details"`
DeathDate *SQLiteDate `db:"death_date" json:"death_date"`
HairColor *sql.NullString `db:"hair_color" json:"hair_color"`
Weight *sql.NullInt64 `db:"weight" json:"weight"`
IgnoreAutoTag *bool `db:"ignore_auto_tag" json:"ignore_auto_tag"`
ID int
Checksum OptionalString
Name OptionalString
Gender OptionalString
URL OptionalString
Twitter OptionalString
Instagram OptionalString
Birthdate OptionalDate
Ethnicity OptionalString
Country OptionalString
EyeColor OptionalString
Height OptionalString
Measurements OptionalString
FakeTits OptionalString
CareerLength OptionalString
Tattoos OptionalString
Piercings OptionalString
Aliases OptionalString
Favorite OptionalBool
CreatedAt OptionalTime
UpdatedAt OptionalTime
Rating OptionalInt
Details OptionalString
DeathDate OptionalDate
HairColor OptionalString
Weight OptionalInt
IgnoreAutoTag OptionalBool
}
func NewPerformer(name string) *Performer {
currentTime := time.Now()
return &Performer{
Checksum: md5.FromString(name),
Name: sql.NullString{String: name, Valid: true},
Favorite: sql.NullBool{Bool: false, Valid: true},
CreatedAt: SQLiteTimestamp{Timestamp: currentTime},
UpdatedAt: SQLiteTimestamp{Timestamp: currentTime},
Name: name,
CreatedAt: currentTime,
UpdatedAt: currentTime,
}
}
func NewPerformerPartial() PerformerPartial {
updatedTime := time.Now()
return PerformerPartial{
UpdatedAt: NewOptionalTime(updatedTime),
}
}

View file

@ -133,7 +133,6 @@ type PerformerReader interface {
Find(ctx context.Context, id int) (*Performer, error)
PerformerFinder
FindBySceneID(ctx context.Context, sceneID int) ([]*Performer, error)
FindNamesBySceneID(ctx context.Context, sceneID int) ([]*Performer, error)
FindByImageID(ctx context.Context, imageID int) ([]*Performer, error)
FindByGalleryID(ctx context.Context, galleryID int) ([]*Performer, error)
FindByNames(ctx context.Context, names []string, nocase bool) ([]*Performer, error)
@ -152,9 +151,9 @@ type PerformerReader interface {
}
type PerformerWriter interface {
Create(ctx context.Context, newPerformer Performer) (*Performer, error)
Update(ctx context.Context, updatedPerformer PerformerPartial) (*Performer, error)
UpdateFull(ctx context.Context, updatedPerformer Performer) (*Performer, error)
Create(ctx context.Context, newPerformer *Performer) error
UpdatePartial(ctx context.Context, id int, updatedPerformer PerformerPartial) (*Performer, error)
Update(ctx context.Context, updatedPerformer *Performer) error
Destroy(ctx context.Context, id int) error
UpdateImage(ctx context.Context, performerID int, image []byte) error
DestroyImage(ctx context.Context, performerID int) error

View file

@ -18,76 +18,40 @@ type ImageStashIDGetter interface {
// ToJSON converts a Performer object into its JSON equivalent.
func ToJSON(ctx context.Context, reader ImageStashIDGetter, performer *models.Performer) (*jsonschema.Performer, error) {
newPerformerJSON := jsonschema.Performer{
Name: performer.Name,
Gender: performer.Gender.String(),
URL: performer.URL,
Ethnicity: performer.Ethnicity,
Country: performer.Country,
EyeColor: performer.EyeColor,
Height: performer.Height,
Measurements: performer.Measurements,
FakeTits: performer.FakeTits,
CareerLength: performer.CareerLength,
Tattoos: performer.Tattoos,
Piercings: performer.Piercings,
Aliases: performer.Aliases,
Twitter: performer.Twitter,
Instagram: performer.Instagram,
Favorite: performer.Favorite,
Details: performer.Details,
HairColor: performer.HairColor,
IgnoreAutoTag: performer.IgnoreAutoTag,
CreatedAt: json.JSONTime{Time: performer.CreatedAt.Timestamp},
UpdatedAt: json.JSONTime{Time: performer.UpdatedAt.Timestamp},
CreatedAt: json.JSONTime{Time: performer.CreatedAt},
UpdatedAt: json.JSONTime{Time: performer.UpdatedAt},
}
if performer.Name.Valid {
newPerformerJSON.Name = performer.Name.String
if performer.Birthdate != nil {
newPerformerJSON.Birthdate = performer.Birthdate.String()
}
if performer.Gender.Valid {
newPerformerJSON.Gender = performer.Gender.String
if performer.Rating != nil {
newPerformerJSON.Rating = *performer.Rating
}
if performer.URL.Valid {
newPerformerJSON.URL = performer.URL.String
if performer.DeathDate != nil {
newPerformerJSON.DeathDate = performer.DeathDate.String()
}
if performer.Birthdate.Valid {
newPerformerJSON.Birthdate = utils.GetYMDFromDatabaseDate(performer.Birthdate.String)
}
if performer.Ethnicity.Valid {
newPerformerJSON.Ethnicity = performer.Ethnicity.String
}
if performer.Country.Valid {
newPerformerJSON.Country = performer.Country.String
}
if performer.EyeColor.Valid {
newPerformerJSON.EyeColor = performer.EyeColor.String
}
if performer.Height.Valid {
newPerformerJSON.Height = performer.Height.String
}
if performer.Measurements.Valid {
newPerformerJSON.Measurements = performer.Measurements.String
}
if performer.FakeTits.Valid {
newPerformerJSON.FakeTits = performer.FakeTits.String
}
if performer.CareerLength.Valid {
newPerformerJSON.CareerLength = performer.CareerLength.String
}
if performer.Tattoos.Valid {
newPerformerJSON.Tattoos = performer.Tattoos.String
}
if performer.Piercings.Valid {
newPerformerJSON.Piercings = performer.Piercings.String
}
if performer.Aliases.Valid {
newPerformerJSON.Aliases = performer.Aliases.String
}
if performer.Twitter.Valid {
newPerformerJSON.Twitter = performer.Twitter.String
}
if performer.Instagram.Valid {
newPerformerJSON.Instagram = performer.Instagram.String
}
if performer.Favorite.Valid {
newPerformerJSON.Favorite = performer.Favorite.Bool
}
if performer.Rating.Valid {
newPerformerJSON.Rating = int(performer.Rating.Int64)
}
if performer.Details.Valid {
newPerformerJSON.Details = performer.Details.String
}
if performer.DeathDate.Valid {
newPerformerJSON.DeathDate = utils.GetYMDFromDatabaseDate(performer.DeathDate.String)
}
if performer.HairColor.Valid {
newPerformerJSON.HairColor = performer.HairColor.String
}
if performer.Weight.Valid {
newPerformerJSON.Weight = int(performer.Weight.Int64)
if performer.Weight != nil {
newPerformerJSON.Weight = *performer.Weight
}
image, err := reader.GetImage(ctx, performer.ID)
@ -126,8 +90,8 @@ func GetIDs(performers []*models.Performer) []int {
func GetNames(performers []*models.Performer) []string {
var results []string
for _, performer := range performers {
if performer.Name.Valid {
results = append(results, performer.Name.String)
if performer.Name != "" {
results = append(results, performer.Name)
}
}

View file

@ -1,7 +1,6 @@
package performer
import (
"database/sql"
"errors"
"github.com/stashapp/stash/pkg/hash/md5"
@ -37,13 +36,17 @@ const (
piercings = "piercings"
tattoos = "tattoos"
twitter = "twitter"
rating = 5
details = "details"
hairColor = "hairColor"
weight = 60
autoTagIgnored = true
)
var (
rating = 5
weight = 60
)
var imageBytes = []byte("imageBytes")
var stashID = models.StashID{
@ -56,14 +59,8 @@ var stashIDs = []models.StashID{
const image = "aW1hZ2VCeXRlcw=="
var birthDate = models.SQLiteDate{
String: "2001-01-01",
Valid: true,
}
var deathDate = models.SQLiteDate{
String: "2021-02-02",
Valid: true,
}
var birthDate = models.NewDate("2001-01-01")
var deathDate = models.NewDate("2021-02-02")
var (
createTime = time.Date(2001, 01, 01, 0, 0, 0, 0, time.Local)
@ -73,41 +70,31 @@ var (
func createFullPerformer(id int, name string) *models.Performer {
return &models.Performer{
ID: id,
Name: models.NullString(name),
Name: name,
Checksum: md5.FromString(name),
URL: models.NullString(url),
Aliases: models.NullString(aliases),
Birthdate: birthDate,
CareerLength: models.NullString(careerLength),
Country: models.NullString(country),
Ethnicity: models.NullString(ethnicity),
EyeColor: models.NullString(eyeColor),
FakeTits: models.NullString(fakeTits),
Favorite: sql.NullBool{
Bool: true,
Valid: true,
},
Gender: models.NullString(gender),
Height: models.NullString(height),
Instagram: models.NullString(instagram),
Measurements: models.NullString(measurements),
Piercings: models.NullString(piercings),
Tattoos: models.NullString(tattoos),
Twitter: models.NullString(twitter),
CreatedAt: models.SQLiteTimestamp{
Timestamp: createTime,
},
UpdatedAt: models.SQLiteTimestamp{
Timestamp: updateTime,
},
Rating: models.NullInt64(rating),
Details: models.NullString(details),
DeathDate: deathDate,
HairColor: models.NullString(hairColor),
Weight: sql.NullInt64{
Int64: weight,
Valid: true,
},
URL: url,
Aliases: aliases,
Birthdate: &birthDate,
CareerLength: careerLength,
Country: country,
Ethnicity: ethnicity,
EyeColor: eyeColor,
FakeTits: fakeTits,
Favorite: true,
Gender: gender,
Height: height,
Instagram: instagram,
Measurements: measurements,
Piercings: piercings,
Tattoos: tattoos,
Twitter: twitter,
CreatedAt: createTime,
UpdatedAt: updateTime,
Rating: &rating,
Details: details,
DeathDate: &deathDate,
HairColor: hairColor,
Weight: &weight,
IgnoreAutoTag: autoTagIgnored,
}
}
@ -115,12 +102,8 @@ func createFullPerformer(id int, name string) *models.Performer {
func createEmptyPerformer(id int) models.Performer {
return models.Performer{
ID: id,
CreatedAt: models.SQLiteTimestamp{
Timestamp: createTime,
},
UpdatedAt: models.SQLiteTimestamp{
Timestamp: updateTime,
},
CreatedAt: createTime,
UpdatedAt: updateTime,
}
}
@ -129,7 +112,7 @@ func createFullJSONPerformer(name string, image string) *jsonschema.Performer {
Name: name,
URL: url,
Aliases: aliases,
Birthdate: birthDate.String,
Birthdate: birthDate.String(),
CareerLength: careerLength,
Country: country,
Ethnicity: ethnicity,
@ -152,7 +135,7 @@ func createFullJSONPerformer(name string, image string) *jsonschema.Performer {
Rating: rating,
Image: image,
Details: details,
DeathDate: deathDate.String,
DeathDate: deathDate.String(),
HairColor: hairColor,
Weight: weight,
StashIDs: []models.StashID{

View file

@ -2,7 +2,6 @@ package performer
import (
"context"
"database/sql"
"fmt"
"strings"
@ -16,7 +15,7 @@ import (
type NameFinderCreatorUpdater interface {
NameFinderCreator
UpdateFull(ctx context.Context, updatedPerformer models.Performer) (*models.Performer, error)
Update(ctx context.Context, updatedPerformer *models.Performer) error
UpdateTags(ctx context.Context, performerID int, tagIDs []int) error
UpdateImage(ctx context.Context, performerID int, image []byte) error
UpdateStashIDs(ctx context.Context, performerID int, stashIDs []models.StashID) error
@ -164,19 +163,19 @@ func (i *Importer) FindExistingID(ctx context.Context) (*int, error) {
}
func (i *Importer) Create(ctx context.Context) (*int, error) {
created, err := i.ReaderWriter.Create(ctx, i.performer)
err := i.ReaderWriter.Create(ctx, &i.performer)
if err != nil {
return nil, fmt.Errorf("error creating performer: %v", err)
}
id := created.ID
id := i.performer.ID
return &id, nil
}
func (i *Importer) Update(ctx context.Context, id int) error {
performer := i.performer
performer.ID = id
_, err := i.ReaderWriter.UpdateFull(ctx, performer)
err := i.ReaderWriter.Update(ctx, &performer)
if err != nil {
return fmt.Errorf("error updating existing performer: %v", err)
}
@ -188,75 +187,52 @@ func performerJSONToPerformer(performerJSON jsonschema.Performer) models.Perform
checksum := md5.FromString(performerJSON.Name)
newPerformer := models.Performer{
Name: performerJSON.Name,
Checksum: checksum,
Favorite: sql.NullBool{Bool: performerJSON.Favorite, Valid: true},
Gender: models.GenderEnum(performerJSON.Gender),
URL: performerJSON.URL,
Ethnicity: performerJSON.Ethnicity,
Country: performerJSON.Country,
EyeColor: performerJSON.EyeColor,
Height: performerJSON.Height,
Measurements: performerJSON.Measurements,
FakeTits: performerJSON.FakeTits,
CareerLength: performerJSON.CareerLength,
Tattoos: performerJSON.Tattoos,
Piercings: performerJSON.Piercings,
Aliases: performerJSON.Aliases,
Twitter: performerJSON.Twitter,
Instagram: performerJSON.Instagram,
Details: performerJSON.Details,
HairColor: performerJSON.HairColor,
Favorite: performerJSON.Favorite,
IgnoreAutoTag: performerJSON.IgnoreAutoTag,
CreatedAt: models.SQLiteTimestamp{Timestamp: performerJSON.CreatedAt.GetTime()},
UpdatedAt: models.SQLiteTimestamp{Timestamp: performerJSON.UpdatedAt.GetTime()},
CreatedAt: performerJSON.CreatedAt.GetTime(),
UpdatedAt: performerJSON.UpdatedAt.GetTime(),
}
if performerJSON.Name != "" {
newPerformer.Name = sql.NullString{String: performerJSON.Name, Valid: true}
}
if performerJSON.Gender != "" {
newPerformer.Gender = sql.NullString{String: performerJSON.Gender, Valid: true}
}
if performerJSON.URL != "" {
newPerformer.URL = sql.NullString{String: performerJSON.URL, Valid: true}
}
if performerJSON.Birthdate != "" {
newPerformer.Birthdate = models.SQLiteDate{String: performerJSON.Birthdate, Valid: true}
d, err := utils.ParseDateStringAsTime(performerJSON.Birthdate)
if err == nil {
newPerformer.Birthdate = &models.Date{
Time: d,
}
if performerJSON.Ethnicity != "" {
newPerformer.Ethnicity = sql.NullString{String: performerJSON.Ethnicity, Valid: true}
}
if performerJSON.Country != "" {
newPerformer.Country = sql.NullString{String: performerJSON.Country, Valid: true}
}
if performerJSON.EyeColor != "" {
newPerformer.EyeColor = sql.NullString{String: performerJSON.EyeColor, Valid: true}
}
if performerJSON.Height != "" {
newPerformer.Height = sql.NullString{String: performerJSON.Height, Valid: true}
}
if performerJSON.Measurements != "" {
newPerformer.Measurements = sql.NullString{String: performerJSON.Measurements, Valid: true}
}
if performerJSON.FakeTits != "" {
newPerformer.FakeTits = sql.NullString{String: performerJSON.FakeTits, Valid: true}
}
if performerJSON.CareerLength != "" {
newPerformer.CareerLength = sql.NullString{String: performerJSON.CareerLength, Valid: true}
}
if performerJSON.Tattoos != "" {
newPerformer.Tattoos = sql.NullString{String: performerJSON.Tattoos, Valid: true}
}
if performerJSON.Piercings != "" {
newPerformer.Piercings = sql.NullString{String: performerJSON.Piercings, Valid: true}
}
if performerJSON.Aliases != "" {
newPerformer.Aliases = sql.NullString{String: performerJSON.Aliases, Valid: true}
}
if performerJSON.Twitter != "" {
newPerformer.Twitter = sql.NullString{String: performerJSON.Twitter, Valid: true}
}
if performerJSON.Instagram != "" {
newPerformer.Instagram = sql.NullString{String: performerJSON.Instagram, Valid: true}
}
if performerJSON.Rating != 0 {
newPerformer.Rating = sql.NullInt64{Int64: int64(performerJSON.Rating), Valid: true}
}
if performerJSON.Details != "" {
newPerformer.Details = sql.NullString{String: performerJSON.Details, Valid: true}
newPerformer.Rating = &performerJSON.Rating
}
if performerJSON.DeathDate != "" {
newPerformer.DeathDate = models.SQLiteDate{String: performerJSON.DeathDate, Valid: true}
d, err := utils.ParseDateStringAsTime(performerJSON.DeathDate)
if err == nil {
newPerformer.DeathDate = &models.Date{
Time: d,
}
if performerJSON.HairColor != "" {
newPerformer.HairColor = sql.NullString{String: performerJSON.HairColor, Valid: true}
}
}
if performerJSON.Weight != 0 {
newPerformer.Weight = sql.NullInt64{Int64: int64(performerJSON.Weight), Valid: true}
newPerformer.Weight = &performerJSON.Weight
}
return newPerformer

View file

@ -237,11 +237,11 @@ func TestCreate(t *testing.T) {
readerWriter := &mocks.PerformerReaderWriter{}
performer := models.Performer{
Name: models.NullString(performerName),
Name: performerName,
}
performerErr := models.Performer{
Name: models.NullString(performerNameErr),
Name: performerNameErr,
}
i := Importer{
@ -250,10 +250,11 @@ func TestCreate(t *testing.T) {
}
errCreate := errors.New("Create error")
readerWriter.On("Create", testCtx, performer).Return(&models.Performer{
ID: performerID,
}, nil).Once()
readerWriter.On("Create", testCtx, performerErr).Return(nil, errCreate).Once()
readerWriter.On("Create", testCtx, &performer).Run(func(args mock.Arguments) {
arg := args.Get(1).(*models.Performer)
arg.ID = performerID
}).Return(nil).Once()
readerWriter.On("Create", testCtx, &performerErr).Return(errCreate).Once()
id, err := i.Create(testCtx)
assert.Equal(t, performerID, *id)
@ -271,11 +272,11 @@ func TestUpdate(t *testing.T) {
readerWriter := &mocks.PerformerReaderWriter{}
performer := models.Performer{
Name: models.NullString(performerName),
Name: performerName,
}
performerErr := models.Performer{
Name: models.NullString(performerNameErr),
Name: performerNameErr,
}
i := Importer{
@ -287,7 +288,7 @@ func TestUpdate(t *testing.T) {
// id needs to be set for the mock input
performer.ID = performerID
readerWriter.On("UpdateFull", testCtx, performer).Return(nil, nil).Once()
readerWriter.On("Update", testCtx, &performer).Return(nil).Once()
err := i.Update(testCtx, performerID)
assert.Nil(t, err)
@ -296,7 +297,7 @@ func TestUpdate(t *testing.T) {
// need to set id separately
performerErr.ID = errImageID
readerWriter.On("UpdateFull", testCtx, performerErr).Return(nil, errUpdate).Once()
readerWriter.On("Update", testCtx, &performerErr).Return(errUpdate).Once()
err = i.Update(testCtx, errImageID)
assert.NotNil(t, err)

View file

@ -8,5 +8,5 @@ import (
type NameFinderCreator interface {
FindByNames(ctx context.Context, names []string, nocase bool) ([]*models.Performer, error)
Create(ctx context.Context, newPerformer models.Performer) (*models.Performer, error)
Create(ctx context.Context, newPerformer *models.Performer) error
}

View file

@ -14,11 +14,13 @@ func ValidateDeathDate(performer *models.Performer, birthdate *string, deathDate
}
if performer != nil {
if birthdate == nil && performer.Birthdate.Valid {
birthdate = &performer.Birthdate.String
if birthdate == nil && performer.Birthdate != nil {
s := performer.Birthdate.String()
birthdate = &s
}
if deathDate == nil && performer.DeathDate.Valid {
deathDate = &performer.DeathDate.String
if deathDate == nil && performer.DeathDate != nil {
s := performer.DeathDate.String()
deathDate = &s
}
}

View file

@ -16,26 +16,17 @@ func TestValidateDeathDate(t *testing.T) {
date4 := "2004-01-01"
empty := ""
md2 := models.NewDate(date2)
md3 := models.NewDate(date3)
emptyPerformer := models.Performer{}
invalidPerformer := models.Performer{
Birthdate: models.SQLiteDate{
String: date3,
Valid: true,
},
DeathDate: models.SQLiteDate{
String: date2,
Valid: true,
},
Birthdate: &md3,
DeathDate: &md2,
}
validPerformer := models.Performer{
Birthdate: models.SQLiteDate{
String: date2,
Valid: true,
},
DeathDate: models.SQLiteDate{
String: date3,
Valid: true,
},
Birthdate: &md2,
DeathDate: &md3,
}
// nil values should always return nil

View file

@ -231,10 +231,10 @@ func (i *Importer) populatePerformers(ctx context.Context) error {
var pluckedNames []string
for _, performer := range performers {
if !performer.Name.Valid {
if performer.Name == "" {
continue
}
pluckedNames = append(pluckedNames, performer.Name.String)
pluckedNames = append(pluckedNames, performer.Name)
}
missingPerformers := stringslice.StrFilter(names, func(name string) bool {
@ -271,12 +271,12 @@ func (i *Importer) createPerformers(ctx context.Context, names []string) ([]*mod
for _, name := range names {
newPerformer := *models.NewPerformer(name)
created, err := i.PerformerWriter.Create(ctx, newPerformer)
err := i.PerformerWriter.Create(ctx, &newPerformer)
if err != nil {
return nil, err
}
ret = append(ret, created)
ret = append(ret, &newPerformer)
}
return ret, nil

View file

@ -147,7 +147,7 @@ func TestImporterPreImportWithPerformer(t *testing.T) {
performerReaderWriter.On("FindByNames", testCtx, []string{existingPerformerName}, false).Return([]*models.Performer{
{
ID: existingPerformerID,
Name: models.NullString(existingPerformerName),
Name: existingPerformerName,
},
}, nil).Once()
performerReaderWriter.On("FindByNames", testCtx, []string{existingPerformerErr}, false).Return(nil, errors.New("FindByNames error")).Once()
@ -177,9 +177,10 @@ func TestImporterPreImportWithMissingPerformer(t *testing.T) {
}
performerReaderWriter.On("FindByNames", testCtx, []string{missingPerformerName}, false).Return(nil, nil).Times(3)
performerReaderWriter.On("Create", testCtx, mock.AnythingOfType("models.Performer")).Return(&models.Performer{
ID: existingPerformerID,
}, nil)
performerReaderWriter.On("Create", testCtx, mock.AnythingOfType("*models.Performer")).Run(func(args mock.Arguments) {
p := args.Get(1).(*models.Performer)
p.ID = existingPerformerID
}).Return(nil)
err := i.PreImport(testCtx)
assert.NotNil(t, err)
@ -210,7 +211,7 @@ func TestImporterPreImportWithMissingPerformerCreateErr(t *testing.T) {
}
performerReaderWriter.On("FindByNames", testCtx, []string{missingPerformerName}, false).Return(nil, nil).Once()
performerReaderWriter.On("Create", testCtx, mock.AnythingOfType("models.Performer")).Return(nil, errors.New("Create error"))
performerReaderWriter.On("Create", testCtx, mock.AnythingOfType("*models.Performer")).Return(errors.New("Create error"))
err := i.PreImport(testCtx)
assert.NotNil(t, err)

View file

@ -38,11 +38,12 @@ func autotagMatchPerformers(ctx context.Context, path string, performerReader ma
id := strconv.Itoa(pp.ID)
sp := &models.ScrapedPerformer{
Name: &pp.Name.String,
Name: &pp.Name,
StoredID: &id,
}
if pp.Gender.Valid {
sp.Gender = &pp.Gender.String
if pp.Gender.IsValid() {
v := pp.Gender.String()
sp.Gender = &v
}
ret = append(ret, sp)

View file

@ -379,7 +379,7 @@ func (c Client) FindStashBoxPerformersByNames(ctx context.Context, performerIDs
return fmt.Errorf("performer with id %d not found", performerID)
}
if performer.Name.Valid {
if performer.Name != "" {
performers = append(performers, performer)
}
}
@ -413,7 +413,7 @@ func (c Client) FindStashBoxPerformersByPerformerNames(ctx context.Context, perf
return fmt.Errorf("performer with id %d not found", performerID)
}
if performer.Name.Valid {
if performer.Name != "" {
performers = append(performers, performer)
}
}
@ -439,8 +439,8 @@ func (c Client) FindStashBoxPerformersByPerformerNames(ctx context.Context, perf
func (c Client) findStashBoxPerformersByNames(ctx context.Context, performers []*models.Performer) ([]*StashBoxPerformerQueryResult, error) {
var ret []*StashBoxPerformerQueryResult
for _, performer := range performers {
if performer.Name.Valid {
performerResults, err := c.queryStashBoxPerformer(ctx, performer.Name.String)
if performer.Name != "" {
performerResults, err := c.queryStashBoxPerformer(ctx, performer.Name)
if err != nil {
return nil, err
}
@ -867,7 +867,7 @@ func (c Client) SubmitSceneDraft(ctx context.Context, scene *models.Scene, endpo
performers := []*graphql.DraftEntityInput{}
for _, p := range scenePerformers {
performerDraft := graphql.DraftEntityInput{
Name: p.Name.String,
Name: p.Name,
}
stashIDs, err := pqb.GetStashIDs(ctx, p.ID)
@ -947,55 +947,57 @@ func (c Client) SubmitPerformerDraft(ctx context.Context, performer *models.Perf
image = bytes.NewReader(img)
}
if performer.Name.Valid {
draft.Name = performer.Name.String
if performer.Name != "" {
draft.Name = performer.Name
}
if performer.Birthdate.Valid {
draft.Birthdate = &performer.Birthdate.String
if performer.Birthdate != nil {
d := performer.Birthdate.String()
draft.Birthdate = &d
}
if performer.Country.Valid {
draft.Country = &performer.Country.String
if performer.Country != "" {
draft.Country = &performer.Country
}
if performer.Ethnicity.Valid {
draft.Ethnicity = &performer.Ethnicity.String
if performer.Ethnicity != "" {
draft.Ethnicity = &performer.Ethnicity
}
if performer.EyeColor.Valid {
draft.EyeColor = &performer.EyeColor.String
if performer.EyeColor != "" {
draft.EyeColor = &performer.EyeColor
}
if performer.FakeTits.Valid {
draft.BreastType = &performer.FakeTits.String
if performer.FakeTits != "" {
draft.BreastType = &performer.FakeTits
}
if performer.Gender.Valid {
draft.Gender = &performer.Gender.String
if performer.Gender.IsValid() {
v := performer.Gender.String()
draft.Gender = &v
}
if performer.HairColor.Valid {
draft.HairColor = &performer.HairColor.String
if performer.HairColor != "" {
draft.HairColor = &performer.HairColor
}
if performer.Height.Valid {
draft.Height = &performer.Height.String
if performer.Height != "" {
draft.Height = &performer.Height
}
if performer.Measurements.Valid {
draft.Measurements = &performer.Measurements.String
if performer.Measurements != "" {
draft.Measurements = &performer.Measurements
}
if performer.Piercings.Valid {
draft.Piercings = &performer.Piercings.String
if performer.Piercings != "" {
draft.Piercings = &performer.Piercings
}
if performer.Tattoos.Valid {
draft.Tattoos = &performer.Tattoos.String
if performer.Tattoos != "" {
draft.Tattoos = &performer.Tattoos
}
if performer.Aliases.Valid {
draft.Aliases = &performer.Aliases.String
if performer.Aliases != "" {
draft.Aliases = &performer.Aliases
}
var urls []string
if len(strings.TrimSpace(performer.Twitter.String)) > 0 {
urls = append(urls, "https://twitter.com/"+strings.TrimSpace(performer.Twitter.String))
if len(strings.TrimSpace(performer.Twitter)) > 0 {
urls = append(urls, "https://twitter.com/"+strings.TrimSpace(performer.Twitter))
}
if len(strings.TrimSpace(performer.Instagram.String)) > 0 {
urls = append(urls, "https://instagram.com/"+strings.TrimSpace(performer.Instagram.String))
if len(strings.TrimSpace(performer.Instagram)) > 0 {
urls = append(urls, "https://instagram.com/"+strings.TrimSpace(performer.Instagram))
}
if len(strings.TrimSpace(performer.URL.String)) > 0 {
urls = append(urls, strings.TrimSpace(performer.URL.String))
if len(strings.TrimSpace(performer.URL)) > 0 {
urls = append(urls, strings.TrimSpace(performer.URL))
}
if len(urls) > 0 {
draft.Urls = urls

View file

@ -66,6 +66,7 @@ type Database struct {
Image *ImageStore
Gallery *GalleryStore
Scene *SceneStore
Performer *PerformerStore
db *sqlx.DB
dbPath string
@ -85,6 +86,7 @@ func NewDatabase() *Database {
Scene: NewSceneStore(fileStore),
Image: NewImageStore(fileStore),
Gallery: NewGalleryStore(fileStore, folderStore),
Performer: NewPerformerStore(),
}
return ret

View file

@ -3,15 +3,17 @@ package sqlite
import (
"context"
"database/sql"
"errors"
"fmt"
"strings"
"github.com/doug-martin/goqu/v9"
"github.com/doug-martin/goqu/v9/exp"
"github.com/jmoiron/sqlx"
"github.com/stashapp/stash/pkg/models"
"github.com/stashapp/stash/pkg/sliceutil/intslice"
"github.com/stashapp/stash/pkg/utils"
"gopkg.in/guregu/null.v4"
"gopkg.in/guregu/null.v4/zero"
)
const performerTable = "performers"
@ -19,82 +21,227 @@ const performerIDColumn = "performer_id"
const performersTagsTable = "performers_tags"
const performersImageTable = "performers_image" // performer cover image
var countPerformersForTagQuery = `
SELECT tag_id AS id FROM performers_tags
WHERE performers_tags.tag_id = ?
GROUP BY performers_tags.performer_id
`
type performerQueryBuilder struct {
repository
type performerRow struct {
ID int `db:"id" goqu:"skipinsert"`
Checksum string `db:"checksum"`
Name zero.String `db:"name"`
Gender zero.String `db:"gender"`
URL zero.String `db:"url"`
Twitter zero.String `db:"twitter"`
Instagram zero.String `db:"instagram"`
Birthdate models.SQLiteDate `db:"birthdate"`
Ethnicity zero.String `db:"ethnicity"`
Country zero.String `db:"country"`
EyeColor zero.String `db:"eye_color"`
Height zero.String `db:"height"`
Measurements zero.String `db:"measurements"`
FakeTits zero.String `db:"fake_tits"`
CareerLength zero.String `db:"career_length"`
Tattoos zero.String `db:"tattoos"`
Piercings zero.String `db:"piercings"`
Aliases zero.String `db:"aliases"`
Favorite sql.NullBool `db:"favorite"`
CreatedAt models.SQLiteTimestamp `db:"created_at"`
UpdatedAt models.SQLiteTimestamp `db:"updated_at"`
Rating null.Int `db:"rating"`
Details zero.String `db:"details"`
DeathDate models.SQLiteDate `db:"death_date"`
HairColor zero.String `db:"hair_color"`
Weight null.Int `db:"weight"`
IgnoreAutoTag bool `db:"ignore_auto_tag"`
}
var PerformerReaderWriter = &performerQueryBuilder{
repository{
func (r *performerRow) fromPerformer(o models.Performer) {
r.ID = o.ID
r.Checksum = o.Checksum
r.Name = zero.StringFrom(o.Name)
if o.Gender.IsValid() {
r.Gender = zero.StringFrom(o.Gender.String())
}
r.URL = zero.StringFrom(o.URL)
r.Twitter = zero.StringFrom(o.Twitter)
r.Instagram = zero.StringFrom(o.Instagram)
if o.Birthdate != nil {
_ = r.Birthdate.Scan(o.Birthdate.Time)
}
r.Ethnicity = zero.StringFrom(o.Ethnicity)
r.Country = zero.StringFrom(o.Country)
r.EyeColor = zero.StringFrom(o.EyeColor)
r.Height = zero.StringFrom(o.Height)
r.Measurements = zero.StringFrom(o.Measurements)
r.FakeTits = zero.StringFrom(o.FakeTits)
r.CareerLength = zero.StringFrom(o.CareerLength)
r.Tattoos = zero.StringFrom(o.Tattoos)
r.Piercings = zero.StringFrom(o.Piercings)
r.Aliases = zero.StringFrom(o.Aliases)
r.Favorite = sql.NullBool{Bool: o.Favorite, Valid: true}
r.CreatedAt = models.SQLiteTimestamp{Timestamp: o.CreatedAt}
r.UpdatedAt = models.SQLiteTimestamp{Timestamp: o.UpdatedAt}
r.Rating = intFromPtr(o.Rating)
r.Details = zero.StringFrom(o.Details)
if o.DeathDate != nil {
_ = r.DeathDate.Scan(o.DeathDate.Time)
}
r.HairColor = zero.StringFrom(o.HairColor)
r.Weight = intFromPtr(o.Weight)
r.IgnoreAutoTag = o.IgnoreAutoTag
}
func (r *performerRow) resolve() *models.Performer {
ret := &models.Performer{
ID: r.ID,
Checksum: r.Checksum,
Name: r.Name.String,
Gender: models.GenderEnum(r.Gender.String),
URL: r.URL.String,
Twitter: r.Twitter.String,
Instagram: r.Instagram.String,
Birthdate: r.Birthdate.DatePtr(),
Ethnicity: r.Ethnicity.String,
Country: r.Country.String,
EyeColor: r.EyeColor.String,
Height: r.Height.String,
Measurements: r.Measurements.String,
FakeTits: r.FakeTits.String,
CareerLength: r.CareerLength.String,
Tattoos: r.Tattoos.String,
Piercings: r.Piercings.String,
Aliases: r.Aliases.String,
Favorite: r.Favorite.Bool,
CreatedAt: r.CreatedAt.Timestamp,
UpdatedAt: r.UpdatedAt.Timestamp,
Rating: nullIntPtr(r.Rating),
Details: r.Details.String,
DeathDate: r.DeathDate.DatePtr(),
HairColor: r.HairColor.String,
Weight: nullIntPtr(r.Weight),
IgnoreAutoTag: r.IgnoreAutoTag,
}
return ret
}
type performerRowRecord struct {
updateRecord
}
func (r *performerRowRecord) fromPartial(o models.PerformerPartial) {
r.setNullString("checksum", o.Checksum)
r.setNullString("name", o.Name)
r.setNullString("gender", o.Gender)
r.setNullString("url", o.URL)
r.setNullString("twitter", o.Twitter)
r.setNullString("instagram", o.Instagram)
r.setSQLiteDate("birthdate", o.Birthdate)
r.setNullString("ethnicity", o.Ethnicity)
r.setNullString("country", o.Country)
r.setNullString("eye_color", o.EyeColor)
r.setNullString("height", o.Height)
r.setNullString("measurements", o.Measurements)
r.setNullString("fake_tits", o.FakeTits)
r.setNullString("career_length", o.CareerLength)
r.setNullString("tattoos", o.Tattoos)
r.setNullString("piercings", o.Piercings)
r.setNullString("aliases", o.Aliases)
r.setBool("favorite", o.Favorite)
r.setSQLiteTimestamp("created_at", o.CreatedAt)
r.setSQLiteTimestamp("updated_at", o.UpdatedAt)
r.setNullInt("rating", o.Rating)
r.setNullString("details", o.Details)
r.setSQLiteDate("death_date", o.DeathDate)
r.setNullString("hair_color", o.HairColor)
r.setNullInt("weight", o.Weight)
r.setBool("ignore_auto_tag", o.IgnoreAutoTag)
}
type PerformerStore struct {
repository
tableMgr *table
}
func NewPerformerStore() *PerformerStore {
return &PerformerStore{
repository: repository{
tableName: performerTable,
idColumn: idColumn,
},
tableMgr: performerTableMgr,
}
}
func (qb *performerQueryBuilder) Create(ctx context.Context, newObject models.Performer) (*models.Performer, error) {
var ret models.Performer
if err := qb.insertObject(ctx, newObject, &ret); err != nil {
return nil, err
}
func (qb *PerformerStore) Create(ctx context.Context, newObject *models.Performer) error {
var r performerRow
r.fromPerformer(*newObject)
return &ret, nil
}
func (qb *performerQueryBuilder) Update(ctx context.Context, updatedObject models.PerformerPartial) (*models.Performer, error) {
const partial = true
if err := qb.update(ctx, updatedObject.ID, updatedObject, partial); err != nil {
return nil, err
}
var ret models.Performer
if err := qb.getByID(ctx, updatedObject.ID, &ret); err != nil {
return nil, err
}
return &ret, nil
}
func (qb *performerQueryBuilder) UpdateFull(ctx context.Context, updatedObject models.Performer) (*models.Performer, error) {
const partial = false
if err := qb.update(ctx, updatedObject.ID, updatedObject, partial); err != nil {
return nil, err
}
var ret models.Performer
if err := qb.getByID(ctx, updatedObject.ID, &ret); err != nil {
return nil, err
}
return &ret, nil
}
func (qb *performerQueryBuilder) Destroy(ctx context.Context, id int) error {
// TODO - add on delete cascade to performers_scenes
_, err := qb.tx.Exec(ctx, "DELETE FROM performers_scenes WHERE performer_id = ?", id)
id, err := qb.tableMgr.insertID(ctx, r)
if err != nil {
return err
}
updated, err := qb.Find(ctx, id)
if err != nil {
return fmt.Errorf("finding after create: %w", err)
}
*newObject = *updated
return nil
}
func (qb *PerformerStore) UpdatePartial(ctx context.Context, id int, updatedObject models.PerformerPartial) (*models.Performer, error) {
r := performerRowRecord{
updateRecord{
Record: make(exp.Record),
},
}
r.fromPartial(updatedObject)
if len(r.Record) > 0 {
if err := qb.tableMgr.updateByID(ctx, id, r.Record); err != nil {
return nil, err
}
}
return qb.Find(ctx, id)
}
func (qb *PerformerStore) Update(ctx context.Context, updatedObject *models.Performer) error {
var r performerRow
r.fromPerformer(*updatedObject)
if err := qb.tableMgr.updateByID(ctx, updatedObject.ID, r); err != nil {
return err
}
return nil
}
func (qb *PerformerStore) Destroy(ctx context.Context, id int) error {
return qb.destroyExisting(ctx, []int{id})
}
func (qb *performerQueryBuilder) Find(ctx context.Context, id int) (*models.Performer, error) {
var ret models.Performer
if err := qb.getByID(ctx, id, &ret); err != nil {
if errors.Is(err, sql.ErrNoRows) {
return nil, nil
}
return nil, err
}
return &ret, nil
func (qb *PerformerStore) table() exp.IdentifierExpression {
return qb.tableMgr.table
}
func (qb *performerQueryBuilder) FindMany(ctx context.Context, ids []int) ([]*models.Performer, error) {
func (qb *PerformerStore) selectDataset() *goqu.SelectDataset {
return dialect.From(qb.table()).Select(qb.table().All())
}
func (qb *PerformerStore) Find(ctx context.Context, id int) (*models.Performer, error) {
q := qb.selectDataset().Where(qb.tableMgr.byID(id))
ret, err := qb.get(ctx, q)
if err != nil {
return nil, fmt.Errorf("getting scene by id %d: %w", id, err)
}
return ret, nil
}
func (qb *PerformerStore) FindMany(ctx context.Context, ids []int) ([]*models.Performer, error) {
tableMgr := performerTableMgr
q := goqu.Select("*").From(tableMgr.table).Where(tableMgr.byIDInts(ids...))
unsorted, err := qb.getMany(ctx, q)
@ -118,16 +265,31 @@ func (qb *performerQueryBuilder) FindMany(ctx context.Context, ids []int) ([]*mo
return ret, nil
}
func (qb *performerQueryBuilder) getMany(ctx context.Context, q *goqu.SelectDataset) ([]*models.Performer, error) {
func (qb *PerformerStore) get(ctx context.Context, q *goqu.SelectDataset) (*models.Performer, error) {
ret, err := qb.getMany(ctx, q)
if err != nil {
return nil, err
}
if len(ret) == 0 {
return nil, sql.ErrNoRows
}
return ret[0], nil
}
func (qb *PerformerStore) getMany(ctx context.Context, q *goqu.SelectDataset) ([]*models.Performer, error) {
const single = false
var ret []*models.Performer
if err := queryFunc(ctx, q, single, func(r *sqlx.Rows) error {
var f models.Performer
var f performerRow
if err := r.StructScan(&f); err != nil {
return err
}
ret = append(ret, &f)
s := f.resolve()
ret = append(ret, s)
return nil
}); err != nil {
return nil, err
@ -136,95 +298,126 @@ func (qb *performerQueryBuilder) getMany(ctx context.Context, q *goqu.SelectData
return ret, nil
}
func (qb *performerQueryBuilder) FindBySceneID(ctx context.Context, sceneID int) ([]*models.Performer, error) {
query := selectAll("performers") + `
LEFT JOIN performers_scenes as scenes_join on scenes_join.performer_id = performers.id
WHERE scenes_join.scene_id = ?
`
args := []interface{}{sceneID}
return qb.queryPerformers(ctx, query, args)
func (qb *PerformerStore) findBySubquery(ctx context.Context, sq *goqu.SelectDataset) ([]*models.Performer, error) {
table := qb.table()
q := qb.selectDataset().Where(
table.Col(idColumn).Eq(
sq,
),
)
return qb.getMany(ctx, q)
}
func (qb *performerQueryBuilder) FindByImageID(ctx context.Context, imageID int) ([]*models.Performer, error) {
query := selectAll("performers") + `
LEFT JOIN performers_images as images_join on images_join.performer_id = performers.id
WHERE images_join.image_id = ?
`
args := []interface{}{imageID}
return qb.queryPerformers(ctx, query, args)
}
func (qb *PerformerStore) FindBySceneID(ctx context.Context, sceneID int) ([]*models.Performer, error) {
sq := dialect.From(scenesPerformersJoinTable).Select(scenesPerformersJoinTable.Col(performerIDColumn)).Where(
scenesPerformersJoinTable.Col(sceneIDColumn).Eq(sceneID),
)
ret, err := qb.findBySubquery(ctx, sq)
func (qb *performerQueryBuilder) FindByGalleryID(ctx context.Context, galleryID int) ([]*models.Performer, error) {
query := selectAll("performers") + `
LEFT JOIN performers_galleries as galleries_join on galleries_join.performer_id = performers.id
WHERE galleries_join.gallery_id = ?
`
args := []interface{}{galleryID}
return qb.queryPerformers(ctx, query, args)
}
func (qb *performerQueryBuilder) FindNamesBySceneID(ctx context.Context, sceneID int) ([]*models.Performer, error) {
query := `
SELECT performers.name FROM performers
LEFT JOIN performers_scenes as scenes_join on scenes_join.performer_id = performers.id
WHERE scenes_join.scene_id = ?
`
args := []interface{}{sceneID}
return qb.queryPerformers(ctx, query, args)
}
func (qb *performerQueryBuilder) FindByNames(ctx context.Context, names []string, nocase bool) ([]*models.Performer, error) {
query := "SELECT * FROM performers WHERE name"
if nocase {
query += " COLLATE NOCASE"
if err != nil {
return nil, fmt.Errorf("getting performers for scene %d: %w", sceneID, err)
}
query += " IN " + getInBinding(len(names))
return ret, nil
}
func (qb *PerformerStore) FindByImageID(ctx context.Context, imageID int) ([]*models.Performer, error) {
sq := dialect.From(performersImagesJoinTable).Select(performersImagesJoinTable.Col(performerIDColumn)).Where(
performersImagesJoinTable.Col(imageIDColumn).Eq(imageID),
)
ret, err := qb.findBySubquery(ctx, sq)
if err != nil {
return nil, fmt.Errorf("getting performers for image %d: %w", imageID, err)
}
return ret, nil
}
func (qb *PerformerStore) FindByGalleryID(ctx context.Context, galleryID int) ([]*models.Performer, error) {
sq := dialect.From(performersGalleriesJoinTable).Select(performersGalleriesJoinTable.Col(performerIDColumn)).Where(
performersGalleriesJoinTable.Col(galleryIDColumn).Eq(galleryID),
)
ret, err := qb.findBySubquery(ctx, sq)
if err != nil {
return nil, fmt.Errorf("getting performers for gallery %d: %w", galleryID, err)
}
return ret, nil
}
func (qb *PerformerStore) FindByNames(ctx context.Context, names []string, nocase bool) ([]*models.Performer, error) {
clause := "name "
if nocase {
clause += "COLLATE NOCASE "
}
clause += "IN " + getInBinding(len(names))
var args []interface{}
for _, name := range names {
args = append(args, name)
}
return qb.queryPerformers(ctx, query, args)
}
func (qb *performerQueryBuilder) CountByTagID(ctx context.Context, tagID int) (int, error) {
args := []interface{}{tagID}
return qb.runCountQuery(ctx, qb.buildCountQuery(countPerformersForTagQuery), args)
}
sq := qb.selectDataset().Prepared(true).Where(
goqu.L(clause, args...),
)
ret, err := qb.getMany(ctx, sq)
func (qb *performerQueryBuilder) Count(ctx context.Context) (int, error) {
return qb.runCountQuery(ctx, qb.buildCountQuery("SELECT performers.id FROM performers"), nil)
}
func (qb *performerQueryBuilder) All(ctx context.Context) ([]*models.Performer, error) {
return qb.queryPerformers(ctx, selectAll("performers")+qb.getPerformerSort(nil), nil)
}
func (qb *performerQueryBuilder) QueryForAutoTag(ctx context.Context, words []string) ([]*models.Performer, error) {
// TODO - Query needs to be changed to support queries of this type, and
// this method should be removed
query := selectAll(performerTable)
var whereClauses []string
var args []interface{}
for _, w := range words {
whereClauses = append(whereClauses, "name like ?")
args = append(args, w+"%")
// TODO - commented out until alias matching works both ways
// whereClauses = append(whereClauses, "aliases like ?")
// args = append(args, w+"%")
if err != nil {
return nil, fmt.Errorf("getting performers by names: %w", err)
}
whereOr := "(" + strings.Join(whereClauses, " OR ") + ")"
where := strings.Join([]string{
"ignore_auto_tag = 0",
whereOr,
}, " AND ")
return qb.queryPerformers(ctx, query+" WHERE "+where, args)
return ret, nil
}
func (qb *performerQueryBuilder) validateFilter(filter *models.PerformerFilterType) error {
func (qb *PerformerStore) CountByTagID(ctx context.Context, tagID int) (int, error) {
joinTable := performersTagsJoinTable
q := dialect.Select(goqu.COUNT("*")).From(joinTable).Where(joinTable.Col(tagIDColumn).Eq(tagID))
return count(ctx, q)
}
func (qb *PerformerStore) Count(ctx context.Context) (int, error) {
q := dialect.Select(goqu.COUNT("*")).From(qb.table())
return count(ctx, q)
}
func (qb *PerformerStore) All(ctx context.Context) ([]*models.Performer, error) {
return qb.getMany(ctx, qb.selectDataset())
}
func (qb *PerformerStore) QueryForAutoTag(ctx context.Context, words []string) ([]*models.Performer, error) {
// TODO - Query needs to be changed to support queries of this type, and
// this method should be removed
table := qb.table()
sq := dialect.From(table).Select(table.Col(idColumn)).Where()
var whereClauses []exp.Expression
for _, w := range words {
whereClauses = append(whereClauses, table.Col("name").Like(w+"%"))
// TODO - commented out until alias matching works both ways
// whereClauses = append(whereClauses, table.Col("aliases").Like(w+"%")
}
sq = sq.Where(
goqu.Or(whereClauses...),
table.Col("ignore_auto_tag").Eq(0),
)
ret, err := qb.findBySubquery(ctx, sq)
if err != nil {
return nil, fmt.Errorf("getting performers for autotag: %w", err)
}
return ret, nil
}
func (qb *PerformerStore) validateFilter(filter *models.PerformerFilterType) error {
const and = "AND"
const or = "OR"
const not = "NOT"
@ -255,7 +448,7 @@ func (qb *performerQueryBuilder) validateFilter(filter *models.PerformerFilterTy
return nil
}
func (qb *performerQueryBuilder) makeFilter(ctx context.Context, filter *models.PerformerFilterType) *filterBuilder {
func (qb *PerformerStore) makeFilter(ctx context.Context, filter *models.PerformerFilterType) *filterBuilder {
query := &filterBuilder{}
if filter.And != nil {
@ -322,7 +515,7 @@ func (qb *performerQueryBuilder) makeFilter(ctx context.Context, filter *models.
return query
}
func (qb *performerQueryBuilder) Query(ctx context.Context, performerFilter *models.PerformerFilterType, findFilter *models.FindFilterType) ([]*models.Performer, int, error) {
func (qb *PerformerStore) Query(ctx context.Context, performerFilter *models.PerformerFilterType, findFilter *models.FindFilterType) ([]*models.Performer, int, error) {
if performerFilter == nil {
performerFilter = &models.PerformerFilterType{}
}
@ -359,7 +552,7 @@ func (qb *performerQueryBuilder) Query(ctx context.Context, performerFilter *mod
return performers, countResult, nil
}
func performerIsMissingCriterionHandler(qb *performerQueryBuilder, isMissing *string) criterionHandlerFunc {
func performerIsMissingCriterionHandler(qb *PerformerStore, isMissing *string) criterionHandlerFunc {
return func(ctx context.Context, f *filterBuilder) {
if isMissing != nil && *isMissing != "" {
switch *isMissing {
@ -400,7 +593,7 @@ func performerAgeFilterCriterionHandler(age *models.IntCriterionInput) criterion
}
}
func performerTagsCriterionHandler(qb *performerQueryBuilder, tags *models.HierarchicalMultiCriterionInput) criterionHandlerFunc {
func performerTagsCriterionHandler(qb *PerformerStore, tags *models.HierarchicalMultiCriterionInput) criterionHandlerFunc {
h := joinedHierarchicalMultiCriterionHandlerBuilder{
tx: qb.tx,
@ -417,7 +610,7 @@ func performerTagsCriterionHandler(qb *performerQueryBuilder, tags *models.Hiera
return h.handler(tags)
}
func performerTagCountCriterionHandler(qb *performerQueryBuilder, count *models.IntCriterionInput) criterionHandlerFunc {
func performerTagCountCriterionHandler(qb *PerformerStore, count *models.IntCriterionInput) criterionHandlerFunc {
h := countCriterionHandlerBuilder{
primaryTable: performerTable,
joinTable: performersTagsTable,
@ -427,7 +620,7 @@ func performerTagCountCriterionHandler(qb *performerQueryBuilder, count *models.
return h.handler(count)
}
func performerSceneCountCriterionHandler(qb *performerQueryBuilder, count *models.IntCriterionInput) criterionHandlerFunc {
func performerSceneCountCriterionHandler(qb *PerformerStore, count *models.IntCriterionInput) criterionHandlerFunc {
h := countCriterionHandlerBuilder{
primaryTable: performerTable,
joinTable: performersScenesTable,
@ -437,7 +630,7 @@ func performerSceneCountCriterionHandler(qb *performerQueryBuilder, count *model
return h.handler(count)
}
func performerImageCountCriterionHandler(qb *performerQueryBuilder, count *models.IntCriterionInput) criterionHandlerFunc {
func performerImageCountCriterionHandler(qb *PerformerStore, count *models.IntCriterionInput) criterionHandlerFunc {
h := countCriterionHandlerBuilder{
primaryTable: performerTable,
joinTable: performersImagesTable,
@ -447,7 +640,7 @@ func performerImageCountCriterionHandler(qb *performerQueryBuilder, count *model
return h.handler(count)
}
func performerGalleryCountCriterionHandler(qb *performerQueryBuilder, count *models.IntCriterionInput) criterionHandlerFunc {
func performerGalleryCountCriterionHandler(qb *PerformerStore, count *models.IntCriterionInput) criterionHandlerFunc {
h := countCriterionHandlerBuilder{
primaryTable: performerTable,
joinTable: performersGalleriesTable,
@ -457,7 +650,7 @@ func performerGalleryCountCriterionHandler(qb *performerQueryBuilder, count *mod
return h.handler(count)
}
func performerStudiosCriterionHandler(qb *performerQueryBuilder, studios *models.HierarchicalMultiCriterionInput) criterionHandlerFunc {
func performerStudiosCriterionHandler(qb *PerformerStore, studios *models.HierarchicalMultiCriterionInput) criterionHandlerFunc {
return func(ctx context.Context, f *filterBuilder) {
if studios != nil {
formatMaps := []utils.StrFormatMap{
@ -534,7 +727,7 @@ func performerStudiosCriterionHandler(qb *performerQueryBuilder, studios *models
}
}
func (qb *performerQueryBuilder) getPerformerSort(findFilter *models.FindFilterType) string {
func (qb *PerformerStore) getPerformerSort(findFilter *models.FindFilterType) string {
var sort string
var direction string
if findFilter == nil {
@ -561,16 +754,7 @@ func (qb *performerQueryBuilder) getPerformerSort(findFilter *models.FindFilterT
return getSort(sort, direction, "performers")
}
func (qb *performerQueryBuilder) queryPerformers(ctx context.Context, query string, args []interface{}) ([]*models.Performer, error) {
var ret models.Performers
if err := qb.query(ctx, query, args, &ret); err != nil {
return nil, err
}
return []*models.Performer(ret), nil
}
func (qb *performerQueryBuilder) tagsRepository() *joinRepository {
func (qb *PerformerStore) tagsRepository() *joinRepository {
return &joinRepository{
repository: repository{
tx: qb.tx,
@ -581,16 +765,16 @@ func (qb *performerQueryBuilder) tagsRepository() *joinRepository {
}
}
func (qb *performerQueryBuilder) GetTagIDs(ctx context.Context, id int) ([]int, error) {
func (qb *PerformerStore) GetTagIDs(ctx context.Context, id int) ([]int, error) {
return qb.tagsRepository().getIDs(ctx, id)
}
func (qb *performerQueryBuilder) UpdateTags(ctx context.Context, id int, tagIDs []int) error {
func (qb *PerformerStore) UpdateTags(ctx context.Context, id int, tagIDs []int) error {
// Delete the existing joins and then create new ones
return qb.tagsRepository().replace(ctx, id, tagIDs)
}
func (qb *performerQueryBuilder) imageRepository() *imageRepository {
func (qb *PerformerStore) imageRepository() *imageRepository {
return &imageRepository{
repository: repository{
tx: qb.tx,
@ -601,19 +785,19 @@ func (qb *performerQueryBuilder) imageRepository() *imageRepository {
}
}
func (qb *performerQueryBuilder) GetImage(ctx context.Context, performerID int) ([]byte, error) {
func (qb *PerformerStore) GetImage(ctx context.Context, performerID int) ([]byte, error) {
return qb.imageRepository().get(ctx, performerID)
}
func (qb *performerQueryBuilder) UpdateImage(ctx context.Context, performerID int, image []byte) error {
func (qb *PerformerStore) UpdateImage(ctx context.Context, performerID int, image []byte) error {
return qb.imageRepository().replace(ctx, performerID, image)
}
func (qb *performerQueryBuilder) DestroyImage(ctx context.Context, performerID int) error {
func (qb *PerformerStore) DestroyImage(ctx context.Context, performerID int) error {
return qb.imageRepository().destroy(ctx, []int{performerID})
}
func (qb *performerQueryBuilder) stashIDRepository() *stashIDRepository {
func (qb *PerformerStore) stashIDRepository() *stashIDRepository {
return &stashIDRepository{
repository{
tx: qb.tx,
@ -623,40 +807,51 @@ func (qb *performerQueryBuilder) stashIDRepository() *stashIDRepository {
}
}
func (qb *performerQueryBuilder) GetStashIDs(ctx context.Context, performerID int) ([]models.StashID, error) {
func (qb *PerformerStore) GetStashIDs(ctx context.Context, performerID int) ([]models.StashID, error) {
return qb.stashIDRepository().get(ctx, performerID)
}
func (qb *performerQueryBuilder) UpdateStashIDs(ctx context.Context, performerID int, stashIDs []models.StashID) error {
func (qb *PerformerStore) UpdateStashIDs(ctx context.Context, performerID int, stashIDs []models.StashID) error {
return qb.stashIDRepository().replace(ctx, performerID, stashIDs)
}
func (qb *performerQueryBuilder) FindByStashID(ctx context.Context, stashID models.StashID) ([]*models.Performer, error) {
query := selectAll("performers") + `
LEFT JOIN performer_stash_ids on performer_stash_ids.performer_id = performers.id
WHERE performer_stash_ids.stash_id = ?
AND performer_stash_ids.endpoint = ?
`
args := []interface{}{stashID.StashID, stashID.Endpoint}
return qb.queryPerformers(ctx, query, args)
}
func (qb *PerformerStore) FindByStashID(ctx context.Context, stashID models.StashID) ([]*models.Performer, error) {
sq := dialect.From(performersStashIDsJoinTable).Select(performersStashIDsJoinTable.Col(performerIDColumn)).Where(
performersStashIDsJoinTable.Col("stash_id").Eq(stashID.StashID),
performersStashIDsJoinTable.Col("endpoint").Eq(stashID.Endpoint),
)
ret, err := qb.findBySubquery(ctx, sq)
func (qb *performerQueryBuilder) FindByStashIDStatus(ctx context.Context, hasStashID bool, stashboxEndpoint string) ([]*models.Performer, error) {
query := selectAll("performers") + `
LEFT JOIN performer_stash_ids on performer_stash_ids.performer_id = performers.id
`
if hasStashID {
query += `
WHERE performer_stash_ids.stash_id IS NOT NULL
AND performer_stash_ids.endpoint = ?
`
} else {
query += `
WHERE performer_stash_ids.stash_id IS NULL
`
if err != nil {
return nil, fmt.Errorf("getting performers for stash ID %s: %w", stashID.StashID, err)
}
args := []interface{}{stashboxEndpoint}
return qb.queryPerformers(ctx, query, args)
return ret, nil
}
func (qb *PerformerStore) FindByStashIDStatus(ctx context.Context, hasStashID bool, stashboxEndpoint string) ([]*models.Performer, error) {
table := qb.table()
sq := dialect.From(table).LeftJoin(
performersStashIDsJoinTable,
goqu.On(table.Col(idColumn).Eq(performersStashIDsJoinTable.Col(performerIDColumn))),
).Select(table.Col(idColumn))
if hasStashID {
sq = sq.Where(
performersStashIDsJoinTable.Col("stash_id").IsNotNull(),
performersStashIDsJoinTable.Col("endpoint").Eq(stashboxEndpoint),
)
} else {
sq = sq.Where(
performersStashIDsJoinTable.Col("stash_id").IsNull(),
)
}
ret, err := qb.findBySubquery(ctx, sq)
if err != nil {
return nil, fmt.Errorf("getting performers for stash-box endpoint %s: %w", stashboxEndpoint, err)
}
return ret, nil
}

File diff suppressed because it is too large Load diff

View file

@ -317,12 +317,6 @@ func (qb *SceneStore) Update(ctx context.Context, updatedObject *models.Scene) e
}
func (qb *SceneStore) Destroy(ctx context.Context, id int) error {
// delete all related table rows
// TODO - this should be handled by a delete cascade
if err := qb.performersRepository().destroy(ctx, []int{id}); err != nil {
return err
}
// scene markers should be handled prior to calling destroy
// galleries should be handled prior to calling destroy

View file

@ -552,7 +552,7 @@ func populateDB() error {
return fmt.Errorf("error creating movies: %s", err.Error())
}
if err := createPerformers(ctx, sqlite.PerformerReaderWriter, performersNameCase, performersNameNoCase); err != nil {
if err := createPerformers(ctx, performersNameCase, performersNameNoCase); err != nil {
return fmt.Errorf("error creating performers: %s", err.Error())
}
@ -584,7 +584,7 @@ func populateDB() error {
return fmt.Errorf("error creating saved filters: %s", err.Error())
}
if err := linkPerformerTags(ctx, sqlite.PerformerReaderWriter); err != nil {
if err := linkPerformerTags(ctx); err != nil {
return fmt.Errorf("error linking performer tags: %s", err.Error())
}
@ -1226,8 +1226,10 @@ func getPerformerStringValue(index int, field string) string {
return getPrefixedStringValue("performer", index, field)
}
func getPerformerNullStringValue(index int, field string) sql.NullString {
return getPrefixedNullStringValue("performer", index, field)
func getPerformerNullStringValue(index int, field string) string {
ret := getPrefixedNullStringValue("performer", index, field)
return ret.String
}
func getPerformerBoolValue(index int) bool {
@ -1235,24 +1237,29 @@ func getPerformerBoolValue(index int) bool {
return index == 1
}
func getPerformerBirthdate(index int) string {
func getPerformerBirthdate(index int) *models.Date {
const minAge = 18
birthdate := time.Now()
birthdate = birthdate.AddDate(-minAge-index, -1, -1)
return birthdate.Format("2006-01-02")
ret := models.Date{
Time: birthdate,
}
return &ret
}
func getPerformerDeathDate(index int) models.SQLiteDate {
func getPerformerDeathDate(index int) *models.Date {
if index != 5 {
return models.SQLiteDate{}
return nil
}
deathDate := time.Now()
deathDate = deathDate.AddDate(-index+1, -1, -1)
return models.SQLiteDate{
String: deathDate.Format("2006-01-02"),
Valid: true,
ret := models.Date{
Time: deathDate,
}
return &ret
}
func getPerformerCareerLength(index int) *string {
@ -1268,8 +1275,17 @@ func getIgnoreAutoTag(index int) bool {
return index%5 == 0
}
func performerStashID(i int) models.StashID {
return models.StashID{
StashID: getPerformerStringValue(i, "stashid"),
Endpoint: getPerformerStringValue(i, "endpoint"),
}
}
// createPerformers creates n performers with plain Name and o performers with camel cased NaMe included
func createPerformers(ctx context.Context, pqb models.PerformerReaderWriter, n int, o int) error {
func createPerformers(ctx context.Context, n int, o int) error {
pqb := db.Performer
const namePlain = "Name"
const nameNoCase = "NaMe"
@ -1285,34 +1301,39 @@ func createPerformers(ctx context.Context, pqb models.PerformerReaderWriter, n i
// performers [ i ] and [ n + o - i - 1 ] should have similar names with only the Name!=NaMe part different
performer := models.Performer{
Name: sql.NullString{String: getPerformerStringValue(index, name), Valid: true},
Name: getPerformerStringValue(index, name),
Checksum: getPerformerStringValue(i, checksumField),
URL: getPerformerNullStringValue(i, urlField),
Favorite: sql.NullBool{Bool: getPerformerBoolValue(i), Valid: true},
Birthdate: models.SQLiteDate{
String: getPerformerBirthdate(i),
Valid: true,
},
Favorite: getPerformerBoolValue(i),
Birthdate: getPerformerBirthdate(i),
DeathDate: getPerformerDeathDate(i),
Details: sql.NullString{String: getPerformerStringValue(i, "Details"), Valid: true},
Ethnicity: sql.NullString{String: getPerformerStringValue(i, "Ethnicity"), Valid: true},
Rating: getRating(i),
Details: getPerformerStringValue(i, "Details"),
Ethnicity: getPerformerStringValue(i, "Ethnicity"),
Rating: getIntPtr(getRating(i)),
IgnoreAutoTag: getIgnoreAutoTag(i),
}
careerLength := getPerformerCareerLength(i)
if careerLength != nil {
performer.CareerLength = models.NullString(*careerLength)
performer.CareerLength = *careerLength
}
created, err := pqb.Create(ctx, performer)
err := pqb.Create(ctx, &performer)
if err != nil {
return fmt.Errorf("Error creating performer %v+: %s", performer, err.Error())
}
performerIDs = append(performerIDs, created.ID)
performerNames = append(performerNames, created.Name.String)
if (index+1)%5 != 0 {
if err := pqb.UpdateStashIDs(ctx, performer.ID, []models.StashID{
performerStashID(i),
}); err != nil {
return fmt.Errorf("setting performer stash ids: %w", err)
}
}
performerIDs = append(performerIDs, performer.ID)
performerNames = append(performerNames, performer.Name)
}
return nil
@ -1581,7 +1602,8 @@ func doLinks(links [][2]int, fn func(idx1, idx2 int) error) error {
return nil
}
func linkPerformerTags(ctx context.Context, qb models.PerformerReaderWriter) error {
func linkPerformerTags(ctx context.Context) error {
qb := db.Performer
return doLinks(performerTagLinks, func(performerIndex, tagIndex int) error {
performerID := performerIDs[performerIndex]
tagID := tagIDs[tagIndex]

View file

@ -24,6 +24,9 @@ var (
scenesPerformersJoinTable = goqu.T(performersScenesTable)
scenesStashIDsJoinTable = goqu.T("scene_stash_ids")
scenesMoviesJoinTable = goqu.T(moviesScenesTable)
performersTagsJoinTable = goqu.T(performersTagsTable)
performersStashIDsJoinTable = goqu.T("performer_stash_ids")
)
var (

View file

@ -1011,7 +1011,7 @@ func TestTagMerge(t *testing.T) {
assert.Contains(g.TagIDs.List(), destID)
// ensure performer points to new tag
performerTagIDs, err := sqlite.PerformerReaderWriter.GetTagIDs(ctx, performerIDs[performerIdxWithTwoTags])
performerTagIDs, err := db.Performer.GetTagIDs(ctx, performerIDs[performerIdxWithTwoTags])
if err != nil {
return err
}

View file

@ -108,7 +108,7 @@ func (db *Database) TxnRepository() models.Repository {
Gallery: db.Gallery,
Image: db.Image,
Movie: MovieReaderWriter,
Performer: PerformerReaderWriter,
Performer: db.Performer,
Scene: db.Scene,
SceneMarker: SceneMarkerReaderWriter,
ScrapedItem: ScrapedItemReaderWriter,