Include subsidiary studios/tags in tab badge counters (#3816)

* Add '_all' counts
* Use '_all' counts in UI
* Make other counts non-nullable
* Hide tab counts if zero
* Add resolver parameter
This commit is contained in:
DingDongSoLong4 2023-06-16 02:46:14 +02:00 committed by GitHub
parent 47c3e855c8
commit f65e87773c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
32 changed files with 453 additions and 193 deletions

View file

@ -17,10 +17,15 @@ fragment StudioData on Studio {
ignore_auto_tag ignore_auto_tag
image_path image_path
scene_count scene_count
scene_count_all: scene_count(depth: -1)
image_count image_count
image_count_all: image_count(depth: -1)
gallery_count gallery_count
gallery_count_all: gallery_count(depth: -1)
performer_count performer_count
performer_count_all: performer_count(depth: -1)
movie_count movie_count
movie_count_all: movie_count(depth: -1)
stash_ids { stash_ids {
stash_id stash_id
endpoint endpoint

View file

@ -6,10 +6,15 @@ fragment TagData on Tag {
ignore_auto_tag ignore_auto_tag
image_path image_path
scene_count scene_count
scene_count_all: scene_count(depth: -1)
scene_marker_count scene_marker_count
scene_marker_count_all: scene_marker_count(depth: -1)
image_count image_count
image_count_all: image_count(depth: -1)
gallery_count gallery_count
gallery_count_all: gallery_count(depth: -1)
performer_count performer_count
performer_count_all: performer_count(depth: -1)
parents { parents {
...SlimTagData ...SlimTagData

View file

@ -19,7 +19,7 @@ type Movie {
front_image_path: String # Resolver front_image_path: String # Resolver
back_image_path: String # Resolver back_image_path: String # Resolver
scene_count: Int # Resolver scene_count: Int! # Resolver
scenes: [Scene!]! scenes: [Scene!]!
} }

View file

@ -41,10 +41,11 @@ type Performer {
ignore_auto_tag: Boolean! ignore_auto_tag: Boolean!
image_path: String # Resolver image_path: String # Resolver
scene_count: Int # Resolver scene_count: Int! # Resolver
image_count: Int # Resolver image_count: Int! # Resolver
gallery_count: Int # Resolver gallery_count: Int! # Resolver
performer_count: Int # Resolver movie_count: Int! # Resolver
performer_count: Int! # Resolver
o_counter: Int # Resolver o_counter: Int # Resolver
scenes: [Scene!]! scenes: [Scene!]!
stash_ids: [StashID!]! stash_ids: [StashID!]!
@ -58,7 +59,6 @@ type Performer {
weight: Int weight: Int
created_at: Time! created_at: Time!
updated_at: Time! updated_at: Time!
movie_count: Int
movies: [Movie!]! movies: [Movie!]!
} }

View file

@ -9,10 +9,11 @@ type Studio {
ignore_auto_tag: Boolean! ignore_auto_tag: Boolean!
image_path: String # Resolver image_path: String # Resolver
scene_count: Int # Resolver scene_count(depth: Int): Int! # Resolver
image_count: Int # Resolver image_count(depth: Int): Int! # Resolver
gallery_count: Int # Resolver gallery_count(depth: Int): Int! # Resolver
performer_count: Int # Resolver performer_count(depth: Int): Int! # Resolver
movie_count(depth: Int): Int! # Resolver
stash_ids: [StashID!]! stash_ids: [StashID!]!
# rating expressed as 1-5 # rating expressed as 1-5
rating: Int @deprecated(reason: "Use 1-100 range with rating100") rating: Int @deprecated(reason: "Use 1-100 range with rating100")
@ -21,7 +22,6 @@ type Studio {
details: String details: String
created_at: Time! created_at: Time!
updated_at: Time! updated_at: Time!
movie_count: Int
movies: [Movie!]! movies: [Movie!]!
} }

View file

@ -8,11 +8,11 @@ type Tag {
updated_at: Time! updated_at: Time!
image_path: String # Resolver image_path: String # Resolver
scene_count: Int # Resolver scene_count(depth: Int): Int! # Resolver
scene_marker_count: Int # Resolver scene_marker_count(depth: Int): Int! # Resolver
image_count: Int # Resolver image_count(depth: Int): Int! # Resolver
gallery_count: Int # Resolver gallery_count(depth: Int): Int! # Resolver
performer_count: Int performer_count(depth: Int): Int! # Resolver
parents: [Tag!]! parents: [Tag!]!
children: [Tag!]! children: [Tag!]!

View file

@ -71,16 +71,15 @@ func (r *movieResolver) BackImagePath(ctx context.Context, obj *models.Movie) (*
return &imagePath, nil return &imagePath, nil
} }
func (r *movieResolver) SceneCount(ctx context.Context, obj *models.Movie) (ret *int, err error) { func (r *movieResolver) SceneCount(ctx context.Context, obj *models.Movie) (ret int, err error) {
var res int
if err := r.withReadTxn(ctx, func(ctx context.Context) error { if err := r.withReadTxn(ctx, func(ctx context.Context) error {
res, err = r.repository.Scene.CountByMovieID(ctx, obj.ID) ret, err = r.repository.Scene.CountByMovieID(ctx, obj.ID)
return err return err
}); err != nil { }); err != nil {
return nil, err return 0, err
} }
return &res, err return ret, nil
} }
func (r *movieResolver) Scenes(ctx context.Context, obj *models.Movie) (ret []*models.Scene, err error) { func (r *movieResolver) Scenes(ctx context.Context, obj *models.Movie) (ret []*models.Scene, err error) {

View file

@ -92,40 +92,59 @@ func (r *performerResolver) Tags(ctx context.Context, obj *models.Performer) (re
return ret, firstError(errs) return ret, firstError(errs)
} }
func (r *performerResolver) SceneCount(ctx context.Context, obj *models.Performer) (ret *int, err error) { func (r *performerResolver) SceneCount(ctx context.Context, obj *models.Performer) (ret int, err error) {
var res int
if err := r.withReadTxn(ctx, func(ctx context.Context) error { if err := r.withReadTxn(ctx, func(ctx context.Context) error {
res, err = r.repository.Scene.CountByPerformerID(ctx, obj.ID) ret, err = r.repository.Scene.CountByPerformerID(ctx, obj.ID)
return err return err
}); err != nil { }); err != nil {
return nil, err return 0, err
} }
return &res, nil return ret, nil
} }
func (r *performerResolver) ImageCount(ctx context.Context, obj *models.Performer) (ret *int, err error) { func (r *performerResolver) ImageCount(ctx context.Context, obj *models.Performer) (ret int, err error) {
var res int
if err := r.withReadTxn(ctx, func(ctx context.Context) error { if err := r.withReadTxn(ctx, func(ctx context.Context) error {
res, err = image.CountByPerformerID(ctx, r.repository.Image, obj.ID) ret, err = image.CountByPerformerID(ctx, r.repository.Image, obj.ID)
return err return err
}); err != nil { }); err != nil {
return nil, err return 0, err
} }
return &res, nil return ret, nil
} }
func (r *performerResolver) GalleryCount(ctx context.Context, obj *models.Performer) (ret *int, err error) { func (r *performerResolver) GalleryCount(ctx context.Context, obj *models.Performer) (ret int, err error) {
var res int
if err := r.withReadTxn(ctx, func(ctx context.Context) error { if err := r.withReadTxn(ctx, func(ctx context.Context) error {
res, err = gallery.CountByPerformerID(ctx, r.repository.Gallery, obj.ID) ret, err = gallery.CountByPerformerID(ctx, r.repository.Gallery, obj.ID)
return err return err
}); err != nil { }); err != nil {
return nil, err return 0, err
} }
return &res, nil return ret, nil
}
func (r *performerResolver) MovieCount(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)
return err
}); err != nil {
return 0, err
}
return ret, nil
}
func (r *performerResolver) PerformerCount(ctx context.Context, obj *models.Performer) (ret int, err error) {
if err := r.withReadTxn(ctx, func(ctx context.Context) error {
ret, err = performer.CountByAppearsWith(ctx, r.repository.Performer, obj.ID)
return err
}); err != nil {
return 0, err
}
return ret, nil
} }
func (r *performerResolver) OCounter(ctx context.Context, obj *models.Performer) (ret *int, err error) { func (r *performerResolver) OCounter(ctx context.Context, obj *models.Performer) (ret *int, err error) {
@ -197,27 +216,3 @@ func (r *performerResolver) Movies(ctx context.Context, obj *models.Performer) (
return ret, nil return ret, nil
} }
func (r *performerResolver) MovieCount(ctx context.Context, obj *models.Performer) (ret *int, err error) {
var res int
if err := r.withReadTxn(ctx, func(ctx context.Context) error {
res, err = r.repository.Movie.CountByPerformerID(ctx, obj.ID)
return err
}); err != nil {
return nil, err
}
return &res, nil
}
func (r *performerResolver) PerformerCount(ctx context.Context, obj *models.Performer) (ret *int, err error) {
var res int
if err := r.withReadTxn(ctx, func(ctx context.Context) error {
res, err = performer.CountByAppearsWith(ctx, r.repository.Performer, obj.ID)
return err
}); err != nil {
return nil, err
}
return &res, nil
}

View file

@ -8,7 +8,9 @@ import (
"github.com/stashapp/stash/pkg/gallery" "github.com/stashapp/stash/pkg/gallery"
"github.com/stashapp/stash/pkg/image" "github.com/stashapp/stash/pkg/image"
"github.com/stashapp/stash/pkg/models" "github.com/stashapp/stash/pkg/models"
"github.com/stashapp/stash/pkg/movie"
"github.com/stashapp/stash/pkg/performer" "github.com/stashapp/stash/pkg/performer"
"github.com/stashapp/stash/pkg/scene"
) )
func (r *studioResolver) ImagePath(ctx context.Context, obj *models.Studio) (*string, error) { func (r *studioResolver) ImagePath(ctx context.Context, obj *models.Studio) (*string, error) {
@ -37,52 +39,59 @@ func (r *studioResolver) Aliases(ctx context.Context, obj *models.Studio) (ret [
return ret, err return ret, err
} }
func (r *studioResolver) SceneCount(ctx context.Context, obj *models.Studio) (ret *int, err error) { func (r *studioResolver) SceneCount(ctx context.Context, obj *models.Studio, depth *int) (ret int, err error) {
var res int
if err := r.withReadTxn(ctx, func(ctx context.Context) error { if err := r.withReadTxn(ctx, func(ctx context.Context) error {
res, err = r.repository.Scene.CountByStudioID(ctx, obj.ID) ret, err = scene.CountByStudioID(ctx, r.repository.Scene, obj.ID, depth)
return err return err
}); err != nil { }); err != nil {
return nil, err return 0, err
} }
return &res, err return ret, nil
} }
func (r *studioResolver) ImageCount(ctx context.Context, obj *models.Studio) (ret *int, err error) { func (r *studioResolver) ImageCount(ctx context.Context, obj *models.Studio, depth *int) (ret int, err error) {
var res int
if err := r.withReadTxn(ctx, func(ctx context.Context) error { if err := r.withReadTxn(ctx, func(ctx context.Context) error {
res, err = image.CountByStudioID(ctx, r.repository.Image, obj.ID) ret, err = image.CountByStudioID(ctx, r.repository.Image, obj.ID, depth)
return err return err
}); err != nil { }); err != nil {
return nil, err return 0, err
} }
return &res, nil return ret, nil
} }
func (r *studioResolver) GalleryCount(ctx context.Context, obj *models.Studio) (ret *int, err error) { func (r *studioResolver) GalleryCount(ctx context.Context, obj *models.Studio, depth *int) (ret int, err error) {
var res int
if err := r.withReadTxn(ctx, func(ctx context.Context) error { if err := r.withReadTxn(ctx, func(ctx context.Context) error {
res, err = gallery.CountByStudioID(ctx, r.repository.Gallery, obj.ID) ret, err = gallery.CountByStudioID(ctx, r.repository.Gallery, obj.ID, depth)
return err return err
}); err != nil { }); err != nil {
return nil, err return 0, err
} }
return &res, nil return ret, nil
} }
func (r *studioResolver) PerformerCount(ctx context.Context, obj *models.Studio) (ret *int, err error) { func (r *studioResolver) PerformerCount(ctx context.Context, obj *models.Studio, depth *int) (ret int, err error) {
var res int
if err := r.withReadTxn(ctx, func(ctx context.Context) error { if err := r.withReadTxn(ctx, func(ctx context.Context) error {
res, err = performer.CountByStudioID(ctx, r.repository.Performer, obj.ID) ret, err = performer.CountByStudioID(ctx, r.repository.Performer, obj.ID, depth)
return err return err
}); err != nil { }); err != nil {
return nil, err return 0, err
} }
return &res, nil return ret, nil
}
func (r *studioResolver) MovieCount(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)
return err
}); err != nil {
return 0, err
}
return ret, nil
} }
func (r *studioResolver) ParentStudio(ctx context.Context, obj *models.Studio) (ret *models.Studio, err error) { func (r *studioResolver) ParentStudio(ctx context.Context, obj *models.Studio) (ret *models.Studio, err error) {
@ -139,15 +148,3 @@ func (r *studioResolver) Movies(ctx context.Context, obj *models.Studio) (ret []
return ret, nil return ret, nil
} }
func (r *studioResolver) MovieCount(ctx context.Context, obj *models.Studio) (ret *int, err error) {
var res int
if err := r.withReadTxn(ctx, func(ctx context.Context) error {
res, err = r.repository.Movie.CountByStudioID(ctx, obj.ID)
return err
}); err != nil {
return nil, err
}
return &res, nil
}

View file

@ -7,6 +7,8 @@ import (
"github.com/stashapp/stash/pkg/gallery" "github.com/stashapp/stash/pkg/gallery"
"github.com/stashapp/stash/pkg/image" "github.com/stashapp/stash/pkg/image"
"github.com/stashapp/stash/pkg/models" "github.com/stashapp/stash/pkg/models"
"github.com/stashapp/stash/pkg/performer"
"github.com/stashapp/stash/pkg/scene"
) )
func (r *tagResolver) Parents(ctx context.Context, obj *models.Tag) (ret []*models.Tag, err error) { func (r *tagResolver) Parents(ctx context.Context, obj *models.Tag) (ret []*models.Tag, err error) {
@ -42,64 +44,59 @@ func (r *tagResolver) Aliases(ctx context.Context, obj *models.Tag) (ret []strin
return ret, err return ret, err
} }
func (r *tagResolver) SceneCount(ctx context.Context, obj *models.Tag) (ret *int, err error) { func (r *tagResolver) SceneCount(ctx context.Context, obj *models.Tag, depth *int) (ret int, err error) {
var count int
if err := r.withReadTxn(ctx, func(ctx context.Context) error { if err := r.withReadTxn(ctx, func(ctx context.Context) error {
count, err = r.repository.Scene.CountByTagID(ctx, obj.ID) ret, err = scene.CountByTagID(ctx, r.repository.Scene, obj.ID, depth)
return err return err
}); err != nil { }); err != nil {
return nil, err return 0, err
} }
return &count, err return ret, nil
} }
func (r *tagResolver) SceneMarkerCount(ctx context.Context, obj *models.Tag) (ret *int, err error) { func (r *tagResolver) SceneMarkerCount(ctx context.Context, obj *models.Tag, depth *int) (ret int, err error) {
var count int
if err := r.withReadTxn(ctx, func(ctx context.Context) error { if err := r.withReadTxn(ctx, func(ctx context.Context) error {
count, err = r.repository.SceneMarker.CountByTagID(ctx, obj.ID) ret, err = scene.MarkerCountByTagID(ctx, r.repository.SceneMarker, obj.ID, depth)
return err return err
}); err != nil { }); err != nil {
return nil, err return 0, err
} }
return &count, err return ret, nil
} }
func (r *tagResolver) ImageCount(ctx context.Context, obj *models.Tag) (ret *int, err error) { func (r *tagResolver) ImageCount(ctx context.Context, obj *models.Tag, depth *int) (ret int, err error) {
var res int
if err := r.withReadTxn(ctx, func(ctx context.Context) error { if err := r.withReadTxn(ctx, func(ctx context.Context) error {
res, err = image.CountByTagID(ctx, r.repository.Image, obj.ID) ret, err = image.CountByTagID(ctx, r.repository.Image, obj.ID, depth)
return err return err
}); err != nil { }); err != nil {
return nil, err return 0, err
} }
return &res, nil return ret, nil
} }
func (r *tagResolver) GalleryCount(ctx context.Context, obj *models.Tag) (ret *int, err error) { func (r *tagResolver) GalleryCount(ctx context.Context, obj *models.Tag, depth *int) (ret int, err error) {
var res int
if err := r.withReadTxn(ctx, func(ctx context.Context) error { if err := r.withReadTxn(ctx, func(ctx context.Context) error {
res, err = gallery.CountByTagID(ctx, r.repository.Gallery, obj.ID) ret, err = gallery.CountByTagID(ctx, r.repository.Gallery, obj.ID, depth)
return err return err
}); err != nil { }); err != nil {
return nil, err return 0, err
} }
return &res, nil return ret, nil
} }
func (r *tagResolver) PerformerCount(ctx context.Context, obj *models.Tag) (ret *int, err error) { func (r *tagResolver) PerformerCount(ctx context.Context, obj *models.Tag, depth *int) (ret int, err error) {
var count int
if err := r.withReadTxn(ctx, func(ctx context.Context) error { if err := r.withReadTxn(ctx, func(ctx context.Context) error {
count, err = r.repository.Performer.CountByTagID(ctx, obj.ID) ret, err = performer.CountByTagID(ctx, r.repository.Performer, obj.ID, depth)
return err return err
}); err != nil { }); err != nil {
return nil, err return 0, err
} }
return &count, err return ret, nil
} }
func (r *tagResolver) ImagePath(ctx context.Context, obj *models.Tag) (*string, error) { func (r *tagResolver) ImagePath(ctx context.Context, obj *models.Tag) (*string, error) {

View file

@ -35,22 +35,24 @@ func CountByPerformerID(ctx context.Context, r CountQueryer, id int) (int, error
return r.QueryCount(ctx, filter, nil) return r.QueryCount(ctx, filter, nil)
} }
func CountByStudioID(ctx context.Context, r CountQueryer, id int) (int, error) { func CountByStudioID(ctx context.Context, r CountQueryer, id int, depth *int) (int, error) {
filter := &models.GalleryFilterType{ filter := &models.GalleryFilterType{
Studios: &models.HierarchicalMultiCriterionInput{ Studios: &models.HierarchicalMultiCriterionInput{
Value: []string{strconv.Itoa(id)}, Value: []string{strconv.Itoa(id)},
Modifier: models.CriterionModifierIncludes, Modifier: models.CriterionModifierIncludes,
Depth: depth,
}, },
} }
return r.QueryCount(ctx, filter, nil) return r.QueryCount(ctx, filter, nil)
} }
func CountByTagID(ctx context.Context, r CountQueryer, id int) (int, error) { func CountByTagID(ctx context.Context, r CountQueryer, id int, depth *int) (int, error) {
filter := &models.GalleryFilterType{ filter := &models.GalleryFilterType{
Tags: &models.HierarchicalMultiCriterionInput{ Tags: &models.HierarchicalMultiCriterionInput{
Value: []string{strconv.Itoa(id)}, Value: []string{strconv.Itoa(id)},
Modifier: models.CriterionModifierIncludes, Modifier: models.CriterionModifierIncludes,
Depth: depth,
}, },
} }

View file

@ -52,22 +52,24 @@ func CountByPerformerID(ctx context.Context, r CountQueryer, id int) (int, error
return r.QueryCount(ctx, filter, nil) return r.QueryCount(ctx, filter, nil)
} }
func CountByStudioID(ctx context.Context, r CountQueryer, id int) (int, error) { func CountByStudioID(ctx context.Context, r CountQueryer, id int, depth *int) (int, error) {
filter := &models.ImageFilterType{ filter := &models.ImageFilterType{
Studios: &models.HierarchicalMultiCriterionInput{ Studios: &models.HierarchicalMultiCriterionInput{
Value: []string{strconv.Itoa(id)}, Value: []string{strconv.Itoa(id)},
Modifier: models.CriterionModifierIncludes, Modifier: models.CriterionModifierIncludes,
Depth: depth,
}, },
} }
return r.QueryCount(ctx, filter, nil) return r.QueryCount(ctx, filter, nil)
} }
func CountByTagID(ctx context.Context, r CountQueryer, id int) (int, error) { func CountByTagID(ctx context.Context, r CountQueryer, id int, depth *int) (int, error) {
filter := &models.ImageFilterType{ filter := &models.ImageFilterType{
Tags: &models.HierarchicalMultiCriterionInput{ Tags: &models.HierarchicalMultiCriterionInput{
Value: []string{strconv.Itoa(id)}, Value: []string{strconv.Itoa(id)},
Modifier: models.CriterionModifierIncludes, Modifier: models.CriterionModifierIncludes,
Depth: depth,
}, },
} }

View file

@ -384,6 +384,27 @@ func (_m *MovieReaderWriter) Query(ctx context.Context, movieFilter *models.Movi
return r0, r1, r2 return r0, r1, r2
} }
// QueryCount provides a mock function with given fields: ctx, movieFilter, findFilter
func (_m *MovieReaderWriter) QueryCount(ctx context.Context, movieFilter *models.MovieFilterType, findFilter *models.FindFilterType) (int, error) {
ret := _m.Called(ctx, movieFilter, findFilter)
var r0 int
if rf, ok := ret.Get(0).(func(context.Context, *models.MovieFilterType, *models.FindFilterType) int); ok {
r0 = rf(ctx, movieFilter, 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)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Update provides a mock function with given fields: ctx, updatedMovie // Update provides a mock function with given fields: ctx, updatedMovie
func (_m *MovieReaderWriter) Update(ctx context.Context, updatedMovie *models.Movie) error { func (_m *MovieReaderWriter) Update(ctx context.Context, updatedMovie *models.Movie) error {
ret := _m.Called(ctx, updatedMovie) ret := _m.Called(ctx, updatedMovie)

View file

@ -252,6 +252,27 @@ func (_m *SceneMarkerReaderWriter) Query(ctx context.Context, sceneMarkerFilter
return r0, r1, r2 return r0, r1, r2
} }
// QueryCount provides a mock function with given fields: ctx, sceneMarkerFilter, findFilter
func (_m *SceneMarkerReaderWriter) QueryCount(ctx context.Context, sceneMarkerFilter *models.SceneMarkerFilterType, findFilter *models.FindFilterType) (int, error) {
ret := _m.Called(ctx, sceneMarkerFilter, findFilter)
var r0 int
if rf, ok := ret.Get(0).(func(context.Context, *models.SceneMarkerFilterType, *models.FindFilterType) int); ok {
r0 = rf(ctx, sceneMarkerFilter, findFilter)
} else {
r0 = ret.Get(0).(int)
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *models.SceneMarkerFilterType, *models.FindFilterType) error); ok {
r1 = rf(ctx, sceneMarkerFilter, findFilter)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Update provides a mock function with given fields: ctx, updatedSceneMarker // Update provides a mock function with given fields: ctx, updatedSceneMarker
func (_m *SceneMarkerReaderWriter) Update(ctx context.Context, updatedSceneMarker *models.SceneMarker) error { func (_m *SceneMarkerReaderWriter) Update(ctx context.Context, updatedSceneMarker *models.SceneMarker) error {
ret := _m.Called(ctx, updatedSceneMarker) ret := _m.Called(ctx, updatedSceneMarker)

View file

@ -731,6 +731,27 @@ func (_m *SceneReaderWriter) Query(ctx context.Context, options models.SceneQuer
return r0, r1 return r0, r1
} }
// QueryCount provides a mock function with given fields: ctx, sceneFilter, findFilter
func (_m *SceneReaderWriter) QueryCount(ctx context.Context, sceneFilter *models.SceneFilterType, findFilter *models.FindFilterType) (int, error) {
ret := _m.Called(ctx, sceneFilter, findFilter)
var r0 int
if rf, ok := ret.Get(0).(func(context.Context, *models.SceneFilterType, *models.FindFilterType) int); ok {
r0 = rf(ctx, sceneFilter, findFilter)
} else {
r0 = ret.Get(0).(int)
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *models.SceneFilterType, *models.FindFilterType) error); ok {
r1 = rf(ctx, sceneFilter, findFilter)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// ResetOCounter provides a mock function with given fields: ctx, id // ResetOCounter provides a mock function with given fields: ctx, id
func (_m *SceneReaderWriter) ResetOCounter(ctx context.Context, id int) (int, error) { func (_m *SceneReaderWriter) ResetOCounter(ctx context.Context, id int) (int, error) {
ret := _m.Called(ctx, id) ret := _m.Called(ctx, id)

View file

@ -37,6 +37,7 @@ type MovieReader interface {
All(ctx context.Context) ([]*Movie, error) All(ctx context.Context) ([]*Movie, error)
Count(ctx context.Context) (int, error) Count(ctx context.Context) (int, error)
Query(ctx context.Context, movieFilter *MovieFilterType, findFilter *FindFilterType) ([]*Movie, int, error) Query(ctx context.Context, movieFilter *MovieFilterType, findFilter *FindFilterType) ([]*Movie, int, error)
QueryCount(ctx context.Context, movieFilter *MovieFilterType, findFilter *FindFilterType) (int, error)
GetFrontImage(ctx context.Context, movieID int) ([]byte, error) GetFrontImage(ctx context.Context, movieID int) ([]byte, error)
HasFrontImage(ctx context.Context, movieID int) (bool, error) HasFrontImage(ctx context.Context, movieID int) (bool, error)
GetBackImage(ctx context.Context, movieID int) ([]byte, error) GetBackImage(ctx context.Context, movieID int) ([]byte, error)

View file

@ -178,6 +178,7 @@ type SceneReader interface {
Wall(ctx context.Context, q *string) ([]*Scene, error) Wall(ctx context.Context, q *string) ([]*Scene, error)
All(ctx context.Context) ([]*Scene, error) All(ctx context.Context) ([]*Scene, error)
Query(ctx context.Context, options SceneQueryOptions) (*SceneQueryResult, error) Query(ctx context.Context, options SceneQueryOptions) (*SceneQueryResult, error)
QueryCount(ctx context.Context, sceneFilter *SceneFilterType, findFilter *FindFilterType) (int, error)
GetCover(ctx context.Context, sceneID int) ([]byte, error) GetCover(ctx context.Context, sceneID int) ([]byte, error)
HasCover(ctx context.Context, sceneID int) (bool, error) HasCover(ctx context.Context, sceneID int) (bool, error)
} }

View file

@ -39,6 +39,7 @@ type SceneMarkerReader interface {
Count(ctx context.Context) (int, error) Count(ctx context.Context) (int, error)
All(ctx context.Context) ([]*SceneMarker, error) All(ctx context.Context) ([]*SceneMarker, error)
Query(ctx context.Context, sceneMarkerFilter *SceneMarkerFilterType, findFilter *FindFilterType) ([]*SceneMarker, int, error) Query(ctx context.Context, sceneMarkerFilter *SceneMarkerFilterType, findFilter *FindFilterType) ([]*SceneMarker, int, error)
QueryCount(ctx context.Context, sceneMarkerFilter *SceneMarkerFilterType, findFilter *FindFilterType) (int, error)
GetTagIDs(ctx context.Context, imageID int) ([]int, error) GetTagIDs(ctx context.Context, imageID int) ([]int, error)
} }

28
pkg/movie/query.go Normal file
View file

@ -0,0 +1,28 @@
package movie
import (
"context"
"strconv"
"github.com/stashapp/stash/pkg/models"
)
type Queryer interface {
Query(ctx context.Context, movieFilter *models.MovieFilterType, findFilter *models.FindFilterType) ([]*models.Movie, int, error)
}
type CountQueryer interface {
QueryCount(ctx context.Context, movieFilter *models.MovieFilterType, findFilter *models.FindFilterType) (int, error)
}
func CountByStudioID(ctx context.Context, r CountQueryer, id int, depth *int) (int, error) {
filter := &models.MovieFilterType{
Studios: &models.HierarchicalMultiCriterionInput{
Value: []string{strconv.Itoa(id)},
Modifier: models.CriterionModifierIncludes,
Depth: depth,
},
}
return r.QueryCount(ctx, filter, nil)
}

View file

@ -15,11 +15,24 @@ type CountQueryer interface {
QueryCount(ctx context.Context, galleryFilter *models.PerformerFilterType, findFilter *models.FindFilterType) (int, error) QueryCount(ctx context.Context, galleryFilter *models.PerformerFilterType, findFilter *models.FindFilterType) (int, error)
} }
func CountByStudioID(ctx context.Context, r CountQueryer, id int) (int, error) { func CountByStudioID(ctx context.Context, r CountQueryer, id int, depth *int) (int, error) {
filter := &models.PerformerFilterType{ filter := &models.PerformerFilterType{
Studios: &models.HierarchicalMultiCriterionInput{ Studios: &models.HierarchicalMultiCriterionInput{
Value: []string{strconv.Itoa(id)}, Value: []string{strconv.Itoa(id)},
Modifier: models.CriterionModifierIncludes, Modifier: models.CriterionModifierIncludes,
Depth: depth,
},
}
return r.QueryCount(ctx, filter, nil)
}
func CountByTagID(ctx context.Context, r CountQueryer, id int, depth *int) (int, error) {
filter := &models.PerformerFilterType{
Tags: &models.HierarchicalMultiCriterionInput{
Value: []string{strconv.Itoa(id)},
Modifier: models.CriterionModifierIncludes,
Depth: depth,
}, },
} }

28
pkg/scene/marker_query.go Normal file
View file

@ -0,0 +1,28 @@
package scene
import (
"context"
"strconv"
"github.com/stashapp/stash/pkg/models"
)
type MarkerQueryer interface {
Query(ctx context.Context, sceneMarkerFilter *models.SceneMarkerFilterType, findFilter *models.FindFilterType) ([]*models.SceneMarker, int, error)
}
type MarkerCountQueryer interface {
QueryCount(ctx context.Context, sceneMarkerFilter *models.SceneMarkerFilterType, findFilter *models.FindFilterType) (int, error)
}
func MarkerCountByTagID(ctx context.Context, r MarkerCountQueryer, id int, depth *int) (int, error) {
filter := &models.SceneMarkerFilterType{
Tags: &models.HierarchicalMultiCriterionInput{
Value: []string{strconv.Itoa(id)},
Modifier: models.CriterionModifierIncludes,
Depth: depth,
},
}
return r.QueryCount(ctx, filter, nil)
}

View file

@ -4,6 +4,7 @@ import (
"context" "context"
"fmt" "fmt"
"path/filepath" "path/filepath"
"strconv"
"strings" "strings"
"github.com/stashapp/stash/pkg/job" "github.com/stashapp/stash/pkg/job"
@ -14,6 +15,10 @@ type Queryer interface {
Query(ctx context.Context, options models.SceneQueryOptions) (*models.SceneQueryResult, error) Query(ctx context.Context, options models.SceneQueryOptions) (*models.SceneQueryResult, error)
} }
type CountQueryer interface {
QueryCount(ctx context.Context, sceneFilter *models.SceneFilterType, findFilter *models.FindFilterType) (int, error)
}
type IDFinder interface { type IDFinder interface {
Find(ctx context.Context, id int) (*models.Scene, error) Find(ctx context.Context, id int) (*models.Scene, error)
FindMany(ctx context.Context, ids []int) ([]*models.Scene, error) FindMany(ctx context.Context, ids []int) ([]*models.Scene, error)
@ -128,3 +133,27 @@ func FilterFromPaths(paths []string) *models.SceneFilterType {
return ret return ret
} }
func CountByStudioID(ctx context.Context, r CountQueryer, id int, depth *int) (int, error) {
filter := &models.SceneFilterType{
Studios: &models.HierarchicalMultiCriterionInput{
Value: []string{strconv.Itoa(id)},
Modifier: models.CriterionModifierIncludes,
Depth: depth,
},
}
return r.QueryCount(ctx, filter, nil)
}
func CountByTagID(ctx context.Context, r CountQueryer, id int, depth *int) (int, error) {
filter := &models.SceneFilterType{
Tags: &models.HierarchicalMultiCriterionInput{
Value: []string{strconv.Itoa(id)},
Modifier: models.CriterionModifierIncludes,
Depth: depth,
},
}
return r.QueryCount(ctx, filter, nil)
}

View file

@ -352,7 +352,7 @@ func (qb *MovieStore) makeFilter(ctx context.Context, movieFilter *models.MovieF
return query return query
} }
func (qb *MovieStore) Query(ctx context.Context, movieFilter *models.MovieFilterType, findFilter *models.FindFilterType) ([]*models.Movie, int, error) { func (qb *MovieStore) makeQuery(ctx context.Context, movieFilter *models.MovieFilterType, findFilter *models.FindFilterType) (*queryBuilder, error) {
if findFilter == nil { if findFilter == nil {
findFilter = &models.FindFilterType{} findFilter = &models.FindFilterType{}
} }
@ -371,10 +371,20 @@ func (qb *MovieStore) Query(ctx context.Context, movieFilter *models.MovieFilter
filter := qb.makeFilter(ctx, movieFilter) filter := qb.makeFilter(ctx, movieFilter)
if err := query.addFilter(filter); err != nil { if err := query.addFilter(filter); err != nil {
return nil, 0, err return nil, err
} }
query.sortAndPagination = qb.getMovieSort(findFilter) + getPagination(findFilter) query.sortAndPagination = qb.getMovieSort(findFilter) + getPagination(findFilter)
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)
if err != nil {
return nil, 0, err
}
idsResult, countResult, err := query.executeFind(ctx) idsResult, countResult, err := query.executeFind(ctx)
if err != nil { if err != nil {
return nil, 0, err return nil, 0, err
@ -388,6 +398,15 @@ func (qb *MovieStore) Query(ctx context.Context, movieFilter *models.MovieFilter
return movies, countResult, nil return movies, countResult, nil
} }
func (qb *MovieStore) QueryCount(ctx context.Context, movieFilter *models.MovieFilterType, findFilter *models.FindFilterType) (int, error) {
query, err := qb.makeQuery(ctx, movieFilter, findFilter)
if err != nil {
return 0, err
}
return query.executeCount(ctx)
}
func movieIsMissingCriterionHandler(qb *MovieStore, isMissing *string) criterionHandlerFunc { func movieIsMissingCriterionHandler(qb *MovieStore, isMissing *string) criterionHandlerFunc {
return func(ctx context.Context, f *filterBuilder) { return func(ctx context.Context, f *filterBuilder) {
if isMissing != nil && *isMissing != "" { if isMissing != nil && *isMissing != "" {

View file

@ -988,10 +988,7 @@ func (qb *SceneStore) addVideoFilesTable(f *filterBuilder) {
f.addLeftJoin(videoFileTable, "", "video_files.file_id = scenes_files.file_id") f.addLeftJoin(videoFileTable, "", "video_files.file_id = scenes_files.file_id")
} }
func (qb *SceneStore) Query(ctx context.Context, options models.SceneQueryOptions) (*models.SceneQueryResult, error) { func (qb *SceneStore) makeQuery(ctx context.Context, sceneFilter *models.SceneFilterType, findFilter *models.FindFilterType) (*queryBuilder, error) {
sceneFilter := options.SceneFilter
findFilter := options.FindFilter
if sceneFilter == nil { if sceneFilter == nil {
sceneFilter = &models.SceneFilterType{} sceneFilter = &models.SceneFilterType{}
} }
@ -1043,7 +1040,16 @@ func (qb *SceneStore) Query(ctx context.Context, options models.SceneQueryOption
qb.setSceneSort(&query, findFilter) qb.setSceneSort(&query, findFilter)
query.sortAndPagination += getPagination(findFilter) query.sortAndPagination += getPagination(findFilter)
result, err := qb.queryGroupedFields(ctx, options, query) return &query, nil
}
func (qb *SceneStore) Query(ctx context.Context, options models.SceneQueryOptions) (*models.SceneQueryResult, error) {
query, err := qb.makeQuery(ctx, options.SceneFilter, options.FindFilter)
if err != nil {
return nil, err
}
result, err := qb.queryGroupedFields(ctx, options, *query)
if err != nil { if err != nil {
return nil, fmt.Errorf("error querying aggregate fields: %w", err) return nil, fmt.Errorf("error querying aggregate fields: %w", err)
} }
@ -1118,6 +1124,15 @@ func (qb *SceneStore) queryGroupedFields(ctx context.Context, options models.Sce
return ret, nil return ret, nil
} }
func (qb *SceneStore) QueryCount(ctx context.Context, sceneFilter *models.SceneFilterType, findFilter *models.FindFilterType) (int, error) {
query, err := qb.makeQuery(ctx, sceneFilter, findFilter)
if err != nil {
return 0, err
}
return query.executeCount(ctx)
}
func sceneFileCountCriterionHandler(qb *SceneStore, fileCount *models.IntCriterionInput) criterionHandlerFunc { func sceneFileCountCriterionHandler(qb *SceneStore, fileCount *models.IntCriterionInput) criterionHandlerFunc {
h := countCriterionHandlerBuilder{ h := countCriterionHandlerBuilder{
primaryTable: sceneTable, primaryTable: sceneTable,

View file

@ -252,8 +252,7 @@ func (qb *SceneMarkerStore) makeFilter(ctx context.Context, sceneMarkerFilter *m
return query return query
} }
func (qb *SceneMarkerStore) makeQuery(ctx context.Context, sceneMarkerFilter *models.SceneMarkerFilterType, findFilter *models.FindFilterType) (*queryBuilder, error) {
func (qb *SceneMarkerStore) Query(ctx context.Context, sceneMarkerFilter *models.SceneMarkerFilterType, findFilter *models.FindFilterType) ([]*models.SceneMarker, int, error) {
if sceneMarkerFilter == nil { if sceneMarkerFilter == nil {
sceneMarkerFilter = &models.SceneMarkerFilterType{} sceneMarkerFilter = &models.SceneMarkerFilterType{}
} }
@ -272,10 +271,20 @@ func (qb *SceneMarkerStore) Query(ctx context.Context, sceneMarkerFilter *models
filter := qb.makeFilter(ctx, sceneMarkerFilter) filter := qb.makeFilter(ctx, sceneMarkerFilter)
if err := query.addFilter(filter); err != nil { if err := query.addFilter(filter); err != nil {
return nil, 0, err return nil, err
} }
query.sortAndPagination = qb.getSceneMarkerSort(&query, findFilter) + getPagination(findFilter) query.sortAndPagination = qb.getSceneMarkerSort(&query, findFilter) + getPagination(findFilter)
return &query, nil
}
func (qb *SceneMarkerStore) Query(ctx context.Context, sceneMarkerFilter *models.SceneMarkerFilterType, findFilter *models.FindFilterType) ([]*models.SceneMarker, int, error) {
query, err := qb.makeQuery(ctx, sceneMarkerFilter, findFilter)
if err != nil {
return nil, 0, err
}
idsResult, countResult, err := query.executeFind(ctx) idsResult, countResult, err := query.executeFind(ctx)
if err != nil { if err != nil {
return nil, 0, err return nil, 0, err
@ -289,6 +298,15 @@ func (qb *SceneMarkerStore) Query(ctx context.Context, sceneMarkerFilter *models
return sceneMarkers, countResult, nil return sceneMarkers, countResult, nil
} }
func (qb *SceneMarkerStore) QueryCount(ctx context.Context, sceneMarkerFilter *models.SceneMarkerFilterType, findFilter *models.FindFilterType) (int, error) {
query, err := qb.makeQuery(ctx, sceneMarkerFilter, findFilter)
if err != nil {
return 0, err
}
return query.executeCount(ctx)
}
func sceneMarkerTagIDCriterionHandler(qb *SceneMarkerStore, tagID *string) criterionHandlerFunc { func sceneMarkerTagIDCriterionHandler(qb *SceneMarkerStore, tagID *string) criterionHandlerFunc {
return func(ctx context.Context, f *filterBuilder) { return func(ctx context.Context, f *filterBuilder) {
if tagID != nil { if tagID != nil {

View file

@ -211,9 +211,7 @@ export const GalleryPage: React.FC<IProps> = ({ gallery }) => {
<Nav.Item> <Nav.Item>
<Nav.Link eventKey="gallery-file-info-panel"> <Nav.Link eventKey="gallery-file-info-panel">
<FormattedMessage id="file_info" /> <FormattedMessage id="file_info" />
{gallery.files.length > 1 && ( <Counter count={gallery.files.length} hideZero hideOne />
<Counter count={gallery.files.length ?? 0} />
)}
</Nav.Link> </Nav.Link>
</Nav.Item> </Nav.Item>
) : undefined} ) : undefined}

View file

@ -195,9 +195,7 @@ export const Image: React.FC = () => {
<Nav.Item> <Nav.Item>
<Nav.Link eventKey="image-file-info-panel"> <Nav.Link eventKey="image-file-info-panel">
<FormattedMessage id="file_info" /> <FormattedMessage id="file_info" />
{image.visual_files.length > 1 && ( <Counter count={image.visual_files.length} hideZero hideOne />
<Counter count={image.visual_files.length ?? 0} />
)}
</Nav.Link> </Nav.Link>
</Nav.Item> </Nav.Item>
<Nav.Item> <Nav.Item>

View file

@ -227,13 +227,14 @@ const PerformerPage: React.FC<IProps> = ({ performer }) => {
<Tab <Tab
eventKey="scenes" eventKey="scenes"
title={ title={
<React.Fragment> <>
{intl.formatMessage({ id: "scenes" })} {intl.formatMessage({ id: "scenes" })}
<Counter <Counter
abbreviateCounter={abbreviateCounter} abbreviateCounter={abbreviateCounter}
count={performer.scene_count ?? 0} count={performer.scene_count}
hideZero
/> />
</React.Fragment> </>
} }
> >
<PerformerScenesPanel <PerformerScenesPanel
@ -244,13 +245,14 @@ const PerformerPage: React.FC<IProps> = ({ performer }) => {
<Tab <Tab
eventKey="galleries" eventKey="galleries"
title={ title={
<React.Fragment> <>
{intl.formatMessage({ id: "galleries" })} {intl.formatMessage({ id: "galleries" })}
<Counter <Counter
abbreviateCounter={abbreviateCounter} abbreviateCounter={abbreviateCounter}
count={performer.gallery_count ?? 0} count={performer.gallery_count}
hideZero
/> />
</React.Fragment> </>
} }
> >
<PerformerGalleriesPanel <PerformerGalleriesPanel
@ -261,13 +263,14 @@ const PerformerPage: React.FC<IProps> = ({ performer }) => {
<Tab <Tab
eventKey="images" eventKey="images"
title={ title={
<React.Fragment> <>
{intl.formatMessage({ id: "images" })} {intl.formatMessage({ id: "images" })}
<Counter <Counter
abbreviateCounter={abbreviateCounter} abbreviateCounter={abbreviateCounter}
count={performer.image_count ?? 0} count={performer.image_count}
hideZero
/> />
</React.Fragment> </>
} }
> >
<PerformerImagesPanel <PerformerImagesPanel
@ -278,13 +281,14 @@ const PerformerPage: React.FC<IProps> = ({ performer }) => {
<Tab <Tab
eventKey="movies" eventKey="movies"
title={ title={
<React.Fragment> <>
{intl.formatMessage({ id: "movies" })} {intl.formatMessage({ id: "movies" })}
<Counter <Counter
abbreviateCounter={abbreviateCounter} abbreviateCounter={abbreviateCounter}
count={performer.movie_count ?? 0} count={performer.movie_count}
hideZero
/> />
</React.Fragment> </>
} }
> >
<PerformerMoviesPanel <PerformerMoviesPanel
@ -295,13 +299,14 @@ const PerformerPage: React.FC<IProps> = ({ performer }) => {
<Tab <Tab
eventKey="appearswith" eventKey="appearswith"
title={ title={
<React.Fragment> <>
{intl.formatMessage({ id: "appears_with" })} {intl.formatMessage({ id: "appears_with" })}
<Counter <Counter
abbreviateCounter={abbreviateCounter} abbreviateCounter={abbreviateCounter}
count={performer.performer_count ?? 0} count={performer.performer_count}
hideZero
/> />
</React.Fragment> </>
} }
> >
<PerformerAppearsWithPanel <PerformerAppearsWithPanel

View file

@ -394,9 +394,7 @@ const ScenePage: React.FC<IProps> = ({
<Nav.Item> <Nav.Item>
<Nav.Link eventKey="scene-file-info-panel"> <Nav.Link eventKey="scene-file-info-panel">
<FormattedMessage id="file_info" /> <FormattedMessage id="file_info" />
{scene.files.length > 1 && ( <Counter count={scene.files.length} hideZero hideOne />
<Counter count={scene.files.length ?? 0} />
)}
</Nav.Link> </Nav.Link>
</Nav.Item> </Nav.Item>
<Nav.Item> <Nav.Item>

View file

@ -6,16 +6,23 @@ import TextUtils from "src/utils/text";
interface IProps { interface IProps {
abbreviateCounter?: boolean; abbreviateCounter?: boolean;
count: number; count: number;
hideZero?: boolean;
hideOne?: boolean;
} }
export const Counter: React.FC<IProps> = ({ export const Counter: React.FC<IProps> = ({
abbreviateCounter = false, abbreviateCounter = false,
count, count,
hideZero = false,
hideOne = false,
}) => { }) => {
const intl = useIntl(); const intl = useIntl();
if (hideZero && count === 0) return null;
if (hideOne && count === 1) return null;
if (abbreviateCounter) { if (abbreviateCounter) {
const formated = TextUtils.abbreviateCounter(count); const formatted = TextUtils.abbreviateCounter(count);
return ( return (
<Badge <Badge
className="left-spacing" className="left-spacing"
@ -24,10 +31,10 @@ export const Counter: React.FC<IProps> = ({
data-value={intl.formatNumber(count)} data-value={intl.formatNumber(count)}
> >
<FormattedNumber <FormattedNumber
value={formated.size} value={formatted.size}
maximumFractionDigits={formated.digits} maximumFractionDigits={formatted.digits}
/> />
{formated.unit} {formatted.unit}
</Badge> </Badge>
); );
} else { } else {

View file

@ -67,6 +67,19 @@ const StudioPage: React.FC<IProps> = ({ studio }) => {
const [updateStudio] = useStudioUpdate(); const [updateStudio] = useStudioUpdate();
const [deleteStudio] = useStudioDestroy({ id: studio.id }); const [deleteStudio] = useStudioDestroy({ id: studio.id });
const showAllCounts = (configuration?.ui as IUIConfig)
?.showChildStudioContent;
const sceneCount =
(showAllCounts ? studio.scene_count_all : studio.scene_count) ?? 0;
const galleryCount =
(showAllCounts ? studio.gallery_count_all : studio.gallery_count) ?? 0;
const imageCount =
(showAllCounts ? studio.image_count_all : studio.image_count) ?? 0;
const performerCount =
(showAllCounts ? studio.performer_count_all : studio.performer_count) ?? 0;
const movieCount =
(showAllCounts ? studio.movie_count_all : studio.movie_count) ?? 0;
// set up hotkeys // set up hotkeys
useEffect(() => { useEffect(() => {
Mousetrap.bind("e", () => toggleEditing()); Mousetrap.bind("e", () => toggleEditing());
@ -253,13 +266,14 @@ const StudioPage: React.FC<IProps> = ({ studio }) => {
<Tab <Tab
eventKey="scenes" eventKey="scenes"
title={ title={
<React.Fragment> <>
{intl.formatMessage({ id: "scenes" })} {intl.formatMessage({ id: "scenes" })}
<Counter <Counter
abbreviateCounter={abbreviateCounter} abbreviateCounter={abbreviateCounter}
count={studio.scene_count ?? 0} count={sceneCount}
hideZero
/> />
</React.Fragment> </>
} }
> >
<StudioScenesPanel <StudioScenesPanel
@ -270,13 +284,14 @@ const StudioPage: React.FC<IProps> = ({ studio }) => {
<Tab <Tab
eventKey="galleries" eventKey="galleries"
title={ title={
<React.Fragment> <>
{intl.formatMessage({ id: "galleries" })} {intl.formatMessage({ id: "galleries" })}
<Counter <Counter
abbreviateCounter={abbreviateCounter} abbreviateCounter={abbreviateCounter}
count={studio.gallery_count ?? 0} count={galleryCount}
hideZero
/> />
</React.Fragment> </>
} }
> >
<StudioGalleriesPanel <StudioGalleriesPanel
@ -287,13 +302,14 @@ const StudioPage: React.FC<IProps> = ({ studio }) => {
<Tab <Tab
eventKey="images" eventKey="images"
title={ title={
<React.Fragment> <>
{intl.formatMessage({ id: "images" })} {intl.formatMessage({ id: "images" })}
<Counter <Counter
abbreviateCounter={abbreviateCounter} abbreviateCounter={abbreviateCounter}
count={studio.image_count ?? 0} count={imageCount}
hideZero
/> />
</React.Fragment> </>
} }
> >
<StudioImagesPanel <StudioImagesPanel
@ -304,13 +320,14 @@ const StudioPage: React.FC<IProps> = ({ studio }) => {
<Tab <Tab
eventKey="performers" eventKey="performers"
title={ title={
<React.Fragment> <>
{intl.formatMessage({ id: "performers" })} {intl.formatMessage({ id: "performers" })}
<Counter <Counter
abbreviateCounter={abbreviateCounter} abbreviateCounter={abbreviateCounter}
count={studio.performer_count ?? 0} count={performerCount}
hideZero
/> />
</React.Fragment> </>
} }
> >
<StudioPerformersPanel <StudioPerformersPanel
@ -321,13 +338,14 @@ const StudioPage: React.FC<IProps> = ({ studio }) => {
<Tab <Tab
eventKey="movies" eventKey="movies"
title={ title={
<React.Fragment> <>
{intl.formatMessage({ id: "movies" })} {intl.formatMessage({ id: "movies" })}
<Counter <Counter
abbreviateCounter={abbreviateCounter} abbreviateCounter={abbreviateCounter}
count={studio.movie_count ?? 0} count={movieCount}
hideZero
/> />
</React.Fragment> </>
} }
> >
<StudioMoviesPanel <StudioMoviesPanel
@ -338,13 +356,14 @@ const StudioPage: React.FC<IProps> = ({ studio }) => {
<Tab <Tab
eventKey="childstudios" eventKey="childstudios"
title={ title={
<React.Fragment> <>
{intl.formatMessage({ id: "subsidiary_studios" })} {intl.formatMessage({ id: "subsidiary_studios" })}
<Counter <Counter
abbreviateCounter={false} abbreviateCounter={false}
count={studio.child_studios?.length ?? 0} count={studio.child_studios.length}
hideZero
/> />
</React.Fragment> </>
} }
> >
<StudioChildrenPanel <StudioChildrenPanel

View file

@ -72,6 +72,18 @@ const TagPage: React.FC<IProps> = ({ tag }) => {
const [updateTag] = useTagUpdate(); const [updateTag] = useTagUpdate();
const [deleteTag] = useTagDestroy({ id: tag.id }); const [deleteTag] = useTagDestroy({ id: tag.id });
const showAllCounts = (configuration?.ui as IUIConfig)?.showChildTagContent;
const sceneCount =
(showAllCounts ? tag.scene_count_all : tag.scene_count) ?? 0;
const imageCount =
(showAllCounts ? tag.image_count_all : tag.image_count) ?? 0;
const galleryCount =
(showAllCounts ? tag.gallery_count_all : tag.gallery_count) ?? 0;
const sceneMarkerCount =
(showAllCounts ? tag.scene_marker_count_all : tag.scene_marker_count) ?? 0;
const performerCount =
(showAllCounts ? tag.performer_count_all : tag.performer_count) ?? 0;
const activeTabKey = const activeTabKey =
tab === "markers" || tab === "markers" ||
tab === "images" || tab === "images" ||
@ -325,13 +337,14 @@ const TagPage: React.FC<IProps> = ({ tag }) => {
<Tab <Tab
eventKey="scenes" eventKey="scenes"
title={ title={
<React.Fragment> <>
{intl.formatMessage({ id: "scenes" })} {intl.formatMessage({ id: "scenes" })}
<Counter <Counter
abbreviateCounter={abbreviateCounter} abbreviateCounter={abbreviateCounter}
count={tag.scene_count ?? 0} count={sceneCount}
hideZero
/> />
</React.Fragment> </>
} }
> >
<TagScenesPanel active={activeTabKey == "scenes"} tag={tag} /> <TagScenesPanel active={activeTabKey == "scenes"} tag={tag} />
@ -339,13 +352,14 @@ const TagPage: React.FC<IProps> = ({ tag }) => {
<Tab <Tab
eventKey="images" eventKey="images"
title={ title={
<React.Fragment> <>
{intl.formatMessage({ id: "images" })} {intl.formatMessage({ id: "images" })}
<Counter <Counter
abbreviateCounter={abbreviateCounter} abbreviateCounter={abbreviateCounter}
count={tag.image_count ?? 0} count={imageCount}
hideZero
/> />
</React.Fragment> </>
} }
> >
<TagImagesPanel active={activeTabKey == "images"} tag={tag} /> <TagImagesPanel active={activeTabKey == "images"} tag={tag} />
@ -353,13 +367,14 @@ const TagPage: React.FC<IProps> = ({ tag }) => {
<Tab <Tab
eventKey="galleries" eventKey="galleries"
title={ title={
<React.Fragment> <>
{intl.formatMessage({ id: "galleries" })} {intl.formatMessage({ id: "galleries" })}
<Counter <Counter
abbreviateCounter={abbreviateCounter} abbreviateCounter={abbreviateCounter}
count={tag.gallery_count ?? 0} count={galleryCount}
hideZero
/> />
</React.Fragment> </>
} }
> >
<TagGalleriesPanel <TagGalleriesPanel
@ -370,13 +385,14 @@ const TagPage: React.FC<IProps> = ({ tag }) => {
<Tab <Tab
eventKey="markers" eventKey="markers"
title={ title={
<React.Fragment> <>
{intl.formatMessage({ id: "markers" })} {intl.formatMessage({ id: "markers" })}
<Counter <Counter
abbreviateCounter={abbreviateCounter} abbreviateCounter={abbreviateCounter}
count={tag.scene_marker_count ?? 0} count={sceneMarkerCount}
hideZero
/> />
</React.Fragment> </>
} }
> >
<TagMarkersPanel active={activeTabKey == "markers"} tag={tag} /> <TagMarkersPanel active={activeTabKey == "markers"} tag={tag} />
@ -384,13 +400,14 @@ const TagPage: React.FC<IProps> = ({ tag }) => {
<Tab <Tab
eventKey="performers" eventKey="performers"
title={ title={
<React.Fragment> <>
{intl.formatMessage({ id: "performers" })} {intl.formatMessage({ id: "performers" })}
<Counter <Counter
abbreviateCounter={abbreviateCounter} abbreviateCounter={abbreviateCounter}
count={tag.performer_count ?? 0} count={performerCount}
hideZero
/> />
</React.Fragment> </>
} }
> >
<TagPerformersPanel <TagPerformersPanel