mirror of
https://github.com/stashapp/stash.git
synced 2025-12-17 20:34:37 +03:00
[Feature] Better resolution search (#1568)
* Fix width in database test setup * Added more filters on resolution field * added test to verify resolution range is defined for every resolution * Refactor UI code Co-authored-by: WithoutPants <53250216+WithoutPants@users.noreply.github.com>
This commit is contained in:
@@ -3,7 +3,6 @@ package sqlite
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/stashapp/stash/pkg/models"
|
||||
)
|
||||
@@ -426,23 +425,25 @@ func galleryPerformerTagsCriterionHandler(qb *galleryQueryBuilder, performerTags
|
||||
}
|
||||
}
|
||||
|
||||
func galleryAverageResolutionCriterionHandler(qb *galleryQueryBuilder, resolution *models.ResolutionEnum) criterionHandlerFunc {
|
||||
func galleryAverageResolutionCriterionHandler(qb *galleryQueryBuilder, resolution *models.ResolutionCriterionInput) criterionHandlerFunc {
|
||||
return func(f *filterBuilder) {
|
||||
if resolution != nil && resolution.IsValid() {
|
||||
if resolution != nil && resolution.Value.IsValid() {
|
||||
qb.imagesRepository().join(f, "images_join", "galleries.id")
|
||||
f.addJoin("images", "", "images_join.image_id = images.id")
|
||||
|
||||
min := resolution.GetMinResolution()
|
||||
max := resolution.GetMaxResolution()
|
||||
min := resolution.Value.GetMinResolution()
|
||||
max := resolution.Value.GetMaxResolution()
|
||||
|
||||
const widthHeight = "avg(MIN(images.width, images.height))"
|
||||
|
||||
if min > 0 {
|
||||
f.addHaving(widthHeight + " >= " + strconv.Itoa(min))
|
||||
}
|
||||
|
||||
if max > 0 {
|
||||
f.addHaving(widthHeight + " < " + strconv.Itoa(max))
|
||||
if resolution.Modifier == models.CriterionModifierEquals {
|
||||
f.addHaving(fmt.Sprintf("%s BETWEEN %d AND %d", widthHeight, min, max))
|
||||
} else if resolution.Modifier == models.CriterionModifierNotEquals {
|
||||
f.addHaving(fmt.Sprintf("%s NOT BETWEEN %d AND %d", widthHeight, min, max))
|
||||
} else if resolution.Modifier == models.CriterionModifierLessThan {
|
||||
f.addHaving(fmt.Sprintf("%s < %d", widthHeight, min))
|
||||
} else if resolution.Modifier == models.CriterionModifierGreaterThan {
|
||||
f.addHaving(fmt.Sprintf("%s > %d", widthHeight, max))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -914,7 +914,10 @@ func TestGalleryQueryAverageResolution(t *testing.T) {
|
||||
qb := r.Gallery()
|
||||
resolution := models.ResolutionEnumLow
|
||||
galleryFilter := models.GalleryFilterType{
|
||||
AverageResolution: &resolution,
|
||||
AverageResolution: &models.ResolutionCriterionInput{
|
||||
Value: resolution,
|
||||
Modifier: models.CriterionModifierEquals,
|
||||
},
|
||||
}
|
||||
|
||||
// not verifying average - just ensure we get at least one
|
||||
|
||||
@@ -389,7 +389,10 @@ func verifyImagesResolution(t *testing.T, resolution models.ResolutionEnum) {
|
||||
withTxn(func(r models.Repository) error {
|
||||
sqb := r.Image()
|
||||
imageFilter := models.ImageFilterType{
|
||||
Resolution: &resolution,
|
||||
Resolution: &models.ResolutionCriterionInput{
|
||||
Value: resolution,
|
||||
Modifier: models.CriterionModifierEquals,
|
||||
},
|
||||
}
|
||||
|
||||
images, _, err := sqb.Query(&imageFilter, nil)
|
||||
|
||||
@@ -495,20 +495,22 @@ func getDurationWhereClause(durationFilter models.IntCriterionInput, column stri
|
||||
return clause, args
|
||||
}
|
||||
|
||||
func resolutionCriterionHandler(resolution *models.ResolutionEnum, heightColumn string, widthColumn string) criterionHandlerFunc {
|
||||
func resolutionCriterionHandler(resolution *models.ResolutionCriterionInput, heightColumn string, widthColumn string) criterionHandlerFunc {
|
||||
return func(f *filterBuilder) {
|
||||
if resolution != nil && resolution.IsValid() {
|
||||
min := resolution.GetMinResolution()
|
||||
max := resolution.GetMaxResolution()
|
||||
if resolution != nil && resolution.Value.IsValid() {
|
||||
min := resolution.Value.GetMinResolution()
|
||||
max := resolution.Value.GetMaxResolution()
|
||||
|
||||
widthHeight := fmt.Sprintf("MIN(%s, %s)", widthColumn, heightColumn)
|
||||
|
||||
if min > 0 {
|
||||
f.addWhere(widthHeight + " >= " + strconv.Itoa(min))
|
||||
}
|
||||
|
||||
if max > 0 {
|
||||
f.addWhere(widthHeight + " < " + strconv.Itoa(max))
|
||||
if resolution.Modifier == models.CriterionModifierEquals {
|
||||
f.addWhere(fmt.Sprintf("%s BETWEEN %d AND %d", widthHeight, min, max))
|
||||
} else if resolution.Modifier == models.CriterionModifierNotEquals {
|
||||
f.addWhere(fmt.Sprintf("%s NOT BETWEEN %d AND %d", widthHeight, min, max))
|
||||
} else if resolution.Modifier == models.CriterionModifierLessThan {
|
||||
f.addWhere(fmt.Sprintf("%s < %d", widthHeight, min))
|
||||
} else if resolution.Modifier == models.CriterionModifierGreaterThan {
|
||||
f.addWhere(fmt.Sprintf("%s > %d", widthHeight, max))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -648,7 +648,10 @@ func verifyScenesResolution(t *testing.T, resolution models.ResolutionEnum) {
|
||||
withTxn(func(r models.Repository) error {
|
||||
sqb := r.Scene()
|
||||
sceneFilter := models.SceneFilterType{
|
||||
Resolution: &resolution,
|
||||
Resolution: &models.ResolutionCriterionInput{
|
||||
Value: resolution,
|
||||
Modifier: models.CriterionModifierEquals,
|
||||
},
|
||||
}
|
||||
|
||||
scenes := queryScene(t, sqb, &sceneFilter, nil)
|
||||
@@ -679,6 +682,76 @@ func verifySceneResolution(t *testing.T, height sql.NullInt64, resolution models
|
||||
}
|
||||
}
|
||||
|
||||
func TestAllResolutionsHaveResolutionRange(t *testing.T) {
|
||||
for _, resolution := range models.AllResolutionEnum {
|
||||
assert.NotZero(t, resolution.GetMinResolution(), "Define resolution range for %s in extension_resolution.go", resolution)
|
||||
assert.NotZero(t, resolution.GetMaxResolution(), "Define resolution range for %s in extension_resolution.go", resolution)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSceneQueryResolutionModifiers(t *testing.T) {
|
||||
if err := withRollbackTxn(func(r models.Repository) error {
|
||||
qb := r.Scene()
|
||||
sceneNoResolution, _ := createScene(qb, 0, 0)
|
||||
firstScene540P, _ := createScene(qb, 960, 540)
|
||||
secondScene540P, _ := createScene(qb, 1280, 719)
|
||||
firstScene720P, _ := createScene(qb, 1280, 720)
|
||||
secondScene720P, _ := createScene(qb, 1280, 721)
|
||||
thirdScene720P, _ := createScene(qb, 1920, 1079)
|
||||
scene1080P, _ := createScene(qb, 1920, 1080)
|
||||
|
||||
scenesEqualTo720P := queryScenes(t, qb, models.ResolutionEnumStandardHd, models.CriterionModifierEquals)
|
||||
scenesNotEqualTo720P := queryScenes(t, qb, models.ResolutionEnumStandardHd, models.CriterionModifierNotEquals)
|
||||
scenesGreaterThan720P := queryScenes(t, qb, models.ResolutionEnumStandardHd, models.CriterionModifierGreaterThan)
|
||||
scenesLessThan720P := queryScenes(t, qb, models.ResolutionEnumStandardHd, models.CriterionModifierLessThan)
|
||||
|
||||
assert.Subset(t, scenesEqualTo720P, []*models.Scene{firstScene720P, secondScene720P, thirdScene720P})
|
||||
assert.NotSubset(t, scenesEqualTo720P, []*models.Scene{sceneNoResolution, firstScene540P, secondScene540P, scene1080P})
|
||||
|
||||
assert.Subset(t, scenesNotEqualTo720P, []*models.Scene{sceneNoResolution, firstScene540P, secondScene540P, scene1080P})
|
||||
assert.NotSubset(t, scenesNotEqualTo720P, []*models.Scene{firstScene720P, secondScene720P, thirdScene720P})
|
||||
|
||||
assert.Subset(t, scenesGreaterThan720P, []*models.Scene{scene1080P})
|
||||
assert.NotSubset(t, scenesGreaterThan720P, []*models.Scene{sceneNoResolution, firstScene540P, secondScene540P, firstScene720P, secondScene720P, thirdScene720P})
|
||||
|
||||
assert.Subset(t, scenesLessThan720P, []*models.Scene{sceneNoResolution, firstScene540P, secondScene540P})
|
||||
assert.NotSubset(t, scenesLessThan720P, []*models.Scene{scene1080P, firstScene720P, secondScene720P, thirdScene720P})
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
t.Error(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func queryScenes(t *testing.T, queryBuilder models.SceneReaderWriter, resolution models.ResolutionEnum, modifier models.CriterionModifier) []*models.Scene {
|
||||
sceneFilter := models.SceneFilterType{
|
||||
Resolution: &models.ResolutionCriterionInput{
|
||||
Value: resolution,
|
||||
Modifier: modifier,
|
||||
},
|
||||
}
|
||||
|
||||
return queryScene(t, queryBuilder, &sceneFilter, nil)
|
||||
}
|
||||
|
||||
func createScene(queryBuilder models.SceneReaderWriter, width int64, height int64) (*models.Scene, error) {
|
||||
name := fmt.Sprintf("TestSceneQueryResolutionModifiers %d %d", width, height)
|
||||
scene := models.Scene{
|
||||
Path: name,
|
||||
Width: sql.NullInt64{
|
||||
Int64: width,
|
||||
Valid: true,
|
||||
},
|
||||
Height: sql.NullInt64{
|
||||
Int64: height,
|
||||
Valid: true,
|
||||
},
|
||||
Checksum: sql.NullString{String: utils.MD5FromString(name), Valid: true},
|
||||
}
|
||||
|
||||
return queryBuilder.Create(scene)
|
||||
}
|
||||
|
||||
func TestSceneQueryHasMarkers(t *testing.T) {
|
||||
withTxn(func(r models.Repository) error {
|
||||
sqb := r.Scene()
|
||||
|
||||
@@ -605,6 +605,7 @@ func createScenes(sqb models.SceneReaderWriter, n int) error {
|
||||
OCounter: getOCounter(i),
|
||||
Duration: getSceneDuration(i),
|
||||
Height: getHeight(i),
|
||||
Width: getWidth(i),
|
||||
Date: getSceneDate(i),
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user