mirror of
https://github.com/stashapp/stash.git
synced 2025-12-17 12:24:38 +03:00
Support filtering by StashID endpoint (#3005)
* Add endpoint to stash_id filter in UI Co-authored-by: WithoutPants <53250216+WithoutPants@users.noreply.github.com>
This commit is contained in:
@@ -39,6 +39,14 @@ input PHashDuplicationCriterionInput {
|
||||
distance: Int
|
||||
}
|
||||
|
||||
input StashIDCriterionInput {
|
||||
"""If present, this value is treated as a predicate.
|
||||
That is, it will filter based on stash_ids with the matching endpoint"""
|
||||
endpoint: String
|
||||
stash_id: String
|
||||
modifier: CriterionModifier!
|
||||
}
|
||||
|
||||
input PerformerFilterType {
|
||||
AND: PerformerFilterType
|
||||
OR: PerformerFilterType
|
||||
@@ -90,7 +98,9 @@ input PerformerFilterType {
|
||||
"""Filter by gallery count"""
|
||||
gallery_count: IntCriterionInput
|
||||
"""Filter by StashID"""
|
||||
stash_id: StringCriterionInput
|
||||
stash_id: StringCriterionInput @deprecated(reason: "Use stash_id_endpoint instead")
|
||||
"""Filter by StashID"""
|
||||
stash_id_endpoint: StashIDCriterionInput
|
||||
"""Filter by rating"""
|
||||
rating: IntCriterionInput @deprecated(reason: "Use 1-100 range with rating100")
|
||||
# rating expressed as 1-100
|
||||
@@ -196,7 +206,9 @@ input SceneFilterType {
|
||||
"""Filter by performer count"""
|
||||
performer_count: IntCriterionInput
|
||||
"""Filter by StashID"""
|
||||
stash_id: StringCriterionInput
|
||||
stash_id: StringCriterionInput @deprecated(reason: "Use stash_id_endpoint instead")
|
||||
"""Filter by StashID"""
|
||||
stash_id_endpoint: StashIDCriterionInput
|
||||
"""Filter by url"""
|
||||
url: StringCriterionInput
|
||||
"""Filter by interactive"""
|
||||
@@ -251,7 +263,9 @@ input StudioFilterType {
|
||||
"""Filter to only include studios with this parent studio"""
|
||||
parents: MultiCriterionInput
|
||||
"""Filter by StashID"""
|
||||
stash_id: StringCriterionInput
|
||||
stash_id: StringCriterionInput @deprecated(reason: "Use stash_id_endpoint instead")
|
||||
"""Filter by StashID"""
|
||||
stash_id_endpoint: StashIDCriterionInput
|
||||
"""Filter to only include studios missing this property"""
|
||||
is_missing: String
|
||||
"""Filter by rating"""
|
||||
|
||||
@@ -111,6 +111,8 @@ type PerformerFilterType struct {
|
||||
GalleryCount *IntCriterionInput `json:"gallery_count"`
|
||||
// Filter by StashID
|
||||
StashID *StringCriterionInput `json:"stash_id"`
|
||||
// Filter by StashID Endpoint
|
||||
StashIDEndpoint *StashIDCriterionInput `json:"stash_id_endpoint"`
|
||||
// Filter by rating expressed as 1-5
|
||||
Rating *IntCriterionInput `json:"rating"`
|
||||
// Filter by rating expressed as 1-100
|
||||
|
||||
@@ -69,6 +69,8 @@ type SceneFilterType struct {
|
||||
PerformerCount *IntCriterionInput `json:"performer_count"`
|
||||
// Filter by StashID
|
||||
StashID *StringCriterionInput `json:"stash_id"`
|
||||
// Filter by StashID Endpoint
|
||||
StashIDEndpoint *StashIDCriterionInput `json:"stash_id_endpoint"`
|
||||
// Filter by url
|
||||
URL *StringCriterionInput `json:"url"`
|
||||
// Filter by interactive
|
||||
|
||||
@@ -20,3 +20,11 @@ func (u *UpdateStashIDs) AddUnique(v StashID) {
|
||||
|
||||
u.StashIDs = append(u.StashIDs, v)
|
||||
}
|
||||
|
||||
type StashIDCriterionInput struct {
|
||||
// If present, this value is treated as a predicate.
|
||||
// That is, it will filter based on stash_ids with the matching endpoint
|
||||
Endpoint *string `json:"endpoint"`
|
||||
StashID *string `json:"stash_id"`
|
||||
Modifier CriterionModifier `json:"modifier"`
|
||||
}
|
||||
|
||||
@@ -12,6 +12,8 @@ type StudioFilterType struct {
|
||||
Parents *MultiCriterionInput `json:"parents"`
|
||||
// Filter by StashID
|
||||
StashID *StringCriterionInput `json:"stash_id"`
|
||||
// Filter by StashID Endpoint
|
||||
StashIDEndpoint *StashIDCriterionInput `json:"stash_id_endpoint"`
|
||||
// Filter to only include studios missing this property
|
||||
IsMissing *string `json:"is_missing"`
|
||||
// Filter by rating expressed as 1-5
|
||||
|
||||
@@ -942,3 +942,39 @@ func (m *joinedHierarchicalMultiCriterionHandlerBuilder) handler(criterion *mode
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type stashIDCriterionHandler struct {
|
||||
c *models.StashIDCriterionInput
|
||||
stashIDRepository *stashIDRepository
|
||||
stashIDTableAs string
|
||||
parentIDCol string
|
||||
}
|
||||
|
||||
func (h *stashIDCriterionHandler) handle(ctx context.Context, f *filterBuilder) {
|
||||
if h.c == nil {
|
||||
return
|
||||
}
|
||||
|
||||
stashIDRepo := h.stashIDRepository
|
||||
t := stashIDRepo.tableName
|
||||
if h.stashIDTableAs != "" {
|
||||
t = h.stashIDTableAs
|
||||
}
|
||||
|
||||
joinClause := fmt.Sprintf("%s.%s = %s", t, stashIDRepo.idColumn, h.parentIDCol)
|
||||
if h.c.Endpoint != nil && *h.c.Endpoint != "" {
|
||||
joinClause += fmt.Sprintf(" AND %s.endpoint = '%s'", t, *h.c.Endpoint)
|
||||
}
|
||||
|
||||
f.addLeftJoin(stashIDRepo.tableName, h.stashIDTableAs, joinClause)
|
||||
|
||||
v := ""
|
||||
if h.c.StashID != nil {
|
||||
v = *h.c.StashID
|
||||
}
|
||||
|
||||
stringCriterionHandler(&models.StringCriterionInput{
|
||||
Value: v,
|
||||
Modifier: h.c.Modifier,
|
||||
}, t+".stash_id")(ctx, f)
|
||||
}
|
||||
|
||||
@@ -533,6 +533,12 @@ func (qb *PerformerStore) makeFilter(ctx context.Context, filter *models.Perform
|
||||
stringCriterionHandler(filter.StashID, "performer_stash_ids.stash_id")(ctx, f)
|
||||
}
|
||||
}))
|
||||
query.handleCriterion(ctx, &stashIDCriterionHandler{
|
||||
c: filter.StashIDEndpoint,
|
||||
stashIDRepository: qb.stashIDRepository(),
|
||||
stashIDTableAs: "performer_stash_ids",
|
||||
parentIDCol: "performers.id",
|
||||
})
|
||||
|
||||
// TODO - need better handling of aliases
|
||||
query.handleCriterion(ctx, stringCriterionHandler(filter.Aliases, tableName+".aliases"))
|
||||
|
||||
@@ -585,6 +585,100 @@ func TestPerformerQueryIgnoreAutoTag(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestPerformerQuery(t *testing.T) {
|
||||
var (
|
||||
endpoint = performerStashID(performerIdxWithGallery).Endpoint
|
||||
stashID = performerStashID(performerIdxWithGallery).StashID
|
||||
)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
findFilter *models.FindFilterType
|
||||
filter *models.PerformerFilterType
|
||||
includeIdxs []int
|
||||
excludeIdxs []int
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
"stash id with endpoint",
|
||||
nil,
|
||||
&models.PerformerFilterType{
|
||||
StashIDEndpoint: &models.StashIDCriterionInput{
|
||||
Endpoint: &endpoint,
|
||||
StashID: &stashID,
|
||||
Modifier: models.CriterionModifierEquals,
|
||||
},
|
||||
},
|
||||
[]int{performerIdxWithGallery},
|
||||
nil,
|
||||
false,
|
||||
},
|
||||
{
|
||||
"exclude stash id with endpoint",
|
||||
nil,
|
||||
&models.PerformerFilterType{
|
||||
StashIDEndpoint: &models.StashIDCriterionInput{
|
||||
Endpoint: &endpoint,
|
||||
StashID: &stashID,
|
||||
Modifier: models.CriterionModifierNotEquals,
|
||||
},
|
||||
},
|
||||
nil,
|
||||
[]int{performerIdxWithGallery},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"null stash id with endpoint",
|
||||
nil,
|
||||
&models.PerformerFilterType{
|
||||
StashIDEndpoint: &models.StashIDCriterionInput{
|
||||
Endpoint: &endpoint,
|
||||
Modifier: models.CriterionModifierIsNull,
|
||||
},
|
||||
},
|
||||
nil,
|
||||
[]int{performerIdxWithGallery},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"not null stash id with endpoint",
|
||||
nil,
|
||||
&models.PerformerFilterType{
|
||||
StashIDEndpoint: &models.StashIDCriterionInput{
|
||||
Endpoint: &endpoint,
|
||||
Modifier: models.CriterionModifierNotNull,
|
||||
},
|
||||
},
|
||||
[]int{performerIdxWithGallery},
|
||||
nil,
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
runWithRollbackTxn(t, tt.name, func(t *testing.T, ctx context.Context) {
|
||||
assert := assert.New(t)
|
||||
|
||||
performers, _, err := db.Performer.Query(ctx, tt.filter, tt.findFilter)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("PerformerStore.Query() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
|
||||
ids := performersToIDs(performers)
|
||||
include := indexesToIDs(performerIDs, tt.includeIdxs)
|
||||
exclude := indexesToIDs(performerIDs, tt.excludeIdxs)
|
||||
|
||||
for _, i := range include {
|
||||
assert.Contains(ids, i)
|
||||
}
|
||||
for _, e := range exclude {
|
||||
assert.NotContains(ids, e)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestPerformerQueryForAutoTag(t *testing.T) {
|
||||
withTxn(func(ctx context.Context) error {
|
||||
tqb := db.Performer
|
||||
|
||||
@@ -864,6 +864,12 @@ func (qb *SceneStore) makeFilter(ctx context.Context, sceneFilter *models.SceneF
|
||||
stringCriterionHandler(sceneFilter.StashID, "scene_stash_ids.stash_id")(ctx, f)
|
||||
}
|
||||
}))
|
||||
query.handleCriterion(ctx, &stashIDCriterionHandler{
|
||||
c: sceneFilter.StashIDEndpoint,
|
||||
stashIDRepository: qb.stashIDRepository(),
|
||||
stashIDTableAs: "scene_stash_ids",
|
||||
parentIDCol: "scenes.id",
|
||||
})
|
||||
|
||||
query.handleCriterion(ctx, boolCriterionHandler(sceneFilter.Interactive, "video_files.interactive", qb.addVideoFilesTable))
|
||||
query.handleCriterion(ctx, intCriterionHandler(sceneFilter.InteractiveSpeed, "video_files.interactive_speed", qb.addVideoFilesTable))
|
||||
|
||||
@@ -2091,6 +2091,104 @@ func sceneQueryQ(ctx context.Context, t *testing.T, sqb models.SceneReader, q st
|
||||
assert.Len(t, scenes, totalScenes)
|
||||
}
|
||||
|
||||
func TestSceneQuery(t *testing.T) {
|
||||
var (
|
||||
endpoint = sceneStashID(sceneIdxWithGallery).Endpoint
|
||||
stashID = sceneStashID(sceneIdxWithGallery).StashID
|
||||
)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
findFilter *models.FindFilterType
|
||||
filter *models.SceneFilterType
|
||||
includeIdxs []int
|
||||
excludeIdxs []int
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
"stash id with endpoint",
|
||||
nil,
|
||||
&models.SceneFilterType{
|
||||
StashIDEndpoint: &models.StashIDCriterionInput{
|
||||
Endpoint: &endpoint,
|
||||
StashID: &stashID,
|
||||
Modifier: models.CriterionModifierEquals,
|
||||
},
|
||||
},
|
||||
[]int{sceneIdxWithGallery},
|
||||
nil,
|
||||
false,
|
||||
},
|
||||
{
|
||||
"exclude stash id with endpoint",
|
||||
nil,
|
||||
&models.SceneFilterType{
|
||||
StashIDEndpoint: &models.StashIDCriterionInput{
|
||||
Endpoint: &endpoint,
|
||||
StashID: &stashID,
|
||||
Modifier: models.CriterionModifierNotEquals,
|
||||
},
|
||||
},
|
||||
nil,
|
||||
[]int{sceneIdxWithGallery},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"null stash id with endpoint",
|
||||
nil,
|
||||
&models.SceneFilterType{
|
||||
StashIDEndpoint: &models.StashIDCriterionInput{
|
||||
Endpoint: &endpoint,
|
||||
Modifier: models.CriterionModifierIsNull,
|
||||
},
|
||||
},
|
||||
nil,
|
||||
[]int{sceneIdxWithGallery},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"not null stash id with endpoint",
|
||||
nil,
|
||||
&models.SceneFilterType{
|
||||
StashIDEndpoint: &models.StashIDCriterionInput{
|
||||
Endpoint: &endpoint,
|
||||
Modifier: models.CriterionModifierNotNull,
|
||||
},
|
||||
},
|
||||
[]int{sceneIdxWithGallery},
|
||||
nil,
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
runWithRollbackTxn(t, tt.name, func(t *testing.T, ctx context.Context) {
|
||||
assert := assert.New(t)
|
||||
|
||||
results, err := db.Scene.Query(ctx, models.SceneQueryOptions{
|
||||
SceneFilter: tt.filter,
|
||||
QueryOptions: models.QueryOptions{
|
||||
FindFilter: tt.findFilter,
|
||||
},
|
||||
})
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("PerformerStore.Query() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
|
||||
include := indexesToIDs(performerIDs, tt.includeIdxs)
|
||||
exclude := indexesToIDs(performerIDs, tt.excludeIdxs)
|
||||
|
||||
for _, i := range include {
|
||||
assert.Contains(results.IDs, i)
|
||||
}
|
||||
for _, e := range exclude {
|
||||
assert.NotContains(results.IDs, e)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSceneQueryPath(t *testing.T) {
|
||||
const (
|
||||
sceneIdx = 1
|
||||
|
||||
@@ -245,6 +245,12 @@ func (qb *studioQueryBuilder) makeFilter(ctx context.Context, studioFilter *mode
|
||||
stringCriterionHandler(studioFilter.StashID, "studio_stash_ids.stash_id")(ctx, f)
|
||||
}
|
||||
}))
|
||||
query.handleCriterion(ctx, &stashIDCriterionHandler{
|
||||
c: studioFilter.StashIDEndpoint,
|
||||
stashIDRepository: qb.stashIDRepository(),
|
||||
stashIDTableAs: "studio_stash_ids",
|
||||
parentIDCol: "studios.id",
|
||||
})
|
||||
|
||||
query.handleCriterion(ctx, studioIsMissingCriterionHandler(qb, studioFilter.IsMissing))
|
||||
query.handleCriterion(ctx, studioSceneCountCriterionHandler(qb, studioFilter.SceneCount))
|
||||
|
||||
@@ -22,6 +22,7 @@ import { FormattedMessage, useIntl } from "react-intl";
|
||||
import {
|
||||
criterionIsHierarchicalLabelValue,
|
||||
criterionIsNumberValue,
|
||||
criterionIsStashIDValue,
|
||||
criterionIsDateValue,
|
||||
criterionIsTimestampValue,
|
||||
CriterionType,
|
||||
@@ -36,6 +37,8 @@ import { DateFilter } from "./Filters/DateFilter";
|
||||
import { TimestampFilter } from "./Filters/TimestampFilter";
|
||||
import { CountryCriterion } from "src/models/list-filter/criteria/country";
|
||||
import { CountrySelect } from "../Shared";
|
||||
import { StashIDCriterion } from "src/models/list-filter/criteria/stash-ids";
|
||||
import { StashIDFilter } from "./Filters/StashIDFilter";
|
||||
import { ConfigurationContext } from "src/hooks/Config";
|
||||
import { RatingCriterion } from "../../models/list-filter/criteria/rating";
|
||||
import { RatingFilter } from "./Filters/RatingFilter";
|
||||
@@ -134,6 +137,16 @@ export const AddFilterDialog: React.FC<IAddFilterProps> = ({
|
||||
}
|
||||
|
||||
function renderSelect() {
|
||||
// always show stashID filter
|
||||
if (criterion instanceof StashIDCriterion) {
|
||||
return (
|
||||
<StashIDFilter
|
||||
criterion={criterion}
|
||||
onValueChanged={onValueChanged}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
// Hide the value select if the modifier is "IsNull" or "NotNull"
|
||||
if (
|
||||
criterion.modifier === CriterionModifier.IsNull ||
|
||||
@@ -162,6 +175,7 @@ export const AddFilterDialog: React.FC<IAddFilterProps> = ({
|
||||
options &&
|
||||
!criterionIsHierarchicalLabelValue(criterion.value) &&
|
||||
!criterionIsNumberValue(criterion.value) &&
|
||||
!criterionIsStashIDValue(criterion.value) &&
|
||||
!criterionIsDateValue(criterion.value) &&
|
||||
!criterionIsTimestampValue(criterion.value) &&
|
||||
!Array.isArray(criterion.value)
|
||||
|
||||
56
ui/v2.5/src/components/List/Filters/StashIDFilter.tsx
Normal file
56
ui/v2.5/src/components/List/Filters/StashIDFilter.tsx
Normal file
@@ -0,0 +1,56 @@
|
||||
import React from "react";
|
||||
import { Form } from "react-bootstrap";
|
||||
import { useIntl } from "react-intl";
|
||||
import { IStashIDValue } from "../../../models/list-filter/types";
|
||||
import { Criterion } from "../../../models/list-filter/criteria/criterion";
|
||||
import { CriterionModifier } from "src/core/generated-graphql";
|
||||
|
||||
interface IStashIDFilterProps {
|
||||
criterion: Criterion<IStashIDValue>;
|
||||
onValueChanged: (value: IStashIDValue) => void;
|
||||
}
|
||||
|
||||
export const StashIDFilter: React.FC<IStashIDFilterProps> = ({
|
||||
criterion,
|
||||
onValueChanged,
|
||||
}) => {
|
||||
const intl = useIntl();
|
||||
|
||||
function onEndpointChanged(event: React.ChangeEvent<HTMLInputElement>) {
|
||||
onValueChanged({
|
||||
endpoint: event.target.value,
|
||||
stashID: criterion.value.stashID,
|
||||
});
|
||||
}
|
||||
|
||||
function onStashIDChanged(event: React.ChangeEvent<HTMLInputElement>) {
|
||||
onValueChanged({
|
||||
stashID: event.target.value,
|
||||
endpoint: criterion.value.endpoint,
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Form.Group>
|
||||
<Form.Control
|
||||
className="btn-secondary"
|
||||
onBlur={onEndpointChanged}
|
||||
defaultValue={criterion.value ? criterion.value.endpoint : ""}
|
||||
placeholder={intl.formatMessage({ id: "stash_id_endpoint" })}
|
||||
/>
|
||||
</Form.Group>
|
||||
{criterion.modifier !== CriterionModifier.IsNull &&
|
||||
criterion.modifier !== CriterionModifier.NotNull && (
|
||||
<Form.Group>
|
||||
<Form.Control
|
||||
className="btn-secondary"
|
||||
onBlur={onStashIDChanged}
|
||||
defaultValue={criterion.value ? criterion.value.stashID : ""}
|
||||
placeholder={intl.formatMessage({ id: "stash_id" })}
|
||||
/>
|
||||
</Form.Group>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,4 +1,5 @@
|
||||
### ✨ New Features
|
||||
* Added support for filtering stash ids by endpoint. ([#3005](https://github.com/stashapp/stash/pull/3005))
|
||||
* Added custom javascript option. ([#3132](https://github.com/stashapp/stash/pull/3132))
|
||||
* Added ability to select rating system in the Interface settings, allowing 5 stars with full-, half- or quarter-stars, or numeric score out of 10 with one decimal point. ([#2830](https://github.com/stashapp/stash/pull/2830))
|
||||
* Added filter criteria for Birthdate, Death Date, Date, Created At and Updated At fields. ([#2834](https://github.com/stashapp/stash/pull/2834))
|
||||
|
||||
@@ -1060,6 +1060,7 @@
|
||||
"submit_update": "Already exists in {endpoint_name}"
|
||||
},
|
||||
"statistics": "Statistics",
|
||||
"stash_id_endpoint": "Stash ID Endpoint",
|
||||
"stats": {
|
||||
"image_size": "Images size",
|
||||
"scenes_duration": "Scenes duration",
|
||||
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
ILabeledValue,
|
||||
INumberValue,
|
||||
IOptionType,
|
||||
IStashIDValue,
|
||||
IDateValue,
|
||||
ITimestampValue,
|
||||
} from "../types";
|
||||
@@ -29,6 +30,7 @@ export type CriterionValue =
|
||||
| ILabeledId[]
|
||||
| IHierarchicalLabelValue
|
||||
| INumberValue
|
||||
| IStashIDValue
|
||||
| IDateValue
|
||||
| ITimestampValue;
|
||||
|
||||
|
||||
@@ -50,6 +50,7 @@ import { DuplicatedCriterion, PhashCriterionOption } from "./phash";
|
||||
import { CaptionCriterion } from "./captions";
|
||||
import { RatingCriterion } from "./rating";
|
||||
import { CountryCriterion } from "./country";
|
||||
import { StashIDCriterion } from "./stash-ids";
|
||||
import * as GQL from "src/core/generated-graphql";
|
||||
import { IUIConfig } from "src/core/config";
|
||||
import { defaultRatingSystemOptions } from "src/utils/rating";
|
||||
@@ -169,6 +170,10 @@ export function makeCriteria(
|
||||
return new NumberCriterion(
|
||||
new NumberCriterionOption("height", "height_cm", type)
|
||||
);
|
||||
// stash_id is deprecated
|
||||
case "stash_id":
|
||||
case "stash_id_endpoint":
|
||||
return new StashIDCriterion();
|
||||
case "ethnicity":
|
||||
case "hair_color":
|
||||
case "eye_color":
|
||||
@@ -179,7 +184,6 @@ export function makeCriteria(
|
||||
case "piercings":
|
||||
case "aliases":
|
||||
case "url":
|
||||
case "stash_id":
|
||||
case "details":
|
||||
case "title":
|
||||
case "director":
|
||||
|
||||
106
ui/v2.5/src/models/list-filter/criteria/stash-ids.ts
Normal file
106
ui/v2.5/src/models/list-filter/criteria/stash-ids.ts
Normal file
@@ -0,0 +1,106 @@
|
||||
/* eslint @typescript-eslint/no-unused-vars: ["error", { "argsIgnorePattern": "^_" }] */
|
||||
import { IntlShape } from "react-intl";
|
||||
import {
|
||||
CriterionModifier,
|
||||
StashIdCriterionInput,
|
||||
} from "src/core/generated-graphql";
|
||||
import { IStashIDValue } from "../types";
|
||||
import { Criterion, CriterionOption } from "./criterion";
|
||||
|
||||
export const StashIDCriterionOption = new CriterionOption({
|
||||
messageID: "stash_id",
|
||||
type: "stash_id_endpoint",
|
||||
parameterName: "stash_id_endpoint",
|
||||
modifierOptions: [
|
||||
CriterionModifier.Equals,
|
||||
CriterionModifier.NotEquals,
|
||||
CriterionModifier.IsNull,
|
||||
CriterionModifier.NotNull,
|
||||
],
|
||||
});
|
||||
|
||||
export class StashIDCriterion extends Criterion<IStashIDValue> {
|
||||
constructor() {
|
||||
super(StashIDCriterionOption, {
|
||||
endpoint: "",
|
||||
stashID: "",
|
||||
});
|
||||
}
|
||||
|
||||
public get value(): IStashIDValue {
|
||||
return this._value;
|
||||
}
|
||||
|
||||
public set value(newValue: string | IStashIDValue) {
|
||||
// backwards compatibility - if this.value is a string, use that as stash_id
|
||||
if (typeof newValue !== "object") {
|
||||
this._value = {
|
||||
endpoint: "",
|
||||
stashID: newValue,
|
||||
};
|
||||
} else {
|
||||
this._value = newValue;
|
||||
}
|
||||
}
|
||||
|
||||
protected toCriterionInput(): StashIdCriterionInput {
|
||||
return {
|
||||
endpoint: this.value.endpoint,
|
||||
stash_id: this.value.stashID,
|
||||
modifier: this.modifier,
|
||||
};
|
||||
}
|
||||
|
||||
public getLabel(intl: IntlShape): string {
|
||||
const modifierString = Criterion.getModifierLabel(intl, this.modifier);
|
||||
let valueString = "";
|
||||
|
||||
if (
|
||||
this.modifier !== CriterionModifier.IsNull &&
|
||||
this.modifier !== CriterionModifier.NotNull
|
||||
) {
|
||||
valueString = this.getLabelValue(intl);
|
||||
} else if (this.value.endpoint) {
|
||||
valueString = "(" + this.value.endpoint + ")";
|
||||
}
|
||||
|
||||
return intl.formatMessage(
|
||||
{ id: "criterion_modifier.format_string" },
|
||||
{
|
||||
criterion: intl.formatMessage({ id: this.criterionOption.messageID }),
|
||||
modifierString,
|
||||
valueString,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public getLabelValue(_intl: IntlShape) {
|
||||
let ret = this.value.stashID;
|
||||
if (this.value.endpoint) {
|
||||
ret += " (" + this.value.endpoint + ")";
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public toJSON() {
|
||||
let encodedCriterion;
|
||||
if (
|
||||
(this.modifier === CriterionModifier.IsNull ||
|
||||
this.modifier === CriterionModifier.NotNull) &&
|
||||
!this.value.endpoint
|
||||
) {
|
||||
encodedCriterion = {
|
||||
type: this.criterionOption.type,
|
||||
modifier: this.modifier,
|
||||
};
|
||||
} else {
|
||||
encodedCriterion = {
|
||||
type: this.criterionOption.type,
|
||||
value: this.value,
|
||||
modifier: this.modifier,
|
||||
};
|
||||
}
|
||||
return JSON.stringify(encodedCriterion);
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
import { FavoriteCriterionOption } from "./criteria/favorite";
|
||||
import { GenderCriterionOption } from "./criteria/gender";
|
||||
import { PerformerIsMissingCriterionOption } from "./criteria/is-missing";
|
||||
import { StashIDCriterionOption } from "./criteria/stash-ids";
|
||||
import { StudiosCriterionOption } from "./criteria/studios";
|
||||
import { TagsCriterionOption } from "./criteria/tags";
|
||||
import { ListFilterOptions } from "./filter-options";
|
||||
@@ -67,7 +68,6 @@ const stringCriteria: CriterionType[] = [
|
||||
"tattoos",
|
||||
"piercings",
|
||||
"aliases",
|
||||
"stash_id",
|
||||
];
|
||||
|
||||
const criterionOptions = [
|
||||
@@ -76,6 +76,7 @@ const criterionOptions = [
|
||||
PerformerIsMissingCriterionOption,
|
||||
TagsCriterionOption,
|
||||
StudiosCriterionOption,
|
||||
StashIDCriterionOption,
|
||||
createStringCriterionOption("url"),
|
||||
new NullNumberCriterionOption("rating", "rating100"),
|
||||
createMandatoryNumberCriterionOption("tag_count"),
|
||||
|
||||
@@ -26,6 +26,7 @@ import {
|
||||
} from "./criteria/phash";
|
||||
import { PerformerFavoriteCriterionOption } from "./criteria/favorite";
|
||||
import { CaptionsCriterionOption } from "./criteria/captions";
|
||||
import { StashIDCriterionOption } from "./criteria/stash-ids";
|
||||
|
||||
const defaultSortBy = "date";
|
||||
const sortByOptions = [
|
||||
@@ -82,7 +83,7 @@ const criterionOptions = [
|
||||
StudiosCriterionOption,
|
||||
MoviesCriterionOption,
|
||||
createStringCriterionOption("url"),
|
||||
createStringCriterionOption("stash_id"),
|
||||
StashIDCriterionOption,
|
||||
InteractiveCriterionOption,
|
||||
CaptionsCriterionOption,
|
||||
createMandatoryNumberCriterionOption("interactive_speed"),
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
createMandatoryTimestampCriterionOption,
|
||||
} from "./criteria/criterion";
|
||||
import { StudioIsMissingCriterionOption } from "./criteria/is-missing";
|
||||
import { StashIDCriterionOption } from "./criteria/stash-ids";
|
||||
import { ParentStudiosCriterionOption } from "./criteria/studios";
|
||||
import { ListFilterOptions } from "./filter-options";
|
||||
import { DisplayMode } from "./types";
|
||||
@@ -41,7 +42,7 @@ const criterionOptions = [
|
||||
createMandatoryNumberCriterionOption("image_count"),
|
||||
createMandatoryNumberCriterionOption("gallery_count"),
|
||||
createStringCriterionOption("url"),
|
||||
createStringCriterionOption("stash_id"),
|
||||
StashIDCriterionOption,
|
||||
createStringCriterionOption("aliases"),
|
||||
createMandatoryTimestampCriterionOption("created_at"),
|
||||
createMandatoryTimestampCriterionOption("updated_at"),
|
||||
|
||||
@@ -33,6 +33,11 @@ export interface IPHashDuplicationValue {
|
||||
distance?: number; // currently not implemented
|
||||
}
|
||||
|
||||
export interface IStashIDValue {
|
||||
endpoint: string;
|
||||
stashID: string;
|
||||
}
|
||||
|
||||
export interface IDateValue {
|
||||
value: string;
|
||||
value2: string | undefined;
|
||||
@@ -57,6 +62,13 @@ export function criterionIsNumberValue(
|
||||
return typeof value === "object" && "value" in value && "value2" in value;
|
||||
}
|
||||
|
||||
export function criterionIsStashIDValue(
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
value: any
|
||||
): value is IStashIDValue {
|
||||
return typeof value === "object" && "endpoint" in value && "stashID" in value;
|
||||
}
|
||||
|
||||
export function criterionIsDateValue(
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
value: any
|
||||
@@ -151,6 +163,7 @@ export type CriterionType =
|
||||
| "duplicated"
|
||||
| "ignore_auto_tag"
|
||||
| "file_count"
|
||||
| "stash_id_endpoint"
|
||||
| "date"
|
||||
| "created_at"
|
||||
| "updated_at"
|
||||
|
||||
Reference in New Issue
Block a user