Movie group renames (#5039)

* Rename Movie and MoviePartial to Group/GroupPartial
* Rename Movie interfaces
* Update movie url builders to use group
* Rename movieRoutes to groupRoutes
* Update dataloader
* Update names in sqlite package
* Rename in resolvers
* Add GroupByURL to scraper config
* Scraper backward compatibility hacks
This commit is contained in:
WithoutPants
2024-07-04 09:10:26 +10:00
committed by GitHub
parent b69d9cc840
commit 15a7b8a859
83 changed files with 1765 additions and 1646 deletions

View File

@@ -51,11 +51,11 @@ models:
fieldName: DurationFinite fieldName: DurationFinite
frame_rate: frame_rate:
fieldName: FrameRateFinite fieldName: FrameRateFinite
# group is movie under the hood # movie is group under the hood
Group: Movie:
model: github.com/stashapp/stash/pkg/models.Movie model: github.com/stashapp/stash/pkg/models.Group
GroupFilterType: MovieFilterType:
model: github.com/stashapp/stash/pkg/models.MovieFilterType model: github.com/stashapp/stash/pkg/models.GroupFilterType
# autobind on config causes generation issues # autobind on config causes generation issues
BlobsStorageType: BlobsStorageType:
model: github.com/stashapp/stash/internal/manager/config.BlobsStorageType model: github.com/stashapp/stash/internal/manager/config.BlobsStorageType

View File

@@ -346,17 +346,17 @@ func (t changesetTranslator) updateStashIDs(value []models.StashID, field string
} }
} }
func (t changesetTranslator) relatedMovies(value []models.SceneMovieInput) (models.RelatedMovies, error) { func (t changesetTranslator) relatedGroupsFromMovies(value []models.SceneMovieInput) (models.RelatedGroups, error) {
moviesScenes, err := models.MoviesScenesFromInput(value) groupsScenes, err := models.GroupsScenesFromInput(value)
if err != nil { if err != nil {
return models.RelatedMovies{}, err return models.RelatedGroups{}, err
} }
return models.NewRelatedMovies(moviesScenes), nil return models.NewRelatedGroups(groupsScenes), nil
} }
func moviesScenesFromGroupInput(input []models.SceneGroupInput) ([]models.MoviesScenes, error) { func groupsScenesFromGroupInput(input []models.SceneGroupInput) ([]models.GroupsScenes, error) {
ret := make([]models.MoviesScenes, len(input)) ret := make([]models.GroupsScenes, len(input))
for i, v := range input { for i, v := range input {
mID, err := strconv.Atoi(v.GroupID) mID, err := strconv.Atoi(v.GroupID)
@@ -364,8 +364,8 @@ func moviesScenesFromGroupInput(input []models.SceneGroupInput) ([]models.Movies
return nil, fmt.Errorf("invalid group ID: %s", v.GroupID) return nil, fmt.Errorf("invalid group ID: %s", v.GroupID)
} }
ret[i] = models.MoviesScenes{ ret[i] = models.GroupsScenes{
MovieID: mID, GroupID: mID,
SceneIndex: v.SceneIndex, SceneIndex: v.SceneIndex,
} }
} }
@@ -373,48 +373,48 @@ func moviesScenesFromGroupInput(input []models.SceneGroupInput) ([]models.Movies
return ret, nil return ret, nil
} }
func (t changesetTranslator) relatedMoviesFromGroups(value []models.SceneGroupInput) (models.RelatedMovies, error) { func (t changesetTranslator) relatedGroups(value []models.SceneGroupInput) (models.RelatedGroups, error) {
moviesScenes, err := moviesScenesFromGroupInput(value) groupsScenes, err := groupsScenesFromGroupInput(value)
if err != nil { if err != nil {
return models.RelatedMovies{}, err return models.RelatedGroups{}, err
} }
return models.NewRelatedMovies(moviesScenes), nil return models.NewRelatedGroups(groupsScenes), nil
} }
func (t changesetTranslator) updateMovieIDs(value []models.SceneMovieInput, field string) (*models.UpdateMovieIDs, error) { func (t changesetTranslator) updateGroupIDsFromMovies(value []models.SceneMovieInput, field string) (*models.UpdateGroupIDs, error) {
if !t.hasField(field) { if !t.hasField(field) {
return nil, nil return nil, nil
} }
moviesScenes, err := models.MoviesScenesFromInput(value) groupsScenes, err := models.GroupsScenesFromInput(value)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &models.UpdateMovieIDs{ return &models.UpdateGroupIDs{
Movies: moviesScenes, Groups: groupsScenes,
Mode: models.RelationshipUpdateModeSet, Mode: models.RelationshipUpdateModeSet,
}, nil }, nil
} }
func (t changesetTranslator) updateMovieIDsFromGroups(value []models.SceneGroupInput, field string) (*models.UpdateMovieIDs, error) { func (t changesetTranslator) updateGroupIDs(value []models.SceneGroupInput, field string) (*models.UpdateGroupIDs, error) {
if !t.hasField(field) { if !t.hasField(field) {
return nil, nil return nil, nil
} }
moviesScenes, err := moviesScenesFromGroupInput(value) groupsScenes, err := groupsScenesFromGroupInput(value)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &models.UpdateMovieIDs{ return &models.UpdateGroupIDs{
Movies: moviesScenes, Groups: groupsScenes,
Mode: models.RelationshipUpdateModeSet, Mode: models.RelationshipUpdateModeSet,
}, nil }, nil
} }
func (t changesetTranslator) updateMovieIDsBulk(value *BulkUpdateIds, field string) (*models.UpdateMovieIDs, error) { func (t changesetTranslator) updateGroupIDsBulk(value *BulkUpdateIds, field string) (*models.UpdateGroupIDs, error) {
if !t.hasField(field) || value == nil { if !t.hasField(field) || value == nil {
return nil, nil return nil, nil
} }
@@ -424,13 +424,13 @@ func (t changesetTranslator) updateMovieIDsBulk(value *BulkUpdateIds, field stri
return nil, fmt.Errorf("converting ids [%v]: %w", value.Ids, err) return nil, fmt.Errorf("converting ids [%v]: %w", value.Ids, err)
} }
movies := make([]models.MoviesScenes, len(ids)) groups := make([]models.GroupsScenes, len(ids))
for i, id := range ids { for i, id := range ids {
movies[i] = models.MoviesScenes{MovieID: id} groups[i] = models.GroupsScenes{GroupID: id}
} }
return &models.UpdateMovieIDs{ return &models.UpdateGroupIDs{
Movies: movies, Groups: groups,
Mode: value.Mode, Mode: value.Mode,
}, nil }, nil
} }

View File

@@ -9,7 +9,7 @@ const (
performerKey key = iota + 1 performerKey key = iota + 1
sceneKey sceneKey
studioKey studioKey
movieKey groupKey
tagKey tagKey
downloadKey downloadKey
imageKey imageKey

View File

@@ -4,7 +4,7 @@
//go:generate go run github.com/vektah/dataloaden PerformerLoader int *github.com/stashapp/stash/pkg/models.Performer //go:generate go run github.com/vektah/dataloaden PerformerLoader int *github.com/stashapp/stash/pkg/models.Performer
//go:generate go run github.com/vektah/dataloaden StudioLoader int *github.com/stashapp/stash/pkg/models.Studio //go:generate go run github.com/vektah/dataloaden StudioLoader int *github.com/stashapp/stash/pkg/models.Studio
//go:generate go run github.com/vektah/dataloaden TagLoader int *github.com/stashapp/stash/pkg/models.Tag //go:generate go run github.com/vektah/dataloaden TagLoader int *github.com/stashapp/stash/pkg/models.Tag
//go:generate go run github.com/vektah/dataloaden MovieLoader int *github.com/stashapp/stash/pkg/models.Movie //go:generate go run github.com/vektah/dataloaden GroupLoader int *github.com/stashapp/stash/pkg/models.Group
//go:generate go run github.com/vektah/dataloaden FileLoader github.com/stashapp/stash/pkg/models.FileID github.com/stashapp/stash/pkg/models.File //go:generate go run github.com/vektah/dataloaden FileLoader github.com/stashapp/stash/pkg/models.FileID github.com/stashapp/stash/pkg/models.File
//go:generate go run github.com/vektah/dataloaden SceneFileIDsLoader int []github.com/stashapp/stash/pkg/models.FileID //go:generate go run github.com/vektah/dataloaden SceneFileIDsLoader int []github.com/stashapp/stash/pkg/models.FileID
//go:generate go run github.com/vektah/dataloaden ImageFileIDsLoader int []github.com/stashapp/stash/pkg/models.FileID //go:generate go run github.com/vektah/dataloaden ImageFileIDsLoader int []github.com/stashapp/stash/pkg/models.FileID
@@ -52,7 +52,7 @@ type Loaders struct {
PerformerByID *PerformerLoader PerformerByID *PerformerLoader
StudioByID *StudioLoader StudioByID *StudioLoader
TagByID *TagLoader TagByID *TagLoader
MovieByID *MovieLoader GroupByID *GroupLoader
FileByID *FileLoader FileByID *FileLoader
} }
@@ -94,10 +94,10 @@ func (m Middleware) Middleware(next http.Handler) http.Handler {
maxBatch: maxBatch, maxBatch: maxBatch,
fetch: m.fetchTags(ctx), fetch: m.fetchTags(ctx),
}, },
MovieByID: &MovieLoader{ GroupByID: &GroupLoader{
wait: wait, wait: wait,
maxBatch: maxBatch, maxBatch: maxBatch,
fetch: m.fetchMovies(ctx), fetch: m.fetchGroups(ctx),
}, },
FileByID: &FileLoader{ FileByID: &FileLoader{
wait: wait, wait: wait,
@@ -232,11 +232,11 @@ func (m Middleware) fetchTags(ctx context.Context) func(keys []int) ([]*models.T
} }
} }
func (m Middleware) fetchMovies(ctx context.Context) func(keys []int) ([]*models.Movie, []error) { func (m Middleware) fetchGroups(ctx context.Context) func(keys []int) ([]*models.Group, []error) {
return func(keys []int) (ret []*models.Movie, errs []error) { return func(keys []int) (ret []*models.Group, errs []error) {
err := m.Repository.WithDB(ctx, func(ctx context.Context) error { err := m.Repository.WithDB(ctx, func(ctx context.Context) error {
var err error var err error
ret, err = m.Repository.Movie.FindMany(ctx, keys) ret, err = m.Repository.Group.FindMany(ctx, keys)
return err return err
}) })
return ret, toErrorSlice(err) return ret, toErrorSlice(err)

View File

@@ -9,10 +9,10 @@ import (
"github.com/stashapp/stash/pkg/models" "github.com/stashapp/stash/pkg/models"
) )
// MovieLoaderConfig captures the config to create a new MovieLoader // GroupLoaderConfig captures the config to create a new GroupLoader
type MovieLoaderConfig struct { type GroupLoaderConfig struct {
// Fetch is a method that provides the data for the loader // Fetch is a method that provides the data for the loader
Fetch func(keys []int) ([]*models.Movie, []error) Fetch func(keys []int) ([]*models.Group, []error)
// Wait is how long wait before sending a batch // Wait is how long wait before sending a batch
Wait time.Duration Wait time.Duration
@@ -21,19 +21,19 @@ type MovieLoaderConfig struct {
MaxBatch int MaxBatch int
} }
// NewMovieLoader creates a new MovieLoader given a fetch, wait, and maxBatch // NewGroupLoader creates a new GroupLoader given a fetch, wait, and maxBatch
func NewMovieLoader(config MovieLoaderConfig) *MovieLoader { func NewGroupLoader(config GroupLoaderConfig) *GroupLoader {
return &MovieLoader{ return &GroupLoader{
fetch: config.Fetch, fetch: config.Fetch,
wait: config.Wait, wait: config.Wait,
maxBatch: config.MaxBatch, maxBatch: config.MaxBatch,
} }
} }
// MovieLoader batches and caches requests // GroupLoader batches and caches requests
type MovieLoader struct { type GroupLoader struct {
// this method provides the data for the loader // this method provides the data for the loader
fetch func(keys []int) ([]*models.Movie, []error) fetch func(keys []int) ([]*models.Group, []error)
// how long to done before sending a batch // how long to done before sending a batch
wait time.Duration wait time.Duration
@@ -44,51 +44,51 @@ type MovieLoader struct {
// INTERNAL // INTERNAL
// lazily created cache // lazily created cache
cache map[int]*models.Movie cache map[int]*models.Group
// the current batch. keys will continue to be collected until timeout is hit, // the current batch. keys will continue to be collected until timeout is hit,
// then everything will be sent to the fetch method and out to the listeners // then everything will be sent to the fetch method and out to the listeners
batch *movieLoaderBatch batch *groupLoaderBatch
// mutex to prevent races // mutex to prevent races
mu sync.Mutex mu sync.Mutex
} }
type movieLoaderBatch struct { type groupLoaderBatch struct {
keys []int keys []int
data []*models.Movie data []*models.Group
error []error error []error
closing bool closing bool
done chan struct{} done chan struct{}
} }
// Load a Movie by key, batching and caching will be applied automatically // Load a Group by key, batching and caching will be applied automatically
func (l *MovieLoader) Load(key int) (*models.Movie, error) { func (l *GroupLoader) Load(key int) (*models.Group, error) {
return l.LoadThunk(key)() return l.LoadThunk(key)()
} }
// LoadThunk returns a function that when called will block waiting for a Movie. // LoadThunk returns a function that when called will block waiting for a Group.
// This method should be used if you want one goroutine to make requests to many // This method should be used if you want one goroutine to make requests to many
// different data loaders without blocking until the thunk is called. // different data loaders without blocking until the thunk is called.
func (l *MovieLoader) LoadThunk(key int) func() (*models.Movie, error) { func (l *GroupLoader) LoadThunk(key int) func() (*models.Group, error) {
l.mu.Lock() l.mu.Lock()
if it, ok := l.cache[key]; ok { if it, ok := l.cache[key]; ok {
l.mu.Unlock() l.mu.Unlock()
return func() (*models.Movie, error) { return func() (*models.Group, error) {
return it, nil return it, nil
} }
} }
if l.batch == nil { if l.batch == nil {
l.batch = &movieLoaderBatch{done: make(chan struct{})} l.batch = &groupLoaderBatch{done: make(chan struct{})}
} }
batch := l.batch batch := l.batch
pos := batch.keyIndex(l, key) pos := batch.keyIndex(l, key)
l.mu.Unlock() l.mu.Unlock()
return func() (*models.Movie, error) { return func() (*models.Group, error) {
<-batch.done <-batch.done
var data *models.Movie var data *models.Group
if pos < len(batch.data) { if pos < len(batch.data) {
data = batch.data[pos] data = batch.data[pos]
} }
@@ -113,43 +113,43 @@ func (l *MovieLoader) LoadThunk(key int) func() (*models.Movie, error) {
// LoadAll fetches many keys at once. It will be broken into appropriate sized // LoadAll fetches many keys at once. It will be broken into appropriate sized
// sub batches depending on how the loader is configured // sub batches depending on how the loader is configured
func (l *MovieLoader) LoadAll(keys []int) ([]*models.Movie, []error) { func (l *GroupLoader) LoadAll(keys []int) ([]*models.Group, []error) {
results := make([]func() (*models.Movie, error), len(keys)) results := make([]func() (*models.Group, error), len(keys))
for i, key := range keys { for i, key := range keys {
results[i] = l.LoadThunk(key) results[i] = l.LoadThunk(key)
} }
movies := make([]*models.Movie, len(keys)) groups := make([]*models.Group, len(keys))
errors := make([]error, len(keys)) errors := make([]error, len(keys))
for i, thunk := range results { for i, thunk := range results {
movies[i], errors[i] = thunk() groups[i], errors[i] = thunk()
} }
return movies, errors return groups, errors
} }
// LoadAllThunk returns a function that when called will block waiting for a Movies. // LoadAllThunk returns a function that when called will block waiting for a Groups.
// This method should be used if you want one goroutine to make requests to many // This method should be used if you want one goroutine to make requests to many
// different data loaders without blocking until the thunk is called. // different data loaders without blocking until the thunk is called.
func (l *MovieLoader) LoadAllThunk(keys []int) func() ([]*models.Movie, []error) { func (l *GroupLoader) LoadAllThunk(keys []int) func() ([]*models.Group, []error) {
results := make([]func() (*models.Movie, error), len(keys)) results := make([]func() (*models.Group, error), len(keys))
for i, key := range keys { for i, key := range keys {
results[i] = l.LoadThunk(key) results[i] = l.LoadThunk(key)
} }
return func() ([]*models.Movie, []error) { return func() ([]*models.Group, []error) {
movies := make([]*models.Movie, len(keys)) groups := make([]*models.Group, len(keys))
errors := make([]error, len(keys)) errors := make([]error, len(keys))
for i, thunk := range results { for i, thunk := range results {
movies[i], errors[i] = thunk() groups[i], errors[i] = thunk()
} }
return movies, errors return groups, errors
} }
} }
// Prime the cache with the provided key and value. If the key already exists, no change is made // Prime the cache with the provided key and value. If the key already exists, no change is made
// and false is returned. // and false is returned.
// (To forcefully prime the cache, clear the key first with loader.clear(key).prime(key, value).) // (To forcefully prime the cache, clear the key first with loader.clear(key).prime(key, value).)
func (l *MovieLoader) Prime(key int, value *models.Movie) bool { func (l *GroupLoader) Prime(key int, value *models.Group) bool {
l.mu.Lock() l.mu.Lock()
var found bool var found bool
if _, found = l.cache[key]; !found { if _, found = l.cache[key]; !found {
@@ -163,22 +163,22 @@ func (l *MovieLoader) Prime(key int, value *models.Movie) bool {
} }
// Clear the value at key from the cache, if it exists // Clear the value at key from the cache, if it exists
func (l *MovieLoader) Clear(key int) { func (l *GroupLoader) Clear(key int) {
l.mu.Lock() l.mu.Lock()
delete(l.cache, key) delete(l.cache, key)
l.mu.Unlock() l.mu.Unlock()
} }
func (l *MovieLoader) unsafeSet(key int, value *models.Movie) { func (l *GroupLoader) unsafeSet(key int, value *models.Group) {
if l.cache == nil { if l.cache == nil {
l.cache = map[int]*models.Movie{} l.cache = map[int]*models.Group{}
} }
l.cache[key] = value l.cache[key] = value
} }
// keyIndex will return the location of the key in the batch, if its not found // keyIndex will return the location of the key in the batch, if its not found
// it will add the key to the batch // it will add the key to the batch
func (b *movieLoaderBatch) keyIndex(l *MovieLoader, key int) int { func (b *groupLoaderBatch) keyIndex(l *GroupLoader, key int) int {
for i, existingKey := range b.keys { for i, existingKey := range b.keys {
if key == existingKey { if key == existingKey {
return i return i
@@ -202,7 +202,7 @@ func (b *movieLoaderBatch) keyIndex(l *MovieLoader, key int) int {
return pos return pos
} }
func (b *movieLoaderBatch) startTimer(l *MovieLoader) { func (b *groupLoaderBatch) startTimer(l *GroupLoader) {
time.Sleep(l.wait) time.Sleep(l.wait)
l.mu.Lock() l.mu.Lock()
@@ -218,7 +218,7 @@ func (b *movieLoaderBatch) startTimer(l *MovieLoader) {
b.end(l) b.end(l)
} }
func (b *movieLoaderBatch) end(l *MovieLoader) { func (b *groupLoaderBatch) end(l *GroupLoader) {
b.data, b.error = l.fetch(b.keys) b.data, b.error = l.fetch(b.keys)
close(b.done) close(b.done)
} }

View File

@@ -74,10 +74,10 @@ func (r *Resolver) Studio() StudioResolver {
} }
func (r *Resolver) Group() GroupResolver { func (r *Resolver) Group() GroupResolver {
return &groupResolver{&movieResolver{r}} return &groupResolver{r}
} }
func (r *Resolver) Movie() MovieResolver { func (r *Resolver) Movie() MovieResolver {
return &movieResolver{r} return &movieResolver{&groupResolver{r}}
} }
func (r *Resolver) Subscription() SubscriptionResolver { func (r *Resolver) Subscription() SubscriptionResolver {
@@ -117,9 +117,9 @@ type sceneMarkerResolver struct{ *Resolver }
type imageResolver struct{ *Resolver } type imageResolver struct{ *Resolver }
type studioResolver struct{ *Resolver } type studioResolver struct{ *Resolver }
// group is movie under the hood // movie is group under the hood
type movieResolver struct{ *Resolver } type groupResolver struct{ *Resolver }
type groupResolver struct{ *movieResolver } type movieResolver struct{ *groupResolver }
type tagResolver struct{ *Resolver } type tagResolver struct{ *Resolver }
type galleryFileResolver struct{ *Resolver } type galleryFileResolver struct{ *Resolver }
@@ -182,7 +182,7 @@ func (r *queryResolver) Stats(ctx context.Context) (*StatsResultType, error) {
galleryQB := repo.Gallery galleryQB := repo.Gallery
studioQB := repo.Studio studioQB := repo.Studio
performerQB := repo.Performer performerQB := repo.Performer
movieQB := repo.Movie movieQB := repo.Group
tagQB := repo.Tag tagQB := repo.Tag
// embrace the error // embrace the error

View File

@@ -8,7 +8,7 @@ import (
"github.com/stashapp/stash/pkg/models" "github.com/stashapp/stash/pkg/models"
) )
func (r *movieResolver) Date(ctx context.Context, obj *models.Movie) (*string, error) { func (r *groupResolver) Date(ctx context.Context, obj *models.Group) (*string, error) {
if obj.Date != nil { if obj.Date != nil {
result := obj.Date.String() result := obj.Date.String()
return &result, nil return &result, nil
@@ -16,14 +16,14 @@ func (r *movieResolver) Date(ctx context.Context, obj *models.Movie) (*string, e
return nil, nil return nil, nil
} }
func (r *movieResolver) Rating100(ctx context.Context, obj *models.Movie) (*int, error) { func (r *groupResolver) Rating100(ctx context.Context, obj *models.Group) (*int, error) {
return obj.Rating, nil return obj.Rating, nil
} }
func (r *movieResolver) URL(ctx context.Context, obj *models.Movie) (*string, error) { func (r *groupResolver) URL(ctx context.Context, obj *models.Group) (*string, error) {
if !obj.URLs.Loaded() { if !obj.URLs.Loaded() {
if err := r.withReadTxn(ctx, func(ctx context.Context) error { if err := r.withReadTxn(ctx, func(ctx context.Context) error {
return obj.LoadURLs(ctx, r.repository.Movie) return obj.LoadURLs(ctx, r.repository.Group)
}); err != nil { }); err != nil {
return nil, err return nil, err
} }
@@ -37,10 +37,10 @@ func (r *movieResolver) URL(ctx context.Context, obj *models.Movie) (*string, er
return &urls[0], nil return &urls[0], nil
} }
func (r *movieResolver) Urls(ctx context.Context, obj *models.Movie) ([]string, error) { func (r *groupResolver) Urls(ctx context.Context, obj *models.Group) ([]string, error) {
if !obj.URLs.Loaded() { if !obj.URLs.Loaded() {
if err := r.withReadTxn(ctx, func(ctx context.Context) error { if err := r.withReadTxn(ctx, func(ctx context.Context) error {
return obj.LoadURLs(ctx, r.repository.Movie) return obj.LoadURLs(ctx, r.repository.Group)
}); err != nil { }); err != nil {
return nil, err return nil, err
} }
@@ -49,7 +49,7 @@ func (r *movieResolver) Urls(ctx context.Context, obj *models.Movie) ([]string,
return obj.URLs.List(), nil return obj.URLs.List(), nil
} }
func (r *movieResolver) Studio(ctx context.Context, obj *models.Movie) (ret *models.Studio, err error) { func (r *groupResolver) Studio(ctx context.Context, obj *models.Group) (ret *models.Studio, err error) {
if obj.StudioID == nil { if obj.StudioID == nil {
return nil, nil return nil, nil
} }
@@ -57,10 +57,10 @@ func (r *movieResolver) Studio(ctx context.Context, obj *models.Movie) (ret *mod
return loaders.From(ctx).StudioByID.Load(*obj.StudioID) return loaders.From(ctx).StudioByID.Load(*obj.StudioID)
} }
func (r movieResolver) Tags(ctx context.Context, obj *models.Movie) (ret []*models.Tag, err error) { func (r groupResolver) Tags(ctx context.Context, obj *models.Group) (ret []*models.Tag, err error) {
if !obj.TagIDs.Loaded() { if !obj.TagIDs.Loaded() {
if err := r.withReadTxn(ctx, func(ctx context.Context) error { if err := r.withReadTxn(ctx, func(ctx context.Context) error {
return obj.LoadTagIDs(ctx, r.repository.Movie) return obj.LoadTagIDs(ctx, r.repository.Group)
}); err != nil { }); err != nil {
return nil, err return nil, err
} }
@@ -71,26 +71,26 @@ func (r movieResolver) Tags(ctx context.Context, obj *models.Movie) (ret []*mode
return ret, firstError(errs) return ret, firstError(errs)
} }
func (r *movieResolver) FrontImagePath(ctx context.Context, obj *models.Movie) (*string, error) { func (r *groupResolver) FrontImagePath(ctx context.Context, obj *models.Group) (*string, error) {
var hasImage bool var hasImage bool
if err := r.withReadTxn(ctx, func(ctx context.Context) error { if err := r.withReadTxn(ctx, func(ctx context.Context) error {
var err error var err error
hasImage, err = r.repository.Movie.HasFrontImage(ctx, obj.ID) hasImage, err = r.repository.Group.HasFrontImage(ctx, obj.ID)
return err return err
}); err != nil { }); err != nil {
return nil, err return nil, err
} }
baseURL, _ := ctx.Value(BaseURLCtxKey).(string) baseURL, _ := ctx.Value(BaseURLCtxKey).(string)
imagePath := urlbuilders.NewMovieURLBuilder(baseURL, obj).GetMovieFrontImageURL(hasImage) imagePath := urlbuilders.NewGroupURLBuilder(baseURL, obj).GetGroupFrontImageURL(hasImage)
return &imagePath, nil return &imagePath, nil
} }
func (r *movieResolver) BackImagePath(ctx context.Context, obj *models.Movie) (*string, error) { func (r *groupResolver) BackImagePath(ctx context.Context, obj *models.Group) (*string, error) {
var hasImage bool var hasImage bool
if err := r.withReadTxn(ctx, func(ctx context.Context) error { if err := r.withReadTxn(ctx, func(ctx context.Context) error {
var err error var err error
hasImage, err = r.repository.Movie.HasBackImage(ctx, obj.ID) hasImage, err = r.repository.Group.HasBackImage(ctx, obj.ID)
return err return err
}); err != nil { }); err != nil {
return nil, err return nil, err
@@ -102,13 +102,13 @@ func (r *movieResolver) BackImagePath(ctx context.Context, obj *models.Movie) (*
} }
baseURL, _ := ctx.Value(BaseURLCtxKey).(string) baseURL, _ := ctx.Value(BaseURLCtxKey).(string)
imagePath := urlbuilders.NewMovieURLBuilder(baseURL, obj).GetMovieBackImageURL() imagePath := urlbuilders.NewGroupURLBuilder(baseURL, obj).GetGroupBackImageURL()
return &imagePath, nil return &imagePath, nil
} }
func (r *movieResolver) SceneCount(ctx context.Context, obj *models.Movie) (ret int, err error) { func (r *groupResolver) SceneCount(ctx context.Context, obj *models.Group) (ret int, err error) {
if err := r.withReadTxn(ctx, func(ctx context.Context) error { if err := r.withReadTxn(ctx, func(ctx context.Context) error {
ret, err = r.repository.Scene.CountByMovieID(ctx, obj.ID) ret, err = r.repository.Scene.CountByGroupID(ctx, obj.ID)
return err return err
}); err != nil { }); err != nil {
return 0, err return 0, err
@@ -117,10 +117,10 @@ func (r *movieResolver) SceneCount(ctx context.Context, obj *models.Movie) (ret
return ret, nil return ret, nil
} }
func (r *movieResolver) Scenes(ctx context.Context, obj *models.Movie) (ret []*models.Scene, err error) { func (r *groupResolver) Scenes(ctx context.Context, obj *models.Group) (ret []*models.Scene, err error) {
if err := r.withReadTxn(ctx, func(ctx context.Context) error { if err := r.withReadTxn(ctx, func(ctx context.Context) error {
var err error var err error
ret, err = r.repository.Scene.FindByMovieID(ctx, obj.ID) ret, err = r.repository.Scene.FindByGroupID(ctx, obj.ID)
return err return err
}); err != nil { }); err != nil {
return nil, err return nil, err

View File

@@ -181,7 +181,7 @@ func (r *performerResolver) GalleryCount(ctx context.Context, obj *models.Perfor
func (r *performerResolver) GroupCount(ctx context.Context, obj *models.Performer) (ret int, err error) { func (r *performerResolver) GroupCount(ctx context.Context, obj *models.Performer) (ret int, err error) {
if err := r.withReadTxn(ctx, func(ctx context.Context) error { if err := r.withReadTxn(ctx, func(ctx context.Context) error {
ret, err = r.repository.Movie.CountByPerformerID(ctx, obj.ID) ret, err = r.repository.Group.CountByPerformerID(ctx, obj.ID)
return err return err
}); err != nil { }); err != nil {
return 0, err return 0, err
@@ -257,9 +257,9 @@ func (r *performerResolver) DeathDate(ctx context.Context, obj *models.Performer
return nil, nil return nil, nil
} }
func (r *performerResolver) Groups(ctx context.Context, obj *models.Performer) (ret []*models.Movie, err error) { func (r *performerResolver) Groups(ctx context.Context, obj *models.Performer) (ret []*models.Group, err error) {
if err := r.withReadTxn(ctx, func(ctx context.Context) error { if err := r.withReadTxn(ctx, func(ctx context.Context) error {
ret, err = r.repository.Movie.FindByPerformerID(ctx, obj.ID) ret, err = r.repository.Group.FindByPerformerID(ctx, obj.ID)
return err return err
}); err != nil { }); err != nil {
return nil, err return nil, err
@@ -269,6 +269,6 @@ func (r *performerResolver) Groups(ctx context.Context, obj *models.Performer) (
} }
// deprecated // deprecated
func (r *performerResolver) Movies(ctx context.Context, obj *models.Performer) (ret []*models.Movie, err error) { func (r *performerResolver) Movies(ctx context.Context, obj *models.Performer) (ret []*models.Group, err error) {
return r.Groups(ctx, obj) return r.Groups(ctx, obj)
} }

View File

@@ -184,20 +184,20 @@ func (r *sceneResolver) Studio(ctx context.Context, obj *models.Scene) (ret *mod
} }
func (r *sceneResolver) Movies(ctx context.Context, obj *models.Scene) (ret []*SceneMovie, err error) { func (r *sceneResolver) Movies(ctx context.Context, obj *models.Scene) (ret []*SceneMovie, err error) {
if !obj.Movies.Loaded() { if !obj.Groups.Loaded() {
if err := r.withReadTxn(ctx, func(ctx context.Context) error { if err := r.withReadTxn(ctx, func(ctx context.Context) error {
qb := r.repository.Scene qb := r.repository.Scene
return obj.LoadMovies(ctx, qb) return obj.LoadGroups(ctx, qb)
}); err != nil { }); err != nil {
return nil, err return nil, err
} }
} }
loader := loaders.From(ctx).MovieByID loader := loaders.From(ctx).GroupByID
for _, sm := range obj.Movies.List() { for _, sm := range obj.Groups.List() {
movie, err := loader.Load(sm.MovieID) movie, err := loader.Load(sm.GroupID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -215,27 +215,27 @@ func (r *sceneResolver) Movies(ctx context.Context, obj *models.Scene) (ret []*S
} }
func (r *sceneResolver) Groups(ctx context.Context, obj *models.Scene) (ret []*SceneGroup, err error) { func (r *sceneResolver) Groups(ctx context.Context, obj *models.Scene) (ret []*SceneGroup, err error) {
if !obj.Movies.Loaded() { if !obj.Groups.Loaded() {
if err := r.withReadTxn(ctx, func(ctx context.Context) error { if err := r.withReadTxn(ctx, func(ctx context.Context) error {
qb := r.repository.Scene qb := r.repository.Scene
return obj.LoadMovies(ctx, qb) return obj.LoadGroups(ctx, qb)
}); err != nil { }); err != nil {
return nil, err return nil, err
} }
} }
loader := loaders.From(ctx).MovieByID loader := loaders.From(ctx).GroupByID
for _, sm := range obj.Movies.List() { for _, sm := range obj.Groups.List() {
movie, err := loader.Load(sm.MovieID) group, err := loader.Load(sm.GroupID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
sceneIdx := sm.SceneIndex sceneIdx := sm.SceneIndex
sceneGroup := &SceneGroup{ sceneGroup := &SceneGroup{
Group: movie, Group: group,
SceneIndex: sceneIdx, SceneIndex: sceneIdx,
} }

View File

@@ -100,7 +100,7 @@ func (r *studioResolver) PerformerCount(ctx context.Context, obj *models.Studio,
func (r *studioResolver) GroupCount(ctx context.Context, obj *models.Studio, depth *int) (ret int, err error) { func (r *studioResolver) GroupCount(ctx context.Context, obj *models.Studio, depth *int) (ret int, err error) {
if err := r.withReadTxn(ctx, func(ctx context.Context) error { if err := r.withReadTxn(ctx, func(ctx context.Context) error {
ret, err = movie.CountByStudioID(ctx, r.repository.Movie, obj.ID, depth) ret, err = movie.CountByStudioID(ctx, r.repository.Group, obj.ID, depth)
return err return err
}); err != nil { }); err != nil {
return 0, err return 0, err
@@ -149,9 +149,9 @@ func (r *studioResolver) Rating100(ctx context.Context, obj *models.Studio) (*in
return obj.Rating, nil return obj.Rating, nil
} }
func (r *studioResolver) Groups(ctx context.Context, obj *models.Studio) (ret []*models.Movie, err error) { func (r *studioResolver) Groups(ctx context.Context, obj *models.Studio) (ret []*models.Group, err error) {
if err := r.withReadTxn(ctx, func(ctx context.Context) error { if err := r.withReadTxn(ctx, func(ctx context.Context) error {
ret, err = r.repository.Movie.FindByStudioID(ctx, obj.ID) ret, err = r.repository.Group.FindByStudioID(ctx, obj.ID)
return err return err
}); err != nil { }); err != nil {
return nil, err return nil, err
@@ -161,6 +161,6 @@ func (r *studioResolver) Groups(ctx context.Context, obj *models.Studio) (ret []
} }
// deprecated // deprecated
func (r *studioResolver) Movies(ctx context.Context, obj *models.Studio) (ret []*models.Movie, err error) { func (r *studioResolver) Movies(ctx context.Context, obj *models.Studio) (ret []*models.Group, err error) {
return r.Groups(ctx, obj) return r.Groups(ctx, obj)
} }

View File

@@ -122,7 +122,7 @@ func (r *tagResolver) StudioCount(ctx context.Context, obj *models.Tag, depth *i
func (r *tagResolver) GroupCount(ctx context.Context, obj *models.Tag, depth *int) (ret int, err error) { func (r *tagResolver) GroupCount(ctx context.Context, obj *models.Tag, depth *int) (ret int, err error) {
if err := r.withReadTxn(ctx, func(ctx context.Context) error { if err := r.withReadTxn(ctx, func(ctx context.Context) error {
ret, err = movie.CountByTagID(ctx, r.repository.Movie, obj.ID, depth) ret, err = movie.CountByTagID(ctx, r.repository.Group, obj.ID, depth)
return err return err
}); err != nil { }); err != nil {
return 0, err return 0, err

View File

@@ -12,46 +12,46 @@ import (
"github.com/stashapp/stash/pkg/utils" "github.com/stashapp/stash/pkg/utils"
) )
func movieFromGroupCreateInput(ctx context.Context, input GroupCreateInput) (*models.Movie, error) { func groupFromGroupCreateInput(ctx context.Context, input GroupCreateInput) (*models.Group, error) {
translator := changesetTranslator{ translator := changesetTranslator{
inputMap: getUpdateInputMap(ctx), inputMap: getUpdateInputMap(ctx),
} }
// Populate a new movie from the input // Populate a new group from the input
newMovie := models.NewMovie() newGroup := models.NewGroup()
newMovie.Name = input.Name newGroup.Name = input.Name
newMovie.Aliases = translator.string(input.Aliases) newGroup.Aliases = translator.string(input.Aliases)
newMovie.Duration = input.Duration newGroup.Duration = input.Duration
newMovie.Rating = input.Rating100 newGroup.Rating = input.Rating100
newMovie.Director = translator.string(input.Director) newGroup.Director = translator.string(input.Director)
newMovie.Synopsis = translator.string(input.Synopsis) newGroup.Synopsis = translator.string(input.Synopsis)
var err error var err error
newMovie.Date, err = translator.datePtr(input.Date) newGroup.Date, err = translator.datePtr(input.Date)
if err != nil { if err != nil {
return nil, fmt.Errorf("converting date: %w", err) return nil, fmt.Errorf("converting date: %w", err)
} }
newMovie.StudioID, err = translator.intPtrFromString(input.StudioID) newGroup.StudioID, err = translator.intPtrFromString(input.StudioID)
if err != nil { if err != nil {
return nil, fmt.Errorf("converting studio id: %w", err) return nil, fmt.Errorf("converting studio id: %w", err)
} }
newMovie.TagIDs, err = translator.relatedIds(input.TagIds) newGroup.TagIDs, err = translator.relatedIds(input.TagIds)
if err != nil { if err != nil {
return nil, fmt.Errorf("converting tag ids: %w", err) return nil, fmt.Errorf("converting tag ids: %w", err)
} }
if input.Urls != nil { if input.Urls != nil {
newMovie.URLs = models.NewRelatedStrings(input.Urls) newGroup.URLs = models.NewRelatedStrings(input.Urls)
} }
return &newMovie, nil return &newGroup, nil
} }
func (r *mutationResolver) GroupCreate(ctx context.Context, input GroupCreateInput) (*models.Movie, error) { func (r *mutationResolver) GroupCreate(ctx context.Context, input GroupCreateInput) (*models.Group, error) {
newMovie, err := movieFromGroupCreateInput(ctx, input) newGroup, err := groupFromGroupCreateInput(ctx, input)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -77,27 +77,27 @@ func (r *mutationResolver) GroupCreate(ctx context.Context, input GroupCreateInp
// HACK: if back image is being set, set the front image to the default. // HACK: if back image is being set, set the front image to the default.
// This is because we can't have a null front image with a non-null back image. // This is because we can't have a null front image with a non-null back image.
if len(frontimageData) == 0 && len(backimageData) != 0 { if len(frontimageData) == 0 && len(backimageData) != 0 {
frontimageData = static.ReadAll(static.DefaultMovieImage) frontimageData = static.ReadAll(static.DefaultGroupImage)
} }
// Start the transaction and save the movie // Start the transaction and save the group
if err := r.withTxn(ctx, func(ctx context.Context) error { if err := r.withTxn(ctx, func(ctx context.Context) error {
qb := r.repository.Movie qb := r.repository.Group
err = qb.Create(ctx, newMovie) err = qb.Create(ctx, newGroup)
if err != nil { if err != nil {
return err return err
} }
// update image table // update image table
if len(frontimageData) > 0 { if len(frontimageData) > 0 {
if err := qb.UpdateFrontImage(ctx, newMovie.ID, frontimageData); err != nil { if err := qb.UpdateFrontImage(ctx, newGroup.ID, frontimageData); err != nil {
return err return err
} }
} }
if len(backimageData) > 0 { if len(backimageData) > 0 {
if err := qb.UpdateBackImage(ctx, newMovie.ID, backimageData); err != nil { if err := qb.UpdateBackImage(ctx, newGroup.ID, backimageData); err != nil {
return err return err
} }
} }
@@ -108,46 +108,46 @@ func (r *mutationResolver) GroupCreate(ctx context.Context, input GroupCreateInp
} }
// for backwards compatibility - run both movie and group hooks // for backwards compatibility - run both movie and group hooks
r.hookExecutor.ExecutePostHooks(ctx, newMovie.ID, hook.GroupCreatePost, input, nil) r.hookExecutor.ExecutePostHooks(ctx, newGroup.ID, hook.GroupCreatePost, input, nil)
r.hookExecutor.ExecutePostHooks(ctx, newMovie.ID, hook.MovieCreatePost, input, nil) r.hookExecutor.ExecutePostHooks(ctx, newGroup.ID, hook.MovieCreatePost, input, nil)
return r.getMovie(ctx, newMovie.ID) return r.getGroup(ctx, newGroup.ID)
} }
func moviePartialFromGroupUpdateInput(translator changesetTranslator, input GroupUpdateInput) (ret models.MoviePartial, err error) { func groupPartialFromGroupUpdateInput(translator changesetTranslator, input GroupUpdateInput) (ret models.GroupPartial, err error) {
// Populate movie from the input // Populate group from the input
updatedMovie := models.NewMoviePartial() updatedGroup := models.NewGroupPartial()
updatedMovie.Name = translator.optionalString(input.Name, "name") updatedGroup.Name = translator.optionalString(input.Name, "name")
updatedMovie.Aliases = translator.optionalString(input.Aliases, "aliases") updatedGroup.Aliases = translator.optionalString(input.Aliases, "aliases")
updatedMovie.Duration = translator.optionalInt(input.Duration, "duration") updatedGroup.Duration = translator.optionalInt(input.Duration, "duration")
updatedMovie.Rating = translator.optionalInt(input.Rating100, "rating100") updatedGroup.Rating = translator.optionalInt(input.Rating100, "rating100")
updatedMovie.Director = translator.optionalString(input.Director, "director") updatedGroup.Director = translator.optionalString(input.Director, "director")
updatedMovie.Synopsis = translator.optionalString(input.Synopsis, "synopsis") updatedGroup.Synopsis = translator.optionalString(input.Synopsis, "synopsis")
updatedMovie.Date, err = translator.optionalDate(input.Date, "date") updatedGroup.Date, err = translator.optionalDate(input.Date, "date")
if err != nil { if err != nil {
err = fmt.Errorf("converting date: %w", err) err = fmt.Errorf("converting date: %w", err)
return return
} }
updatedMovie.StudioID, err = translator.optionalIntFromString(input.StudioID, "studio_id") updatedGroup.StudioID, err = translator.optionalIntFromString(input.StudioID, "studio_id")
if err != nil { if err != nil {
err = fmt.Errorf("converting studio id: %w", err) err = fmt.Errorf("converting studio id: %w", err)
return return
} }
updatedMovie.TagIDs, err = translator.updateIds(input.TagIds, "tag_ids") updatedGroup.TagIDs, err = translator.updateIds(input.TagIds, "tag_ids")
if err != nil { if err != nil {
err = fmt.Errorf("converting tag ids: %w", err) err = fmt.Errorf("converting tag ids: %w", err)
return return
} }
updatedMovie.URLs = translator.updateStrings(input.Urls, "urls") updatedGroup.URLs = translator.updateStrings(input.Urls, "urls")
return updatedMovie, nil return updatedGroup, nil
} }
func (r *mutationResolver) GroupUpdate(ctx context.Context, input GroupUpdateInput) (*models.Movie, error) { func (r *mutationResolver) GroupUpdate(ctx context.Context, input GroupUpdateInput) (*models.Group, error) {
movieID, err := strconv.Atoi(input.ID) groupID, err := strconv.Atoi(input.ID)
if err != nil { if err != nil {
return nil, fmt.Errorf("converting id: %w", err) return nil, fmt.Errorf("converting id: %w", err)
} }
@@ -156,7 +156,7 @@ func (r *mutationResolver) GroupUpdate(ctx context.Context, input GroupUpdateInp
inputMap: getUpdateInputMap(ctx), inputMap: getUpdateInputMap(ctx),
} }
updatedMovie, err := moviePartialFromGroupUpdateInput(translator, input) updatedGroup, err := groupPartialFromGroupUpdateInput(translator, input)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -179,24 +179,24 @@ func (r *mutationResolver) GroupUpdate(ctx context.Context, input GroupUpdateInp
} }
} }
// Start the transaction and save the movie // Start the transaction and save the group
var movie *models.Movie var group *models.Group
if err := r.withTxn(ctx, func(ctx context.Context) error { if err := r.withTxn(ctx, func(ctx context.Context) error {
qb := r.repository.Movie qb := r.repository.Group
movie, err = qb.UpdatePartial(ctx, movieID, updatedMovie) group, err = qb.UpdatePartial(ctx, groupID, updatedGroup)
if err != nil { if err != nil {
return err return err
} }
// update image table // update image table
if frontImageIncluded { if frontImageIncluded {
if err := qb.UpdateFrontImage(ctx, movie.ID, frontimageData); err != nil { if err := qb.UpdateFrontImage(ctx, group.ID, frontimageData); err != nil {
return err return err
} }
} }
if backImageIncluded { if backImageIncluded {
if err := qb.UpdateBackImage(ctx, movie.ID, backimageData); err != nil { if err := qb.UpdateBackImage(ctx, group.ID, backimageData); err != nil {
return err return err
} }
} }
@@ -207,36 +207,36 @@ func (r *mutationResolver) GroupUpdate(ctx context.Context, input GroupUpdateInp
} }
// for backwards compatibility - run both movie and group hooks // for backwards compatibility - run both movie and group hooks
r.hookExecutor.ExecutePostHooks(ctx, movie.ID, hook.GroupUpdatePost, input, translator.getFields()) r.hookExecutor.ExecutePostHooks(ctx, group.ID, hook.GroupUpdatePost, input, translator.getFields())
r.hookExecutor.ExecutePostHooks(ctx, movie.ID, hook.MovieUpdatePost, input, translator.getFields()) r.hookExecutor.ExecutePostHooks(ctx, group.ID, hook.MovieUpdatePost, input, translator.getFields())
return r.getMovie(ctx, movie.ID) return r.getGroup(ctx, group.ID)
} }
func moviePartialFromBulkGroupUpdateInput(translator changesetTranslator, input BulkGroupUpdateInput) (ret models.MoviePartial, err error) { func groupPartialFromBulkGroupUpdateInput(translator changesetTranslator, input BulkGroupUpdateInput) (ret models.GroupPartial, err error) {
updatedMovie := models.NewMoviePartial() updatedGroup := models.NewGroupPartial()
updatedMovie.Rating = translator.optionalInt(input.Rating100, "rating100") updatedGroup.Rating = translator.optionalInt(input.Rating100, "rating100")
updatedMovie.Director = translator.optionalString(input.Director, "director") updatedGroup.Director = translator.optionalString(input.Director, "director")
updatedMovie.StudioID, err = translator.optionalIntFromString(input.StudioID, "studio_id") updatedGroup.StudioID, err = translator.optionalIntFromString(input.StudioID, "studio_id")
if err != nil { if err != nil {
err = fmt.Errorf("converting studio id: %w", err) err = fmt.Errorf("converting studio id: %w", err)
return return
} }
updatedMovie.TagIDs, err = translator.updateIdsBulk(input.TagIds, "tag_ids") updatedGroup.TagIDs, err = translator.updateIdsBulk(input.TagIds, "tag_ids")
if err != nil { if err != nil {
err = fmt.Errorf("converting tag ids: %w", err) err = fmt.Errorf("converting tag ids: %w", err)
return return
} }
updatedMovie.URLs = translator.optionalURLsBulk(input.Urls, nil) updatedGroup.URLs = translator.optionalURLsBulk(input.Urls, nil)
return updatedMovie, nil return updatedGroup, nil
} }
func (r *mutationResolver) BulkGroupUpdate(ctx context.Context, input BulkGroupUpdateInput) ([]*models.Movie, error) { func (r *mutationResolver) BulkGroupUpdate(ctx context.Context, input BulkGroupUpdateInput) ([]*models.Group, error) {
movieIDs, err := stringslice.StringSliceToIntSlice(input.Ids) groupIDs, err := stringslice.StringSliceToIntSlice(input.Ids)
if err != nil { if err != nil {
return nil, fmt.Errorf("converting ids: %w", err) return nil, fmt.Errorf("converting ids: %w", err)
} }
@@ -245,24 +245,24 @@ func (r *mutationResolver) BulkGroupUpdate(ctx context.Context, input BulkGroupU
inputMap: getUpdateInputMap(ctx), inputMap: getUpdateInputMap(ctx),
} }
// Populate movie from the input // Populate group from the input
updatedMovie, err := moviePartialFromBulkGroupUpdateInput(translator, input) updatedGroup, err := groupPartialFromBulkGroupUpdateInput(translator, input)
if err != nil { if err != nil {
return nil, err return nil, err
} }
ret := []*models.Movie{} ret := []*models.Group{}
if err := r.withTxn(ctx, func(ctx context.Context) error { if err := r.withTxn(ctx, func(ctx context.Context) error {
qb := r.repository.Movie qb := r.repository.Group
for _, movieID := range movieIDs { for _, groupID := range groupIDs {
movie, err := qb.UpdatePartial(ctx, movieID, updatedMovie) group, err := qb.UpdatePartial(ctx, groupID, updatedGroup)
if err != nil { if err != nil {
return err return err
} }
ret = append(ret, movie) ret = append(ret, group)
} }
return nil return nil
@@ -270,18 +270,18 @@ func (r *mutationResolver) BulkGroupUpdate(ctx context.Context, input BulkGroupU
return nil, err return nil, err
} }
var newRet []*models.Movie var newRet []*models.Group
for _, movie := range ret { for _, group := range ret {
// for backwards compatibility - run both movie and group hooks // for backwards compatibility - run both movie and group hooks
r.hookExecutor.ExecutePostHooks(ctx, movie.ID, hook.GroupUpdatePost, input, translator.getFields()) r.hookExecutor.ExecutePostHooks(ctx, group.ID, hook.GroupUpdatePost, input, translator.getFields())
r.hookExecutor.ExecutePostHooks(ctx, movie.ID, hook.MovieUpdatePost, input, translator.getFields()) r.hookExecutor.ExecutePostHooks(ctx, group.ID, hook.MovieUpdatePost, input, translator.getFields())
movie, err = r.getMovie(ctx, movie.ID) group, err = r.getGroup(ctx, group.ID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
newRet = append(newRet, movie) newRet = append(newRet, group)
} }
return newRet, nil return newRet, nil
@@ -294,7 +294,7 @@ func (r *mutationResolver) GroupDestroy(ctx context.Context, input GroupDestroyI
} }
if err := r.withTxn(ctx, func(ctx context.Context) error { if err := r.withTxn(ctx, func(ctx context.Context) error {
return r.repository.Movie.Destroy(ctx, id) return r.repository.Group.Destroy(ctx, id)
}); err != nil { }); err != nil {
return false, err return false, err
} }
@@ -313,7 +313,7 @@ func (r *mutationResolver) GroupsDestroy(ctx context.Context, groupIDs []string)
} }
if err := r.withTxn(ctx, func(ctx context.Context) error { if err := r.withTxn(ctx, func(ctx context.Context) error {
qb := r.repository.Movie qb := r.repository.Group
for _, id := range ids { for _, id := range ids {
if err := qb.Destroy(ctx, id); err != nil { if err := qb.Destroy(ctx, id); err != nil {
return err return err

View File

@@ -12,10 +12,10 @@ import (
"github.com/stashapp/stash/pkg/utils" "github.com/stashapp/stash/pkg/utils"
) )
// used to refetch movie after hooks run // used to refetch group after hooks run
func (r *mutationResolver) getMovie(ctx context.Context, id int) (ret *models.Movie, err error) { func (r *mutationResolver) getGroup(ctx context.Context, id int) (ret *models.Group, err error) {
if err := r.withTxn(ctx, func(ctx context.Context) error { if err := r.withTxn(ctx, func(ctx context.Context) error {
ret, err = r.repository.Movie.Find(ctx, id) ret, err = r.repository.Group.Find(ctx, id)
return err return err
}); err != nil { }); err != nil {
return nil, err return nil, err
@@ -24,41 +24,41 @@ func (r *mutationResolver) getMovie(ctx context.Context, id int) (ret *models.Mo
return ret, nil return ret, nil
} }
func (r *mutationResolver) MovieCreate(ctx context.Context, input MovieCreateInput) (*models.Movie, error) { func (r *mutationResolver) MovieCreate(ctx context.Context, input MovieCreateInput) (*models.Group, error) {
translator := changesetTranslator{ translator := changesetTranslator{
inputMap: getUpdateInputMap(ctx), inputMap: getUpdateInputMap(ctx),
} }
// Populate a new movie from the input // Populate a new group from the input
newMovie := models.NewMovie() newGroup := models.NewGroup()
newMovie.Name = input.Name newGroup.Name = input.Name
newMovie.Aliases = translator.string(input.Aliases) newGroup.Aliases = translator.string(input.Aliases)
newMovie.Duration = input.Duration newGroup.Duration = input.Duration
newMovie.Rating = input.Rating100 newGroup.Rating = input.Rating100
newMovie.Director = translator.string(input.Director) newGroup.Director = translator.string(input.Director)
newMovie.Synopsis = translator.string(input.Synopsis) newGroup.Synopsis = translator.string(input.Synopsis)
var err error var err error
newMovie.Date, err = translator.datePtr(input.Date) newGroup.Date, err = translator.datePtr(input.Date)
if err != nil { if err != nil {
return nil, fmt.Errorf("converting date: %w", err) return nil, fmt.Errorf("converting date: %w", err)
} }
newMovie.StudioID, err = translator.intPtrFromString(input.StudioID) newGroup.StudioID, err = translator.intPtrFromString(input.StudioID)
if err != nil { if err != nil {
return nil, fmt.Errorf("converting studio id: %w", err) return nil, fmt.Errorf("converting studio id: %w", err)
} }
newMovie.TagIDs, err = translator.relatedIds(input.TagIds) newGroup.TagIDs, err = translator.relatedIds(input.TagIds)
if err != nil { if err != nil {
return nil, fmt.Errorf("converting tag ids: %w", err) return nil, fmt.Errorf("converting tag ids: %w", err)
} }
if input.Urls != nil { if input.Urls != nil {
newMovie.URLs = models.NewRelatedStrings(input.Urls) newGroup.URLs = models.NewRelatedStrings(input.Urls)
} else if input.URL != nil { } else if input.URL != nil {
newMovie.URLs = models.NewRelatedStrings([]string{*input.URL}) newGroup.URLs = models.NewRelatedStrings([]string{*input.URL})
} }
// Process the base 64 encoded image string // Process the base 64 encoded image string
@@ -82,27 +82,27 @@ func (r *mutationResolver) MovieCreate(ctx context.Context, input MovieCreateInp
// HACK: if back image is being set, set the front image to the default. // HACK: if back image is being set, set the front image to the default.
// This is because we can't have a null front image with a non-null back image. // This is because we can't have a null front image with a non-null back image.
if len(frontimageData) == 0 && len(backimageData) != 0 { if len(frontimageData) == 0 && len(backimageData) != 0 {
frontimageData = static.ReadAll(static.DefaultMovieImage) frontimageData = static.ReadAll(static.DefaultGroupImage)
} }
// Start the transaction and save the movie // Start the transaction and save the group
if err := r.withTxn(ctx, func(ctx context.Context) error { if err := r.withTxn(ctx, func(ctx context.Context) error {
qb := r.repository.Movie qb := r.repository.Group
err = qb.Create(ctx, &newMovie) err = qb.Create(ctx, &newGroup)
if err != nil { if err != nil {
return err return err
} }
// update image table // update image table
if len(frontimageData) > 0 { if len(frontimageData) > 0 {
if err := qb.UpdateFrontImage(ctx, newMovie.ID, frontimageData); err != nil { if err := qb.UpdateFrontImage(ctx, newGroup.ID, frontimageData); err != nil {
return err return err
} }
} }
if len(backimageData) > 0 { if len(backimageData) > 0 {
if err := qb.UpdateBackImage(ctx, newMovie.ID, backimageData); err != nil { if err := qb.UpdateBackImage(ctx, newGroup.ID, backimageData); err != nil {
return err return err
} }
} }
@@ -113,13 +113,13 @@ func (r *mutationResolver) MovieCreate(ctx context.Context, input MovieCreateInp
} }
// for backwards compatibility - run both movie and group hooks // for backwards compatibility - run both movie and group hooks
r.hookExecutor.ExecutePostHooks(ctx, newMovie.ID, hook.GroupCreatePost, input, nil) r.hookExecutor.ExecutePostHooks(ctx, newGroup.ID, hook.GroupCreatePost, input, nil)
r.hookExecutor.ExecutePostHooks(ctx, newMovie.ID, hook.MovieCreatePost, input, nil) r.hookExecutor.ExecutePostHooks(ctx, newGroup.ID, hook.MovieCreatePost, input, nil)
return r.getMovie(ctx, newMovie.ID) return r.getGroup(ctx, newGroup.ID)
} }
func (r *mutationResolver) MovieUpdate(ctx context.Context, input MovieUpdateInput) (*models.Movie, error) { func (r *mutationResolver) MovieUpdate(ctx context.Context, input MovieUpdateInput) (*models.Group, error) {
movieID, err := strconv.Atoi(input.ID) groupID, err := strconv.Atoi(input.ID)
if err != nil { if err != nil {
return nil, fmt.Errorf("converting id: %w", err) return nil, fmt.Errorf("converting id: %w", err)
} }
@@ -128,31 +128,31 @@ func (r *mutationResolver) MovieUpdate(ctx context.Context, input MovieUpdateInp
inputMap: getUpdateInputMap(ctx), inputMap: getUpdateInputMap(ctx),
} }
// Populate movie from the input // Populate group from the input
updatedMovie := models.NewMoviePartial() updatedGroup := models.NewGroupPartial()
updatedMovie.Name = translator.optionalString(input.Name, "name") updatedGroup.Name = translator.optionalString(input.Name, "name")
updatedMovie.Aliases = translator.optionalString(input.Aliases, "aliases") updatedGroup.Aliases = translator.optionalString(input.Aliases, "aliases")
updatedMovie.Duration = translator.optionalInt(input.Duration, "duration") updatedGroup.Duration = translator.optionalInt(input.Duration, "duration")
updatedMovie.Rating = translator.optionalInt(input.Rating100, "rating100") updatedGroup.Rating = translator.optionalInt(input.Rating100, "rating100")
updatedMovie.Director = translator.optionalString(input.Director, "director") updatedGroup.Director = translator.optionalString(input.Director, "director")
updatedMovie.Synopsis = translator.optionalString(input.Synopsis, "synopsis") updatedGroup.Synopsis = translator.optionalString(input.Synopsis, "synopsis")
updatedMovie.Date, err = translator.optionalDate(input.Date, "date") updatedGroup.Date, err = translator.optionalDate(input.Date, "date")
if err != nil { if err != nil {
return nil, fmt.Errorf("converting date: %w", err) return nil, fmt.Errorf("converting date: %w", err)
} }
updatedMovie.StudioID, err = translator.optionalIntFromString(input.StudioID, "studio_id") updatedGroup.StudioID, err = translator.optionalIntFromString(input.StudioID, "studio_id")
if err != nil { if err != nil {
return nil, fmt.Errorf("converting studio id: %w", err) return nil, fmt.Errorf("converting studio id: %w", err)
} }
updatedMovie.TagIDs, err = translator.updateIds(input.TagIds, "tag_ids") updatedGroup.TagIDs, err = translator.updateIds(input.TagIds, "tag_ids")
if err != nil { if err != nil {
return nil, fmt.Errorf("converting tag ids: %w", err) return nil, fmt.Errorf("converting tag ids: %w", err)
} }
updatedMovie.URLs = translator.optionalURLs(input.Urls, input.URL) updatedGroup.URLs = translator.optionalURLs(input.Urls, input.URL)
var frontimageData []byte var frontimageData []byte
frontImageIncluded := translator.hasField("front_image") frontImageIncluded := translator.hasField("front_image")
@@ -172,24 +172,24 @@ func (r *mutationResolver) MovieUpdate(ctx context.Context, input MovieUpdateInp
} }
} }
// Start the transaction and save the movie // Start the transaction and save the group
var movie *models.Movie var group *models.Group
if err := r.withTxn(ctx, func(ctx context.Context) error { if err := r.withTxn(ctx, func(ctx context.Context) error {
qb := r.repository.Movie qb := r.repository.Group
movie, err = qb.UpdatePartial(ctx, movieID, updatedMovie) group, err = qb.UpdatePartial(ctx, groupID, updatedGroup)
if err != nil { if err != nil {
return err return err
} }
// update image table // update image table
if frontImageIncluded { if frontImageIncluded {
if err := qb.UpdateFrontImage(ctx, movie.ID, frontimageData); err != nil { if err := qb.UpdateFrontImage(ctx, group.ID, frontimageData); err != nil {
return err return err
} }
} }
if backImageIncluded { if backImageIncluded {
if err := qb.UpdateBackImage(ctx, movie.ID, backimageData); err != nil { if err := qb.UpdateBackImage(ctx, group.ID, backimageData); err != nil {
return err return err
} }
} }
@@ -200,13 +200,13 @@ func (r *mutationResolver) MovieUpdate(ctx context.Context, input MovieUpdateInp
} }
// for backwards compatibility - run both movie and group hooks // for backwards compatibility - run both movie and group hooks
r.hookExecutor.ExecutePostHooks(ctx, movie.ID, hook.GroupUpdatePost, input, translator.getFields()) r.hookExecutor.ExecutePostHooks(ctx, group.ID, hook.GroupUpdatePost, input, translator.getFields())
r.hookExecutor.ExecutePostHooks(ctx, movie.ID, hook.MovieUpdatePost, input, translator.getFields()) r.hookExecutor.ExecutePostHooks(ctx, group.ID, hook.MovieUpdatePost, input, translator.getFields())
return r.getMovie(ctx, movie.ID) return r.getGroup(ctx, group.ID)
} }
func (r *mutationResolver) BulkMovieUpdate(ctx context.Context, input BulkMovieUpdateInput) ([]*models.Movie, error) { func (r *mutationResolver) BulkMovieUpdate(ctx context.Context, input BulkMovieUpdateInput) ([]*models.Group, error) {
movieIDs, err := stringslice.StringSliceToIntSlice(input.Ids) groupIDs, err := stringslice.StringSliceToIntSlice(input.Ids)
if err != nil { if err != nil {
return nil, fmt.Errorf("converting ids: %w", err) return nil, fmt.Errorf("converting ids: %w", err)
} }
@@ -215,36 +215,36 @@ func (r *mutationResolver) BulkMovieUpdate(ctx context.Context, input BulkMovieU
inputMap: getUpdateInputMap(ctx), inputMap: getUpdateInputMap(ctx),
} }
// Populate movie from the input // Populate group from the input
updatedMovie := models.NewMoviePartial() updatedGroup := models.NewGroupPartial()
updatedMovie.Rating = translator.optionalInt(input.Rating100, "rating100") updatedGroup.Rating = translator.optionalInt(input.Rating100, "rating100")
updatedMovie.Director = translator.optionalString(input.Director, "director") updatedGroup.Director = translator.optionalString(input.Director, "director")
updatedMovie.StudioID, err = translator.optionalIntFromString(input.StudioID, "studio_id") updatedGroup.StudioID, err = translator.optionalIntFromString(input.StudioID, "studio_id")
if err != nil { if err != nil {
return nil, fmt.Errorf("converting studio id: %w", err) return nil, fmt.Errorf("converting studio id: %w", err)
} }
updatedMovie.TagIDs, err = translator.updateIdsBulk(input.TagIds, "tag_ids") updatedGroup.TagIDs, err = translator.updateIdsBulk(input.TagIds, "tag_ids")
if err != nil { if err != nil {
return nil, fmt.Errorf("converting tag ids: %w", err) return nil, fmt.Errorf("converting tag ids: %w", err)
} }
updatedMovie.URLs = translator.optionalURLsBulk(input.Urls, nil) updatedGroup.URLs = translator.optionalURLsBulk(input.Urls, nil)
ret := []*models.Movie{} ret := []*models.Group{}
if err := r.withTxn(ctx, func(ctx context.Context) error { if err := r.withTxn(ctx, func(ctx context.Context) error {
qb := r.repository.Movie qb := r.repository.Group
for _, movieID := range movieIDs { for _, groupID := range groupIDs {
movie, err := qb.UpdatePartial(ctx, movieID, updatedMovie) group, err := qb.UpdatePartial(ctx, groupID, updatedGroup)
if err != nil { if err != nil {
return err return err
} }
ret = append(ret, movie) ret = append(ret, group)
} }
return nil return nil
@@ -252,18 +252,18 @@ func (r *mutationResolver) BulkMovieUpdate(ctx context.Context, input BulkMovieU
return nil, err return nil, err
} }
var newRet []*models.Movie var newRet []*models.Group
for _, movie := range ret { for _, group := range ret {
// for backwards compatibility - run both movie and group hooks // for backwards compatibility - run both movie and group hooks
r.hookExecutor.ExecutePostHooks(ctx, movie.ID, hook.GroupUpdatePost, input, translator.getFields()) r.hookExecutor.ExecutePostHooks(ctx, group.ID, hook.GroupUpdatePost, input, translator.getFields())
r.hookExecutor.ExecutePostHooks(ctx, movie.ID, hook.MovieUpdatePost, input, translator.getFields()) r.hookExecutor.ExecutePostHooks(ctx, group.ID, hook.MovieUpdatePost, input, translator.getFields())
movie, err = r.getMovie(ctx, movie.ID) group, err = r.getGroup(ctx, group.ID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
newRet = append(newRet, movie) newRet = append(newRet, group)
} }
return newRet, nil return newRet, nil
@@ -276,7 +276,7 @@ func (r *mutationResolver) MovieDestroy(ctx context.Context, input MovieDestroyI
} }
if err := r.withTxn(ctx, func(ctx context.Context) error { if err := r.withTxn(ctx, func(ctx context.Context) error {
return r.repository.Movie.Destroy(ctx, id) return r.repository.Group.Destroy(ctx, id)
}); err != nil { }); err != nil {
return false, err return false, err
} }
@@ -288,14 +288,14 @@ func (r *mutationResolver) MovieDestroy(ctx context.Context, input MovieDestroyI
return true, nil return true, nil
} }
func (r *mutationResolver) MoviesDestroy(ctx context.Context, movieIDs []string) (bool, error) { func (r *mutationResolver) MoviesDestroy(ctx context.Context, groupIDs []string) (bool, error) {
ids, err := stringslice.StringSliceToIntSlice(movieIDs) ids, err := stringslice.StringSliceToIntSlice(groupIDs)
if err != nil { if err != nil {
return false, fmt.Errorf("converting ids: %w", err) return false, fmt.Errorf("converting ids: %w", err)
} }
if err := r.withTxn(ctx, func(ctx context.Context) error { if err := r.withTxn(ctx, func(ctx context.Context) error {
qb := r.repository.Movie qb := r.repository.Group
for _, id := range ids { for _, id := range ids {
if err := qb.Destroy(ctx, id); err != nil { if err := qb.Destroy(ctx, id); err != nil {
return err return err
@@ -309,8 +309,8 @@ func (r *mutationResolver) MoviesDestroy(ctx context.Context, movieIDs []string)
for _, id := range ids { for _, id := range ids {
// for backwards compatibility - run both movie and group hooks // for backwards compatibility - run both movie and group hooks
r.hookExecutor.ExecutePostHooks(ctx, id, hook.GroupDestroyPost, movieIDs, nil) r.hookExecutor.ExecutePostHooks(ctx, id, hook.GroupDestroyPost, groupIDs, nil)
r.hookExecutor.ExecutePostHooks(ctx, id, hook.MovieDestroyPost, movieIDs, nil) r.hookExecutor.ExecutePostHooks(ctx, id, hook.MovieDestroyPost, groupIDs, nil)
} }
return true, nil return true, nil

View File

@@ -82,12 +82,12 @@ func (r *mutationResolver) SceneCreate(ctx context.Context, input models.SceneCr
// prefer groups over movies // prefer groups over movies
if len(input.Groups) > 0 { if len(input.Groups) > 0 {
newScene.Movies, err = translator.relatedMoviesFromGroups(input.Groups) newScene.Groups, err = translator.relatedGroups(input.Groups)
if err != nil { if err != nil {
return nil, fmt.Errorf("converting groups: %w", err) return nil, fmt.Errorf("converting groups: %w", err)
} }
} else if len(input.Movies) > 0 { } else if len(input.Movies) > 0 {
newScene.Movies, err = translator.relatedMovies(input.Movies) newScene.Groups, err = translator.relatedGroupsFromMovies(input.Movies)
if err != nil { if err != nil {
return nil, fmt.Errorf("converting movies: %w", err) return nil, fmt.Errorf("converting movies: %w", err)
} }
@@ -225,12 +225,12 @@ func scenePartialFromInput(input models.SceneUpdateInput, translator changesetTr
} }
if translator.hasField("groups") { if translator.hasField("groups") {
updatedScene.MovieIDs, err = translator.updateMovieIDsFromGroups(input.Groups, "groups") updatedScene.GroupIDs, err = translator.updateGroupIDs(input.Groups, "groups")
if err != nil { if err != nil {
return nil, fmt.Errorf("converting movies: %w", err) return nil, fmt.Errorf("converting groups: %w", err)
} }
} else if translator.hasField("movies") { } else if translator.hasField("movies") {
updatedScene.MovieIDs, err = translator.updateMovieIDs(input.Movies, "movies") updatedScene.GroupIDs, err = translator.updateGroupIDsFromMovies(input.Movies, "movies")
if err != nil { if err != nil {
return nil, fmt.Errorf("converting movies: %w", err) return nil, fmt.Errorf("converting movies: %w", err)
} }
@@ -374,12 +374,12 @@ func (r *mutationResolver) BulkSceneUpdate(ctx context.Context, input BulkSceneU
} }
if translator.hasField("groups") { if translator.hasField("groups") {
updatedScene.MovieIDs, err = translator.updateMovieIDsBulk(input.GroupIds, "group_ids") updatedScene.GroupIDs, err = translator.updateGroupIDsBulk(input.GroupIds, "group_ids")
if err != nil { if err != nil {
return nil, fmt.Errorf("converting group ids: %w", err) return nil, fmt.Errorf("converting group ids: %w", err)
} }
} else if translator.hasField("movies") { } else if translator.hasField("movies") {
updatedScene.MovieIDs, err = translator.updateMovieIDsBulk(input.MovieIds, "movie_ids") updatedScene.GroupIDs, err = translator.updateGroupIDsBulk(input.MovieIds, "movie_ids")
if err != nil { if err != nil {
return nil, fmt.Errorf("converting movie ids: %w", err) return nil, fmt.Errorf("converting movie ids: %w", err)
} }

View File

@@ -8,14 +8,14 @@ import (
"github.com/stashapp/stash/pkg/sliceutil/stringslice" "github.com/stashapp/stash/pkg/sliceutil/stringslice"
) )
func (r *queryResolver) FindGroup(ctx context.Context, id string) (ret *models.Movie, err error) { func (r *queryResolver) FindGroup(ctx context.Context, id string) (ret *models.Group, err error) {
idInt, err := strconv.Atoi(id) idInt, err := strconv.Atoi(id)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if err := r.withReadTxn(ctx, func(ctx context.Context) error { if err := r.withReadTxn(ctx, func(ctx context.Context) error {
ret, err = r.repository.Movie.Find(ctx, idInt) ret, err = r.repository.Group.Find(ctx, idInt)
return err return err
}); err != nil { }); err != nil {
return nil, err return nil, err
@@ -24,22 +24,22 @@ func (r *queryResolver) FindGroup(ctx context.Context, id string) (ret *models.M
return ret, nil return ret, nil
} }
func (r *queryResolver) FindGroups(ctx context.Context, movieFilter *models.MovieFilterType, filter *models.FindFilterType, ids []string) (ret *FindGroupsResultType, err error) { func (r *queryResolver) FindGroups(ctx context.Context, groupFilter *models.GroupFilterType, filter *models.FindFilterType, ids []string) (ret *FindGroupsResultType, err error) {
idInts, err := stringslice.StringSliceToIntSlice(ids) idInts, err := stringslice.StringSliceToIntSlice(ids)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if err := r.withReadTxn(ctx, func(ctx context.Context) error { if err := r.withReadTxn(ctx, func(ctx context.Context) error {
var movies []*models.Movie var groups []*models.Group
var err error var err error
var total int var total int
if len(idInts) > 0 { if len(idInts) > 0 {
movies, err = r.repository.Movie.FindMany(ctx, idInts) groups, err = r.repository.Group.FindMany(ctx, idInts)
total = len(movies) total = len(groups)
} else { } else {
movies, total, err = r.repository.Movie.Query(ctx, movieFilter, filter) groups, total, err = r.repository.Group.Query(ctx, groupFilter, filter)
} }
if err != nil { if err != nil {
@@ -48,7 +48,7 @@ func (r *queryResolver) FindGroups(ctx context.Context, movieFilter *models.Movi
ret = &FindGroupsResultType{ ret = &FindGroupsResultType{
Count: total, Count: total,
Groups: movies, Groups: groups,
} }
return nil return nil
}); err != nil { }); err != nil {

View File

@@ -8,14 +8,14 @@ import (
"github.com/stashapp/stash/pkg/sliceutil/stringslice" "github.com/stashapp/stash/pkg/sliceutil/stringslice"
) )
func (r *queryResolver) FindMovie(ctx context.Context, id string) (ret *models.Movie, err error) { func (r *queryResolver) FindMovie(ctx context.Context, id string) (ret *models.Group, err error) {
idInt, err := strconv.Atoi(id) idInt, err := strconv.Atoi(id)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if err := r.withReadTxn(ctx, func(ctx context.Context) error { if err := r.withReadTxn(ctx, func(ctx context.Context) error {
ret, err = r.repository.Movie.Find(ctx, idInt) ret, err = r.repository.Group.Find(ctx, idInt)
return err return err
}); err != nil { }); err != nil {
return nil, err return nil, err
@@ -24,22 +24,22 @@ func (r *queryResolver) FindMovie(ctx context.Context, id string) (ret *models.M
return ret, nil return ret, nil
} }
func (r *queryResolver) FindMovies(ctx context.Context, movieFilter *models.MovieFilterType, filter *models.FindFilterType, ids []string) (ret *FindMoviesResultType, err error) { func (r *queryResolver) FindMovies(ctx context.Context, movieFilter *models.GroupFilterType, filter *models.FindFilterType, ids []string) (ret *FindMoviesResultType, err error) {
idInts, err := stringslice.StringSliceToIntSlice(ids) idInts, err := stringslice.StringSliceToIntSlice(ids)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if err := r.withReadTxn(ctx, func(ctx context.Context) error { if err := r.withReadTxn(ctx, func(ctx context.Context) error {
var movies []*models.Movie var groups []*models.Group
var err error var err error
var total int var total int
if len(idInts) > 0 { if len(idInts) > 0 {
movies, err = r.repository.Movie.FindMany(ctx, idInts) groups, err = r.repository.Group.FindMany(ctx, idInts)
total = len(movies) total = len(groups)
} else { } else {
movies, total, err = r.repository.Movie.Query(ctx, movieFilter, filter) groups, total, err = r.repository.Group.Query(ctx, movieFilter, filter)
} }
if err != nil { if err != nil {
@@ -48,7 +48,7 @@ func (r *queryResolver) FindMovies(ctx context.Context, movieFilter *models.Movi
ret = &FindMoviesResultType{ ret = &FindMoviesResultType{
Count: total, Count: total,
Movies: movies, Movies: groups,
} }
return nil return nil
}); err != nil { }); err != nil {
@@ -58,9 +58,9 @@ func (r *queryResolver) FindMovies(ctx context.Context, movieFilter *models.Movi
return ret, nil return ret, nil
} }
func (r *queryResolver) AllMovies(ctx context.Context) (ret []*models.Movie, err error) { func (r *queryResolver) AllMovies(ctx context.Context) (ret []*models.Group, err error) {
if err := r.withReadTxn(ctx, func(ctx context.Context) error { if err := r.withReadTxn(ctx, func(ctx context.Context) error {
ret, err = r.repository.Movie.All(ctx) ret, err = r.repository.Group.All(ctx)
return err return err
}); err != nil { }); err != nil {
return nil, err return nil, err

View File

@@ -144,8 +144,8 @@ func filterPerformerTags(p []*models.ScrapedPerformer) {
} }
} }
// filterMovieTags removes tags matching excluded tag patterns from the provided scraped movies // filterGroupTags removes tags matching excluded tag patterns from the provided scraped movies
func filterMovieTags(p []*models.ScrapedMovie) { func filterGroupTags(p []*models.ScrapedMovie) {
excludeRegexps := compileRegexps(manager.GetInstance().Config.GetScraperExcludeTagPatterns()) excludeRegexps := compileRegexps(manager.GetInstance().Config.GetScraperExcludeTagPatterns())
var ignoredTags []string var ignoredTags []string
@@ -208,7 +208,7 @@ func (r *queryResolver) ScrapeMovieURL(ctx context.Context, url string) (*models
return nil, err return nil, err
} }
filterMovieTags([]*models.ScrapedMovie{ret}) filterGroupTags([]*models.ScrapedMovie{ret})
return ret, nil return ret, nil
} }
@@ -224,7 +224,7 @@ func (r *queryResolver) ScrapeGroupURL(ctx context.Context, url string) (*models
return nil, err return nil, err
} }
filterMovieTags([]*models.ScrapedMovie{ret}) filterGroupTags([]*models.ScrapedMovie{ret})
// convert to scraped group // convert to scraped group
group := &models.ScrapedGroup{ group := &models.ScrapedGroup{

View File

@@ -14,22 +14,22 @@ import (
"github.com/stashapp/stash/pkg/utils" "github.com/stashapp/stash/pkg/utils"
) )
type MovieFinder interface { type GroupFinder interface {
models.MovieGetter models.GroupGetter
GetFrontImage(ctx context.Context, movieID int) ([]byte, error) GetFrontImage(ctx context.Context, groupID int) ([]byte, error)
GetBackImage(ctx context.Context, movieID int) ([]byte, error) GetBackImage(ctx context.Context, groupID int) ([]byte, error)
} }
type movieRoutes struct { type groupRoutes struct {
routes routes
movieFinder MovieFinder groupFinder GroupFinder
} }
func (rs movieRoutes) Routes() chi.Router { func (rs groupRoutes) Routes() chi.Router {
r := chi.NewRouter() r := chi.NewRouter()
r.Route("/{movieId}", func(r chi.Router) { r.Route("/{groupId}", func(r chi.Router) {
r.Use(rs.MovieCtx) r.Use(rs.GroupCtx)
r.Get("/frontimage", rs.FrontImage) r.Get("/frontimage", rs.FrontImage)
r.Get("/backimage", rs.BackImage) r.Get("/backimage", rs.BackImage)
}) })
@@ -37,77 +37,77 @@ func (rs movieRoutes) Routes() chi.Router {
return r return r
} }
func (rs movieRoutes) FrontImage(w http.ResponseWriter, r *http.Request) { func (rs groupRoutes) FrontImage(w http.ResponseWriter, r *http.Request) {
movie := r.Context().Value(movieKey).(*models.Movie) group := r.Context().Value(groupKey).(*models.Group)
defaultParam := r.URL.Query().Get("default") defaultParam := r.URL.Query().Get("default")
var image []byte var image []byte
if defaultParam != "true" { if defaultParam != "true" {
readTxnErr := rs.withReadTxn(r, func(ctx context.Context) error { readTxnErr := rs.withReadTxn(r, func(ctx context.Context) error {
var err error var err error
image, err = rs.movieFinder.GetFrontImage(ctx, movie.ID) image, err = rs.groupFinder.GetFrontImage(ctx, group.ID)
return err return err
}) })
if errors.Is(readTxnErr, context.Canceled) { if errors.Is(readTxnErr, context.Canceled) {
return return
} }
if readTxnErr != nil { if readTxnErr != nil {
logger.Warnf("read transaction error on fetch movie front image: %v", readTxnErr) logger.Warnf("read transaction error on fetch group front image: %v", readTxnErr)
} }
} }
// fallback to default image // fallback to default image
if len(image) == 0 { if len(image) == 0 {
image = static.ReadAll(static.DefaultMovieImage) image = static.ReadAll(static.DefaultGroupImage)
} }
utils.ServeImage(w, r, image) utils.ServeImage(w, r, image)
} }
func (rs movieRoutes) BackImage(w http.ResponseWriter, r *http.Request) { func (rs groupRoutes) BackImage(w http.ResponseWriter, r *http.Request) {
movie := r.Context().Value(movieKey).(*models.Movie) group := r.Context().Value(groupKey).(*models.Group)
defaultParam := r.URL.Query().Get("default") defaultParam := r.URL.Query().Get("default")
var image []byte var image []byte
if defaultParam != "true" { if defaultParam != "true" {
readTxnErr := rs.withReadTxn(r, func(ctx context.Context) error { readTxnErr := rs.withReadTxn(r, func(ctx context.Context) error {
var err error var err error
image, err = rs.movieFinder.GetBackImage(ctx, movie.ID) image, err = rs.groupFinder.GetBackImage(ctx, group.ID)
return err return err
}) })
if errors.Is(readTxnErr, context.Canceled) { if errors.Is(readTxnErr, context.Canceled) {
return return
} }
if readTxnErr != nil { if readTxnErr != nil {
logger.Warnf("read transaction error on fetch movie back image: %v", readTxnErr) logger.Warnf("read transaction error on fetch group back image: %v", readTxnErr)
} }
} }
// fallback to default image // fallback to default image
if len(image) == 0 { if len(image) == 0 {
image = static.ReadAll(static.DefaultMovieImage) image = static.ReadAll(static.DefaultGroupImage)
} }
utils.ServeImage(w, r, image) utils.ServeImage(w, r, image)
} }
func (rs movieRoutes) MovieCtx(next http.Handler) http.Handler { func (rs groupRoutes) GroupCtx(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
movieID, err := strconv.Atoi(chi.URLParam(r, "movieId")) groupID, err := strconv.Atoi(chi.URLParam(r, "groupId"))
if err != nil { if err != nil {
http.Error(w, http.StatusText(404), 404) http.Error(w, http.StatusText(404), 404)
return return
} }
var movie *models.Movie var group *models.Group
_ = rs.withReadTxn(r, func(ctx context.Context) error { _ = rs.withReadTxn(r, func(ctx context.Context) error {
movie, _ = rs.movieFinder.Find(ctx, movieID) group, _ = rs.groupFinder.Find(ctx, groupID)
return nil return nil
}) })
if movie == nil { if group == nil {
http.Error(w, http.StatusText(404), 404) http.Error(w, http.StatusText(404), 404)
return return
} }
ctx := context.WithValue(r.Context(), movieKey, movie) ctx := context.WithValue(r.Context(), groupKey, group)
next.ServeHTTP(w, r.WithContext(ctx)) next.ServeHTTP(w, r.WithContext(ctx))
}) })
} }

View File

@@ -209,7 +209,7 @@ func Initialize() (*Server, error) {
r.Mount("/scene", server.getSceneRoutes()) r.Mount("/scene", server.getSceneRoutes())
r.Mount("/image", server.getImageRoutes()) r.Mount("/image", server.getImageRoutes())
r.Mount("/studio", server.getStudioRoutes()) r.Mount("/studio", server.getStudioRoutes())
r.Mount("/movie", server.getMovieRoutes()) r.Mount("/group", server.getGroupRoutes())
r.Mount("/tag", server.getTagRoutes()) r.Mount("/tag", server.getTagRoutes())
r.Mount("/downloads", server.getDownloadsRoutes()) r.Mount("/downloads", server.getDownloadsRoutes())
r.Mount("/plugin", server.getPluginRoutes()) r.Mount("/plugin", server.getPluginRoutes())
@@ -343,11 +343,11 @@ func (s *Server) getStudioRoutes() chi.Router {
}.Routes() }.Routes()
} }
func (s *Server) getMovieRoutes() chi.Router { func (s *Server) getGroupRoutes() chi.Router {
repo := s.manager.Repository repo := s.manager.Repository
return movieRoutes{ return groupRoutes{
routes: routes{txnManager: repo.TxnManager}, routes: routes{txnManager: repo.TxnManager},
movieFinder: repo.Movie, groupFinder: repo.Group,
}.Routes() }.Routes()
} }

View File

@@ -1,32 +1,33 @@
package urlbuilders package urlbuilders
import ( import (
"github.com/stashapp/stash/pkg/models"
"strconv" "strconv"
"github.com/stashapp/stash/pkg/models"
) )
type MovieURLBuilder struct { type GroupURLBuilder struct {
BaseURL string BaseURL string
MovieID string GroupID string
UpdatedAt string UpdatedAt string
} }
func NewMovieURLBuilder(baseURL string, movie *models.Movie) MovieURLBuilder { func NewGroupURLBuilder(baseURL string, group *models.Group) GroupURLBuilder {
return MovieURLBuilder{ return GroupURLBuilder{
BaseURL: baseURL, BaseURL: baseURL,
MovieID: strconv.Itoa(movie.ID), GroupID: strconv.Itoa(group.ID),
UpdatedAt: strconv.FormatInt(movie.UpdatedAt.Unix(), 10), UpdatedAt: strconv.FormatInt(group.UpdatedAt.Unix(), 10),
} }
} }
func (b MovieURLBuilder) GetMovieFrontImageURL(hasImage bool) string { func (b GroupURLBuilder) GetGroupFrontImageURL(hasImage bool) string {
url := b.BaseURL + "/movie/" + b.MovieID + "/frontimage?t=" + b.UpdatedAt url := b.BaseURL + "/group/" + b.GroupID + "/frontimage?t=" + b.UpdatedAt
if !hasImage { if !hasImage {
url += "&default=true" url += "&default=true"
} }
return url return url
} }
func (b MovieURLBuilder) GetMovieBackImageURL() string { func (b GroupURLBuilder) GetGroupBackImageURL() string {
return b.BaseURL + "/movie/" + b.MovieID + "/backimage?t=" + b.UpdatedAt return b.BaseURL + "/group/" + b.GroupID + "/backimage?t=" + b.UpdatedAt
} }

View File

@@ -316,13 +316,13 @@ func (me *contentDirectoryService) handleBrowseDirectChildren(obj object, host s
objs = me.getPerformerScenes(childPath(paths), host) objs = me.getPerformerScenes(childPath(paths), host)
} }
// Movies // Groups - deprecated
if obj.Path == "movies" { if obj.Path == "groups" {
objs = me.getMovies() objs = me.getGroups()
} }
if strings.HasPrefix(obj.Path, "movies/") { if strings.HasPrefix(obj.Path, "groups/") {
objs = me.getMovieScenes(childPath(paths), host) objs = me.getGroupScenes(childPath(paths), host)
} }
// Rating // Rating
@@ -433,7 +433,7 @@ func getRootObjects() []interface{} {
objs = append(objs, makeStorageFolder("performers", "performers", rootID)) objs = append(objs, makeStorageFolder("performers", "performers", rootID))
objs = append(objs, makeStorageFolder("tags", "tags", rootID)) objs = append(objs, makeStorageFolder("tags", "tags", rootID))
objs = append(objs, makeStorageFolder("studios", "studios", rootID)) objs = append(objs, makeStorageFolder("studios", "studios", rootID))
objs = append(objs, makeStorageFolder("movies", "movies", rootID)) objs = append(objs, makeStorageFolder("groups", "groups", rootID))
objs = append(objs, makeStorageFolder("rating", "rating", rootID)) objs = append(objs, makeStorageFolder("rating", "rating", rootID))
return objs return objs
@@ -658,18 +658,18 @@ func (me *contentDirectoryService) getPerformerScenes(paths []string, host strin
return me.getVideos(sceneFilter, parentID, host) return me.getVideos(sceneFilter, parentID, host)
} }
func (me *contentDirectoryService) getMovies() []interface{} { func (me *contentDirectoryService) getGroups() []interface{} {
var objs []interface{} var objs []interface{}
r := me.repository r := me.repository
if err := r.WithReadTxn(context.TODO(), func(ctx context.Context) error { if err := r.WithReadTxn(context.TODO(), func(ctx context.Context) error {
movies, err := r.MovieFinder.All(ctx) groups, err := r.GroupFinder.All(ctx)
if err != nil { if err != nil {
return err return err
} }
for _, s := range movies { for _, s := range groups {
objs = append(objs, makeStorageFolder("movies/"+strconv.Itoa(s.ID), s.Name, "movies")) objs = append(objs, makeStorageFolder("groups/"+strconv.Itoa(s.ID), s.Name, "groups"))
} }
return nil return nil
@@ -680,15 +680,15 @@ func (me *contentDirectoryService) getMovies() []interface{} {
return objs return objs
} }
func (me *contentDirectoryService) getMovieScenes(paths []string, host string) []interface{} { func (me *contentDirectoryService) getGroupScenes(paths []string, host string) []interface{} {
sceneFilter := &models.SceneFilterType{ sceneFilter := &models.SceneFilterType{
Movies: &models.MultiCriterionInput{ Groups: &models.MultiCriterionInput{
Modifier: models.CriterionModifierIncludes, Modifier: models.CriterionModifierIncludes,
Value: []string{paths[0]}, Value: []string{paths[0]},
}, },
} }
parentID := "movies/" + strings.Join(paths, "/") parentID := "groups/" + strings.Join(paths, "/")
page := getPageFromID(paths) page := getPageFromID(paths)
if page != nil { if page != nil {

View File

@@ -67,8 +67,8 @@ type PerformerFinder interface {
All(ctx context.Context) ([]*models.Performer, error) All(ctx context.Context) ([]*models.Performer, error)
} }
type MovieFinder interface { type GroupFinder interface {
All(ctx context.Context) ([]*models.Movie, error) All(ctx context.Context) ([]*models.Group, error)
} }
const ( const (

View File

@@ -22,7 +22,7 @@ type Repository struct {
StudioFinder StudioFinder StudioFinder StudioFinder
TagFinder TagFinder TagFinder TagFinder
PerformerFinder PerformerFinder PerformerFinder PerformerFinder
MovieFinder MovieFinder GroupFinder GroupFinder
} }
func NewRepository(repo models.Repository) Repository { func NewRepository(repo models.Repository) Repository {
@@ -33,7 +33,7 @@ func NewRepository(repo models.Repository) Repository {
StudioFinder: repo.Studio, StudioFinder: repo.Studio,
TagFinder: repo.Tag, TagFinder: repo.Tag,
PerformerFinder: repo.Performer, PerformerFinder: repo.Performer,
MovieFinder: repo.Movie, GroupFinder: repo.Group,
} }
} }

View File

@@ -23,8 +23,8 @@ func (jp *jsonUtils) saveTag(fn string, tag *jsonschema.Tag) error {
return jsonschema.SaveTagFile(filepath.Join(jp.json.Tags, fn), tag) return jsonschema.SaveTagFile(filepath.Join(jp.json.Tags, fn), tag)
} }
func (jp *jsonUtils) saveMovie(fn string, movie *jsonschema.Movie) error { func (jp *jsonUtils) saveGroup(fn string, group *jsonschema.Group) error {
return jsonschema.SaveMovieFile(filepath.Join(jp.json.Movies, fn), movie) return jsonschema.SaveGroupFile(filepath.Join(jp.json.Groups, fn), group)
} }
func (jp *jsonUtils) saveScene(fn string, scene *jsonschema.Scene) error { func (jp *jsonUtils) saveScene(fn string, scene *jsonschema.Scene) error {

View File

@@ -120,7 +120,7 @@ func CreateExportTask(a models.HashAlgorithm, input ExportObjectsInput) *ExportT
func (t *ExportTask) Start(ctx context.Context, wg *sync.WaitGroup) { func (t *ExportTask) Start(ctx context.Context, wg *sync.WaitGroup) {
defer wg.Done() defer wg.Done()
// @manager.total = Scene.count + Gallery.count + Performer.count + Studio.count + Movie.count // @manager.total = Scene.count + Gallery.count + Performer.count + Studio.count + Group.count
workerCount := runtime.GOMAXPROCS(0) // set worker count to number of cpus available workerCount := runtime.GOMAXPROCS(0) // set worker count to number of cpus available
startTime := time.Now() startTime := time.Now()
@@ -156,11 +156,11 @@ func (t *ExportTask) Start(ctx context.Context, wg *sync.WaitGroup) {
paths.EnsureJSONDirs(t.baseDir) paths.EnsureJSONDirs(t.baseDir)
txnErr := t.repository.WithTxn(ctx, func(ctx context.Context) error { txnErr := t.repository.WithTxn(ctx, func(ctx context.Context) error {
// include movie scenes and gallery images // include group scenes and gallery images
if !t.full { if !t.full {
// only include movie scenes if includeDependencies is also set // only include group scenes if includeDependencies is also set
if !t.scenes.all && t.includeDependencies { if !t.scenes.all && t.includeDependencies {
t.populateMovieScenes(ctx) t.populateGroupScenes(ctx)
} }
// always export gallery images // always export gallery images
@@ -172,7 +172,7 @@ func (t *ExportTask) Start(ctx context.Context, wg *sync.WaitGroup) {
t.ExportScenes(ctx, workerCount) t.ExportScenes(ctx, workerCount)
t.ExportImages(ctx, workerCount) t.ExportImages(ctx, workerCount)
t.ExportGalleries(ctx, workerCount) t.ExportGalleries(ctx, workerCount)
t.ExportMovies(ctx, workerCount) t.ExportGroups(ctx, workerCount)
t.ExportPerformers(ctx, workerCount) t.ExportPerformers(ctx, workerCount)
t.ExportStudios(ctx, workerCount) t.ExportStudios(ctx, workerCount)
t.ExportTags(ctx, workerCount) t.ExportTags(ctx, workerCount)
@@ -229,7 +229,7 @@ func (t *ExportTask) zipFiles(w io.Writer) error {
walkWarn(t.json.json.Galleries, t.zipWalkFunc(u.json.Galleries, z)) walkWarn(t.json.json.Galleries, t.zipWalkFunc(u.json.Galleries, z))
walkWarn(t.json.json.Performers, t.zipWalkFunc(u.json.Performers, z)) walkWarn(t.json.json.Performers, t.zipWalkFunc(u.json.Performers, z))
walkWarn(t.json.json.Studios, t.zipWalkFunc(u.json.Studios, z)) walkWarn(t.json.json.Studios, t.zipWalkFunc(u.json.Studios, z))
walkWarn(t.json.json.Movies, t.zipWalkFunc(u.json.Movies, z)) walkWarn(t.json.json.Groups, t.zipWalkFunc(u.json.Groups, z))
walkWarn(t.json.json.Scenes, t.zipWalkFunc(u.json.Scenes, z)) walkWarn(t.json.json.Scenes, t.zipWalkFunc(u.json.Scenes, z))
walkWarn(t.json.json.Images, t.zipWalkFunc(u.json.Images, z)) walkWarn(t.json.json.Images, t.zipWalkFunc(u.json.Images, z))
@@ -282,28 +282,28 @@ func (t *ExportTask) zipFile(fn, outDir string, z *zip.Writer) error {
return nil return nil
} }
func (t *ExportTask) populateMovieScenes(ctx context.Context) { func (t *ExportTask) populateGroupScenes(ctx context.Context) {
r := t.repository r := t.repository
reader := r.Movie reader := r.Group
sceneReader := r.Scene sceneReader := r.Scene
var movies []*models.Movie var groups []*models.Group
var err error var err error
all := t.full || (t.groups != nil && t.groups.all) all := t.full || (t.groups != nil && t.groups.all)
if all { if all {
movies, err = reader.All(ctx) groups, err = reader.All(ctx)
} else if t.groups != nil && len(t.groups.IDs) > 0 { } else if t.groups != nil && len(t.groups.IDs) > 0 {
movies, err = reader.FindMany(ctx, t.groups.IDs) groups, err = reader.FindMany(ctx, t.groups.IDs)
} }
if err != nil { if err != nil {
logger.Errorf("[movies] failed to fetch movies: %v", err) logger.Errorf("[groups] failed to fetch groups: %v", err)
} }
for _, m := range movies { for _, m := range groups {
scenes, err := sceneReader.FindByMovieID(ctx, m.ID) scenes, err := sceneReader.FindByGroupID(ctx, m.ID)
if err != nil { if err != nil {
logger.Errorf("[movies] <%s> failed to fetch scenes for movie: %v", m.Name, err) logger.Errorf("[groups] <%s> failed to fetch scenes for group: %v", m.Name, err)
continue continue
} }
@@ -488,7 +488,7 @@ func (t *ExportTask) exportScene(ctx context.Context, wg *sync.WaitGroup, jobCha
r := t.repository r := t.repository
sceneReader := r.Scene sceneReader := r.Scene
studioReader := r.Studio studioReader := r.Studio
movieReader := r.Movie groupReader := r.Group
galleryReader := r.Gallery galleryReader := r.Gallery
performerReader := r.Performer performerReader := r.Performer
tagReader := r.Tag tagReader := r.Tag
@@ -556,9 +556,9 @@ func (t *ExportTask) exportScene(ctx context.Context, wg *sync.WaitGroup, jobCha
continue continue
} }
newSceneJSON.Movies, err = scene.GetSceneMoviesJSON(ctx, movieReader, s) newSceneJSON.Groups, err = scene.GetSceneGroupsJSON(ctx, groupReader, s)
if err != nil { if err != nil {
logger.Errorf("[scenes] <%s> error getting scene movies JSON: %v", sceneHash, err) logger.Errorf("[scenes] <%s> error getting scene groups JSON: %v", sceneHash, err)
continue continue
} }
@@ -576,12 +576,12 @@ func (t *ExportTask) exportScene(ctx context.Context, wg *sync.WaitGroup, jobCha
} }
t.tags.IDs = sliceutil.AppendUniques(t.tags.IDs, tagIDs) t.tags.IDs = sliceutil.AppendUniques(t.tags.IDs, tagIDs)
movieIDs, err := scene.GetDependentMovieIDs(ctx, s) groupIDs, err := scene.GetDependentGroupIDs(ctx, s)
if err != nil { if err != nil {
logger.Errorf("[scenes] <%s> error getting scene movies: %v", sceneHash, err) logger.Errorf("[scenes] <%s> error getting scene groups: %v", sceneHash, err)
continue continue
} }
t.groups.IDs = sliceutil.AppendUniques(t.groups.IDs, movieIDs) t.groups.IDs = sliceutil.AppendUniques(t.groups.IDs, groupIDs)
t.performers.IDs = sliceutil.AppendUniques(t.performers.IDs, performer.GetIDs(performers)) t.performers.IDs = sliceutil.AppendUniques(t.performers.IDs, performer.GetIDs(performers))
} }
@@ -1081,74 +1081,74 @@ func (t *ExportTask) exportTag(ctx context.Context, wg *sync.WaitGroup, jobChan
} }
} }
func (t *ExportTask) ExportMovies(ctx context.Context, workers int) { func (t *ExportTask) ExportGroups(ctx context.Context, workers int) {
var moviesWg sync.WaitGroup var groupsWg sync.WaitGroup
reader := t.repository.Movie reader := t.repository.Group
var movies []*models.Movie var groups []*models.Group
var err error var err error
all := t.full || (t.groups != nil && t.groups.all) all := t.full || (t.groups != nil && t.groups.all)
if all { if all {
movies, err = reader.All(ctx) groups, err = reader.All(ctx)
} else if t.groups != nil && len(t.groups.IDs) > 0 { } else if t.groups != nil && len(t.groups.IDs) > 0 {
movies, err = reader.FindMany(ctx, t.groups.IDs) groups, err = reader.FindMany(ctx, t.groups.IDs)
} }
if err != nil { if err != nil {
logger.Errorf("[movies] failed to fetch movies: %v", err) logger.Errorf("[groups] failed to fetch groups: %v", err)
} }
logger.Info("[movies] exporting") logger.Info("[groups] exporting")
startTime := time.Now() startTime := time.Now()
jobCh := make(chan *models.Movie, workers*2) // make a buffered channel to feed workers jobCh := make(chan *models.Group, workers*2) // make a buffered channel to feed workers
for w := 0; w < workers; w++ { // create export Studio workers for w := 0; w < workers; w++ { // create export Studio workers
moviesWg.Add(1) groupsWg.Add(1)
go t.exportMovie(ctx, &moviesWg, jobCh) go t.exportGroup(ctx, &groupsWg, jobCh)
} }
for i, movie := range movies { for i, group := range groups {
index := i + 1 index := i + 1
logger.Progressf("[movies] %d of %d", index, len(movies)) logger.Progressf("[groups] %d of %d", index, len(groups))
jobCh <- movie // feed workers jobCh <- group // feed workers
} }
close(jobCh) close(jobCh)
moviesWg.Wait() groupsWg.Wait()
logger.Infof("[movies] export complete in %s. %d workers used.", time.Since(startTime), workers) logger.Infof("[groups] export complete in %s. %d workers used.", time.Since(startTime), workers)
} }
func (t *ExportTask) exportMovie(ctx context.Context, wg *sync.WaitGroup, jobChan <-chan *models.Movie) { func (t *ExportTask) exportGroup(ctx context.Context, wg *sync.WaitGroup, jobChan <-chan *models.Group) {
defer wg.Done() defer wg.Done()
r := t.repository r := t.repository
movieReader := r.Movie groupReader := r.Group
studioReader := r.Studio studioReader := r.Studio
tagReader := r.Tag tagReader := r.Tag
for m := range jobChan { for m := range jobChan {
if err := m.LoadURLs(ctx, r.Movie); err != nil { if err := m.LoadURLs(ctx, r.Group); err != nil {
logger.Errorf("[movies] <%s> error getting movie urls: %v", m.Name, err) logger.Errorf("[groups] <%s> error getting group urls: %v", m.Name, err)
continue continue
} }
newMovieJSON, err := movie.ToJSON(ctx, movieReader, studioReader, m) newGroupJSON, err := movie.ToJSON(ctx, groupReader, studioReader, m)
if err != nil { if err != nil {
logger.Errorf("[movies] <%s> error getting tag JSON: %v", m.Name, err) logger.Errorf("[groups] <%s> error getting tag JSON: %v", m.Name, err)
continue continue
} }
tags, err := tagReader.FindByMovieID(ctx, m.ID) tags, err := tagReader.FindByGroupID(ctx, m.ID)
if err != nil { if err != nil {
logger.Errorf("[movies] <%s> error getting image tag names: %v", m.Name, err) logger.Errorf("[groups] <%s> error getting image tag names: %v", m.Name, err)
continue continue
} }
newMovieJSON.Tags = tag.GetNames(tags) newGroupJSON.Tags = tag.GetNames(tags)
if t.includeDependencies { if t.includeDependencies {
if m.StudioID != nil { if m.StudioID != nil {
@@ -1156,10 +1156,10 @@ func (t *ExportTask) exportMovie(ctx context.Context, wg *sync.WaitGroup, jobCha
} }
} }
fn := newMovieJSON.Filename() fn := newGroupJSON.Filename()
if err := t.json.saveMovie(fn, newMovieJSON); err != nil { if err := t.json.saveGroup(fn, newGroupJSON); err != nil {
logger.Errorf("[movies] <%s> failed to save json: %v", m.Name, err) logger.Errorf("[groups] <%s> failed to save json: %v", m.Name, err)
} }
} }
} }

View File

@@ -127,7 +127,7 @@ func (t *ImportTask) Start(ctx context.Context) {
t.ImportTags(ctx) t.ImportTags(ctx)
t.ImportPerformers(ctx) t.ImportPerformers(ctx)
t.ImportStudios(ctx) t.ImportStudios(ctx)
t.ImportMovies(ctx) t.ImportGroups(ctx)
t.ImportFiles(ctx) t.ImportFiles(ctx)
t.ImportGalleries(ctx) t.ImportGalleries(ctx)
@@ -325,14 +325,14 @@ func (t *ImportTask) importStudio(ctx context.Context, studioJSON *jsonschema.St
return nil return nil
} }
func (t *ImportTask) ImportMovies(ctx context.Context) { func (t *ImportTask) ImportGroups(ctx context.Context) {
logger.Info("[movies] importing") logger.Info("[groups] importing")
path := t.json.json.Movies path := t.json.json.Groups
files, err := os.ReadDir(path) files, err := os.ReadDir(path)
if err != nil { if err != nil {
if !errors.Is(err, os.ErrNotExist) { if !errors.Is(err, os.ErrNotExist) {
logger.Errorf("[movies] failed to read movies directory: %v", err) logger.Errorf("[groups] failed to read movies directory: %v", err)
} }
return return
@@ -342,31 +342,31 @@ func (t *ImportTask) ImportMovies(ctx context.Context) {
for i, fi := range files { for i, fi := range files {
index := i + 1 index := i + 1
movieJSON, err := jsonschema.LoadMovieFile(filepath.Join(path, fi.Name())) groupJSON, err := jsonschema.LoadGroupFile(filepath.Join(path, fi.Name()))
if err != nil { if err != nil {
logger.Errorf("[movies] failed to read json: %v", err) logger.Errorf("[groups] failed to read json: %v", err)
continue continue
} }
logger.Progressf("[movies] %d of %d", index, len(files)) logger.Progressf("[groups] %d of %d", index, len(files))
if err := r.WithTxn(ctx, func(ctx context.Context) error { if err := r.WithTxn(ctx, func(ctx context.Context) error {
movieImporter := &movie.Importer{ groupImporter := &movie.Importer{
ReaderWriter: r.Movie, ReaderWriter: r.Group,
StudioWriter: r.Studio, StudioWriter: r.Studio,
TagWriter: r.Tag, TagWriter: r.Tag,
Input: *movieJSON, Input: *groupJSON,
MissingRefBehaviour: t.MissingRefBehaviour, MissingRefBehaviour: t.MissingRefBehaviour,
} }
return performImport(ctx, movieImporter, t.DuplicateBehaviour) return performImport(ctx, groupImporter, t.DuplicateBehaviour)
}); err != nil { }); err != nil {
logger.Errorf("[movies] <%s> import failed: %v", fi.Name(), err) logger.Errorf("[groups] <%s> import failed: %v", fi.Name(), err)
continue continue
} }
} }
logger.Info("[movies] import complete") logger.Info("[groups] import complete")
} }
func (t *ImportTask) ImportFiles(ctx context.Context) { func (t *ImportTask) ImportFiles(ctx context.Context) {
@@ -648,7 +648,7 @@ func (t *ImportTask) ImportScenes(ctx context.Context) {
MissingRefBehaviour: t.MissingRefBehaviour, MissingRefBehaviour: t.MissingRefBehaviour,
GalleryFinder: r.Gallery, GalleryFinder: r.Gallery,
MovieWriter: r.Movie, GroupWriter: r.Group,
PerformerWriter: r.Performer, PerformerWriter: r.Performer,
StudioWriter: r.Studio, StudioWriter: r.Studio,
TagWriter: r.Tag, TagWriter: r.Tag,

View File

@@ -26,8 +26,8 @@ const (
Studio = "studio" Studio = "studio"
DefaultStudioImage = "studio/studio.svg" DefaultStudioImage = "studio/studio.svg"
Movie = "movie" Group = "movie"
DefaultMovieImage = "movie/movie.png" DefaultGroupImage = "movie/movie.png"
) )
// Sub returns an FS rooted at path, using fs.Sub. // Sub returns an FS rooted at path, using fs.Sub.

View File

@@ -16,8 +16,8 @@ type PerformerFinder interface {
FindByStashID(ctx context.Context, stashID models.StashID) ([]*models.Performer, error) FindByStashID(ctx context.Context, stashID models.StashID) ([]*models.Performer, error)
} }
type MovieNamesFinder interface { type GroupNamesFinder interface {
FindByNames(ctx context.Context, names []string, nocase bool) ([]*models.Movie, error) FindByNames(ctx context.Context, names []string, nocase bool) ([]*models.Group, error)
} }
// ScrapedPerformer matches the provided performer with the // ScrapedPerformer matches the provided performer with the
@@ -118,27 +118,27 @@ func ScrapedStudio(ctx context.Context, qb StudioFinder, s *models.ScrapedStudio
return nil return nil
} }
// ScrapedMovie matches the provided movie with the movies // ScrapedGroup matches the provided movie with the movies
// in the database and sets the ID field if one is found. // in the database and returns the ID field if one is found.
func ScrapedMovie(ctx context.Context, qb MovieNamesFinder, m *models.ScrapedMovie) error { func ScrapedGroup(ctx context.Context, qb GroupNamesFinder, storedID *string, name *string) (matchedID *string, err error) {
if m.StoredID != nil || m.Name == nil { if storedID != nil || name == nil {
return nil return
} }
movies, err := qb.FindByNames(ctx, []string{*m.Name}, true) movies, err := qb.FindByNames(ctx, []string{*name}, true)
if err != nil { if err != nil {
return err return
} }
if len(movies) != 1 { if len(movies) != 1 {
// ignore - cannot match // ignore - cannot match
return nil return
} }
id := strconv.Itoa(movies[0].ID) id := strconv.Itoa(movies[0].ID)
m.StoredID = &id matchedID = &id
return nil return
} }
// ScrapedTag matches the provided tag with the tags // ScrapedTag matches the provided tag with the tags

View File

@@ -11,7 +11,7 @@ import (
"github.com/stashapp/stash/pkg/models/json" "github.com/stashapp/stash/pkg/models/json"
) )
type Movie struct { type Group struct {
Name string `json:"name,omitempty"` Name string `json:"name,omitempty"`
Aliases string `json:"aliases,omitempty"` Aliases string `json:"aliases,omitempty"`
Duration int `json:"duration,omitempty"` Duration int `json:"duration,omitempty"`
@@ -31,7 +31,7 @@ type Movie struct {
URL string `json:"url,omitempty"` URL string `json:"url,omitempty"`
} }
func (s Movie) Filename() string { func (s Group) Filename() string {
return fsutil.SanitiseBasename(s.Name) + ".json" return fsutil.SanitiseBasename(s.Name) + ".json"
} }
@@ -40,8 +40,8 @@ type MovieSynopsisBC struct {
Synopsis string `json:"sypnopsis,omitempty"` Synopsis string `json:"sypnopsis,omitempty"`
} }
func LoadMovieFile(filePath string) (*Movie, error) { func LoadGroupFile(filePath string) (*Group, error) {
var movie Movie var movie Group
file, err := os.Open(filePath) file, err := os.Open(filePath)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -72,7 +72,7 @@ func LoadMovieFile(filePath string) (*Movie, error) {
return &movie, nil return &movie, nil
} }
func SaveMovieFile(filePath string, movie *Movie) error { func SaveGroupFile(filePath string, movie *Group) error {
if movie == nil { if movie == nil {
return fmt.Errorf("movie must not be nil") return fmt.Errorf("movie must not be nil")
} }

View File

@@ -33,8 +33,8 @@ type SceneFile struct {
Bitrate int `json:"bitrate"` Bitrate int `json:"bitrate"`
} }
type SceneMovie struct { type SceneGroup struct {
MovieName string `json:"movieName,omitempty"` GroupName string `json:"movieName,omitempty"`
SceneIndex int `json:"scene_index,omitempty"` SceneIndex int `json:"scene_index,omitempty"`
} }
@@ -58,7 +58,7 @@ type Scene struct {
Director string `json:"director,omitempty"` Director string `json:"director,omitempty"`
Galleries []GalleryRef `json:"galleries,omitempty"` Galleries []GalleryRef `json:"galleries,omitempty"`
Performers []string `json:"performers,omitempty"` Performers []string `json:"performers,omitempty"`
Movies []SceneMovie `json:"movies,omitempty"` Groups []SceneGroup `json:"movies,omitempty"`
Tags []string `json:"tags,omitempty"` Tags []string `json:"tags,omitempty"`
Markers []SceneMarker `json:"markers,omitempty"` Markers []SceneMarker `json:"markers,omitempty"`
Files []string `json:"files,omitempty"` Files []string `json:"files,omitempty"`

View File

@@ -9,21 +9,21 @@ import (
mock "github.com/stretchr/testify/mock" mock "github.com/stretchr/testify/mock"
) )
// MovieReaderWriter is an autogenerated mock type for the MovieReaderWriter type // GroupReaderWriter is an autogenerated mock type for the GroupReaderWriter type
type MovieReaderWriter struct { type GroupReaderWriter struct {
mock.Mock mock.Mock
} }
// All provides a mock function with given fields: ctx // All provides a mock function with given fields: ctx
func (_m *MovieReaderWriter) All(ctx context.Context) ([]*models.Movie, error) { func (_m *GroupReaderWriter) All(ctx context.Context) ([]*models.Group, error) {
ret := _m.Called(ctx) ret := _m.Called(ctx)
var r0 []*models.Movie var r0 []*models.Group
if rf, ok := ret.Get(0).(func(context.Context) []*models.Movie); ok { if rf, ok := ret.Get(0).(func(context.Context) []*models.Group); ok {
r0 = rf(ctx) r0 = rf(ctx)
} else { } else {
if ret.Get(0) != nil { if ret.Get(0) != nil {
r0 = ret.Get(0).([]*models.Movie) r0 = ret.Get(0).([]*models.Group)
} }
} }
@@ -38,7 +38,7 @@ func (_m *MovieReaderWriter) All(ctx context.Context) ([]*models.Movie, error) {
} }
// Count provides a mock function with given fields: ctx // Count provides a mock function with given fields: ctx
func (_m *MovieReaderWriter) Count(ctx context.Context) (int, error) { func (_m *GroupReaderWriter) Count(ctx context.Context) (int, error) {
ret := _m.Called(ctx) ret := _m.Called(ctx)
var r0 int var r0 int
@@ -59,7 +59,7 @@ func (_m *MovieReaderWriter) Count(ctx context.Context) (int, error) {
} }
// CountByPerformerID provides a mock function with given fields: ctx, performerID // CountByPerformerID provides a mock function with given fields: ctx, performerID
func (_m *MovieReaderWriter) CountByPerformerID(ctx context.Context, performerID int) (int, error) { func (_m *GroupReaderWriter) CountByPerformerID(ctx context.Context, performerID int) (int, error) {
ret := _m.Called(ctx, performerID) ret := _m.Called(ctx, performerID)
var r0 int var r0 int
@@ -80,7 +80,7 @@ func (_m *MovieReaderWriter) CountByPerformerID(ctx context.Context, performerID
} }
// CountByStudioID provides a mock function with given fields: ctx, studioID // CountByStudioID provides a mock function with given fields: ctx, studioID
func (_m *MovieReaderWriter) CountByStudioID(ctx context.Context, studioID int) (int, error) { func (_m *GroupReaderWriter) CountByStudioID(ctx context.Context, studioID int) (int, error) {
ret := _m.Called(ctx, studioID) ret := _m.Called(ctx, studioID)
var r0 int var r0 int
@@ -100,13 +100,13 @@ func (_m *MovieReaderWriter) CountByStudioID(ctx context.Context, studioID int)
return r0, r1 return r0, r1
} }
// Create provides a mock function with given fields: ctx, newMovie // Create provides a mock function with given fields: ctx, newGroup
func (_m *MovieReaderWriter) Create(ctx context.Context, newMovie *models.Movie) error { func (_m *GroupReaderWriter) Create(ctx context.Context, newGroup *models.Group) error {
ret := _m.Called(ctx, newMovie) ret := _m.Called(ctx, newGroup)
var r0 error var r0 error
if rf, ok := ret.Get(0).(func(context.Context, *models.Movie) error); ok { if rf, ok := ret.Get(0).(func(context.Context, *models.Group) error); ok {
r0 = rf(ctx, newMovie) r0 = rf(ctx, newGroup)
} else { } else {
r0 = ret.Error(0) r0 = ret.Error(0)
} }
@@ -115,7 +115,7 @@ func (_m *MovieReaderWriter) Create(ctx context.Context, newMovie *models.Movie)
} }
// Destroy provides a mock function with given fields: ctx, id // Destroy provides a mock function with given fields: ctx, id
func (_m *MovieReaderWriter) Destroy(ctx context.Context, id int) error { func (_m *GroupReaderWriter) Destroy(ctx context.Context, id int) error {
ret := _m.Called(ctx, id) ret := _m.Called(ctx, id)
var r0 error var r0 error
@@ -129,15 +129,15 @@ func (_m *MovieReaderWriter) Destroy(ctx context.Context, id int) error {
} }
// Find provides a mock function with given fields: ctx, id // Find provides a mock function with given fields: ctx, id
func (_m *MovieReaderWriter) Find(ctx context.Context, id int) (*models.Movie, error) { func (_m *GroupReaderWriter) Find(ctx context.Context, id int) (*models.Group, error) {
ret := _m.Called(ctx, id) ret := _m.Called(ctx, id)
var r0 *models.Movie var r0 *models.Group
if rf, ok := ret.Get(0).(func(context.Context, int) *models.Movie); ok { if rf, ok := ret.Get(0).(func(context.Context, int) *models.Group); ok {
r0 = rf(ctx, id) r0 = rf(ctx, id)
} else { } else {
if ret.Get(0) != nil { if ret.Get(0) != nil {
r0 = ret.Get(0).(*models.Movie) r0 = ret.Get(0).(*models.Group)
} }
} }
@@ -152,15 +152,15 @@ func (_m *MovieReaderWriter) Find(ctx context.Context, id int) (*models.Movie, e
} }
// FindByName provides a mock function with given fields: ctx, name, nocase // FindByName provides a mock function with given fields: ctx, name, nocase
func (_m *MovieReaderWriter) FindByName(ctx context.Context, name string, nocase bool) (*models.Movie, error) { func (_m *GroupReaderWriter) FindByName(ctx context.Context, name string, nocase bool) (*models.Group, error) {
ret := _m.Called(ctx, name, nocase) ret := _m.Called(ctx, name, nocase)
var r0 *models.Movie var r0 *models.Group
if rf, ok := ret.Get(0).(func(context.Context, string, bool) *models.Movie); ok { if rf, ok := ret.Get(0).(func(context.Context, string, bool) *models.Group); ok {
r0 = rf(ctx, name, nocase) r0 = rf(ctx, name, nocase)
} else { } else {
if ret.Get(0) != nil { if ret.Get(0) != nil {
r0 = ret.Get(0).(*models.Movie) r0 = ret.Get(0).(*models.Group)
} }
} }
@@ -175,15 +175,15 @@ func (_m *MovieReaderWriter) FindByName(ctx context.Context, name string, nocase
} }
// FindByNames provides a mock function with given fields: ctx, names, nocase // FindByNames provides a mock function with given fields: ctx, names, nocase
func (_m *MovieReaderWriter) FindByNames(ctx context.Context, names []string, nocase bool) ([]*models.Movie, error) { func (_m *GroupReaderWriter) FindByNames(ctx context.Context, names []string, nocase bool) ([]*models.Group, error) {
ret := _m.Called(ctx, names, nocase) ret := _m.Called(ctx, names, nocase)
var r0 []*models.Movie var r0 []*models.Group
if rf, ok := ret.Get(0).(func(context.Context, []string, bool) []*models.Movie); ok { if rf, ok := ret.Get(0).(func(context.Context, []string, bool) []*models.Group); ok {
r0 = rf(ctx, names, nocase) r0 = rf(ctx, names, nocase)
} else { } else {
if ret.Get(0) != nil { if ret.Get(0) != nil {
r0 = ret.Get(0).([]*models.Movie) r0 = ret.Get(0).([]*models.Group)
} }
} }
@@ -198,15 +198,15 @@ func (_m *MovieReaderWriter) FindByNames(ctx context.Context, names []string, no
} }
// FindByPerformerID provides a mock function with given fields: ctx, performerID // FindByPerformerID provides a mock function with given fields: ctx, performerID
func (_m *MovieReaderWriter) FindByPerformerID(ctx context.Context, performerID int) ([]*models.Movie, error) { func (_m *GroupReaderWriter) FindByPerformerID(ctx context.Context, performerID int) ([]*models.Group, error) {
ret := _m.Called(ctx, performerID) ret := _m.Called(ctx, performerID)
var r0 []*models.Movie var r0 []*models.Group
if rf, ok := ret.Get(0).(func(context.Context, int) []*models.Movie); ok { if rf, ok := ret.Get(0).(func(context.Context, int) []*models.Group); ok {
r0 = rf(ctx, performerID) r0 = rf(ctx, performerID)
} else { } else {
if ret.Get(0) != nil { if ret.Get(0) != nil {
r0 = ret.Get(0).([]*models.Movie) r0 = ret.Get(0).([]*models.Group)
} }
} }
@@ -221,15 +221,15 @@ func (_m *MovieReaderWriter) FindByPerformerID(ctx context.Context, performerID
} }
// FindByStudioID provides a mock function with given fields: ctx, studioID // FindByStudioID provides a mock function with given fields: ctx, studioID
func (_m *MovieReaderWriter) FindByStudioID(ctx context.Context, studioID int) ([]*models.Movie, error) { func (_m *GroupReaderWriter) FindByStudioID(ctx context.Context, studioID int) ([]*models.Group, error) {
ret := _m.Called(ctx, studioID) ret := _m.Called(ctx, studioID)
var r0 []*models.Movie var r0 []*models.Group
if rf, ok := ret.Get(0).(func(context.Context, int) []*models.Movie); ok { if rf, ok := ret.Get(0).(func(context.Context, int) []*models.Group); ok {
r0 = rf(ctx, studioID) r0 = rf(ctx, studioID)
} else { } else {
if ret.Get(0) != nil { if ret.Get(0) != nil {
r0 = ret.Get(0).([]*models.Movie) r0 = ret.Get(0).([]*models.Group)
} }
} }
@@ -244,15 +244,15 @@ func (_m *MovieReaderWriter) FindByStudioID(ctx context.Context, studioID int) (
} }
// FindMany provides a mock function with given fields: ctx, ids // FindMany provides a mock function with given fields: ctx, ids
func (_m *MovieReaderWriter) FindMany(ctx context.Context, ids []int) ([]*models.Movie, error) { func (_m *GroupReaderWriter) FindMany(ctx context.Context, ids []int) ([]*models.Group, error) {
ret := _m.Called(ctx, ids) ret := _m.Called(ctx, ids)
var r0 []*models.Movie var r0 []*models.Group
if rf, ok := ret.Get(0).(func(context.Context, []int) []*models.Movie); ok { if rf, ok := ret.Get(0).(func(context.Context, []int) []*models.Group); ok {
r0 = rf(ctx, ids) r0 = rf(ctx, ids)
} else { } else {
if ret.Get(0) != nil { if ret.Get(0) != nil {
r0 = ret.Get(0).([]*models.Movie) r0 = ret.Get(0).([]*models.Group)
} }
} }
@@ -266,13 +266,13 @@ func (_m *MovieReaderWriter) FindMany(ctx context.Context, ids []int) ([]*models
return r0, r1 return r0, r1
} }
// GetBackImage provides a mock function with given fields: ctx, movieID // GetBackImage provides a mock function with given fields: ctx, groupID
func (_m *MovieReaderWriter) GetBackImage(ctx context.Context, movieID int) ([]byte, error) { func (_m *GroupReaderWriter) GetBackImage(ctx context.Context, groupID int) ([]byte, error) {
ret := _m.Called(ctx, movieID) ret := _m.Called(ctx, groupID)
var r0 []byte var r0 []byte
if rf, ok := ret.Get(0).(func(context.Context, int) []byte); ok { if rf, ok := ret.Get(0).(func(context.Context, int) []byte); ok {
r0 = rf(ctx, movieID) r0 = rf(ctx, groupID)
} else { } else {
if ret.Get(0) != nil { if ret.Get(0) != nil {
r0 = ret.Get(0).([]byte) r0 = ret.Get(0).([]byte)
@@ -281,7 +281,7 @@ func (_m *MovieReaderWriter) GetBackImage(ctx context.Context, movieID int) ([]b
var r1 error var r1 error
if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { if rf, ok := ret.Get(1).(func(context.Context, int) error); ok {
r1 = rf(ctx, movieID) r1 = rf(ctx, groupID)
} else { } else {
r1 = ret.Error(1) r1 = ret.Error(1)
} }
@@ -289,13 +289,13 @@ func (_m *MovieReaderWriter) GetBackImage(ctx context.Context, movieID int) ([]b
return r0, r1 return r0, r1
} }
// GetFrontImage provides a mock function with given fields: ctx, movieID // GetFrontImage provides a mock function with given fields: ctx, groupID
func (_m *MovieReaderWriter) GetFrontImage(ctx context.Context, movieID int) ([]byte, error) { func (_m *GroupReaderWriter) GetFrontImage(ctx context.Context, groupID int) ([]byte, error) {
ret := _m.Called(ctx, movieID) ret := _m.Called(ctx, groupID)
var r0 []byte var r0 []byte
if rf, ok := ret.Get(0).(func(context.Context, int) []byte); ok { if rf, ok := ret.Get(0).(func(context.Context, int) []byte); ok {
r0 = rf(ctx, movieID) r0 = rf(ctx, groupID)
} else { } else {
if ret.Get(0) != nil { if ret.Get(0) != nil {
r0 = ret.Get(0).([]byte) r0 = ret.Get(0).([]byte)
@@ -304,7 +304,7 @@ func (_m *MovieReaderWriter) GetFrontImage(ctx context.Context, movieID int) ([]
var r1 error var r1 error
if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { if rf, ok := ret.Get(1).(func(context.Context, int) error); ok {
r1 = rf(ctx, movieID) r1 = rf(ctx, groupID)
} else { } else {
r1 = ret.Error(1) r1 = ret.Error(1)
} }
@@ -313,7 +313,7 @@ func (_m *MovieReaderWriter) GetFrontImage(ctx context.Context, movieID int) ([]
} }
// GetTagIDs provides a mock function with given fields: ctx, relatedID // GetTagIDs provides a mock function with given fields: ctx, relatedID
func (_m *MovieReaderWriter) GetTagIDs(ctx context.Context, relatedID int) ([]int, error) { func (_m *GroupReaderWriter) GetTagIDs(ctx context.Context, relatedID int) ([]int, error) {
ret := _m.Called(ctx, relatedID) ret := _m.Called(ctx, relatedID)
var r0 []int var r0 []int
@@ -336,7 +336,7 @@ func (_m *MovieReaderWriter) GetTagIDs(ctx context.Context, relatedID int) ([]in
} }
// GetURLs provides a mock function with given fields: ctx, relatedID // GetURLs provides a mock function with given fields: ctx, relatedID
func (_m *MovieReaderWriter) GetURLs(ctx context.Context, relatedID int) ([]string, error) { func (_m *GroupReaderWriter) GetURLs(ctx context.Context, relatedID int) ([]string, error) {
ret := _m.Called(ctx, relatedID) ret := _m.Called(ctx, relatedID)
var r0 []string var r0 []string
@@ -358,20 +358,20 @@ func (_m *MovieReaderWriter) GetURLs(ctx context.Context, relatedID int) ([]stri
return r0, r1 return r0, r1
} }
// HasBackImage provides a mock function with given fields: ctx, movieID // HasBackImage provides a mock function with given fields: ctx, groupID
func (_m *MovieReaderWriter) HasBackImage(ctx context.Context, movieID int) (bool, error) { func (_m *GroupReaderWriter) HasBackImage(ctx context.Context, groupID int) (bool, error) {
ret := _m.Called(ctx, movieID) ret := _m.Called(ctx, groupID)
var r0 bool var r0 bool
if rf, ok := ret.Get(0).(func(context.Context, int) bool); ok { if rf, ok := ret.Get(0).(func(context.Context, int) bool); ok {
r0 = rf(ctx, movieID) r0 = rf(ctx, groupID)
} else { } else {
r0 = ret.Get(0).(bool) r0 = ret.Get(0).(bool)
} }
var r1 error var r1 error
if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { if rf, ok := ret.Get(1).(func(context.Context, int) error); ok {
r1 = rf(ctx, movieID) r1 = rf(ctx, groupID)
} else { } else {
r1 = ret.Error(1) r1 = ret.Error(1)
} }
@@ -379,20 +379,20 @@ func (_m *MovieReaderWriter) HasBackImage(ctx context.Context, movieID int) (boo
return r0, r1 return r0, r1
} }
// HasFrontImage provides a mock function with given fields: ctx, movieID // HasFrontImage provides a mock function with given fields: ctx, groupID
func (_m *MovieReaderWriter) HasFrontImage(ctx context.Context, movieID int) (bool, error) { func (_m *GroupReaderWriter) HasFrontImage(ctx context.Context, groupID int) (bool, error) {
ret := _m.Called(ctx, movieID) ret := _m.Called(ctx, groupID)
var r0 bool var r0 bool
if rf, ok := ret.Get(0).(func(context.Context, int) bool); ok { if rf, ok := ret.Get(0).(func(context.Context, int) bool); ok {
r0 = rf(ctx, movieID) r0 = rf(ctx, groupID)
} else { } else {
r0 = ret.Get(0).(bool) r0 = ret.Get(0).(bool)
} }
var r1 error var r1 error
if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { if rf, ok := ret.Get(1).(func(context.Context, int) error); ok {
r1 = rf(ctx, movieID) r1 = rf(ctx, groupID)
} else { } else {
r1 = ret.Error(1) r1 = ret.Error(1)
} }
@@ -400,29 +400,29 @@ func (_m *MovieReaderWriter) HasFrontImage(ctx context.Context, movieID int) (bo
return r0, r1 return r0, r1
} }
// Query provides a mock function with given fields: ctx, movieFilter, findFilter // Query provides a mock function with given fields: ctx, groupFilter, findFilter
func (_m *MovieReaderWriter) Query(ctx context.Context, movieFilter *models.MovieFilterType, findFilter *models.FindFilterType) ([]*models.Movie, int, error) { func (_m *GroupReaderWriter) Query(ctx context.Context, groupFilter *models.GroupFilterType, findFilter *models.FindFilterType) ([]*models.Group, int, error) {
ret := _m.Called(ctx, movieFilter, findFilter) ret := _m.Called(ctx, groupFilter, findFilter)
var r0 []*models.Movie var r0 []*models.Group
if rf, ok := ret.Get(0).(func(context.Context, *models.MovieFilterType, *models.FindFilterType) []*models.Movie); ok { if rf, ok := ret.Get(0).(func(context.Context, *models.GroupFilterType, *models.FindFilterType) []*models.Group); ok {
r0 = rf(ctx, movieFilter, findFilter) r0 = rf(ctx, groupFilter, findFilter)
} else { } else {
if ret.Get(0) != nil { if ret.Get(0) != nil {
r0 = ret.Get(0).([]*models.Movie) r0 = ret.Get(0).([]*models.Group)
} }
} }
var r1 int var r1 int
if rf, ok := ret.Get(1).(func(context.Context, *models.MovieFilterType, *models.FindFilterType) int); ok { if rf, ok := ret.Get(1).(func(context.Context, *models.GroupFilterType, *models.FindFilterType) int); ok {
r1 = rf(ctx, movieFilter, findFilter) r1 = rf(ctx, groupFilter, findFilter)
} else { } else {
r1 = ret.Get(1).(int) r1 = ret.Get(1).(int)
} }
var r2 error var r2 error
if rf, ok := ret.Get(2).(func(context.Context, *models.MovieFilterType, *models.FindFilterType) error); ok { if rf, ok := ret.Get(2).(func(context.Context, *models.GroupFilterType, *models.FindFilterType) error); ok {
r2 = rf(ctx, movieFilter, findFilter) r2 = rf(ctx, groupFilter, findFilter)
} else { } else {
r2 = ret.Error(2) r2 = ret.Error(2)
} }
@@ -430,20 +430,20 @@ func (_m *MovieReaderWriter) Query(ctx context.Context, movieFilter *models.Movi
return r0, r1, r2 return r0, r1, r2
} }
// QueryCount provides a mock function with given fields: ctx, movieFilter, findFilter // QueryCount provides a mock function with given fields: ctx, groupFilter, findFilter
func (_m *MovieReaderWriter) QueryCount(ctx context.Context, movieFilter *models.MovieFilterType, findFilter *models.FindFilterType) (int, error) { func (_m *GroupReaderWriter) QueryCount(ctx context.Context, groupFilter *models.GroupFilterType, findFilter *models.FindFilterType) (int, error) {
ret := _m.Called(ctx, movieFilter, findFilter) ret := _m.Called(ctx, groupFilter, findFilter)
var r0 int var r0 int
if rf, ok := ret.Get(0).(func(context.Context, *models.MovieFilterType, *models.FindFilterType) int); ok { if rf, ok := ret.Get(0).(func(context.Context, *models.GroupFilterType, *models.FindFilterType) int); ok {
r0 = rf(ctx, movieFilter, findFilter) r0 = rf(ctx, groupFilter, findFilter)
} else { } else {
r0 = ret.Get(0).(int) r0 = ret.Get(0).(int)
} }
var r1 error var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *models.MovieFilterType, *models.FindFilterType) error); ok { if rf, ok := ret.Get(1).(func(context.Context, *models.GroupFilterType, *models.FindFilterType) error); ok {
r1 = rf(ctx, movieFilter, findFilter) r1 = rf(ctx, groupFilter, findFilter)
} else { } else {
r1 = ret.Error(1) r1 = ret.Error(1)
} }
@@ -451,13 +451,13 @@ func (_m *MovieReaderWriter) QueryCount(ctx context.Context, movieFilter *models
return r0, r1 return r0, r1
} }
// Update provides a mock function with given fields: ctx, updatedMovie // Update provides a mock function with given fields: ctx, updatedGroup
func (_m *MovieReaderWriter) Update(ctx context.Context, updatedMovie *models.Movie) error { func (_m *GroupReaderWriter) Update(ctx context.Context, updatedGroup *models.Group) error {
ret := _m.Called(ctx, updatedMovie) ret := _m.Called(ctx, updatedGroup)
var r0 error var r0 error
if rf, ok := ret.Get(0).(func(context.Context, *models.Movie) error); ok { if rf, ok := ret.Get(0).(func(context.Context, *models.Group) error); ok {
r0 = rf(ctx, updatedMovie) r0 = rf(ctx, updatedGroup)
} else { } else {
r0 = ret.Error(0) r0 = ret.Error(0)
} }
@@ -465,13 +465,13 @@ func (_m *MovieReaderWriter) Update(ctx context.Context, updatedMovie *models.Mo
return r0 return r0
} }
// UpdateBackImage provides a mock function with given fields: ctx, movieID, backImage // UpdateBackImage provides a mock function with given fields: ctx, groupID, backImage
func (_m *MovieReaderWriter) UpdateBackImage(ctx context.Context, movieID int, backImage []byte) error { func (_m *GroupReaderWriter) UpdateBackImage(ctx context.Context, groupID int, backImage []byte) error {
ret := _m.Called(ctx, movieID, backImage) ret := _m.Called(ctx, groupID, backImage)
var r0 error var r0 error
if rf, ok := ret.Get(0).(func(context.Context, int, []byte) error); ok { if rf, ok := ret.Get(0).(func(context.Context, int, []byte) error); ok {
r0 = rf(ctx, movieID, backImage) r0 = rf(ctx, groupID, backImage)
} else { } else {
r0 = ret.Error(0) r0 = ret.Error(0)
} }
@@ -479,13 +479,13 @@ func (_m *MovieReaderWriter) UpdateBackImage(ctx context.Context, movieID int, b
return r0 return r0
} }
// UpdateFrontImage provides a mock function with given fields: ctx, movieID, frontImage // UpdateFrontImage provides a mock function with given fields: ctx, groupID, frontImage
func (_m *MovieReaderWriter) UpdateFrontImage(ctx context.Context, movieID int, frontImage []byte) error { func (_m *GroupReaderWriter) UpdateFrontImage(ctx context.Context, groupID int, frontImage []byte) error {
ret := _m.Called(ctx, movieID, frontImage) ret := _m.Called(ctx, groupID, frontImage)
var r0 error var r0 error
if rf, ok := ret.Get(0).(func(context.Context, int, []byte) error); ok { if rf, ok := ret.Get(0).(func(context.Context, int, []byte) error); ok {
r0 = rf(ctx, movieID, frontImage) r0 = rf(ctx, groupID, frontImage)
} else { } else {
r0 = ret.Error(0) r0 = ret.Error(0)
} }
@@ -493,22 +493,22 @@ func (_m *MovieReaderWriter) UpdateFrontImage(ctx context.Context, movieID int,
return r0 return r0
} }
// UpdatePartial provides a mock function with given fields: ctx, id, updatedMovie // UpdatePartial provides a mock function with given fields: ctx, id, updatedGroup
func (_m *MovieReaderWriter) UpdatePartial(ctx context.Context, id int, updatedMovie models.MoviePartial) (*models.Movie, error) { func (_m *GroupReaderWriter) UpdatePartial(ctx context.Context, id int, updatedGroup models.GroupPartial) (*models.Group, error) {
ret := _m.Called(ctx, id, updatedMovie) ret := _m.Called(ctx, id, updatedGroup)
var r0 *models.Movie var r0 *models.Group
if rf, ok := ret.Get(0).(func(context.Context, int, models.MoviePartial) *models.Movie); ok { if rf, ok := ret.Get(0).(func(context.Context, int, models.GroupPartial) *models.Group); ok {
r0 = rf(ctx, id, updatedMovie) r0 = rf(ctx, id, updatedGroup)
} else { } else {
if ret.Get(0) != nil { if ret.Get(0) != nil {
r0 = ret.Get(0).(*models.Movie) r0 = ret.Get(0).(*models.Group)
} }
} }
var r1 error var r1 error
if rf, ok := ret.Get(1).(func(context.Context, int, models.MoviePartial) error); ok { if rf, ok := ret.Get(1).(func(context.Context, int, models.GroupPartial) error); ok {
r1 = rf(ctx, id, updatedMovie) r1 = rf(ctx, id, updatedGroup)
} else { } else {
r1 = ret.Error(1) r1 = ret.Error(1)
} }

View File

@@ -111,29 +111,6 @@ func (_m *SavedFilterReaderWriter) FindByMode(ctx context.Context, mode models.F
return r0, r1 return r0, r1
} }
// FindDefault provides a mock function with given fields: ctx, mode
func (_m *SavedFilterReaderWriter) FindDefault(ctx context.Context, mode models.FilterMode) (*models.SavedFilter, error) {
ret := _m.Called(ctx, mode)
var r0 *models.SavedFilter
if rf, ok := ret.Get(0).(func(context.Context, models.FilterMode) *models.SavedFilter); ok {
r0 = rf(ctx, mode)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*models.SavedFilter)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, models.FilterMode) error); ok {
r1 = rf(ctx, mode)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// FindMany provides a mock function with given fields: ctx, ids, ignoreNotFound // FindMany provides a mock function with given fields: ctx, ids, ignoreNotFound
func (_m *SavedFilterReaderWriter) FindMany(ctx context.Context, ids []int, ignoreNotFound bool) ([]*models.SavedFilter, error) { func (_m *SavedFilterReaderWriter) FindMany(ctx context.Context, ids []int, ignoreNotFound bool) ([]*models.SavedFilter, error) {
ret := _m.Called(ctx, ids, ignoreNotFound) ret := _m.Called(ctx, ids, ignoreNotFound)
@@ -157,20 +134,6 @@ func (_m *SavedFilterReaderWriter) FindMany(ctx context.Context, ids []int, igno
return r0, r1 return r0, r1
} }
// SetDefault provides a mock function with given fields: ctx, obj
func (_m *SavedFilterReaderWriter) SetDefault(ctx context.Context, obj *models.SavedFilter) error {
ret := _m.Called(ctx, obj)
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, *models.SavedFilter) error); ok {
r0 = rf(ctx, obj)
} else {
r0 = ret.Error(0)
}
return r0
}
// Update provides a mock function with given fields: ctx, obj // Update provides a mock function with given fields: ctx, obj
func (_m *SavedFilterReaderWriter) Update(ctx context.Context, obj *models.SavedFilter) error { func (_m *SavedFilterReaderWriter) Update(ctx context.Context, obj *models.SavedFilter) error {
ret := _m.Called(ctx, obj) ret := _m.Called(ctx, obj)

View File

@@ -190,20 +190,20 @@ func (_m *SceneReaderWriter) CountByFileID(ctx context.Context, fileID models.Fi
return r0, r1 return r0, r1
} }
// CountByMovieID provides a mock function with given fields: ctx, movieID // CountByGroupID provides a mock function with given fields: ctx, groupID
func (_m *SceneReaderWriter) CountByMovieID(ctx context.Context, movieID int) (int, error) { func (_m *SceneReaderWriter) CountByGroupID(ctx context.Context, groupID int) (int, error) {
ret := _m.Called(ctx, movieID) ret := _m.Called(ctx, groupID)
var r0 int var r0 int
if rf, ok := ret.Get(0).(func(context.Context, int) int); ok { if rf, ok := ret.Get(0).(func(context.Context, int) int); ok {
r0 = rf(ctx, movieID) r0 = rf(ctx, groupID)
} else { } else {
r0 = ret.Get(0).(int) r0 = ret.Get(0).(int)
} }
var r1 error var r1 error
if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { if rf, ok := ret.Get(1).(func(context.Context, int) error); ok {
r1 = rf(ctx, movieID) r1 = rf(ctx, groupID)
} else { } else {
r1 = ret.Error(1) r1 = ret.Error(1)
} }
@@ -589,13 +589,13 @@ func (_m *SceneReaderWriter) FindByGalleryID(ctx context.Context, performerID in
return r0, r1 return r0, r1
} }
// FindByMovieID provides a mock function with given fields: ctx, movieID // FindByGroupID provides a mock function with given fields: ctx, groupID
func (_m *SceneReaderWriter) FindByMovieID(ctx context.Context, movieID int) ([]*models.Scene, error) { func (_m *SceneReaderWriter) FindByGroupID(ctx context.Context, groupID int) ([]*models.Scene, error) {
ret := _m.Called(ctx, movieID) ret := _m.Called(ctx, groupID)
var r0 []*models.Scene var r0 []*models.Scene
if rf, ok := ret.Get(0).(func(context.Context, int) []*models.Scene); ok { if rf, ok := ret.Get(0).(func(context.Context, int) []*models.Scene); ok {
r0 = rf(ctx, movieID) r0 = rf(ctx, groupID)
} else { } else {
if ret.Get(0) != nil { if ret.Get(0) != nil {
r0 = ret.Get(0).([]*models.Scene) r0 = ret.Get(0).([]*models.Scene)
@@ -604,7 +604,7 @@ func (_m *SceneReaderWriter) FindByMovieID(ctx context.Context, movieID int) ([]
var r1 error var r1 error
if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { if rf, ok := ret.Get(1).(func(context.Context, int) error); ok {
r1 = rf(ctx, movieID) r1 = rf(ctx, groupID)
} else { } else {
r1 = ret.Error(1) r1 = ret.Error(1)
} }
@@ -840,6 +840,29 @@ func (_m *SceneReaderWriter) GetGalleryIDs(ctx context.Context, relatedID int) (
return r0, r1 return r0, r1
} }
// GetGroups provides a mock function with given fields: ctx, id
func (_m *SceneReaderWriter) GetGroups(ctx context.Context, id int) ([]models.GroupsScenes, error) {
ret := _m.Called(ctx, id)
var r0 []models.GroupsScenes
if rf, ok := ret.Get(0).(func(context.Context, int) []models.GroupsScenes); ok {
r0 = rf(ctx, id)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]models.GroupsScenes)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, int) error); ok {
r1 = rf(ctx, id)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// GetManyFileIDs provides a mock function with given fields: ctx, ids // GetManyFileIDs provides a mock function with given fields: ctx, ids
func (_m *SceneReaderWriter) GetManyFileIDs(ctx context.Context, ids []int) ([][]models.FileID, error) { func (_m *SceneReaderWriter) GetManyFileIDs(ctx context.Context, ids []int) ([][]models.FileID, error) {
ret := _m.Called(ctx, ids) ret := _m.Called(ctx, ids)
@@ -978,29 +1001,6 @@ func (_m *SceneReaderWriter) GetManyViewDates(ctx context.Context, ids []int) ([
return r0, r1 return r0, r1
} }
// GetMovies provides a mock function with given fields: ctx, id
func (_m *SceneReaderWriter) GetMovies(ctx context.Context, id int) ([]models.MoviesScenes, error) {
ret := _m.Called(ctx, id)
var r0 []models.MoviesScenes
if rf, ok := ret.Get(0).(func(context.Context, int) []models.MoviesScenes); ok {
r0 = rf(ctx, id)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]models.MoviesScenes)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, int) error); ok {
r1 = rf(ctx, id)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// GetOCount provides a mock function with given fields: ctx, id // GetOCount provides a mock function with given fields: ctx, id
func (_m *SceneReaderWriter) GetOCount(ctx context.Context, id int) (int, error) { func (_m *SceneReaderWriter) GetOCount(ctx context.Context, id int) (int, error) {
ret := _m.Called(ctx, id) ret := _m.Called(ctx, id)

View File

@@ -243,6 +243,29 @@ func (_m *TagReaderWriter) FindByGalleryID(ctx context.Context, galleryID int) (
return r0, r1 return r0, r1
} }
// FindByGroupID provides a mock function with given fields: ctx, groupID
func (_m *TagReaderWriter) FindByGroupID(ctx context.Context, groupID int) ([]*models.Tag, error) {
ret := _m.Called(ctx, groupID)
var r0 []*models.Tag
if rf, ok := ret.Get(0).(func(context.Context, int) []*models.Tag); ok {
r0 = rf(ctx, groupID)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]*models.Tag)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, int) error); ok {
r1 = rf(ctx, groupID)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// FindByImageID provides a mock function with given fields: ctx, imageID // FindByImageID provides a mock function with given fields: ctx, imageID
func (_m *TagReaderWriter) FindByImageID(ctx context.Context, imageID int) ([]*models.Tag, error) { func (_m *TagReaderWriter) FindByImageID(ctx context.Context, imageID int) ([]*models.Tag, error) {
ret := _m.Called(ctx, imageID) ret := _m.Called(ctx, imageID)
@@ -266,29 +289,6 @@ func (_m *TagReaderWriter) FindByImageID(ctx context.Context, imageID int) ([]*m
return r0, r1 return r0, r1
} }
// FindByMovieID provides a mock function with given fields: ctx, movieID
func (_m *TagReaderWriter) FindByMovieID(ctx context.Context, movieID int) ([]*models.Tag, error) {
ret := _m.Called(ctx, movieID)
var r0 []*models.Tag
if rf, ok := ret.Get(0).(func(context.Context, int) []*models.Tag); ok {
r0 = rf(ctx, movieID)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]*models.Tag)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, int) error); ok {
r1 = rf(ctx, movieID)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// FindByName provides a mock function with given fields: ctx, name, nocase // FindByName provides a mock function with given fields: ctx, name, nocase
func (_m *TagReaderWriter) FindByName(ctx context.Context, name string, nocase bool) (*models.Tag, error) { func (_m *TagReaderWriter) FindByName(ctx context.Context, name string, nocase bool) (*models.Tag, error) {
ret := _m.Called(ctx, name, nocase) ret := _m.Called(ctx, name, nocase)

View File

@@ -14,7 +14,7 @@ type Database struct {
Gallery *GalleryReaderWriter Gallery *GalleryReaderWriter
GalleryChapter *GalleryChapterReaderWriter GalleryChapter *GalleryChapterReaderWriter
Image *ImageReaderWriter Image *ImageReaderWriter
Movie *MovieReaderWriter Group *GroupReaderWriter
Performer *PerformerReaderWriter Performer *PerformerReaderWriter
Scene *SceneReaderWriter Scene *SceneReaderWriter
SceneMarker *SceneMarkerReaderWriter SceneMarker *SceneMarkerReaderWriter
@@ -63,7 +63,7 @@ func NewDatabase() *Database {
Gallery: &GalleryReaderWriter{}, Gallery: &GalleryReaderWriter{},
GalleryChapter: &GalleryChapterReaderWriter{}, GalleryChapter: &GalleryChapterReaderWriter{},
Image: &ImageReaderWriter{}, Image: &ImageReaderWriter{},
Movie: &MovieReaderWriter{}, Group: &GroupReaderWriter{},
Performer: &PerformerReaderWriter{}, Performer: &PerformerReaderWriter{},
Scene: &SceneReaderWriter{}, Scene: &SceneReaderWriter{},
SceneMarker: &SceneMarkerReaderWriter{}, SceneMarker: &SceneMarkerReaderWriter{},
@@ -79,7 +79,7 @@ func (db *Database) AssertExpectations(t mock.TestingT) {
db.Gallery.AssertExpectations(t) db.Gallery.AssertExpectations(t)
db.GalleryChapter.AssertExpectations(t) db.GalleryChapter.AssertExpectations(t)
db.Image.AssertExpectations(t) db.Image.AssertExpectations(t)
db.Movie.AssertExpectations(t) db.Group.AssertExpectations(t)
db.Performer.AssertExpectations(t) db.Performer.AssertExpectations(t)
db.Scene.AssertExpectations(t) db.Scene.AssertExpectations(t)
db.SceneMarker.AssertExpectations(t) db.SceneMarker.AssertExpectations(t)
@@ -96,7 +96,7 @@ func (db *Database) Repository() models.Repository {
Gallery: db.Gallery, Gallery: db.Gallery,
GalleryChapter: db.GalleryChapter, GalleryChapter: db.GalleryChapter,
Image: db.Image, Image: db.Image,
Movie: db.Movie, Group: db.Group,
Performer: db.Performer, Performer: db.Performer,
Scene: db.Scene, Scene: db.Scene,
SceneMarker: db.SceneMarker, SceneMarker: db.SceneMarker,

View File

@@ -5,54 +5,54 @@ import (
"strconv" "strconv"
) )
type MoviesScenes struct { type GroupsScenes struct {
MovieID int `json:"movie_id"` GroupID int `json:"movie_id"`
// SceneID int `json:"scene_id"` // SceneID int `json:"scene_id"`
SceneIndex *int `json:"scene_index"` SceneIndex *int `json:"scene_index"`
} }
func (s MoviesScenes) SceneMovieInput() SceneMovieInput { func (s GroupsScenes) SceneMovieInput() SceneMovieInput {
return SceneMovieInput{ return SceneMovieInput{
MovieID: strconv.Itoa(s.MovieID), MovieID: strconv.Itoa(s.GroupID),
SceneIndex: s.SceneIndex, SceneIndex: s.SceneIndex,
} }
} }
func (s MoviesScenes) Equal(o MoviesScenes) bool { func (s GroupsScenes) Equal(o GroupsScenes) bool {
return o.MovieID == s.MovieID && ((o.SceneIndex == nil && s.SceneIndex == nil) || return o.GroupID == s.GroupID && ((o.SceneIndex == nil && s.SceneIndex == nil) ||
(o.SceneIndex != nil && s.SceneIndex != nil && *o.SceneIndex == *s.SceneIndex)) (o.SceneIndex != nil && s.SceneIndex != nil && *o.SceneIndex == *s.SceneIndex))
} }
type UpdateMovieIDs struct { type UpdateGroupIDs struct {
Movies []MoviesScenes `json:"movies"` Groups []GroupsScenes `json:"movies"`
Mode RelationshipUpdateMode `json:"mode"` Mode RelationshipUpdateMode `json:"mode"`
} }
func (u *UpdateMovieIDs) SceneMovieInputs() []SceneMovieInput { func (u *UpdateGroupIDs) SceneMovieInputs() []SceneMovieInput {
if u == nil { if u == nil {
return nil return nil
} }
ret := make([]SceneMovieInput, len(u.Movies)) ret := make([]SceneMovieInput, len(u.Groups))
for _, id := range u.Movies { for _, id := range u.Groups {
ret = append(ret, id.SceneMovieInput()) ret = append(ret, id.SceneMovieInput())
} }
return ret return ret
} }
func (u *UpdateMovieIDs) AddUnique(v MoviesScenes) { func (u *UpdateGroupIDs) AddUnique(v GroupsScenes) {
for _, vv := range u.Movies { for _, vv := range u.Groups {
if vv.MovieID == v.MovieID { if vv.GroupID == v.GroupID {
return return
} }
} }
u.Movies = append(u.Movies, v) u.Groups = append(u.Groups, v)
} }
func MoviesScenesFromInput(input []SceneMovieInput) ([]MoviesScenes, error) { func GroupsScenesFromInput(input []SceneMovieInput) ([]GroupsScenes, error) {
ret := make([]MoviesScenes, len(input)) ret := make([]GroupsScenes, len(input))
for i, v := range input { for i, v := range input {
mID, err := strconv.Atoi(v.MovieID) mID, err := strconv.Atoi(v.MovieID)
@@ -60,8 +60,8 @@ func MoviesScenesFromInput(input []SceneMovieInput) ([]MoviesScenes, error) {
return nil, fmt.Errorf("invalid movie ID: %s", v.MovieID) return nil, fmt.Errorf("invalid movie ID: %s", v.MovieID)
} }
ret[i] = MoviesScenes{ ret[i] = GroupsScenes{
MovieID: mID, GroupID: mID,
SceneIndex: v.SceneIndex, SceneIndex: v.SceneIndex,
} }
} }

View File

@@ -5,7 +5,7 @@ import (
"time" "time"
) )
type Movie struct { type Group struct {
ID int `json:"id"` ID int `json:"id"`
Name string `json:"name"` Name string `json:"name"`
Aliases string `json:"aliases"` Aliases string `json:"aliases"`
@@ -23,27 +23,27 @@ type Movie struct {
TagIDs RelatedIDs `json:"tag_ids"` TagIDs RelatedIDs `json:"tag_ids"`
} }
func NewMovie() Movie { func NewGroup() Group {
currentTime := time.Now() currentTime := time.Now()
return Movie{ return Group{
CreatedAt: currentTime, CreatedAt: currentTime,
UpdatedAt: currentTime, UpdatedAt: currentTime,
} }
} }
func (m *Movie) LoadURLs(ctx context.Context, l URLLoader) error { func (m *Group) LoadURLs(ctx context.Context, l URLLoader) error {
return m.URLs.load(func() ([]string, error) { return m.URLs.load(func() ([]string, error) {
return l.GetURLs(ctx, m.ID) return l.GetURLs(ctx, m.ID)
}) })
} }
func (m *Movie) LoadTagIDs(ctx context.Context, l TagIDLoader) error { func (m *Group) LoadTagIDs(ctx context.Context, l TagIDLoader) error {
return m.TagIDs.load(func() ([]int, error) { return m.TagIDs.load(func() ([]int, error) {
return l.GetTagIDs(ctx, m.ID) return l.GetTagIDs(ctx, m.ID)
}) })
} }
type MoviePartial struct { type GroupPartial struct {
Name OptionalString Name OptionalString
Aliases OptionalString Aliases OptionalString
Duration OptionalInt Duration OptionalInt
@@ -59,9 +59,9 @@ type MoviePartial struct {
UpdatedAt OptionalTime UpdatedAt OptionalTime
} }
func NewMoviePartial() MoviePartial { func NewGroupPartial() GroupPartial {
currentTime := time.Now() currentTime := time.Now()
return MoviePartial{ return GroupPartial{
UpdatedAt: NewOptionalTime(currentTime), UpdatedAt: NewOptionalTime(currentTime),
} }
} }

View File

@@ -41,7 +41,7 @@ type Scene struct {
GalleryIDs RelatedIDs `json:"gallery_ids"` GalleryIDs RelatedIDs `json:"gallery_ids"`
TagIDs RelatedIDs `json:"tag_ids"` TagIDs RelatedIDs `json:"tag_ids"`
PerformerIDs RelatedIDs `json:"performer_ids"` PerformerIDs RelatedIDs `json:"performer_ids"`
Movies RelatedMovies `json:"movies"` Groups RelatedGroups `json:"groups"`
StashIDs RelatedStashIDs `json:"stash_ids"` StashIDs RelatedStashIDs `json:"stash_ids"`
} }
@@ -74,7 +74,7 @@ type ScenePartial struct {
GalleryIDs *UpdateIDs GalleryIDs *UpdateIDs
TagIDs *UpdateIDs TagIDs *UpdateIDs
PerformerIDs *UpdateIDs PerformerIDs *UpdateIDs
MovieIDs *UpdateMovieIDs GroupIDs *UpdateGroupIDs
StashIDs *UpdateStashIDs StashIDs *UpdateStashIDs
PrimaryFileID *FileID PrimaryFileID *FileID
} }
@@ -139,9 +139,9 @@ func (s *Scene) LoadTagIDs(ctx context.Context, l TagIDLoader) error {
}) })
} }
func (s *Scene) LoadMovies(ctx context.Context, l SceneMovieLoader) error { func (s *Scene) LoadGroups(ctx context.Context, l SceneGroupLoader) error {
return s.Movies.load(func() ([]MoviesScenes, error) { return s.Groups.load(func() ([]GroupsScenes, error) {
return l.GetMovies(ctx, s.ID) return l.GetGroups(ctx, s.ID)
}) })
} }
@@ -168,7 +168,7 @@ func (s *Scene) LoadRelationships(ctx context.Context, l SceneReader) error {
return err return err
} }
if err := s.LoadMovies(ctx, l); err != nil { if err := s.LoadGroups(ctx, l); err != nil {
return err return err
} }
@@ -210,7 +210,7 @@ func (s ScenePartial) UpdateInput(id int) SceneUpdateInput {
StudioID: s.StudioID.StringPtr(), StudioID: s.StudioID.StringPtr(),
GalleryIds: s.GalleryIDs.IDStrings(), GalleryIds: s.GalleryIDs.IDStrings(),
PerformerIds: s.PerformerIDs.IDStrings(), PerformerIds: s.PerformerIDs.IDStrings(),
Movies: s.MovieIDs.SceneMovieInputs(), Movies: s.GroupIDs.SceneMovieInputs(),
TagIds: s.TagIDs.IDStrings(), TagIds: s.TagIDs.IDStrings(),
StashIds: stashIDs, StashIds: stashIDs,
} }

View File

@@ -415,6 +415,30 @@ type ScrapedMovie struct {
func (ScrapedMovie) IsScrapedContent() {} func (ScrapedMovie) IsScrapedContent() {}
func (m ScrapedMovie) ScrapedGroup() ScrapedGroup {
ret := ScrapedGroup{
StoredID: m.StoredID,
Name: m.Name,
Aliases: m.Aliases,
Duration: m.Duration,
Date: m.Date,
Rating: m.Rating,
Director: m.Director,
URLs: m.URLs,
Synopsis: m.Synopsis,
Studio: m.Studio,
Tags: m.Tags,
FrontImage: m.FrontImage,
BackImage: m.BackImage,
}
if len(m.URLs) == 0 && m.URL != nil {
ret.URLs = []string{*m.URL}
}
return ret
}
// ScrapedGroup is a group from a scraping operation // ScrapedGroup is a group from a scraping operation
type ScrapedGroup struct { type ScrapedGroup struct {
StoredID *string `json:"stored_id"` StoredID *string `json:"stored_id"`
@@ -435,3 +459,27 @@ type ScrapedGroup struct {
} }
func (ScrapedGroup) IsScrapedContent() {} func (ScrapedGroup) IsScrapedContent() {}
func (g ScrapedGroup) ScrapedMovie() ScrapedMovie {
ret := ScrapedMovie{
StoredID: g.StoredID,
Name: g.Name,
Aliases: g.Aliases,
Duration: g.Duration,
Date: g.Date,
Rating: g.Rating,
Director: g.Director,
URLs: g.URLs,
Synopsis: g.Synopsis,
Studio: g.Studio,
Tags: g.Tags,
FrontImage: g.FrontImage,
BackImage: g.BackImage,
}
if len(g.URLs) > 0 {
ret.URL = &g.URLs[0]
}
return ret
}

View File

@@ -1,7 +1,7 @@
package models package models
type MovieFilterType struct { type GroupFilterType struct {
OperatorFilter[MovieFilterType] OperatorFilter[GroupFilterType]
Name *StringCriterionInput `json:"name"` Name *StringCriterionInput `json:"name"`
Director *StringCriterionInput `json:"director"` Director *StringCriterionInput `json:"director"`
Synopsis *StringCriterionInput `json:"synopsis"` Synopsis *StringCriterionInput `json:"synopsis"`

View File

@@ -18,7 +18,7 @@ type JSONPaths struct {
Galleries string Galleries string
Studios string Studios string
Tags string Tags string
Movies string Groups string
Files string Files string
} }
@@ -31,7 +31,7 @@ func newJSONPaths(baseDir string) *JSONPaths {
jp.Images = filepath.Join(baseDir, "images") jp.Images = filepath.Join(baseDir, "images")
jp.Galleries = filepath.Join(baseDir, "galleries") jp.Galleries = filepath.Join(baseDir, "galleries")
jp.Studios = filepath.Join(baseDir, "studios") jp.Studios = filepath.Join(baseDir, "studios")
jp.Movies = filepath.Join(baseDir, "movies") jp.Groups = filepath.Join(baseDir, "movies")
jp.Tags = filepath.Join(baseDir, "tags") jp.Tags = filepath.Join(baseDir, "tags")
jp.Files = filepath.Join(baseDir, "files") jp.Files = filepath.Join(baseDir, "files")
return &jp return &jp
@@ -49,7 +49,7 @@ func EmptyJSONDirs(baseDir string) {
_ = fsutil.EmptyDir(jsonPaths.Galleries) _ = fsutil.EmptyDir(jsonPaths.Galleries)
_ = fsutil.EmptyDir(jsonPaths.Performers) _ = fsutil.EmptyDir(jsonPaths.Performers)
_ = fsutil.EmptyDir(jsonPaths.Studios) _ = fsutil.EmptyDir(jsonPaths.Studios)
_ = fsutil.EmptyDir(jsonPaths.Movies) _ = fsutil.EmptyDir(jsonPaths.Groups)
_ = fsutil.EmptyDir(jsonPaths.Tags) _ = fsutil.EmptyDir(jsonPaths.Tags)
_ = fsutil.EmptyDir(jsonPaths.Files) _ = fsutil.EmptyDir(jsonPaths.Files)
} }
@@ -74,8 +74,8 @@ func EnsureJSONDirs(baseDir string) {
if err := fsutil.EnsureDir(jsonPaths.Studios); err != nil { if err := fsutil.EnsureDir(jsonPaths.Studios); err != nil {
logger.Warnf("couldn't create directories for Studios: %v", err) logger.Warnf("couldn't create directories for Studios: %v", err)
} }
if err := fsutil.EnsureDir(jsonPaths.Movies); err != nil { if err := fsutil.EnsureDir(jsonPaths.Groups); err != nil {
logger.Warnf("couldn't create directories for Movies: %v", err) logger.Warnf("couldn't create directories for Groups: %v", err)
} }
if err := fsutil.EnsureDir(jsonPaths.Tags); err != nil { if err := fsutil.EnsureDir(jsonPaths.Tags); err != nil {
logger.Warnf("couldn't create directories for Tags: %v", err) logger.Warnf("couldn't create directories for Tags: %v", err)

View File

@@ -33,8 +33,8 @@ type FileIDLoader interface {
GetManyFileIDs(ctx context.Context, ids []int) ([][]FileID, error) GetManyFileIDs(ctx context.Context, ids []int) ([][]FileID, error)
} }
type SceneMovieLoader interface { type SceneGroupLoader interface {
GetMovies(ctx context.Context, id int) ([]MoviesScenes, error) GetGroups(ctx context.Context, id int) ([]GroupsScenes, error)
} }
type StashIDLoader interface { type StashIDLoader interface {
@@ -115,50 +115,50 @@ func (r *RelatedIDs) load(fn func() ([]int, error)) error {
return nil return nil
} }
// RelatedMovies represents a list of related Movies. // RelatedGroups represents a list of related Groups.
type RelatedMovies struct { type RelatedGroups struct {
list []MoviesScenes list []GroupsScenes
} }
// NewRelatedMovies returns a loaded RelatedMovies object with the provided movies. // NewRelatedGroups returns a loaded RelateGroups object with the provided groups.
// Loaded will return true when called on the returned object if the provided slice is not nil. // Loaded will return true when called on the returned object if the provided slice is not nil.
func NewRelatedMovies(list []MoviesScenes) RelatedMovies { func NewRelatedGroups(list []GroupsScenes) RelatedGroups {
return RelatedMovies{ return RelatedGroups{
list: list, list: list,
} }
} }
// Loaded returns true if the relationship has been loaded. // Loaded returns true if the relationship has been loaded.
func (r RelatedMovies) Loaded() bool { func (r RelatedGroups) Loaded() bool {
return r.list != nil return r.list != nil
} }
func (r RelatedMovies) mustLoaded() { func (r RelatedGroups) mustLoaded() {
if !r.Loaded() { if !r.Loaded() {
panic("list has not been loaded") panic("list has not been loaded")
} }
} }
// List returns the related Movies. Panics if the relationship has not been loaded. // List returns the related Groups. Panics if the relationship has not been loaded.
func (r RelatedMovies) List() []MoviesScenes { func (r RelatedGroups) List() []GroupsScenes {
r.mustLoaded() r.mustLoaded()
return r.list return r.list
} }
// Add adds the provided ids to the list. Panics if the relationship has not been loaded. // Add adds the provided ids to the list. Panics if the relationship has not been loaded.
func (r *RelatedMovies) Add(movies ...MoviesScenes) { func (r *RelatedGroups) Add(groups ...GroupsScenes) {
r.mustLoaded() r.mustLoaded()
r.list = append(r.list, movies...) r.list = append(r.list, groups...)
} }
// ForID returns the MoviesScenes object for the given movie ID. Returns nil if not found. // ForID returns the GroupsScenes object for the given group ID. Returns nil if not found.
func (r *RelatedMovies) ForID(id int) *MoviesScenes { func (r *RelatedGroups) ForID(id int) *GroupsScenes {
r.mustLoaded() r.mustLoaded()
for _, v := range r.list { for _, v := range r.list {
if v.MovieID == id { if v.GroupID == id {
return &v return &v
} }
} }
@@ -166,7 +166,7 @@ func (r *RelatedMovies) ForID(id int) *MoviesScenes {
return nil return nil
} }
func (r *RelatedMovies) load(fn func() ([]MoviesScenes, error)) error { func (r *RelatedGroups) load(fn func() ([]GroupsScenes, error)) error {
if r.Loaded() { if r.Loaded() {
return nil return nil
} }
@@ -177,7 +177,7 @@ func (r *RelatedMovies) load(fn func() ([]MoviesScenes, error)) error {
} }
if ids == nil { if ids == nil {
ids = []MoviesScenes{} ids = []GroupsScenes{}
} }
r.list = ids r.list = ids

View File

@@ -20,7 +20,7 @@ type Repository struct {
Gallery GalleryReaderWriter Gallery GalleryReaderWriter
GalleryChapter GalleryChapterReaderWriter GalleryChapter GalleryChapterReaderWriter
Image ImageReaderWriter Image ImageReaderWriter
Movie MovieReaderWriter Group GroupReaderWriter
Performer PerformerReaderWriter Performer PerformerReaderWriter
Scene SceneReaderWriter Scene SceneReaderWriter
SceneMarker SceneMarkerReaderWriter SceneMarker SceneMarkerReaderWriter

View File

@@ -2,87 +2,87 @@ package models
import "context" import "context"
// MovieGetter provides methods to get movies by ID. // GroupGetter provides methods to get groups by ID.
type MovieGetter interface { type GroupGetter interface {
// TODO - rename this to Find and remove existing method // TODO - rename this to Find and remove existing method
FindMany(ctx context.Context, ids []int) ([]*Movie, error) FindMany(ctx context.Context, ids []int) ([]*Group, error)
Find(ctx context.Context, id int) (*Movie, error) Find(ctx context.Context, id int) (*Group, error)
} }
// MovieFinder provides methods to find movies. // GroupFinder provides methods to find groups.
type MovieFinder interface { type GroupFinder interface {
MovieGetter GroupGetter
FindByPerformerID(ctx context.Context, performerID int) ([]*Movie, error) FindByPerformerID(ctx context.Context, performerID int) ([]*Group, error)
FindByStudioID(ctx context.Context, studioID int) ([]*Movie, error) FindByStudioID(ctx context.Context, studioID int) ([]*Group, error)
FindByName(ctx context.Context, name string, nocase bool) (*Movie, error) FindByName(ctx context.Context, name string, nocase bool) (*Group, error)
FindByNames(ctx context.Context, names []string, nocase bool) ([]*Movie, error) FindByNames(ctx context.Context, names []string, nocase bool) ([]*Group, error)
} }
// MovieQueryer provides methods to query movies. // GroupQueryer provides methods to query groups.
type MovieQueryer interface { type GroupQueryer interface {
Query(ctx context.Context, movieFilter *MovieFilterType, findFilter *FindFilterType) ([]*Movie, int, error) Query(ctx context.Context, groupFilter *GroupFilterType, findFilter *FindFilterType) ([]*Group, int, error)
QueryCount(ctx context.Context, movieFilter *MovieFilterType, findFilter *FindFilterType) (int, error) QueryCount(ctx context.Context, groupFilter *GroupFilterType, findFilter *FindFilterType) (int, error)
} }
// MovieCounter provides methods to count movies. // GroupCounter provides methods to count groups.
type MovieCounter interface { type GroupCounter interface {
Count(ctx context.Context) (int, error) Count(ctx context.Context) (int, error)
CountByPerformerID(ctx context.Context, performerID int) (int, error) CountByPerformerID(ctx context.Context, performerID int) (int, error)
CountByStudioID(ctx context.Context, studioID int) (int, error) CountByStudioID(ctx context.Context, studioID int) (int, error)
} }
// MovieCreator provides methods to create movies. // GroupCreator provides methods to create groups.
type MovieCreator interface { type GroupCreator interface {
Create(ctx context.Context, newMovie *Movie) error Create(ctx context.Context, newGroup *Group) error
} }
// MovieUpdater provides methods to update movies. // GroupUpdater provides methods to update groups.
type MovieUpdater interface { type GroupUpdater interface {
Update(ctx context.Context, updatedMovie *Movie) error Update(ctx context.Context, updatedGroup *Group) error
UpdatePartial(ctx context.Context, id int, updatedMovie MoviePartial) (*Movie, error) UpdatePartial(ctx context.Context, id int, updatedGroup GroupPartial) (*Group, error)
UpdateFrontImage(ctx context.Context, movieID int, frontImage []byte) error UpdateFrontImage(ctx context.Context, groupID int, frontImage []byte) error
UpdateBackImage(ctx context.Context, movieID int, backImage []byte) error UpdateBackImage(ctx context.Context, groupID int, backImage []byte) error
} }
// MovieDestroyer provides methods to destroy movies. // GroupDestroyer provides methods to destroy groups.
type MovieDestroyer interface { type GroupDestroyer interface {
Destroy(ctx context.Context, id int) error Destroy(ctx context.Context, id int) error
} }
type MovieCreatorUpdater interface { type GroupCreatorUpdater interface {
MovieCreator GroupCreator
MovieUpdater GroupUpdater
} }
type MovieFinderCreator interface { type GroupFinderCreator interface {
MovieFinder GroupFinder
MovieCreator GroupCreator
} }
// MovieReader provides all methods to read movies. // GroupReader provides all methods to read groups.
type MovieReader interface { type GroupReader interface {
MovieFinder GroupFinder
MovieQueryer GroupQueryer
MovieCounter GroupCounter
URLLoader URLLoader
TagIDLoader TagIDLoader
All(ctx context.Context) ([]*Movie, error) All(ctx context.Context) ([]*Group, error)
GetFrontImage(ctx context.Context, movieID int) ([]byte, error) GetFrontImage(ctx context.Context, groupID int) ([]byte, error)
HasFrontImage(ctx context.Context, movieID int) (bool, error) HasFrontImage(ctx context.Context, groupID int) (bool, error)
GetBackImage(ctx context.Context, movieID int) ([]byte, error) GetBackImage(ctx context.Context, groupID int) ([]byte, error)
HasBackImage(ctx context.Context, movieID int) (bool, error) HasBackImage(ctx context.Context, groupID int) (bool, error)
} }
// MovieWriter provides all methods to modify movies. // GroupWriter provides all methods to modify groups.
type MovieWriter interface { type GroupWriter interface {
MovieCreator GroupCreator
MovieUpdater GroupUpdater
MovieDestroyer GroupDestroyer
} }
// MovieReaderWriter provides all movie methods. // GroupReaderWriter provides all group methods.
type MovieReaderWriter interface { type GroupReaderWriter interface {
MovieReader GroupReader
MovieWriter GroupWriter
} }

View File

@@ -23,7 +23,7 @@ type SceneFinder interface {
FindByPrimaryFileID(ctx context.Context, fileID FileID) ([]*Scene, error) FindByPrimaryFileID(ctx context.Context, fileID FileID) ([]*Scene, error)
FindByPerformerID(ctx context.Context, performerID int) ([]*Scene, error) FindByPerformerID(ctx context.Context, performerID int) ([]*Scene, error)
FindByGalleryID(ctx context.Context, performerID int) ([]*Scene, error) FindByGalleryID(ctx context.Context, performerID int) ([]*Scene, error)
FindByMovieID(ctx context.Context, movieID int) ([]*Scene, error) FindByGroupID(ctx context.Context, groupID int) ([]*Scene, error)
FindDuplicates(ctx context.Context, distance int, durationDiff float64) ([][]*Scene, error) FindDuplicates(ctx context.Context, distance int, durationDiff float64) ([][]*Scene, error)
} }
@@ -37,7 +37,7 @@ type SceneQueryer interface {
type SceneCounter interface { type SceneCounter interface {
Count(ctx context.Context) (int, error) Count(ctx context.Context) (int, error)
CountByPerformerID(ctx context.Context, performerID int) (int, error) CountByPerformerID(ctx context.Context, performerID int) (int, error)
CountByMovieID(ctx context.Context, movieID int) (int, error) CountByGroupID(ctx context.Context, groupID int) (int, error)
CountByFileID(ctx context.Context, fileID FileID) (int, error) CountByFileID(ctx context.Context, fileID FileID) (int, error)
CountByStudioID(ctx context.Context, studioID int) (int, error) CountByStudioID(ctx context.Context, studioID int) (int, error)
CountByTagID(ctx context.Context, tagID int) (int, error) CountByTagID(ctx context.Context, tagID int) (int, error)
@@ -99,7 +99,7 @@ type SceneReader interface {
GalleryIDLoader GalleryIDLoader
PerformerIDLoader PerformerIDLoader
TagIDLoader TagIDLoader
SceneMovieLoader SceneGroupLoader
StashIDLoader StashIDLoader
VideoFileLoader VideoFileLoader

View File

@@ -20,7 +20,7 @@ type TagFinder interface {
FindByImageID(ctx context.Context, imageID int) ([]*Tag, error) FindByImageID(ctx context.Context, imageID int) ([]*Tag, error)
FindByGalleryID(ctx context.Context, galleryID int) ([]*Tag, error) FindByGalleryID(ctx context.Context, galleryID int) ([]*Tag, error)
FindByPerformerID(ctx context.Context, performerID int) ([]*Tag, error) FindByPerformerID(ctx context.Context, performerID int) ([]*Tag, error)
FindByMovieID(ctx context.Context, movieID int) ([]*Tag, error) FindByGroupID(ctx context.Context, groupID int) ([]*Tag, error)
FindBySceneMarkerID(ctx context.Context, sceneMarkerID int) ([]*Tag, error) FindBySceneMarkerID(ctx context.Context, sceneMarkerID int) ([]*Tag, error)
FindByStudioID(ctx context.Context, studioID int) ([]*Tag, error) FindByStudioID(ctx context.Context, studioID int) ([]*Tag, error)
FindByName(ctx context.Context, name string, nocase bool) (*Tag, error) FindByName(ctx context.Context, name string, nocase bool) (*Tag, error)

View File

@@ -106,9 +106,9 @@ type SceneFilterType struct {
// Filter by related tags that meet this criteria // Filter by related tags that meet this criteria
TagsFilter *TagFilterType `json:"tags_filter"` TagsFilter *TagFilterType `json:"tags_filter"`
// Filter by related groups that meet this criteria // Filter by related groups that meet this criteria
GroupsFilter *MovieFilterType `json:"groups_filter"` GroupsFilter *GroupFilterType `json:"groups_filter"`
// Filter by related movies that meet this criteria // Filter by related movies that meet this criteria
MoviesFilter *MovieFilterType `json:"movies_filter"` MoviesFilter *GroupFilterType `json:"movies_filter"`
// Filter by related markers that meet this criteria // Filter by related markers that meet this criteria
MarkersFilter *SceneMarkerFilterType `json:"markers_filter"` MarkersFilter *SceneMarkerFilterType `json:"markers_filter"`
// Filter by created at // Filter by created at

View File

@@ -17,8 +17,8 @@ type ImageGetter interface {
} }
// ToJSON converts a Movie into its JSON equivalent. // ToJSON converts a Movie into its JSON equivalent.
func ToJSON(ctx context.Context, reader ImageGetter, studioReader models.StudioGetter, movie *models.Movie) (*jsonschema.Movie, error) { func ToJSON(ctx context.Context, reader ImageGetter, studioReader models.StudioGetter, movie *models.Group) (*jsonschema.Group, error) {
newMovieJSON := jsonschema.Movie{ newMovieJSON := jsonschema.Group{
Name: movie.Name, Name: movie.Name,
Aliases: movie.Aliases, Aliases: movie.Aliases,
Director: movie.Director, Director: movie.Director,

View File

@@ -62,8 +62,8 @@ var (
updateTime = time.Date(2002, 01, 01, 0, 0, 0, 0, time.UTC) updateTime = time.Date(2002, 01, 01, 0, 0, 0, 0, time.UTC)
) )
func createFullMovie(id int, studioID int) models.Movie { func createFullMovie(id int, studioID int) models.Group {
return models.Movie{ return models.Group{
ID: id, ID: id,
Name: movieName, Name: movieName,
Aliases: movieAliases, Aliases: movieAliases,
@@ -79,8 +79,8 @@ func createFullMovie(id int, studioID int) models.Movie {
} }
} }
func createEmptyMovie(id int) models.Movie { func createEmptyMovie(id int) models.Group {
return models.Movie{ return models.Group{
ID: id, ID: id,
URLs: models.NewRelatedStrings([]string{}), URLs: models.NewRelatedStrings([]string{}),
CreatedAt: createTime, CreatedAt: createTime,
@@ -88,8 +88,8 @@ func createEmptyMovie(id int) models.Movie {
} }
} }
func createFullJSONMovie(studio, frontImage, backImage string) *jsonschema.Movie { func createFullJSONMovie(studio, frontImage, backImage string) *jsonschema.Group {
return &jsonschema.Movie{ return &jsonschema.Group{
Name: movieName, Name: movieName,
Aliases: movieAliases, Aliases: movieAliases,
Date: date, Date: date,
@@ -110,8 +110,8 @@ func createFullJSONMovie(studio, frontImage, backImage string) *jsonschema.Movie
} }
} }
func createEmptyJSONMovie() *jsonschema.Movie { func createEmptyJSONMovie() *jsonschema.Group {
return &jsonschema.Movie{ return &jsonschema.Group{
URLs: []string{}, URLs: []string{},
CreatedAt: json.JSONTime{ CreatedAt: json.JSONTime{
Time: createTime, Time: createTime,
@@ -123,8 +123,8 @@ func createEmptyJSONMovie() *jsonschema.Movie {
} }
type testScenario struct { type testScenario struct {
movie models.Movie movie models.Group
expected *jsonschema.Movie expected *jsonschema.Group
err bool err bool
} }
@@ -174,18 +174,18 @@ func TestToJSON(t *testing.T) {
imageErr := errors.New("error getting image") imageErr := errors.New("error getting image")
db.Movie.On("GetFrontImage", testCtx, movieID).Return(frontImageBytes, nil).Once() db.Group.On("GetFrontImage", testCtx, movieID).Return(frontImageBytes, nil).Once()
db.Movie.On("GetFrontImage", testCtx, missingStudioMovieID).Return(frontImageBytes, nil).Once() db.Group.On("GetFrontImage", testCtx, missingStudioMovieID).Return(frontImageBytes, nil).Once()
db.Movie.On("GetFrontImage", testCtx, emptyID).Return(nil, nil).Once().Maybe() db.Group.On("GetFrontImage", testCtx, emptyID).Return(nil, nil).Once().Maybe()
db.Movie.On("GetFrontImage", testCtx, errFrontImageID).Return(nil, imageErr).Once() db.Group.On("GetFrontImage", testCtx, errFrontImageID).Return(nil, imageErr).Once()
db.Movie.On("GetFrontImage", testCtx, errBackImageID).Return(frontImageBytes, nil).Once() db.Group.On("GetFrontImage", testCtx, errBackImageID).Return(frontImageBytes, nil).Once()
db.Movie.On("GetBackImage", testCtx, movieID).Return(backImageBytes, nil).Once() db.Group.On("GetBackImage", testCtx, movieID).Return(backImageBytes, nil).Once()
db.Movie.On("GetBackImage", testCtx, missingStudioMovieID).Return(backImageBytes, nil).Once() db.Group.On("GetBackImage", testCtx, missingStudioMovieID).Return(backImageBytes, nil).Once()
db.Movie.On("GetBackImage", testCtx, emptyID).Return(nil, nil).Once() db.Group.On("GetBackImage", testCtx, emptyID).Return(nil, nil).Once()
db.Movie.On("GetBackImage", testCtx, errBackImageID).Return(nil, imageErr).Once() db.Group.On("GetBackImage", testCtx, errBackImageID).Return(nil, imageErr).Once()
db.Movie.On("GetBackImage", testCtx, errFrontImageID).Return(backImageBytes, nil).Maybe() db.Group.On("GetBackImage", testCtx, errFrontImageID).Return(backImageBytes, nil).Maybe()
db.Movie.On("GetBackImage", testCtx, errStudioMovieID).Return(backImageBytes, nil).Maybe() db.Group.On("GetBackImage", testCtx, errStudioMovieID).Return(backImageBytes, nil).Maybe()
studioErr := errors.New("error getting studio") studioErr := errors.New("error getting studio")
@@ -195,7 +195,7 @@ func TestToJSON(t *testing.T) {
for i, s := range scenarios { for i, s := range scenarios {
movie := s.movie movie := s.movie
json, err := ToJSON(testCtx, db.Movie, db.Studio, &movie) json, err := ToJSON(testCtx, db.Group, db.Studio, &movie)
switch { switch {
case !s.err && err != nil: case !s.err && err != nil:

View File

@@ -12,24 +12,24 @@ import (
) )
type ImporterReaderWriter interface { type ImporterReaderWriter interface {
models.MovieCreatorUpdater models.GroupCreatorUpdater
FindByName(ctx context.Context, name string, nocase bool) (*models.Movie, error) FindByName(ctx context.Context, name string, nocase bool) (*models.Group, error)
} }
type Importer struct { type Importer struct {
ReaderWriter ImporterReaderWriter ReaderWriter ImporterReaderWriter
StudioWriter models.StudioFinderCreator StudioWriter models.StudioFinderCreator
TagWriter models.TagFinderCreator TagWriter models.TagFinderCreator
Input jsonschema.Movie Input jsonschema.Group
MissingRefBehaviour models.ImportMissingRefEnum MissingRefBehaviour models.ImportMissingRefEnum
movie models.Movie group models.Group
frontImageData []byte frontImageData []byte
backImageData []byte backImageData []byte
} }
func (i *Importer) PreImport(ctx context.Context) error { func (i *Importer) PreImport(ctx context.Context) error {
i.movie = i.movieJSONToMovie(i.Input) i.group = i.groupJSONToGroup(i.Input)
if err := i.populateStudio(ctx); err != nil { if err := i.populateStudio(ctx); err != nil {
return err return err
@@ -65,7 +65,7 @@ func (i *Importer) populateTags(ctx context.Context) error {
} }
for _, p := range tags { for _, p := range tags {
i.movie.TagIDs.Add(p.ID) i.group.TagIDs.Add(p.ID)
} }
} }
@@ -124,38 +124,38 @@ func createTags(ctx context.Context, tagWriter models.TagFinderCreator, names []
return ret, nil return ret, nil
} }
func (i *Importer) movieJSONToMovie(movieJSON jsonschema.Movie) models.Movie { func (i *Importer) groupJSONToGroup(groupJSON jsonschema.Group) models.Group {
newMovie := models.Movie{ newGroup := models.Group{
Name: movieJSON.Name, Name: groupJSON.Name,
Aliases: movieJSON.Aliases, Aliases: groupJSON.Aliases,
Director: movieJSON.Director, Director: groupJSON.Director,
Synopsis: movieJSON.Synopsis, Synopsis: groupJSON.Synopsis,
CreatedAt: movieJSON.CreatedAt.GetTime(), CreatedAt: groupJSON.CreatedAt.GetTime(),
UpdatedAt: movieJSON.UpdatedAt.GetTime(), UpdatedAt: groupJSON.UpdatedAt.GetTime(),
TagIDs: models.NewRelatedIDs([]int{}), TagIDs: models.NewRelatedIDs([]int{}),
} }
if len(movieJSON.URLs) > 0 { if len(groupJSON.URLs) > 0 {
newMovie.URLs = models.NewRelatedStrings(movieJSON.URLs) newGroup.URLs = models.NewRelatedStrings(groupJSON.URLs)
} else if movieJSON.URL != "" { } else if groupJSON.URL != "" {
newMovie.URLs = models.NewRelatedStrings([]string{movieJSON.URL}) newGroup.URLs = models.NewRelatedStrings([]string{groupJSON.URL})
} }
if movieJSON.Date != "" { if groupJSON.Date != "" {
d, err := models.ParseDate(movieJSON.Date) d, err := models.ParseDate(groupJSON.Date)
if err == nil { if err == nil {
newMovie.Date = &d newGroup.Date = &d
} }
} }
if movieJSON.Rating != 0 { if groupJSON.Rating != 0 {
newMovie.Rating = &movieJSON.Rating newGroup.Rating = &groupJSON.Rating
} }
if movieJSON.Duration != 0 { if groupJSON.Duration != 0 {
newMovie.Duration = &movieJSON.Duration newGroup.Duration = &groupJSON.Duration
} }
return newMovie return newGroup
} }
func (i *Importer) populateStudio(ctx context.Context) error { func (i *Importer) populateStudio(ctx context.Context) error {
@@ -167,7 +167,7 @@ func (i *Importer) populateStudio(ctx context.Context) error {
if studio == nil { if studio == nil {
if i.MissingRefBehaviour == models.ImportMissingRefEnumFail { if i.MissingRefBehaviour == models.ImportMissingRefEnumFail {
return fmt.Errorf("movie studio '%s' not found", i.Input.Studio) return fmt.Errorf("group studio '%s' not found", i.Input.Studio)
} }
if i.MissingRefBehaviour == models.ImportMissingRefEnumIgnore { if i.MissingRefBehaviour == models.ImportMissingRefEnumIgnore {
@@ -179,10 +179,10 @@ func (i *Importer) populateStudio(ctx context.Context) error {
if err != nil { if err != nil {
return err return err
} }
i.movie.StudioID = &studioID i.group.StudioID = &studioID
} }
} else { } else {
i.movie.StudioID = &studio.ID i.group.StudioID = &studio.ID
} }
} }
@@ -204,13 +204,13 @@ func (i *Importer) createStudio(ctx context.Context, name string) (int, error) {
func (i *Importer) PostImport(ctx context.Context, id int) error { func (i *Importer) PostImport(ctx context.Context, id int) error {
if len(i.frontImageData) > 0 { if len(i.frontImageData) > 0 {
if err := i.ReaderWriter.UpdateFrontImage(ctx, id, i.frontImageData); err != nil { if err := i.ReaderWriter.UpdateFrontImage(ctx, id, i.frontImageData); err != nil {
return fmt.Errorf("error setting movie front image: %v", err) return fmt.Errorf("error setting group front image: %v", err)
} }
} }
if len(i.backImageData) > 0 { if len(i.backImageData) > 0 {
if err := i.ReaderWriter.UpdateBackImage(ctx, id, i.backImageData); err != nil { if err := i.ReaderWriter.UpdateBackImage(ctx, id, i.backImageData); err != nil {
return fmt.Errorf("error setting movie back image: %v", err) return fmt.Errorf("error setting group back image: %v", err)
} }
} }
@@ -237,21 +237,21 @@ func (i *Importer) FindExistingID(ctx context.Context) (*int, error) {
} }
func (i *Importer) Create(ctx context.Context) (*int, error) { func (i *Importer) Create(ctx context.Context) (*int, error) {
err := i.ReaderWriter.Create(ctx, &i.movie) err := i.ReaderWriter.Create(ctx, &i.group)
if err != nil { if err != nil {
return nil, fmt.Errorf("error creating movie: %v", err) return nil, fmt.Errorf("error creating group: %v", err)
} }
id := i.movie.ID id := i.group.ID
return &id, nil return &id, nil
} }
func (i *Importer) Update(ctx context.Context, id int) error { func (i *Importer) Update(ctx context.Context, id int) error {
movie := i.movie group := i.group
movie.ID = id group.ID = id
err := i.ReaderWriter.Update(ctx, &movie) err := i.ReaderWriter.Update(ctx, &group)
if err != nil { if err != nil {
return fmt.Errorf("error updating existing movie: %v", err) return fmt.Errorf("error updating existing group: %v", err)
} }
return nil return nil

View File

@@ -39,7 +39,7 @@ var testCtx = context.Background()
func TestImporterName(t *testing.T) { func TestImporterName(t *testing.T) {
i := Importer{ i := Importer{
Input: jsonschema.Movie{ Input: jsonschema.Group{
Name: movieName, Name: movieName,
}, },
} }
@@ -49,7 +49,7 @@ func TestImporterName(t *testing.T) {
func TestImporterPreImport(t *testing.T) { func TestImporterPreImport(t *testing.T) {
i := Importer{ i := Importer{
Input: jsonschema.Movie{ Input: jsonschema.Group{
Name: movieName, Name: movieName,
FrontImage: invalidImage, FrontImage: invalidImage,
}, },
@@ -79,9 +79,9 @@ func TestImporterPreImportWithStudio(t *testing.T) {
db := mocks.NewDatabase() db := mocks.NewDatabase()
i := Importer{ i := Importer{
ReaderWriter: db.Movie, ReaderWriter: db.Group,
StudioWriter: db.Studio, StudioWriter: db.Studio,
Input: jsonschema.Movie{ Input: jsonschema.Group{
Name: movieName, Name: movieName,
FrontImage: frontImage, FrontImage: frontImage,
Studio: existingStudioName, Studio: existingStudioName,
@@ -97,7 +97,7 @@ func TestImporterPreImportWithStudio(t *testing.T) {
err := i.PreImport(testCtx) err := i.PreImport(testCtx)
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, existingStudioID, *i.movie.StudioID) assert.Equal(t, existingStudioID, *i.group.StudioID)
i.Input.Studio = existingStudioErr i.Input.Studio = existingStudioErr
err = i.PreImport(testCtx) err = i.PreImport(testCtx)
@@ -110,9 +110,9 @@ func TestImporterPreImportWithMissingStudio(t *testing.T) {
db := mocks.NewDatabase() db := mocks.NewDatabase()
i := Importer{ i := Importer{
ReaderWriter: db.Movie, ReaderWriter: db.Group,
StudioWriter: db.Studio, StudioWriter: db.Studio,
Input: jsonschema.Movie{ Input: jsonschema.Group{
Name: movieName, Name: movieName,
FrontImage: frontImage, FrontImage: frontImage,
Studio: missingStudioName, Studio: missingStudioName,
@@ -136,7 +136,7 @@ func TestImporterPreImportWithMissingStudio(t *testing.T) {
i.MissingRefBehaviour = models.ImportMissingRefEnumCreate i.MissingRefBehaviour = models.ImportMissingRefEnumCreate
err = i.PreImport(testCtx) err = i.PreImport(testCtx)
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, existingStudioID, *i.movie.StudioID) assert.Equal(t, existingStudioID, *i.group.StudioID)
db.AssertExpectations(t) db.AssertExpectations(t)
} }
@@ -145,9 +145,9 @@ func TestImporterPreImportWithMissingStudioCreateErr(t *testing.T) {
db := mocks.NewDatabase() db := mocks.NewDatabase()
i := Importer{ i := Importer{
ReaderWriter: db.Movie, ReaderWriter: db.Group,
StudioWriter: db.Studio, StudioWriter: db.Studio,
Input: jsonschema.Movie{ Input: jsonschema.Group{
Name: movieName, Name: movieName,
FrontImage: frontImage, FrontImage: frontImage,
Studio: missingStudioName, Studio: missingStudioName,
@@ -168,10 +168,10 @@ func TestImporterPreImportWithTag(t *testing.T) {
db := mocks.NewDatabase() db := mocks.NewDatabase()
i := Importer{ i := Importer{
ReaderWriter: db.Movie, ReaderWriter: db.Group,
TagWriter: db.Tag, TagWriter: db.Tag,
MissingRefBehaviour: models.ImportMissingRefEnumFail, MissingRefBehaviour: models.ImportMissingRefEnumFail,
Input: jsonschema.Movie{ Input: jsonschema.Group{
Tags: []string{ Tags: []string{
existingTagName, existingTagName,
}, },
@@ -188,7 +188,7 @@ func TestImporterPreImportWithTag(t *testing.T) {
err := i.PreImport(testCtx) err := i.PreImport(testCtx)
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, existingTagID, i.movie.TagIDs.List()[0]) assert.Equal(t, existingTagID, i.group.TagIDs.List()[0])
i.Input.Tags = []string{existingTagErr} i.Input.Tags = []string{existingTagErr}
err = i.PreImport(testCtx) err = i.PreImport(testCtx)
@@ -201,9 +201,9 @@ func TestImporterPreImportWithMissingTag(t *testing.T) {
db := mocks.NewDatabase() db := mocks.NewDatabase()
i := Importer{ i := Importer{
ReaderWriter: db.Movie, ReaderWriter: db.Group,
TagWriter: db.Tag, TagWriter: db.Tag,
Input: jsonschema.Movie{ Input: jsonschema.Group{
Tags: []string{ Tags: []string{
missingTagName, missingTagName,
}, },
@@ -227,7 +227,7 @@ func TestImporterPreImportWithMissingTag(t *testing.T) {
i.MissingRefBehaviour = models.ImportMissingRefEnumCreate i.MissingRefBehaviour = models.ImportMissingRefEnumCreate
err = i.PreImport(testCtx) err = i.PreImport(testCtx)
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, existingTagID, i.movie.TagIDs.List()[0]) assert.Equal(t, existingTagID, i.group.TagIDs.List()[0])
db.AssertExpectations(t) db.AssertExpectations(t)
} }
@@ -236,9 +236,9 @@ func TestImporterPreImportWithMissingTagCreateErr(t *testing.T) {
db := mocks.NewDatabase() db := mocks.NewDatabase()
i := Importer{ i := Importer{
ReaderWriter: db.Movie, ReaderWriter: db.Group,
TagWriter: db.Tag, TagWriter: db.Tag,
Input: jsonschema.Movie{ Input: jsonschema.Group{
Tags: []string{ Tags: []string{
missingTagName, missingTagName,
}, },
@@ -259,7 +259,7 @@ func TestImporterPostImport(t *testing.T) {
db := mocks.NewDatabase() db := mocks.NewDatabase()
i := Importer{ i := Importer{
ReaderWriter: db.Movie, ReaderWriter: db.Group,
StudioWriter: db.Studio, StudioWriter: db.Studio,
frontImageData: frontImageBytes, frontImageData: frontImageBytes,
backImageData: backImageBytes, backImageData: backImageBytes,
@@ -267,9 +267,9 @@ func TestImporterPostImport(t *testing.T) {
updateMovieImageErr := errors.New("UpdateImages error") updateMovieImageErr := errors.New("UpdateImages error")
db.Movie.On("UpdateFrontImage", testCtx, movieID, frontImageBytes).Return(nil).Once() db.Group.On("UpdateFrontImage", testCtx, movieID, frontImageBytes).Return(nil).Once()
db.Movie.On("UpdateBackImage", testCtx, movieID, backImageBytes).Return(nil).Once() db.Group.On("UpdateBackImage", testCtx, movieID, backImageBytes).Return(nil).Once()
db.Movie.On("UpdateFrontImage", testCtx, errImageID, frontImageBytes).Return(updateMovieImageErr).Once() db.Group.On("UpdateFrontImage", testCtx, errImageID, frontImageBytes).Return(updateMovieImageErr).Once()
err := i.PostImport(testCtx, movieID) err := i.PostImport(testCtx, movieID)
assert.Nil(t, err) assert.Nil(t, err)
@@ -284,19 +284,19 @@ func TestImporterFindExistingID(t *testing.T) {
db := mocks.NewDatabase() db := mocks.NewDatabase()
i := Importer{ i := Importer{
ReaderWriter: db.Movie, ReaderWriter: db.Group,
StudioWriter: db.Studio, StudioWriter: db.Studio,
Input: jsonschema.Movie{ Input: jsonschema.Group{
Name: movieName, Name: movieName,
}, },
} }
errFindByName := errors.New("FindByName error") errFindByName := errors.New("FindByName error")
db.Movie.On("FindByName", testCtx, movieName, false).Return(nil, nil).Once() db.Group.On("FindByName", testCtx, movieName, false).Return(nil, nil).Once()
db.Movie.On("FindByName", testCtx, existingMovieName, false).Return(&models.Movie{ db.Group.On("FindByName", testCtx, existingMovieName, false).Return(&models.Group{
ID: existingMovieID, ID: existingMovieID,
}, nil).Once() }, nil).Once()
db.Movie.On("FindByName", testCtx, movieNameErr, false).Return(nil, errFindByName).Once() db.Group.On("FindByName", testCtx, movieNameErr, false).Return(nil, errFindByName).Once()
id, err := i.FindExistingID(testCtx) id, err := i.FindExistingID(testCtx)
assert.Nil(t, id) assert.Nil(t, id)
@@ -318,32 +318,32 @@ func TestImporterFindExistingID(t *testing.T) {
func TestCreate(t *testing.T) { func TestCreate(t *testing.T) {
db := mocks.NewDatabase() db := mocks.NewDatabase()
movie := models.Movie{ movie := models.Group{
Name: movieName, Name: movieName,
} }
movieErr := models.Movie{ movieErr := models.Group{
Name: movieNameErr, Name: movieNameErr,
} }
i := Importer{ i := Importer{
ReaderWriter: db.Movie, ReaderWriter: db.Group,
StudioWriter: db.Studio, StudioWriter: db.Studio,
movie: movie, group: movie,
} }
errCreate := errors.New("Create error") errCreate := errors.New("Create error")
db.Movie.On("Create", testCtx, &movie).Run(func(args mock.Arguments) { db.Group.On("Create", testCtx, &movie).Run(func(args mock.Arguments) {
m := args.Get(1).(*models.Movie) m := args.Get(1).(*models.Group)
m.ID = movieID m.ID = movieID
}).Return(nil).Once() }).Return(nil).Once()
db.Movie.On("Create", testCtx, &movieErr).Return(errCreate).Once() db.Group.On("Create", testCtx, &movieErr).Return(errCreate).Once()
id, err := i.Create(testCtx) id, err := i.Create(testCtx)
assert.Equal(t, movieID, *id) assert.Equal(t, movieID, *id)
assert.Nil(t, err) assert.Nil(t, err)
i.movie = movieErr i.group = movieErr
id, err = i.Create(testCtx) id, err = i.Create(testCtx)
assert.Nil(t, id) assert.Nil(t, id)
assert.NotNil(t, err) assert.NotNil(t, err)
@@ -354,34 +354,34 @@ func TestCreate(t *testing.T) {
func TestUpdate(t *testing.T) { func TestUpdate(t *testing.T) {
db := mocks.NewDatabase() db := mocks.NewDatabase()
movie := models.Movie{ movie := models.Group{
Name: movieName, Name: movieName,
} }
movieErr := models.Movie{ movieErr := models.Group{
Name: movieNameErr, Name: movieNameErr,
} }
i := Importer{ i := Importer{
ReaderWriter: db.Movie, ReaderWriter: db.Group,
StudioWriter: db.Studio, StudioWriter: db.Studio,
movie: movie, group: movie,
} }
errUpdate := errors.New("Update error") errUpdate := errors.New("Update error")
// id needs to be set for the mock input // id needs to be set for the mock input
movie.ID = movieID movie.ID = movieID
db.Movie.On("Update", testCtx, &movie).Return(nil).Once() db.Group.On("Update", testCtx, &movie).Return(nil).Once()
err := i.Update(testCtx, movieID) err := i.Update(testCtx, movieID)
assert.Nil(t, err) assert.Nil(t, err)
i.movie = movieErr i.group = movieErr
// need to set id separately // need to set id separately
movieErr.ID = errImageID movieErr.ID = errImageID
db.Movie.On("Update", testCtx, &movieErr).Return(errUpdate).Once() db.Group.On("Update", testCtx, &movieErr).Return(errUpdate).Once()
err = i.Update(testCtx, errImageID) err = i.Update(testCtx, errImageID)
assert.NotNil(t, err) assert.NotNil(t, err)

View File

@@ -7,8 +7,8 @@ import (
"github.com/stashapp/stash/pkg/models" "github.com/stashapp/stash/pkg/models"
) )
func CountByStudioID(ctx context.Context, r models.MovieQueryer, id int, depth *int) (int, error) { func CountByStudioID(ctx context.Context, r models.GroupQueryer, id int, depth *int) (int, error) {
filter := &models.MovieFilterType{ filter := &models.GroupFilterType{
Studios: &models.HierarchicalMultiCriterionInput{ Studios: &models.HierarchicalMultiCriterionInput{
Value: []string{strconv.Itoa(id)}, Value: []string{strconv.Itoa(id)},
Modifier: models.CriterionModifierIncludes, Modifier: models.CriterionModifierIncludes,
@@ -19,8 +19,8 @@ func CountByStudioID(ctx context.Context, r models.MovieQueryer, id int, depth *
return r.QueryCount(ctx, filter, nil) return r.QueryCount(ctx, filter, nil)
} }
func CountByTagID(ctx context.Context, r models.MovieQueryer, id int, depth *int) (int, error) { func CountByTagID(ctx context.Context, r models.GroupQueryer, id int, depth *int) (int, error) {
filter := &models.MovieFilterType{ filter := &models.GroupFilterType{
Tags: &models.HierarchicalMultiCriterionInput{ Tags: &models.HierarchicalMultiCriterionInput{
Value: []string{strconv.Itoa(id)}, Value: []string{strconv.Itoa(id)},
Modifier: models.CriterionModifierIncludes, Modifier: models.CriterionModifierIncludes,

View File

@@ -167,39 +167,39 @@ func GetDependentTagIDs(ctx context.Context, tags TagFinder, markerReader models
return ret, nil return ret, nil
} }
// GetSceneMoviesJSON returns a slice of SceneMovie JSON representation objects // GetSceneGroupsJSON returns a slice of SceneGroup JSON representation objects
// corresponding to the provided scene's scene movie relationships. // corresponding to the provided scene's scene group relationships.
func GetSceneMoviesJSON(ctx context.Context, movieReader models.MovieGetter, scene *models.Scene) ([]jsonschema.SceneMovie, error) { func GetSceneGroupsJSON(ctx context.Context, groupReader models.GroupGetter, scene *models.Scene) ([]jsonschema.SceneGroup, error) {
sceneMovies := scene.Movies.List() sceneGroups := scene.Groups.List()
var results []jsonschema.SceneMovie var results []jsonschema.SceneGroup
for _, sceneMovie := range sceneMovies { for _, sceneGroup := range sceneGroups {
movie, err := movieReader.Find(ctx, sceneMovie.MovieID) group, err := groupReader.Find(ctx, sceneGroup.GroupID)
if err != nil { if err != nil {
return nil, fmt.Errorf("error getting movie: %v", err) return nil, fmt.Errorf("error getting group: %v", err)
} }
if movie != nil { if group != nil {
sceneMovieJSON := jsonschema.SceneMovie{ sceneGroupJSON := jsonschema.SceneGroup{
MovieName: movie.Name, GroupName: group.Name,
} }
if sceneMovie.SceneIndex != nil { if sceneGroup.SceneIndex != nil {
sceneMovieJSON.SceneIndex = *sceneMovie.SceneIndex sceneGroupJSON.SceneIndex = *sceneGroup.SceneIndex
} }
results = append(results, sceneMovieJSON) results = append(results, sceneGroupJSON)
} }
} }
return results, nil return results, nil
} }
// GetDependentMovieIDs returns a slice of movie IDs that this scene references. // GetDependentGroupIDs returns a slice of group IDs that this scene references.
func GetDependentMovieIDs(ctx context.Context, scene *models.Scene) ([]int, error) { func GetDependentGroupIDs(ctx context.Context, scene *models.Scene) ([]int, error) {
var ret []int var ret []int
m := scene.Movies.List() m := scene.Groups.List()
for _, mm := range m { for _, mm := range m {
ret = append(ret, mm.MovieID) ret = append(ret, mm.GroupID)
} }
return ret, nil return ret, nil

View File

@@ -26,8 +26,8 @@ const (
noTagsID = 11 noTagsID = 11
errTagsID = 12 errTagsID = 12
noMoviesID = 13 noGroupsID = 13
errFindMovieID = 15 errFindGroupID = 15
noMarkersID = 16 noMarkersID = 16
errMarkersID = 17 errMarkersID = 17
@@ -49,15 +49,15 @@ var (
studioName = "studioName" studioName = "studioName"
// galleryChecksum = "galleryChecksum" // galleryChecksum = "galleryChecksum"
validMovie1 = 1 validGroup1 = 1
validMovie2 = 2 validGroup2 = 2
invalidMovie = 3 invalidGroup = 3
movie1Name = "movie1Name" group1Name = "group1Name"
movie2Name = "movie2Name" group2Name = "group2Name"
movie1Scene = 1 group1Scene = 1
movie2Scene = 2 group2Scene = 2
) )
var names = []string{ var names = []string{
@@ -330,82 +330,82 @@ func TestGetTagNames(t *testing.T) {
db.AssertExpectations(t) db.AssertExpectations(t)
} }
type sceneMoviesTestScenario struct { type sceneGroupsTestScenario struct {
input models.Scene input models.Scene
expected []jsonschema.SceneMovie expected []jsonschema.SceneGroup
err bool err bool
} }
var validMovies = models.NewRelatedMovies([]models.MoviesScenes{ var validGroups = models.NewRelatedGroups([]models.GroupsScenes{
{ {
MovieID: validMovie1, GroupID: validGroup1,
SceneIndex: &movie1Scene, SceneIndex: &group1Scene,
}, },
{ {
MovieID: validMovie2, GroupID: validGroup2,
SceneIndex: &movie2Scene, SceneIndex: &group2Scene,
}, },
}) })
var invalidMovies = models.NewRelatedMovies([]models.MoviesScenes{ var invalidGroups = models.NewRelatedGroups([]models.GroupsScenes{
{ {
MovieID: invalidMovie, GroupID: invalidGroup,
SceneIndex: &movie1Scene, SceneIndex: &group1Scene,
}, },
}) })
var getSceneMoviesJSONScenarios = []sceneMoviesTestScenario{ var getSceneGroupsJSONScenarios = []sceneGroupsTestScenario{
{ {
models.Scene{ models.Scene{
ID: sceneID, ID: sceneID,
Movies: validMovies, Groups: validGroups,
}, },
[]jsonschema.SceneMovie{ []jsonschema.SceneGroup{
{ {
MovieName: movie1Name, GroupName: group1Name,
SceneIndex: movie1Scene, SceneIndex: group1Scene,
}, },
{ {
MovieName: movie2Name, GroupName: group2Name,
SceneIndex: movie2Scene, SceneIndex: group2Scene,
}, },
}, },
false, false,
}, },
{ {
models.Scene{ models.Scene{
ID: noMoviesID, ID: noGroupsID,
Movies: models.NewRelatedMovies([]models.MoviesScenes{}), Groups: models.NewRelatedGroups([]models.GroupsScenes{}),
}, },
nil, nil,
false, false,
}, },
{ {
models.Scene{ models.Scene{
ID: errFindMovieID, ID: errFindGroupID,
Movies: invalidMovies, Groups: invalidGroups,
}, },
nil, nil,
true, true,
}, },
} }
func TestGetSceneMoviesJSON(t *testing.T) { func TestGetSceneGroupsJSON(t *testing.T) {
db := mocks.NewDatabase() db := mocks.NewDatabase()
movieErr := errors.New("error getting movie") groupErr := errors.New("error getting group")
db.Movie.On("Find", testCtx, validMovie1).Return(&models.Movie{ db.Group.On("Find", testCtx, validGroup1).Return(&models.Group{
Name: movie1Name, Name: group1Name,
}, nil).Once() }, nil).Once()
db.Movie.On("Find", testCtx, validMovie2).Return(&models.Movie{ db.Group.On("Find", testCtx, validGroup2).Return(&models.Group{
Name: movie2Name, Name: group2Name,
}, nil).Once() }, nil).Once()
db.Movie.On("Find", testCtx, invalidMovie).Return(nil, movieErr).Once() db.Group.On("Find", testCtx, invalidGroup).Return(nil, groupErr).Once()
for i, s := range getSceneMoviesJSONScenarios { for i, s := range getSceneGroupsJSONScenarios {
scene := s.input scene := s.input
json, err := GetSceneMoviesJSON(testCtx, db.Movie, &scene) json, err := GetSceneGroupsJSON(testCtx, db.Group, &scene)
switch { switch {
case !s.err && err != nil: case !s.err && err != nil:

View File

@@ -204,7 +204,7 @@ type sceneHolder struct {
mm string mm string
dd string dd string
performers []string performers []string
movies []string groups []string
studio string studio string
tags []string tags []string
} }
@@ -340,7 +340,7 @@ func (h *sceneHolder) setField(field parserField, value interface{}) {
case "studio": case "studio":
h.studio = value.(string) h.studio = value.(string)
case "movie": case "movie":
h.movies = append(h.movies, value.(string)) h.groups = append(h.groups, value.(string))
case "tag": case "tag":
h.tags = append(h.tags, value.(string)) h.tags = append(h.tags, value.(string))
case "yyyy": case "yyyy":
@@ -413,7 +413,7 @@ type FilenameParser struct {
repository FilenameParserRepository repository FilenameParserRepository
performerCache map[string]*models.Performer performerCache map[string]*models.Performer
studioCache map[string]*models.Studio studioCache map[string]*models.Studio
movieCache map[string]*models.Movie groupCache map[string]*models.Group
tagCache map[string]*models.Tag tagCache map[string]*models.Tag
} }
@@ -427,7 +427,7 @@ func NewFilenameParser(filter *models.FindFilterType, config models.SceneParserI
p.performerCache = make(map[string]*models.Performer) p.performerCache = make(map[string]*models.Performer)
p.studioCache = make(map[string]*models.Studio) p.studioCache = make(map[string]*models.Studio)
p.movieCache = make(map[string]*models.Movie) p.groupCache = make(map[string]*models.Group)
p.tagCache = make(map[string]*models.Tag) p.tagCache = make(map[string]*models.Tag)
p.initWhiteSpaceRegex() p.initWhiteSpaceRegex()
@@ -455,7 +455,7 @@ type FilenameParserRepository struct {
Scene models.SceneQueryer Scene models.SceneQueryer
Performer PerformerNamesFinder Performer PerformerNamesFinder
Studio models.StudioQueryer Studio models.StudioQueryer
Movie MovieNameFinder Group GroupNameFinder
Tag models.TagQueryer Tag models.TagQueryer
} }
@@ -464,7 +464,7 @@ func NewFilenameParserRepository(repo models.Repository) FilenameParserRepositor
Scene: repo.Scene, Scene: repo.Scene,
Performer: repo.Performer, Performer: repo.Performer,
Studio: repo.Studio, Studio: repo.Studio,
Movie: repo.Movie, Group: repo.Group,
Tag: repo.Tag, Tag: repo.Tag,
} }
} }
@@ -578,23 +578,23 @@ func (p *FilenameParser) queryStudio(ctx context.Context, qb models.StudioQuerye
return ret return ret
} }
type MovieNameFinder interface { type GroupNameFinder interface {
FindByName(ctx context.Context, name string, nocase bool) (*models.Movie, error) FindByName(ctx context.Context, name string, nocase bool) (*models.Group, error)
} }
func (p *FilenameParser) queryMovie(ctx context.Context, qb MovieNameFinder, movieName string) *models.Movie { func (p *FilenameParser) queryGroup(ctx context.Context, qb GroupNameFinder, groupName string) *models.Group {
// massage the movie name // massage the group name
movieName = delimiterRE.ReplaceAllString(movieName, " ") groupName = delimiterRE.ReplaceAllString(groupName, " ")
// check cache first // check cache first
if ret, found := p.movieCache[movieName]; found { if ret, found := p.groupCache[groupName]; found {
return ret return ret
} }
ret, _ := qb.FindByName(ctx, movieName, true) ret, _ := qb.FindByName(ctx, groupName, true)
// add result to cache // add result to cache
p.movieCache[movieName] = ret p.groupCache[groupName] = ret
return ret return ret
} }
@@ -665,18 +665,18 @@ func (p *FilenameParser) setStudio(ctx context.Context, qb models.StudioQueryer,
} }
} }
func (p *FilenameParser) setMovies(ctx context.Context, qb MovieNameFinder, h sceneHolder, result *models.SceneParserResult) { func (p *FilenameParser) setGroups(ctx context.Context, qb GroupNameFinder, h sceneHolder, result *models.SceneParserResult) {
// query for each movie // query for each group
moviesSet := make(map[int]bool) groupsSet := make(map[int]bool)
for _, movieName := range h.movies { for _, groupName := range h.groups {
if movieName != "" { if groupName != "" {
movie := p.queryMovie(ctx, qb, movieName) group := p.queryGroup(ctx, qb, groupName)
if movie != nil { if group != nil {
if _, found := moviesSet[movie.ID]; !found { if _, found := groupsSet[group.ID]; !found {
result.Movies = append(result.Movies, &models.SceneMovieID{ result.Movies = append(result.Movies, &models.SceneMovieID{
MovieID: strconv.Itoa(movie.ID), MovieID: strconv.Itoa(group.ID),
}) })
moviesSet[movie.ID] = true groupsSet[group.ID] = true
} }
} }
} }
@@ -714,7 +714,7 @@ func (p *FilenameParser) setParserResult(ctx context.Context, h sceneHolder, res
} }
p.setStudio(ctx, r.Studio, h, result) p.setStudio(ctx, r.Studio, h, result)
if len(h.movies) > 0 { if len(h.groups) > 0 {
p.setMovies(ctx, r.Movie, h, result) p.setGroups(ctx, r.Group, h, result)
} }
} }

View File

@@ -26,7 +26,7 @@ type Importer struct {
StudioWriter models.StudioFinderCreator StudioWriter models.StudioFinderCreator
GalleryFinder models.GalleryFinder GalleryFinder models.GalleryFinder
PerformerWriter models.PerformerFinderCreator PerformerWriter models.PerformerFinderCreator
MovieWriter models.MovieFinderCreator GroupWriter models.GroupFinderCreator
TagWriter models.TagFinderCreator TagWriter models.TagFinderCreator
Input jsonschema.Scene Input jsonschema.Scene
MissingRefBehaviour models.ImportMissingRefEnum MissingRefBehaviour models.ImportMissingRefEnum
@@ -62,7 +62,7 @@ func (i *Importer) PreImport(ctx context.Context) error {
return err return err
} }
if err := i.populateMovies(ctx); err != nil { if err := i.populateGroups(ctx); err != nil {
return err return err
} }
@@ -89,7 +89,7 @@ func (i *Importer) sceneJSONToScene(sceneJSON jsonschema.Scene) models.Scene {
PerformerIDs: models.NewRelatedIDs([]int{}), PerformerIDs: models.NewRelatedIDs([]int{}),
TagIDs: models.NewRelatedIDs([]int{}), TagIDs: models.NewRelatedIDs([]int{}),
GalleryIDs: models.NewRelatedIDs([]int{}), GalleryIDs: models.NewRelatedIDs([]int{}),
Movies: models.NewRelatedMovies([]models.MoviesScenes{}), Groups: models.NewRelatedGroups([]models.GroupsScenes{}),
StashIDs: models.NewRelatedStashIDs(sceneJSON.StashIDs), StashIDs: models.NewRelatedStashIDs(sceneJSON.StashIDs),
} }
@@ -335,24 +335,24 @@ func (i *Importer) createPerformers(ctx context.Context, names []string) ([]*mod
return ret, nil return ret, nil
} }
func (i *Importer) populateMovies(ctx context.Context) error { func (i *Importer) populateGroups(ctx context.Context) error {
if len(i.Input.Movies) > 0 { if len(i.Input.Groups) > 0 {
for _, inputMovie := range i.Input.Movies { for _, inputGroup := range i.Input.Groups {
movie, err := i.MovieWriter.FindByName(ctx, inputMovie.MovieName, false) group, err := i.GroupWriter.FindByName(ctx, inputGroup.GroupName, false)
if err != nil { if err != nil {
return fmt.Errorf("error finding scene movie: %v", err) return fmt.Errorf("error finding scene group: %v", err)
} }
var movieID int var groupID int
if movie == nil { if group == nil {
if i.MissingRefBehaviour == models.ImportMissingRefEnumFail { if i.MissingRefBehaviour == models.ImportMissingRefEnumFail {
return fmt.Errorf("scene movie [%s] not found", inputMovie.MovieName) return fmt.Errorf("scene group [%s] not found", inputGroup.GroupName)
} }
if i.MissingRefBehaviour == models.ImportMissingRefEnumCreate { if i.MissingRefBehaviour == models.ImportMissingRefEnumCreate {
movieID, err = i.createMovie(ctx, inputMovie.MovieName) groupID, err = i.createGroup(ctx, inputGroup.GroupName)
if err != nil { if err != nil {
return fmt.Errorf("error creating scene movie: %v", err) return fmt.Errorf("error creating scene group: %v", err)
} }
} }
@@ -361,35 +361,35 @@ func (i *Importer) populateMovies(ctx context.Context) error {
continue continue
} }
} else { } else {
movieID = movie.ID groupID = group.ID
} }
toAdd := models.MoviesScenes{ toAdd := models.GroupsScenes{
MovieID: movieID, GroupID: groupID,
} }
if inputMovie.SceneIndex != 0 { if inputGroup.SceneIndex != 0 {
index := inputMovie.SceneIndex index := inputGroup.SceneIndex
toAdd.SceneIndex = &index toAdd.SceneIndex = &index
} }
i.scene.Movies.Add(toAdd) i.scene.Groups.Add(toAdd)
} }
} }
return nil return nil
} }
func (i *Importer) createMovie(ctx context.Context, name string) (int, error) { func (i *Importer) createGroup(ctx context.Context, name string) (int, error) {
newMovie := models.NewMovie() newGroup := models.NewGroup()
newMovie.Name = name newGroup.Name = name
err := i.MovieWriter.Create(ctx, &newMovie) err := i.GroupWriter.Create(ctx, &newGroup)
if err != nil { if err != nil {
return 0, err return 0, err
} }
return newMovie.ID, nil return newGroup.ID, nil
} }
func (i *Importer) populateTags(ctx context.Context) error { func (i *Importer) populateTags(ctx context.Context) error {

View File

@@ -17,7 +17,7 @@ const invalidImage = "aW1hZ2VCeXRlcw&&"
var ( var (
existingStudioID = 101 existingStudioID = 101
existingPerformerID = 103 existingPerformerID = 103
existingMovieID = 104 existingGroupID = 104
existingTagID = 105 existingTagID = 105
existingStudioName = "existingStudioName" existingStudioName = "existingStudioName"
@@ -28,9 +28,9 @@ var (
existingPerformerErr = "existingPerformerErr" existingPerformerErr = "existingPerformerErr"
missingPerformerName = "missingPerformerName" missingPerformerName = "missingPerformerName"
existingMovieName = "existingMovieName" existingGroupName = "existingGroupName"
existingMovieErr = "existingMovieErr" existingGroupErr = "existingGroupErr"
missingMovieName = "missingMovieName" missingGroupName = "missingGroupName"
existingTagName = "existingTagName" existingTagName = "existingTagName"
existingTagErr = "existingTagErr" existingTagErr = "existingTagErr"
@@ -221,58 +221,58 @@ func TestImporterPreImportWithMissingPerformerCreateErr(t *testing.T) {
db.AssertExpectations(t) db.AssertExpectations(t)
} }
func TestImporterPreImportWithMovie(t *testing.T) { func TestImporterPreImportWithGroup(t *testing.T) {
db := mocks.NewDatabase() db := mocks.NewDatabase()
i := Importer{ i := Importer{
MovieWriter: db.Movie, GroupWriter: db.Group,
MissingRefBehaviour: models.ImportMissingRefEnumFail, MissingRefBehaviour: models.ImportMissingRefEnumFail,
Input: jsonschema.Scene{ Input: jsonschema.Scene{
Movies: []jsonschema.SceneMovie{ Groups: []jsonschema.SceneGroup{
{ {
MovieName: existingMovieName, GroupName: existingGroupName,
SceneIndex: 1, SceneIndex: 1,
}, },
}, },
}, },
} }
db.Movie.On("FindByName", testCtx, existingMovieName, false).Return(&models.Movie{ db.Group.On("FindByName", testCtx, existingGroupName, false).Return(&models.Group{
ID: existingMovieID, ID: existingGroupID,
Name: existingMovieName, Name: existingGroupName,
}, nil).Once() }, nil).Once()
db.Movie.On("FindByName", testCtx, existingMovieErr, false).Return(nil, errors.New("FindByName error")).Once() db.Group.On("FindByName", testCtx, existingGroupErr, false).Return(nil, errors.New("FindByName error")).Once()
err := i.PreImport(testCtx) err := i.PreImport(testCtx)
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, existingMovieID, i.scene.Movies.List()[0].MovieID) assert.Equal(t, existingGroupID, i.scene.Groups.List()[0].GroupID)
i.Input.Movies[0].MovieName = existingMovieErr i.Input.Groups[0].GroupName = existingGroupErr
err = i.PreImport(testCtx) err = i.PreImport(testCtx)
assert.NotNil(t, err) assert.NotNil(t, err)
db.AssertExpectations(t) db.AssertExpectations(t)
} }
func TestImporterPreImportWithMissingMovie(t *testing.T) { func TestImporterPreImportWithMissingGroup(t *testing.T) {
db := mocks.NewDatabase() db := mocks.NewDatabase()
i := Importer{ i := Importer{
MovieWriter: db.Movie, GroupWriter: db.Group,
Input: jsonschema.Scene{ Input: jsonschema.Scene{
Movies: []jsonschema.SceneMovie{ Groups: []jsonschema.SceneGroup{
{ {
MovieName: missingMovieName, GroupName: missingGroupName,
}, },
}, },
}, },
MissingRefBehaviour: models.ImportMissingRefEnumFail, MissingRefBehaviour: models.ImportMissingRefEnumFail,
} }
db.Movie.On("FindByName", testCtx, missingMovieName, false).Return(nil, nil).Times(3) db.Group.On("FindByName", testCtx, missingGroupName, false).Return(nil, nil).Times(3)
db.Movie.On("Create", testCtx, mock.AnythingOfType("*models.Movie")).Run(func(args mock.Arguments) { db.Group.On("Create", testCtx, mock.AnythingOfType("*models.Group")).Run(func(args mock.Arguments) {
m := args.Get(1).(*models.Movie) m := args.Get(1).(*models.Group)
m.ID = existingMovieID m.ID = existingGroupID
}).Return(nil) }).Return(nil)
err := i.PreImport(testCtx) err := i.PreImport(testCtx)
@@ -285,28 +285,28 @@ func TestImporterPreImportWithMissingMovie(t *testing.T) {
i.MissingRefBehaviour = models.ImportMissingRefEnumCreate i.MissingRefBehaviour = models.ImportMissingRefEnumCreate
err = i.PreImport(testCtx) err = i.PreImport(testCtx)
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, existingMovieID, i.scene.Movies.List()[0].MovieID) assert.Equal(t, existingGroupID, i.scene.Groups.List()[0].GroupID)
db.AssertExpectations(t) db.AssertExpectations(t)
} }
func TestImporterPreImportWithMissingMovieCreateErr(t *testing.T) { func TestImporterPreImportWithMissingGroupCreateErr(t *testing.T) {
db := mocks.NewDatabase() db := mocks.NewDatabase()
i := Importer{ i := Importer{
MovieWriter: db.Movie, GroupWriter: db.Group,
Input: jsonschema.Scene{ Input: jsonschema.Scene{
Movies: []jsonschema.SceneMovie{ Groups: []jsonschema.SceneGroup{
{ {
MovieName: missingMovieName, GroupName: missingGroupName,
}, },
}, },
}, },
MissingRefBehaviour: models.ImportMissingRefEnumCreate, MissingRefBehaviour: models.ImportMissingRefEnumCreate,
} }
db.Movie.On("FindByName", testCtx, missingMovieName, false).Return(nil, nil).Once() db.Group.On("FindByName", testCtx, missingGroupName, false).Return(nil, nil).Once()
db.Movie.On("Create", testCtx, mock.AnythingOfType("*models.Movie")).Return(errors.New("Create error")) db.Group.On("Create", testCtx, mock.AnythingOfType("*models.Group")).Return(errors.New("Create error"))
err := i.PreImport(testCtx) err := i.PreImport(testCtx)
assert.NotNil(t, err) assert.NotNil(t, err)

View File

@@ -84,7 +84,7 @@ type Repository struct {
GalleryFinder GalleryFinder GalleryFinder GalleryFinder
TagFinder TagFinder TagFinder TagFinder
PerformerFinder PerformerFinder PerformerFinder PerformerFinder
MovieFinder match.MovieNamesFinder GroupFinder match.GroupNamesFinder
StudioFinder StudioFinder StudioFinder StudioFinder
} }
@@ -95,7 +95,7 @@ func NewRepository(repo models.Repository) Repository {
GalleryFinder: repo.Gallery, GalleryFinder: repo.Gallery,
TagFinder: repo.Tag, TagFinder: repo.Tag,
PerformerFinder: repo.Performer, PerformerFinder: repo.Performer,
MovieFinder: repo.Movie, GroupFinder: repo.Group,
StudioFinder: repo.Studio, StudioFinder: repo.Studio,
} }
} }

View File

@@ -45,8 +45,9 @@ type config struct {
// Configuration for querying a gallery by a URL // Configuration for querying a gallery by a URL
GalleryByURL []*scrapeByURLConfig `yaml:"galleryByURL"` GalleryByURL []*scrapeByURLConfig `yaml:"galleryByURL"`
// Configuration for querying a movie by a URL // Configuration for querying a movie by a URL - deprecated, use GroupByURL
MovieByURL []*scrapeByURLConfig `yaml:"movieByURL"` MovieByURL []*scrapeByURLConfig `yaml:"movieByURL"`
GroupByURL []*scrapeByURLConfig `yaml:"groupByURL"`
// Scraper debugging options // Scraper debugging options
DebugOptions *scraperDebugOptions `yaml:"debug"` DebugOptions *scraperDebugOptions `yaml:"debug"`
@@ -99,7 +100,11 @@ func (c config) validate() error {
} }
} }
for _, s := range c.MovieByURL { if len(c.MovieByURL) > 0 && len(c.GroupByURL) > 0 {
return errors.New("movieByURL disallowed if groupByURL is present")
}
for _, s := range append(c.MovieByURL, c.GroupByURL...) {
if err := s.validate(); err != nil { if err := s.validate(); err != nil {
return err return err
} }
@@ -289,17 +294,17 @@ func (c config) spec() Scraper {
ret.Gallery = &gallery ret.Gallery = &gallery
} }
movie := ScraperSpec{} group := ScraperSpec{}
if len(c.MovieByURL) > 0 { if len(c.MovieByURL) > 0 || len(c.GroupByURL) > 0 {
movie.SupportedScrapes = append(movie.SupportedScrapes, ScrapeTypeURL) group.SupportedScrapes = append(group.SupportedScrapes, ScrapeTypeURL)
for _, v := range c.MovieByURL { for _, v := range append(c.MovieByURL, c.GroupByURL...) {
movie.Urls = append(movie.Urls, v.URL...) group.Urls = append(group.Urls, v.URL...)
} }
} }
if len(movie.SupportedScrapes) > 0 { if len(group.SupportedScrapes) > 0 {
ret.Movie = &movie ret.Movie = &group
ret.Group = &movie ret.Group = &group
} }
return ret return ret
@@ -314,7 +319,7 @@ func (c config) supports(ty ScrapeContentType) bool {
case ScrapeContentTypeGallery: case ScrapeContentTypeGallery:
return c.GalleryByFragment != nil || len(c.GalleryByURL) > 0 return c.GalleryByFragment != nil || len(c.GalleryByURL) > 0
case ScrapeContentTypeMovie, ScrapeContentTypeGroup: case ScrapeContentTypeMovie, ScrapeContentTypeGroup:
return len(c.MovieByURL) > 0 return len(c.MovieByURL) > 0 || len(c.GroupByURL) > 0
} }
panic("Unhandled ScrapeContentType") panic("Unhandled ScrapeContentType")

View File

@@ -82,7 +82,7 @@ func loadUrlCandidates(c config, ty ScrapeContentType) []*scrapeByURLConfig {
case ScrapeContentTypeScene: case ScrapeContentTypeScene:
return c.SceneByURL return c.SceneByURL
case ScrapeContentTypeMovie, ScrapeContentTypeGroup: case ScrapeContentTypeMovie, ScrapeContentTypeGroup:
return c.MovieByURL return append(c.MovieByURL, c.GroupByURL...)
case ScrapeContentTypeGallery: case ScrapeContentTypeGallery:
return c.GalleryByURL return c.GalleryByURL
} }

View File

@@ -88,6 +88,40 @@ func setMovieBackImage(ctx context.Context, client *http.Client, m *models.Scrap
return nil return nil
} }
func setGroupFrontImage(ctx context.Context, client *http.Client, m *models.ScrapedGroup, globalConfig GlobalConfig) error {
// don't try to get the image if it doesn't appear to be a URL
if m.FrontImage == nil || !strings.HasPrefix(*m.FrontImage, "http") {
// nothing to do
return nil
}
img, err := getImage(ctx, *m.FrontImage, client, globalConfig)
if err != nil {
return err
}
m.FrontImage = img
return nil
}
func setGroupBackImage(ctx context.Context, client *http.Client, m *models.ScrapedGroup, globalConfig GlobalConfig) error {
// don't try to get the image if it doesn't appear to be a URL
if m.BackImage == nil || !strings.HasPrefix(*m.BackImage, "http") {
// nothing to do
return nil
}
img, err := getImage(ctx, *m.BackImage, client, globalConfig)
if err != nil {
return err
}
m.BackImage = img
return nil
}
func getImage(ctx context.Context, url string, client *http.Client, globalConfig GlobalConfig) (*string, error) { func getImage(ctx context.Context, url string, client *http.Client, globalConfig GlobalConfig) (*string, error) {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil { if err != nil {

View File

@@ -103,7 +103,7 @@ func (s *jsonScraper) scrapeByURL(ctx context.Context, url string, ty ScrapeCont
} }
return ret, nil return ret, nil
case ScrapeContentTypeMovie, ScrapeContentTypeGroup: case ScrapeContentTypeMovie, ScrapeContentTypeGroup:
ret, err := scraper.scrapeMovie(ctx, q) ret, err := scraper.scrapeGroup(ctx, q)
if err != nil || ret == nil { if err != nil || ret == nil {
return nil, err return nil, err
} }

View File

@@ -1079,7 +1079,7 @@ func (s mappedScraper) scrapeGallery(ctx context.Context, q mappedQuery) (*Scrap
return &ret, nil return &ret, nil
} }
func (s mappedScraper) scrapeMovie(ctx context.Context, q mappedQuery) (*models.ScrapedMovie, error) { func (s mappedScraper) scrapeGroup(ctx context.Context, q mappedQuery) (*models.ScrapedMovie, error) {
var ret models.ScrapedMovie var ret models.ScrapedMovie
movieScraperConfig := s.Movie movieScraperConfig := s.Movie

View File

@@ -39,6 +39,12 @@ func (c Cache) postScrape(ctx context.Context, content ScrapedContent) (ScrapedC
} }
case models.ScrapedMovie: case models.ScrapedMovie:
return c.postScrapeMovie(ctx, v) return c.postScrapeMovie(ctx, v)
case *models.ScrapedGroup:
if v != nil {
return c.postScrapeGroup(ctx, *v)
}
case models.ScrapedGroup:
return c.postScrapeGroup(ctx, v)
} }
// If nothing matches, pass the content through // If nothing matches, pass the content through
@@ -128,6 +134,38 @@ func (c Cache) postScrapeMovie(ctx context.Context, m models.ScrapedMovie) (Scra
return m, nil return m, nil
} }
func (c Cache) postScrapeGroup(ctx context.Context, m models.ScrapedGroup) (ScrapedContent, error) {
r := c.repository
if err := r.WithReadTxn(ctx, func(ctx context.Context) error {
tqb := r.TagFinder
tags, err := postProcessTags(ctx, tqb, m.Tags)
if err != nil {
return err
}
m.Tags = tags
if m.Studio != nil {
if err := match.ScrapedStudio(ctx, r.StudioFinder, m.Studio, nil); err != nil {
return err
}
}
return nil
}); err != nil {
return nil, err
}
// post-process - set the image if applicable
if err := setGroupFrontImage(ctx, c.client, &m, c.globalConfig); err != nil {
logger.Warnf("could not set front image using URL %s: %v", *m.FrontImage, err)
}
if err := setGroupBackImage(ctx, c.client, &m, c.globalConfig); err != nil {
logger.Warnf("could not set back image using URL %s: %v", *m.BackImage, err)
}
return m, nil
}
func (c Cache) postScrapeScenePerformer(ctx context.Context, p models.ScrapedPerformer) error { func (c Cache) postScrapeScenePerformer(ctx context.Context, p models.ScrapedPerformer) error {
tqb := c.repository.TagFinder tqb := c.repository.TagFinder
@@ -154,7 +192,7 @@ func (c Cache) postScrapeScene(ctx context.Context, scene ScrapedScene) (Scraped
r := c.repository r := c.repository
if err := r.WithReadTxn(ctx, func(ctx context.Context) error { if err := r.WithReadTxn(ctx, func(ctx context.Context) error {
pqb := r.PerformerFinder pqb := r.PerformerFinder
mqb := r.MovieFinder gqb := r.GroupFinder
tqb := r.TagFinder tqb := r.TagFinder
sqb := r.StudioFinder sqb := r.StudioFinder
@@ -173,10 +211,39 @@ func (c Cache) postScrapeScene(ctx context.Context, scene ScrapedScene) (Scraped
} }
for _, p := range scene.Movies { for _, p := range scene.Movies {
err := match.ScrapedMovie(ctx, mqb, p) matchedID, err := match.ScrapedGroup(ctx, gqb, p.StoredID, p.Name)
if err != nil { if err != nil {
return err return err
} }
if matchedID != nil {
p.StoredID = matchedID
}
}
for _, p := range scene.Groups {
matchedID, err := match.ScrapedGroup(ctx, gqb, p.StoredID, p.Name)
if err != nil {
return err
}
if matchedID != nil {
p.StoredID = matchedID
}
}
// HACK - if movies was returned but not groups, add the groups from the movies
// if groups was returned but not movies, add the movies from the groups for backward compatibility
if len(scene.Movies) > 0 && len(scene.Groups) == 0 {
for _, m := range scene.Movies {
g := m.ScrapedGroup()
scene.Groups = append(scene.Groups, &g)
}
} else if len(scene.Groups) > 0 && len(scene.Movies) == 0 {
for _, g := range scene.Groups {
m := g.ScrapedMovie()
scene.Movies = append(scene.Movies, &m)
}
} }
tags, err := postProcessTags(ctx, tqb, scene.Tags) tags, err := postProcessTags(ctx, tqb, scene.Tags)

View File

@@ -84,7 +84,7 @@ func (s *xpathScraper) scrapeByURL(ctx context.Context, url string, ty ScrapeCon
} }
return ret, nil return ret, nil
case ScrapeContentTypeMovie, ScrapeContentTypeGroup: case ScrapeContentTypeMovie, ScrapeContentTypeGroup:
ret, err := scraper.scrapeMovie(ctx, q) ret, err := scraper.scrapeGroup(ctx, q)
if err != nil || ret == nil { if err != nil || ret == nil {
return nil, err return nil, err
} }

View File

@@ -57,7 +57,7 @@ func (db *Anonymiser) Anonymise(ctx context.Context) error {
func() error { return db.anonymisePerformers(ctx) }, func() error { return db.anonymisePerformers(ctx) },
func() error { return db.anonymiseStudios(ctx) }, func() error { return db.anonymiseStudios(ctx) },
func() error { return db.anonymiseTags(ctx) }, func() error { return db.anonymiseTags(ctx) },
func() error { return db.anonymiseMovies(ctx) }, func() error { return db.anonymiseGroups(ctx) },
func() error { return db.Optimise(ctx) }, func() error { return db.Optimise(ctx) },
}) })
}(); err != nil { }(); err != nil {
@@ -825,9 +825,9 @@ func (db *Anonymiser) anonymiseTags(ctx context.Context) error {
return nil return nil
} }
func (db *Anonymiser) anonymiseMovies(ctx context.Context) error { func (db *Anonymiser) anonymiseGroups(ctx context.Context) error {
logger.Infof("Anonymising movies") logger.Infof("Anonymising groups")
table := movieTableMgr.table table := groupTableMgr.table
lastID := 0 lastID := 0
total := 0 total := 0
const logEvery = 10000 const logEvery = 10000
@@ -883,7 +883,7 @@ func (db *Anonymiser) anonymiseMovies(ctx context.Context) error {
total++ total++
if total%logEvery == 0 { if total%logEvery == 0 {
logger.Infof("Anonymised %d movies", total) logger.Infof("Anonymised %d groups", total)
} }
return nil return nil
@@ -893,7 +893,7 @@ func (db *Anonymiser) anonymiseMovies(ctx context.Context) error {
} }
} }
if err := db.anonymiseURLs(ctx, goqu.T(movieURLsTable), "movie_id"); err != nil { if err := db.anonymiseURLs(ctx, goqu.T(groupURLsTable), "movie_id"); err != nil {
return err return err
} }

View File

@@ -12,7 +12,7 @@ import (
) )
type updateImageFunc func(ctx context.Context, id int, image []byte) error type updateImageFunc func(ctx context.Context, id int, image []byte) error
type getImageFunc func(ctx context.Context, movieID int) ([]byte, error) type getImageFunc func(ctx context.Context, id int) ([]byte, error)
func testUpdateImage(t *testing.T, ctx context.Context, id int, updateFn updateImageFunc, getFn getImageFunc) error { func testUpdateImage(t *testing.T, ctx context.Context, id int, updateFn updateImageFunc, getFn getImageFunc) error {
image := []byte("image") image := []byte("image")

View File

@@ -74,7 +74,7 @@ type storeRepository struct {
SavedFilter *SavedFilterStore SavedFilter *SavedFilterStore
Studio *StudioStore Studio *StudioStore
Tag *TagStore Tag *TagStore
Movie *MovieStore Group *GroupStore
} }
type Database struct { type Database struct {
@@ -110,7 +110,7 @@ func NewDatabase() *Database {
Performer: performerStore, Performer: performerStore,
Studio: studioStore, Studio: studioStore,
Tag: tagStore, Tag: tagStore,
Movie: NewMovieStore(blobStore), Group: NewGroupStore(blobStore),
SavedFilter: NewSavedFilterStore(), SavedFilter: NewSavedFilterStore(),
} }

View File

@@ -17,19 +17,19 @@ import (
) )
const ( const (
movieTable = "movies" groupTable = "movies"
movieIDColumn = "movie_id" groupIDColumn = "movie_id"
movieFrontImageBlobColumn = "front_image_blob" groupFrontImageBlobColumn = "front_image_blob"
movieBackImageBlobColumn = "back_image_blob" groupBackImageBlobColumn = "back_image_blob"
moviesTagsTable = "movies_tags" groupsTagsTable = "movies_tags"
movieURLsTable = "movie_urls" groupURLsTable = "movie_urls"
movieURLColumn = "url" groupURLColumn = "url"
) )
type movieRow struct { type groupRow struct {
ID int `db:"id" goqu:"skipinsert"` ID int `db:"id" goqu:"skipinsert"`
Name zero.String `db:"name"` Name zero.String `db:"name"`
Aliases zero.String `db:"aliases"` Aliases zero.String `db:"aliases"`
@@ -48,7 +48,7 @@ type movieRow struct {
BackImageBlob zero.String `db:"back_image_blob"` BackImageBlob zero.String `db:"back_image_blob"`
} }
func (r *movieRow) fromMovie(o models.Movie) { func (r *groupRow) fromGroup(o models.Group) {
r.ID = o.ID r.ID = o.ID
r.Name = zero.StringFrom(o.Name) r.Name = zero.StringFrom(o.Name)
r.Aliases = zero.StringFrom(o.Aliases) r.Aliases = zero.StringFrom(o.Aliases)
@@ -62,8 +62,8 @@ func (r *movieRow) fromMovie(o models.Movie) {
r.UpdatedAt = Timestamp{Timestamp: o.UpdatedAt} r.UpdatedAt = Timestamp{Timestamp: o.UpdatedAt}
} }
func (r *movieRow) resolve() *models.Movie { func (r *groupRow) resolve() *models.Group {
ret := &models.Movie{ ret := &models.Group{
ID: r.ID, ID: r.ID,
Name: r.Name.String, Name: r.Name.String,
Aliases: r.Aliases.String, Aliases: r.Aliases.String,
@@ -80,11 +80,11 @@ func (r *movieRow) resolve() *models.Movie {
return ret return ret
} }
type movieRowRecord struct { type groupRowRecord struct {
updateRecord updateRecord
} }
func (r *movieRowRecord) fromPartial(o models.MoviePartial) { func (r *groupRowRecord) fromPartial(o models.GroupPartial) {
r.setNullString("name", o.Name) r.setNullString("name", o.Name)
r.setNullString("aliases", o.Aliases) r.setNullString("aliases", o.Aliases)
r.setNullInt("duration", o.Duration) r.setNullInt("duration", o.Duration)
@@ -97,26 +97,26 @@ func (r *movieRowRecord) fromPartial(o models.MoviePartial) {
r.setTimestamp("updated_at", o.UpdatedAt) r.setTimestamp("updated_at", o.UpdatedAt)
} }
type movieRepositoryType struct { type groupRepositoryType struct {
repository repository
scenes repository scenes repository
tags joinRepository tags joinRepository
} }
var ( var (
movieRepository = movieRepositoryType{ groupRepository = groupRepositoryType{
repository: repository{ repository: repository{
tableName: movieTable, tableName: groupTable,
idColumn: idColumn, idColumn: idColumn,
}, },
scenes: repository{ scenes: repository{
tableName: moviesScenesTable, tableName: groupsScenesTable,
idColumn: movieIDColumn, idColumn: groupIDColumn,
}, },
tags: joinRepository{ tags: joinRepository{
repository: repository{ repository: repository{
tableName: moviesTagsTable, tableName: groupsTagsTable,
idColumn: movieIDColumn, idColumn: groupIDColumn,
}, },
fkColumn: tagIDColumn, fkColumn: tagIDColumn,
foreignTable: tagTable, foreignTable: tagTable,
@@ -125,40 +125,40 @@ var (
} }
) )
type MovieStore struct { type GroupStore struct {
blobJoinQueryBuilder blobJoinQueryBuilder
tagRelationshipStore tagRelationshipStore
tableMgr *table tableMgr *table
} }
func NewMovieStore(blobStore *BlobStore) *MovieStore { func NewGroupStore(blobStore *BlobStore) *GroupStore {
return &MovieStore{ return &GroupStore{
blobJoinQueryBuilder: blobJoinQueryBuilder{ blobJoinQueryBuilder: blobJoinQueryBuilder{
blobStore: blobStore, blobStore: blobStore,
joinTable: movieTable, joinTable: groupTable,
}, },
tagRelationshipStore: tagRelationshipStore{ tagRelationshipStore: tagRelationshipStore{
idRelationshipStore: idRelationshipStore{ idRelationshipStore: idRelationshipStore{
joinTable: moviesTagsTableMgr, joinTable: groupsTagsTableMgr,
}, },
}, },
tableMgr: movieTableMgr, tableMgr: groupTableMgr,
} }
} }
func (qb *MovieStore) table() exp.IdentifierExpression { func (qb *GroupStore) table() exp.IdentifierExpression {
return qb.tableMgr.table return qb.tableMgr.table
} }
func (qb *MovieStore) selectDataset() *goqu.SelectDataset { func (qb *GroupStore) selectDataset() *goqu.SelectDataset {
return dialect.From(qb.table()).Select(qb.table().All()) return dialect.From(qb.table()).Select(qb.table().All())
} }
func (qb *MovieStore) Create(ctx context.Context, newObject *models.Movie) error { func (qb *GroupStore) Create(ctx context.Context, newObject *models.Group) error {
var r movieRow var r groupRow
r.fromMovie(*newObject) r.fromGroup(*newObject)
id, err := qb.tableMgr.insertID(ctx, r) id, err := qb.tableMgr.insertID(ctx, r)
if err != nil { if err != nil {
@@ -167,7 +167,7 @@ func (qb *MovieStore) Create(ctx context.Context, newObject *models.Movie) error
if newObject.URLs.Loaded() { if newObject.URLs.Loaded() {
const startPos = 0 const startPos = 0
if err := moviesURLsTableMgr.insertJoins(ctx, id, startPos, newObject.URLs.List()); err != nil { if err := groupsURLsTableMgr.insertJoins(ctx, id, startPos, newObject.URLs.List()); err != nil {
return err return err
} }
} }
@@ -186,8 +186,8 @@ func (qb *MovieStore) Create(ctx context.Context, newObject *models.Movie) error
return nil return nil
} }
func (qb *MovieStore) UpdatePartial(ctx context.Context, id int, partial models.MoviePartial) (*models.Movie, error) { func (qb *GroupStore) UpdatePartial(ctx context.Context, id int, partial models.GroupPartial) (*models.Group, error) {
r := movieRowRecord{ r := groupRowRecord{
updateRecord{ updateRecord{
Record: make(exp.Record), Record: make(exp.Record),
}, },
@@ -202,7 +202,7 @@ func (qb *MovieStore) UpdatePartial(ctx context.Context, id int, partial models.
} }
if partial.URLs != nil { if partial.URLs != nil {
if err := moviesURLsTableMgr.modifyJoins(ctx, id, partial.URLs.Values, partial.URLs.Mode); err != nil { if err := groupsURLsTableMgr.modifyJoins(ctx, id, partial.URLs.Values, partial.URLs.Mode); err != nil {
return nil, err return nil, err
} }
} }
@@ -214,16 +214,16 @@ func (qb *MovieStore) UpdatePartial(ctx context.Context, id int, partial models.
return qb.find(ctx, id) return qb.find(ctx, id)
} }
func (qb *MovieStore) Update(ctx context.Context, updatedObject *models.Movie) error { func (qb *GroupStore) Update(ctx context.Context, updatedObject *models.Group) error {
var r movieRow var r groupRow
r.fromMovie(*updatedObject) r.fromGroup(*updatedObject)
if err := qb.tableMgr.updateByID(ctx, updatedObject.ID, r); err != nil { if err := qb.tableMgr.updateByID(ctx, updatedObject.ID, r); err != nil {
return err return err
} }
if updatedObject.URLs.Loaded() { if updatedObject.URLs.Loaded() {
if err := moviesURLsTableMgr.replaceJoins(ctx, updatedObject.ID, updatedObject.URLs.List()); err != nil { if err := groupsURLsTableMgr.replaceJoins(ctx, updatedObject.ID, updatedObject.URLs.List()); err != nil {
return err return err
} }
} }
@@ -235,17 +235,17 @@ func (qb *MovieStore) Update(ctx context.Context, updatedObject *models.Movie) e
return nil return nil
} }
func (qb *MovieStore) Destroy(ctx context.Context, id int) error { func (qb *GroupStore) Destroy(ctx context.Context, id int) error {
// must handle image checksums manually // must handle image checksums manually
if err := qb.destroyImages(ctx, id); err != nil { if err := qb.destroyImages(ctx, id); err != nil {
return err return err
} }
return movieRepository.destroyExisting(ctx, []int{id}) return groupRepository.destroyExisting(ctx, []int{id})
} }
// returns nil, nil if not found // returns nil, nil if not found
func (qb *MovieStore) Find(ctx context.Context, id int) (*models.Movie, error) { func (qb *GroupStore) Find(ctx context.Context, id int) (*models.Group, error) {
ret, err := qb.find(ctx, id) ret, err := qb.find(ctx, id)
if errors.Is(err, sql.ErrNoRows) { if errors.Is(err, sql.ErrNoRows) {
return nil, nil return nil, nil
@@ -253,8 +253,8 @@ func (qb *MovieStore) Find(ctx context.Context, id int) (*models.Movie, error) {
return ret, err return ret, err
} }
func (qb *MovieStore) FindMany(ctx context.Context, ids []int) ([]*models.Movie, error) { func (qb *GroupStore) FindMany(ctx context.Context, ids []int) ([]*models.Group, error) {
ret := make([]*models.Movie, len(ids)) ret := make([]*models.Group, len(ids))
table := qb.table() table := qb.table()
if err := batchExec(ids, defaultBatchSize, func(batch []int) error { if err := batchExec(ids, defaultBatchSize, func(batch []int) error {
@@ -276,7 +276,7 @@ func (qb *MovieStore) FindMany(ctx context.Context, ids []int) ([]*models.Movie,
for i := range ret { for i := range ret {
if ret[i] == nil { if ret[i] == nil {
return nil, fmt.Errorf("movie with id %d not found", ids[i]) return nil, fmt.Errorf("group with id %d not found", ids[i])
} }
} }
@@ -284,7 +284,7 @@ func (qb *MovieStore) FindMany(ctx context.Context, ids []int) ([]*models.Movie,
} }
// returns nil, sql.ErrNoRows if not found // returns nil, sql.ErrNoRows if not found
func (qb *MovieStore) find(ctx context.Context, id int) (*models.Movie, error) { func (qb *GroupStore) find(ctx context.Context, id int) (*models.Group, error) {
q := qb.selectDataset().Where(qb.tableMgr.byID(id)) q := qb.selectDataset().Where(qb.tableMgr.byID(id))
ret, err := qb.get(ctx, q) ret, err := qb.get(ctx, q)
@@ -296,7 +296,7 @@ func (qb *MovieStore) find(ctx context.Context, id int) (*models.Movie, error) {
} }
// returns nil, sql.ErrNoRows if not found // returns nil, sql.ErrNoRows if not found
func (qb *MovieStore) get(ctx context.Context, q *goqu.SelectDataset) (*models.Movie, error) { func (qb *GroupStore) get(ctx context.Context, q *goqu.SelectDataset) (*models.Group, error) {
ret, err := qb.getMany(ctx, q) ret, err := qb.getMany(ctx, q)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -309,11 +309,11 @@ func (qb *MovieStore) get(ctx context.Context, q *goqu.SelectDataset) (*models.M
return ret[0], nil return ret[0], nil
} }
func (qb *MovieStore) getMany(ctx context.Context, q *goqu.SelectDataset) ([]*models.Movie, error) { func (qb *GroupStore) getMany(ctx context.Context, q *goqu.SelectDataset) ([]*models.Group, error) {
const single = false const single = false
var ret []*models.Movie var ret []*models.Group
if err := queryFunc(ctx, q, single, func(r *sqlx.Rows) error { if err := queryFunc(ctx, q, single, func(r *sqlx.Rows) error {
var f movieRow var f groupRow
if err := r.StructScan(&f); err != nil { if err := r.StructScan(&f); err != nil {
return err return err
} }
@@ -329,7 +329,7 @@ func (qb *MovieStore) getMany(ctx context.Context, q *goqu.SelectDataset) ([]*mo
return ret, nil return ret, nil
} }
func (qb *MovieStore) FindByName(ctx context.Context, name string, nocase bool) (*models.Movie, error) { func (qb *GroupStore) FindByName(ctx context.Context, name string, nocase bool) (*models.Group, error) {
// query := "SELECT * FROM movies WHERE name = ?" // query := "SELECT * FROM movies WHERE name = ?"
// if nocase { // if nocase {
// query += " COLLATE NOCASE" // query += " COLLATE NOCASE"
@@ -349,7 +349,7 @@ func (qb *MovieStore) FindByName(ctx context.Context, name string, nocase bool)
return ret, nil return ret, nil
} }
func (qb *MovieStore) FindByNames(ctx context.Context, names []string, nocase bool) ([]*models.Movie, error) { func (qb *GroupStore) FindByNames(ctx context.Context, names []string, nocase bool) ([]*models.Group, error) {
// query := "SELECT * FROM movies WHERE name" // query := "SELECT * FROM movies WHERE name"
// if nocase { // if nocase {
// query += " COLLATE NOCASE" // query += " COLLATE NOCASE"
@@ -374,12 +374,12 @@ func (qb *MovieStore) FindByNames(ctx context.Context, names []string, nocase bo
return ret, nil return ret, nil
} }
func (qb *MovieStore) Count(ctx context.Context) (int, error) { func (qb *GroupStore) Count(ctx context.Context) (int, error) {
q := dialect.Select(goqu.COUNT("*")).From(qb.table()) q := dialect.Select(goqu.COUNT("*")).From(qb.table())
return count(ctx, q) return count(ctx, q)
} }
func (qb *MovieStore) All(ctx context.Context) ([]*models.Movie, error) { func (qb *GroupStore) All(ctx context.Context) ([]*models.Group, error) {
table := qb.table() table := qb.table()
return qb.getMany(ctx, qb.selectDataset().Order( return qb.getMany(ctx, qb.selectDataset().Order(
@@ -388,24 +388,24 @@ func (qb *MovieStore) All(ctx context.Context) ([]*models.Movie, error) {
)) ))
} }
func (qb *MovieStore) makeQuery(ctx context.Context, movieFilter *models.MovieFilterType, findFilter *models.FindFilterType) (*queryBuilder, error) { func (qb *GroupStore) makeQuery(ctx context.Context, groupFilter *models.GroupFilterType, findFilter *models.FindFilterType) (*queryBuilder, error) {
if findFilter == nil { if findFilter == nil {
findFilter = &models.FindFilterType{} findFilter = &models.FindFilterType{}
} }
if movieFilter == nil { if groupFilter == nil {
movieFilter = &models.MovieFilterType{} groupFilter = &models.GroupFilterType{}
} }
query := movieRepository.newQuery() query := groupRepository.newQuery()
distinctIDs(&query, movieTable) distinctIDs(&query, groupTable)
if q := findFilter.Q; q != nil && *q != "" { if q := findFilter.Q; q != nil && *q != "" {
searchColumns := []string{"movies.name", "movies.aliases"} searchColumns := []string{"movies.name", "movies.aliases"}
query.parseQueryString(searchColumns, *q) query.parseQueryString(searchColumns, *q)
} }
filter := filterBuilderFromHandler(ctx, &movieFilterHandler{ filter := filterBuilderFromHandler(ctx, &groupFilterHandler{
movieFilter: movieFilter, groupFilter: groupFilter,
}) })
if err := query.addFilter(filter); err != nil { if err := query.addFilter(filter); err != nil {
@@ -413,7 +413,7 @@ func (qb *MovieStore) makeQuery(ctx context.Context, movieFilter *models.MovieFi
} }
var err error var err error
query.sortAndPagination, err = qb.getMovieSort(findFilter) query.sortAndPagination, err = qb.getGroupSort(findFilter)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -423,8 +423,8 @@ func (qb *MovieStore) makeQuery(ctx context.Context, movieFilter *models.MovieFi
return &query, nil return &query, nil
} }
func (qb *MovieStore) Query(ctx context.Context, movieFilter *models.MovieFilterType, findFilter *models.FindFilterType) ([]*models.Movie, int, error) { func (qb *GroupStore) Query(ctx context.Context, groupFilter *models.GroupFilterType, findFilter *models.FindFilterType) ([]*models.Group, int, error) {
query, err := qb.makeQuery(ctx, movieFilter, findFilter) query, err := qb.makeQuery(ctx, groupFilter, findFilter)
if err != nil { if err != nil {
return nil, 0, err return nil, 0, err
} }
@@ -434,16 +434,16 @@ func (qb *MovieStore) Query(ctx context.Context, movieFilter *models.MovieFilter
return nil, 0, err return nil, 0, err
} }
movies, err := qb.FindMany(ctx, idsResult) groups, err := qb.FindMany(ctx, idsResult)
if err != nil { if err != nil {
return nil, 0, err return nil, 0, err
} }
return movies, countResult, nil return groups, countResult, nil
} }
func (qb *MovieStore) QueryCount(ctx context.Context, movieFilter *models.MovieFilterType, findFilter *models.FindFilterType) (int, error) { func (qb *GroupStore) QueryCount(ctx context.Context, groupFilter *models.GroupFilterType, findFilter *models.FindFilterType) (int, error) {
query, err := qb.makeQuery(ctx, movieFilter, findFilter) query, err := qb.makeQuery(ctx, groupFilter, findFilter)
if err != nil { if err != nil {
return 0, err return 0, err
} }
@@ -451,7 +451,7 @@ func (qb *MovieStore) QueryCount(ctx context.Context, movieFilter *models.MovieF
return query.executeCount(ctx) return query.executeCount(ctx)
} }
var movieSortOptions = sortOptions{ var groupSortOptions = sortOptions{
"created_at", "created_at",
"date", "date",
"duration", "duration",
@@ -464,7 +464,7 @@ var movieSortOptions = sortOptions{
"updated_at", "updated_at",
} }
func (qb *MovieStore) getMovieSort(findFilter *models.FindFilterType) (string, error) { func (qb *GroupStore) getGroupSort(findFilter *models.FindFilterType) (string, error) {
var sort string var sort string
var direction string var direction string
if findFilter == nil { if findFilter == nil {
@@ -476,16 +476,16 @@ func (qb *MovieStore) getMovieSort(findFilter *models.FindFilterType) (string, e
} }
// CVE-2024-32231 - ensure sort is in the list of allowed sorts // CVE-2024-32231 - ensure sort is in the list of allowed sorts
if err := movieSortOptions.validateSort(sort); err != nil { if err := groupSortOptions.validateSort(sort); err != nil {
return "", err return "", err
} }
sortQuery := "" sortQuery := ""
switch sort { switch sort {
case "tag_count": case "tag_count":
sortQuery += getCountSort(movieTable, moviesTagsTable, movieIDColumn, direction) sortQuery += getCountSort(groupTable, groupsTagsTable, groupIDColumn, direction)
case "scenes_count": // generic getSort won't work for this case "scenes_count": // generic getSort won't work for this
sortQuery += getCountSort(movieTable, moviesScenesTable, movieIDColumn, direction) sortQuery += getCountSort(groupTable, groupsScenesTable, groupIDColumn, direction)
default: default:
sortQuery += getSort(sort, direction, "movies") sortQuery += getSort(sort, direction, "movies")
} }
@@ -495,11 +495,11 @@ func (qb *MovieStore) getMovieSort(findFilter *models.FindFilterType) (string, e
return sortQuery, nil return sortQuery, nil
} }
func (qb *MovieStore) queryMovies(ctx context.Context, query string, args []interface{}) ([]*models.Movie, error) { func (qb *GroupStore) queryGroups(ctx context.Context, query string, args []interface{}) ([]*models.Group, error) {
const single = false const single = false
var ret []*models.Movie var ret []*models.Group
if err := movieRepository.queryFunc(ctx, query, args, single, func(r *sqlx.Rows) error { if err := groupRepository.queryFunc(ctx, query, args, single, func(r *sqlx.Rows) error {
var f movieRow var f groupRow
if err := r.StructScan(&f); err != nil { if err := r.StructScan(&f); err != nil {
return err return err
} }
@@ -515,42 +515,42 @@ func (qb *MovieStore) queryMovies(ctx context.Context, query string, args []inte
return ret, nil return ret, nil
} }
func (qb *MovieStore) UpdateFrontImage(ctx context.Context, movieID int, frontImage []byte) error { func (qb *GroupStore) UpdateFrontImage(ctx context.Context, groupID int, frontImage []byte) error {
return qb.UpdateImage(ctx, movieID, movieFrontImageBlobColumn, frontImage) return qb.UpdateImage(ctx, groupID, groupFrontImageBlobColumn, frontImage)
} }
func (qb *MovieStore) UpdateBackImage(ctx context.Context, movieID int, backImage []byte) error { func (qb *GroupStore) UpdateBackImage(ctx context.Context, groupID int, backImage []byte) error {
return qb.UpdateImage(ctx, movieID, movieBackImageBlobColumn, backImage) return qb.UpdateImage(ctx, groupID, groupBackImageBlobColumn, backImage)
} }
func (qb *MovieStore) destroyImages(ctx context.Context, movieID int) error { func (qb *GroupStore) destroyImages(ctx context.Context, groupID int) error {
if err := qb.DestroyImage(ctx, movieID, movieFrontImageBlobColumn); err != nil { if err := qb.DestroyImage(ctx, groupID, groupFrontImageBlobColumn); err != nil {
return err return err
} }
if err := qb.DestroyImage(ctx, movieID, movieBackImageBlobColumn); err != nil { if err := qb.DestroyImage(ctx, groupID, groupBackImageBlobColumn); err != nil {
return err return err
} }
return nil return nil
} }
func (qb *MovieStore) GetFrontImage(ctx context.Context, movieID int) ([]byte, error) { func (qb *GroupStore) GetFrontImage(ctx context.Context, groupID int) ([]byte, error) {
return qb.GetImage(ctx, movieID, movieFrontImageBlobColumn) return qb.GetImage(ctx, groupID, groupFrontImageBlobColumn)
} }
func (qb *MovieStore) HasFrontImage(ctx context.Context, movieID int) (bool, error) { func (qb *GroupStore) HasFrontImage(ctx context.Context, groupID int) (bool, error) {
return qb.HasImage(ctx, movieID, movieFrontImageBlobColumn) return qb.HasImage(ctx, groupID, groupFrontImageBlobColumn)
} }
func (qb *MovieStore) GetBackImage(ctx context.Context, movieID int) ([]byte, error) { func (qb *GroupStore) GetBackImage(ctx context.Context, groupID int) ([]byte, error) {
return qb.GetImage(ctx, movieID, movieBackImageBlobColumn) return qb.GetImage(ctx, groupID, groupBackImageBlobColumn)
} }
func (qb *MovieStore) HasBackImage(ctx context.Context, movieID int) (bool, error) { func (qb *GroupStore) HasBackImage(ctx context.Context, groupID int) (bool, error) {
return qb.HasImage(ctx, movieID, movieBackImageBlobColumn) return qb.HasImage(ctx, groupID, groupBackImageBlobColumn)
} }
func (qb *MovieStore) FindByPerformerID(ctx context.Context, performerID int) ([]*models.Movie, error) { func (qb *GroupStore) FindByPerformerID(ctx context.Context, performerID int) ([]*models.Group, error) {
query := `SELECT DISTINCT movies.* query := `SELECT DISTINCT movies.*
FROM movies FROM movies
INNER JOIN movies_scenes ON movies.id = movies_scenes.movie_id INNER JOIN movies_scenes ON movies.id = movies_scenes.movie_id
@@ -558,37 +558,37 @@ INNER JOIN performers_scenes ON performers_scenes.scene_id = movies_scenes.scene
WHERE performers_scenes.performer_id = ? WHERE performers_scenes.performer_id = ?
` `
args := []interface{}{performerID} args := []interface{}{performerID}
return qb.queryMovies(ctx, query, args) return qb.queryGroups(ctx, query, args)
} }
func (qb *MovieStore) CountByPerformerID(ctx context.Context, performerID int) (int, error) { func (qb *GroupStore) CountByPerformerID(ctx context.Context, performerID int) (int, error) {
query := `SELECT COUNT(DISTINCT movies_scenes.movie_id) AS count query := `SELECT COUNT(DISTINCT movies_scenes.movie_id) AS count
FROM movies_scenes FROM movies_scenes
INNER JOIN performers_scenes ON performers_scenes.scene_id = movies_scenes.scene_id INNER JOIN performers_scenes ON performers_scenes.scene_id = movies_scenes.scene_id
WHERE performers_scenes.performer_id = ? WHERE performers_scenes.performer_id = ?
` `
args := []interface{}{performerID} args := []interface{}{performerID}
return movieRepository.runCountQuery(ctx, query, args) return groupRepository.runCountQuery(ctx, query, args)
} }
func (qb *MovieStore) FindByStudioID(ctx context.Context, studioID int) ([]*models.Movie, error) { func (qb *GroupStore) FindByStudioID(ctx context.Context, studioID int) ([]*models.Group, error) {
query := `SELECT movies.* query := `SELECT movies.*
FROM movies FROM movies
WHERE movies.studio_id = ? WHERE movies.studio_id = ?
` `
args := []interface{}{studioID} args := []interface{}{studioID}
return qb.queryMovies(ctx, query, args) return qb.queryGroups(ctx, query, args)
} }
func (qb *MovieStore) CountByStudioID(ctx context.Context, studioID int) (int, error) { func (qb *GroupStore) CountByStudioID(ctx context.Context, studioID int) (int, error) {
query := `SELECT COUNT(1) AS count query := `SELECT COUNT(1) AS count
FROM movies FROM movies
WHERE movies.studio_id = ? WHERE movies.studio_id = ?
` `
args := []interface{}{studioID} args := []interface{}{studioID}
return movieRepository.runCountQuery(ctx, query, args) return groupRepository.runCountQuery(ctx, query, args)
} }
func (qb *MovieStore) GetURLs(ctx context.Context, movieID int) ([]string, error) { func (qb *GroupStore) GetURLs(ctx context.Context, groupID int) ([]string, error) {
return moviesURLsTableMgr.get(ctx, movieID) return groupsURLsTableMgr.get(ctx, groupID)
} }

View File

@@ -7,22 +7,22 @@ import (
"github.com/stashapp/stash/pkg/models" "github.com/stashapp/stash/pkg/models"
) )
type movieFilterHandler struct { type groupFilterHandler struct {
movieFilter *models.MovieFilterType groupFilter *models.GroupFilterType
} }
func (qb *movieFilterHandler) validate() error { func (qb *groupFilterHandler) validate() error {
movieFilter := qb.movieFilter groupFilter := qb.groupFilter
if movieFilter == nil { if groupFilter == nil {
return nil return nil
} }
if err := validateFilterCombination(movieFilter.OperatorFilter); err != nil { if err := validateFilterCombination(groupFilter.OperatorFilter); err != nil {
return err return err
} }
if subFilter := movieFilter.SubFilter(); subFilter != nil { if subFilter := groupFilter.SubFilter(); subFilter != nil {
sqb := &movieFilterHandler{movieFilter: subFilter} sqb := &groupFilterHandler{groupFilter: subFilter}
if err := sqb.validate(); err != nil { if err := sqb.validate(); err != nil {
return err return err
} }
@@ -31,9 +31,9 @@ func (qb *movieFilterHandler) validate() error {
return nil return nil
} }
func (qb *movieFilterHandler) handle(ctx context.Context, f *filterBuilder) { func (qb *groupFilterHandler) handle(ctx context.Context, f *filterBuilder) {
movieFilter := qb.movieFilter groupFilter := qb.groupFilter
if movieFilter == nil { if groupFilter == nil {
return return
} }
@@ -42,51 +42,51 @@ func (qb *movieFilterHandler) handle(ctx context.Context, f *filterBuilder) {
return return
} }
sf := movieFilter.SubFilter() sf := groupFilter.SubFilter()
if sf != nil { if sf != nil {
sub := &movieFilterHandler{sf} sub := &groupFilterHandler{sf}
handleSubFilter(ctx, sub, f, movieFilter.OperatorFilter) handleSubFilter(ctx, sub, f, groupFilter.OperatorFilter)
} }
f.handleCriterion(ctx, qb.criterionHandler()) f.handleCriterion(ctx, qb.criterionHandler())
} }
func (qb *movieFilterHandler) criterionHandler() criterionHandler { func (qb *groupFilterHandler) criterionHandler() criterionHandler {
movieFilter := qb.movieFilter groupFilter := qb.groupFilter
return compoundHandler{ return compoundHandler{
stringCriterionHandler(movieFilter.Name, "movies.name"), stringCriterionHandler(groupFilter.Name, "movies.name"),
stringCriterionHandler(movieFilter.Director, "movies.director"), stringCriterionHandler(groupFilter.Director, "movies.director"),
stringCriterionHandler(movieFilter.Synopsis, "movies.synopsis"), stringCriterionHandler(groupFilter.Synopsis, "movies.synopsis"),
intCriterionHandler(movieFilter.Rating100, "movies.rating", nil), intCriterionHandler(groupFilter.Rating100, "movies.rating", nil),
floatIntCriterionHandler(movieFilter.Duration, "movies.duration", nil), floatIntCriterionHandler(groupFilter.Duration, "movies.duration", nil),
qb.missingCriterionHandler(movieFilter.IsMissing), qb.missingCriterionHandler(groupFilter.IsMissing),
qb.urlsCriterionHandler(movieFilter.URL), qb.urlsCriterionHandler(groupFilter.URL),
studioCriterionHandler(movieTable, movieFilter.Studios), studioCriterionHandler(groupTable, groupFilter.Studios),
qb.performersCriterionHandler(movieFilter.Performers), qb.performersCriterionHandler(groupFilter.Performers),
qb.tagsCriterionHandler(movieFilter.Tags), qb.tagsCriterionHandler(groupFilter.Tags),
qb.tagCountCriterionHandler(movieFilter.TagCount), qb.tagCountCriterionHandler(groupFilter.TagCount),
&dateCriterionHandler{movieFilter.Date, "movies.date", nil}, &dateCriterionHandler{groupFilter.Date, "movies.date", nil},
&timestampCriterionHandler{movieFilter.CreatedAt, "movies.created_at", nil}, &timestampCriterionHandler{groupFilter.CreatedAt, "movies.created_at", nil},
&timestampCriterionHandler{movieFilter.UpdatedAt, "movies.updated_at", nil}, &timestampCriterionHandler{groupFilter.UpdatedAt, "movies.updated_at", nil},
&relatedFilterHandler{ &relatedFilterHandler{
relatedIDCol: "movies_scenes.scene_id", relatedIDCol: "movies_scenes.scene_id",
relatedRepo: sceneRepository.repository, relatedRepo: sceneRepository.repository,
relatedHandler: &sceneFilterHandler{movieFilter.ScenesFilter}, relatedHandler: &sceneFilterHandler{groupFilter.ScenesFilter},
joinFn: func(f *filterBuilder) { joinFn: func(f *filterBuilder) {
movieRepository.scenes.innerJoin(f, "", "movies.id") groupRepository.scenes.innerJoin(f, "", "movies.id")
}, },
}, },
&relatedFilterHandler{ &relatedFilterHandler{
relatedIDCol: "movies.studio_id", relatedIDCol: "movies.studio_id",
relatedRepo: studioRepository.repository, relatedRepo: studioRepository.repository,
relatedHandler: &studioFilterHandler{movieFilter.StudiosFilter}, relatedHandler: &studioFilterHandler{groupFilter.StudiosFilter},
}, },
} }
} }
func (qb *movieFilterHandler) missingCriterionHandler(isMissing *string) criterionHandlerFunc { func (qb *groupFilterHandler) missingCriterionHandler(isMissing *string) criterionHandlerFunc {
return func(ctx context.Context, f *filterBuilder) { return func(ctx context.Context, f *filterBuilder) {
if isMissing != nil && *isMissing != "" { if isMissing != nil && *isMissing != "" {
switch *isMissing { switch *isMissing {
@@ -104,21 +104,21 @@ func (qb *movieFilterHandler) missingCriterionHandler(isMissing *string) criteri
} }
} }
func (qb *movieFilterHandler) urlsCriterionHandler(url *models.StringCriterionInput) criterionHandlerFunc { func (qb *groupFilterHandler) urlsCriterionHandler(url *models.StringCriterionInput) criterionHandlerFunc {
h := stringListCriterionHandlerBuilder{ h := stringListCriterionHandlerBuilder{
primaryTable: movieTable, primaryTable: groupTable,
primaryFK: movieIDColumn, primaryFK: groupIDColumn,
joinTable: movieURLsTable, joinTable: groupURLsTable,
stringColumn: movieURLColumn, stringColumn: groupURLColumn,
addJoinTable: func(f *filterBuilder) { addJoinTable: func(f *filterBuilder) {
moviesURLsTableMgr.join(f, "", "movies.id") groupsURLsTableMgr.join(f, "", "movies.id")
}, },
} }
return h.handler(url) return h.handler(url)
} }
func (qb *movieFilterHandler) performersCriterionHandler(performers *models.MultiCriterionInput) criterionHandlerFunc { func (qb *groupFilterHandler) performersCriterionHandler(performers *models.MultiCriterionInput) criterionHandlerFunc {
return func(ctx context.Context, f *filterBuilder) { return func(ctx context.Context, f *filterBuilder) {
if performers != nil { if performers != nil {
if performers.Modifier == models.CriterionModifierIsNull || performers.Modifier == models.CriterionModifierNotNull { if performers.Modifier == models.CriterionModifierIsNull || performers.Modifier == models.CriterionModifierNotNull {
@@ -165,26 +165,26 @@ func (qb *movieFilterHandler) performersCriterionHandler(performers *models.Mult
} }
} }
func (qb *movieFilterHandler) tagsCriterionHandler(tags *models.HierarchicalMultiCriterionInput) criterionHandlerFunc { func (qb *groupFilterHandler) tagsCriterionHandler(tags *models.HierarchicalMultiCriterionInput) criterionHandlerFunc {
h := joinedHierarchicalMultiCriterionHandlerBuilder{ h := joinedHierarchicalMultiCriterionHandlerBuilder{
primaryTable: movieTable, primaryTable: groupTable,
foreignTable: tagTable, foreignTable: tagTable,
foreignFK: "tag_id", foreignFK: "tag_id",
relationsTable: "tags_relations", relationsTable: "tags_relations",
joinAs: "movie_tag", joinAs: "movie_tag",
joinTable: moviesTagsTable, joinTable: groupsTagsTable,
primaryFK: movieIDColumn, primaryFK: groupIDColumn,
} }
return h.handler(tags) return h.handler(tags)
} }
func (qb *movieFilterHandler) tagCountCriterionHandler(count *models.IntCriterionInput) criterionHandlerFunc { func (qb *groupFilterHandler) tagCountCriterionHandler(count *models.IntCriterionInput) criterionHandlerFunc {
h := countCriterionHandlerBuilder{ h := countCriterionHandlerBuilder{
primaryTable: movieTable, primaryTable: groupTable,
joinTable: moviesTagsTable, joinTable: groupsTagsTable,
primaryFK: movieIDColumn, primaryFK: groupIDColumn,
} }
return h.handler(count) return h.handler(count)

View File

@@ -16,14 +16,14 @@ import (
"github.com/stashapp/stash/pkg/models" "github.com/stashapp/stash/pkg/models"
) )
func loadMovieRelationships(ctx context.Context, expected models.Movie, actual *models.Movie) error { func loadGroupRelationships(ctx context.Context, expected models.Group, actual *models.Group) error {
if expected.URLs.Loaded() { if expected.URLs.Loaded() {
if err := actual.LoadURLs(ctx, db.Movie); err != nil { if err := actual.LoadURLs(ctx, db.Group); err != nil {
return err return err
} }
} }
if expected.TagIDs.Loaded() { if expected.TagIDs.Loaded() {
if err := actual.LoadTagIDs(ctx, db.Movie); err != nil { if err := actual.LoadTagIDs(ctx, db.Group); err != nil {
return err return err
} }
} }
@@ -31,7 +31,7 @@ func loadMovieRelationships(ctx context.Context, expected models.Movie, actual *
return nil return nil
} }
func Test_MovieStore_Create(t *testing.T) { func Test_GroupStore_Create(t *testing.T) {
var ( var (
name = "name" name = "name"
url = "url" url = "url"
@@ -47,21 +47,21 @@ func Test_MovieStore_Create(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
newObject models.Movie newObject models.Group
wantErr bool wantErr bool
}{ }{
{ {
"full", "full",
models.Movie{ models.Group{
Name: name, Name: name,
Duration: &duration, Duration: &duration,
Date: &date, Date: &date,
Rating: &rating, Rating: &rating,
StudioID: &studioIDs[studioIdxWithMovie], StudioID: &studioIDs[studioIdxWithGroup],
Director: director, Director: director,
Synopsis: synopsis, Synopsis: synopsis,
URLs: models.NewRelatedStrings([]string{url}), URLs: models.NewRelatedStrings([]string{url}),
TagIDs: models.NewRelatedIDs([]int{tagIDs[tagIdx1WithDupName], tagIDs[tagIdx1WithMovie]}), TagIDs: models.NewRelatedIDs([]int{tagIDs[tagIdx1WithDupName], tagIDs[tagIdx1WithGroup]}),
Aliases: aliases, Aliases: aliases,
CreatedAt: createdAt, CreatedAt: createdAt,
UpdatedAt: updatedAt, UpdatedAt: updatedAt,
@@ -70,7 +70,7 @@ func Test_MovieStore_Create(t *testing.T) {
}, },
{ {
"invalid tag id", "invalid tag id",
models.Movie{ models.Group{
Name: name, Name: name,
TagIDs: models.NewRelatedIDs([]int{invalidID}), TagIDs: models.NewRelatedIDs([]int{invalidID}),
}, },
@@ -78,7 +78,7 @@ func Test_MovieStore_Create(t *testing.T) {
}, },
} }
qb := db.Movie qb := db.Group
for _, tt := range tests { for _, tt := range tests {
runWithRollbackTxn(t, tt.name, func(t *testing.T, ctx context.Context) { runWithRollbackTxn(t, tt.name, func(t *testing.T, ctx context.Context) {
@@ -86,7 +86,7 @@ func Test_MovieStore_Create(t *testing.T) {
p := tt.newObject p := tt.newObject
if err := qb.Create(ctx, &p); (err != nil) != tt.wantErr { if err := qb.Create(ctx, &p); (err != nil) != tt.wantErr {
t.Errorf("MovieStore.Create() error = %v, wantErr = %v", err, tt.wantErr) t.Errorf("GroupStore.Create() error = %v, wantErr = %v", err, tt.wantErr)
} }
if tt.wantErr { if tt.wantErr {
@@ -100,17 +100,17 @@ func Test_MovieStore_Create(t *testing.T) {
copy.ID = p.ID copy.ID = p.ID
// load relationships // load relationships
if err := loadMovieRelationships(ctx, copy, &p); err != nil { if err := loadGroupRelationships(ctx, copy, &p); err != nil {
t.Errorf("loadMovieRelationships() error = %v", err) t.Errorf("loadGroupRelationships() error = %v", err)
return return
} }
assert.Equal(copy, p) assert.Equal(copy, p)
// ensure can find the movie // ensure can find the group
found, err := qb.Find(ctx, p.ID) found, err := qb.Find(ctx, p.ID)
if err != nil { if err != nil {
t.Errorf("MovieStore.Find() error = %v", err) t.Errorf("GroupStore.Find() error = %v", err)
} }
if !assert.NotNil(found) { if !assert.NotNil(found) {
@@ -118,8 +118,8 @@ func Test_MovieStore_Create(t *testing.T) {
} }
// load relationships // load relationships
if err := loadMovieRelationships(ctx, copy, found); err != nil { if err := loadGroupRelationships(ctx, copy, found); err != nil {
t.Errorf("loadMovieRelationships() error = %v", err) t.Errorf("loadGroupRelationships() error = %v", err)
return return
} }
assert.Equal(copy, *found) assert.Equal(copy, *found)
@@ -129,7 +129,7 @@ func Test_MovieStore_Create(t *testing.T) {
} }
} }
func Test_movieQueryBuilder_Update(t *testing.T) { func Test_groupQueryBuilder_Update(t *testing.T) {
var ( var (
name = "name" name = "name"
url = "url" url = "url"
@@ -145,22 +145,22 @@ func Test_movieQueryBuilder_Update(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
updatedObject *models.Movie updatedObject *models.Group
wantErr bool wantErr bool
}{ }{
{ {
"full", "full",
&models.Movie{ &models.Group{
ID: movieIDs[movieIdxWithTag], ID: groupIDs[groupIdxWithTag],
Name: name, Name: name,
Duration: &duration, Duration: &duration,
Date: &date, Date: &date,
Rating: &rating, Rating: &rating,
StudioID: &studioIDs[studioIdxWithMovie], StudioID: &studioIDs[studioIdxWithGroup],
Director: director, Director: director,
Synopsis: synopsis, Synopsis: synopsis,
URLs: models.NewRelatedStrings([]string{url}), URLs: models.NewRelatedStrings([]string{url}),
TagIDs: models.NewRelatedIDs([]int{tagIDs[tagIdx1WithDupName], tagIDs[tagIdx1WithMovie]}), TagIDs: models.NewRelatedIDs([]int{tagIDs[tagIdx1WithDupName], tagIDs[tagIdx1WithGroup]}),
Aliases: aliases, Aliases: aliases,
CreatedAt: createdAt, CreatedAt: createdAt,
UpdatedAt: updatedAt, UpdatedAt: updatedAt,
@@ -169,8 +169,8 @@ func Test_movieQueryBuilder_Update(t *testing.T) {
}, },
{ {
"clear tag ids", "clear tag ids",
&models.Movie{ &models.Group{
ID: movieIDs[movieIdxWithTag], ID: groupIDs[groupIdxWithTag],
Name: name, Name: name,
TagIDs: models.NewRelatedIDs([]int{}), TagIDs: models.NewRelatedIDs([]int{}),
}, },
@@ -178,8 +178,8 @@ func Test_movieQueryBuilder_Update(t *testing.T) {
}, },
{ {
"invalid studio id", "invalid studio id",
&models.Movie{ &models.Group{
ID: movieIDs[movieIdxWithScene], ID: groupIDs[groupIdxWithScene],
Name: name, Name: name,
StudioID: &invalidID, StudioID: &invalidID,
}, },
@@ -187,8 +187,8 @@ func Test_movieQueryBuilder_Update(t *testing.T) {
}, },
{ {
"invalid tag id", "invalid tag id",
&models.Movie{ &models.Group{
ID: movieIDs[movieIdxWithScene], ID: groupIDs[groupIdxWithScene],
Name: name, Name: name,
TagIDs: models.NewRelatedIDs([]int{invalidID}), TagIDs: models.NewRelatedIDs([]int{invalidID}),
}, },
@@ -196,7 +196,7 @@ func Test_movieQueryBuilder_Update(t *testing.T) {
}, },
} }
qb := db.Movie qb := db.Group
for _, tt := range tests { for _, tt := range tests {
runWithRollbackTxn(t, tt.name, func(t *testing.T, ctx context.Context) { runWithRollbackTxn(t, tt.name, func(t *testing.T, ctx context.Context) {
assert := assert.New(t) assert := assert.New(t)
@@ -204,7 +204,7 @@ func Test_movieQueryBuilder_Update(t *testing.T) {
copy := *tt.updatedObject copy := *tt.updatedObject
if err := qb.Update(ctx, tt.updatedObject); (err != nil) != tt.wantErr { if err := qb.Update(ctx, tt.updatedObject); (err != nil) != tt.wantErr {
t.Errorf("movieQueryBuilder.Update() error = %v, wantErr %v", err, tt.wantErr) t.Errorf("groupQueryBuilder.Update() error = %v, wantErr %v", err, tt.wantErr)
} }
if tt.wantErr { if tt.wantErr {
@@ -213,12 +213,12 @@ func Test_movieQueryBuilder_Update(t *testing.T) {
s, err := qb.Find(ctx, tt.updatedObject.ID) s, err := qb.Find(ctx, tt.updatedObject.ID)
if err != nil { if err != nil {
t.Errorf("movieQueryBuilder.Find() error = %v", err) t.Errorf("groupQueryBuilder.Find() error = %v", err)
} }
// load relationships // load relationships
if err := loadMovieRelationships(ctx, copy, s); err != nil { if err := loadGroupRelationships(ctx, copy, s); err != nil {
t.Errorf("loadMovieRelationships() error = %v", err) t.Errorf("loadGroupRelationships() error = %v", err)
return return
} }
@@ -227,9 +227,9 @@ func Test_movieQueryBuilder_Update(t *testing.T) {
} }
} }
func clearMoviePartial() models.MoviePartial { func clearGroupPartial() models.GroupPartial {
// leave mandatory fields // leave mandatory fields
return models.MoviePartial{ return models.GroupPartial{
Aliases: models.OptionalString{Set: true, Null: true}, Aliases: models.OptionalString{Set: true, Null: true},
Synopsis: models.OptionalString{Set: true, Null: true}, Synopsis: models.OptionalString{Set: true, Null: true},
Director: models.OptionalString{Set: true, Null: true}, Director: models.OptionalString{Set: true, Null: true},
@@ -242,7 +242,7 @@ func clearMoviePartial() models.MoviePartial {
} }
} }
func Test_movieQueryBuilder_UpdatePartial(t *testing.T) { func Test_groupQueryBuilder_UpdatePartial(t *testing.T) {
var ( var (
name = "name" name = "name"
url = "url" url = "url"
@@ -259,14 +259,14 @@ func Test_movieQueryBuilder_UpdatePartial(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
id int id int
partial models.MoviePartial partial models.GroupPartial
want models.Movie want models.Group
wantErr bool wantErr bool
}{ }{
{ {
"full", "full",
movieIDs[movieIdxWithScene], groupIDs[groupIdxWithScene],
models.MoviePartial{ models.GroupPartial{
Name: models.NewOptionalString(name), Name: models.NewOptionalString(name),
Director: models.NewOptionalString(director), Director: models.NewOptionalString(director),
Synopsis: models.NewOptionalString(synopsis), Synopsis: models.NewOptionalString(synopsis),
@@ -278,16 +278,16 @@ func Test_movieQueryBuilder_UpdatePartial(t *testing.T) {
Date: models.NewOptionalDate(date), Date: models.NewOptionalDate(date),
Duration: models.NewOptionalInt(duration), Duration: models.NewOptionalInt(duration),
Rating: models.NewOptionalInt(rating), Rating: models.NewOptionalInt(rating),
StudioID: models.NewOptionalInt(studioIDs[studioIdxWithMovie]), StudioID: models.NewOptionalInt(studioIDs[studioIdxWithGroup]),
CreatedAt: models.NewOptionalTime(createdAt), CreatedAt: models.NewOptionalTime(createdAt),
UpdatedAt: models.NewOptionalTime(updatedAt), UpdatedAt: models.NewOptionalTime(updatedAt),
TagIDs: &models.UpdateIDs{ TagIDs: &models.UpdateIDs{
IDs: []int{tagIDs[tagIdx1WithMovie], tagIDs[tagIdx1WithDupName]}, IDs: []int{tagIDs[tagIdx1WithGroup], tagIDs[tagIdx1WithDupName]},
Mode: models.RelationshipUpdateModeSet, Mode: models.RelationshipUpdateModeSet,
}, },
}, },
models.Movie{ models.Group{
ID: movieIDs[movieIdxWithScene], ID: groupIDs[groupIdxWithScene],
Name: name, Name: name,
Director: director, Director: director,
Synopsis: synopsis, Synopsis: synopsis,
@@ -296,20 +296,20 @@ func Test_movieQueryBuilder_UpdatePartial(t *testing.T) {
Date: &date, Date: &date,
Duration: &duration, Duration: &duration,
Rating: &rating, Rating: &rating,
StudioID: &studioIDs[studioIdxWithMovie], StudioID: &studioIDs[studioIdxWithGroup],
CreatedAt: createdAt, CreatedAt: createdAt,
UpdatedAt: updatedAt, UpdatedAt: updatedAt,
TagIDs: models.NewRelatedIDs([]int{tagIDs[tagIdx1WithDupName], tagIDs[tagIdx1WithMovie]}), TagIDs: models.NewRelatedIDs([]int{tagIDs[tagIdx1WithDupName], tagIDs[tagIdx1WithGroup]}),
}, },
false, false,
}, },
{ {
"clear all", "clear all",
movieIDs[movieIdxWithScene], groupIDs[groupIdxWithScene],
clearMoviePartial(), clearGroupPartial(),
models.Movie{ models.Group{
ID: movieIDs[movieIdxWithScene], ID: groupIDs[groupIdxWithScene],
Name: movieNames[movieIdxWithScene], Name: groupNames[groupIdxWithScene],
TagIDs: models.NewRelatedIDs([]int{}), TagIDs: models.NewRelatedIDs([]int{}),
}, },
false, false,
@@ -317,20 +317,20 @@ func Test_movieQueryBuilder_UpdatePartial(t *testing.T) {
{ {
"invalid id", "invalid id",
invalidID, invalidID,
models.MoviePartial{}, models.GroupPartial{},
models.Movie{}, models.Group{},
true, true,
}, },
} }
for _, tt := range tests { for _, tt := range tests {
qb := db.Movie qb := db.Group
runWithRollbackTxn(t, tt.name, func(t *testing.T, ctx context.Context) { runWithRollbackTxn(t, tt.name, func(t *testing.T, ctx context.Context) {
assert := assert.New(t) assert := assert.New(t)
got, err := qb.UpdatePartial(ctx, tt.id, tt.partial) got, err := qb.UpdatePartial(ctx, tt.id, tt.partial)
if (err != nil) != tt.wantErr { if (err != nil) != tt.wantErr {
t.Errorf("movieQueryBuilder.UpdatePartial() error = %v, wantErr %v", err, tt.wantErr) t.Errorf("groupQueryBuilder.UpdatePartial() error = %v, wantErr %v", err, tt.wantErr)
return return
} }
@@ -339,8 +339,8 @@ func Test_movieQueryBuilder_UpdatePartial(t *testing.T) {
} }
// load relationships // load relationships
if err := loadMovieRelationships(ctx, tt.want, got); err != nil { if err := loadGroupRelationships(ctx, tt.want, got); err != nil {
t.Errorf("loadMovieRelationships() error = %v", err) t.Errorf("loadGroupRelationships() error = %v", err)
return return
} }
@@ -348,12 +348,12 @@ func Test_movieQueryBuilder_UpdatePartial(t *testing.T) {
s, err := qb.Find(ctx, tt.id) s, err := qb.Find(ctx, tt.id)
if err != nil { if err != nil {
t.Errorf("movieQueryBuilder.Find() error = %v", err) t.Errorf("groupQueryBuilder.Find() error = %v", err)
} }
// load relationships // load relationships
if err := loadMovieRelationships(ctx, tt.want, s); err != nil { if err := loadGroupRelationships(ctx, tt.want, s); err != nil {
t.Errorf("loadMovieRelationships() error = %v", err) t.Errorf("loadGroupRelationships() error = %v", err)
return return
} }
@@ -362,65 +362,65 @@ func Test_movieQueryBuilder_UpdatePartial(t *testing.T) {
} }
} }
func TestMovieFindByName(t *testing.T) { func TestGroupFindByName(t *testing.T) {
withTxn(func(ctx context.Context) error { withTxn(func(ctx context.Context) error {
mqb := db.Movie mqb := db.Group
name := movieNames[movieIdxWithScene] // find a movie by name name := groupNames[groupIdxWithScene] // find a group by name
movie, err := mqb.FindByName(ctx, name, false) group, err := mqb.FindByName(ctx, name, false)
if err != nil { if err != nil {
t.Errorf("Error finding movies: %s", err.Error()) t.Errorf("Error finding groups: %s", err.Error())
} }
assert.Equal(t, movieNames[movieIdxWithScene], movie.Name) assert.Equal(t, groupNames[groupIdxWithScene], group.Name)
name = movieNames[movieIdxWithDupName] // find a movie by name nocase name = groupNames[groupIdxWithDupName] // find a group by name nocase
movie, err = mqb.FindByName(ctx, name, true) group, err = mqb.FindByName(ctx, name, true)
if err != nil { if err != nil {
t.Errorf("Error finding movies: %s", err.Error()) t.Errorf("Error finding groups: %s", err.Error())
} }
// movieIdxWithDupName and movieIdxWithScene should have similar names ( only diff should be Name vs NaMe) // groupIdxWithDupName and groupIdxWithScene should have similar names ( only diff should be Name vs NaMe)
//movie.Name should match with movieIdxWithScene since its ID is before moveIdxWithDupName //group.Name should match with groupIdxWithScene since its ID is before moveIdxWithDupName
assert.Equal(t, movieNames[movieIdxWithScene], movie.Name) assert.Equal(t, groupNames[groupIdxWithScene], group.Name)
//movie.Name should match with movieIdxWithDupName if the check is not case sensitive //group.Name should match with groupIdxWithDupName if the check is not case sensitive
assert.Equal(t, strings.ToLower(movieNames[movieIdxWithDupName]), strings.ToLower(movie.Name)) assert.Equal(t, strings.ToLower(groupNames[groupIdxWithDupName]), strings.ToLower(group.Name))
return nil return nil
}) })
} }
func TestMovieFindByNames(t *testing.T) { func TestGroupFindByNames(t *testing.T) {
withTxn(func(ctx context.Context) error { withTxn(func(ctx context.Context) error {
var names []string var names []string
mqb := db.Movie mqb := db.Group
names = append(names, movieNames[movieIdxWithScene]) // find movies by names names = append(names, groupNames[groupIdxWithScene]) // find groups by names
movies, err := mqb.FindByNames(ctx, names, false) groups, err := mqb.FindByNames(ctx, names, false)
if err != nil { if err != nil {
t.Errorf("Error finding movies: %s", err.Error()) t.Errorf("Error finding groups: %s", err.Error())
} }
assert.Len(t, movies, 1) assert.Len(t, groups, 1)
assert.Equal(t, movieNames[movieIdxWithScene], movies[0].Name) assert.Equal(t, groupNames[groupIdxWithScene], groups[0].Name)
movies, err = mqb.FindByNames(ctx, names, true) // find movies by names nocase groups, err = mqb.FindByNames(ctx, names, true) // find groups by names nocase
if err != nil { if err != nil {
t.Errorf("Error finding movies: %s", err.Error()) t.Errorf("Error finding groups: %s", err.Error())
} }
assert.Len(t, movies, 2) // movieIdxWithScene and movieIdxWithDupName assert.Len(t, groups, 2) // groupIdxWithScene and groupIdxWithDupName
assert.Equal(t, strings.ToLower(movieNames[movieIdxWithScene]), strings.ToLower(movies[0].Name)) assert.Equal(t, strings.ToLower(groupNames[groupIdxWithScene]), strings.ToLower(groups[0].Name))
assert.Equal(t, strings.ToLower(movieNames[movieIdxWithScene]), strings.ToLower(movies[1].Name)) assert.Equal(t, strings.ToLower(groupNames[groupIdxWithScene]), strings.ToLower(groups[1].Name))
return nil return nil
}) })
} }
func moviesToIDs(i []*models.Movie) []int { func groupsToIDs(i []*models.Group) []int {
ret := make([]int, len(i)) ret := make([]int, len(i))
for i, v := range i { for i, v := range i {
ret[i] = v.ID ret[i] = v.ID
@@ -429,7 +429,7 @@ func moviesToIDs(i []*models.Movie) []int {
return ret return ret
} }
func TestMovieQuery(t *testing.T) { func TestGroupQuery(t *testing.T) {
var ( var (
frontImage = "front_image" frontImage = "front_image"
backImage = "back_image" backImage = "back_image"
@@ -438,7 +438,7 @@ func TestMovieQuery(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
findFilter *models.FindFilterType findFilter *models.FindFilterType
filter *models.MovieFilterType filter *models.GroupFilterType
includeIdxs []int includeIdxs []int
excludeIdxs []int excludeIdxs []int
wantErr bool wantErr bool
@@ -446,7 +446,7 @@ func TestMovieQuery(t *testing.T) {
{ {
"is missing front image", "is missing front image",
nil, nil,
&models.MovieFilterType{ &models.GroupFilterType{
IsMissing: &frontImage, IsMissing: &frontImage,
}, },
// just ensure that it doesn't error // just ensure that it doesn't error
@@ -457,7 +457,7 @@ func TestMovieQuery(t *testing.T) {
{ {
"is missing back image", "is missing back image",
nil, nil,
&models.MovieFilterType{ &models.GroupFilterType{
IsMissing: &backImage, IsMissing: &backImage,
}, },
// just ensure that it doesn't error // just ensure that it doesn't error
@@ -471,13 +471,13 @@ func TestMovieQuery(t *testing.T) {
runWithRollbackTxn(t, tt.name, func(t *testing.T, ctx context.Context) { runWithRollbackTxn(t, tt.name, func(t *testing.T, ctx context.Context) {
assert := assert.New(t) assert := assert.New(t)
results, _, err := db.Movie.Query(ctx, tt.filter, tt.findFilter) results, _, err := db.Group.Query(ctx, tt.filter, tt.findFilter)
if (err != nil) != tt.wantErr { if (err != nil) != tt.wantErr {
t.Errorf("MovieQueryBuilder.Query() error = %v, wantErr %v", err, tt.wantErr) t.Errorf("GroupQueryBuilder.Query() error = %v, wantErr %v", err, tt.wantErr)
return return
} }
ids := moviesToIDs(results) ids := groupsToIDs(results)
include := indexesToIDs(performerIDs, tt.includeIdxs) include := indexesToIDs(performerIDs, tt.includeIdxs)
exclude := indexesToIDs(performerIDs, tt.excludeIdxs) exclude := indexesToIDs(performerIDs, tt.excludeIdxs)
@@ -491,66 +491,66 @@ func TestMovieQuery(t *testing.T) {
} }
} }
func TestMovieQueryStudio(t *testing.T) { func TestGroupQueryStudio(t *testing.T) {
withTxn(func(ctx context.Context) error { withTxn(func(ctx context.Context) error {
mqb := db.Movie mqb := db.Group
studioCriterion := models.HierarchicalMultiCriterionInput{ studioCriterion := models.HierarchicalMultiCriterionInput{
Value: []string{ Value: []string{
strconv.Itoa(studioIDs[studioIdxWithMovie]), strconv.Itoa(studioIDs[studioIdxWithGroup]),
}, },
Modifier: models.CriterionModifierIncludes, Modifier: models.CriterionModifierIncludes,
} }
movieFilter := models.MovieFilterType{ groupFilter := models.GroupFilterType{
Studios: &studioCriterion, Studios: &studioCriterion,
} }
movies, _, err := mqb.Query(ctx, &movieFilter, nil) groups, _, err := mqb.Query(ctx, &groupFilter, nil)
if err != nil { if err != nil {
t.Errorf("Error querying movie: %s", err.Error()) t.Errorf("Error querying group: %s", err.Error())
} }
assert.Len(t, movies, 1) assert.Len(t, groups, 1)
// ensure id is correct // ensure id is correct
assert.Equal(t, movieIDs[movieIdxWithStudio], movies[0].ID) assert.Equal(t, groupIDs[groupIdxWithStudio], groups[0].ID)
studioCriterion = models.HierarchicalMultiCriterionInput{ studioCriterion = models.HierarchicalMultiCriterionInput{
Value: []string{ Value: []string{
strconv.Itoa(studioIDs[studioIdxWithMovie]), strconv.Itoa(studioIDs[studioIdxWithGroup]),
}, },
Modifier: models.CriterionModifierExcludes, Modifier: models.CriterionModifierExcludes,
} }
q := getMovieStringValue(movieIdxWithStudio, titleField) q := getGroupStringValue(groupIdxWithStudio, titleField)
findFilter := models.FindFilterType{ findFilter := models.FindFilterType{
Q: &q, Q: &q,
} }
movies, _, err = mqb.Query(ctx, &movieFilter, &findFilter) groups, _, err = mqb.Query(ctx, &groupFilter, &findFilter)
if err != nil { if err != nil {
t.Errorf("Error querying movie: %s", err.Error()) t.Errorf("Error querying group: %s", err.Error())
} }
assert.Len(t, movies, 0) assert.Len(t, groups, 0)
return nil return nil
}) })
} }
func TestMovieQueryURL(t *testing.T) { func TestGroupQueryURL(t *testing.T) {
const sceneIdx = 1 const sceneIdx = 1
movieURL := getMovieStringValue(sceneIdx, urlField) groupURL := getGroupStringValue(sceneIdx, urlField)
urlCriterion := models.StringCriterionInput{ urlCriterion := models.StringCriterionInput{
Value: movieURL, Value: groupURL,
Modifier: models.CriterionModifierEquals, Modifier: models.CriterionModifierEquals,
} }
filter := models.MovieFilterType{ filter := models.GroupFilterType{
URL: &urlCriterion, URL: &urlCriterion,
} }
verifyFn := func(n *models.Movie) { verifyFn := func(n *models.Group) {
t.Helper() t.Helper()
urls := n.URLs.List() urls := n.URLs.List()
@@ -562,93 +562,93 @@ func TestMovieQueryURL(t *testing.T) {
verifyString(t, url, urlCriterion) verifyString(t, url, urlCriterion)
} }
verifyMovieQuery(t, filter, verifyFn) verifyGroupQuery(t, filter, verifyFn)
urlCriterion.Modifier = models.CriterionModifierNotEquals urlCriterion.Modifier = models.CriterionModifierNotEquals
verifyMovieQuery(t, filter, verifyFn) verifyGroupQuery(t, filter, verifyFn)
urlCriterion.Modifier = models.CriterionModifierMatchesRegex urlCriterion.Modifier = models.CriterionModifierMatchesRegex
urlCriterion.Value = "movie_.*1_URL" urlCriterion.Value = "group_.*1_URL"
verifyMovieQuery(t, filter, verifyFn) verifyGroupQuery(t, filter, verifyFn)
urlCriterion.Modifier = models.CriterionModifierNotMatchesRegex urlCriterion.Modifier = models.CriterionModifierNotMatchesRegex
verifyMovieQuery(t, filter, verifyFn) verifyGroupQuery(t, filter, verifyFn)
urlCriterion.Modifier = models.CriterionModifierIsNull urlCriterion.Modifier = models.CriterionModifierIsNull
urlCriterion.Value = "" urlCriterion.Value = ""
verifyMovieQuery(t, filter, verifyFn) verifyGroupQuery(t, filter, verifyFn)
urlCriterion.Modifier = models.CriterionModifierNotNull urlCriterion.Modifier = models.CriterionModifierNotNull
verifyMovieQuery(t, filter, verifyFn) verifyGroupQuery(t, filter, verifyFn)
} }
func TestMovieQueryURLExcludes(t *testing.T) { func TestGroupQueryURLExcludes(t *testing.T) {
withRollbackTxn(func(ctx context.Context) error { withRollbackTxn(func(ctx context.Context) error {
mqb := db.Movie mqb := db.Group
// create movie with two URLs // create group with two URLs
movie := models.Movie{ group := models.Group{
Name: "TestMovieQueryURLExcludes", Name: "TestGroupQueryURLExcludes",
URLs: models.NewRelatedStrings([]string{ URLs: models.NewRelatedStrings([]string{
"aaa", "aaa",
"bbb", "bbb",
}), }),
} }
err := mqb.Create(ctx, &movie) err := mqb.Create(ctx, &group)
if err != nil { if err != nil {
return fmt.Errorf("Error creating movie: %w", err) return fmt.Errorf("Error creating group: %w", err)
} }
// query for movies that exclude the URL "aaa" // query for groups that exclude the URL "aaa"
urlCriterion := models.StringCriterionInput{ urlCriterion := models.StringCriterionInput{
Value: "aaa", Value: "aaa",
Modifier: models.CriterionModifierExcludes, Modifier: models.CriterionModifierExcludes,
} }
nameCriterion := models.StringCriterionInput{ nameCriterion := models.StringCriterionInput{
Value: movie.Name, Value: group.Name,
Modifier: models.CriterionModifierEquals, Modifier: models.CriterionModifierEquals,
} }
filter := models.MovieFilterType{ filter := models.GroupFilterType{
URL: &urlCriterion, URL: &urlCriterion,
Name: &nameCriterion, Name: &nameCriterion,
} }
movies := queryMovies(ctx, t, &filter, nil) groups := queryGroups(ctx, t, &filter, nil)
assert.Len(t, movies, 0, "Expected no movies to be found") assert.Len(t, groups, 0, "Expected no groups to be found")
// query for movies that exclude the URL "ccc" // query for groups that exclude the URL "ccc"
urlCriterion.Value = "ccc" urlCriterion.Value = "ccc"
movies = queryMovies(ctx, t, &filter, nil) groups = queryGroups(ctx, t, &filter, nil)
if assert.Len(t, movies, 1, "Expected one movie to be found") { if assert.Len(t, groups, 1, "Expected one group to be found") {
assert.Equal(t, movie.Name, movies[0].Name) assert.Equal(t, group.Name, groups[0].Name)
} }
return nil return nil
}) })
} }
func verifyMovieQuery(t *testing.T, filter models.MovieFilterType, verifyFn func(s *models.Movie)) { func verifyGroupQuery(t *testing.T, filter models.GroupFilterType, verifyFn func(s *models.Group)) {
withTxn(func(ctx context.Context) error { withTxn(func(ctx context.Context) error {
t.Helper() t.Helper()
sqb := db.Movie sqb := db.Group
movies := queryMovies(ctx, t, &filter, nil) groups := queryGroups(ctx, t, &filter, nil)
for _, movie := range movies { for _, group := range groups {
if err := movie.LoadURLs(ctx, sqb); err != nil { if err := group.LoadURLs(ctx, sqb); err != nil {
t.Errorf("Error loading movie relationships: %v", err) t.Errorf("Error loading group relationships: %v", err)
} }
} }
// assume it should find at least one // assume it should find at least one
assert.Greater(t, len(movies), 0) assert.Greater(t, len(groups), 0)
for _, m := range movies { for _, m := range groups {
verifyFn(m) verifyFn(m)
} }
@@ -656,102 +656,102 @@ func verifyMovieQuery(t *testing.T, filter models.MovieFilterType, verifyFn func
}) })
} }
func queryMovies(ctx context.Context, t *testing.T, movieFilter *models.MovieFilterType, findFilter *models.FindFilterType) []*models.Movie { func queryGroups(ctx context.Context, t *testing.T, groupFilter *models.GroupFilterType, findFilter *models.FindFilterType) []*models.Group {
sqb := db.Movie sqb := db.Group
movies, _, err := sqb.Query(ctx, movieFilter, findFilter) groups, _, err := sqb.Query(ctx, groupFilter, findFilter)
if err != nil { if err != nil {
t.Errorf("Error querying movie: %s", err.Error()) t.Errorf("Error querying group: %s", err.Error())
} }
return movies return groups
} }
func TestMovieQueryTags(t *testing.T) { func TestGroupQueryTags(t *testing.T) {
withTxn(func(ctx context.Context) error { withTxn(func(ctx context.Context) error {
tagCriterion := models.HierarchicalMultiCriterionInput{ tagCriterion := models.HierarchicalMultiCriterionInput{
Value: []string{ Value: []string{
strconv.Itoa(tagIDs[tagIdxWithMovie]), strconv.Itoa(tagIDs[tagIdxWithGroup]),
strconv.Itoa(tagIDs[tagIdx1WithMovie]), strconv.Itoa(tagIDs[tagIdx1WithGroup]),
}, },
Modifier: models.CriterionModifierIncludes, Modifier: models.CriterionModifierIncludes,
} }
movieFilter := models.MovieFilterType{ groupFilter := models.GroupFilterType{
Tags: &tagCriterion, Tags: &tagCriterion,
} }
// ensure ids are correct // ensure ids are correct
movies := queryMovies(ctx, t, &movieFilter, nil) groups := queryGroups(ctx, t, &groupFilter, nil)
assert.Len(t, movies, 3) assert.Len(t, groups, 3)
for _, movie := range movies { for _, group := range groups {
assert.True(t, movie.ID == movieIDs[movieIdxWithTag] || movie.ID == movieIDs[movieIdxWithTwoTags] || movie.ID == movieIDs[movieIdxWithThreeTags]) assert.True(t, group.ID == groupIDs[groupIdxWithTag] || group.ID == groupIDs[groupIdxWithTwoTags] || group.ID == groupIDs[groupIdxWithThreeTags])
} }
tagCriterion = models.HierarchicalMultiCriterionInput{ tagCriterion = models.HierarchicalMultiCriterionInput{
Value: []string{ Value: []string{
strconv.Itoa(tagIDs[tagIdx1WithMovie]), strconv.Itoa(tagIDs[tagIdx1WithGroup]),
strconv.Itoa(tagIDs[tagIdx2WithMovie]), strconv.Itoa(tagIDs[tagIdx2WithGroup]),
}, },
Modifier: models.CriterionModifierIncludesAll, Modifier: models.CriterionModifierIncludesAll,
} }
movies = queryMovies(ctx, t, &movieFilter, nil) groups = queryGroups(ctx, t, &groupFilter, nil)
if assert.Len(t, movies, 2) { if assert.Len(t, groups, 2) {
assert.Equal(t, sceneIDs[movieIdxWithTwoTags], movies[0].ID) assert.Equal(t, sceneIDs[groupIdxWithTwoTags], groups[0].ID)
assert.Equal(t, sceneIDs[movieIdxWithThreeTags], movies[1].ID) assert.Equal(t, sceneIDs[groupIdxWithThreeTags], groups[1].ID)
} }
tagCriterion = models.HierarchicalMultiCriterionInput{ tagCriterion = models.HierarchicalMultiCriterionInput{
Value: []string{ Value: []string{
strconv.Itoa(tagIDs[tagIdx1WithMovie]), strconv.Itoa(tagIDs[tagIdx1WithGroup]),
}, },
Modifier: models.CriterionModifierExcludes, Modifier: models.CriterionModifierExcludes,
} }
q := getSceneStringValue(movieIdxWithTwoTags, titleField) q := getSceneStringValue(groupIdxWithTwoTags, titleField)
findFilter := models.FindFilterType{ findFilter := models.FindFilterType{
Q: &q, Q: &q,
} }
movies = queryMovies(ctx, t, &movieFilter, &findFilter) groups = queryGroups(ctx, t, &groupFilter, &findFilter)
assert.Len(t, movies, 0) assert.Len(t, groups, 0)
return nil return nil
}) })
} }
func TestMovieQueryTagCount(t *testing.T) { func TestGroupQueryTagCount(t *testing.T) {
const tagCount = 1 const tagCount = 1
tagCountCriterion := models.IntCriterionInput{ tagCountCriterion := models.IntCriterionInput{
Value: tagCount, Value: tagCount,
Modifier: models.CriterionModifierEquals, Modifier: models.CriterionModifierEquals,
} }
verifyMoviesTagCount(t, tagCountCriterion) verifyGroupsTagCount(t, tagCountCriterion)
tagCountCriterion.Modifier = models.CriterionModifierNotEquals tagCountCriterion.Modifier = models.CriterionModifierNotEquals
verifyMoviesTagCount(t, tagCountCriterion) verifyGroupsTagCount(t, tagCountCriterion)
tagCountCriterion.Modifier = models.CriterionModifierGreaterThan tagCountCriterion.Modifier = models.CriterionModifierGreaterThan
verifyMoviesTagCount(t, tagCountCriterion) verifyGroupsTagCount(t, tagCountCriterion)
tagCountCriterion.Modifier = models.CriterionModifierLessThan tagCountCriterion.Modifier = models.CriterionModifierLessThan
verifyMoviesTagCount(t, tagCountCriterion) verifyGroupsTagCount(t, tagCountCriterion)
} }
func verifyMoviesTagCount(t *testing.T, tagCountCriterion models.IntCriterionInput) { func verifyGroupsTagCount(t *testing.T, tagCountCriterion models.IntCriterionInput) {
withTxn(func(ctx context.Context) error { withTxn(func(ctx context.Context) error {
sqb := db.Movie sqb := db.Group
movieFilter := models.MovieFilterType{ groupFilter := models.GroupFilterType{
TagCount: &tagCountCriterion, TagCount: &tagCountCriterion,
} }
movies := queryMovies(ctx, t, &movieFilter, nil) groups := queryGroups(ctx, t, &groupFilter, nil)
assert.Greater(t, len(movies), 0) assert.Greater(t, len(groups), 0)
for _, movie := range movies { for _, group := range groups {
ids, err := sqb.GetTagIDs(ctx, movie.ID) ids, err := sqb.GetTagIDs(ctx, group.ID)
if err != nil { if err != nil {
return err return err
} }
@@ -762,7 +762,7 @@ func verifyMoviesTagCount(t *testing.T, tagCountCriterion models.IntCriterionInp
}) })
} }
func TestMovieQuerySorting(t *testing.T) { func TestGroupQuerySorting(t *testing.T) {
sort := "scenes_count" sort := "scenes_count"
direction := models.SortDirectionEnumDesc direction := models.SortDirectionEnumDesc
findFilter := models.FindFilterType{ findFilter := models.FindFilterType{
@@ -771,60 +771,60 @@ func TestMovieQuerySorting(t *testing.T) {
} }
withTxn(func(ctx context.Context) error { withTxn(func(ctx context.Context) error {
movies := queryMovies(ctx, t, nil, &findFilter) groups := queryGroups(ctx, t, nil, &findFilter)
// scenes should be in same order as indexes // scenes should be in same order as indexes
firstMovie := movies[0] firstGroup := groups[0]
assert.Equal(t, movieIDs[movieIdxWithScene], firstMovie.ID) assert.Equal(t, groupIDs[groupIdxWithScene], firstGroup.ID)
// sort in descending order // sort in descending order
direction = models.SortDirectionEnumAsc direction = models.SortDirectionEnumAsc
movies = queryMovies(ctx, t, nil, &findFilter) groups = queryGroups(ctx, t, nil, &findFilter)
lastMovie := movies[len(movies)-1] lastGroup := groups[len(groups)-1]
assert.Equal(t, movieIDs[movieIdxWithScene], lastMovie.ID) assert.Equal(t, groupIDs[groupIdxWithScene], lastGroup.ID)
return nil return nil
}) })
} }
func TestMovieUpdateFrontImage(t *testing.T) { func TestGroupUpdateFrontImage(t *testing.T) {
if err := withRollbackTxn(func(ctx context.Context) error { if err := withRollbackTxn(func(ctx context.Context) error {
qb := db.Movie qb := db.Group
// create movie to test against // create group to test against
const name = "TestMovieUpdateMovieImages" const name = "TestGroupUpdateGroupImages"
movie := models.Movie{ group := models.Group{
Name: name, Name: name,
} }
err := qb.Create(ctx, &movie) err := qb.Create(ctx, &group)
if err != nil { if err != nil {
return fmt.Errorf("Error creating movie: %s", err.Error()) return fmt.Errorf("Error creating group: %s", err.Error())
} }
return testUpdateImage(t, ctx, movie.ID, qb.UpdateFrontImage, qb.GetFrontImage) return testUpdateImage(t, ctx, group.ID, qb.UpdateFrontImage, qb.GetFrontImage)
}); err != nil { }); err != nil {
t.Error(err.Error()) t.Error(err.Error())
} }
} }
func TestMovieUpdateBackImage(t *testing.T) { func TestGroupUpdateBackImage(t *testing.T) {
if err := withRollbackTxn(func(ctx context.Context) error { if err := withRollbackTxn(func(ctx context.Context) error {
qb := db.Movie qb := db.Group
// create movie to test against // create group to test against
const name = "TestMovieUpdateMovieImages" const name = "TestGroupUpdateGroupImages"
movie := models.Movie{ group := models.Group{
Name: name, Name: name,
} }
err := qb.Create(ctx, &movie) err := qb.Create(ctx, &group)
if err != nil { if err != nil {
return fmt.Errorf("Error creating movie: %s", err.Error()) return fmt.Errorf("Error creating group: %s", err.Error())
} }
return testUpdateImage(t, ctx, movie.ID, qb.UpdateBackImage, qb.GetBackImage) return testUpdateImage(t, ctx, group.ID, qb.UpdateBackImage, qb.GetBackImage)
}); err != nil { }); err != nil {
t.Error(err.Error()) t.Error(err.Error())
} }

View File

@@ -28,7 +28,7 @@ const (
performersScenesTable = "performers_scenes" performersScenesTable = "performers_scenes"
scenesTagsTable = "scenes_tags" scenesTagsTable = "scenes_tags"
scenesGalleriesTable = "scenes_galleries" scenesGalleriesTable = "scenes_galleries"
moviesScenesTable = "movies_scenes" groupsScenesTable = "movies_scenes"
scenesURLsTable = "scene_urls" scenesURLsTable = "scene_urls"
sceneURLColumn = "url" sceneURLColumn = "url"
scenesViewDatesTable = "scenes_view_dates" scenesViewDatesTable = "scenes_view_dates"
@@ -173,7 +173,7 @@ type sceneRepositoryType struct {
galleries joinRepository galleries joinRepository
tags joinRepository tags joinRepository
performers joinRepository performers joinRepository
movies repository groups repository
files filesRepository files filesRepository
@@ -209,8 +209,8 @@ var (
}, },
fkColumn: performerIDColumn, fkColumn: performerIDColumn,
}, },
movies: repository{ groups: repository{
tableName: moviesScenesTable, tableName: groupsScenesTable,
idColumn: sceneIDColumn, idColumn: sceneIDColumn,
}, },
files: filesRepository{ files: filesRepository{
@@ -343,8 +343,8 @@ func (qb *SceneStore) Create(ctx context.Context, newObject *models.Scene, fileI
} }
} }
if newObject.Movies.Loaded() { if newObject.Groups.Loaded() {
if err := scenesMoviesTableMgr.insertJoins(ctx, id, newObject.Movies.List()); err != nil { if err := scenesGroupsTableMgr.insertJoins(ctx, id, newObject.Groups.List()); err != nil {
return err return err
} }
} }
@@ -399,8 +399,8 @@ func (qb *SceneStore) UpdatePartial(ctx context.Context, id int, partial models.
return nil, err return nil, err
} }
} }
if partial.MovieIDs != nil { if partial.GroupIDs != nil {
if err := scenesMoviesTableMgr.modifyJoins(ctx, id, partial.MovieIDs.Movies, partial.MovieIDs.Mode); err != nil { if err := scenesGroupsTableMgr.modifyJoins(ctx, id, partial.GroupIDs.Groups, partial.GroupIDs.Mode); err != nil {
return nil, err return nil, err
} }
} }
@@ -451,8 +451,8 @@ func (qb *SceneStore) Update(ctx context.Context, updatedObject *models.Scene) e
} }
} }
if updatedObject.Movies.Loaded() { if updatedObject.Groups.Loaded() {
if err := scenesMoviesTableMgr.replaceJoins(ctx, updatedObject.ID, updatedObject.Movies.List()); err != nil { if err := scenesGroupsTableMgr.replaceJoins(ctx, updatedObject.ID, updatedObject.Groups.List()); err != nil {
return err return err
} }
} }
@@ -778,23 +778,23 @@ func (qb *SceneStore) OCountByPerformerID(ctx context.Context, performerID int)
return ret, nil return ret, nil
} }
func (qb *SceneStore) FindByMovieID(ctx context.Context, movieID int) ([]*models.Scene, error) { func (qb *SceneStore) FindByGroupID(ctx context.Context, groupID int) ([]*models.Scene, error) {
sq := dialect.From(scenesMoviesJoinTable).Select(scenesMoviesJoinTable.Col(sceneIDColumn)).Where( sq := dialect.From(scenesGroupsJoinTable).Select(scenesGroupsJoinTable.Col(sceneIDColumn)).Where(
scenesMoviesJoinTable.Col(movieIDColumn).Eq(movieID), scenesGroupsJoinTable.Col(groupIDColumn).Eq(groupID),
) )
ret, err := qb.findBySubquery(ctx, sq) ret, err := qb.findBySubquery(ctx, sq)
if err != nil { if err != nil {
return nil, fmt.Errorf("getting scenes for movie %d: %w", movieID, err) return nil, fmt.Errorf("getting scenes for group %d: %w", groupID, err)
} }
return ret, nil return ret, nil
} }
func (qb *SceneStore) CountByMovieID(ctx context.Context, movieID int) (int, error) { func (qb *SceneStore) CountByGroupID(ctx context.Context, groupID int) (int, error) {
joinTable := scenesMoviesJoinTable joinTable := scenesGroupsJoinTable
q := dialect.Select(goqu.COUNT("*")).From(joinTable).Where(joinTable.Col(movieIDColumn).Eq(movieID)) q := dialect.Select(goqu.COUNT("*")).From(joinTable).Where(joinTable.Col(groupIDColumn).Eq(groupID))
return count(ctx, q) return count(ctx, q)
} }
@@ -1142,8 +1142,8 @@ func (qb *SceneStore) setSceneSort(query *queryBuilder, findFilter *models.FindF
direction := findFilter.GetDirection() direction := findFilter.GetDirection()
switch sort { switch sort {
case "movie_scene_number", "group_scene_number": case "movie_scene_number", "group_scene_number":
query.join(moviesScenesTable, "", "scenes.id = movies_scenes.scene_id") query.join(groupsScenesTable, "", "scenes.id = movies_scenes.scene_id")
query.sortAndPagination += getSort("scene_index", direction, moviesScenesTable) query.sortAndPagination += getSort("scene_index", direction, groupsScenesTable)
case "tag_count": case "tag_count":
query.sortAndPagination += getCountSort(sceneTable, scenesTagsTable, sceneIDColumn, direction) query.sortAndPagination += getCountSort(sceneTable, scenesTagsTable, sceneIDColumn, direction)
case "performer_count": case "performer_count":
@@ -1270,11 +1270,11 @@ func (qb *SceneStore) AssignFiles(ctx context.Context, sceneID int, fileIDs []mo
return scenesFilesTableMgr.insertJoins(ctx, sceneID, firstPrimary, fileIDs) return scenesFilesTableMgr.insertJoins(ctx, sceneID, firstPrimary, fileIDs)
} }
func (qb *SceneStore) GetMovies(ctx context.Context, id int) (ret []models.MoviesScenes, err error) { func (qb *SceneStore) GetGroups(ctx context.Context, id int) (ret []models.GroupsScenes, err error) {
ret = []models.MoviesScenes{} ret = []models.GroupsScenes{}
if err := sceneRepository.movies.getAll(ctx, id, func(rows *sqlx.Rows) error { if err := sceneRepository.groups.getAll(ctx, id, func(rows *sqlx.Rows) error {
var ms moviesScenesRow var ms groupsScenesRow
if err := rows.StructScan(&ms); err != nil { if err := rows.StructScan(&ms); err != nil {
return err return err
} }

View File

@@ -195,10 +195,10 @@ func (qb *sceneFilterHandler) criterionHandler() criterionHandler {
&relatedFilterHandler{ &relatedFilterHandler{
relatedIDCol: "movies_scenes.movie_id", relatedIDCol: "movies_scenes.movie_id",
relatedRepo: movieRepository.repository, relatedRepo: groupRepository.repository,
relatedHandler: &movieFilterHandler{sceneFilter.MoviesFilter}, relatedHandler: &groupFilterHandler{sceneFilter.MoviesFilter},
joinFn: func(f *filterBuilder) { joinFn: func(f *filterBuilder) {
sceneRepository.movies.innerJoin(f, "", "scenes.id") sceneRepository.groups.innerJoin(f, "", "scenes.id")
}, },
}, },
@@ -320,7 +320,7 @@ func (qb *sceneFilterHandler) isMissingCriterionHandler(isMissing *string) crite
case "studio": case "studio":
f.addWhere("scenes.studio_id IS NULL") f.addWhere("scenes.studio_id IS NULL")
case "movie": case "movie":
sceneRepository.movies.join(f, "movies_join", "scenes.id") sceneRepository.groups.join(f, "movies_join", "scenes.id")
f.addWhere("movies_join.scene_id IS NULL") f.addWhere("movies_join.scene_id IS NULL")
case "performers": case "performers":
sceneRepository.performers.join(f, "performers_join", "scenes.id") sceneRepository.performers.join(f, "performers_join", "scenes.id")
@@ -485,10 +485,10 @@ func (qb *sceneFilterHandler) performerAgeCriterionHandler(performerAge *models.
func (qb *sceneFilterHandler) groupsCriterionHandler(movies *models.MultiCriterionInput) criterionHandlerFunc { func (qb *sceneFilterHandler) groupsCriterionHandler(movies *models.MultiCriterionInput) criterionHandlerFunc {
addJoinsFunc := func(f *filterBuilder) { addJoinsFunc := func(f *filterBuilder) {
sceneRepository.movies.join(f, "", "scenes.id") sceneRepository.groups.join(f, "", "scenes.id")
f.addLeftJoin("movies", "", "movies_scenes.movie_id = movies.id") f.addLeftJoin("movies", "", "movies_scenes.movie_id = movies.id")
} }
h := qb.getMultiCriterionHandlerBuilder(movieTable, moviesScenesTable, "movie_id", addJoinsFunc) h := qb.getMultiCriterionHandlerBuilder(groupTable, groupsScenesTable, "movie_id", addJoinsFunc)
return h.handler(movies) return h.handler(movies)
} }

View File

@@ -41,8 +41,8 @@ func loadSceneRelationships(ctx context.Context, expected models.Scene, actual *
return err return err
} }
} }
if expected.Movies.Loaded() { if expected.Groups.Loaded() {
if err := actual.LoadMovies(ctx, db.Scene); err != nil { if err := actual.LoadGroups(ctx, db.Scene); err != nil {
return err return err
} }
} }
@@ -120,13 +120,13 @@ func Test_sceneQueryBuilder_Create(t *testing.T) {
GalleryIDs: models.NewRelatedIDs([]int{galleryIDs[galleryIdxWithScene]}), GalleryIDs: models.NewRelatedIDs([]int{galleryIDs[galleryIdxWithScene]}),
TagIDs: models.NewRelatedIDs([]int{tagIDs[tagIdx1WithDupName], tagIDs[tagIdx1WithScene]}), TagIDs: models.NewRelatedIDs([]int{tagIDs[tagIdx1WithDupName], tagIDs[tagIdx1WithScene]}),
PerformerIDs: models.NewRelatedIDs([]int{performerIDs[performerIdx1WithScene], performerIDs[performerIdx1WithDupName]}), PerformerIDs: models.NewRelatedIDs([]int{performerIDs[performerIdx1WithScene], performerIDs[performerIdx1WithDupName]}),
Movies: models.NewRelatedMovies([]models.MoviesScenes{ Groups: models.NewRelatedGroups([]models.GroupsScenes{
{ {
MovieID: movieIDs[movieIdxWithScene], GroupID: groupIDs[groupIdxWithScene],
SceneIndex: &sceneIndex, SceneIndex: &sceneIndex,
}, },
{ {
MovieID: movieIDs[movieIdxWithStudio], GroupID: groupIDs[groupIdxWithStudio],
SceneIndex: &sceneIndex2, SceneIndex: &sceneIndex2,
}, },
}), }),
@@ -165,13 +165,13 @@ func Test_sceneQueryBuilder_Create(t *testing.T) {
GalleryIDs: models.NewRelatedIDs([]int{galleryIDs[galleryIdxWithScene]}), GalleryIDs: models.NewRelatedIDs([]int{galleryIDs[galleryIdxWithScene]}),
TagIDs: models.NewRelatedIDs([]int{tagIDs[tagIdx1WithDupName], tagIDs[tagIdx1WithScene]}), TagIDs: models.NewRelatedIDs([]int{tagIDs[tagIdx1WithDupName], tagIDs[tagIdx1WithScene]}),
PerformerIDs: models.NewRelatedIDs([]int{performerIDs[performerIdx1WithScene], performerIDs[performerIdx1WithDupName]}), PerformerIDs: models.NewRelatedIDs([]int{performerIDs[performerIdx1WithScene], performerIDs[performerIdx1WithDupName]}),
Movies: models.NewRelatedMovies([]models.MoviesScenes{ Groups: models.NewRelatedGroups([]models.GroupsScenes{
{ {
MovieID: movieIDs[movieIdxWithScene], GroupID: groupIDs[groupIdxWithScene],
SceneIndex: &sceneIndex, SceneIndex: &sceneIndex,
}, },
{ {
MovieID: movieIDs[movieIdxWithStudio], GroupID: groupIDs[groupIdxWithStudio],
SceneIndex: &sceneIndex2, SceneIndex: &sceneIndex2,
}, },
}), }),
@@ -219,11 +219,11 @@ func Test_sceneQueryBuilder_Create(t *testing.T) {
true, true,
}, },
{ {
"invalid movie id", "invalid group id",
models.Scene{ models.Scene{
Movies: models.NewRelatedMovies([]models.MoviesScenes{ Groups: models.NewRelatedGroups([]models.GroupsScenes{
{ {
MovieID: invalidID, GroupID: invalidID,
SceneIndex: &sceneIndex, SceneIndex: &sceneIndex,
}, },
}), }),
@@ -349,13 +349,13 @@ func Test_sceneQueryBuilder_Update(t *testing.T) {
GalleryIDs: models.NewRelatedIDs([]int{galleryIDs[galleryIdxWithScene]}), GalleryIDs: models.NewRelatedIDs([]int{galleryIDs[galleryIdxWithScene]}),
TagIDs: models.NewRelatedIDs([]int{tagIDs[tagIdx1WithDupName], tagIDs[tagIdx1WithScene]}), TagIDs: models.NewRelatedIDs([]int{tagIDs[tagIdx1WithDupName], tagIDs[tagIdx1WithScene]}),
PerformerIDs: models.NewRelatedIDs([]int{performerIDs[performerIdx1WithScene], performerIDs[performerIdx1WithDupName]}), PerformerIDs: models.NewRelatedIDs([]int{performerIDs[performerIdx1WithScene], performerIDs[performerIdx1WithDupName]}),
Movies: models.NewRelatedMovies([]models.MoviesScenes{ Groups: models.NewRelatedGroups([]models.GroupsScenes{
{ {
MovieID: movieIDs[movieIdxWithScene], GroupID: groupIDs[groupIdxWithScene],
SceneIndex: &sceneIndex, SceneIndex: &sceneIndex,
}, },
{ {
MovieID: movieIDs[movieIdxWithStudio], GroupID: groupIDs[groupIdxWithStudio],
SceneIndex: &sceneIndex2, SceneIndex: &sceneIndex2,
}, },
}), }),
@@ -381,7 +381,7 @@ func Test_sceneQueryBuilder_Update(t *testing.T) {
GalleryIDs: models.NewRelatedIDs([]int{}), GalleryIDs: models.NewRelatedIDs([]int{}),
TagIDs: models.NewRelatedIDs([]int{}), TagIDs: models.NewRelatedIDs([]int{}),
PerformerIDs: models.NewRelatedIDs([]int{}), PerformerIDs: models.NewRelatedIDs([]int{}),
Movies: models.NewRelatedMovies([]models.MoviesScenes{}), Groups: models.NewRelatedGroups([]models.GroupsScenes{}),
StashIDs: models.NewRelatedStashIDs([]models.StashID{}), StashIDs: models.NewRelatedStashIDs([]models.StashID{}),
}, },
false, false,
@@ -411,10 +411,10 @@ func Test_sceneQueryBuilder_Update(t *testing.T) {
false, false,
}, },
{ {
"clear movies", "clear groups",
&models.Scene{ &models.Scene{
ID: sceneIDs[sceneIdxWithMovie], ID: sceneIDs[sceneIdxWithGroup],
Movies: models.NewRelatedMovies([]models.MoviesScenes{}), Groups: models.NewRelatedGroups([]models.GroupsScenes{}),
}, },
false, false,
}, },
@@ -451,12 +451,12 @@ func Test_sceneQueryBuilder_Update(t *testing.T) {
true, true,
}, },
{ {
"invalid movie id", "invalid group id",
&models.Scene{ &models.Scene{
ID: sceneIDs[sceneIdxWithSpacedName], ID: sceneIDs[sceneIdxWithSpacedName],
Movies: models.NewRelatedMovies([]models.MoviesScenes{ Groups: models.NewRelatedGroups([]models.GroupsScenes{
{ {
MovieID: invalidID, GroupID: invalidID,
SceneIndex: &sceneIndex, SceneIndex: &sceneIndex,
}, },
}), }),
@@ -573,14 +573,14 @@ func Test_sceneQueryBuilder_UpdatePartial(t *testing.T) {
IDs: []int{performerIDs[performerIdx1WithScene], performerIDs[performerIdx1WithDupName]}, IDs: []int{performerIDs[performerIdx1WithScene], performerIDs[performerIdx1WithDupName]},
Mode: models.RelationshipUpdateModeSet, Mode: models.RelationshipUpdateModeSet,
}, },
MovieIDs: &models.UpdateMovieIDs{ GroupIDs: &models.UpdateGroupIDs{
Movies: []models.MoviesScenes{ Groups: []models.GroupsScenes{
{ {
MovieID: movieIDs[movieIdxWithScene], GroupID: groupIDs[groupIdxWithScene],
SceneIndex: &sceneIndex, SceneIndex: &sceneIndex,
}, },
{ {
MovieID: movieIDs[movieIdxWithStudio], GroupID: groupIDs[groupIdxWithStudio],
SceneIndex: &sceneIndex2, SceneIndex: &sceneIndex2,
}, },
}, },
@@ -621,13 +621,13 @@ func Test_sceneQueryBuilder_UpdatePartial(t *testing.T) {
GalleryIDs: models.NewRelatedIDs([]int{galleryIDs[galleryIdxWithScene]}), GalleryIDs: models.NewRelatedIDs([]int{galleryIDs[galleryIdxWithScene]}),
TagIDs: models.NewRelatedIDs([]int{tagIDs[tagIdx1WithDupName], tagIDs[tagIdx1WithScene]}), TagIDs: models.NewRelatedIDs([]int{tagIDs[tagIdx1WithDupName], tagIDs[tagIdx1WithScene]}),
PerformerIDs: models.NewRelatedIDs([]int{performerIDs[performerIdx1WithScene], performerIDs[performerIdx1WithDupName]}), PerformerIDs: models.NewRelatedIDs([]int{performerIDs[performerIdx1WithScene], performerIDs[performerIdx1WithDupName]}),
Movies: models.NewRelatedMovies([]models.MoviesScenes{ Groups: models.NewRelatedGroups([]models.GroupsScenes{
{ {
MovieID: movieIDs[movieIdxWithScene], GroupID: groupIDs[groupIdxWithScene],
SceneIndex: &sceneIndex, SceneIndex: &sceneIndex,
}, },
{ {
MovieID: movieIDs[movieIdxWithStudio], GroupID: groupIDs[groupIdxWithStudio],
SceneIndex: &sceneIndex2, SceneIndex: &sceneIndex2,
}, },
}), }),
@@ -658,7 +658,7 @@ func Test_sceneQueryBuilder_UpdatePartial(t *testing.T) {
GalleryIDs: models.NewRelatedIDs([]int{}), GalleryIDs: models.NewRelatedIDs([]int{}),
TagIDs: models.NewRelatedIDs([]int{}), TagIDs: models.NewRelatedIDs([]int{}),
PerformerIDs: models.NewRelatedIDs([]int{}), PerformerIDs: models.NewRelatedIDs([]int{}),
Movies: models.NewRelatedMovies([]models.MoviesScenes{}), Groups: models.NewRelatedGroups([]models.GroupsScenes{}),
StashIDs: models.NewRelatedStashIDs([]models.StashID{}), StashIDs: models.NewRelatedStashIDs([]models.StashID{}),
PlayDuration: getScenePlayDuration(sceneIdxWithSpacedName), PlayDuration: getScenePlayDuration(sceneIdxWithSpacedName),
ResumeTime: getSceneResumeTime(sceneIdxWithSpacedName), ResumeTime: getSceneResumeTime(sceneIdxWithSpacedName),
@@ -727,13 +727,13 @@ func Test_sceneQueryBuilder_UpdatePartialRelationships(t *testing.T) {
stashID1 = "stashid1" stashID1 = "stashid1"
stashID2 = "stashid2" stashID2 = "stashid2"
movieScenes = []models.MoviesScenes{ groupScenes = []models.GroupsScenes{
{ {
MovieID: movieIDs[movieIdxWithDupName], GroupID: groupIDs[groupIdxWithDupName],
SceneIndex: &sceneIndex, SceneIndex: &sceneIndex,
}, },
{ {
MovieID: movieIDs[movieIdxWithStudio], GroupID: groupIDs[groupIdxWithStudio],
SceneIndex: &sceneIndex2, SceneIndex: &sceneIndex2,
}, },
} }
@@ -863,40 +863,40 @@ func Test_sceneQueryBuilder_UpdatePartialRelationships(t *testing.T) {
false, false,
}, },
{ {
"add movies", "add groups",
sceneIDs[sceneIdxWithMovie], sceneIDs[sceneIdxWithGroup],
models.ScenePartial{ models.ScenePartial{
MovieIDs: &models.UpdateMovieIDs{ GroupIDs: &models.UpdateGroupIDs{
Movies: movieScenes, Groups: groupScenes,
Mode: models.RelationshipUpdateModeAdd, Mode: models.RelationshipUpdateModeAdd,
}, },
}, },
models.Scene{ models.Scene{
Movies: models.NewRelatedMovies(append([]models.MoviesScenes{ Groups: models.NewRelatedGroups(append([]models.GroupsScenes{
{ {
MovieID: indexesToIDs(movieIDs, sceneMovies[sceneIdxWithMovie])[0], GroupID: indexesToIDs(groupIDs, sceneGroups[sceneIdxWithGroup])[0],
}, },
}, movieScenes...)), }, groupScenes...)),
}, },
false, false,
}, },
{ {
"add movies to empty", "add groups to empty",
sceneIDs[sceneIdx1WithPerformer], sceneIDs[sceneIdx1WithPerformer],
models.ScenePartial{ models.ScenePartial{
MovieIDs: &models.UpdateMovieIDs{ GroupIDs: &models.UpdateGroupIDs{
Movies: movieScenes, Groups: groupScenes,
Mode: models.RelationshipUpdateModeAdd, Mode: models.RelationshipUpdateModeAdd,
}, },
}, },
models.Scene{ models.Scene{
Movies: models.NewRelatedMovies([]models.MoviesScenes{ Groups: models.NewRelatedGroups([]models.GroupsScenes{
{ {
MovieID: movieIDs[movieIdxWithDupName], GroupID: groupIDs[groupIdxWithDupName],
SceneIndex: &sceneIndex, SceneIndex: &sceneIndex,
}, },
{ {
MovieID: movieIDs[movieIdxWithStudio], GroupID: groupIDs[groupIdxWithStudio],
SceneIndex: &sceneIndex2, SceneIndex: &sceneIndex2,
}, },
}), }),
@@ -967,27 +967,27 @@ func Test_sceneQueryBuilder_UpdatePartialRelationships(t *testing.T) {
false, false,
}, },
{ {
"add duplicate movies", "add duplicate groups",
sceneIDs[sceneIdxWithMovie], sceneIDs[sceneIdxWithGroup],
models.ScenePartial{ models.ScenePartial{
MovieIDs: &models.UpdateMovieIDs{ GroupIDs: &models.UpdateGroupIDs{
Movies: append([]models.MoviesScenes{ Groups: append([]models.GroupsScenes{
{ {
MovieID: movieIDs[movieIdxWithScene], GroupID: groupIDs[groupIdxWithScene],
SceneIndex: &sceneIndex, SceneIndex: &sceneIndex,
}, },
}, },
movieScenes..., groupScenes...,
), ),
Mode: models.RelationshipUpdateModeAdd, Mode: models.RelationshipUpdateModeAdd,
}, },
}, },
models.Scene{ models.Scene{
Movies: models.NewRelatedMovies(append([]models.MoviesScenes{ Groups: models.NewRelatedGroups(append([]models.GroupsScenes{
{ {
MovieID: indexesToIDs(movieIDs, sceneMovies[sceneIdxWithMovie])[0], GroupID: indexesToIDs(groupIDs, sceneGroups[sceneIdxWithGroup])[0],
}, },
}, movieScenes...)), }, groupScenes...)),
}, },
false, false,
}, },
@@ -1044,13 +1044,13 @@ func Test_sceneQueryBuilder_UpdatePartialRelationships(t *testing.T) {
true, true,
}, },
{ {
"add invalid movies", "add invalid groups",
sceneIDs[sceneIdxWithMovie], sceneIDs[sceneIdxWithGroup],
models.ScenePartial{ models.ScenePartial{
MovieIDs: &models.UpdateMovieIDs{ GroupIDs: &models.UpdateGroupIDs{
Movies: []models.MoviesScenes{ Groups: []models.GroupsScenes{
{ {
MovieID: invalidID, GroupID: invalidID,
}, },
}, },
Mode: models.RelationshipUpdateModeAdd, Mode: models.RelationshipUpdateModeAdd,
@@ -1102,20 +1102,20 @@ func Test_sceneQueryBuilder_UpdatePartialRelationships(t *testing.T) {
false, false,
}, },
{ {
"remove movies", "remove groups",
sceneIDs[sceneIdxWithMovie], sceneIDs[sceneIdxWithGroup],
models.ScenePartial{ models.ScenePartial{
MovieIDs: &models.UpdateMovieIDs{ GroupIDs: &models.UpdateGroupIDs{
Movies: []models.MoviesScenes{ Groups: []models.GroupsScenes{
{ {
MovieID: movieIDs[movieIdxWithScene], GroupID: groupIDs[groupIdxWithScene],
}, },
}, },
Mode: models.RelationshipUpdateModeRemove, Mode: models.RelationshipUpdateModeRemove,
}, },
}, },
models.Scene{ models.Scene{
Movies: models.NewRelatedMovies([]models.MoviesScenes{}), Groups: models.NewRelatedGroups([]models.GroupsScenes{}),
}, },
false, false,
}, },
@@ -1176,22 +1176,22 @@ func Test_sceneQueryBuilder_UpdatePartialRelationships(t *testing.T) {
false, false,
}, },
{ {
"remove unrelated movies", "remove unrelated groups",
sceneIDs[sceneIdxWithMovie], sceneIDs[sceneIdxWithGroup],
models.ScenePartial{ models.ScenePartial{
MovieIDs: &models.UpdateMovieIDs{ GroupIDs: &models.UpdateGroupIDs{
Movies: []models.MoviesScenes{ Groups: []models.GroupsScenes{
{ {
MovieID: movieIDs[movieIdxWithDupName], GroupID: groupIDs[groupIdxWithDupName],
}, },
}, },
Mode: models.RelationshipUpdateModeRemove, Mode: models.RelationshipUpdateModeRemove,
}, },
}, },
models.Scene{ models.Scene{
Movies: models.NewRelatedMovies([]models.MoviesScenes{ Groups: models.NewRelatedGroups([]models.GroupsScenes{
{ {
MovieID: indexesToIDs(movieIDs, sceneMovies[sceneIdxWithMovie])[0], GroupID: indexesToIDs(groupIDs, sceneGroups[sceneIdxWithGroup])[0],
}, },
}), }),
}, },
@@ -1257,9 +1257,9 @@ func Test_sceneQueryBuilder_UpdatePartialRelationships(t *testing.T) {
assert.ElementsMatch(tt.want.GalleryIDs.List(), got.GalleryIDs.List()) assert.ElementsMatch(tt.want.GalleryIDs.List(), got.GalleryIDs.List())
assert.ElementsMatch(tt.want.GalleryIDs.List(), s.GalleryIDs.List()) assert.ElementsMatch(tt.want.GalleryIDs.List(), s.GalleryIDs.List())
} }
if tt.partial.MovieIDs != nil { if tt.partial.GroupIDs != nil {
assert.ElementsMatch(tt.want.Movies.List(), got.Movies.List()) assert.ElementsMatch(tt.want.Groups.List(), got.Groups.List())
assert.ElementsMatch(tt.want.Movies.List(), s.Movies.List()) assert.ElementsMatch(tt.want.Groups.List(), s.Groups.List())
} }
if tt.partial.StashIDs != nil { if tt.partial.StashIDs != nil {
assert.ElementsMatch(tt.want.StashIDs.List(), got.StashIDs.List()) assert.ElementsMatch(tt.want.StashIDs.List(), got.StashIDs.List())
@@ -1467,9 +1467,9 @@ func Test_sceneQueryBuilder_Find(t *testing.T) {
false, false,
}, },
{ {
"with movies", "with groups",
sceneIDs[sceneIdxWithMovie], sceneIDs[sceneIdxWithGroup],
makeSceneWithID(sceneIdxWithMovie), makeSceneWithID(sceneIdxWithGroup),
false, false,
}, },
} }
@@ -1527,13 +1527,13 @@ func Test_sceneQueryBuilder_FindMany(t *testing.T) {
sceneIDs[sceneIdxWithGallery], sceneIDs[sceneIdxWithGallery],
sceneIDs[sceneIdxWithTwoPerformers], sceneIDs[sceneIdxWithTwoPerformers],
sceneIDs[sceneIdxWithTwoTags], sceneIDs[sceneIdxWithTwoTags],
sceneIDs[sceneIdxWithMovie], sceneIDs[sceneIdxWithGroup],
}, },
[]*models.Scene{ []*models.Scene{
makeSceneWithID(sceneIdxWithGallery), makeSceneWithID(sceneIdxWithGallery),
makeSceneWithID(sceneIdxWithTwoPerformers), makeSceneWithID(sceneIdxWithTwoPerformers),
makeSceneWithID(sceneIdxWithTwoTags), makeSceneWithID(sceneIdxWithTwoTags),
makeSceneWithID(sceneIdxWithMovie), makeSceneWithID(sceneIdxWithGroup),
}, },
false, false,
}, },
@@ -1608,9 +1608,9 @@ func Test_sceneQueryBuilder_FindByChecksum(t *testing.T) {
false, false,
}, },
{ {
"with movies", "with groups",
getChecksum(sceneIdxWithMovie), getChecksum(sceneIdxWithGroup),
[]*models.Scene{makeSceneWithID(sceneIdxWithMovie)}, []*models.Scene{makeSceneWithID(sceneIdxWithGroup)},
false, false,
}, },
} }
@@ -1678,9 +1678,9 @@ func Test_sceneQueryBuilder_FindByOSHash(t *testing.T) {
false, false,
}, },
{ {
"with movies", "with groups",
getOSHash(sceneIdxWithMovie), getOSHash(sceneIdxWithGroup),
[]*models.Scene{makeSceneWithID(sceneIdxWithMovie)}, []*models.Scene{makeSceneWithID(sceneIdxWithGroup)},
false, false,
}, },
} }
@@ -1749,9 +1749,9 @@ func Test_sceneQueryBuilder_FindByPath(t *testing.T) {
false, false,
}, },
{ {
"with movies", "with groups",
getPath(sceneIdxWithMovie), getPath(sceneIdxWithGroup),
[]*models.Scene{makeSceneWithID(sceneIdxWithMovie)}, []*models.Scene{makeSceneWithID(sceneIdxWithGroup)},
false, false,
}, },
} }
@@ -2107,7 +2107,7 @@ func TestSceneQuery(t *testing.T) {
}, },
}, },
[]int{sceneIdxWithGallery}, []int{sceneIdxWithGallery},
[]int{sceneIdxWithMovie}, []int{sceneIdxWithGroup},
false, false,
}, },
{ {
@@ -2120,7 +2120,7 @@ func TestSceneQuery(t *testing.T) {
}, },
}, },
[]int{sceneIdxWithGallery}, []int{sceneIdxWithGallery},
[]int{sceneIdxWithMovie}, []int{sceneIdxWithGroup},
false, false,
}, },
// { // {
@@ -2133,7 +2133,7 @@ func TestSceneQuery(t *testing.T) {
// }, // },
// }, // },
// []int{sceneIdxWithGallery}, // []int{sceneIdxWithGallery},
// []int{sceneIdxWithMovie}, // []int{sceneIdxWithGroup},
// false, // false,
// }, // },
{ {
@@ -3108,7 +3108,7 @@ func TestSceneQueryIsMissingMovies(t *testing.T) {
IsMissing: &isMissing, IsMissing: &isMissing,
} }
q := getSceneStringValue(sceneIdxWithMovie, titleField) q := getSceneStringValue(sceneIdxWithGroup, titleField)
findFilter := models.FindFilterType{ findFilter := models.FindFilterType{
Q: &q, Q: &q,
} }
@@ -3122,7 +3122,7 @@ func TestSceneQueryIsMissingMovies(t *testing.T) {
// ensure non of the ids equal the one with movies // ensure non of the ids equal the one with movies
for _, scene := range scenes { for _, scene := range scenes {
assert.NotEqual(t, sceneIDs[sceneIdxWithMovie], scene.ID) assert.NotEqual(t, sceneIDs[sceneIdxWithGroup], scene.ID)
} }
return nil return nil
@@ -3878,7 +3878,7 @@ func TestSceneQueryMovies(t *testing.T) {
sqb := db.Scene sqb := db.Scene
movieCriterion := models.MultiCriterionInput{ movieCriterion := models.MultiCriterionInput{
Value: []string{ Value: []string{
strconv.Itoa(movieIDs[movieIdxWithScene]), strconv.Itoa(groupIDs[groupIdxWithScene]),
}, },
Modifier: models.CriterionModifierIncludes, Modifier: models.CriterionModifierIncludes,
} }
@@ -3892,16 +3892,16 @@ func TestSceneQueryMovies(t *testing.T) {
assert.Len(t, scenes, 1) assert.Len(t, scenes, 1)
// ensure id is correct // ensure id is correct
assert.Equal(t, sceneIDs[sceneIdxWithMovie], scenes[0].ID) assert.Equal(t, sceneIDs[sceneIdxWithGroup], scenes[0].ID)
movieCriterion = models.MultiCriterionInput{ movieCriterion = models.MultiCriterionInput{
Value: []string{ Value: []string{
strconv.Itoa(movieIDs[movieIdxWithScene]), strconv.Itoa(groupIDs[groupIdxWithScene]),
}, },
Modifier: models.CriterionModifierExcludes, Modifier: models.CriterionModifierExcludes,
} }
q := getSceneStringValue(sceneIdxWithMovie, titleField) q := getSceneStringValue(sceneIdxWithGroup, titleField)
findFilter := models.FindFilterType{ findFilter := models.FindFilterType{
Q: &q, Q: &q,
} }
@@ -4212,22 +4212,22 @@ func TestSceneCountByTagID(t *testing.T) {
}) })
} }
func TestSceneCountByMovieID(t *testing.T) { func TestSceneCountByGroupID(t *testing.T) {
withTxn(func(ctx context.Context) error { withTxn(func(ctx context.Context) error {
sqb := db.Scene sqb := db.Scene
sceneCount, err := sqb.CountByMovieID(ctx, movieIDs[movieIdxWithScene]) sceneCount, err := sqb.CountByGroupID(ctx, groupIDs[groupIdxWithScene])
if err != nil { if err != nil {
t.Errorf("error calling CountByMovieID: %s", err.Error()) t.Errorf("error calling CountByGroupID: %s", err.Error())
} }
assert.Equal(t, 1, sceneCount) assert.Equal(t, 1, sceneCount)
sceneCount, err = sqb.CountByMovieID(ctx, 0) sceneCount, err = sqb.CountByGroupID(ctx, 0)
if err != nil { if err != nil {
t.Errorf("error calling CountByMovieID: %s", err.Error()) t.Errorf("error calling CountByGroupID: %s", err.Error())
} }
assert.Equal(t, 0, sceneCount) assert.Equal(t, 0, sceneCount)
@@ -4264,16 +4264,16 @@ func TestFindByMovieID(t *testing.T) {
withTxn(func(ctx context.Context) error { withTxn(func(ctx context.Context) error {
sqb := db.Scene sqb := db.Scene
scenes, err := sqb.FindByMovieID(ctx, movieIDs[movieIdxWithScene]) scenes, err := sqb.FindByGroupID(ctx, groupIDs[groupIdxWithScene])
if err != nil { if err != nil {
t.Errorf("error calling FindByMovieID: %s", err.Error()) t.Errorf("error calling FindByMovieID: %s", err.Error())
} }
assert.Len(t, scenes, 1) assert.Len(t, scenes, 1)
assert.Equal(t, sceneIDs[sceneIdxWithMovie], scenes[0].ID) assert.Equal(t, sceneIDs[sceneIdxWithGroup], scenes[0].ID)
scenes, err = sqb.FindByMovieID(ctx, 0) scenes, err = sqb.FindByGroupID(ctx, 0)
if err != nil { if err != nil {
t.Errorf("error calling FindByMovieID: %s", err.Error()) t.Errorf("error calling FindByMovieID: %s", err.Error())

View File

@@ -54,7 +54,7 @@ const (
) )
const ( const (
sceneIdxWithMovie = iota sceneIdxWithGroup = iota
sceneIdxWithGallery sceneIdxWithGallery
sceneIdxWithPerformer sceneIdxWithPerformer
sceneIdx1WithPerformer sceneIdx1WithPerformer
@@ -148,17 +148,17 @@ const (
) )
const ( const (
movieIdxWithScene = iota groupIdxWithScene = iota
movieIdxWithStudio groupIdxWithStudio
movieIdxWithTag groupIdxWithTag
movieIdxWithTwoTags groupIdxWithTwoTags
movieIdxWithThreeTags groupIdxWithThreeTags
// movies with dup names start from the end // groups with dup names start from the end
// create 7 more basic movies (can remove this if we add more indexes) // create 7 more basic groups (can remove this if we add more indexes)
movieIdxWithDupName = movieIdxWithStudio + 7 groupIdxWithDupName = groupIdxWithStudio + 7
moviesNameCase = movieIdxWithDupName groupsNameCase = groupIdxWithDupName
moviesNameNoCase = 1 groupsNameNoCase = 1
) )
const ( const (
@@ -220,10 +220,10 @@ const (
tagIdxWithParentAndChild tagIdxWithParentAndChild
tagIdxWithGrandParent tagIdxWithGrandParent
tagIdx2WithMarkers tagIdx2WithMarkers
tagIdxWithMovie tagIdxWithGroup
tagIdx1WithMovie tagIdx1WithGroup
tagIdx2WithMovie tagIdx2WithGroup
tagIdx3WithMovie tagIdx3WithGroup
// new indexes above // new indexes above
// tags with dup names start from the end // tags with dup names start from the end
tagIdx1WithDupName tagIdx1WithDupName
@@ -238,7 +238,7 @@ const (
const ( const (
studioIdxWithScene = iota studioIdxWithScene = iota
studioIdxWithTwoScenes studioIdxWithTwoScenes
studioIdxWithMovie studioIdxWithGroup
studioIdxWithChildStudio studioIdxWithChildStudio
studioIdxWithParentStudio studioIdxWithParentStudio
studioIdxWithImage studioIdxWithImage
@@ -305,7 +305,7 @@ var (
sceneIDs []int sceneIDs []int
imageIDs []int imageIDs []int
performerIDs []int performerIDs []int
movieIDs []int groupIDs []int
galleryIDs []int galleryIDs []int
tagIDs []int tagIDs []int
studioIDs []int studioIDs []int
@@ -316,7 +316,7 @@ var (
tagNames []string tagNames []string
studioNames []string studioNames []string
movieNames []string groupNames []string
performerNames []string performerNames []string
) )
@@ -389,8 +389,8 @@ var (
sceneIdxWithGallery: {galleryIdxWithScene}, sceneIdxWithGallery: {galleryIdxWithScene},
} }
sceneMovies = linkMap{ sceneGroups = linkMap{
sceneIdxWithMovie: {movieIdxWithScene}, sceneIdxWithGroup: {groupIdxWithScene},
} }
sceneStudios = map[int]int{ sceneStudios = map[int]int{
@@ -496,14 +496,14 @@ var (
) )
var ( var (
movieStudioLinks = [][2]int{ groupStudioLinks = [][2]int{
{movieIdxWithStudio, studioIdxWithMovie}, {groupIdxWithStudio, studioIdxWithGroup},
} }
movieTags = linkMap{ groupTags = linkMap{
movieIdxWithTag: {tagIdxWithMovie}, groupIdxWithTag: {tagIdxWithGroup},
movieIdxWithTwoTags: {tagIdx1WithMovie, tagIdx2WithMovie}, groupIdxWithTwoTags: {tagIdx1WithGroup, tagIdx2WithGroup},
movieIdxWithThreeTags: {tagIdx1WithMovie, tagIdx2WithMovie, tagIdx3WithMovie}, groupIdxWithThreeTags: {tagIdx1WithGroup, tagIdx2WithGroup, tagIdx3WithGroup},
} }
) )
@@ -653,8 +653,8 @@ func populateDB() error {
return fmt.Errorf("error creating tags: %s", err.Error()) return fmt.Errorf("error creating tags: %s", err.Error())
} }
if err := createMovies(ctx, db.Movie, moviesNameCase, moviesNameNoCase); err != nil { if err := createGroups(ctx, db.Group, groupsNameCase, groupsNameNoCase); err != nil {
return fmt.Errorf("error creating movies: %s", err.Error()) return fmt.Errorf("error creating groups: %s", err.Error())
} }
if err := createPerformers(ctx, performersNameCase, performersNameNoCase); err != nil { if err := createPerformers(ctx, performersNameCase, performersNameNoCase); err != nil {
@@ -685,8 +685,8 @@ func populateDB() error {
return fmt.Errorf("error creating saved filters: %s", err.Error()) return fmt.Errorf("error creating saved filters: %s", err.Error())
} }
if err := linkMovieStudios(ctx, db.Movie); err != nil { if err := linkGroupStudios(ctx, db.Group); err != nil {
return fmt.Errorf("error linking movie studios: %s", err.Error()) return fmt.Errorf("error linking group studios: %s", err.Error())
} }
if err := linkStudiosParent(ctx); err != nil { if err := linkStudiosParent(ctx); err != nil {
@@ -1069,12 +1069,12 @@ func makeScene(i int) *models.Scene {
pids := indexesToIDs(performerIDs, scenePerformers[i]) pids := indexesToIDs(performerIDs, scenePerformers[i])
tids := indexesToIDs(tagIDs, sceneTags[i]) tids := indexesToIDs(tagIDs, sceneTags[i])
mids := indexesToIDs(movieIDs, sceneMovies[i]) mids := indexesToIDs(groupIDs, sceneGroups[i])
movies := make([]models.MoviesScenes, len(mids)) groups := make([]models.GroupsScenes, len(mids))
for i, m := range mids { for i, m := range mids {
movies[i] = models.MoviesScenes{ groups[i] = models.GroupsScenes{
MovieID: m, GroupID: m,
} }
} }
@@ -1092,7 +1092,7 @@ func makeScene(i int) *models.Scene {
GalleryIDs: models.NewRelatedIDs(gids), GalleryIDs: models.NewRelatedIDs(gids),
PerformerIDs: models.NewRelatedIDs(pids), PerformerIDs: models.NewRelatedIDs(pids),
TagIDs: models.NewRelatedIDs(tids), TagIDs: models.NewRelatedIDs(tids),
Movies: models.NewRelatedMovies(movies), Groups: models.NewRelatedGroups(groups),
StashIDs: models.NewRelatedStashIDs([]models.StashID{ StashIDs: models.NewRelatedStashIDs([]models.StashID{
sceneStashID(i), sceneStashID(i),
}), }),
@@ -1320,18 +1320,18 @@ func createGalleries(ctx context.Context, n int) error {
return nil return nil
} }
func getMovieStringValue(index int, field string) string { func getGroupStringValue(index int, field string) string {
return getPrefixedStringValue("movie", index, field) return getPrefixedStringValue("group", index, field)
} }
func getMovieNullStringValue(index int, field string) string { func getGroupNullStringValue(index int, field string) string {
ret := getPrefixedNullStringValue("movie", index, field) ret := getPrefixedNullStringValue("group", index, field)
return ret.String return ret.String
} }
func getMovieEmptyString(index int, field string) string { func getGroupEmptyString(index int, field string) string {
v := getPrefixedNullStringValue("movie", index, field) v := getPrefixedNullStringValue("group", index, field)
if !v.Valid { if !v.Valid {
return "" return ""
} }
@@ -1339,8 +1339,8 @@ func getMovieEmptyString(index int, field string) string {
return v.String return v.String
} }
// createMoviees creates n movies with plain Name and o movies with camel cased NaMe included // createGroups creates n groups with plain Name and o groups with camel cased NaMe included
func createMovies(ctx context.Context, mqb models.MovieReaderWriter, n int, o int) error { func createGroups(ctx context.Context, mqb models.GroupReaderWriter, n int, o int) error {
const namePlain = "Name" const namePlain = "Name"
const nameNoCase = "NaMe" const nameNoCase = "NaMe"
@@ -1348,31 +1348,31 @@ func createMovies(ctx context.Context, mqb models.MovieReaderWriter, n int, o in
index := i index := i
name := namePlain name := namePlain
tids := indexesToIDs(tagIDs, movieTags[i]) tids := indexesToIDs(tagIDs, groupTags[i])
if i >= n { // i<n tags get normal names if i >= n { // i<n tags get normal names
name = nameNoCase // i>=n movies get dup names if case is not checked name = nameNoCase // i>=n groups get dup names if case is not checked
index = n + o - (i + 1) // for the name to be the same the number (index) must be the same also index = n + o - (i + 1) // for the name to be the same the number (index) must be the same also
} // so count backwards to 0 as needed } // so count backwards to 0 as needed
// movies [ i ] and [ n + o - i - 1 ] should have similar names with only the Name!=NaMe part different // groups [ i ] and [ n + o - i - 1 ] should have similar names with only the Name!=NaMe part different
name = getMovieStringValue(index, name) name = getGroupStringValue(index, name)
movie := models.Movie{ group := models.Group{
Name: name, Name: name,
URLs: models.NewRelatedStrings([]string{ URLs: models.NewRelatedStrings([]string{
getMovieEmptyString(i, urlField), getGroupEmptyString(i, urlField),
}), }),
TagIDs: models.NewRelatedIDs(tids), TagIDs: models.NewRelatedIDs(tids),
} }
err := mqb.Create(ctx, &movie) err := mqb.Create(ctx, &group)
if err != nil { if err != nil {
return fmt.Errorf("Error creating movie [%d] %v+: %s", i, movie, err.Error()) return fmt.Errorf("Error creating group [%d] %v+: %s", i, group, err.Error())
} }
movieIDs = append(movieIDs, movie.ID) groupIDs = append(groupIDs, group.ID)
movieNames = append(movieNames, movie.Name) groupNames = append(groupNames, group.Name)
} }
return nil return nil
@@ -1709,7 +1709,7 @@ func createStudios(ctx context.Context, n int, o int) error {
TagIDs: models.NewRelatedIDs(tids), TagIDs: models.NewRelatedIDs(tids),
} }
// only add aliases for some scenes // only add aliases for some scenes
if i == studioIdxWithMovie || i%5 == 0 { if i == studioIdxWithGroup || i%5 == 0 {
alias := getStudioStringValue(i, "Alias") alias := getStudioStringValue(i, "Alias")
studio.Aliases = models.NewRelatedStrings([]string{alias}) studio.Aliases = models.NewRelatedStrings([]string{alias})
} }
@@ -1842,12 +1842,12 @@ func doLinks(links [][2]int, fn func(idx1, idx2 int) error) error {
return nil return nil
} }
func linkMovieStudios(ctx context.Context, mqb models.MovieWriter) error { func linkGroupStudios(ctx context.Context, mqb models.GroupWriter) error {
return doLinks(movieStudioLinks, func(movieIndex, studioIndex int) error { return doLinks(groupStudioLinks, func(groupIndex, studioIndex int) error {
movie := models.MoviePartial{ group := models.GroupPartial{
StudioID: models.NewOptionalInt(studioIDs[studioIndex]), StudioID: models.NewOptionalInt(studioIDs[studioIndex]),
} }
_, err := mqb.UpdatePartial(ctx, movieIDs[movieIndex], movie) _, err := mqb.UpdatePartial(ctx, groupIDs[groupIndex], group)
return err return err
}) })

View File

@@ -216,7 +216,7 @@ func TestStudioQueryForAutoTag(t *testing.T) {
withTxn(func(ctx context.Context) error { withTxn(func(ctx context.Context) error {
tqb := db.Studio tqb := db.Studio
name := studioNames[studioIdxWithMovie] // find a studio by name name := studioNames[studioIdxWithGroup] // find a studio by name
studios, err := tqb.QueryForAutoTag(ctx, []string{name}) studios, err := tqb.QueryForAutoTag(ctx, []string{name})
@@ -225,16 +225,16 @@ func TestStudioQueryForAutoTag(t *testing.T) {
} }
assert.Len(t, studios, 1) assert.Len(t, studios, 1)
assert.Equal(t, strings.ToLower(studioNames[studioIdxWithMovie]), strings.ToLower(studios[0].Name)) assert.Equal(t, strings.ToLower(studioNames[studioIdxWithGroup]), strings.ToLower(studios[0].Name))
name = getStudioStringValue(studioIdxWithMovie, "Alias") name = getStudioStringValue(studioIdxWithGroup, "Alias")
studios, err = tqb.QueryForAutoTag(ctx, []string{name}) studios, err = tqb.QueryForAutoTag(ctx, []string{name})
if err != nil { if err != nil {
t.Errorf("Error finding studios: %s", err.Error()) t.Errorf("Error finding studios: %s", err.Error())
} }
if assert.Len(t, studios, 1) { if assert.Len(t, studios, 1) {
assert.Equal(t, studioIDs[studioIdxWithMovie], studios[0].ID) assert.Equal(t, studioIDs[studioIdxWithGroup], studios[0].ID)
} }
return nil return nil
}) })
@@ -911,7 +911,7 @@ func TestStudioQueryName(t *testing.T) {
} }
func TestStudioQueryAlias(t *testing.T) { func TestStudioQueryAlias(t *testing.T) {
const studioIdx = studioIdxWithMovie const studioIdx = studioIdxWithGroup
studioName := getStudioStringValue(studioIdx, "Alias") studioName := getStudioStringValue(studioIdx, "Alias")
aliasCriterion := &models.StringCriterionInput{ aliasCriterion := &models.StringCriterionInput{

View File

@@ -588,30 +588,30 @@ func (t *orderedValueTable[T]) modifyJoins(ctx context.Context, id int, v []T, m
return nil return nil
} }
type scenesMoviesTable struct { type scenesGroupsTable struct {
table table
} }
type moviesScenesRow struct { type groupsScenesRow struct {
SceneID null.Int `db:"scene_id"` SceneID null.Int `db:"scene_id"`
MovieID null.Int `db:"movie_id"` GroupID null.Int `db:"movie_id"`
SceneIndex null.Int `db:"scene_index"` SceneIndex null.Int `db:"scene_index"`
} }
func (r moviesScenesRow) resolve(sceneID int) models.MoviesScenes { func (r groupsScenesRow) resolve(sceneID int) models.GroupsScenes {
return models.MoviesScenes{ return models.GroupsScenes{
MovieID: int(r.MovieID.Int64), GroupID: int(r.GroupID.Int64),
SceneIndex: nullIntPtr(r.SceneIndex), SceneIndex: nullIntPtr(r.SceneIndex),
} }
} }
func (t *scenesMoviesTable) get(ctx context.Context, id int) ([]models.MoviesScenes, error) { func (t *scenesGroupsTable) get(ctx context.Context, id int) ([]models.GroupsScenes, error) {
q := dialect.Select("movie_id", "scene_index").From(t.table.table).Where(t.idColumn.Eq(id)) q := dialect.Select("movie_id", "scene_index").From(t.table.table).Where(t.idColumn.Eq(id))
const single = false const single = false
var ret []models.MoviesScenes var ret []models.GroupsScenes
if err := queryFunc(ctx, q, single, func(rows *sqlx.Rows) error { if err := queryFunc(ctx, q, single, func(rows *sqlx.Rows) error {
var v moviesScenesRow var v groupsScenesRow
if err := rows.StructScan(&v); err != nil { if err := rows.StructScan(&v); err != nil {
return err return err
} }
@@ -620,15 +620,15 @@ func (t *scenesMoviesTable) get(ctx context.Context, id int) ([]models.MoviesSce
return nil return nil
}); err != nil { }); err != nil {
return nil, fmt.Errorf("getting scene movies from %s: %w", t.table.table.GetTable(), err) return nil, fmt.Errorf("getting scene groups from %s: %w", t.table.table.GetTable(), err)
} }
return ret, nil return ret, nil
} }
func (t *scenesMoviesTable) insertJoin(ctx context.Context, id int, v models.MoviesScenes) (sql.Result, error) { func (t *scenesGroupsTable) insertJoin(ctx context.Context, id int, v models.GroupsScenes) (sql.Result, error) {
q := dialect.Insert(t.table.table).Cols(t.idColumn.GetCol(), "movie_id", "scene_index").Vals( q := dialect.Insert(t.table.table).Cols(t.idColumn.GetCol(), "movie_id", "scene_index").Vals(
goqu.Vals{id, v.MovieID, intFromPtr(v.SceneIndex)}, goqu.Vals{id, v.GroupID, intFromPtr(v.SceneIndex)},
) )
ret, err := exec(ctx, q) ret, err := exec(ctx, q)
if err != nil { if err != nil {
@@ -638,7 +638,7 @@ func (t *scenesMoviesTable) insertJoin(ctx context.Context, id int, v models.Mov
return ret, nil return ret, nil
} }
func (t *scenesMoviesTable) insertJoins(ctx context.Context, id int, v []models.MoviesScenes) error { func (t *scenesGroupsTable) insertJoins(ctx context.Context, id int, v []models.GroupsScenes) error {
for _, fk := range v { for _, fk := range v {
if _, err := t.insertJoin(ctx, id, fk); err != nil { if _, err := t.insertJoin(ctx, id, fk); err != nil {
return err return err
@@ -648,7 +648,7 @@ func (t *scenesMoviesTable) insertJoins(ctx context.Context, id int, v []models.
return nil return nil
} }
func (t *scenesMoviesTable) replaceJoins(ctx context.Context, id int, v []models.MoviesScenes) error { func (t *scenesGroupsTable) replaceJoins(ctx context.Context, id int, v []models.GroupsScenes) error {
if err := t.destroy(ctx, []int{id}); err != nil { if err := t.destroy(ctx, []int{id}); err != nil {
return err return err
} }
@@ -656,7 +656,7 @@ func (t *scenesMoviesTable) replaceJoins(ctx context.Context, id int, v []models
return t.insertJoins(ctx, id, v) return t.insertJoins(ctx, id, v)
} }
func (t *scenesMoviesTable) addJoins(ctx context.Context, id int, v []models.MoviesScenes) error { func (t *scenesGroupsTable) addJoins(ctx context.Context, id int, v []models.GroupsScenes) error {
// get existing foreign keys // get existing foreign keys
fks, err := t.get(ctx, id) fks, err := t.get(ctx, id)
if err != nil { if err != nil {
@@ -664,12 +664,12 @@ func (t *scenesMoviesTable) addJoins(ctx context.Context, id int, v []models.Mov
} }
// only add values that are not already present // only add values that are not already present
var filtered []models.MoviesScenes var filtered []models.GroupsScenes
for _, vv := range v { for _, vv := range v {
found := false found := false
for _, e := range fks { for _, e := range fks {
if vv.MovieID == e.MovieID { if vv.GroupID == e.GroupID {
found = true found = true
break break
} }
@@ -682,11 +682,11 @@ func (t *scenesMoviesTable) addJoins(ctx context.Context, id int, v []models.Mov
return t.insertJoins(ctx, id, filtered) return t.insertJoins(ctx, id, filtered)
} }
func (t *scenesMoviesTable) destroyJoins(ctx context.Context, id int, v []models.MoviesScenes) error { func (t *scenesGroupsTable) destroyJoins(ctx context.Context, id int, v []models.GroupsScenes) error {
for _, vv := range v { for _, vv := range v {
q := dialect.Delete(t.table.table).Where( q := dialect.Delete(t.table.table).Where(
t.idColumn.Eq(id), t.idColumn.Eq(id),
t.table.table.Col("movie_id").Eq(vv.MovieID), t.table.table.Col("movie_id").Eq(vv.GroupID),
) )
if _, err := exec(ctx, q); err != nil { if _, err := exec(ctx, q); err != nil {
@@ -697,7 +697,7 @@ func (t *scenesMoviesTable) destroyJoins(ctx context.Context, id int, v []models
return nil return nil
} }
func (t *scenesMoviesTable) modifyJoins(ctx context.Context, id int, v []models.MoviesScenes, mode models.RelationshipUpdateMode) error { func (t *scenesGroupsTable) modifyJoins(ctx context.Context, id int, v []models.GroupsScenes, mode models.RelationshipUpdateMode) error {
switch mode { switch mode {
case models.RelationshipUpdateModeSet: case models.RelationshipUpdateModeSet:
return t.replaceJoins(ctx, id, v) return t.replaceJoins(ctx, id, v)

View File

@@ -25,7 +25,7 @@ var (
scenesTagsJoinTable = goqu.T(scenesTagsTable) scenesTagsJoinTable = goqu.T(scenesTagsTable)
scenesPerformersJoinTable = goqu.T(performersScenesTable) scenesPerformersJoinTable = goqu.T(performersScenesTable)
scenesStashIDsJoinTable = goqu.T("scene_stash_ids") scenesStashIDsJoinTable = goqu.T("scene_stash_ids")
scenesMoviesJoinTable = goqu.T(moviesScenesTable) scenesGroupsJoinTable = goqu.T(groupsScenesTable)
scenesURLsJoinTable = goqu.T(scenesURLsTable) scenesURLsJoinTable = goqu.T(scenesURLsTable)
performersAliasesJoinTable = goqu.T(performersAliasesTable) performersAliasesJoinTable = goqu.T(performersAliasesTable)
@@ -37,8 +37,8 @@ var (
studiosTagsJoinTable = goqu.T(studiosTagsTable) studiosTagsJoinTable = goqu.T(studiosTagsTable)
studiosStashIDsJoinTable = goqu.T("studio_stash_ids") studiosStashIDsJoinTable = goqu.T("studio_stash_ids")
moviesURLsJoinTable = goqu.T(movieURLsTable) groupsURLsJoinTable = goqu.T(groupURLsTable)
moviesTagsJoinTable = goqu.T(moviesTagsTable) groupsTagsJoinTable = goqu.T(groupsTagsTable)
tagsAliasesJoinTable = goqu.T(tagAliasesTable) tagsAliasesJoinTable = goqu.T(tagAliasesTable)
tagRelationsJoinTable = goqu.T(tagRelationsTable) tagRelationsJoinTable = goqu.T(tagRelationsTable)
@@ -184,10 +184,10 @@ var (
}, },
} }
scenesMoviesTableMgr = &scenesMoviesTable{ scenesGroupsTableMgr = &scenesGroupsTable{
table: table{ table: table{
table: scenesMoviesJoinTable, table: scenesGroupsJoinTable,
idColumn: scenesMoviesJoinTable.Col(sceneIDColumn), idColumn: scenesGroupsJoinTable.Col(sceneIDColumn),
}, },
} }
@@ -337,25 +337,25 @@ var (
) )
var ( var (
movieTableMgr = &table{ groupTableMgr = &table{
table: goqu.T(movieTable), table: goqu.T(groupTable),
idColumn: goqu.T(movieTable).Col(idColumn), idColumn: goqu.T(groupTable).Col(idColumn),
} }
moviesURLsTableMgr = &orderedValueTable[string]{ groupsURLsTableMgr = &orderedValueTable[string]{
table: table{ table: table{
table: moviesURLsJoinTable, table: groupsURLsJoinTable,
idColumn: moviesURLsJoinTable.Col(movieIDColumn), idColumn: groupsURLsJoinTable.Col(groupIDColumn),
}, },
valueColumn: moviesURLsJoinTable.Col(movieURLColumn), valueColumn: groupsURLsJoinTable.Col(groupURLColumn),
} }
moviesTagsTableMgr = &joinTable{ groupsTagsTableMgr = &joinTable{
table: table{ table: table{
table: moviesTagsJoinTable, table: groupsTagsJoinTable,
idColumn: moviesTagsJoinTable.Col(movieIDColumn), idColumn: groupsTagsJoinTable.Col(groupIDColumn),
}, },
fkColumn: moviesTagsJoinTable.Col(tagIDColumn), fkColumn: groupsTagsJoinTable.Col(tagIDColumn),
foreignTable: tagTableMgr, foreignTable: tagTableMgr,
orderBy: tagTableMgr.table.Col("name").Asc(), orderBy: tagTableMgr.table.Col("name").Asc(),
} }

View File

@@ -424,7 +424,7 @@ func (qb *TagStore) FindByGalleryID(ctx context.Context, galleryID int) ([]*mode
return qb.queryTags(ctx, query, args) return qb.queryTags(ctx, query, args)
} }
func (qb *TagStore) FindByMovieID(ctx context.Context, movieID int) ([]*models.Tag, error) { func (qb *TagStore) FindByGroupID(ctx context.Context, movieID int) ([]*models.Tag, error) {
query := ` query := `
SELECT tags.* FROM tags SELECT tags.* FROM tags
LEFT JOIN movies_tags as movies_join on movies_join.tag_id = tags.id LEFT JOIN movies_tags as movies_join on movies_join.tag_id = tags.id
@@ -637,6 +637,7 @@ func (qb *TagStore) Query(ctx context.Context, tagFilter *models.TagFilterType,
var tagSortOptions = sortOptions{ var tagSortOptions = sortOptions{
"created_at", "created_at",
"galleries_count", "galleries_count",
"groups_count",
"id", "id",
"images_count", "images_count",
"movies_count", "movies_count",
@@ -684,7 +685,7 @@ func (qb *TagStore) getTagSort(query *queryBuilder, findFilter *models.FindFilte
case "studios_count": case "studios_count":
sortQuery += getCountSort(tagTable, studiosTagsTable, tagIDColumn, direction) sortQuery += getCountSort(tagTable, studiosTagsTable, tagIDColumn, direction)
case "movies_count", "groups_count": case "movies_count", "groups_count":
sortQuery += getCountSort(tagTable, moviesTagsTable, tagIDColumn, direction) sortQuery += getCountSort(tagTable, groupsTagsTable, tagIDColumn, direction)
default: default:
sortQuery += getSort(sort, direction, "tags") sortQuery += getSort(sort, direction, "tags")
} }

View File

@@ -190,11 +190,11 @@ func (qb *tagFilterHandler) studioCountCriterionHandler(studioCount *models.IntC
} }
} }
func (qb *tagFilterHandler) groupCountCriterionHandler(movieCount *models.IntCriterionInput) criterionHandlerFunc { func (qb *tagFilterHandler) groupCountCriterionHandler(groupCount *models.IntCriterionInput) criterionHandlerFunc {
return func(ctx context.Context, f *filterBuilder) { return func(ctx context.Context, f *filterBuilder) {
if movieCount != nil { if groupCount != nil {
f.addLeftJoin("movies_tags", "", "movies_tags.tag_id = tags.id") f.addLeftJoin("movies_tags", "", "movies_tags.tag_id = tags.id")
clause, args := getIntCriterionWhereClause("count(distinct movies_tags.movie_id)", *movieCount) clause, args := getIntCriterionWhereClause("count(distinct movies_tags.movie_id)", *groupCount)
f.addHaving(clause, args...) f.addHaving(clause, args...)
} }

View File

@@ -42,22 +42,22 @@ func TestMarkerFindBySceneMarkerID(t *testing.T) {
}) })
} }
func TestTagFindByMovieID(t *testing.T) { func TestTagFindByGroupID(t *testing.T) {
withTxn(func(ctx context.Context) error { withTxn(func(ctx context.Context) error {
tqb := db.Tag tqb := db.Tag
movieID := movieIDs[movieIdxWithTag] groupID := groupIDs[groupIdxWithTag]
tags, err := tqb.FindByMovieID(ctx, movieID) tags, err := tqb.FindByGroupID(ctx, groupID)
if err != nil { if err != nil {
t.Errorf("Error finding tags: %s", err.Error()) t.Errorf("Error finding tags: %s", err.Error())
} }
assert.Len(t, tags, 1) assert.Len(t, tags, 1)
assert.Equal(t, tagIDs[tagIdxWithMovie], tags[0].ID) assert.Equal(t, tagIDs[tagIdxWithGroup], tags[0].ID)
tags, err = tqb.FindByMovieID(ctx, 0) tags, err = tqb.FindByGroupID(ctx, 0)
if err != nil { if err != nil {
t.Errorf("Error finding tags: %s", err.Error()) t.Errorf("Error finding tags: %s", err.Error())
@@ -236,7 +236,7 @@ func TestTagQuerySort(t *testing.T) {
sortBy = "movies_count" sortBy = "movies_count"
tags = queryTags(ctx, t, sqb, nil, findFilter) tags = queryTags(ctx, t, sqb, nil, findFilter)
assert.Equal(tagIDs[tagIdx1WithMovie], tags[0].ID) assert.Equal(tagIDs[tagIdx1WithGroup], tags[0].ID)
return nil return nil
}) })

View File

@@ -132,7 +132,7 @@ func (db *Database) Repository() models.Repository {
Gallery: db.Gallery, Gallery: db.Gallery,
GalleryChapter: db.GalleryChapter, GalleryChapter: db.GalleryChapter,
Image: db.Image, Image: db.Image,
Movie: db.Movie, Group: db.Group,
Performer: db.Performer, Performer: db.Performer,
Scene: db.Scene, Scene: db.Scene,
SceneMarker: db.SceneMarker, SceneMarker: db.SceneMarker,

View File

@@ -20,7 +20,7 @@ sceneByFragment:
<single scraper config> <single scraper config>
sceneByURL: sceneByURL:
<multiple scraper URL configs> <multiple scraper URL configs>
movieByURL: groupByURL:
<multiple scraper URL configs> <multiple scraper URL configs>
galleryByFragment: galleryByFragment:
<single scraper config> <single scraper config>
@@ -42,7 +42,7 @@ The scraping types and their required fields are outlined in the following table
| Scraper in query dropdown button in Scene Edit page | Valid `sceneByName` and `sceneByQueryFragment` configurations. | | Scraper in query dropdown button in Scene Edit page | Valid `sceneByName` and `sceneByQueryFragment` configurations. |
| Scraper in `Scrape...` dropdown button in Scene Edit page | Valid `sceneByFragment` configuration. | | Scraper in `Scrape...` dropdown button in Scene Edit page | Valid `sceneByFragment` configuration. |
| Scrape scene from URL | Valid `sceneByURL` configuration with matching URL. | | Scrape scene from URL | Valid `sceneByURL` configuration with matching URL. |
| Scrape movie from URL | Valid `movieByURL` configuration with matching URL. | | Scrape group from URL | Valid `groupByURL` configuration with matching URL. **Note:** `movieByURL` is also supported but is deprecated. |
| Scraper in `Scrape...` dropdown button in Gallery Edit page | Valid `galleryByFragment` configuration. | | Scraper in `Scrape...` dropdown button in Gallery Edit page | Valid `galleryByFragment` configuration. |
| Scrape gallery from URL | Valid `galleryByURL` configuration with matching URL. | | Scrape gallery from URL | Valid `galleryByURL` configuration with matching URL. |
@@ -78,7 +78,7 @@ The script is sent input and expects output based on the scraping type, as detai
| `sceneByName` | `{"name": "<scene query string>"}` | Array of JSON-encoded scene fragments | | `sceneByName` | `{"name": "<scene query string>"}` | Array of JSON-encoded scene fragments |
| `sceneByQueryFragment`, `sceneByFragment` | JSON-encoded scene fragment | JSON-encoded scene fragment | | `sceneByQueryFragment`, `sceneByFragment` | JSON-encoded scene fragment | JSON-encoded scene fragment |
| `sceneByURL` | `{"url": "<url>"}` | JSON-encoded scene fragment | | `sceneByURL` | `{"url": "<url>"}` | JSON-encoded scene fragment |
| `movieByURL` | `{"url": "<url>"}` | JSON-encoded movie fragment | | `groupByURL` | `{"url": "<url>"}` | JSON-encoded group fragment |
| `galleryByFragment` | JSON-encoded gallery fragment | JSON-encoded gallery fragment | | `galleryByFragment` | JSON-encoded gallery fragment | JSON-encoded gallery fragment |
| `galleryByURL` | `{"url": "<url>"}` | JSON-encoded gallery fragment | | `galleryByURL` | `{"url": "<url>"}` | JSON-encoded gallery fragment |
@@ -225,7 +225,7 @@ sceneByFragment:
The above configuration would scrape from the value of `queryURL`, replacing `{filename}` with the base filename of the scene, after it has been manipulated by the regex replacements. The above configuration would scrape from the value of `queryURL`, replacing `{filename}` with the base filename of the scene, after it has been manipulated by the regex replacements.
### scrapeXPath and scrapeJson use with `<scene|performer|gallery|movie>ByURL` ### scrapeXPath and scrapeJson use with `<scene|performer|gallery|group>ByURL`
For `sceneByURL`, `performerByURL`, `galleryByURL` the `queryURL` can also be present if we want to use `queryURLReplace`. The functionality is the same as `sceneByFragment`, the only placeholder field available though is the `url`: For `sceneByURL`, `performerByURL`, `galleryByURL` the `queryURL` can also be present if we want to use `queryURLReplace`. The functionality is the same as `sceneByFragment`, the only placeholder field available though is the `url`:
* `{url}` - the url of the scene/performer/gallery * `{url}` - the url of the scene/performer/gallery
@@ -271,9 +271,9 @@ Likewise, the top-level `jsonScrapers` field contains json scraping configuratio
Collectively, these configurations are known as mapped scraping configurations. Collectively, these configurations are known as mapped scraping configurations.
A mapped scraping configuration may contain a `common` field, and must contain `performer`, `scene`, `movie` or `gallery` depending on the scraping type it is configured for. A mapped scraping configuration may contain a `common` field, and must contain `performer`, `scene`, `group` or `gallery` depending on the scraping type it is configured for.
Within the `performer`/`scene`/`movie`/`gallery` field are key/value pairs corresponding to the [golang fields](/help/ScraperDevelopment.md#object-fields) on the performer/scene object. These fields are case-sensitive. Within the `performer`/`scene`/`group`/`gallery` field are key/value pairs corresponding to the [golang fields](/help/ScraperDevelopment.md#object-fields) on the performer/scene object. These fields are case-sensitive.
The values of these may be either a simple selector value, which tells the system where to get the value of the field from, or a more advanced configuration (see below). For example, for an xpath configuration: The values of these may be either a simple selector value, which tells the system where to get the value of the field from, or a more advanced configuration (see below). For example, for an xpath configuration:
@@ -820,7 +820,7 @@ URL
Date Date
Image Image
Studio (see Studio Fields) Studio (see Studio Fields)
Movies (see Movie Fields) Groups (see Group Fields)
Tags (see Tag fields) Tags (see Tag fields)
Performers (list of Performer fields) Performers (list of Performer fields)
``` ```
@@ -835,7 +835,7 @@ URL
Name Name
``` ```
### Movie ### Group
``` ```
Name Name
Aliases Aliases