Add various filter criteria (#1505)

* Add various filter criteria
* Add tag name criterion
This commit is contained in:
WithoutPants
2021-06-23 09:10:20 +10:00
committed by GitHub
parent 5ecea3f69f
commit 4165e7779f
20 changed files with 189 additions and 7 deletions

View File

@@ -33,6 +33,9 @@ input PerformerFilterType {
OR: PerformerFilterType
NOT: PerformerFilterType
name: StringCriterionInput
details: StringCriterionInput
"""Filter by favorite"""
filter_favorites: Boolean
"""Filter by birth year"""
@@ -105,6 +108,15 @@ input SceneFilterType {
OR: SceneFilterType
NOT: SceneFilterType
title: StringCriterionInput
details: StringCriterionInput
"""Filter by file oshash"""
oshash: StringCriterionInput
"""Filter by file checksum"""
checksum: StringCriterionInput
"""Filter by file phash"""
phash: StringCriterionInput
"""Filter by path"""
path: StringCriterionInput
"""Filter by rating"""
@@ -144,6 +156,15 @@ input SceneFilterType {
}
input MovieFilterType {
name: StringCriterionInput
director: StringCriterionInput
synopsis: StringCriterionInput
"""Filter by duration (in seconds)"""
duration: IntCriterionInput
"""Filter by rating"""
rating: IntCriterionInput
"""Filter to only include movies with this studio"""
studios: HierarchicalMultiCriterionInput
"""Filter to only include movies missing this property"""
@@ -153,6 +174,8 @@ input MovieFilterType {
}
input StudioFilterType {
name: StringCriterionInput
details: StringCriterionInput
"""Filter to only include studios with this parent studio"""
parents: MultiCriterionInput
"""Filter by StashID"""
@@ -176,6 +199,11 @@ input GalleryFilterType {
OR: GalleryFilterType
NOT: GalleryFilterType
title: StringCriterionInput
details: StringCriterionInput
"""Filter by file checksum"""
checksum: StringCriterionInput
"""Filter by path"""
path: StringCriterionInput
"""Filter to only include galleries missing this property"""
@@ -241,6 +269,10 @@ input ImageFilterType {
OR: ImageFilterType
NOT: ImageFilterType
title: StringCriterionInput
"""Filter by file checksum"""
checksum: StringCriterionInput
"""Filter by path"""
path: StringCriterionInput
"""Filter by rating"""

View File

@@ -203,6 +203,9 @@ func (qb *galleryQueryBuilder) makeFilter(galleryFilter *models.GalleryFilterTyp
query.not(qb.makeFilter(galleryFilter.Not))
}
query.handleCriterion(stringCriterionHandler(galleryFilter.Title, "galleries.title"))
query.handleCriterion(stringCriterionHandler(galleryFilter.Details, "galleries.details"))
query.handleCriterion(stringCriterionHandler(galleryFilter.Checksum, "galleries.checksum"))
query.handleCriterion(boolCriterionHandler(galleryFilter.IsZip, "galleries.zip"))
query.handleCriterion(stringCriterionHandler(galleryFilter.Path, "galleries.path"))
query.handleCriterion(intCriterionHandler(galleryFilter.Rating, "galleries.rating"))

View File

@@ -231,6 +231,8 @@ func (qb *imageQueryBuilder) makeFilter(imageFilter *models.ImageFilterType) *fi
query.not(qb.makeFilter(imageFilter.Not))
}
query.handleCriterion(stringCriterionHandler(imageFilter.Checksum, "images.checksum"))
query.handleCriterion(stringCriterionHandler(imageFilter.Title, "images.title"))
query.handleCriterion(stringCriterionHandler(imageFilter.Path, "images.path"))
query.handleCriterion(intCriterionHandler(imageFilter.Rating, "images.rating"))
query.handleCriterion(intCriterionHandler(imageFilter.OCounter, "images.o_counter"))

View File

@@ -118,6 +118,11 @@ func (qb *movieQueryBuilder) All() ([]*models.Movie, error) {
func (qb *movieQueryBuilder) makeFilter(movieFilter *models.MovieFilterType) *filterBuilder {
query := &filterBuilder{}
query.handleCriterion(stringCriterionHandler(movieFilter.Name, "movies.name"))
query.handleCriterion(stringCriterionHandler(movieFilter.Director, "movies.director"))
query.handleCriterion(stringCriterionHandler(movieFilter.Synopsis, "movies.synopsis"))
query.handleCriterion(intCriterionHandler(movieFilter.Rating, "movies.rating"))
query.handleCriterion(durationCriterionHandler(movieFilter.Duration, "movies.duration"))
query.handleCriterion(movieIsMissingCriterionHandler(qb, movieFilter.IsMissing))
query.handleCriterion(stringCriterionHandler(movieFilter.URL, "movies.url"))
query.handleCriterion(movieStudioCriterionHandler(qb, movieFilter.Studios))

View File

@@ -239,6 +239,9 @@ func (qb *performerQueryBuilder) makeFilter(filter *models.PerformerFilterType)
}
const tableName = performerTable
query.handleCriterion(stringCriterionHandler(filter.Name, tableName+".name"))
query.handleCriterion(stringCriterionHandler(filter.Details, tableName+".details"))
query.handleCriterion(boolCriterionHandler(filter.FilterFavorites, tableName+".favorite"))
query.handleCriterion(yearFilterCriterionHandler(filter.BirthYear, tableName+".birthdate"))

View File

@@ -354,6 +354,11 @@ func (qb *sceneQueryBuilder) makeFilter(sceneFilter *models.SceneFilterType) *fi
}
query.handleCriterion(stringCriterionHandler(sceneFilter.Path, "scenes.path"))
query.handleCriterion(stringCriterionHandler(sceneFilter.Title, "scenes.title"))
query.handleCriterion(stringCriterionHandler(sceneFilter.Details, "scenes.details"))
query.handleCriterion(stringCriterionHandler(sceneFilter.Oshash, "scenes.oshash"))
query.handleCriterion(stringCriterionHandler(sceneFilter.Checksum, "scenes.checksum"))
query.handleCriterion(phashCriterionHandler(sceneFilter.Phash))
query.handleCriterion(intCriterionHandler(sceneFilter.Rating, "scenes.rating"))
query.handleCriterion(intCriterionHandler(sceneFilter.OCounter, "scenes.o_counter"))
query.handleCriterion(boolCriterionHandler(sceneFilter.Organized, "scenes.organized"))
@@ -430,6 +435,29 @@ func (qb *sceneQueryBuilder) Query(sceneFilter *models.SceneFilterType, findFilt
return scenes, countResult, nil
}
func phashCriterionHandler(phashFilter *models.StringCriterionInput) criterionHandlerFunc {
return func(f *filterBuilder) {
if phashFilter != nil {
// convert value to int from hex
// ignore errors
value, _ := utils.StringToPhash(phashFilter.Value)
if modifier := phashFilter.Modifier; phashFilter.Modifier.IsValid() {
switch modifier {
case models.CriterionModifierEquals:
f.addWhere("scenes.phash = ?", value)
case models.CriterionModifierNotEquals:
f.addWhere("scenes.phash != ?", value)
case models.CriterionModifierIsNull:
f.addWhere("scenes.phash IS NULL")
case models.CriterionModifierNotNull:
f.addWhere("scenes.phash IS NOT NULL")
}
}
}
}
}
func durationCriterionHandler(durationFilter *models.IntCriterionInput, column string) criterionHandlerFunc {
return func(f *filterBuilder) {
if durationFilter != nil {

View File

@@ -184,6 +184,8 @@ func (qb *studioQueryBuilder) Query(studioFilter *models.StudioFilterType, findF
query.handleCountCriterion(studioFilter.SceneCount, studioTable, sceneTable, studioIDColumn)
query.handleCountCriterion(studioFilter.ImageCount, studioTable, imageTable, studioIDColumn)
query.handleCountCriterion(studioFilter.GalleryCount, studioTable, galleryTable, studioIDColumn)
query.handleStringCriterionInput(studioFilter.Name, "studios.name")
query.handleStringCriterionInput(studioFilter.Details, "studios.details")
query.handleStringCriterionInput(studioFilter.URL, "studios.url")
query.handleStringCriterionInput(studioFilter.StashID, "studio_stash_ids.stash_id")

View File

@@ -55,3 +55,12 @@ func findNeighbors(bucket int, neighbors []int, hashes []*Phash, scenes *[]int)
func PhashToString(phash int64) string {
return strconv.FormatUint(uint64(phash), 16)
}
func StringToPhash(s string) (int64, error) {
ret, err := strconv.ParseUint(s, 16, 64)
if err != nil {
return 0, err
}
return int64(ret), nil
}

View File

@@ -1,5 +1,6 @@
### ✨ New Features
* Add button to open scene in external player on handheld devices. ([#679](https://github.com/stashapp/stash/pull/679))
* Added filter criteria for name, details and hash related fields. ([#1505](https://github.com/stashapp/stash/pull/1505))
* Added button to open scene in external player on handheld devices. ([#679](https://github.com/stashapp/stash/pull/679))
* Added support for saved and default filters. ([#1474](https://github.com/stashapp/stash/pull/1474))
* Added merge tags functionality. ([#1481](https://github.com/stashapp/stash/pull/1481))
* Added support for triggering plugin tasks during operations. ([#1452](https://github.com/stashapp/stash/pull/1452))

View File

@@ -184,8 +184,16 @@ export class StringCriterionOption extends CriterionOption {
}
}
export function createStringCriterionOption(value: CriterionType) {
return new StringCriterionOption(value, value, value);
export function createStringCriterionOption(
value: CriterionType,
messageID?: string,
parameterName?: string
) {
return new StringCriterionOption(
messageID ?? value,
value,
parameterName ?? messageID ?? value
);
}
export class StringCriterion extends Criterion<string> {
@@ -236,6 +244,18 @@ export class MandatoryStringCriterionOption extends CriterionOption {
}
}
export function createMandatoryStringCriterionOption(
value: CriterionType,
messageID?: string,
parameterName?: string
) {
return new MandatoryStringCriterionOption(
messageID ?? value,
value,
parameterName ?? messageID ?? value
);
}
export class BooleanCriterionOption extends CriterionOption {
constructor(messageID: string, value: CriterionType, parameterName?: string) {
super({

View File

@@ -38,12 +38,16 @@ import { GalleriesCriterion } from "./galleries";
import { CriterionType } from "../types";
import { InteractiveCriterion } from "./interactive";
import { RatingCriterionOption } from "./rating";
import { PhashCriterionOption } from "./phash";
export function makeCriteria(type: CriterionType = "none") {
switch (type) {
case "none":
return new NoneCriterion();
case "name":
case "path":
case "checksum":
case "oshash":
return new StringCriterion(
new MandatoryStringCriterionOption(type, type)
);
@@ -111,6 +115,13 @@ export function makeCriteria(type: CriterionType = "none") {
);
case "gender":
return new GenderCriterion();
case "sceneChecksum":
case "galleryChecksum":
return new StringCriterion(
new StringCriterionOption("checksum", type, "checksum")
);
case "phash":
return new StringCriterion(PhashCriterionOption);
case "ethnicity":
case "country":
case "hair_color":
@@ -124,6 +135,10 @@ export function makeCriteria(type: CriterionType = "none") {
case "aliases":
case "url":
case "stash_id":
case "details":
case "title":
case "director":
case "synopsis":
return new StringCriterion(new StringCriterionOption(type, type));
case "interactive":
return new InteractiveCriterion();

View File

@@ -0,0 +1,15 @@
import { CriterionModifier } from "src/core/generated-graphql";
import { CriterionOption } from "./criterion";
export const PhashCriterionOption = new CriterionOption({
messageID: "media_info.phash",
type: "phash",
parameterName: "phash",
inputType: "text",
modifierOptions: [
CriterionModifier.Equals,
CriterionModifier.NotEquals,
CriterionModifier.IsNull,
CriterionModifier.NotNull,
],
});

View File

@@ -38,7 +38,14 @@ const displayModeOptions = [
];
const criterionOptions = [
createStringCriterionOption("title"),
createStringCriterionOption("details"),
createStringCriterionOption("path"),
createStringCriterionOption(
"galleryChecksum",
"media_info.checksum",
"checksum"
),
RatingCriterionOption,
OrganizedCriterionOption,
AverageResolutionCriterionOption,

View File

@@ -1,5 +1,6 @@
import {
createMandatoryNumberCriterionOption,
createMandatoryStringCriterionOption,
createStringCriterionOption,
} from "./criteria/criterion";
import { ImageIsMissingCriterionOption } from "./criteria/is-missing";
@@ -31,7 +32,9 @@ const sortByOptions = [
const displayModeOptions = [DisplayMode.Grid, DisplayMode.Wall];
const criterionOptions = [
createStringCriterionOption("path"),
createStringCriterionOption("title"),
createMandatoryStringCriterionOption("checksum", "media_info.checksum"),
createMandatoryStringCriterionOption("path"),
RatingCriterionOption,
OrganizedCriterionOption,
createMandatoryNumberCriterionOption("o_counter"),

View File

@@ -1,5 +1,9 @@
import { createStringCriterionOption } from "./criteria/criterion";
import {
createMandatoryNumberCriterionOption,
createStringCriterionOption,
} from "./criteria/criterion";
import { MovieIsMissingCriterionOption } from "./criteria/is-missing";
import { RatingCriterionOption } from "./criteria/rating";
import { StudiosCriterionOption } from "./criteria/studios";
import { ListFilterOptions } from "./filter-options";
import { DisplayMode } from "./types";
@@ -19,6 +23,11 @@ const criterionOptions = [
StudiosCriterionOption,
MovieIsMissingCriterionOption,
createStringCriterionOption("url"),
createStringCriterionOption("name"),
createStringCriterionOption("director"),
createStringCriterionOption("synopsis"),
createMandatoryNumberCriterionOption("duration"),
RatingCriterionOption,
];
export const MovieListFilterOptions = new ListFilterOptions(

View File

@@ -43,6 +43,8 @@ const numberCriteria: CriterionType[] = [
];
const stringCriteria: CriterionType[] = [
"name",
"details",
"ethnicity",
"country",
"hair_color",

View File

@@ -1,5 +1,6 @@
import {
createMandatoryNumberCriterionOption,
createMandatoryStringCriterionOption,
createStringCriterionOption,
} from "./criteria/criterion";
import { HasMarkersCriterionOption } from "./criteria/has-markers";
@@ -17,6 +18,7 @@ import {
} from "./criteria/tags";
import { ListFilterOptions } from "./filter-options";
import { DisplayMode } from "./types";
import { PhashCriterionOption } from "./criteria/phash";
const defaultSortBy = "date";
const sortByOptions = [
@@ -46,7 +48,16 @@ const displayModeOptions = [
];
const criterionOptions = [
createStringCriterionOption("path"),
createStringCriterionOption("title"),
createMandatoryStringCriterionOption("path"),
createStringCriterionOption("details"),
createMandatoryStringCriterionOption("oshash", "media_info.hash"),
createStringCriterionOption(
"sceneChecksum",
"media_info.checksum",
"checksum"
),
PhashCriterionOption,
RatingCriterionOption,
OrganizedCriterionOption,
createMandatoryNumberCriterionOption("o_counter"),

View File

@@ -1,5 +1,6 @@
import {
createMandatoryNumberCriterionOption,
createMandatoryStringCriterionOption,
createStringCriterionOption,
} from "./criteria/criterion";
import { StudioIsMissingCriterionOption } from "./criteria/is-missing";
@@ -28,6 +29,8 @@ const sortByOptions = ["name", "random", "rating"]
const displayModeOptions = [DisplayMode.Grid];
const criterionOptions = [
createMandatoryStringCriterionOption("name"),
createStringCriterionOption("details"),
ParentStudiosCriterionOption,
StudioIsMissingCriterionOption,
RatingCriterionOption,

View File

@@ -1,5 +1,6 @@
import {
createMandatoryNumberCriterionOption,
createMandatoryStringCriterionOption,
createStringCriterionOption,
} from "./criteria/criterion";
import { TagIsMissingCriterionOption } from "./criteria/is-missing";
@@ -36,6 +37,7 @@ const sortByOptions = [
const displayModeOptions = [DisplayMode.Grid, DisplayMode.List];
const criterionOptions = [
createMandatoryStringCriterionOption("name"),
TagIsMissingCriterionOption,
createStringCriterionOption("aliases"),
createMandatoryNumberCriterionOption("scene_count"),

View File

@@ -92,4 +92,14 @@ export type CriterionType =
| "death_year"
| "url"
| "stash_id"
| "interactive";
| "interactive"
| "name"
| "details"
| "title"
| "oshash"
| "checksum"
| "sceneChecksum"
| "galleryChecksum"
| "phash"
| "director"
| "synopsis";