mirror of
https://github.com/stashapp/stash.git
synced 2025-12-17 20:34:37 +03:00
Add tags to studios (#4858)
* Fix makeTagFilter mode * Remove studio_tags filter criterion This is handled by studios_filter. The support for this still needs to be added in the UI, so I have removed the criterion options in the short-term. --------- Co-authored-by: WithoutPants <53250216+WithoutPants@users.noreply.github.com>
This commit is contained in:
@@ -22,6 +22,7 @@ type Studio struct {
|
||||
Details string `json:"details,omitempty"`
|
||||
Aliases []string `json:"aliases,omitempty"`
|
||||
StashIDs []models.StashID `json:"stash_ids,omitempty"`
|
||||
Tags []string `json:"tags,omitempty"`
|
||||
IgnoreAutoTag bool `json:"ignore_auto_tag,omitempty"`
|
||||
}
|
||||
|
||||
|
||||
@@ -58,6 +58,27 @@ func (_m *StudioReaderWriter) Count(ctx context.Context) (int, error) {
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// CountByTagID provides a mock function with given fields: ctx, tagID
|
||||
func (_m *StudioReaderWriter) CountByTagID(ctx context.Context, tagID int) (int, error) {
|
||||
ret := _m.Called(ctx, tagID)
|
||||
|
||||
var r0 int
|
||||
if rf, ok := ret.Get(0).(func(context.Context, int) int); ok {
|
||||
r0 = rf(ctx, tagID)
|
||||
} else {
|
||||
r0 = ret.Get(0).(int)
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, int) error); ok {
|
||||
r1 = rf(ctx, tagID)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// Create provides a mock function with given fields: ctx, newStudio
|
||||
func (_m *StudioReaderWriter) Create(ctx context.Context, newStudio *models.Studio) error {
|
||||
ret := _m.Called(ctx, newStudio)
|
||||
@@ -316,6 +337,29 @@ func (_m *StudioReaderWriter) GetStashIDs(ctx context.Context, relatedID int) ([
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// GetTagIDs provides a mock function with given fields: ctx, relatedID
|
||||
func (_m *StudioReaderWriter) GetTagIDs(ctx context.Context, relatedID int) ([]int, error) {
|
||||
ret := _m.Called(ctx, relatedID)
|
||||
|
||||
var r0 []int
|
||||
if rf, ok := ret.Get(0).(func(context.Context, int) []int); ok {
|
||||
r0 = rf(ctx, relatedID)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]int)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, int) error); ok {
|
||||
r1 = rf(ctx, relatedID)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// HasImage provides a mock function with given fields: ctx, studioID
|
||||
func (_m *StudioReaderWriter) HasImage(ctx context.Context, studioID int) (bool, error) {
|
||||
ret := _m.Called(ctx, studioID)
|
||||
@@ -367,6 +411,27 @@ func (_m *StudioReaderWriter) Query(ctx context.Context, studioFilter *models.St
|
||||
return r0, r1, r2
|
||||
}
|
||||
|
||||
// QueryCount provides a mock function with given fields: ctx, studioFilter, findFilter
|
||||
func (_m *StudioReaderWriter) QueryCount(ctx context.Context, studioFilter *models.StudioFilterType, findFilter *models.FindFilterType) (int, error) {
|
||||
ret := _m.Called(ctx, studioFilter, findFilter)
|
||||
|
||||
var r0 int
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *models.StudioFilterType, *models.FindFilterType) int); ok {
|
||||
r0 = rf(ctx, studioFilter, findFilter)
|
||||
} else {
|
||||
r0 = ret.Get(0).(int)
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, *models.StudioFilterType, *models.FindFilterType) error); ok {
|
||||
r1 = rf(ctx, studioFilter, findFilter)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// QueryForAutoTag provides a mock function with given fields: ctx, words
|
||||
func (_m *StudioReaderWriter) QueryForAutoTag(ctx context.Context, words []string) ([]*models.Studio, error) {
|
||||
ret := _m.Called(ctx, words)
|
||||
|
||||
@@ -427,6 +427,29 @@ func (_m *TagReaderWriter) FindBySceneMarkerID(ctx context.Context, sceneMarkerI
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// FindByStudioID provides a mock function with given fields: ctx, studioID
|
||||
func (_m *TagReaderWriter) FindByStudioID(ctx context.Context, studioID int) ([]*models.Tag, error) {
|
||||
ret := _m.Called(ctx, studioID)
|
||||
|
||||
var r0 []*models.Tag
|
||||
if rf, ok := ret.Get(0).(func(context.Context, int) []*models.Tag); ok {
|
||||
r0 = rf(ctx, studioID)
|
||||
} 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, studioID)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// FindMany provides a mock function with given fields: ctx, ids
|
||||
func (_m *TagReaderWriter) FindMany(ctx context.Context, ids []int) ([]*models.Tag, error) {
|
||||
ret := _m.Called(ctx, ids)
|
||||
|
||||
@@ -19,6 +19,7 @@ type Studio struct {
|
||||
IgnoreAutoTag bool `json:"ignore_auto_tag"`
|
||||
|
||||
Aliases RelatedStrings `json:"aliases"`
|
||||
TagIDs RelatedIDs `json:"tag_ids"`
|
||||
StashIDs RelatedStashIDs `json:"stash_ids"`
|
||||
}
|
||||
|
||||
@@ -45,6 +46,7 @@ type StudioPartial struct {
|
||||
IgnoreAutoTag OptionalBool
|
||||
|
||||
Aliases *UpdateStrings
|
||||
TagIDs *UpdateIDs
|
||||
StashIDs *UpdateStashIDs
|
||||
}
|
||||
|
||||
@@ -61,6 +63,12 @@ func (s *Studio) LoadAliases(ctx context.Context, l AliasLoader) error {
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Studio) LoadTagIDs(ctx context.Context, l TagIDLoader) error {
|
||||
return s.TagIDs.load(func() ([]int, error) {
|
||||
return l.GetTagIDs(ctx, s.ID)
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Studio) LoadStashIDs(ctx context.Context, l StashIDLoader) error {
|
||||
return s.StashIDs.load(func() ([]StashID, error) {
|
||||
return l.GetStashIDs(ctx, s.ID)
|
||||
@@ -72,6 +80,10 @@ func (s *Studio) LoadRelationships(ctx context.Context, l PerformerReader) error
|
||||
return err
|
||||
}
|
||||
|
||||
if err := s.LoadTagIDs(ctx, l); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := s.LoadStashIDs(ctx, l); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ type StudioFinder interface {
|
||||
// StudioQueryer provides methods to query studios.
|
||||
type StudioQueryer interface {
|
||||
Query(ctx context.Context, studioFilter *StudioFilterType, findFilter *FindFilterType) ([]*Studio, int, error)
|
||||
QueryCount(ctx context.Context, studioFilter *StudioFilterType, findFilter *FindFilterType) (int, error)
|
||||
}
|
||||
|
||||
type StudioAutoTagQueryer interface {
|
||||
@@ -36,6 +37,7 @@ type StudioAutoTagQueryer interface {
|
||||
// StudioCounter provides methods to count studios.
|
||||
type StudioCounter interface {
|
||||
Count(ctx context.Context) (int, error)
|
||||
CountByTagID(ctx context.Context, tagID int) (int, error)
|
||||
}
|
||||
|
||||
// StudioCreator provides methods to create studios.
|
||||
@@ -74,6 +76,7 @@ type StudioReader interface {
|
||||
|
||||
AliasLoader
|
||||
StashIDLoader
|
||||
TagIDLoader
|
||||
|
||||
All(ctx context.Context) ([]*Studio, error)
|
||||
GetImage(ctx context.Context, studioID int) ([]byte, error)
|
||||
|
||||
@@ -22,6 +22,7 @@ type TagFinder interface {
|
||||
FindByPerformerID(ctx context.Context, performerID int) ([]*Tag, error)
|
||||
FindByMovieID(ctx context.Context, movieID 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)
|
||||
FindByNames(ctx context.Context, names []string, nocase bool) ([]*Tag, error)
|
||||
}
|
||||
|
||||
@@ -14,6 +14,10 @@ type StudioFilterType struct {
|
||||
IsMissing *string `json:"is_missing"`
|
||||
// Filter by rating expressed as 1-100
|
||||
Rating100 *IntCriterionInput `json:"rating100"`
|
||||
// Filter to only include studios with these tags
|
||||
Tags *HierarchicalMultiCriterionInput `json:"tags"`
|
||||
// Filter by tag count
|
||||
TagCount *IntCriterionInput `json:"tag_count"`
|
||||
// Filter by favorite
|
||||
Favorite *bool `json:"favorite"`
|
||||
// Filter by scene count
|
||||
@@ -53,6 +57,7 @@ type StudioCreateInput struct {
|
||||
Favorite *bool `json:"favorite"`
|
||||
Details *string `json:"details"`
|
||||
Aliases []string `json:"aliases"`
|
||||
TagIds []string `json:"tag_ids"`
|
||||
IgnoreAutoTag *bool `json:"ignore_auto_tag"`
|
||||
}
|
||||
|
||||
@@ -68,5 +73,6 @@ type StudioUpdateInput struct {
|
||||
Favorite *bool `json:"favorite"`
|
||||
Details *string `json:"details"`
|
||||
Aliases []string `json:"aliases"`
|
||||
TagIds []string `json:"tag_ids"`
|
||||
IgnoreAutoTag *bool `json:"ignore_auto_tag"`
|
||||
}
|
||||
|
||||
@@ -20,6 +20,8 @@ type TagFilterType struct {
|
||||
GalleryCount *IntCriterionInput `json:"gallery_count"`
|
||||
// Filter by number of performers with this tag
|
||||
PerformerCount *IntCriterionInput `json:"performer_count"`
|
||||
// Filter by number of studios with this tag
|
||||
StudioCount *IntCriterionInput `json:"studio_count"`
|
||||
// Filter by number of movies with this tag
|
||||
MovieCount *IntCriterionInput `json:"movie_count"`
|
||||
// Filter by number of markers with this tag
|
||||
|
||||
Reference in New Issue
Block a user