mirror of
https://github.com/stashapp/stash.git
synced 2025-12-18 04:44:37 +03:00
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:
@@ -35,22 +35,24 @@ func CountByPerformerID(ctx context.Context, r CountQueryer, id int) (int, error
|
||||
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{
|
||||
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) (int, error) {
|
||||
func CountByTagID(ctx context.Context, r CountQueryer, id int, depth *int) (int, error) {
|
||||
filter := &models.GalleryFilterType{
|
||||
Tags: &models.HierarchicalMultiCriterionInput{
|
||||
Value: []string{strconv.Itoa(id)},
|
||||
Modifier: models.CriterionModifierIncludes,
|
||||
Depth: depth,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -52,22 +52,24 @@ func CountByPerformerID(ctx context.Context, r CountQueryer, id int) (int, error
|
||||
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{
|
||||
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) (int, error) {
|
||||
func CountByTagID(ctx context.Context, r CountQueryer, id int, depth *int) (int, error) {
|
||||
filter := &models.ImageFilterType{
|
||||
Tags: &models.HierarchicalMultiCriterionInput{
|
||||
Value: []string{strconv.Itoa(id)},
|
||||
Modifier: models.CriterionModifierIncludes,
|
||||
Depth: depth,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -384,6 +384,27 @@ 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)
|
||||
|
||||
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
|
||||
func (_m *MovieReaderWriter) Update(ctx context.Context, updatedMovie *models.Movie) error {
|
||||
ret := _m.Called(ctx, updatedMovie)
|
||||
|
||||
@@ -252,6 +252,27 @@ func (_m *SceneMarkerReaderWriter) Query(ctx context.Context, sceneMarkerFilter
|
||||
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
|
||||
func (_m *SceneMarkerReaderWriter) Update(ctx context.Context, updatedSceneMarker *models.SceneMarker) error {
|
||||
ret := _m.Called(ctx, updatedSceneMarker)
|
||||
|
||||
@@ -731,6 +731,27 @@ func (_m *SceneReaderWriter) Query(ctx context.Context, options models.SceneQuer
|
||||
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
|
||||
func (_m *SceneReaderWriter) ResetOCounter(ctx context.Context, id int) (int, error) {
|
||||
ret := _m.Called(ctx, id)
|
||||
|
||||
@@ -37,6 +37,7 @@ type MovieReader interface {
|
||||
All(ctx context.Context) ([]*Movie, error)
|
||||
Count(ctx context.Context) (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)
|
||||
HasFrontImage(ctx context.Context, movieID int) (bool, error)
|
||||
GetBackImage(ctx context.Context, movieID int) ([]byte, error)
|
||||
|
||||
@@ -178,6 +178,7 @@ type SceneReader interface {
|
||||
Wall(ctx context.Context, q *string) ([]*Scene, error)
|
||||
All(ctx context.Context) ([]*Scene, 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)
|
||||
HasCover(ctx context.Context, sceneID int) (bool, error)
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ type SceneMarkerReader interface {
|
||||
Count(ctx context.Context) (int, error)
|
||||
All(ctx context.Context) ([]*SceneMarker, 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)
|
||||
}
|
||||
|
||||
|
||||
28
pkg/movie/query.go
Normal file
28
pkg/movie/query.go
Normal 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)
|
||||
}
|
||||
@@ -15,11 +15,24 @@ type CountQueryer interface {
|
||||
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{
|
||||
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.PerformerFilterType{
|
||||
Tags: &models.HierarchicalMultiCriterionInput{
|
||||
Value: []string{strconv.Itoa(id)},
|
||||
Modifier: models.CriterionModifierIncludes,
|
||||
Depth: depth,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
28
pkg/scene/marker_query.go
Normal file
28
pkg/scene/marker_query.go
Normal 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)
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/stashapp/stash/pkg/job"
|
||||
@@ -14,6 +15,10 @@ type Queryer interface {
|
||||
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 {
|
||||
Find(ctx context.Context, id 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
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -352,7 +352,7 @@ func (qb *MovieStore) makeFilter(ctx context.Context, movieFilter *models.MovieF
|
||||
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 {
|
||||
findFilter = &models.FindFilterType{}
|
||||
}
|
||||
@@ -371,10 +371,20 @@ func (qb *MovieStore) Query(ctx context.Context, movieFilter *models.MovieFilter
|
||||
filter := qb.makeFilter(ctx, movieFilter)
|
||||
|
||||
if err := query.addFilter(filter); err != nil {
|
||||
return nil, 0, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
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)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
@@ -388,6 +398,15 @@ func (qb *MovieStore) Query(ctx context.Context, movieFilter *models.MovieFilter
|
||||
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 {
|
||||
return func(ctx context.Context, f *filterBuilder) {
|
||||
if isMissing != nil && *isMissing != "" {
|
||||
|
||||
@@ -988,10 +988,7 @@ func (qb *SceneStore) addVideoFilesTable(f *filterBuilder) {
|
||||
f.addLeftJoin(videoFileTable, "", "video_files.file_id = scenes_files.file_id")
|
||||
}
|
||||
|
||||
func (qb *SceneStore) Query(ctx context.Context, options models.SceneQueryOptions) (*models.SceneQueryResult, error) {
|
||||
sceneFilter := options.SceneFilter
|
||||
findFilter := options.FindFilter
|
||||
|
||||
func (qb *SceneStore) makeQuery(ctx context.Context, sceneFilter *models.SceneFilterType, findFilter *models.FindFilterType) (*queryBuilder, error) {
|
||||
if sceneFilter == nil {
|
||||
sceneFilter = &models.SceneFilterType{}
|
||||
}
|
||||
@@ -1043,7 +1040,16 @@ func (qb *SceneStore) Query(ctx context.Context, options models.SceneQueryOption
|
||||
qb.setSceneSort(&query, 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 {
|
||||
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
|
||||
}
|
||||
|
||||
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 {
|
||||
h := countCriterionHandlerBuilder{
|
||||
primaryTable: sceneTable,
|
||||
|
||||
@@ -252,8 +252,7 @@ func (qb *SceneMarkerStore) makeFilter(ctx context.Context, sceneMarkerFilter *m
|
||||
|
||||
return query
|
||||
}
|
||||
|
||||
func (qb *SceneMarkerStore) Query(ctx context.Context, sceneMarkerFilter *models.SceneMarkerFilterType, findFilter *models.FindFilterType) ([]*models.SceneMarker, int, error) {
|
||||
func (qb *SceneMarkerStore) makeQuery(ctx context.Context, sceneMarkerFilter *models.SceneMarkerFilterType, findFilter *models.FindFilterType) (*queryBuilder, error) {
|
||||
if sceneMarkerFilter == nil {
|
||||
sceneMarkerFilter = &models.SceneMarkerFilterType{}
|
||||
}
|
||||
@@ -272,10 +271,20 @@ func (qb *SceneMarkerStore) Query(ctx context.Context, sceneMarkerFilter *models
|
||||
filter := qb.makeFilter(ctx, sceneMarkerFilter)
|
||||
|
||||
if err := query.addFilter(filter); err != nil {
|
||||
return nil, 0, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
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)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
@@ -289,6 +298,15 @@ func (qb *SceneMarkerStore) Query(ctx context.Context, sceneMarkerFilter *models
|
||||
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 {
|
||||
return func(ctx context.Context, f *filterBuilder) {
|
||||
if tagID != nil {
|
||||
|
||||
Reference in New Issue
Block a user