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
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
83 changed files with 1765 additions and 1646 deletions

View file

@ -51,11 +51,11 @@ models:
fieldName: DurationFinite
frame_rate:
fieldName: FrameRateFinite
# group is movie under the hood
Group:
model: github.com/stashapp/stash/pkg/models.Movie
GroupFilterType:
model: github.com/stashapp/stash/pkg/models.MovieFilterType
# movie is group under the hood
Movie:
model: github.com/stashapp/stash/pkg/models.Group
MovieFilterType:
model: github.com/stashapp/stash/pkg/models.GroupFilterType
# autobind on config causes generation issues
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) {
moviesScenes, err := models.MoviesScenesFromInput(value)
func (t changesetTranslator) relatedGroupsFromMovies(value []models.SceneMovieInput) (models.RelatedGroups, error) {
groupsScenes, err := models.GroupsScenesFromInput(value)
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) {
ret := make([]models.MoviesScenes, len(input))
func groupsScenesFromGroupInput(input []models.SceneGroupInput) ([]models.GroupsScenes, error) {
ret := make([]models.GroupsScenes, len(input))
for i, v := range input {
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)
}
ret[i] = models.MoviesScenes{
MovieID: mID,
ret[i] = models.GroupsScenes{
GroupID: mID,
SceneIndex: v.SceneIndex,
}
}
@ -373,48 +373,48 @@ func moviesScenesFromGroupInput(input []models.SceneGroupInput) ([]models.Movies
return ret, nil
}
func (t changesetTranslator) relatedMoviesFromGroups(value []models.SceneGroupInput) (models.RelatedMovies, error) {
moviesScenes, err := moviesScenesFromGroupInput(value)
func (t changesetTranslator) relatedGroups(value []models.SceneGroupInput) (models.RelatedGroups, error) {
groupsScenes, err := groupsScenesFromGroupInput(value)
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) {
return nil, nil
}
moviesScenes, err := models.MoviesScenesFromInput(value)
groupsScenes, err := models.GroupsScenesFromInput(value)
if err != nil {
return nil, err
}
return &models.UpdateMovieIDs{
Movies: moviesScenes,
return &models.UpdateGroupIDs{
Groups: groupsScenes,
Mode: models.RelationshipUpdateModeSet,
}, 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) {
return nil, nil
}
moviesScenes, err := moviesScenesFromGroupInput(value)
groupsScenes, err := groupsScenesFromGroupInput(value)
if err != nil {
return nil, err
}
return &models.UpdateMovieIDs{
Movies: moviesScenes,
return &models.UpdateGroupIDs{
Groups: groupsScenes,
Mode: models.RelationshipUpdateModeSet,
}, 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 {
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)
}
movies := make([]models.MoviesScenes, len(ids))
groups := make([]models.GroupsScenes, len(ids))
for i, id := range ids {
movies[i] = models.MoviesScenes{MovieID: id}
groups[i] = models.GroupsScenes{GroupID: id}
}
return &models.UpdateMovieIDs{
Movies: movies,
return &models.UpdateGroupIDs{
Groups: groups,
Mode: value.Mode,
}, nil
}

View file

@ -9,7 +9,7 @@ const (
performerKey key = iota + 1
sceneKey
studioKey
movieKey
groupKey
tagKey
downloadKey
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 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 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 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
@ -52,7 +52,7 @@ type Loaders struct {
PerformerByID *PerformerLoader
StudioByID *StudioLoader
TagByID *TagLoader
MovieByID *MovieLoader
GroupByID *GroupLoader
FileByID *FileLoader
}
@ -94,10 +94,10 @@ func (m Middleware) Middleware(next http.Handler) http.Handler {
maxBatch: maxBatch,
fetch: m.fetchTags(ctx),
},
MovieByID: &MovieLoader{
GroupByID: &GroupLoader{
wait: wait,
maxBatch: maxBatch,
fetch: m.fetchMovies(ctx),
fetch: m.fetchGroups(ctx),
},
FileByID: &FileLoader{
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) {
return func(keys []int) (ret []*models.Movie, errs []error) {
func (m Middleware) fetchGroups(ctx context.Context) func(keys []int) ([]*models.Group, []error) {
return func(keys []int) (ret []*models.Group, errs []error) {
err := m.Repository.WithDB(ctx, func(ctx context.Context) error {
var err error
ret, err = m.Repository.Movie.FindMany(ctx, keys)
ret, err = m.Repository.Group.FindMany(ctx, keys)
return err
})
return ret, toErrorSlice(err)

View file

@ -9,10 +9,10 @@ import (
"github.com/stashapp/stash/pkg/models"
)
// MovieLoaderConfig captures the config to create a new MovieLoader
type MovieLoaderConfig struct {
// GroupLoaderConfig captures the config to create a new GroupLoader
type GroupLoaderConfig struct {
// 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 time.Duration
@ -21,19 +21,19 @@ type MovieLoaderConfig struct {
MaxBatch int
}
// NewMovieLoader creates a new MovieLoader given a fetch, wait, and maxBatch
func NewMovieLoader(config MovieLoaderConfig) *MovieLoader {
return &MovieLoader{
// NewGroupLoader creates a new GroupLoader given a fetch, wait, and maxBatch
func NewGroupLoader(config GroupLoaderConfig) *GroupLoader {
return &GroupLoader{
fetch: config.Fetch,
wait: config.Wait,
maxBatch: config.MaxBatch,
}
}
// MovieLoader batches and caches requests
type MovieLoader struct {
// GroupLoader batches and caches requests
type GroupLoader struct {
// 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
wait time.Duration
@ -44,51 +44,51 @@ type MovieLoader struct {
// INTERNAL
// 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,
// then everything will be sent to the fetch method and out to the listeners
batch *movieLoaderBatch
batch *groupLoaderBatch
// mutex to prevent races
mu sync.Mutex
}
type movieLoaderBatch struct {
type groupLoaderBatch struct {
keys []int
data []*models.Movie
data []*models.Group
error []error
closing bool
done chan struct{}
}
// Load a Movie by key, batching and caching will be applied automatically
func (l *MovieLoader) Load(key int) (*models.Movie, error) {
// Load a Group by key, batching and caching will be applied automatically
func (l *GroupLoader) Load(key int) (*models.Group, error) {
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
// 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()
if it, ok := l.cache[key]; ok {
l.mu.Unlock()
return func() (*models.Movie, error) {
return func() (*models.Group, error) {
return it, nil
}
}
if l.batch == nil {
l.batch = &movieLoaderBatch{done: make(chan struct{})}
l.batch = &groupLoaderBatch{done: make(chan struct{})}
}
batch := l.batch
pos := batch.keyIndex(l, key)
l.mu.Unlock()
return func() (*models.Movie, error) {
return func() (*models.Group, error) {
<-batch.done
var data *models.Movie
var data *models.Group
if pos < len(batch.data) {
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
// sub batches depending on how the loader is configured
func (l *MovieLoader) LoadAll(keys []int) ([]*models.Movie, []error) {
results := make([]func() (*models.Movie, error), len(keys))
func (l *GroupLoader) LoadAll(keys []int) ([]*models.Group, []error) {
results := make([]func() (*models.Group, error), len(keys))
for i, key := range keys {
results[i] = l.LoadThunk(key)
}
movies := make([]*models.Movie, len(keys))
groups := make([]*models.Group, len(keys))
errors := make([]error, len(keys))
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
// different data loaders without blocking until the thunk is called.
func (l *MovieLoader) LoadAllThunk(keys []int) func() ([]*models.Movie, []error) {
results := make([]func() (*models.Movie, error), len(keys))
func (l *GroupLoader) LoadAllThunk(keys []int) func() ([]*models.Group, []error) {
results := make([]func() (*models.Group, error), len(keys))
for i, key := range keys {
results[i] = l.LoadThunk(key)
}
return func() ([]*models.Movie, []error) {
movies := make([]*models.Movie, len(keys))
return func() ([]*models.Group, []error) {
groups := make([]*models.Group, len(keys))
errors := make([]error, len(keys))
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
// and false is returned.
// (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()
var found bool
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
func (l *MovieLoader) Clear(key int) {
func (l *GroupLoader) Clear(key int) {
l.mu.Lock()
delete(l.cache, key)
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 {
l.cache = map[int]*models.Movie{}
l.cache = map[int]*models.Group{}
}
l.cache[key] = value
}
// keyIndex will return the location of the key in the batch, if its not found
// 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 {
if key == existingKey {
return i
@ -202,7 +202,7 @@ func (b *movieLoaderBatch) keyIndex(l *MovieLoader, key int) int {
return pos
}
func (b *movieLoaderBatch) startTimer(l *MovieLoader) {
func (b *groupLoaderBatch) startTimer(l *GroupLoader) {
time.Sleep(l.wait)
l.mu.Lock()
@ -218,7 +218,7 @@ func (b *movieLoaderBatch) startTimer(l *MovieLoader) {
b.end(l)
}
func (b *movieLoaderBatch) end(l *MovieLoader) {
func (b *groupLoaderBatch) end(l *GroupLoader) {
b.data, b.error = l.fetch(b.keys)
close(b.done)
}

View file

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

View file

@ -8,7 +8,7 @@ import (
"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 {
result := obj.Date.String()
return &result, nil
@ -16,14 +16,14 @@ func (r *movieResolver) Date(ctx context.Context, obj *models.Movie) (*string, e
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
}
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 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 {
return nil, err
}
@ -37,10 +37,10 @@ func (r *movieResolver) URL(ctx context.Context, obj *models.Movie) (*string, er
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 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 {
return nil, err
}
@ -49,7 +49,7 @@ func (r *movieResolver) Urls(ctx context.Context, obj *models.Movie) ([]string,
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 {
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)
}
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 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 {
return nil, err
}
@ -71,26 +71,26 @@ func (r movieResolver) Tags(ctx context.Context, obj *models.Movie) (ret []*mode
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
if err := r.withReadTxn(ctx, func(ctx context.Context) error {
var err error
hasImage, err = r.repository.Movie.HasFrontImage(ctx, obj.ID)
hasImage, err = r.repository.Group.HasFrontImage(ctx, obj.ID)
return err
}); err != nil {
return nil, err
}
baseURL, _ := ctx.Value(BaseURLCtxKey).(string)
imagePath := urlbuilders.NewMovieURLBuilder(baseURL, obj).GetMovieFrontImageURL(hasImage)
imagePath := urlbuilders.NewGroupURLBuilder(baseURL, obj).GetGroupFrontImageURL(hasImage)
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
if err := r.withReadTxn(ctx, func(ctx context.Context) error {
var err error
hasImage, err = r.repository.Movie.HasBackImage(ctx, obj.ID)
hasImage, err = r.repository.Group.HasBackImage(ctx, obj.ID)
return err
}); err != nil {
return nil, err
@ -102,13 +102,13 @@ func (r *movieResolver) BackImagePath(ctx context.Context, obj *models.Movie) (*
}
baseURL, _ := ctx.Value(BaseURLCtxKey).(string)
imagePath := urlbuilders.NewMovieURLBuilder(baseURL, obj).GetMovieBackImageURL()
imagePath := urlbuilders.NewGroupURLBuilder(baseURL, obj).GetGroupBackImageURL()
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 {
ret, err = r.repository.Scene.CountByMovieID(ctx, obj.ID)
ret, err = r.repository.Scene.CountByGroupID(ctx, obj.ID)
return err
}); err != nil {
return 0, err
@ -117,10 +117,10 @@ func (r *movieResolver) SceneCount(ctx context.Context, obj *models.Movie) (ret
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 {
var err error
ret, err = r.repository.Scene.FindByMovieID(ctx, obj.ID)
ret, err = r.repository.Scene.FindByGroupID(ctx, obj.ID)
return err
}); err != nil {
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) {
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
}); err != nil {
return 0, err
@ -257,9 +257,9 @@ func (r *performerResolver) DeathDate(ctx context.Context, obj *models.Performer
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 {
ret, err = r.repository.Movie.FindByPerformerID(ctx, obj.ID)
ret, err = r.repository.Group.FindByPerformerID(ctx, obj.ID)
return err
}); err != nil {
return nil, err
@ -269,6 +269,6 @@ func (r *performerResolver) Groups(ctx context.Context, obj *models.Performer) (
}
// 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)
}

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) {
if !obj.Movies.Loaded() {
if !obj.Groups.Loaded() {
if err := r.withReadTxn(ctx, func(ctx context.Context) error {
qb := r.repository.Scene
return obj.LoadMovies(ctx, qb)
return obj.LoadGroups(ctx, qb)
}); err != nil {
return nil, err
}
}
loader := loaders.From(ctx).MovieByID
loader := loaders.From(ctx).GroupByID
for _, sm := range obj.Movies.List() {
movie, err := loader.Load(sm.MovieID)
for _, sm := range obj.Groups.List() {
movie, err := loader.Load(sm.GroupID)
if err != nil {
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) {
if !obj.Movies.Loaded() {
if !obj.Groups.Loaded() {
if err := r.withReadTxn(ctx, func(ctx context.Context) error {
qb := r.repository.Scene
return obj.LoadMovies(ctx, qb)
return obj.LoadGroups(ctx, qb)
}); err != nil {
return nil, err
}
}
loader := loaders.From(ctx).MovieByID
loader := loaders.From(ctx).GroupByID
for _, sm := range obj.Movies.List() {
movie, err := loader.Load(sm.MovieID)
for _, sm := range obj.Groups.List() {
group, err := loader.Load(sm.GroupID)
if err != nil {
return nil, err
}
sceneIdx := sm.SceneIndex
sceneGroup := &SceneGroup{
Group: movie,
Group: group,
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) {
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
}); err != nil {
return 0, err
@ -149,9 +149,9 @@ func (r *studioResolver) Rating100(ctx context.Context, obj *models.Studio) (*in
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 {
ret, err = r.repository.Movie.FindByStudioID(ctx, obj.ID)
ret, err = r.repository.Group.FindByStudioID(ctx, obj.ID)
return err
}); err != nil {
return nil, err
@ -161,6 +161,6 @@ func (r *studioResolver) Groups(ctx context.Context, obj *models.Studio) (ret []
}
// 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)
}

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) {
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
}); err != nil {
return 0, err

View file

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

View file

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

View file

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

View file

@ -8,14 +8,14 @@ import (
"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)
if err != nil {
return nil, err
}
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
}); err != nil {
return nil, err
@ -24,22 +24,22 @@ func (r *queryResolver) FindGroup(ctx context.Context, id string) (ret *models.M
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)
if err != nil {
return nil, err
}
if err := r.withReadTxn(ctx, func(ctx context.Context) error {
var movies []*models.Movie
var groups []*models.Group
var err error
var total int
if len(idInts) > 0 {
movies, err = r.repository.Movie.FindMany(ctx, idInts)
total = len(movies)
groups, err = r.repository.Group.FindMany(ctx, idInts)
total = len(groups)
} else {
movies, total, err = r.repository.Movie.Query(ctx, movieFilter, filter)
groups, total, err = r.repository.Group.Query(ctx, groupFilter, filter)
}
if err != nil {
@ -48,7 +48,7 @@ func (r *queryResolver) FindGroups(ctx context.Context, movieFilter *models.Movi
ret = &FindGroupsResultType{
Count: total,
Groups: movies,
Groups: groups,
}
return nil
}); err != nil {

View file

@ -8,14 +8,14 @@ import (
"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)
if err != nil {
return nil, err
}
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
}); err != nil {
return nil, err
@ -24,22 +24,22 @@ func (r *queryResolver) FindMovie(ctx context.Context, id string) (ret *models.M
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)
if err != nil {
return nil, err
}
if err := r.withReadTxn(ctx, func(ctx context.Context) error {
var movies []*models.Movie
var groups []*models.Group
var err error
var total int
if len(idInts) > 0 {
movies, err = r.repository.Movie.FindMany(ctx, idInts)
total = len(movies)
groups, err = r.repository.Group.FindMany(ctx, idInts)
total = len(groups)
} else {
movies, total, err = r.repository.Movie.Query(ctx, movieFilter, filter)
groups, total, err = r.repository.Group.Query(ctx, movieFilter, filter)
}
if err != nil {
@ -48,7 +48,7 @@ func (r *queryResolver) FindMovies(ctx context.Context, movieFilter *models.Movi
ret = &FindMoviesResultType{
Count: total,
Movies: movies,
Movies: groups,
}
return nil
}); err != nil {
@ -58,9 +58,9 @@ func (r *queryResolver) FindMovies(ctx context.Context, movieFilter *models.Movi
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 {
ret, err = r.repository.Movie.All(ctx)
ret, err = r.repository.Group.All(ctx)
return err
}); err != nil {
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
func filterMovieTags(p []*models.ScrapedMovie) {
// filterGroupTags removes tags matching excluded tag patterns from the provided scraped movies
func filterGroupTags(p []*models.ScrapedMovie) {
excludeRegexps := compileRegexps(manager.GetInstance().Config.GetScraperExcludeTagPatterns())
var ignoredTags []string
@ -208,7 +208,7 @@ func (r *queryResolver) ScrapeMovieURL(ctx context.Context, url string) (*models
return nil, err
}
filterMovieTags([]*models.ScrapedMovie{ret})
filterGroupTags([]*models.ScrapedMovie{ret})
return ret, nil
}
@ -224,7 +224,7 @@ func (r *queryResolver) ScrapeGroupURL(ctx context.Context, url string) (*models
return nil, err
}
filterMovieTags([]*models.ScrapedMovie{ret})
filterGroupTags([]*models.ScrapedMovie{ret})
// convert to scraped group
group := &models.ScrapedGroup{

View file

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

View file

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

View file

@ -1,32 +1,33 @@
package urlbuilders
import (
"github.com/stashapp/stash/pkg/models"
"strconv"
"github.com/stashapp/stash/pkg/models"
)
type MovieURLBuilder struct {
type GroupURLBuilder struct {
BaseURL string
MovieID string
GroupID string
UpdatedAt string
}
func NewMovieURLBuilder(baseURL string, movie *models.Movie) MovieURLBuilder {
return MovieURLBuilder{
func NewGroupURLBuilder(baseURL string, group *models.Group) GroupURLBuilder {
return GroupURLBuilder{
BaseURL: baseURL,
MovieID: strconv.Itoa(movie.ID),
UpdatedAt: strconv.FormatInt(movie.UpdatedAt.Unix(), 10),
GroupID: strconv.Itoa(group.ID),
UpdatedAt: strconv.FormatInt(group.UpdatedAt.Unix(), 10),
}
}
func (b MovieURLBuilder) GetMovieFrontImageURL(hasImage bool) string {
url := b.BaseURL + "/movie/" + b.MovieID + "/frontimage?t=" + b.UpdatedAt
func (b GroupURLBuilder) GetGroupFrontImageURL(hasImage bool) string {
url := b.BaseURL + "/group/" + b.GroupID + "/frontimage?t=" + b.UpdatedAt
if !hasImage {
url += "&default=true"
}
return url
}
func (b MovieURLBuilder) GetMovieBackImageURL() string {
return b.BaseURL + "/movie/" + b.MovieID + "/backimage?t=" + b.UpdatedAt
func (b GroupURLBuilder) GetGroupBackImageURL() string {
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)
}
// Movies
if obj.Path == "movies" {
objs = me.getMovies()
// Groups - deprecated
if obj.Path == "groups" {
objs = me.getGroups()
}
if strings.HasPrefix(obj.Path, "movies/") {
objs = me.getMovieScenes(childPath(paths), host)
if strings.HasPrefix(obj.Path, "groups/") {
objs = me.getGroupScenes(childPath(paths), host)
}
// Rating
@ -433,7 +433,7 @@ func getRootObjects() []interface{} {
objs = append(objs, makeStorageFolder("performers", "performers", rootID))
objs = append(objs, makeStorageFolder("tags", "tags", 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))
return objs
@ -658,18 +658,18 @@ func (me *contentDirectoryService) getPerformerScenes(paths []string, host strin
return me.getVideos(sceneFilter, parentID, host)
}
func (me *contentDirectoryService) getMovies() []interface{} {
func (me *contentDirectoryService) getGroups() []interface{} {
var objs []interface{}
r := me.repository
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 {
return err
}
for _, s := range movies {
objs = append(objs, makeStorageFolder("movies/"+strconv.Itoa(s.ID), s.Name, "movies"))
for _, s := range groups {
objs = append(objs, makeStorageFolder("groups/"+strconv.Itoa(s.ID), s.Name, "groups"))
}
return nil
@ -680,15 +680,15 @@ func (me *contentDirectoryService) getMovies() []interface{} {
return objs
}
func (me *contentDirectoryService) getMovieScenes(paths []string, host string) []interface{} {
func (me *contentDirectoryService) getGroupScenes(paths []string, host string) []interface{} {
sceneFilter := &models.SceneFilterType{
Movies: &models.MultiCriterionInput{
Groups: &models.MultiCriterionInput{
Modifier: models.CriterionModifierIncludes,
Value: []string{paths[0]},
},
}
parentID := "movies/" + strings.Join(paths, "/")
parentID := "groups/" + strings.Join(paths, "/")
page := getPageFromID(paths)
if page != nil {

View file

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

View file

@ -22,7 +22,7 @@ type Repository struct {
StudioFinder StudioFinder
TagFinder TagFinder
PerformerFinder PerformerFinder
MovieFinder MovieFinder
GroupFinder GroupFinder
}
func NewRepository(repo models.Repository) Repository {
@ -33,7 +33,7 @@ func NewRepository(repo models.Repository) Repository {
StudioFinder: repo.Studio,
TagFinder: repo.Tag,
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)
}
func (jp *jsonUtils) saveMovie(fn string, movie *jsonschema.Movie) error {
return jsonschema.SaveMovieFile(filepath.Join(jp.json.Movies, fn), movie)
func (jp *jsonUtils) saveGroup(fn string, group *jsonschema.Group) error {
return jsonschema.SaveGroupFile(filepath.Join(jp.json.Groups, fn), group)
}
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) {
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
startTime := time.Now()
@ -156,11 +156,11 @@ func (t *ExportTask) Start(ctx context.Context, wg *sync.WaitGroup) {
paths.EnsureJSONDirs(t.baseDir)
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 {
// only include movie scenes if includeDependencies is also set
// only include group scenes if includeDependencies is also set
if !t.scenes.all && t.includeDependencies {
t.populateMovieScenes(ctx)
t.populateGroupScenes(ctx)
}
// always export gallery images
@ -172,7 +172,7 @@ func (t *ExportTask) Start(ctx context.Context, wg *sync.WaitGroup) {
t.ExportScenes(ctx, workerCount)
t.ExportImages(ctx, workerCount)
t.ExportGalleries(ctx, workerCount)
t.ExportMovies(ctx, workerCount)
t.ExportGroups(ctx, workerCount)
t.ExportPerformers(ctx, workerCount)
t.ExportStudios(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.Performers, t.zipWalkFunc(u.json.Performers, 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.Images, t.zipWalkFunc(u.json.Images, z))
@ -282,28 +282,28 @@ func (t *ExportTask) zipFile(fn, outDir string, z *zip.Writer) error {
return nil
}
func (t *ExportTask) populateMovieScenes(ctx context.Context) {
func (t *ExportTask) populateGroupScenes(ctx context.Context) {
r := t.repository
reader := r.Movie
reader := r.Group
sceneReader := r.Scene
var movies []*models.Movie
var groups []*models.Group
var err error
all := t.full || (t.groups != nil && t.groups.all)
if all {
movies, err = reader.All(ctx)
groups, err = reader.All(ctx)
} 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 {
logger.Errorf("[movies] failed to fetch movies: %v", err)
logger.Errorf("[groups] failed to fetch groups: %v", err)
}
for _, m := range movies {
scenes, err := sceneReader.FindByMovieID(ctx, m.ID)
for _, m := range groups {
scenes, err := sceneReader.FindByGroupID(ctx, m.ID)
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
}
@ -488,7 +488,7 @@ func (t *ExportTask) exportScene(ctx context.Context, wg *sync.WaitGroup, jobCha
r := t.repository
sceneReader := r.Scene
studioReader := r.Studio
movieReader := r.Movie
groupReader := r.Group
galleryReader := r.Gallery
performerReader := r.Performer
tagReader := r.Tag
@ -556,9 +556,9 @@ func (t *ExportTask) exportScene(ctx context.Context, wg *sync.WaitGroup, jobCha
continue
}
newSceneJSON.Movies, err = scene.GetSceneMoviesJSON(ctx, movieReader, s)
newSceneJSON.Groups, err = scene.GetSceneGroupsJSON(ctx, groupReader, s)
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
}
@ -576,12 +576,12 @@ func (t *ExportTask) exportScene(ctx context.Context, wg *sync.WaitGroup, jobCha
}
t.tags.IDs = sliceutil.AppendUniques(t.tags.IDs, tagIDs)
movieIDs, err := scene.GetDependentMovieIDs(ctx, s)
groupIDs, err := scene.GetDependentGroupIDs(ctx, s)
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
}
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))
}
@ -1081,74 +1081,74 @@ func (t *ExportTask) exportTag(ctx context.Context, wg *sync.WaitGroup, jobChan
}
}
func (t *ExportTask) ExportMovies(ctx context.Context, workers int) {
var moviesWg sync.WaitGroup
func (t *ExportTask) ExportGroups(ctx context.Context, workers int) {
var groupsWg sync.WaitGroup
reader := t.repository.Movie
var movies []*models.Movie
reader := t.repository.Group
var groups []*models.Group
var err error
all := t.full || (t.groups != nil && t.groups.all)
if all {
movies, err = reader.All(ctx)
groups, err = reader.All(ctx)
} 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 {
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()
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
moviesWg.Add(1)
go t.exportMovie(ctx, &moviesWg, jobCh)
groupsWg.Add(1)
go t.exportGroup(ctx, &groupsWg, jobCh)
}
for i, movie := range movies {
for i, group := range groups {
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)
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()
r := t.repository
movieReader := r.Movie
groupReader := r.Group
studioReader := r.Studio
tagReader := r.Tag
for m := range jobChan {
if err := m.LoadURLs(ctx, r.Movie); err != nil {
logger.Errorf("[movies] <%s> error getting movie urls: %v", m.Name, err)
if err := m.LoadURLs(ctx, r.Group); err != nil {
logger.Errorf("[groups] <%s> error getting group urls: %v", m.Name, err)
continue
}
newMovieJSON, err := movie.ToJSON(ctx, movieReader, studioReader, m)
newGroupJSON, err := movie.ToJSON(ctx, groupReader, studioReader, m)
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
}
tags, err := tagReader.FindByMovieID(ctx, m.ID)
tags, err := tagReader.FindByGroupID(ctx, m.ID)
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
}
newMovieJSON.Tags = tag.GetNames(tags)
newGroupJSON.Tags = tag.GetNames(tags)
if t.includeDependencies {
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 {
logger.Errorf("[movies] <%s> failed to save json: %v", m.Name, err)
if err := t.json.saveGroup(fn, newGroupJSON); err != nil {
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.ImportPerformers(ctx)
t.ImportStudios(ctx)
t.ImportMovies(ctx)
t.ImportGroups(ctx)
t.ImportFiles(ctx)
t.ImportGalleries(ctx)
@ -325,14 +325,14 @@ func (t *ImportTask) importStudio(ctx context.Context, studioJSON *jsonschema.St
return nil
}
func (t *ImportTask) ImportMovies(ctx context.Context) {
logger.Info("[movies] importing")
func (t *ImportTask) ImportGroups(ctx context.Context) {
logger.Info("[groups] importing")
path := t.json.json.Movies
path := t.json.json.Groups
files, err := os.ReadDir(path)
if err != nil {
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
@ -342,31 +342,31 @@ func (t *ImportTask) ImportMovies(ctx context.Context) {
for i, fi := range files {
index := i + 1
movieJSON, err := jsonschema.LoadMovieFile(filepath.Join(path, fi.Name()))
groupJSON, err := jsonschema.LoadGroupFile(filepath.Join(path, fi.Name()))
if err != nil {
logger.Errorf("[movies] failed to read json: %v", err)
logger.Errorf("[groups] failed to read json: %v", err)
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 {
movieImporter := &movie.Importer{
ReaderWriter: r.Movie,
groupImporter := &movie.Importer{
ReaderWriter: r.Group,
StudioWriter: r.Studio,
TagWriter: r.Tag,
Input: *movieJSON,
Input: *groupJSON,
MissingRefBehaviour: t.MissingRefBehaviour,
}
return performImport(ctx, movieImporter, t.DuplicateBehaviour)
return performImport(ctx, groupImporter, t.DuplicateBehaviour)
}); err != nil {
logger.Errorf("[movies] <%s> import failed: %v", fi.Name(), err)
logger.Errorf("[groups] <%s> import failed: %v", fi.Name(), err)
continue
}
}
logger.Info("[movies] import complete")
logger.Info("[groups] import complete")
}
func (t *ImportTask) ImportFiles(ctx context.Context) {
@ -648,7 +648,7 @@ func (t *ImportTask) ImportScenes(ctx context.Context) {
MissingRefBehaviour: t.MissingRefBehaviour,
GalleryFinder: r.Gallery,
MovieWriter: r.Movie,
GroupWriter: r.Group,
PerformerWriter: r.Performer,
StudioWriter: r.Studio,
TagWriter: r.Tag,

View file

@ -26,8 +26,8 @@ const (
Studio = "studio"
DefaultStudioImage = "studio/studio.svg"
Movie = "movie"
DefaultMovieImage = "movie/movie.png"
Group = "movie"
DefaultGroupImage = "movie/movie.png"
)
// 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)
}
type MovieNamesFinder interface {
FindByNames(ctx context.Context, names []string, nocase bool) ([]*models.Movie, error)
type GroupNamesFinder interface {
FindByNames(ctx context.Context, names []string, nocase bool) ([]*models.Group, error)
}
// ScrapedPerformer matches the provided performer with the
@ -118,27 +118,27 @@ func ScrapedStudio(ctx context.Context, qb StudioFinder, s *models.ScrapedStudio
return nil
}
// ScrapedMovie matches the provided movie with the movies
// in the database and sets the ID field if one is found.
func ScrapedMovie(ctx context.Context, qb MovieNamesFinder, m *models.ScrapedMovie) error {
if m.StoredID != nil || m.Name == nil {
return nil
// ScrapedGroup matches the provided movie with the movies
// in the database and returns the ID field if one is found.
func ScrapedGroup(ctx context.Context, qb GroupNamesFinder, storedID *string, name *string) (matchedID *string, err error) {
if storedID != nil || name == nil {
return
}
movies, err := qb.FindByNames(ctx, []string{*m.Name}, true)
movies, err := qb.FindByNames(ctx, []string{*name}, true)
if err != nil {
return err
return
}
if len(movies) != 1 {
// ignore - cannot match
return nil
return
}
id := strconv.Itoa(movies[0].ID)
m.StoredID = &id
return nil
matchedID = &id
return
}
// ScrapedTag matches the provided tag with the tags

View file

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

View file

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

View file

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

View file

@ -111,29 +111,6 @@ func (_m *SavedFilterReaderWriter) FindByMode(ctx context.Context, mode models.F
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
func (_m *SavedFilterReaderWriter) FindMany(ctx context.Context, ids []int, ignoreNotFound bool) ([]*models.SavedFilter, error) {
ret := _m.Called(ctx, ids, ignoreNotFound)
@ -157,20 +134,6 @@ func (_m *SavedFilterReaderWriter) FindMany(ctx context.Context, ids []int, igno
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
func (_m *SavedFilterReaderWriter) Update(ctx context.Context, obj *models.SavedFilter) error {
ret := _m.Called(ctx, obj)

View file

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

View file

@ -243,6 +243,29 @@ func (_m *TagReaderWriter) FindByGalleryID(ctx context.Context, galleryID int) (
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
func (_m *TagReaderWriter) FindByImageID(ctx context.Context, imageID int) ([]*models.Tag, error) {
ret := _m.Called(ctx, imageID)
@ -266,29 +289,6 @@ func (_m *TagReaderWriter) FindByImageID(ctx context.Context, imageID int) ([]*m
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
func (_m *TagReaderWriter) FindByName(ctx context.Context, name string, nocase bool) (*models.Tag, error) {
ret := _m.Called(ctx, name, nocase)

View file

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

View file

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

View file

@ -5,7 +5,7 @@ import (
"time"
)
type Movie struct {
type Group struct {
ID int `json:"id"`
Name string `json:"name"`
Aliases string `json:"aliases"`
@ -23,27 +23,27 @@ type Movie struct {
TagIDs RelatedIDs `json:"tag_ids"`
}
func NewMovie() Movie {
func NewGroup() Group {
currentTime := time.Now()
return Movie{
return Group{
CreatedAt: 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 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 l.GetTagIDs(ctx, m.ID)
})
}
type MoviePartial struct {
type GroupPartial struct {
Name OptionalString
Aliases OptionalString
Duration OptionalInt
@ -59,9 +59,9 @@ type MoviePartial struct {
UpdatedAt OptionalTime
}
func NewMoviePartial() MoviePartial {
func NewGroupPartial() GroupPartial {
currentTime := time.Now()
return MoviePartial{
return GroupPartial{
UpdatedAt: NewOptionalTime(currentTime),
}
}

View file

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

View file

@ -415,6 +415,30 @@ type ScrapedMovie struct {
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
type ScrapedGroup struct {
StoredID *string `json:"stored_id"`
@ -435,3 +459,27 @@ type ScrapedGroup struct {
}
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
type MovieFilterType struct {
OperatorFilter[MovieFilterType]
type GroupFilterType struct {
OperatorFilter[GroupFilterType]
Name *StringCriterionInput `json:"name"`
Director *StringCriterionInput `json:"director"`
Synopsis *StringCriterionInput `json:"synopsis"`

View file

@ -18,7 +18,7 @@ type JSONPaths struct {
Galleries string
Studios string
Tags string
Movies string
Groups string
Files string
}
@ -31,7 +31,7 @@ func newJSONPaths(baseDir string) *JSONPaths {
jp.Images = filepath.Join(baseDir, "images")
jp.Galleries = filepath.Join(baseDir, "galleries")
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.Files = filepath.Join(baseDir, "files")
return &jp
@ -49,7 +49,7 @@ func EmptyJSONDirs(baseDir string) {
_ = fsutil.EmptyDir(jsonPaths.Galleries)
_ = fsutil.EmptyDir(jsonPaths.Performers)
_ = fsutil.EmptyDir(jsonPaths.Studios)
_ = fsutil.EmptyDir(jsonPaths.Movies)
_ = fsutil.EmptyDir(jsonPaths.Groups)
_ = fsutil.EmptyDir(jsonPaths.Tags)
_ = fsutil.EmptyDir(jsonPaths.Files)
}
@ -74,8 +74,8 @@ func EnsureJSONDirs(baseDir string) {
if err := fsutil.EnsureDir(jsonPaths.Studios); err != nil {
logger.Warnf("couldn't create directories for Studios: %v", err)
}
if err := fsutil.EnsureDir(jsonPaths.Movies); err != nil {
logger.Warnf("couldn't create directories for Movies: %v", err)
if err := fsutil.EnsureDir(jsonPaths.Groups); err != nil {
logger.Warnf("couldn't create directories for Groups: %v", err)
}
if err := fsutil.EnsureDir(jsonPaths.Tags); err != nil {
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)
}
type SceneMovieLoader interface {
GetMovies(ctx context.Context, id int) ([]MoviesScenes, error)
type SceneGroupLoader interface {
GetGroups(ctx context.Context, id int) ([]GroupsScenes, error)
}
type StashIDLoader interface {
@ -115,50 +115,50 @@ func (r *RelatedIDs) load(fn func() ([]int, error)) error {
return nil
}
// RelatedMovies represents a list of related Movies.
type RelatedMovies struct {
list []MoviesScenes
// RelatedGroups represents a list of related Groups.
type RelatedGroups struct {
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.
func NewRelatedMovies(list []MoviesScenes) RelatedMovies {
return RelatedMovies{
func NewRelatedGroups(list []GroupsScenes) RelatedGroups {
return RelatedGroups{
list: list,
}
}
// Loaded returns true if the relationship has been loaded.
func (r RelatedMovies) Loaded() bool {
func (r RelatedGroups) Loaded() bool {
return r.list != nil
}
func (r RelatedMovies) mustLoaded() {
func (r RelatedGroups) mustLoaded() {
if !r.Loaded() {
panic("list has not been loaded")
}
}
// List returns the related Movies. Panics if the relationship has not been loaded.
func (r RelatedMovies) List() []MoviesScenes {
// List returns the related Groups. Panics if the relationship has not been loaded.
func (r RelatedGroups) List() []GroupsScenes {
r.mustLoaded()
return r.list
}
// 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.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.
func (r *RelatedMovies) ForID(id int) *MoviesScenes {
// ForID returns the GroupsScenes object for the given group ID. Returns nil if not found.
func (r *RelatedGroups) ForID(id int) *GroupsScenes {
r.mustLoaded()
for _, v := range r.list {
if v.MovieID == id {
if v.GroupID == id {
return &v
}
}
@ -166,7 +166,7 @@ func (r *RelatedMovies) ForID(id int) *MoviesScenes {
return nil
}
func (r *RelatedMovies) load(fn func() ([]MoviesScenes, error)) error {
func (r *RelatedGroups) load(fn func() ([]GroupsScenes, error)) error {
if r.Loaded() {
return nil
}
@ -177,7 +177,7 @@ func (r *RelatedMovies) load(fn func() ([]MoviesScenes, error)) error {
}
if ids == nil {
ids = []MoviesScenes{}
ids = []GroupsScenes{}
}
r.list = ids

View file

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

View file

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

View file

@ -23,7 +23,7 @@ type SceneFinder interface {
FindByPrimaryFileID(ctx context.Context, fileID FileID) ([]*Scene, error)
FindByPerformerID(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)
}
@ -37,7 +37,7 @@ type SceneQueryer interface {
type SceneCounter interface {
Count(ctx context.Context) (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)
CountByStudioID(ctx context.Context, studioID int) (int, error)
CountByTagID(ctx context.Context, tagID int) (int, error)
@ -99,7 +99,7 @@ type SceneReader interface {
GalleryIDLoader
PerformerIDLoader
TagIDLoader
SceneMovieLoader
SceneGroupLoader
StashIDLoader
VideoFileLoader

View file

@ -20,7 +20,7 @@ type TagFinder interface {
FindByImageID(ctx context.Context, imageID int) ([]*Tag, error)
FindByGalleryID(ctx context.Context, galleryID 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)
FindByStudioID(ctx context.Context, studioID int) ([]*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
TagsFilter *TagFilterType `json:"tags_filter"`
// 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
MoviesFilter *MovieFilterType `json:"movies_filter"`
MoviesFilter *GroupFilterType `json:"movies_filter"`
// Filter by related markers that meet this criteria
MarkersFilter *SceneMarkerFilterType `json:"markers_filter"`
// Filter by created at

View file

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

View file

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

View file

@ -12,24 +12,24 @@ import (
)
type ImporterReaderWriter interface {
models.MovieCreatorUpdater
FindByName(ctx context.Context, name string, nocase bool) (*models.Movie, error)
models.GroupCreatorUpdater
FindByName(ctx context.Context, name string, nocase bool) (*models.Group, error)
}
type Importer struct {
ReaderWriter ImporterReaderWriter
StudioWriter models.StudioFinderCreator
TagWriter models.TagFinderCreator
Input jsonschema.Movie
Input jsonschema.Group
MissingRefBehaviour models.ImportMissingRefEnum
movie models.Movie
group models.Group
frontImageData []byte
backImageData []byte
}
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 {
return err
@ -65,7 +65,7 @@ func (i *Importer) populateTags(ctx context.Context) error {
}
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
}
func (i *Importer) movieJSONToMovie(movieJSON jsonschema.Movie) models.Movie {
newMovie := models.Movie{
Name: movieJSON.Name,
Aliases: movieJSON.Aliases,
Director: movieJSON.Director,
Synopsis: movieJSON.Synopsis,
CreatedAt: movieJSON.CreatedAt.GetTime(),
UpdatedAt: movieJSON.UpdatedAt.GetTime(),
func (i *Importer) groupJSONToGroup(groupJSON jsonschema.Group) models.Group {
newGroup := models.Group{
Name: groupJSON.Name,
Aliases: groupJSON.Aliases,
Director: groupJSON.Director,
Synopsis: groupJSON.Synopsis,
CreatedAt: groupJSON.CreatedAt.GetTime(),
UpdatedAt: groupJSON.UpdatedAt.GetTime(),
TagIDs: models.NewRelatedIDs([]int{}),
}
if len(movieJSON.URLs) > 0 {
newMovie.URLs = models.NewRelatedStrings(movieJSON.URLs)
} else if movieJSON.URL != "" {
newMovie.URLs = models.NewRelatedStrings([]string{movieJSON.URL})
if len(groupJSON.URLs) > 0 {
newGroup.URLs = models.NewRelatedStrings(groupJSON.URLs)
} else if groupJSON.URL != "" {
newGroup.URLs = models.NewRelatedStrings([]string{groupJSON.URL})
}
if movieJSON.Date != "" {
d, err := models.ParseDate(movieJSON.Date)
if groupJSON.Date != "" {
d, err := models.ParseDate(groupJSON.Date)
if err == nil {
newMovie.Date = &d
newGroup.Date = &d
}
}
if movieJSON.Rating != 0 {
newMovie.Rating = &movieJSON.Rating
if groupJSON.Rating != 0 {
newGroup.Rating = &groupJSON.Rating
}
if movieJSON.Duration != 0 {
newMovie.Duration = &movieJSON.Duration
if groupJSON.Duration != 0 {
newGroup.Duration = &groupJSON.Duration
}
return newMovie
return newGroup
}
func (i *Importer) populateStudio(ctx context.Context) error {
@ -167,7 +167,7 @@ func (i *Importer) populateStudio(ctx context.Context) error {
if studio == nil {
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 {
@ -179,10 +179,10 @@ func (i *Importer) populateStudio(ctx context.Context) error {
if err != nil {
return err
}
i.movie.StudioID = &studioID
i.group.StudioID = &studioID
}
} 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 {
if len(i.frontImageData) > 0 {
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 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) {
err := i.ReaderWriter.Create(ctx, &i.movie)
err := i.ReaderWriter.Create(ctx, &i.group)
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
}
func (i *Importer) Update(ctx context.Context, id int) error {
movie := i.movie
movie.ID = id
err := i.ReaderWriter.Update(ctx, &movie)
group := i.group
group.ID = id
err := i.ReaderWriter.Update(ctx, &group)
if err != nil {
return fmt.Errorf("error updating existing movie: %v", err)
return fmt.Errorf("error updating existing group: %v", err)
}
return nil

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -45,8 +45,9 @@ type config struct {
// Configuration for querying a gallery by a URL
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"`
GroupByURL []*scrapeByURLConfig `yaml:"groupByURL"`
// Scraper debugging options
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 {
return err
}
@ -289,17 +294,17 @@ func (c config) spec() Scraper {
ret.Gallery = &gallery
}
movie := ScraperSpec{}
if len(c.MovieByURL) > 0 {
movie.SupportedScrapes = append(movie.SupportedScrapes, ScrapeTypeURL)
for _, v := range c.MovieByURL {
movie.Urls = append(movie.Urls, v.URL...)
group := ScraperSpec{}
if len(c.MovieByURL) > 0 || len(c.GroupByURL) > 0 {
group.SupportedScrapes = append(group.SupportedScrapes, ScrapeTypeURL)
for _, v := range append(c.MovieByURL, c.GroupByURL...) {
group.Urls = append(group.Urls, v.URL...)
}
}
if len(movie.SupportedScrapes) > 0 {
ret.Movie = &movie
ret.Group = &movie
if len(group.SupportedScrapes) > 0 {
ret.Movie = &group
ret.Group = &group
}
return ret
@ -314,7 +319,7 @@ func (c config) supports(ty ScrapeContentType) bool {
case ScrapeContentTypeGallery:
return c.GalleryByFragment != nil || len(c.GalleryByURL) > 0
case ScrapeContentTypeMovie, ScrapeContentTypeGroup:
return len(c.MovieByURL) > 0
return len(c.MovieByURL) > 0 || len(c.GroupByURL) > 0
}
panic("Unhandled ScrapeContentType")

View file

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

View file

@ -88,6 +88,40 @@ func setMovieBackImage(ctx context.Context, client *http.Client, m *models.Scrap
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) {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {

View file

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

View file

@ -1079,7 +1079,7 @@ func (s mappedScraper) scrapeGallery(ctx context.Context, q mappedQuery) (*Scrap
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
movieScraperConfig := s.Movie

View file

@ -39,6 +39,12 @@ func (c Cache) postScrape(ctx context.Context, content ScrapedContent) (ScrapedC
}
case models.ScrapedMovie:
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
@ -128,6 +134,38 @@ func (c Cache) postScrapeMovie(ctx context.Context, m models.ScrapedMovie) (Scra
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 {
tqb := c.repository.TagFinder
@ -154,7 +192,7 @@ func (c Cache) postScrapeScene(ctx context.Context, scene ScrapedScene) (Scraped
r := c.repository
if err := r.WithReadTxn(ctx, func(ctx context.Context) error {
pqb := r.PerformerFinder
mqb := r.MovieFinder
gqb := r.GroupFinder
tqb := r.TagFinder
sqb := r.StudioFinder
@ -173,10 +211,39 @@ func (c Cache) postScrapeScene(ctx context.Context, scene ScrapedScene) (Scraped
}
for _, p := range scene.Movies {
err := match.ScrapedMovie(ctx, mqb, p)
matchedID, err := match.ScrapedGroup(ctx, gqb, p.StoredID, p.Name)
if err != nil {
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)

View file

@ -84,7 +84,7 @@ func (s *xpathScraper) scrapeByURL(ctx context.Context, url string, ty ScrapeCon
}
return ret, nil
case ScrapeContentTypeMovie, ScrapeContentTypeGroup:
ret, err := scraper.scrapeMovie(ctx, q)
ret, err := scraper.scrapeGroup(ctx, q)
if err != nil || ret == nil {
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.anonymiseStudios(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) },
})
}(); err != nil {
@ -825,9 +825,9 @@ func (db *Anonymiser) anonymiseTags(ctx context.Context) error {
return nil
}
func (db *Anonymiser) anonymiseMovies(ctx context.Context) error {
logger.Infof("Anonymising movies")
table := movieTableMgr.table
func (db *Anonymiser) anonymiseGroups(ctx context.Context) error {
logger.Infof("Anonymising groups")
table := groupTableMgr.table
lastID := 0
total := 0
const logEvery = 10000
@ -883,7 +883,7 @@ func (db *Anonymiser) anonymiseMovies(ctx context.Context) error {
total++
if total%logEvery == 0 {
logger.Infof("Anonymised %d movies", total)
logger.Infof("Anonymised %d groups", total)
}
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
}

View file

@ -12,7 +12,7 @@ import (
)
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 {
image := []byte("image")

View file

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

View file

@ -17,19 +17,19 @@ import (
)
const (
movieTable = "movies"
movieIDColumn = "movie_id"
groupTable = "movies"
groupIDColumn = "movie_id"
movieFrontImageBlobColumn = "front_image_blob"
movieBackImageBlobColumn = "back_image_blob"
groupFrontImageBlobColumn = "front_image_blob"
groupBackImageBlobColumn = "back_image_blob"
moviesTagsTable = "movies_tags"
groupsTagsTable = "movies_tags"
movieURLsTable = "movie_urls"
movieURLColumn = "url"
groupURLsTable = "movie_urls"
groupURLColumn = "url"
)
type movieRow struct {
type groupRow struct {
ID int `db:"id" goqu:"skipinsert"`
Name zero.String `db:"name"`
Aliases zero.String `db:"aliases"`
@ -48,7 +48,7 @@ type movieRow struct {
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.Name = zero.StringFrom(o.Name)
r.Aliases = zero.StringFrom(o.Aliases)
@ -62,8 +62,8 @@ func (r *movieRow) fromMovie(o models.Movie) {
r.UpdatedAt = Timestamp{Timestamp: o.UpdatedAt}
}
func (r *movieRow) resolve() *models.Movie {
ret := &models.Movie{
func (r *groupRow) resolve() *models.Group {
ret := &models.Group{
ID: r.ID,
Name: r.Name.String,
Aliases: r.Aliases.String,
@ -80,11 +80,11 @@ func (r *movieRow) resolve() *models.Movie {
return ret
}
type movieRowRecord struct {
type groupRowRecord struct {
updateRecord
}
func (r *movieRowRecord) fromPartial(o models.MoviePartial) {
func (r *groupRowRecord) fromPartial(o models.GroupPartial) {
r.setNullString("name", o.Name)
r.setNullString("aliases", o.Aliases)
r.setNullInt("duration", o.Duration)
@ -97,26 +97,26 @@ func (r *movieRowRecord) fromPartial(o models.MoviePartial) {
r.setTimestamp("updated_at", o.UpdatedAt)
}
type movieRepositoryType struct {
type groupRepositoryType struct {
repository
scenes repository
tags joinRepository
}
var (
movieRepository = movieRepositoryType{
groupRepository = groupRepositoryType{
repository: repository{
tableName: movieTable,
tableName: groupTable,
idColumn: idColumn,
},
scenes: repository{
tableName: moviesScenesTable,
idColumn: movieIDColumn,
tableName: groupsScenesTable,
idColumn: groupIDColumn,
},
tags: joinRepository{
repository: repository{
tableName: moviesTagsTable,
idColumn: movieIDColumn,
tableName: groupsTagsTable,
idColumn: groupIDColumn,
},
fkColumn: tagIDColumn,
foreignTable: tagTable,
@ -125,40 +125,40 @@ var (
}
)
type MovieStore struct {
type GroupStore struct {
blobJoinQueryBuilder
tagRelationshipStore
tableMgr *table
}
func NewMovieStore(blobStore *BlobStore) *MovieStore {
return &MovieStore{
func NewGroupStore(blobStore *BlobStore) *GroupStore {
return &GroupStore{
blobJoinQueryBuilder: blobJoinQueryBuilder{
blobStore: blobStore,
joinTable: movieTable,
joinTable: groupTable,
},
tagRelationshipStore: tagRelationshipStore{
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
}
func (qb *MovieStore) selectDataset() *goqu.SelectDataset {
func (qb *GroupStore) selectDataset() *goqu.SelectDataset {
return dialect.From(qb.table()).Select(qb.table().All())
}
func (qb *MovieStore) Create(ctx context.Context, newObject *models.Movie) error {
var r movieRow
r.fromMovie(*newObject)
func (qb *GroupStore) Create(ctx context.Context, newObject *models.Group) error {
var r groupRow
r.fromGroup(*newObject)
id, err := qb.tableMgr.insertID(ctx, r)
if err != nil {
@ -167,7 +167,7 @@ func (qb *MovieStore) Create(ctx context.Context, newObject *models.Movie) error
if newObject.URLs.Loaded() {
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
}
}
@ -186,8 +186,8 @@ func (qb *MovieStore) Create(ctx context.Context, newObject *models.Movie) error
return nil
}
func (qb *MovieStore) UpdatePartial(ctx context.Context, id int, partial models.MoviePartial) (*models.Movie, error) {
r := movieRowRecord{
func (qb *GroupStore) UpdatePartial(ctx context.Context, id int, partial models.GroupPartial) (*models.Group, error) {
r := groupRowRecord{
updateRecord{
Record: make(exp.Record),
},
@ -202,7 +202,7 @@ func (qb *MovieStore) UpdatePartial(ctx context.Context, id int, partial models.
}
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
}
}
@ -214,16 +214,16 @@ func (qb *MovieStore) UpdatePartial(ctx context.Context, id int, partial models.
return qb.find(ctx, id)
}
func (qb *MovieStore) Update(ctx context.Context, updatedObject *models.Movie) error {
var r movieRow
r.fromMovie(*updatedObject)
func (qb *GroupStore) Update(ctx context.Context, updatedObject *models.Group) error {
var r groupRow
r.fromGroup(*updatedObject)
if err := qb.tableMgr.updateByID(ctx, updatedObject.ID, r); err != nil {
return err
}
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
}
}
@ -235,17 +235,17 @@ func (qb *MovieStore) Update(ctx context.Context, updatedObject *models.Movie) e
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
if err := qb.destroyImages(ctx, id); err != nil {
return err
}
return movieRepository.destroyExisting(ctx, []int{id})
return groupRepository.destroyExisting(ctx, []int{id})
}
// 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)
if errors.Is(err, sql.ErrNoRows) {
return nil, nil
@ -253,8 +253,8 @@ func (qb *MovieStore) Find(ctx context.Context, id int) (*models.Movie, error) {
return ret, err
}
func (qb *MovieStore) FindMany(ctx context.Context, ids []int) ([]*models.Movie, error) {
ret := make([]*models.Movie, len(ids))
func (qb *GroupStore) FindMany(ctx context.Context, ids []int) ([]*models.Group, error) {
ret := make([]*models.Group, len(ids))
table := qb.table()
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 {
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
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))
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
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)
if err != nil {
return nil, err
@ -309,11 +309,11 @@ func (qb *MovieStore) get(ctx context.Context, q *goqu.SelectDataset) (*models.M
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
var ret []*models.Movie
var ret []*models.Group
if err := queryFunc(ctx, q, single, func(r *sqlx.Rows) error {
var f movieRow
var f groupRow
if err := r.StructScan(&f); err != nil {
return err
}
@ -329,7 +329,7 @@ func (qb *MovieStore) getMany(ctx context.Context, q *goqu.SelectDataset) ([]*mo
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 = ?"
// if nocase {
// query += " COLLATE NOCASE"
@ -349,7 +349,7 @@ func (qb *MovieStore) FindByName(ctx context.Context, name string, nocase bool)
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"
// if nocase {
// query += " COLLATE NOCASE"
@ -374,12 +374,12 @@ func (qb *MovieStore) FindByNames(ctx context.Context, names []string, nocase bo
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())
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()
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 {
findFilter = &models.FindFilterType{}
}
if movieFilter == nil {
movieFilter = &models.MovieFilterType{}
if groupFilter == nil {
groupFilter = &models.GroupFilterType{}
}
query := movieRepository.newQuery()
distinctIDs(&query, movieTable)
query := groupRepository.newQuery()
distinctIDs(&query, groupTable)
if q := findFilter.Q; q != nil && *q != "" {
searchColumns := []string{"movies.name", "movies.aliases"}
query.parseQueryString(searchColumns, *q)
}
filter := filterBuilderFromHandler(ctx, &movieFilterHandler{
movieFilter: movieFilter,
filter := filterBuilderFromHandler(ctx, &groupFilterHandler{
groupFilter: groupFilter,
})
if err := query.addFilter(filter); err != nil {
@ -413,7 +413,7 @@ func (qb *MovieStore) makeQuery(ctx context.Context, movieFilter *models.MovieFi
}
var err error
query.sortAndPagination, err = qb.getMovieSort(findFilter)
query.sortAndPagination, err = qb.getGroupSort(findFilter)
if err != nil {
return nil, err
}
@ -423,8 +423,8 @@ func (qb *MovieStore) makeQuery(ctx context.Context, movieFilter *models.MovieFi
return &query, nil
}
func (qb *MovieStore) Query(ctx context.Context, movieFilter *models.MovieFilterType, findFilter *models.FindFilterType) ([]*models.Movie, int, error) {
query, err := qb.makeQuery(ctx, movieFilter, findFilter)
func (qb *GroupStore) Query(ctx context.Context, groupFilter *models.GroupFilterType, findFilter *models.FindFilterType) ([]*models.Group, int, error) {
query, err := qb.makeQuery(ctx, groupFilter, findFilter)
if err != nil {
return nil, 0, err
}
@ -434,16 +434,16 @@ func (qb *MovieStore) Query(ctx context.Context, movieFilter *models.MovieFilter
return nil, 0, err
}
movies, err := qb.FindMany(ctx, idsResult)
groups, err := qb.FindMany(ctx, idsResult)
if err != nil {
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) {
query, err := qb.makeQuery(ctx, movieFilter, findFilter)
func (qb *GroupStore) QueryCount(ctx context.Context, groupFilter *models.GroupFilterType, findFilter *models.FindFilterType) (int, error) {
query, err := qb.makeQuery(ctx, groupFilter, findFilter)
if err != nil {
return 0, err
}
@ -451,7 +451,7 @@ func (qb *MovieStore) QueryCount(ctx context.Context, movieFilter *models.MovieF
return query.executeCount(ctx)
}
var movieSortOptions = sortOptions{
var groupSortOptions = sortOptions{
"created_at",
"date",
"duration",
@ -464,7 +464,7 @@ var movieSortOptions = sortOptions{
"updated_at",
}
func (qb *MovieStore) getMovieSort(findFilter *models.FindFilterType) (string, error) {
func (qb *GroupStore) getGroupSort(findFilter *models.FindFilterType) (string, error) {
var sort string
var direction string
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
if err := movieSortOptions.validateSort(sort); err != nil {
if err := groupSortOptions.validateSort(sort); err != nil {
return "", err
}
sortQuery := ""
switch sort {
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
sortQuery += getCountSort(movieTable, moviesScenesTable, movieIDColumn, direction)
sortQuery += getCountSort(groupTable, groupsScenesTable, groupIDColumn, direction)
default:
sortQuery += getSort(sort, direction, "movies")
}
@ -495,11 +495,11 @@ func (qb *MovieStore) getMovieSort(findFilter *models.FindFilterType) (string, e
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
var ret []*models.Movie
if err := movieRepository.queryFunc(ctx, query, args, single, func(r *sqlx.Rows) error {
var f movieRow
var ret []*models.Group
if err := groupRepository.queryFunc(ctx, query, args, single, func(r *sqlx.Rows) error {
var f groupRow
if err := r.StructScan(&f); err != nil {
return err
}
@ -515,42 +515,42 @@ func (qb *MovieStore) queryMovies(ctx context.Context, query string, args []inte
return ret, nil
}
func (qb *MovieStore) UpdateFrontImage(ctx context.Context, movieID int, frontImage []byte) error {
return qb.UpdateImage(ctx, movieID, movieFrontImageBlobColumn, frontImage)
func (qb *GroupStore) UpdateFrontImage(ctx context.Context, groupID int, frontImage []byte) error {
return qb.UpdateImage(ctx, groupID, groupFrontImageBlobColumn, frontImage)
}
func (qb *MovieStore) UpdateBackImage(ctx context.Context, movieID int, backImage []byte) error {
return qb.UpdateImage(ctx, movieID, movieBackImageBlobColumn, backImage)
func (qb *GroupStore) UpdateBackImage(ctx context.Context, groupID int, backImage []byte) error {
return qb.UpdateImage(ctx, groupID, groupBackImageBlobColumn, backImage)
}
func (qb *MovieStore) destroyImages(ctx context.Context, movieID int) error {
if err := qb.DestroyImage(ctx, movieID, movieFrontImageBlobColumn); err != nil {
func (qb *GroupStore) destroyImages(ctx context.Context, groupID int) error {
if err := qb.DestroyImage(ctx, groupID, groupFrontImageBlobColumn); err != nil {
return err
}
if err := qb.DestroyImage(ctx, movieID, movieBackImageBlobColumn); err != nil {
if err := qb.DestroyImage(ctx, groupID, groupBackImageBlobColumn); err != nil {
return err
}
return nil
}
func (qb *MovieStore) GetFrontImage(ctx context.Context, movieID int) ([]byte, error) {
return qb.GetImage(ctx, movieID, movieFrontImageBlobColumn)
func (qb *GroupStore) GetFrontImage(ctx context.Context, groupID int) ([]byte, error) {
return qb.GetImage(ctx, groupID, groupFrontImageBlobColumn)
}
func (qb *MovieStore) HasFrontImage(ctx context.Context, movieID int) (bool, error) {
return qb.HasImage(ctx, movieID, movieFrontImageBlobColumn)
func (qb *GroupStore) HasFrontImage(ctx context.Context, groupID int) (bool, error) {
return qb.HasImage(ctx, groupID, groupFrontImageBlobColumn)
}
func (qb *MovieStore) GetBackImage(ctx context.Context, movieID int) ([]byte, error) {
return qb.GetImage(ctx, movieID, movieBackImageBlobColumn)
func (qb *GroupStore) GetBackImage(ctx context.Context, groupID int) ([]byte, error) {
return qb.GetImage(ctx, groupID, groupBackImageBlobColumn)
}
func (qb *MovieStore) HasBackImage(ctx context.Context, movieID int) (bool, error) {
return qb.HasImage(ctx, movieID, movieBackImageBlobColumn)
func (qb *GroupStore) HasBackImage(ctx context.Context, groupID int) (bool, error) {
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.*
FROM movies
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 = ?
`
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
FROM movies_scenes
INNER JOIN performers_scenes ON performers_scenes.scene_id = movies_scenes.scene_id
WHERE performers_scenes.performer_id = ?
`
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.*
FROM movies
WHERE movies.studio_id = ?
`
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
FROM movies
WHERE movies.studio_id = ?
`
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) {
return moviesURLsTableMgr.get(ctx, movieID)
func (qb *GroupStore) GetURLs(ctx context.Context, groupID int) ([]string, error) {
return groupsURLsTableMgr.get(ctx, groupID)
}

View file

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

View file

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

View file

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

View file

@ -195,10 +195,10 @@ func (qb *sceneFilterHandler) criterionHandler() criterionHandler {
&relatedFilterHandler{
relatedIDCol: "movies_scenes.movie_id",
relatedRepo: movieRepository.repository,
relatedHandler: &movieFilterHandler{sceneFilter.MoviesFilter},
relatedRepo: groupRepository.repository,
relatedHandler: &groupFilterHandler{sceneFilter.MoviesFilter},
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":
f.addWhere("scenes.studio_id IS NULL")
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")
case "performers":
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 {
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")
}
h := qb.getMultiCriterionHandlerBuilder(movieTable, moviesScenesTable, "movie_id", addJoinsFunc)
h := qb.getMultiCriterionHandlerBuilder(groupTable, groupsScenesTable, "movie_id", addJoinsFunc)
return h.handler(movies)
}

View file

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

View file

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

View file

@ -216,7 +216,7 @@ func TestStudioQueryForAutoTag(t *testing.T) {
withTxn(func(ctx context.Context) error {
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})
@ -225,16 +225,16 @@ func TestStudioQueryForAutoTag(t *testing.T) {
}
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})
if err != nil {
t.Errorf("Error finding studios: %s", err.Error())
}
if assert.Len(t, studios, 1) {
assert.Equal(t, studioIDs[studioIdxWithMovie], studios[0].ID)
assert.Equal(t, studioIDs[studioIdxWithGroup], studios[0].ID)
}
return nil
})
@ -911,7 +911,7 @@ func TestStudioQueryName(t *testing.T) {
}
func TestStudioQueryAlias(t *testing.T) {
const studioIdx = studioIdxWithMovie
const studioIdx = studioIdxWithGroup
studioName := getStudioStringValue(studioIdx, "Alias")
aliasCriterion := &models.StringCriterionInput{

View file

@ -588,30 +588,30 @@ func (t *orderedValueTable[T]) modifyJoins(ctx context.Context, id int, v []T, m
return nil
}
type scenesMoviesTable struct {
type scenesGroupsTable struct {
table
}
type moviesScenesRow struct {
type groupsScenesRow struct {
SceneID null.Int `db:"scene_id"`
MovieID null.Int `db:"movie_id"`
GroupID null.Int `db:"movie_id"`
SceneIndex null.Int `db:"scene_index"`
}
func (r moviesScenesRow) resolve(sceneID int) models.MoviesScenes {
return models.MoviesScenes{
MovieID: int(r.MovieID.Int64),
func (r groupsScenesRow) resolve(sceneID int) models.GroupsScenes {
return models.GroupsScenes{
GroupID: int(r.GroupID.Int64),
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))
const single = false
var ret []models.MoviesScenes
var ret []models.GroupsScenes
if err := queryFunc(ctx, q, single, func(rows *sqlx.Rows) error {
var v moviesScenesRow
var v groupsScenesRow
if err := rows.StructScan(&v); err != nil {
return err
}
@ -620,15 +620,15 @@ func (t *scenesMoviesTable) get(ctx context.Context, id int) ([]models.MoviesSce
return 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
}
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(
goqu.Vals{id, v.MovieID, intFromPtr(v.SceneIndex)},
goqu.Vals{id, v.GroupID, intFromPtr(v.SceneIndex)},
)
ret, err := exec(ctx, q)
if err != nil {
@ -638,7 +638,7 @@ func (t *scenesMoviesTable) insertJoin(ctx context.Context, id int, v models.Mov
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 {
if _, err := t.insertJoin(ctx, id, fk); err != nil {
return err
@ -648,7 +648,7 @@ func (t *scenesMoviesTable) insertJoins(ctx context.Context, id int, v []models.
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 {
return err
}
@ -656,7 +656,7 @@ func (t *scenesMoviesTable) replaceJoins(ctx context.Context, id int, v []models
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
fks, err := t.get(ctx, id)
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
var filtered []models.MoviesScenes
var filtered []models.GroupsScenes
for _, vv := range v {
found := false
for _, e := range fks {
if vv.MovieID == e.MovieID {
if vv.GroupID == e.GroupID {
found = true
break
}
@ -682,11 +682,11 @@ func (t *scenesMoviesTable) addJoins(ctx context.Context, id int, v []models.Mov
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 {
q := dialect.Delete(t.table.table).Where(
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 {
@ -697,7 +697,7 @@ func (t *scenesMoviesTable) destroyJoins(ctx context.Context, id int, v []models
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 {
case models.RelationshipUpdateModeSet:
return t.replaceJoins(ctx, id, v)

View file

@ -25,7 +25,7 @@ var (
scenesTagsJoinTable = goqu.T(scenesTagsTable)
scenesPerformersJoinTable = goqu.T(performersScenesTable)
scenesStashIDsJoinTable = goqu.T("scene_stash_ids")
scenesMoviesJoinTable = goqu.T(moviesScenesTable)
scenesGroupsJoinTable = goqu.T(groupsScenesTable)
scenesURLsJoinTable = goqu.T(scenesURLsTable)
performersAliasesJoinTable = goqu.T(performersAliasesTable)
@ -37,8 +37,8 @@ var (
studiosTagsJoinTable = goqu.T(studiosTagsTable)
studiosStashIDsJoinTable = goqu.T("studio_stash_ids")
moviesURLsJoinTable = goqu.T(movieURLsTable)
moviesTagsJoinTable = goqu.T(moviesTagsTable)
groupsURLsJoinTable = goqu.T(groupURLsTable)
groupsTagsJoinTable = goqu.T(groupsTagsTable)
tagsAliasesJoinTable = goqu.T(tagAliasesTable)
tagRelationsJoinTable = goqu.T(tagRelationsTable)
@ -184,10 +184,10 @@ var (
},
}
scenesMoviesTableMgr = &scenesMoviesTable{
scenesGroupsTableMgr = &scenesGroupsTable{
table: table{
table: scenesMoviesJoinTable,
idColumn: scenesMoviesJoinTable.Col(sceneIDColumn),
table: scenesGroupsJoinTable,
idColumn: scenesGroupsJoinTable.Col(sceneIDColumn),
},
}
@ -337,25 +337,25 @@ var (
)
var (
movieTableMgr = &table{
table: goqu.T(movieTable),
idColumn: goqu.T(movieTable).Col(idColumn),
groupTableMgr = &table{
table: goqu.T(groupTable),
idColumn: goqu.T(groupTable).Col(idColumn),
}
moviesURLsTableMgr = &orderedValueTable[string]{
groupsURLsTableMgr = &orderedValueTable[string]{
table: table{
table: moviesURLsJoinTable,
idColumn: moviesURLsJoinTable.Col(movieIDColumn),
table: groupsURLsJoinTable,
idColumn: groupsURLsJoinTable.Col(groupIDColumn),
},
valueColumn: moviesURLsJoinTable.Col(movieURLColumn),
valueColumn: groupsURLsJoinTable.Col(groupURLColumn),
}
moviesTagsTableMgr = &joinTable{
groupsTagsTableMgr = &joinTable{
table: table{
table: moviesTagsJoinTable,
idColumn: moviesTagsJoinTable.Col(movieIDColumn),
table: groupsTagsJoinTable,
idColumn: groupsTagsJoinTable.Col(groupIDColumn),
},
fkColumn: moviesTagsJoinTable.Col(tagIDColumn),
fkColumn: groupsTagsJoinTable.Col(tagIDColumn),
foreignTable: tagTableMgr,
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)
}
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 := `
SELECT tags.* FROM tags
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{
"created_at",
"galleries_count",
"groups_count",
"id",
"images_count",
"movies_count",
@ -684,7 +685,7 @@ func (qb *TagStore) getTagSort(query *queryBuilder, findFilter *models.FindFilte
case "studios_count":
sortQuery += getCountSort(tagTable, studiosTagsTable, tagIDColumn, direction)
case "movies_count", "groups_count":
sortQuery += getCountSort(tagTable, moviesTagsTable, tagIDColumn, direction)
sortQuery += getCountSort(tagTable, groupsTagsTable, tagIDColumn, direction)
default:
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) {
if movieCount != nil {
if groupCount != nil {
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...)
}

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 {
tqb := db.Tag
movieID := movieIDs[movieIdxWithTag]
groupID := groupIDs[groupIdxWithTag]
tags, err := tqb.FindByMovieID(ctx, movieID)
tags, err := tqb.FindByGroupID(ctx, groupID)
if err != nil {
t.Errorf("Error finding tags: %s", err.Error())
}
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 {
t.Errorf("Error finding tags: %s", err.Error())
@ -236,7 +236,7 @@ func TestTagQuerySort(t *testing.T) {
sortBy = "movies_count"
tags = queryTags(ctx, t, sqb, nil, findFilter)
assert.Equal(tagIDs[tagIdx1WithMovie], tags[0].ID)
assert.Equal(tagIDs[tagIdx1WithGroup], tags[0].ID)
return nil
})

View file

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

View file

@ -20,7 +20,7 @@ sceneByFragment:
<single scraper config>
sceneByURL:
<multiple scraper URL configs>
movieByURL:
groupByURL:
<multiple scraper URL configs>
galleryByFragment:
<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 `Scrape...` dropdown button in Scene Edit page | Valid `sceneByFragment` configuration. |
| 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. |
| 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 |
| `sceneByQueryFragment`, `sceneByFragment` | JSON-encoded scene fragment | 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 |
| `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.
### 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`:
* `{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.
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:
@ -820,7 +820,7 @@ URL
Date
Image
Studio (see Studio Fields)
Movies (see Movie Fields)
Groups (see Group Fields)
Tags (see Tag fields)
Performers (list of Performer fields)
```
@ -835,7 +835,7 @@ URL
Name
```
### Movie
### Group
```
Name
Aliases