mirror of
https://github.com/stashapp/stash.git
synced 2025-12-17 12:24:38 +03:00
Show duration and filesize in results (#1776)
* Add new query interface * Refactor query builder * Change Query interface * Return duration and filesize in scene query * Adjust UI for scene metadata * Introduce new image query interface * Change image Query interface * Add megapixels and size to image query * Update image UI Co-authored-by: WithoutPants <53250216+WithoutPants@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
1b411e3f43
commit
4dd56c3d82
@@ -1,6 +1,8 @@
|
|||||||
query FindImages($filter: FindFilterType, $image_filter: ImageFilterType, $image_ids: [Int!]) {
|
query FindImages($filter: FindFilterType, $image_filter: ImageFilterType, $image_ids: [Int!]) {
|
||||||
findImages(filter: $filter, image_filter: $image_filter, image_ids: $image_ids) {
|
findImages(filter: $filter, image_filter: $image_filter, image_ids: $image_ids) {
|
||||||
count
|
count
|
||||||
|
megapixels
|
||||||
|
filesize
|
||||||
images {
|
images {
|
||||||
...SlimImageData
|
...SlimImageData
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
query FindScenes($filter: FindFilterType, $scene_filter: SceneFilterType, $scene_ids: [Int!]) {
|
query FindScenes($filter: FindFilterType, $scene_filter: SceneFilterType, $scene_ids: [Int!]) {
|
||||||
findScenes(filter: $filter, scene_filter: $scene_filter, scene_ids: $scene_ids) {
|
findScenes(filter: $filter, scene_filter: $scene_filter, scene_ids: $scene_ids) {
|
||||||
count
|
count
|
||||||
|
filesize
|
||||||
|
duration
|
||||||
scenes {
|
scenes {
|
||||||
...SlimSceneData
|
...SlimSceneData
|
||||||
}
|
}
|
||||||
@@ -10,6 +12,8 @@ query FindScenes($filter: FindFilterType, $scene_filter: SceneFilterType, $scene
|
|||||||
query FindScenesByPathRegex($filter: FindFilterType) {
|
query FindScenesByPathRegex($filter: FindFilterType) {
|
||||||
findScenesByPathRegex(filter: $filter) {
|
findScenesByPathRegex(filter: $filter) {
|
||||||
count
|
count
|
||||||
|
filesize
|
||||||
|
duration
|
||||||
scenes {
|
scenes {
|
||||||
...SlimSceneData
|
...SlimSceneData
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,5 +70,9 @@ input ImagesDestroyInput {
|
|||||||
|
|
||||||
type FindImagesResultType {
|
type FindImagesResultType {
|
||||||
count: Int!
|
count: Int!
|
||||||
|
"""Total megapixels of the images"""
|
||||||
|
megapixels: Float!
|
||||||
|
"""Total file size in bytes"""
|
||||||
|
filesize: Int!
|
||||||
images: [Image!]!
|
images: [Image!]!
|
||||||
}
|
}
|
||||||
@@ -120,6 +120,10 @@ input ScenesDestroyInput {
|
|||||||
|
|
||||||
type FindScenesResultType {
|
type FindScenesResultType {
|
||||||
count: Int!
|
count: Int!
|
||||||
|
"""Total duration in seconds"""
|
||||||
|
duration: Float!
|
||||||
|
"""Total file size in bytes"""
|
||||||
|
filesize: Int!
|
||||||
scenes: [Scene!]!
|
scenes: [Scene!]!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,9 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/99designs/gqlgen/graphql"
|
||||||
"github.com/stashapp/stash/pkg/models"
|
"github.com/stashapp/stash/pkg/models"
|
||||||
|
"github.com/stashapp/stash/pkg/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (r *queryResolver) FindImage(ctx context.Context, id *string, checksum *string) (*models.Image, error) {
|
func (r *queryResolver) FindImage(ctx context.Context, id *string, checksum *string) (*models.Image, error) {
|
||||||
@@ -39,14 +41,32 @@ func (r *queryResolver) FindImage(ctx context.Context, id *string, checksum *str
|
|||||||
func (r *queryResolver) FindImages(ctx context.Context, imageFilter *models.ImageFilterType, imageIds []int, filter *models.FindFilterType) (ret *models.FindImagesResultType, err error) {
|
func (r *queryResolver) FindImages(ctx context.Context, imageFilter *models.ImageFilterType, imageIds []int, filter *models.FindFilterType) (ret *models.FindImagesResultType, err error) {
|
||||||
if err := r.withReadTxn(ctx, func(repo models.ReaderRepository) error {
|
if err := r.withReadTxn(ctx, func(repo models.ReaderRepository) error {
|
||||||
qb := repo.Image()
|
qb := repo.Image()
|
||||||
images, total, err := qb.Query(imageFilter, filter)
|
|
||||||
|
fields := graphql.CollectAllFields(ctx)
|
||||||
|
|
||||||
|
result, err := qb.Query(models.ImageQueryOptions{
|
||||||
|
QueryOptions: models.QueryOptions{
|
||||||
|
FindFilter: filter,
|
||||||
|
Count: utils.StrInclude(fields, "count"),
|
||||||
|
},
|
||||||
|
ImageFilter: imageFilter,
|
||||||
|
Megapixels: utils.StrInclude(fields, "megapixels"),
|
||||||
|
TotalSize: utils.StrInclude(fields, "filesize"),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
images, err := result.Resolve()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = &models.FindImagesResultType{
|
ret = &models.FindImagesResultType{
|
||||||
Count: total,
|
Count: result.Count,
|
||||||
Images: images,
|
Images: images,
|
||||||
|
Megapixels: result.Megapixels,
|
||||||
|
Filesize: result.TotalSize,
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -4,8 +4,10 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/99designs/gqlgen/graphql"
|
||||||
"github.com/stashapp/stash/pkg/manager"
|
"github.com/stashapp/stash/pkg/manager"
|
||||||
"github.com/stashapp/stash/pkg/models"
|
"github.com/stashapp/stash/pkg/models"
|
||||||
|
"github.com/stashapp/stash/pkg/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (r *queryResolver) FindScene(ctx context.Context, id *string, checksum *string) (*models.Scene, error) {
|
func (r *queryResolver) FindScene(ctx context.Context, id *string, checksum *string) (*models.Scene, error) {
|
||||||
@@ -65,16 +67,34 @@ func (r *queryResolver) FindSceneByHash(ctx context.Context, input models.SceneH
|
|||||||
func (r *queryResolver) FindScenes(ctx context.Context, sceneFilter *models.SceneFilterType, sceneIDs []int, filter *models.FindFilterType) (ret *models.FindScenesResultType, err error) {
|
func (r *queryResolver) FindScenes(ctx context.Context, sceneFilter *models.SceneFilterType, sceneIDs []int, filter *models.FindFilterType) (ret *models.FindScenesResultType, err error) {
|
||||||
if err := r.withReadTxn(ctx, func(repo models.ReaderRepository) error {
|
if err := r.withReadTxn(ctx, func(repo models.ReaderRepository) error {
|
||||||
var scenes []*models.Scene
|
var scenes []*models.Scene
|
||||||
var total int
|
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
|
fields := graphql.CollectAllFields(ctx)
|
||||||
|
result := &models.SceneQueryResult{}
|
||||||
|
|
||||||
if len(sceneIDs) > 0 {
|
if len(sceneIDs) > 0 {
|
||||||
scenes, err = repo.Scene().FindMany(sceneIDs)
|
scenes, err = repo.Scene().FindMany(sceneIDs)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
total = len(scenes)
|
result.Count = len(scenes)
|
||||||
|
for _, s := range scenes {
|
||||||
|
result.TotalDuration += s.Duration.Float64
|
||||||
|
size, _ := strconv.Atoi(s.Size.String)
|
||||||
|
result.TotalSize += size
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
scenes, total, err = repo.Scene().Query(sceneFilter, filter)
|
result, err = repo.Scene().Query(models.SceneQueryOptions{
|
||||||
|
QueryOptions: models.QueryOptions{
|
||||||
|
FindFilter: filter,
|
||||||
|
Count: utils.StrInclude(fields, "count"),
|
||||||
|
},
|
||||||
|
SceneFilter: sceneFilter,
|
||||||
|
TotalDuration: utils.StrInclude(fields, "duration"),
|
||||||
|
TotalSize: utils.StrInclude(fields, "filesize"),
|
||||||
|
})
|
||||||
|
if err == nil {
|
||||||
|
scenes, err = result.Resolve()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -82,8 +102,10 @@ func (r *queryResolver) FindScenes(ctx context.Context, sceneFilter *models.Scen
|
|||||||
}
|
}
|
||||||
|
|
||||||
ret = &models.FindScenesResultType{
|
ret = &models.FindScenesResultType{
|
||||||
Count: total,
|
Count: result.Count,
|
||||||
Scenes: scenes,
|
Scenes: scenes,
|
||||||
|
Duration: result.TotalDuration,
|
||||||
|
Filesize: result.TotalSize,
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -114,14 +136,31 @@ func (r *queryResolver) FindScenesByPathRegex(ctx context.Context, filter *model
|
|||||||
queryFilter.Q = nil
|
queryFilter.Q = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
scenes, total, err := repo.Scene().Query(sceneFilter, queryFilter)
|
fields := graphql.CollectAllFields(ctx)
|
||||||
|
|
||||||
|
result, err := repo.Scene().Query(models.SceneQueryOptions{
|
||||||
|
QueryOptions: models.QueryOptions{
|
||||||
|
FindFilter: queryFilter,
|
||||||
|
Count: utils.StrInclude(fields, "count"),
|
||||||
|
},
|
||||||
|
SceneFilter: sceneFilter,
|
||||||
|
TotalDuration: utils.StrInclude(fields, "duration"),
|
||||||
|
TotalSize: utils.StrInclude(fields, "filesize"),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
scenes, err := result.Resolve()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = &models.FindScenesResultType{
|
ret = &models.FindScenesResultType{
|
||||||
Count: total,
|
Count: result.Count,
|
||||||
Scenes: scenes,
|
Scenes: scenes,
|
||||||
|
Duration: result.TotalDuration,
|
||||||
|
Filesize: result.TotalSize,
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -3,8 +3,10 @@ package autotag
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stashapp/stash/pkg/image"
|
||||||
"github.com/stashapp/stash/pkg/models"
|
"github.com/stashapp/stash/pkg/models"
|
||||||
"github.com/stashapp/stash/pkg/models/mocks"
|
"github.com/stashapp/stash/pkg/models/mocks"
|
||||||
|
"github.com/stashapp/stash/pkg/scene"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -70,7 +72,8 @@ func testPerformerScenes(t *testing.T, performerName, expectedRegex string) {
|
|||||||
PerPage: &perPage,
|
PerPage: &perPage,
|
||||||
}
|
}
|
||||||
|
|
||||||
mockSceneReader.On("Query", expectedSceneFilter, expectedFindFilter).Return(scenes, len(scenes), nil).Once()
|
mockSceneReader.On("Query", scene.QueryOptions(expectedSceneFilter, expectedFindFilter, false)).
|
||||||
|
Return(mocks.SceneQueryResult(scenes, len(scenes)), nil).Once()
|
||||||
|
|
||||||
for i := range matchingPaths {
|
for i := range matchingPaths {
|
||||||
sceneID := i + 1
|
sceneID := i + 1
|
||||||
@@ -144,7 +147,8 @@ func testPerformerImages(t *testing.T, performerName, expectedRegex string) {
|
|||||||
PerPage: &perPage,
|
PerPage: &perPage,
|
||||||
}
|
}
|
||||||
|
|
||||||
mockImageReader.On("Query", expectedImageFilter, expectedFindFilter).Return(images, len(images), nil).Once()
|
mockImageReader.On("Query", image.QueryOptions(expectedImageFilter, expectedFindFilter, false)).
|
||||||
|
Return(mocks.ImageQueryResult(images, len(images)), nil).Once()
|
||||||
|
|
||||||
for i := range matchingPaths {
|
for i := range matchingPaths {
|
||||||
imageID := i + 1
|
imageID := i + 1
|
||||||
|
|||||||
@@ -3,8 +3,10 @@ package autotag
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stashapp/stash/pkg/image"
|
||||||
"github.com/stashapp/stash/pkg/models"
|
"github.com/stashapp/stash/pkg/models"
|
||||||
"github.com/stashapp/stash/pkg/models/mocks"
|
"github.com/stashapp/stash/pkg/models/mocks"
|
||||||
|
"github.com/stashapp/stash/pkg/scene"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -111,11 +113,12 @@ func testStudioScenes(t *testing.T, tc testStudioCase) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// if alias provided, then don't find by name
|
// if alias provided, then don't find by name
|
||||||
onNameQuery := mockSceneReader.On("Query", expectedSceneFilter, expectedFindFilter)
|
onNameQuery := mockSceneReader.On("Query", scene.QueryOptions(expectedSceneFilter, expectedFindFilter, false))
|
||||||
|
|
||||||
if aliasName == "" {
|
if aliasName == "" {
|
||||||
onNameQuery.Return(scenes, len(scenes), nil).Once()
|
onNameQuery.Return(mocks.SceneQueryResult(scenes, len(scenes)), nil).Once()
|
||||||
} else {
|
} else {
|
||||||
onNameQuery.Return(nil, 0, nil).Once()
|
onNameQuery.Return(mocks.SceneQueryResult(nil, 0), nil).Once()
|
||||||
|
|
||||||
expectedAliasFilter := &models.SceneFilterType{
|
expectedAliasFilter := &models.SceneFilterType{
|
||||||
Organized: &organized,
|
Organized: &organized,
|
||||||
@@ -125,7 +128,8 @@ func testStudioScenes(t *testing.T, tc testStudioCase) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
mockSceneReader.On("Query", expectedAliasFilter, expectedFindFilter).Return(scenes, len(scenes), nil).Once()
|
mockSceneReader.On("Query", scene.QueryOptions(expectedAliasFilter, expectedFindFilter, false)).
|
||||||
|
Return(mocks.SceneQueryResult(scenes, len(scenes)), nil).Once()
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := range matchingPaths {
|
for i := range matchingPaths {
|
||||||
@@ -202,11 +206,11 @@ func testStudioImages(t *testing.T, tc testStudioCase) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// if alias provided, then don't find by name
|
// if alias provided, then don't find by name
|
||||||
onNameQuery := mockImageReader.On("Query", expectedImageFilter, expectedFindFilter)
|
onNameQuery := mockImageReader.On("Query", image.QueryOptions(expectedImageFilter, expectedFindFilter, false))
|
||||||
if aliasName == "" {
|
if aliasName == "" {
|
||||||
onNameQuery.Return(images, len(images), nil).Once()
|
onNameQuery.Return(mocks.ImageQueryResult(images, len(images)), nil).Once()
|
||||||
} else {
|
} else {
|
||||||
onNameQuery.Return(nil, 0, nil).Once()
|
onNameQuery.Return(mocks.ImageQueryResult(nil, 0), nil).Once()
|
||||||
|
|
||||||
expectedAliasFilter := &models.ImageFilterType{
|
expectedAliasFilter := &models.ImageFilterType{
|
||||||
Organized: &organized,
|
Organized: &organized,
|
||||||
@@ -216,7 +220,8 @@ func testStudioImages(t *testing.T, tc testStudioCase) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
mockImageReader.On("Query", expectedAliasFilter, expectedFindFilter).Return(images, len(images), nil).Once()
|
mockImageReader.On("Query", image.QueryOptions(expectedAliasFilter, expectedFindFilter, false)).
|
||||||
|
Return(mocks.ImageQueryResult(images, len(images)), nil).Once()
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := range matchingPaths {
|
for i := range matchingPaths {
|
||||||
|
|||||||
@@ -3,8 +3,10 @@ package autotag
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stashapp/stash/pkg/image"
|
||||||
"github.com/stashapp/stash/pkg/models"
|
"github.com/stashapp/stash/pkg/models"
|
||||||
"github.com/stashapp/stash/pkg/models/mocks"
|
"github.com/stashapp/stash/pkg/models/mocks"
|
||||||
|
"github.com/stashapp/stash/pkg/scene"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -111,11 +113,11 @@ func testTagScenes(t *testing.T, tc testTagCase) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// if alias provided, then don't find by name
|
// if alias provided, then don't find by name
|
||||||
onNameQuery := mockSceneReader.On("Query", expectedSceneFilter, expectedFindFilter)
|
onNameQuery := mockSceneReader.On("Query", scene.QueryOptions(expectedSceneFilter, expectedFindFilter, false))
|
||||||
if aliasName == "" {
|
if aliasName == "" {
|
||||||
onNameQuery.Return(scenes, len(scenes), nil).Once()
|
onNameQuery.Return(mocks.SceneQueryResult(scenes, len(scenes)), nil).Once()
|
||||||
} else {
|
} else {
|
||||||
onNameQuery.Return(nil, 0, nil).Once()
|
onNameQuery.Return(mocks.SceneQueryResult(nil, 0), nil).Once()
|
||||||
|
|
||||||
expectedAliasFilter := &models.SceneFilterType{
|
expectedAliasFilter := &models.SceneFilterType{
|
||||||
Organized: &organized,
|
Organized: &organized,
|
||||||
@@ -125,7 +127,8 @@ func testTagScenes(t *testing.T, tc testTagCase) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
mockSceneReader.On("Query", expectedAliasFilter, expectedFindFilter).Return(scenes, len(scenes), nil).Once()
|
mockSceneReader.On("Query", scene.QueryOptions(expectedAliasFilter, expectedFindFilter, false)).
|
||||||
|
Return(mocks.SceneQueryResult(scenes, len(scenes)), nil).Once()
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := range matchingPaths {
|
for i := range matchingPaths {
|
||||||
@@ -198,11 +201,11 @@ func testTagImages(t *testing.T, tc testTagCase) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// if alias provided, then don't find by name
|
// if alias provided, then don't find by name
|
||||||
onNameQuery := mockImageReader.On("Query", expectedImageFilter, expectedFindFilter)
|
onNameQuery := mockImageReader.On("Query", image.QueryOptions(expectedImageFilter, expectedFindFilter, false))
|
||||||
if aliasName == "" {
|
if aliasName == "" {
|
||||||
onNameQuery.Return(images, len(images), nil).Once()
|
onNameQuery.Return(mocks.ImageQueryResult(images, len(images)), nil).Once()
|
||||||
} else {
|
} else {
|
||||||
onNameQuery.Return(nil, 0, nil).Once()
|
onNameQuery.Return(mocks.ImageQueryResult(nil, 0), nil).Once()
|
||||||
|
|
||||||
expectedAliasFilter := &models.ImageFilterType{
|
expectedAliasFilter := &models.ImageFilterType{
|
||||||
Organized: &organized,
|
Organized: &organized,
|
||||||
@@ -212,7 +215,8 @@ func testTagImages(t *testing.T, tc testTagCase) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
mockImageReader.On("Query", expectedAliasFilter, expectedFindFilter).Return(images, len(images), nil).Once()
|
mockImageReader.On("Query", image.QueryOptions(expectedAliasFilter, expectedFindFilter, false)).
|
||||||
|
Return(mocks.ImageQueryResult(images, len(images)), nil).Once()
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := range matchingPaths {
|
for i := range matchingPaths {
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ import (
|
|||||||
"github.com/anacrolix/dms/upnpav"
|
"github.com/anacrolix/dms/upnpav"
|
||||||
"github.com/stashapp/stash/pkg/logger"
|
"github.com/stashapp/stash/pkg/logger"
|
||||||
"github.com/stashapp/stash/pkg/models"
|
"github.com/stashapp/stash/pkg/models"
|
||||||
|
"github.com/stashapp/stash/pkg/scene"
|
||||||
"github.com/stashapp/stash/pkg/utils"
|
"github.com/stashapp/stash/pkg/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -437,7 +438,7 @@ func (me *contentDirectoryService) getVideos(sceneFilter *models.SceneFilterType
|
|||||||
Sort: &sort,
|
Sort: &sort,
|
||||||
}
|
}
|
||||||
|
|
||||||
scenes, total, err := r.Scene().Query(sceneFilter, findFilter)
|
scenes, total, err := scene.QueryWithCount(r.Scene(), sceneFilter, findFilter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/stashapp/stash/pkg/models"
|
"github.com/stashapp/stash/pkg/models"
|
||||||
|
"github.com/stashapp/stash/pkg/scene"
|
||||||
)
|
)
|
||||||
|
|
||||||
type scenePager struct {
|
type scenePager struct {
|
||||||
@@ -36,7 +37,7 @@ func (p *scenePager) getPages(r models.ReaderRepository, total int) ([]interface
|
|||||||
if pages <= 10 || (page-1)%(pages/10) == 0 {
|
if pages <= 10 || (page-1)%(pages/10) == 0 {
|
||||||
thisPage := ((page - 1) * pageSize) + 1
|
thisPage := ((page - 1) * pageSize) + 1
|
||||||
findFilter.Page = &thisPage
|
findFilter.Page = &thisPage
|
||||||
scenes, _, err := r.Scene().Query(p.sceneFilter, findFilter)
|
scenes, err := scene.Query(r.Scene(), p.sceneFilter, findFilter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -67,7 +68,7 @@ func (p *scenePager) getPageVideos(r models.ReaderRepository, page int, host str
|
|||||||
Sort: &sort,
|
Sort: &sort,
|
||||||
}
|
}
|
||||||
|
|
||||||
scenes, _, err := r.Scene().Query(p.sceneFilter, findFilter)
|
scenes, err := scene.Query(r.Scene(), p.sceneFilter, findFilter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,36 @@ import (
|
|||||||
"github.com/stashapp/stash/pkg/models"
|
"github.com/stashapp/stash/pkg/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type Queryer interface {
|
||||||
|
Query(options models.ImageQueryOptions) (*models.ImageQueryResult, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryOptions returns a ImageQueryResult populated with the provided filters.
|
||||||
|
func QueryOptions(imageFilter *models.ImageFilterType, findFilter *models.FindFilterType, count bool) models.ImageQueryOptions {
|
||||||
|
return models.ImageQueryOptions{
|
||||||
|
QueryOptions: models.QueryOptions{
|
||||||
|
FindFilter: findFilter,
|
||||||
|
Count: count,
|
||||||
|
},
|
||||||
|
ImageFilter: imageFilter,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Query queries for images using the provided filters.
|
||||||
|
func Query(qb Queryer, imageFilter *models.ImageFilterType, findFilter *models.FindFilterType) ([]*models.Image, error) {
|
||||||
|
result, err := qb.Query(QueryOptions(imageFilter, findFilter, false))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
images, err := result.Resolve()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return images, nil
|
||||||
|
}
|
||||||
|
|
||||||
func CountByPerformerID(r models.ImageReader, id int) (int, error) {
|
func CountByPerformerID(r models.ImageReader, id int) (int, error) {
|
||||||
filter := &models.ImageFilterType{
|
filter := &models.ImageFilterType{
|
||||||
Performers: &models.MultiCriterionInput{
|
Performers: &models.MultiCriterionInput{
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/stashapp/stash/pkg/scene"
|
||||||
"github.com/stashapp/stash/pkg/studio"
|
"github.com/stashapp/stash/pkg/studio"
|
||||||
|
|
||||||
"github.com/stashapp/stash/pkg/models"
|
"github.com/stashapp/stash/pkg/models"
|
||||||
@@ -465,7 +466,7 @@ func (p *SceneFilenameParser) Parse(repo models.ReaderRepository) ([]*models.Sce
|
|||||||
|
|
||||||
p.Filter.Q = nil
|
p.Filter.Q = nil
|
||||||
|
|
||||||
scenes, total, err := repo.Scene().Query(sceneFilter, p.Filter)
|
scenes, total, err := scene.QueryWithCount(repo.Scene(), sceneFilter, p.Filter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,9 +9,11 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/stashapp/stash/pkg/autotag"
|
"github.com/stashapp/stash/pkg/autotag"
|
||||||
|
"github.com/stashapp/stash/pkg/image"
|
||||||
"github.com/stashapp/stash/pkg/job"
|
"github.com/stashapp/stash/pkg/job"
|
||||||
"github.com/stashapp/stash/pkg/logger"
|
"github.com/stashapp/stash/pkg/logger"
|
||||||
"github.com/stashapp/stash/pkg/models"
|
"github.com/stashapp/stash/pkg/models"
|
||||||
|
"github.com/stashapp/stash/pkg/scene"
|
||||||
)
|
)
|
||||||
|
|
||||||
type autoTagJob struct {
|
type autoTagJob struct {
|
||||||
@@ -426,16 +428,32 @@ func (t *autoTagFilesTask) getCount(r models.ReaderRepository) (int, error) {
|
|||||||
PerPage: &pp,
|
PerPage: &pp,
|
||||||
}
|
}
|
||||||
|
|
||||||
_, sceneCount, err := r.Scene().Query(t.makeSceneFilter(), findFilter)
|
sceneResults, err := r.Scene().Query(models.SceneQueryOptions{
|
||||||
|
QueryOptions: models.QueryOptions{
|
||||||
|
FindFilter: findFilter,
|
||||||
|
Count: true,
|
||||||
|
},
|
||||||
|
SceneFilter: t.makeSceneFilter(),
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, imageCount, err := r.Image().Query(t.makeImageFilter(), findFilter)
|
sceneCount := sceneResults.Count
|
||||||
|
|
||||||
|
imageResults, err := r.Image().Query(models.ImageQueryOptions{
|
||||||
|
QueryOptions: models.QueryOptions{
|
||||||
|
FindFilter: findFilter,
|
||||||
|
Count: true,
|
||||||
|
},
|
||||||
|
ImageFilter: t.makeImageFilter(),
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
imageCount := imageResults.Count
|
||||||
|
|
||||||
_, galleryCount, err := r.Gallery().Query(t.makeGalleryFilter(), findFilter)
|
_, galleryCount, err := r.Gallery().Query(t.makeGalleryFilter(), findFilter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
@@ -456,7 +474,7 @@ func (t *autoTagFilesTask) processScenes(r models.ReaderRepository) error {
|
|||||||
|
|
||||||
more := true
|
more := true
|
||||||
for more {
|
for more {
|
||||||
scenes, _, err := r.Scene().Query(sceneFilter, findFilter)
|
scenes, err := scene.Query(r.Scene(), sceneFilter, findFilter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -504,7 +522,7 @@ func (t *autoTagFilesTask) processImages(r models.ReaderRepository) error {
|
|||||||
|
|
||||||
more := true
|
more := true
|
||||||
for more {
|
for more {
|
||||||
images, _, err := r.Image().Query(imageFilter, findFilter)
|
images, err := image.Query(r.Image(), imageFilter, findFilter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import (
|
|||||||
"github.com/stashapp/stash/pkg/manager/config"
|
"github.com/stashapp/stash/pkg/manager/config"
|
||||||
"github.com/stashapp/stash/pkg/models"
|
"github.com/stashapp/stash/pkg/models"
|
||||||
"github.com/stashapp/stash/pkg/plugin"
|
"github.com/stashapp/stash/pkg/plugin"
|
||||||
|
"github.com/stashapp/stash/pkg/scene"
|
||||||
"github.com/stashapp/stash/pkg/utils"
|
"github.com/stashapp/stash/pkg/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -98,7 +99,7 @@ func (j *cleanJob) processScenes(ctx context.Context, progress *job.Progress, qb
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
scenes, _, err := qb.Query(nil, findFilter)
|
scenes, err := scene.Query(qb, nil, findFilter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error querying for scenes: %w", err)
|
return fmt.Errorf("error querying for scenes: %w", err)
|
||||||
}
|
}
|
||||||
@@ -223,7 +224,7 @@ func (j *cleanJob) processImages(ctx context.Context, progress *job.Progress, qb
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
images, _, err := qb.Query(nil, findFilter)
|
images, err := image.Query(qb, nil, findFilter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error querying for images: %w", err)
|
return fmt.Errorf("error querying for images: %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
"github.com/stashapp/stash/pkg/logger"
|
"github.com/stashapp/stash/pkg/logger"
|
||||||
"github.com/stashapp/stash/pkg/manager/config"
|
"github.com/stashapp/stash/pkg/manager/config"
|
||||||
"github.com/stashapp/stash/pkg/models"
|
"github.com/stashapp/stash/pkg/models"
|
||||||
|
"github.com/stashapp/stash/pkg/scene"
|
||||||
"github.com/stashapp/stash/pkg/utils"
|
"github.com/stashapp/stash/pkg/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -149,7 +150,7 @@ func (j *GenerateJob) queueTasks(ctx context.Context, queue chan<- Task) totalsG
|
|||||||
return context.Canceled
|
return context.Canceled
|
||||||
}
|
}
|
||||||
|
|
||||||
scenes, _, err := r.Scene().Query(nil, findFilter)
|
scenes, err := scene.Query(r.Scene(), nil, findFilter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,9 @@ import (
|
|||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/stashapp/stash/pkg/image"
|
||||||
"github.com/stashapp/stash/pkg/models"
|
"github.com/stashapp/stash/pkg/models"
|
||||||
|
"github.com/stashapp/stash/pkg/scene"
|
||||||
)
|
)
|
||||||
|
|
||||||
const separatorChars = `.\-_ `
|
const separatorChars = `.\-_ `
|
||||||
@@ -211,7 +213,7 @@ func PathToScenes(name string, paths []string, sceneReader models.SceneReader) (
|
|||||||
filter.And = scenePathsFilter(paths)
|
filter.And = scenePathsFilter(paths)
|
||||||
|
|
||||||
pp := models.PerPageAll
|
pp := models.PerPageAll
|
||||||
scenes, _, err := sceneReader.Query(&filter, &models.FindFilterType{
|
scenes, err := scene.Query(sceneReader, &filter, &models.FindFilterType{
|
||||||
PerPage: &pp,
|
PerPage: &pp,
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -275,7 +277,7 @@ func PathToImages(name string, paths []string, imageReader models.ImageReader) (
|
|||||||
filter.And = imagePathsFilter(paths)
|
filter.And = imagePathsFilter(paths)
|
||||||
|
|
||||||
pp := models.PerPageAll
|
pp := models.PerPageAll
|
||||||
images, _, err := imageReader.Query(&filter, &models.FindFilterType{
|
images, err := image.Query(imageReader, &filter, &models.FindFilterType{
|
||||||
PerPage: &pp,
|
PerPage: &pp,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,46 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
type ImageReader interface {
|
type ImageQueryOptions struct {
|
||||||
Find(id int) (*Image, error)
|
QueryOptions
|
||||||
|
ImageFilter *ImageFilterType
|
||||||
|
|
||||||
|
Megapixels bool
|
||||||
|
TotalSize bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type ImageQueryResult struct {
|
||||||
|
QueryResult
|
||||||
|
Megapixels float64
|
||||||
|
TotalSize int
|
||||||
|
|
||||||
|
finder ImageFinder
|
||||||
|
images []*Image
|
||||||
|
resolveErr error
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewImageQueryResult(finder ImageFinder) *ImageQueryResult {
|
||||||
|
return &ImageQueryResult{
|
||||||
|
finder: finder,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *ImageQueryResult) Resolve() ([]*Image, error) {
|
||||||
|
// cache results
|
||||||
|
if r.images == nil && r.resolveErr == nil {
|
||||||
|
r.images, r.resolveErr = r.finder.FindMany(r.IDs)
|
||||||
|
}
|
||||||
|
return r.images, r.resolveErr
|
||||||
|
}
|
||||||
|
|
||||||
|
type ImageFinder interface {
|
||||||
|
// TODO - rename to Find and remove existing method
|
||||||
FindMany(ids []int) ([]*Image, error)
|
FindMany(ids []int) ([]*Image, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type ImageReader interface {
|
||||||
|
ImageFinder
|
||||||
|
// TODO - remove this in another PR
|
||||||
|
Find(id int) (*Image, error)
|
||||||
FindByChecksum(checksum string) (*Image, error)
|
FindByChecksum(checksum string) (*Image, error)
|
||||||
FindByGalleryID(galleryID int) ([]*Image, error)
|
FindByGalleryID(galleryID int) ([]*Image, error)
|
||||||
CountByGalleryID(galleryID int) (int, error)
|
CountByGalleryID(galleryID int) (int, error)
|
||||||
@@ -16,7 +54,7 @@ type ImageReader interface {
|
|||||||
// CountByStudioID(studioID int) (int, error)
|
// CountByStudioID(studioID int) (int, error)
|
||||||
// CountByTagID(tagID int) (int, error)
|
// CountByTagID(tagID int) (int, error)
|
||||||
All() ([]*Image, error)
|
All() ([]*Image, error)
|
||||||
Query(imageFilter *ImageFilterType, findFilter *FindFilterType) ([]*Image, int, error)
|
Query(options ImageQueryOptions) (*ImageQueryResult, error)
|
||||||
QueryCount(imageFilter *ImageFilterType, findFilter *FindFilterType) (int, error)
|
QueryCount(imageFilter *ImageFilterType, findFilter *FindFilterType) (int, error)
|
||||||
GetGalleryIDs(imageID int) ([]int, error)
|
GetGalleryIDs(imageID int) ([]int, error)
|
||||||
GetTagIDs(imageID int) ([]int, error)
|
GetTagIDs(imageID int) ([]int, error)
|
||||||
|
|||||||
@@ -340,34 +340,27 @@ func (_m *ImageReaderWriter) IncrementOCounter(id int) (int, error) {
|
|||||||
return r0, r1
|
return r0, r1
|
||||||
}
|
}
|
||||||
|
|
||||||
// Query provides a mock function with given fields: imageFilter, findFilter
|
// Query provides a mock function with given fields: options
|
||||||
func (_m *ImageReaderWriter) Query(imageFilter *models.ImageFilterType, findFilter *models.FindFilterType) ([]*models.Image, int, error) {
|
func (_m *ImageReaderWriter) Query(options models.ImageQueryOptions) (*models.ImageQueryResult, error) {
|
||||||
ret := _m.Called(imageFilter, findFilter)
|
ret := _m.Called(options)
|
||||||
|
|
||||||
var r0 []*models.Image
|
var r0 *models.ImageQueryResult
|
||||||
if rf, ok := ret.Get(0).(func(*models.ImageFilterType, *models.FindFilterType) []*models.Image); ok {
|
if rf, ok := ret.Get(0).(func(models.ImageQueryOptions) *models.ImageQueryResult); ok {
|
||||||
r0 = rf(imageFilter, findFilter)
|
r0 = rf(options)
|
||||||
} else {
|
} else {
|
||||||
if ret.Get(0) != nil {
|
if ret.Get(0) != nil {
|
||||||
r0 = ret.Get(0).([]*models.Image)
|
r0 = ret.Get(0).(*models.ImageQueryResult)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var r1 int
|
var r1 error
|
||||||
if rf, ok := ret.Get(1).(func(*models.ImageFilterType, *models.FindFilterType) int); ok {
|
if rf, ok := ret.Get(1).(func(models.ImageQueryOptions) error); ok {
|
||||||
r1 = rf(imageFilter, findFilter)
|
r1 = rf(options)
|
||||||
} else {
|
} else {
|
||||||
r1 = ret.Get(1).(int)
|
r1 = ret.Error(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
var r2 error
|
return r0, r1
|
||||||
if rf, ok := ret.Get(2).(func(*models.ImageFilterType, *models.FindFilterType) error); ok {
|
|
||||||
r2 = rf(imageFilter, findFilter)
|
|
||||||
} else {
|
|
||||||
r2 = ret.Error(2)
|
|
||||||
}
|
|
||||||
|
|
||||||
return r0, r1, r2
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// QueryCount provides a mock function with given fields: imageFilter, findFilter
|
// QueryCount provides a mock function with given fields: imageFilter, findFilter
|
||||||
|
|||||||
@@ -641,34 +641,27 @@ func (_m *SceneReaderWriter) IncrementOCounter(id int) (int, error) {
|
|||||||
return r0, r1
|
return r0, r1
|
||||||
}
|
}
|
||||||
|
|
||||||
// Query provides a mock function with given fields: sceneFilter, findFilter
|
// Query provides a mock function with given fields: options
|
||||||
func (_m *SceneReaderWriter) Query(sceneFilter *models.SceneFilterType, findFilter *models.FindFilterType) ([]*models.Scene, int, error) {
|
func (_m *SceneReaderWriter) Query(options models.SceneQueryOptions) (*models.SceneQueryResult, error) {
|
||||||
ret := _m.Called(sceneFilter, findFilter)
|
ret := _m.Called(options)
|
||||||
|
|
||||||
var r0 []*models.Scene
|
var r0 *models.SceneQueryResult
|
||||||
if rf, ok := ret.Get(0).(func(*models.SceneFilterType, *models.FindFilterType) []*models.Scene); ok {
|
if rf, ok := ret.Get(0).(func(models.SceneQueryOptions) *models.SceneQueryResult); ok {
|
||||||
r0 = rf(sceneFilter, findFilter)
|
r0 = rf(options)
|
||||||
} else {
|
} else {
|
||||||
if ret.Get(0) != nil {
|
if ret.Get(0) != nil {
|
||||||
r0 = ret.Get(0).([]*models.Scene)
|
r0 = ret.Get(0).(*models.SceneQueryResult)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var r1 int
|
var r1 error
|
||||||
if rf, ok := ret.Get(1).(func(*models.SceneFilterType, *models.FindFilterType) int); ok {
|
if rf, ok := ret.Get(1).(func(models.SceneQueryOptions) error); ok {
|
||||||
r1 = rf(sceneFilter, findFilter)
|
r1 = rf(options)
|
||||||
} else {
|
} else {
|
||||||
r1 = ret.Get(1).(int)
|
r1 = ret.Error(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
var r2 error
|
return r0, r1
|
||||||
if rf, ok := ret.Get(2).(func(*models.SceneFilterType, *models.FindFilterType) error); ok {
|
|
||||||
r2 = rf(sceneFilter, findFilter)
|
|
||||||
} else {
|
|
||||||
r2 = ret.Error(2)
|
|
||||||
}
|
|
||||||
|
|
||||||
return r0, r1, r2
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResetOCounter provides a mock function with given fields: id
|
// ResetOCounter provides a mock function with given fields: id
|
||||||
|
|||||||
41
pkg/models/mocks/query.go
Normal file
41
pkg/models/mocks/query.go
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
package mocks
|
||||||
|
|
||||||
|
import "github.com/stashapp/stash/pkg/models"
|
||||||
|
|
||||||
|
type sceneResolver struct {
|
||||||
|
scenes []*models.Scene
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *sceneResolver) Find(id int) (*models.Scene, error) {
|
||||||
|
panic("not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *sceneResolver) FindMany(ids []int) ([]*models.Scene, error) {
|
||||||
|
return s.scenes, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func SceneQueryResult(scenes []*models.Scene, count int) *models.SceneQueryResult {
|
||||||
|
ret := models.NewSceneQueryResult(&sceneResolver{
|
||||||
|
scenes: scenes,
|
||||||
|
})
|
||||||
|
|
||||||
|
ret.Count = count
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
type imageResolver struct {
|
||||||
|
images []*models.Image
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *imageResolver) FindMany(ids []int) ([]*models.Image, error) {
|
||||||
|
return s.images, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ImageQueryResult(images []*models.Image, count int) *models.ImageQueryResult {
|
||||||
|
ret := models.NewImageQueryResult(&imageResolver{
|
||||||
|
images: images,
|
||||||
|
})
|
||||||
|
|
||||||
|
ret.Count = count
|
||||||
|
return ret
|
||||||
|
}
|
||||||
11
pkg/models/query.go
Normal file
11
pkg/models/query.go
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
type QueryOptions struct {
|
||||||
|
FindFilter *FindFilterType
|
||||||
|
Count bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type QueryResult struct {
|
||||||
|
IDs []int
|
||||||
|
Count int
|
||||||
|
}
|
||||||
@@ -1,8 +1,46 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
type SceneReader interface {
|
type SceneQueryOptions struct {
|
||||||
Find(id int) (*Scene, error)
|
QueryOptions
|
||||||
|
SceneFilter *SceneFilterType
|
||||||
|
|
||||||
|
TotalDuration bool
|
||||||
|
TotalSize bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type SceneQueryResult struct {
|
||||||
|
QueryResult
|
||||||
|
TotalDuration float64
|
||||||
|
TotalSize int
|
||||||
|
|
||||||
|
finder SceneFinder
|
||||||
|
scenes []*Scene
|
||||||
|
resolveErr error
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSceneQueryResult(finder SceneFinder) *SceneQueryResult {
|
||||||
|
return &SceneQueryResult{
|
||||||
|
finder: finder,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *SceneQueryResult) Resolve() ([]*Scene, error) {
|
||||||
|
// cache results
|
||||||
|
if r.scenes == nil && r.resolveErr == nil {
|
||||||
|
r.scenes, r.resolveErr = r.finder.FindMany(r.IDs)
|
||||||
|
}
|
||||||
|
return r.scenes, r.resolveErr
|
||||||
|
}
|
||||||
|
|
||||||
|
type SceneFinder interface {
|
||||||
|
// TODO - rename this to Find and remove existing method
|
||||||
FindMany(ids []int) ([]*Scene, error)
|
FindMany(ids []int) ([]*Scene, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type SceneReader interface {
|
||||||
|
SceneFinder
|
||||||
|
// TODO - remove this in another PR
|
||||||
|
Find(id int) (*Scene, error)
|
||||||
FindByChecksum(checksum string) (*Scene, error)
|
FindByChecksum(checksum string) (*Scene, error)
|
||||||
FindByOSHash(oshash string) (*Scene, error)
|
FindByOSHash(oshash string) (*Scene, error)
|
||||||
FindByPath(path string) (*Scene, error)
|
FindByPath(path string) (*Scene, error)
|
||||||
@@ -23,7 +61,7 @@ type SceneReader interface {
|
|||||||
CountMissingOSHash() (int, error)
|
CountMissingOSHash() (int, error)
|
||||||
Wall(q *string) ([]*Scene, error)
|
Wall(q *string) ([]*Scene, error)
|
||||||
All() ([]*Scene, error)
|
All() ([]*Scene, error)
|
||||||
Query(sceneFilter *SceneFilterType, findFilter *FindFilterType) ([]*Scene, int, error)
|
Query(options SceneQueryOptions) (*SceneQueryResult, error)
|
||||||
GetCover(sceneID int) ([]byte, error)
|
GetCover(sceneID int) ([]byte, error)
|
||||||
GetMovies(sceneID int) ([]MoviesScenes, error)
|
GetMovies(sceneID int) ([]MoviesScenes, error)
|
||||||
GetTagIDs(sceneID int) ([]int, error)
|
GetTagIDs(sceneID int) ([]int, error)
|
||||||
|
|||||||
@@ -28,8 +28,10 @@ type TagDestroyInput struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type FindScenesResultType struct {
|
type FindScenesResultType struct {
|
||||||
Count graphql.Int
|
Count graphql.Int
|
||||||
Scenes []Scene
|
DurationSeconds graphql.Float
|
||||||
|
FilesizeBytes graphql.Int
|
||||||
|
Scenes []Scene
|
||||||
}
|
}
|
||||||
|
|
||||||
type Tag struct {
|
type Tag struct {
|
||||||
|
|||||||
50
pkg/scene/query.go
Normal file
50
pkg/scene/query.go
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
package scene
|
||||||
|
|
||||||
|
import "github.com/stashapp/stash/pkg/models"
|
||||||
|
|
||||||
|
type Queryer interface {
|
||||||
|
Query(options models.SceneQueryOptions) (*models.SceneQueryResult, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryOptions returns a SceneQueryOptions populated with the provided filters.
|
||||||
|
func QueryOptions(sceneFilter *models.SceneFilterType, findFilter *models.FindFilterType, count bool) models.SceneQueryOptions {
|
||||||
|
return models.SceneQueryOptions{
|
||||||
|
QueryOptions: models.QueryOptions{
|
||||||
|
FindFilter: findFilter,
|
||||||
|
Count: count,
|
||||||
|
},
|
||||||
|
SceneFilter: sceneFilter,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryWithCount queries for scenes, returning the scene objects and the total count.
|
||||||
|
func QueryWithCount(qb Queryer, sceneFilter *models.SceneFilterType, findFilter *models.FindFilterType) ([]*models.Scene, int, error) {
|
||||||
|
// this was moved from the queryBuilder code
|
||||||
|
// left here so that calling functions can reference this instead
|
||||||
|
result, err := qb.Query(QueryOptions(sceneFilter, findFilter, true))
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
scenes, err := result.Resolve()
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return scenes, result.Count, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Query queries for scenes using the provided filters.
|
||||||
|
func Query(qb Queryer, sceneFilter *models.SceneFilterType, findFilter *models.FindFilterType) ([]*models.Scene, error) {
|
||||||
|
result, err := qb.Query(QueryOptions(sceneFilter, findFilter, false))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
scenes, err := result.Resolve()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return scenes, nil
|
||||||
|
}
|
||||||
@@ -84,12 +84,16 @@ func (j *joins) add(newJoins ...join) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (j *joins) toSQL() string {
|
func (j *joins) toSQL() string {
|
||||||
|
if len(*j) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
var ret []string
|
var ret []string
|
||||||
for _, jj := range *j {
|
for _, jj := range *j {
|
||||||
ret = append(ret, jj.toSQL())
|
ret = append(ret, jj.toSQL())
|
||||||
}
|
}
|
||||||
|
|
||||||
return strings.Join(ret, " ")
|
return " " + strings.Join(ret, " ")
|
||||||
}
|
}
|
||||||
|
|
||||||
type filterBuilder struct {
|
type filterBuilder struct {
|
||||||
|
|||||||
@@ -233,8 +233,7 @@ func (qb *galleryQueryBuilder) makeQuery(galleryFilter *models.GalleryFilterType
|
|||||||
}
|
}
|
||||||
|
|
||||||
query := qb.newQuery()
|
query := qb.newQuery()
|
||||||
|
distinctIDs(&query, galleryTable)
|
||||||
query.body = selectDistinctIDs(galleryTable)
|
|
||||||
|
|
||||||
if q := findFilter.Q; q != nil && *q != "" {
|
if q := findFilter.Q; q != nil && *q != "" {
|
||||||
searchColumns := []string{"galleries.title", "galleries.path", "galleries.checksum"}
|
searchColumns := []string{"galleries.title", "galleries.path", "galleries.checksum"}
|
||||||
|
|||||||
@@ -962,18 +962,24 @@ func verifyGalleriesImageCount(t *testing.T, imageCountCriterion models.IntCrite
|
|||||||
for _, gallery := range galleries {
|
for _, gallery := range galleries {
|
||||||
pp := 0
|
pp := 0
|
||||||
|
|
||||||
_, count, err := r.Image().Query(&models.ImageFilterType{
|
result, err := r.Image().Query(models.ImageQueryOptions{
|
||||||
Galleries: &models.MultiCriterionInput{
|
QueryOptions: models.QueryOptions{
|
||||||
Value: []string{strconv.Itoa(gallery.ID)},
|
FindFilter: &models.FindFilterType{
|
||||||
Modifier: models.CriterionModifierIncludes,
|
PerPage: &pp,
|
||||||
|
},
|
||||||
|
Count: true,
|
||||||
|
},
|
||||||
|
ImageFilter: &models.ImageFilterType{
|
||||||
|
Galleries: &models.MultiCriterionInput{
|
||||||
|
Value: []string{strconv.Itoa(gallery.ID)},
|
||||||
|
Modifier: models.CriterionModifierIncludes,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}, &models.FindFilterType{
|
|
||||||
PerPage: &pp,
|
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
verifyInt(t, count, imageCountCriterion)
|
verifyInt(t, result.Count, imageCountCriterion)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -261,8 +261,7 @@ func (qb *imageQueryBuilder) makeQuery(imageFilter *models.ImageFilterType, find
|
|||||||
}
|
}
|
||||||
|
|
||||||
query := qb.newQuery()
|
query := qb.newQuery()
|
||||||
|
distinctIDs(&query, imageTable)
|
||||||
query.body = selectDistinctIDs(imageTable)
|
|
||||||
|
|
||||||
if q := findFilter.Q; q != nil && *q != "" {
|
if q := findFilter.Q; q != nil && *q != "" {
|
||||||
searchColumns := []string{"images.title", "images.path", "images.checksum"}
|
searchColumns := []string{"images.title", "images.path", "images.checksum"}
|
||||||
@@ -283,28 +282,65 @@ func (qb *imageQueryBuilder) makeQuery(imageFilter *models.ImageFilterType, find
|
|||||||
return &query, nil
|
return &query, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (qb *imageQueryBuilder) Query(imageFilter *models.ImageFilterType, findFilter *models.FindFilterType) ([]*models.Image, int, error) {
|
func (qb *imageQueryBuilder) Query(options models.ImageQueryOptions) (*models.ImageQueryResult, error) {
|
||||||
query, err := qb.makeQuery(imageFilter, findFilter)
|
query, err := qb.makeQuery(options.ImageFilter, options.FindFilter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
idsResult, countResult, err := query.executeFind()
|
result, err := qb.queryGroupedFields(options, *query)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, err
|
return nil, fmt.Errorf("error querying aggregate fields: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var images []*models.Image
|
idsResult, err := query.findIDs()
|
||||||
for _, id := range idsResult {
|
if err != nil {
|
||||||
image, err := qb.Find(id)
|
return nil, fmt.Errorf("error finding IDs: %w", err)
|
||||||
if err != nil {
|
|
||||||
return nil, 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
images = append(images, image)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return images, countResult, nil
|
result.IDs = idsResult
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (qb *imageQueryBuilder) queryGroupedFields(options models.ImageQueryOptions, query queryBuilder) (*models.ImageQueryResult, error) {
|
||||||
|
if !options.Count && !options.Megapixels && !options.TotalSize {
|
||||||
|
// nothing to do - return empty result
|
||||||
|
return models.NewImageQueryResult(qb), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
aggregateQuery := qb.newQuery()
|
||||||
|
|
||||||
|
if options.Count {
|
||||||
|
aggregateQuery.addColumn("COUNT(temp.id) as total")
|
||||||
|
}
|
||||||
|
|
||||||
|
if options.Megapixels {
|
||||||
|
query.addColumn("COALESCE(images.width, 0) * COALESCE(images.height, 0) / 1000000 as megapixels")
|
||||||
|
aggregateQuery.addColumn("COALESCE(SUM(temp.megapixels), 0) as megapixels")
|
||||||
|
}
|
||||||
|
|
||||||
|
if options.TotalSize {
|
||||||
|
query.addColumn("COALESCE(images.size, 0) as size")
|
||||||
|
aggregateQuery.addColumn("COALESCE(SUM(temp.size), 0) as size")
|
||||||
|
}
|
||||||
|
|
||||||
|
const includeSortPagination = false
|
||||||
|
aggregateQuery.from = fmt.Sprintf("(%s) as temp", query.toSQL(includeSortPagination))
|
||||||
|
|
||||||
|
out := struct {
|
||||||
|
Total int
|
||||||
|
Megapixels float64
|
||||||
|
Size int
|
||||||
|
}{}
|
||||||
|
if err := qb.repository.queryStruct(aggregateQuery.toSQL(includeSortPagination), query.args, &out); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ret := models.NewImageQueryResult(qb)
|
||||||
|
ret.Count = out.Total
|
||||||
|
ret.Megapixels = out.Megapixels
|
||||||
|
ret.TotalSize = out.Size
|
||||||
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (qb *imageQueryBuilder) QueryCount(imageFilter *models.ImageFilterType, findFilter *models.FindFilterType) (int, error) {
|
func (qb *imageQueryBuilder) QueryCount(imageFilter *models.ImageFilterType, findFilter *models.FindFilterType) (int, error) {
|
||||||
|
|||||||
@@ -83,14 +83,31 @@ func TestImageQueryQ(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func queryImagesWithCount(sqb models.ImageReader, imageFilter *models.ImageFilterType, findFilter *models.FindFilterType) ([]*models.Image, int, error) {
|
||||||
|
result, err := sqb.Query(models.ImageQueryOptions{
|
||||||
|
QueryOptions: models.QueryOptions{
|
||||||
|
FindFilter: findFilter,
|
||||||
|
Count: true,
|
||||||
|
},
|
||||||
|
ImageFilter: imageFilter,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
images, err := result.Resolve()
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return images, result.Count, nil
|
||||||
|
}
|
||||||
|
|
||||||
func imageQueryQ(t *testing.T, sqb models.ImageReader, q string, expectedImageIdx int) {
|
func imageQueryQ(t *testing.T, sqb models.ImageReader, q string, expectedImageIdx int) {
|
||||||
filter := models.FindFilterType{
|
filter := models.FindFilterType{
|
||||||
Q: &q,
|
Q: &q,
|
||||||
}
|
}
|
||||||
images, _, err := sqb.Query(nil, &filter)
|
images := queryImages(t, sqb, nil, &filter)
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Error querying image: %s", err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.Len(t, images, 1)
|
assert.Len(t, images, 1)
|
||||||
image := images[0]
|
image := images[0]
|
||||||
@@ -104,10 +121,7 @@ func imageQueryQ(t *testing.T, sqb models.ImageReader, q string, expectedImageId
|
|||||||
|
|
||||||
// no Q should return all results
|
// no Q should return all results
|
||||||
filter.Q = nil
|
filter.Q = nil
|
||||||
images, _, err = sqb.Query(nil, &filter)
|
images = queryImages(t, sqb, nil, &filter)
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Error querying image: %s", err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.Len(t, images, totalImages)
|
assert.Len(t, images, totalImages)
|
||||||
}
|
}
|
||||||
@@ -141,10 +155,7 @@ func verifyImagePath(t *testing.T, pathCriterion models.StringCriterionInput, ex
|
|||||||
Path: &pathCriterion,
|
Path: &pathCriterion,
|
||||||
}
|
}
|
||||||
|
|
||||||
images, _, err := sqb.Query(&imageFilter, nil)
|
images := queryImages(t, sqb, &imageFilter, nil)
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Error querying image: %s", err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.Equal(t, expected, len(images), "number of returned images")
|
assert.Equal(t, expected, len(images), "number of returned images")
|
||||||
|
|
||||||
@@ -276,17 +287,17 @@ func TestImageIllegalQuery(t *testing.T) {
|
|||||||
withTxn(func(r models.Repository) error {
|
withTxn(func(r models.Repository) error {
|
||||||
sqb := r.Image()
|
sqb := r.Image()
|
||||||
|
|
||||||
_, _, err := sqb.Query(imageFilter, nil)
|
_, _, err := queryImagesWithCount(sqb, imageFilter, nil)
|
||||||
assert.NotNil(err)
|
assert.NotNil(err)
|
||||||
|
|
||||||
imageFilter.Or = nil
|
imageFilter.Or = nil
|
||||||
imageFilter.Not = &subFilter
|
imageFilter.Not = &subFilter
|
||||||
_, _, err = sqb.Query(imageFilter, nil)
|
_, _, err = queryImagesWithCount(sqb, imageFilter, nil)
|
||||||
assert.NotNil(err)
|
assert.NotNil(err)
|
||||||
|
|
||||||
imageFilter.And = nil
|
imageFilter.And = nil
|
||||||
imageFilter.Or = &subFilter
|
imageFilter.Or = &subFilter
|
||||||
_, _, err = sqb.Query(imageFilter, nil)
|
_, _, err = queryImagesWithCount(sqb, imageFilter, nil)
|
||||||
assert.NotNil(err)
|
assert.NotNil(err)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -325,7 +336,7 @@ func verifyImagesRating(t *testing.T, ratingCriterion models.IntCriterionInput)
|
|||||||
Rating: &ratingCriterion,
|
Rating: &ratingCriterion,
|
||||||
}
|
}
|
||||||
|
|
||||||
images, _, err := sqb.Query(&imageFilter, nil)
|
images, _, err := queryImagesWithCount(sqb, &imageFilter, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Error querying image: %s", err.Error())
|
t.Errorf("Error querying image: %s", err.Error())
|
||||||
}
|
}
|
||||||
@@ -364,7 +375,7 @@ func verifyImagesOCounter(t *testing.T, oCounterCriterion models.IntCriterionInp
|
|||||||
OCounter: &oCounterCriterion,
|
OCounter: &oCounterCriterion,
|
||||||
}
|
}
|
||||||
|
|
||||||
images, _, err := sqb.Query(&imageFilter, nil)
|
images, _, err := queryImagesWithCount(sqb, &imageFilter, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Error querying image: %s", err.Error())
|
t.Errorf("Error querying image: %s", err.Error())
|
||||||
}
|
}
|
||||||
@@ -396,7 +407,7 @@ func verifyImagesResolution(t *testing.T, resolution models.ResolutionEnum) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
images, _, err := sqb.Query(&imageFilter, nil)
|
images, _, err := queryImagesWithCount(sqb, &imageFilter, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Error querying image: %s", err.Error())
|
t.Errorf("Error querying image: %s", err.Error())
|
||||||
}
|
}
|
||||||
@@ -440,7 +451,7 @@ func TestImageQueryIsMissingGalleries(t *testing.T) {
|
|||||||
Q: &q,
|
Q: &q,
|
||||||
}
|
}
|
||||||
|
|
||||||
images, _, err := sqb.Query(&imageFilter, &findFilter)
|
images, _, err := queryImagesWithCount(sqb, &imageFilter, &findFilter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Error querying image: %s", err.Error())
|
t.Errorf("Error querying image: %s", err.Error())
|
||||||
}
|
}
|
||||||
@@ -448,7 +459,7 @@ func TestImageQueryIsMissingGalleries(t *testing.T) {
|
|||||||
assert.Len(t, images, 0)
|
assert.Len(t, images, 0)
|
||||||
|
|
||||||
findFilter.Q = nil
|
findFilter.Q = nil
|
||||||
images, _, err = sqb.Query(&imageFilter, &findFilter)
|
images, _, err = queryImagesWithCount(sqb, &imageFilter, &findFilter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Error querying image: %s", err.Error())
|
t.Errorf("Error querying image: %s", err.Error())
|
||||||
}
|
}
|
||||||
@@ -475,7 +486,7 @@ func TestImageQueryIsMissingStudio(t *testing.T) {
|
|||||||
Q: &q,
|
Q: &q,
|
||||||
}
|
}
|
||||||
|
|
||||||
images, _, err := sqb.Query(&imageFilter, &findFilter)
|
images, _, err := queryImagesWithCount(sqb, &imageFilter, &findFilter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Error querying image: %s", err.Error())
|
t.Errorf("Error querying image: %s", err.Error())
|
||||||
}
|
}
|
||||||
@@ -483,7 +494,7 @@ func TestImageQueryIsMissingStudio(t *testing.T) {
|
|||||||
assert.Len(t, images, 0)
|
assert.Len(t, images, 0)
|
||||||
|
|
||||||
findFilter.Q = nil
|
findFilter.Q = nil
|
||||||
images, _, err = sqb.Query(&imageFilter, &findFilter)
|
images, _, err = queryImagesWithCount(sqb, &imageFilter, &findFilter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Error querying image: %s", err.Error())
|
t.Errorf("Error querying image: %s", err.Error())
|
||||||
}
|
}
|
||||||
@@ -510,7 +521,7 @@ func TestImageQueryIsMissingPerformers(t *testing.T) {
|
|||||||
Q: &q,
|
Q: &q,
|
||||||
}
|
}
|
||||||
|
|
||||||
images, _, err := sqb.Query(&imageFilter, &findFilter)
|
images, _, err := queryImagesWithCount(sqb, &imageFilter, &findFilter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Error querying image: %s", err.Error())
|
t.Errorf("Error querying image: %s", err.Error())
|
||||||
}
|
}
|
||||||
@@ -518,7 +529,7 @@ func TestImageQueryIsMissingPerformers(t *testing.T) {
|
|||||||
assert.Len(t, images, 0)
|
assert.Len(t, images, 0)
|
||||||
|
|
||||||
findFilter.Q = nil
|
findFilter.Q = nil
|
||||||
images, _, err = sqb.Query(&imageFilter, &findFilter)
|
images, _, err = queryImagesWithCount(sqb, &imageFilter, &findFilter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Error querying image: %s", err.Error())
|
t.Errorf("Error querying image: %s", err.Error())
|
||||||
}
|
}
|
||||||
@@ -547,7 +558,7 @@ func TestImageQueryIsMissingTags(t *testing.T) {
|
|||||||
Q: &q,
|
Q: &q,
|
||||||
}
|
}
|
||||||
|
|
||||||
images, _, err := sqb.Query(&imageFilter, &findFilter)
|
images, _, err := queryImagesWithCount(sqb, &imageFilter, &findFilter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Error querying image: %s", err.Error())
|
t.Errorf("Error querying image: %s", err.Error())
|
||||||
}
|
}
|
||||||
@@ -555,7 +566,7 @@ func TestImageQueryIsMissingTags(t *testing.T) {
|
|||||||
assert.Len(t, images, 0)
|
assert.Len(t, images, 0)
|
||||||
|
|
||||||
findFilter.Q = nil
|
findFilter.Q = nil
|
||||||
images, _, err = sqb.Query(&imageFilter, &findFilter)
|
images, _, err = queryImagesWithCount(sqb, &imageFilter, &findFilter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Error querying image: %s", err.Error())
|
t.Errorf("Error querying image: %s", err.Error())
|
||||||
}
|
}
|
||||||
@@ -574,7 +585,7 @@ func TestImageQueryIsMissingRating(t *testing.T) {
|
|||||||
IsMissing: &isMissing,
|
IsMissing: &isMissing,
|
||||||
}
|
}
|
||||||
|
|
||||||
images, _, err := sqb.Query(&imageFilter, nil)
|
images, _, err := queryImagesWithCount(sqb, &imageFilter, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Error querying image: %s", err.Error())
|
t.Errorf("Error querying image: %s", err.Error())
|
||||||
}
|
}
|
||||||
@@ -604,7 +615,7 @@ func TestImageQueryGallery(t *testing.T) {
|
|||||||
Galleries: &galleryCriterion,
|
Galleries: &galleryCriterion,
|
||||||
}
|
}
|
||||||
|
|
||||||
images, _, err := sqb.Query(&imageFilter, nil)
|
images, _, err := queryImagesWithCount(sqb, &imageFilter, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Error querying image: %s", err.Error())
|
t.Errorf("Error querying image: %s", err.Error())
|
||||||
}
|
}
|
||||||
@@ -624,7 +635,7 @@ func TestImageQueryGallery(t *testing.T) {
|
|||||||
Modifier: models.CriterionModifierIncludesAll,
|
Modifier: models.CriterionModifierIncludesAll,
|
||||||
}
|
}
|
||||||
|
|
||||||
images, _, err = sqb.Query(&imageFilter, nil)
|
images, _, err = queryImagesWithCount(sqb, &imageFilter, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Error querying image: %s", err.Error())
|
t.Errorf("Error querying image: %s", err.Error())
|
||||||
}
|
}
|
||||||
@@ -644,7 +655,7 @@ func TestImageQueryGallery(t *testing.T) {
|
|||||||
Q: &q,
|
Q: &q,
|
||||||
}
|
}
|
||||||
|
|
||||||
images, _, err = sqb.Query(&imageFilter, &findFilter)
|
images, _, err = queryImagesWithCount(sqb, &imageFilter, &findFilter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Error querying image: %s", err.Error())
|
t.Errorf("Error querying image: %s", err.Error())
|
||||||
}
|
}
|
||||||
@@ -669,7 +680,7 @@ func TestImageQueryPerformers(t *testing.T) {
|
|||||||
Performers: &performerCriterion,
|
Performers: &performerCriterion,
|
||||||
}
|
}
|
||||||
|
|
||||||
images, _, err := sqb.Query(&imageFilter, nil)
|
images, _, err := queryImagesWithCount(sqb, &imageFilter, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Error querying image: %s", err.Error())
|
t.Errorf("Error querying image: %s", err.Error())
|
||||||
}
|
}
|
||||||
@@ -689,7 +700,7 @@ func TestImageQueryPerformers(t *testing.T) {
|
|||||||
Modifier: models.CriterionModifierIncludesAll,
|
Modifier: models.CriterionModifierIncludesAll,
|
||||||
}
|
}
|
||||||
|
|
||||||
images, _, err = sqb.Query(&imageFilter, nil)
|
images, _, err = queryImagesWithCount(sqb, &imageFilter, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Error querying image: %s", err.Error())
|
t.Errorf("Error querying image: %s", err.Error())
|
||||||
}
|
}
|
||||||
@@ -709,7 +720,7 @@ func TestImageQueryPerformers(t *testing.T) {
|
|||||||
Q: &q,
|
Q: &q,
|
||||||
}
|
}
|
||||||
|
|
||||||
images, _, err = sqb.Query(&imageFilter, &findFilter)
|
images, _, err = queryImagesWithCount(sqb, &imageFilter, &findFilter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Error querying image: %s", err.Error())
|
t.Errorf("Error querying image: %s", err.Error())
|
||||||
}
|
}
|
||||||
@@ -734,7 +745,7 @@ func TestImageQueryTags(t *testing.T) {
|
|||||||
Tags: &tagCriterion,
|
Tags: &tagCriterion,
|
||||||
}
|
}
|
||||||
|
|
||||||
images, _, err := sqb.Query(&imageFilter, nil)
|
images, _, err := queryImagesWithCount(sqb, &imageFilter, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Error querying image: %s", err.Error())
|
t.Errorf("Error querying image: %s", err.Error())
|
||||||
}
|
}
|
||||||
@@ -754,7 +765,7 @@ func TestImageQueryTags(t *testing.T) {
|
|||||||
Modifier: models.CriterionModifierIncludesAll,
|
Modifier: models.CriterionModifierIncludesAll,
|
||||||
}
|
}
|
||||||
|
|
||||||
images, _, err = sqb.Query(&imageFilter, nil)
|
images, _, err = queryImagesWithCount(sqb, &imageFilter, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Error querying image: %s", err.Error())
|
t.Errorf("Error querying image: %s", err.Error())
|
||||||
}
|
}
|
||||||
@@ -774,7 +785,7 @@ func TestImageQueryTags(t *testing.T) {
|
|||||||
Q: &q,
|
Q: &q,
|
||||||
}
|
}
|
||||||
|
|
||||||
images, _, err = sqb.Query(&imageFilter, &findFilter)
|
images, _, err = queryImagesWithCount(sqb, &imageFilter, &findFilter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Error querying image: %s", err.Error())
|
t.Errorf("Error querying image: %s", err.Error())
|
||||||
}
|
}
|
||||||
@@ -798,7 +809,7 @@ func TestImageQueryStudio(t *testing.T) {
|
|||||||
Studios: &studioCriterion,
|
Studios: &studioCriterion,
|
||||||
}
|
}
|
||||||
|
|
||||||
images, _, err := sqb.Query(&imageFilter, nil)
|
images, _, err := queryImagesWithCount(sqb, &imageFilter, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Error querying image: %s", err.Error())
|
t.Errorf("Error querying image: %s", err.Error())
|
||||||
}
|
}
|
||||||
@@ -820,7 +831,7 @@ func TestImageQueryStudio(t *testing.T) {
|
|||||||
Q: &q,
|
Q: &q,
|
||||||
}
|
}
|
||||||
|
|
||||||
images, _, err = sqb.Query(&imageFilter, &findFilter)
|
images, _, err = queryImagesWithCount(sqb, &imageFilter, &findFilter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Error querying image: %s", err.Error())
|
t.Errorf("Error querying image: %s", err.Error())
|
||||||
}
|
}
|
||||||
@@ -892,7 +903,7 @@ func TestImageQueryStudioDepth(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func queryImages(t *testing.T, sqb models.ImageReader, imageFilter *models.ImageFilterType, findFilter *models.FindFilterType) []*models.Image {
|
func queryImages(t *testing.T, sqb models.ImageReader, imageFilter *models.ImageFilterType, findFilter *models.FindFilterType) []*models.Image {
|
||||||
images, _, err := sqb.Query(imageFilter, findFilter)
|
images, _, err := queryImagesWithCount(sqb, imageFilter, findFilter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Error querying images: %s", err.Error())
|
t.Errorf("Error querying images: %s", err.Error())
|
||||||
}
|
}
|
||||||
@@ -1047,7 +1058,7 @@ func TestImageQuerySorting(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sqb := r.Image()
|
sqb := r.Image()
|
||||||
images, _, err := sqb.Query(nil, &findFilter)
|
images, _, err := queryImagesWithCount(sqb, nil, &findFilter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Error querying image: %s", err.Error())
|
t.Errorf("Error querying image: %s", err.Error())
|
||||||
}
|
}
|
||||||
@@ -1062,7 +1073,7 @@ func TestImageQuerySorting(t *testing.T) {
|
|||||||
// sort in descending order
|
// sort in descending order
|
||||||
direction = models.SortDirectionEnumDesc
|
direction = models.SortDirectionEnumDesc
|
||||||
|
|
||||||
images, _, err = sqb.Query(nil, &findFilter)
|
images, _, err = queryImagesWithCount(sqb, nil, &findFilter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Error querying image: %s", err.Error())
|
t.Errorf("Error querying image: %s", err.Error())
|
||||||
}
|
}
|
||||||
@@ -1084,7 +1095,7 @@ func TestImageQueryPagination(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sqb := r.Image()
|
sqb := r.Image()
|
||||||
images, _, err := sqb.Query(nil, &findFilter)
|
images, _, err := queryImagesWithCount(sqb, nil, &findFilter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Error querying image: %s", err.Error())
|
t.Errorf("Error querying image: %s", err.Error())
|
||||||
}
|
}
|
||||||
@@ -1095,7 +1106,7 @@ func TestImageQueryPagination(t *testing.T) {
|
|||||||
|
|
||||||
page := 2
|
page := 2
|
||||||
findFilter.Page = &page
|
findFilter.Page = &page
|
||||||
images, _, err = sqb.Query(nil, &findFilter)
|
images, _, err = queryImagesWithCount(sqb, nil, &findFilter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Error querying image: %s", err.Error())
|
t.Errorf("Error querying image: %s", err.Error())
|
||||||
}
|
}
|
||||||
@@ -1107,7 +1118,7 @@ func TestImageQueryPagination(t *testing.T) {
|
|||||||
perPage = 2
|
perPage = 2
|
||||||
page = 1
|
page = 1
|
||||||
|
|
||||||
images, _, err = sqb.Query(nil, &findFilter)
|
images, _, err = queryImagesWithCount(sqb, nil, &findFilter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Error querying image: %s", err.Error())
|
t.Errorf("Error querying image: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -141,8 +141,7 @@ func (qb *movieQueryBuilder) Query(movieFilter *models.MovieFilterType, findFilt
|
|||||||
}
|
}
|
||||||
|
|
||||||
query := qb.newQuery()
|
query := qb.newQuery()
|
||||||
|
distinctIDs(&query, movieTable)
|
||||||
query.body = selectDistinctIDs("movies")
|
|
||||||
|
|
||||||
if q := findFilter.Q; q != nil && *q != "" {
|
if q := findFilter.Q; q != nil && *q != "" {
|
||||||
searchColumns := []string{"movies.name"}
|
searchColumns := []string{"movies.name"}
|
||||||
|
|||||||
@@ -303,10 +303,8 @@ func (qb *performerQueryBuilder) Query(performerFilter *models.PerformerFilterTy
|
|||||||
findFilter = &models.FindFilterType{}
|
findFilter = &models.FindFilterType{}
|
||||||
}
|
}
|
||||||
|
|
||||||
tableName := "performers"
|
|
||||||
query := qb.newQuery()
|
query := qb.newQuery()
|
||||||
|
distinctIDs(&query, performerTable)
|
||||||
query.body = selectDistinctIDs(tableName)
|
|
||||||
|
|
||||||
if q := findFilter.Q; q != nil && *q != "" {
|
if q := findFilter.Q; q != nil && *q != "" {
|
||||||
searchColumns := []string{"performers.name", "performers.aliases"}
|
searchColumns := []string{"performers.name", "performers.aliases"}
|
||||||
|
|||||||
@@ -665,18 +665,24 @@ func verifyPerformersImageCount(t *testing.T, imageCountCriterion models.IntCrit
|
|||||||
for _, performer := range performers {
|
for _, performer := range performers {
|
||||||
pp := 0
|
pp := 0
|
||||||
|
|
||||||
_, count, err := r.Image().Query(&models.ImageFilterType{
|
result, err := r.Image().Query(models.ImageQueryOptions{
|
||||||
Performers: &models.MultiCriterionInput{
|
QueryOptions: models.QueryOptions{
|
||||||
Value: []string{strconv.Itoa(performer.ID)},
|
FindFilter: &models.FindFilterType{
|
||||||
Modifier: models.CriterionModifierIncludes,
|
PerPage: &pp,
|
||||||
|
},
|
||||||
|
Count: true,
|
||||||
|
},
|
||||||
|
ImageFilter: &models.ImageFilterType{
|
||||||
|
Performers: &models.MultiCriterionInput{
|
||||||
|
Value: []string{strconv.Itoa(performer.ID)},
|
||||||
|
Modifier: models.CriterionModifierIncludes,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}, &models.FindFilterType{
|
|
||||||
PerPage: &pp,
|
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
verifyInt(t, count, imageCountCriterion)
|
verifyInt(t, result.Count, imageCountCriterion)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
package sqlite
|
package sqlite
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type queryBuilder struct {
|
type queryBuilder struct {
|
||||||
repository *repository
|
repository *repository
|
||||||
|
|
||||||
body string
|
columns []string
|
||||||
|
from string
|
||||||
|
|
||||||
joins joins
|
joins joins
|
||||||
whereClauses []string
|
whereClauses []string
|
||||||
@@ -21,13 +23,45 @@ type queryBuilder struct {
|
|||||||
err error
|
err error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (qb queryBuilder) body() string {
|
||||||
|
return fmt.Sprintf("SELECT %s FROM %s%s", strings.Join(qb.columns, ", "), qb.from, qb.joins.toSQL())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (qb *queryBuilder) addColumn(column string) {
|
||||||
|
qb.columns = append(qb.columns, column)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (qb queryBuilder) toSQL(includeSortPagination bool) string {
|
||||||
|
body := qb.body()
|
||||||
|
|
||||||
|
withClause := ""
|
||||||
|
if len(qb.withClauses) > 0 {
|
||||||
|
var recursive string
|
||||||
|
if qb.recursiveWith {
|
||||||
|
recursive = " RECURSIVE "
|
||||||
|
}
|
||||||
|
withClause = "WITH " + recursive + strings.Join(qb.withClauses, ", ") + " "
|
||||||
|
}
|
||||||
|
|
||||||
|
body = withClause + qb.repository.buildQueryBody(body, qb.whereClauses, qb.havingClauses)
|
||||||
|
if includeSortPagination {
|
||||||
|
body += qb.sortAndPagination
|
||||||
|
}
|
||||||
|
|
||||||
|
return body
|
||||||
|
}
|
||||||
|
|
||||||
|
func (qb queryBuilder) findIDs() ([]int, error) {
|
||||||
|
const includeSortPagination = true
|
||||||
|
return qb.repository.runIdsQuery(qb.toSQL(includeSortPagination), qb.args)
|
||||||
|
}
|
||||||
|
|
||||||
func (qb queryBuilder) executeFind() ([]int, int, error) {
|
func (qb queryBuilder) executeFind() ([]int, int, error) {
|
||||||
if qb.err != nil {
|
if qb.err != nil {
|
||||||
return nil, 0, qb.err
|
return nil, 0, qb.err
|
||||||
}
|
}
|
||||||
|
|
||||||
body := qb.body
|
body := qb.body()
|
||||||
body += qb.joins.toSQL()
|
|
||||||
|
|
||||||
return qb.repository.executeFindQuery(body, qb.args, qb.sortAndPagination, qb.whereClauses, qb.havingClauses, qb.withClauses, qb.recursiveWith)
|
return qb.repository.executeFindQuery(body, qb.args, qb.sortAndPagination, qb.whereClauses, qb.havingClauses, qb.withClauses, qb.recursiveWith)
|
||||||
}
|
}
|
||||||
@@ -37,8 +71,7 @@ func (qb queryBuilder) executeCount() (int, error) {
|
|||||||
return 0, qb.err
|
return 0, qb.err
|
||||||
}
|
}
|
||||||
|
|
||||||
body := qb.body
|
body := qb.body()
|
||||||
body += qb.joins.toSQL()
|
|
||||||
|
|
||||||
withClause := ""
|
withClause := ""
|
||||||
if len(qb.withClauses) > 0 {
|
if len(qb.withClauses) > 0 {
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ func (r *repository) get(id int, dest interface{}) error {
|
|||||||
|
|
||||||
func (r *repository) getAll(id int, f func(rows *sqlx.Rows) error) error {
|
func (r *repository) getAll(id int, f func(rows *sqlx.Rows) error) error {
|
||||||
stmt := fmt.Sprintf("SELECT * FROM %s WHERE %s = ?", r.tableName, r.idColumn)
|
stmt := fmt.Sprintf("SELECT * FROM %s WHERE %s = ?", r.tableName, r.idColumn)
|
||||||
return r.queryFunc(stmt, []interface{}{id}, f)
|
return r.queryFunc(stmt, []interface{}{id}, false, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *repository) insert(obj interface{}) (sql.Result, error) {
|
func (r *repository) insert(obj interface{}) (sql.Result, error) {
|
||||||
@@ -170,7 +170,7 @@ func (r *repository) runSumQuery(query string, args []interface{}) (float64, err
|
|||||||
return result.Float64, nil
|
return result.Float64, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *repository) queryFunc(query string, args []interface{}, f func(rows *sqlx.Rows) error) error {
|
func (r *repository) queryFunc(query string, args []interface{}, single bool, f func(rows *sqlx.Rows) error) error {
|
||||||
rows, err := r.tx.Queryx(query, args...)
|
rows, err := r.tx.Queryx(query, args...)
|
||||||
|
|
||||||
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||||
@@ -182,6 +182,9 @@ func (r *repository) queryFunc(query string, args []interface{}, f func(rows *sq
|
|||||||
if err := f(rows); err != nil {
|
if err := f(rows); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if single {
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := rows.Err(); err != nil {
|
if err := rows.Err(); err != nil {
|
||||||
@@ -192,26 +195,23 @@ func (r *repository) queryFunc(query string, args []interface{}, f func(rows *sq
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *repository) query(query string, args []interface{}, out objectList) error {
|
func (r *repository) query(query string, args []interface{}, out objectList) error {
|
||||||
rows, err := r.tx.Queryx(query, args...)
|
return r.queryFunc(query, args, false, func(rows *sqlx.Rows) error {
|
||||||
|
|
||||||
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer rows.Close()
|
|
||||||
|
|
||||||
for rows.Next() {
|
|
||||||
object := out.New()
|
object := out.New()
|
||||||
if err := rows.StructScan(object); err != nil {
|
if err := rows.StructScan(object); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
out.Append(object)
|
out.Append(object)
|
||||||
}
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
if err := rows.Err(); err != nil {
|
func (r *repository) queryStruct(query string, args []interface{}, out interface{}) error {
|
||||||
return err
|
return r.queryFunc(query, args, true, func(rows *sqlx.Rows) error {
|
||||||
}
|
if err := rows.StructScan(out); err != nil {
|
||||||
|
return err
|
||||||
return nil
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *repository) querySimple(query string, args []interface{}, out interface{}) error {
|
func (r *repository) querySimple(query string, args []interface{}, out interface{}) error {
|
||||||
@@ -361,7 +361,7 @@ type stringRepository struct {
|
|||||||
func (r *stringRepository) get(id int) ([]string, error) {
|
func (r *stringRepository) get(id int) ([]string, error) {
|
||||||
query := fmt.Sprintf("SELECT %s from %s WHERE %s = ?", r.stringColumn, r.tableName, r.idColumn)
|
query := fmt.Sprintf("SELECT %s from %s WHERE %s = ?", r.stringColumn, r.tableName, r.idColumn)
|
||||||
var ret []string
|
var ret []string
|
||||||
err := r.queryFunc(query, []interface{}{id}, func(rows *sqlx.Rows) error {
|
err := r.queryFunc(query, []interface{}{id}, false, func(rows *sqlx.Rows) error {
|
||||||
var out string
|
var out string
|
||||||
if err := rows.Scan(&out); err != nil {
|
if err := rows.Scan(&out); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -395,7 +395,10 @@ func (qb *sceneQueryBuilder) makeFilter(sceneFilter *models.SceneFilterType) *fi
|
|||||||
return query
|
return query
|
||||||
}
|
}
|
||||||
|
|
||||||
func (qb *sceneQueryBuilder) Query(sceneFilter *models.SceneFilterType, findFilter *models.FindFilterType) ([]*models.Scene, int, error) {
|
func (qb *sceneQueryBuilder) Query(options models.SceneQueryOptions) (*models.SceneQueryResult, error) {
|
||||||
|
sceneFilter := options.SceneFilter
|
||||||
|
findFilter := options.FindFilter
|
||||||
|
|
||||||
if sceneFilter == nil {
|
if sceneFilter == nil {
|
||||||
sceneFilter = &models.SceneFilterType{}
|
sceneFilter = &models.SceneFilterType{}
|
||||||
}
|
}
|
||||||
@@ -404,8 +407,7 @@ func (qb *sceneQueryBuilder) Query(sceneFilter *models.SceneFilterType, findFilt
|
|||||||
}
|
}
|
||||||
|
|
||||||
query := qb.newQuery()
|
query := qb.newQuery()
|
||||||
|
distinctIDs(&query, sceneTable)
|
||||||
query.body = selectDistinctIDs(sceneTable)
|
|
||||||
|
|
||||||
if q := findFilter.Q; q != nil && *q != "" {
|
if q := findFilter.Q; q != nil && *q != "" {
|
||||||
query.join("scene_markers", "", "scene_markers.scene_id = scenes.id")
|
query.join("scene_markers", "", "scene_markers.scene_id = scenes.id")
|
||||||
@@ -416,7 +418,7 @@ func (qb *sceneQueryBuilder) Query(sceneFilter *models.SceneFilterType, findFilt
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err := qb.validateFilter(sceneFilter); err != nil {
|
if err := qb.validateFilter(sceneFilter); err != nil {
|
||||||
return nil, 0, err
|
return nil, err
|
||||||
}
|
}
|
||||||
filter := qb.makeFilter(sceneFilter)
|
filter := qb.makeFilter(sceneFilter)
|
||||||
|
|
||||||
@@ -425,21 +427,59 @@ func (qb *sceneQueryBuilder) Query(sceneFilter *models.SceneFilterType, findFilt
|
|||||||
qb.setSceneSort(&query, findFilter)
|
qb.setSceneSort(&query, findFilter)
|
||||||
query.sortAndPagination += getPagination(findFilter)
|
query.sortAndPagination += getPagination(findFilter)
|
||||||
|
|
||||||
idsResult, countResult, err := query.executeFind()
|
result, err := qb.queryGroupedFields(options, query)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, err
|
return nil, fmt.Errorf("error querying aggregate fields: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var scenes []*models.Scene
|
idsResult, err := query.findIDs()
|
||||||
for _, id := range idsResult {
|
if err != nil {
|
||||||
scene, err := qb.Find(id)
|
return nil, fmt.Errorf("error finding IDs: %w", err)
|
||||||
if err != nil {
|
|
||||||
return nil, 0, err
|
|
||||||
}
|
|
||||||
scenes = append(scenes, scene)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return scenes, countResult, nil
|
result.IDs = idsResult
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (qb *sceneQueryBuilder) queryGroupedFields(options models.SceneQueryOptions, query queryBuilder) (*models.SceneQueryResult, error) {
|
||||||
|
if !options.Count && !options.TotalDuration && !options.TotalSize {
|
||||||
|
// nothing to do - return empty result
|
||||||
|
return models.NewSceneQueryResult(qb), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
aggregateQuery := qb.newQuery()
|
||||||
|
|
||||||
|
if options.Count {
|
||||||
|
aggregateQuery.addColumn("COUNT(temp.id) as total")
|
||||||
|
}
|
||||||
|
|
||||||
|
if options.TotalDuration {
|
||||||
|
query.addColumn("COALESCE(scenes.duration, 0) as duration")
|
||||||
|
aggregateQuery.addColumn("COALESCE(SUM(temp.duration), 0) as duration")
|
||||||
|
}
|
||||||
|
|
||||||
|
if options.TotalSize {
|
||||||
|
query.addColumn("COALESCE(scenes.size, 0) as size")
|
||||||
|
aggregateQuery.addColumn("COALESCE(SUM(temp.size), 0) as size")
|
||||||
|
}
|
||||||
|
|
||||||
|
const includeSortPagination = false
|
||||||
|
aggregateQuery.from = fmt.Sprintf("(%s) as temp", query.toSQL(includeSortPagination))
|
||||||
|
|
||||||
|
out := struct {
|
||||||
|
Total int
|
||||||
|
Duration float64
|
||||||
|
Size int
|
||||||
|
}{}
|
||||||
|
if err := qb.repository.queryStruct(aggregateQuery.toSQL(includeSortPagination), query.args, &out); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ret := models.NewSceneQueryResult(qb)
|
||||||
|
ret.Count = out.Total
|
||||||
|
ret.TotalDuration = out.Duration
|
||||||
|
ret.TotalSize = out.Size
|
||||||
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func phashCriterionHandler(phashFilter *models.StringCriterionInput) criterionHandlerFunc {
|
func phashCriterionHandler(phashFilter *models.StringCriterionInput) criterionHandlerFunc {
|
||||||
@@ -848,7 +888,7 @@ func (qb *sceneQueryBuilder) FindDuplicates(distance int) ([][]*models.Scene, er
|
|||||||
} else {
|
} else {
|
||||||
var hashes []*utils.Phash
|
var hashes []*utils.Phash
|
||||||
|
|
||||||
if err := qb.queryFunc(findAllPhashesQuery, nil, func(rows *sqlx.Rows) error {
|
if err := qb.queryFunc(findAllPhashesQuery, nil, false, func(rows *sqlx.Rows) error {
|
||||||
phash := utils.Phash{
|
phash := utils.Phash{
|
||||||
Bucket: -1,
|
Bucket: -1,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -147,8 +147,7 @@ func (qb *sceneMarkerQueryBuilder) Query(sceneMarkerFilter *models.SceneMarkerFi
|
|||||||
}
|
}
|
||||||
|
|
||||||
query := qb.newQuery()
|
query := qb.newQuery()
|
||||||
|
distinctIDs(&query, sceneMarkerTable)
|
||||||
query.body = selectDistinctIDs("scene_markers")
|
|
||||||
|
|
||||||
if q := findFilter.Q; q != nil && *q != "" {
|
if q := findFilter.Q; q != nil && *q != "" {
|
||||||
searchColumns := []string{"scene_markers.title", "scenes.title"}
|
searchColumns := []string{"scene_markers.title", "scenes.title"}
|
||||||
|
|||||||
@@ -141,9 +141,19 @@ func TestSceneQueryQ(t *testing.T) {
|
|||||||
|
|
||||||
func queryScene(t *testing.T, sqb models.SceneReader, sceneFilter *models.SceneFilterType, findFilter *models.FindFilterType) []*models.Scene {
|
func queryScene(t *testing.T, sqb models.SceneReader, sceneFilter *models.SceneFilterType, findFilter *models.FindFilterType) []*models.Scene {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
scenes, _, err := sqb.Query(sceneFilter, findFilter)
|
result, err := sqb.Query(models.SceneQueryOptions{
|
||||||
|
QueryOptions: models.QueryOptions{
|
||||||
|
FindFilter: findFilter,
|
||||||
|
},
|
||||||
|
SceneFilter: sceneFilter,
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Error querying scene: %s", err.Error())
|
t.Errorf("Error querying scene: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
scenes, err := result.Resolve()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Error resolving scenes: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return scenes
|
return scenes
|
||||||
@@ -346,17 +356,21 @@ func TestSceneIllegalQuery(t *testing.T) {
|
|||||||
withTxn(func(r models.Repository) error {
|
withTxn(func(r models.Repository) error {
|
||||||
sqb := r.Scene()
|
sqb := r.Scene()
|
||||||
|
|
||||||
_, _, err := sqb.Query(sceneFilter, nil)
|
queryOptions := models.SceneQueryOptions{
|
||||||
|
SceneFilter: sceneFilter,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := sqb.Query(queryOptions)
|
||||||
assert.NotNil(err)
|
assert.NotNil(err)
|
||||||
|
|
||||||
sceneFilter.Or = nil
|
sceneFilter.Or = nil
|
||||||
sceneFilter.Not = &subFilter
|
sceneFilter.Not = &subFilter
|
||||||
_, _, err = sqb.Query(sceneFilter, nil)
|
_, err = sqb.Query(queryOptions)
|
||||||
assert.NotNil(err)
|
assert.NotNil(err)
|
||||||
|
|
||||||
sceneFilter.And = nil
|
sceneFilter.And = nil
|
||||||
sceneFilter.Or = &subFilter
|
sceneFilter.Or = &subFilter
|
||||||
_, _, err = sqb.Query(sceneFilter, nil)
|
_, err = sqb.Query(queryOptions)
|
||||||
assert.NotNil(err)
|
assert.NotNil(err)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -20,9 +20,9 @@ func selectAll(tableName string) string {
|
|||||||
return "SELECT " + idColumn + " FROM " + tableName + " "
|
return "SELECT " + idColumn + " FROM " + tableName + " "
|
||||||
}
|
}
|
||||||
|
|
||||||
func selectDistinctIDs(tableName string) string {
|
func distinctIDs(qb *queryBuilder, tableName string) {
|
||||||
idColumn := getColumn(tableName, "id")
|
qb.addColumn("DISTINCT " + getColumn(tableName, "id"))
|
||||||
return "SELECT DISTINCT " + idColumn + " FROM " + tableName + " "
|
qb.from = tableName
|
||||||
}
|
}
|
||||||
|
|
||||||
func getColumn(tableName string, columnName string) string {
|
func getColumn(tableName string, columnName string) string {
|
||||||
|
|||||||
@@ -228,8 +228,7 @@ func (qb *studioQueryBuilder) Query(studioFilter *models.StudioFilterType, findF
|
|||||||
}
|
}
|
||||||
|
|
||||||
query := qb.newQuery()
|
query := qb.newQuery()
|
||||||
|
distinctIDs(&query, studioTable)
|
||||||
query.body = selectDistinctIDs("studios")
|
|
||||||
|
|
||||||
if q := findFilter.Q; q != nil && *q != "" {
|
if q := findFilter.Q; q != nil && *q != "" {
|
||||||
query.join(studioAliasesTable, "", "studio_aliases.studio_id = studios.id")
|
query.join(studioAliasesTable, "", "studio_aliases.studio_id = studios.id")
|
||||||
|
|||||||
@@ -507,18 +507,24 @@ func verifyStudiosImageCount(t *testing.T, imageCountCriterion models.IntCriteri
|
|||||||
for _, studio := range studios {
|
for _, studio := range studios {
|
||||||
pp := 0
|
pp := 0
|
||||||
|
|
||||||
_, count, err := r.Image().Query(&models.ImageFilterType{
|
result, err := r.Image().Query(models.ImageQueryOptions{
|
||||||
Studios: &models.HierarchicalMultiCriterionInput{
|
QueryOptions: models.QueryOptions{
|
||||||
Value: []string{strconv.Itoa(studio.ID)},
|
FindFilter: &models.FindFilterType{
|
||||||
Modifier: models.CriterionModifierIncludes,
|
PerPage: &pp,
|
||||||
|
},
|
||||||
|
Count: true,
|
||||||
|
},
|
||||||
|
ImageFilter: &models.ImageFilterType{
|
||||||
|
Studios: &models.HierarchicalMultiCriterionInput{
|
||||||
|
Value: []string{strconv.Itoa(studio.ID)},
|
||||||
|
Modifier: models.CriterionModifierIncludes,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}, &models.FindFilterType{
|
|
||||||
PerPage: &pp,
|
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
verifyInt(t, count, imageCountCriterion)
|
verifyInt(t, result.Count, imageCountCriterion)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -324,8 +324,7 @@ func (qb *tagQueryBuilder) Query(tagFilter *models.TagFilterType, findFilter *mo
|
|||||||
}
|
}
|
||||||
|
|
||||||
query := qb.newQuery()
|
query := qb.newQuery()
|
||||||
|
distinctIDs(&query, tagTable)
|
||||||
query.body = selectDistinctIDs(tagTable)
|
|
||||||
|
|
||||||
if q := findFilter.Q; q != nil && *q != "" {
|
if q := findFilter.Q; q != nil && *q != "" {
|
||||||
query.join(tagAliasesTable, "", "tag_aliases.tag_id = tags.id")
|
query.join(tagAliasesTable, "", "tag_aliases.tag_id = tags.id")
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package tag
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/stashapp/stash/pkg/models"
|
"github.com/stashapp/stash/pkg/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,8 @@
|
|||||||
* Added interface options to disable creating performers/studios/tags from dropdown selectors. ([#1814](https://github.com/stashapp/stash/pull/1814))
|
* Added interface options to disable creating performers/studios/tags from dropdown selectors. ([#1814](https://github.com/stashapp/stash/pull/1814))
|
||||||
|
|
||||||
### 🎨 Improvements
|
### 🎨 Improvements
|
||||||
|
* Show pagination at top and bottom of page. ([#1776](https://github.com/stashapp/stash/pull/1776))
|
||||||
|
* Include total duration/megapixels and filesize information on Scenes and Images pages. ([#1776](https://github.com/stashapp/stash/pull/1776))
|
||||||
* Added it-IT language option. ([#1875](https://github.com/stashapp/stash/pull/1875))
|
* Added it-IT language option. ([#1875](https://github.com/stashapp/stash/pull/1875))
|
||||||
* Optimised generate process. ([#1871](https://github.com/stashapp/stash/pull/1871))
|
* Optimised generate process. ([#1871](https://github.com/stashapp/stash/pull/1871))
|
||||||
* Added clear button to query text field. ([#1845](https://github.com/stashapp/stash/pull/1845))
|
* Added clear button to query text field. ([#1845](https://github.com/stashapp/stash/pull/1845))
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ interface IPaginationProps {
|
|||||||
itemsPerPage: number;
|
itemsPerPage: number;
|
||||||
currentPage: number;
|
currentPage: number;
|
||||||
totalItems: number;
|
totalItems: number;
|
||||||
|
metadataByline?: React.ReactNode;
|
||||||
onChangePage: (page: number) => void;
|
onChangePage: (page: number) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -13,6 +14,7 @@ interface IPaginationIndexProps {
|
|||||||
itemsPerPage: number;
|
itemsPerPage: number;
|
||||||
currentPage: number;
|
currentPage: number;
|
||||||
totalItems: number;
|
totalItems: number;
|
||||||
|
metadataByline?: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Pagination: React.FC<IPaginationProps> = ({
|
export const Pagination: React.FC<IPaginationProps> = ({
|
||||||
@@ -115,6 +117,7 @@ export const PaginationIndex: React.FC<IPaginationIndexProps> = ({
|
|||||||
itemsPerPage,
|
itemsPerPage,
|
||||||
currentPage,
|
currentPage,
|
||||||
totalItems,
|
totalItems,
|
||||||
|
metadataByline,
|
||||||
}) => {
|
}) => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
||||||
@@ -132,8 +135,10 @@ export const PaginationIndex: React.FC<IPaginationIndexProps> = ({
|
|||||||
)}-${intl.formatNumber(lastItemCount)} of ${intl.formatNumber(totalItems)}`;
|
)}-${intl.formatNumber(lastItemCount)} of ${intl.formatNumber(totalItems)}`;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span className="filter-container text-muted paginationIndex">
|
<span className="filter-container text-muted paginationIndex center-text">
|
||||||
{indexText}
|
{indexText}
|
||||||
|
<br />
|
||||||
|
{metadataByline}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -17,6 +17,10 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.center-text {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
input[type="range"].zoom-slider {
|
input[type="range"].zoom-slider {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
|||||||
@@ -402,6 +402,7 @@ export const SceneDuplicateChecker: React.FC = () => {
|
|||||||
itemsPerPage={pageSize}
|
itemsPerPage={pageSize}
|
||||||
currentPage={currentPage}
|
currentPage={currentPage}
|
||||||
totalItems={scenes.length}
|
totalItems={scenes.length}
|
||||||
|
metadataByline={[]}
|
||||||
onChangePage={(newPage) =>
|
onChangePage={(newPage) =>
|
||||||
setQuery({ page: newPage === 1 ? undefined : newPage })
|
setQuery({ page: newPage === 1 ? undefined : newPage })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -395,6 +395,7 @@ export const SceneFilenameParser: React.FC = () => {
|
|||||||
currentPage={parserInput.page}
|
currentPage={parserInput.page}
|
||||||
itemsPerPage={parserInput.pageSize}
|
itemsPerPage={parserInput.pageSize}
|
||||||
totalItems={totalItems}
|
totalItems={totalItems}
|
||||||
|
metadataByline={[]}
|
||||||
onChangePage={(page) => onPageChanged(page)}
|
onChangePage={(page) => onPageChanged(page)}
|
||||||
/>
|
/>
|
||||||
<Button variant="primary" onClick={onApply}>
|
<Button variant="primary" onClick={onApply}>
|
||||||
|
|||||||
@@ -58,6 +58,8 @@ import {
|
|||||||
CriterionValue,
|
CriterionValue,
|
||||||
} from "src/models/list-filter/criteria/criterion";
|
} from "src/models/list-filter/criteria/criterion";
|
||||||
import { AddFilterDialog } from "src/components/List/AddFilterDialog";
|
import { AddFilterDialog } from "src/components/List/AddFilterDialog";
|
||||||
|
import { TextUtils } from "src/utils";
|
||||||
|
import { FormattedNumber } from "react-intl";
|
||||||
|
|
||||||
const getSelectedData = <I extends IDataItem>(
|
const getSelectedData = <I extends IDataItem>(
|
||||||
result: I[],
|
result: I[],
|
||||||
@@ -159,6 +161,7 @@ interface IQuery<T extends IQueryResult, T2 extends IDataItem> {
|
|||||||
useData: (filter: ListFilterModel) => T;
|
useData: (filter: ListFilterModel) => T;
|
||||||
getData: (data: T) => T2[];
|
getData: (data: T) => T2[];
|
||||||
getCount: (data: T) => number;
|
getCount: (data: T) => number;
|
||||||
|
getMetadataByline: (data: T) => React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IRenderListProps {
|
interface IRenderListProps {
|
||||||
@@ -179,6 +182,7 @@ const RenderList = <
|
|||||||
useData,
|
useData,
|
||||||
getCount,
|
getCount,
|
||||||
getData,
|
getData,
|
||||||
|
getMetadataByline,
|
||||||
otherOperations,
|
otherOperations,
|
||||||
renderContent,
|
renderContent,
|
||||||
zoomable,
|
zoomable,
|
||||||
@@ -203,6 +207,7 @@ const RenderList = <
|
|||||||
|
|
||||||
const result = useData(filter);
|
const result = useData(filter);
|
||||||
const totalCount = getCount(result);
|
const totalCount = getCount(result);
|
||||||
|
const metadataByline = getMetadataByline(result);
|
||||||
const items = getData(result);
|
const items = getData(result);
|
||||||
const pages = Math.ceil(totalCount / filter.itemsPerPage);
|
const pages = Math.ceil(totalCount / filter.itemsPerPage);
|
||||||
|
|
||||||
@@ -397,6 +402,7 @@ const RenderList = <
|
|||||||
itemsPerPage={filter.itemsPerPage}
|
itemsPerPage={filter.itemsPerPage}
|
||||||
currentPage={filter.currentPage}
|
currentPage={filter.currentPage}
|
||||||
totalItems={totalCount}
|
totalItems={totalCount}
|
||||||
|
metadataByline={metadataByline}
|
||||||
onChangePage={onChangePage}
|
onChangePage={onChangePage}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@@ -409,11 +415,18 @@ const RenderList = <
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{renderPagination()}
|
{renderPagination()}
|
||||||
|
<PaginationIndex
|
||||||
|
itemsPerPage={filter.itemsPerPage}
|
||||||
|
currentPage={filter.currentPage}
|
||||||
|
totalItems={totalCount}
|
||||||
|
metadataByline={metadataByline}
|
||||||
|
/>
|
||||||
{renderContent(result, filter, selectedIds, onChangePage, pages)}
|
{renderContent(result, filter, selectedIds, onChangePage, pages)}
|
||||||
<PaginationIndex
|
<PaginationIndex
|
||||||
itemsPerPage={filter.itemsPerPage}
|
itemsPerPage={filter.itemsPerPage}
|
||||||
currentPage={filter.currentPage}
|
currentPage={filter.currentPage}
|
||||||
totalItems={totalCount}
|
totalItems={totalCount}
|
||||||
|
metadataByline={metadataByline}
|
||||||
/>
|
/>
|
||||||
{renderPagination()}
|
{renderPagination()}
|
||||||
</>
|
</>
|
||||||
@@ -732,6 +745,41 @@ export const useScenesList = (
|
|||||||
result?.data?.findScenes?.scenes ?? [],
|
result?.data?.findScenes?.scenes ?? [],
|
||||||
getCount: (result: FindScenesQueryResult) =>
|
getCount: (result: FindScenesQueryResult) =>
|
||||||
result?.data?.findScenes?.count ?? 0,
|
result?.data?.findScenes?.count ?? 0,
|
||||||
|
getMetadataByline: (result: FindScenesQueryResult) => {
|
||||||
|
const duration = result?.data?.findScenes?.duration;
|
||||||
|
const size = result?.data?.findScenes?.filesize;
|
||||||
|
const filesize = size ? TextUtils.fileSize(size) : undefined;
|
||||||
|
|
||||||
|
if (!duration && !size) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const separator = duration && size ? " - " : "";
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span className="scenes-stats">
|
||||||
|
(
|
||||||
|
{duration ? (
|
||||||
|
<span className="scenes-duration">
|
||||||
|
{TextUtils.secondsAsTimeString(duration, 3)}
|
||||||
|
</span>
|
||||||
|
) : undefined}
|
||||||
|
{separator}
|
||||||
|
{size && filesize ? (
|
||||||
|
<span className="scenes-size">
|
||||||
|
<FormattedNumber
|
||||||
|
value={filesize.size}
|
||||||
|
maximumFractionDigits={TextUtils.fileSizeFractionalDigits(
|
||||||
|
filesize.unit
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
{` ${TextUtils.formatFileSizeUnit(filesize.unit)}`}
|
||||||
|
</span>
|
||||||
|
) : undefined}
|
||||||
|
)
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export const useSceneMarkersList = (
|
export const useSceneMarkersList = (
|
||||||
@@ -745,6 +793,7 @@ export const useSceneMarkersList = (
|
|||||||
result?.data?.findSceneMarkers?.scene_markers ?? [],
|
result?.data?.findSceneMarkers?.scene_markers ?? [],
|
||||||
getCount: (result: FindSceneMarkersQueryResult) =>
|
getCount: (result: FindSceneMarkersQueryResult) =>
|
||||||
result?.data?.findSceneMarkers?.count ?? 0,
|
result?.data?.findSceneMarkers?.count ?? 0,
|
||||||
|
getMetadataByline: () => [],
|
||||||
});
|
});
|
||||||
|
|
||||||
export const useImagesList = (
|
export const useImagesList = (
|
||||||
@@ -758,6 +807,41 @@ export const useImagesList = (
|
|||||||
result?.data?.findImages?.images ?? [],
|
result?.data?.findImages?.images ?? [],
|
||||||
getCount: (result: FindImagesQueryResult) =>
|
getCount: (result: FindImagesQueryResult) =>
|
||||||
result?.data?.findImages?.count ?? 0,
|
result?.data?.findImages?.count ?? 0,
|
||||||
|
getMetadataByline: (result: FindImagesQueryResult) => {
|
||||||
|
const megapixels = result?.data?.findImages?.megapixels;
|
||||||
|
const size = result?.data?.findImages?.filesize;
|
||||||
|
const filesize = size ? TextUtils.fileSize(size) : undefined;
|
||||||
|
|
||||||
|
if (!megapixels && !size) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const separator = megapixels && size ? " - " : "";
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span className="images-stats">
|
||||||
|
(
|
||||||
|
{megapixels ? (
|
||||||
|
<span className="images-megapixels">
|
||||||
|
<FormattedNumber value={megapixels} /> Megapixels
|
||||||
|
</span>
|
||||||
|
) : undefined}
|
||||||
|
{separator}
|
||||||
|
{size && filesize ? (
|
||||||
|
<span className="images-size">
|
||||||
|
<FormattedNumber
|
||||||
|
value={filesize.size}
|
||||||
|
maximumFractionDigits={TextUtils.fileSizeFractionalDigits(
|
||||||
|
filesize.unit
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
{` ${TextUtils.formatFileSizeUnit(filesize.unit)}`}
|
||||||
|
</span>
|
||||||
|
) : undefined}
|
||||||
|
)
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export const useGalleriesList = (
|
export const useGalleriesList = (
|
||||||
@@ -771,6 +855,7 @@ export const useGalleriesList = (
|
|||||||
result?.data?.findGalleries?.galleries ?? [],
|
result?.data?.findGalleries?.galleries ?? [],
|
||||||
getCount: (result: FindGalleriesQueryResult) =>
|
getCount: (result: FindGalleriesQueryResult) =>
|
||||||
result?.data?.findGalleries?.count ?? 0,
|
result?.data?.findGalleries?.count ?? 0,
|
||||||
|
getMetadataByline: () => [],
|
||||||
});
|
});
|
||||||
|
|
||||||
export const useStudiosList = (
|
export const useStudiosList = (
|
||||||
@@ -784,6 +869,7 @@ export const useStudiosList = (
|
|||||||
result?.data?.findStudios?.studios ?? [],
|
result?.data?.findStudios?.studios ?? [],
|
||||||
getCount: (result: FindStudiosQueryResult) =>
|
getCount: (result: FindStudiosQueryResult) =>
|
||||||
result?.data?.findStudios?.count ?? 0,
|
result?.data?.findStudios?.count ?? 0,
|
||||||
|
getMetadataByline: () => [],
|
||||||
});
|
});
|
||||||
|
|
||||||
export const usePerformersList = (
|
export const usePerformersList = (
|
||||||
@@ -797,6 +883,7 @@ export const usePerformersList = (
|
|||||||
result?.data?.findPerformers?.performers ?? [],
|
result?.data?.findPerformers?.performers ?? [],
|
||||||
getCount: (result: FindPerformersQueryResult) =>
|
getCount: (result: FindPerformersQueryResult) =>
|
||||||
result?.data?.findPerformers?.count ?? 0,
|
result?.data?.findPerformers?.count ?? 0,
|
||||||
|
getMetadataByline: () => [],
|
||||||
});
|
});
|
||||||
|
|
||||||
export const useMoviesList = (
|
export const useMoviesList = (
|
||||||
@@ -810,6 +897,7 @@ export const useMoviesList = (
|
|||||||
result?.data?.findMovies?.movies ?? [],
|
result?.data?.findMovies?.movies ?? [],
|
||||||
getCount: (result: FindMoviesQueryResult) =>
|
getCount: (result: FindMoviesQueryResult) =>
|
||||||
result?.data?.findMovies?.count ?? 0,
|
result?.data?.findMovies?.count ?? 0,
|
||||||
|
getMetadataByline: () => [],
|
||||||
});
|
});
|
||||||
|
|
||||||
export const useTagsList = (
|
export const useTagsList = (
|
||||||
@@ -823,6 +911,7 @@ export const useTagsList = (
|
|||||||
result?.data?.findTags?.tags ?? [],
|
result?.data?.findTags?.tags ?? [],
|
||||||
getCount: (result: FindTagsQueryResult) =>
|
getCount: (result: FindTagsQueryResult) =>
|
||||||
result?.data?.findTags?.count ?? 0,
|
result?.data?.findTags?.count ?? 0,
|
||||||
|
getMetadataByline: () => [],
|
||||||
});
|
});
|
||||||
|
|
||||||
export const showWhenSelected = <T extends IQueryResult>(
|
export const showWhenSelected = <T extends IQueryResult>(
|
||||||
|
|||||||
Reference in New Issue
Block a user