Skip to content

Commit

Permalink
Scene Marker duration filter and sort (#5472)
Browse files Browse the repository at this point in the history
Co-authored-by: WithoutPants <53250216+WithoutPants@users.noreply.github.com>
  • Loading branch information
dogwithakeyboard and WithoutPants authored Nov 29, 2024
1 parent e097f2b commit 6ad0951
Show file tree
Hide file tree
Showing 8 changed files with 155 additions and 2 deletions.
2 changes: 2 additions & 0 deletions graphql/schema/types/filters.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,8 @@ input SceneMarkerFilterType {
performers: MultiCriterionInput
"Filter to only include scene markers from these scenes"
scenes: MultiCriterionInput
"Filter by duration (in seconds)"
duration: FloatCriterionInput
"Filter by creation time"
created_at: TimestampCriterionInput
"Filter by last update time"
Expand Down
2 changes: 2 additions & 0 deletions pkg/models/scene_marker.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ type SceneMarkerFilterType struct {
Performers *MultiCriterionInput `json:"performers"`
// Filter to only include scene markers from these scenes
Scenes *MultiCriterionInput `json:"scenes"`
// Filter by duration (in seconds)
Duration *FloatCriterionInput `json:"duration"`
// Filter by created at
CreatedAt *TimestampCriterionInput `json:"created_at"`
// Filter by updated at
Expand Down
4 changes: 4 additions & 0 deletions pkg/sqlite/scene_marker.go
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,7 @@ var sceneMarkerSortOptions = sortOptions{
"scenes_updated_at",
"seconds",
"updated_at",
"duration",
}

func (qb *SceneMarkerStore) setSceneMarkerSort(query *queryBuilder, findFilter *models.FindFilterType) error {
Expand All @@ -386,6 +387,9 @@ func (qb *SceneMarkerStore) setSceneMarkerSort(query *queryBuilder, findFilter *
case "title":
query.join(tagTable, "", "scene_markers.primary_tag_id = tags.id")
query.sortAndPagination += " ORDER BY COALESCE(NULLIF(scene_markers.title,''), tags.name) COLLATE NATURAL_CI " + direction
case "duration":
sort = "(scene_markers.end_seconds - scene_markers.seconds)"
query.sortAndPagination += getSort(sort, direction, sceneMarkerTable)
default:
query.sortAndPagination += getSort(sort, direction, sceneMarkerTable)
}
Expand Down
1 change: 1 addition & 0 deletions pkg/sqlite/scene_marker_filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ func (qb *sceneMarkerFilterHandler) criterionHandler() criterionHandler {
qb.sceneTagsCriterionHandler(sceneMarkerFilter.SceneTags),
qb.performersCriterionHandler(sceneMarkerFilter.Performers),
qb.scenesCriterionHandler(sceneMarkerFilter.Scenes),
floatCriterionHandler(sceneMarkerFilter.Duration, "COALESCE(scene_markers.end_seconds - scene_markers.seconds, NULL)", nil),
&timestampCriterionHandler{sceneMarkerFilter.CreatedAt, "scene_markers.created_at", nil},
&timestampCriterionHandler{sceneMarkerFilter.UpdatedAt, "scene_markers.updated_at", nil},
&dateCriterionHandler{sceneMarkerFilter.SceneDate, "scenes.date", qb.joinScenes},
Expand Down
110 changes: 110 additions & 0 deletions pkg/sqlite/scene_marker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,116 @@ func TestMarkerQuerySceneTags(t *testing.T) {
})
}

func markersToIDs(i []*models.SceneMarker) []int {
ret := make([]int, len(i))
for i, v := range i {
ret[i] = v.ID
}

return ret
}

func TestMarkerQueryDuration(t *testing.T) {
type test struct {
name string
markerFilter *models.SceneMarkerFilterType
include []int
exclude []int
}

cases := []test{
{
"is null",
&models.SceneMarkerFilterType{
Duration: &models.FloatCriterionInput{
Modifier: models.CriterionModifierIsNull,
},
},
[]int{markerIdxWithScene},
[]int{markerIdxWithDuration},
},
{
"not null",
&models.SceneMarkerFilterType{
Duration: &models.FloatCriterionInput{
Modifier: models.CriterionModifierNotNull,
},
},
[]int{markerIdxWithDuration},
[]int{markerIdxWithScene},
},
{
"equals",
&models.SceneMarkerFilterType{
Duration: &models.FloatCriterionInput{
Modifier: models.CriterionModifierEquals,
Value: markerIdxWithDuration,
},
},
[]int{markerIdxWithDuration},
[]int{markerIdx2WithDuration, markerIdxWithScene},
},
{
"not equals",
&models.SceneMarkerFilterType{
Duration: &models.FloatCriterionInput{
Modifier: models.CriterionModifierNotEquals,
Value: markerIdx2WithDuration,
},
},
[]int{markerIdxWithDuration},
[]int{markerIdx2WithDuration, markerIdxWithScene},
},
{
"greater than",
&models.SceneMarkerFilterType{
Duration: &models.FloatCriterionInput{
Modifier: models.CriterionModifierGreaterThan,
Value: markerIdxWithDuration,
},
},
[]int{markerIdx2WithDuration},
[]int{markerIdxWithDuration, markerIdxWithScene},
},
{
"less than",
&models.SceneMarkerFilterType{
Duration: &models.FloatCriterionInput{
Modifier: models.CriterionModifierLessThan,
Value: markerIdx2WithDuration,
},
},
[]int{markerIdxWithDuration},
[]int{markerIdx2WithDuration, markerIdxWithScene},
},
}

qb := db.SceneMarker

for _, tt := range cases {
runWithRollbackTxn(t, tt.name, func(t *testing.T, ctx context.Context) {
assert := assert.New(t)
got, _, err := qb.Query(ctx, tt.markerFilter, nil)
if err != nil {
t.Errorf("SceneMarkerStore.Query() error = %v", err)
return
}

ids := markersToIDs(got)
include := indexesToIDs(markerIDs, tt.include)
exclude := indexesToIDs(markerIDs, tt.exclude)

for _, i := range include {
assert.Contains(ids, i)
}
for _, e := range exclude {
assert.NotContains(ids, e)
}
})
}

}

func queryMarkers(ctx context.Context, t *testing.T, sqb models.SceneMarkerReader, markerFilter *models.SceneMarkerFilterType, findFilter *models.FindFilterType) []*models.SceneMarker {
t.Helper()
result, _, err := sqb.Query(ctx, markerFilter, findFilter)
Expand Down
12 changes: 12 additions & 0 deletions pkg/sqlite/setup_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,8 @@ const (
markerIdxWithScene = iota
markerIdxWithTag
markerIdxWithSceneTag
markerIdxWithDuration
markerIdx2WithDuration
totalMarkers
)

Expand Down Expand Up @@ -1754,10 +1756,20 @@ func createStudios(ctx context.Context, n int, o int) error {
return nil
}

func getMarkerEndSeconds(index int) *float64 {
if index != markerIdxWithDuration && index != markerIdx2WithDuration {
return nil
}
ret := float64(index)
return &ret
}

func createMarker(ctx context.Context, mqb models.SceneMarkerReaderWriter, markerSpec markerSpec) error {
markerIdx := len(markerIDs)
marker := models.SceneMarker{
SceneID: sceneIDs[markerSpec.sceneIdx],
PrimaryTagID: tagIDs[markerSpec.primaryTagIdx],
EndSeconds: getMarkerEndSeconds(markerIdx),
}

err := mqb.Create(ctx, &marker)
Expand Down
23 changes: 21 additions & 2 deletions ui/v2.5/src/models/list-filter/criteria/criterion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -637,7 +637,11 @@ export function createNumberCriterionOption(
}

export class NullNumberCriterionOption extends CriterionOption {
constructor(messageID: string, value: CriterionType) {
constructor(
messageID: string,
value: CriterionType,
makeCriterion?: () => Criterion<CriterionValue>
) {
super({
messageID,
type: value,
Expand All @@ -653,7 +657,9 @@ export class NullNumberCriterionOption extends CriterionOption {
],
defaultModifier: CriterionModifier.Equals,
inputType: "number",
makeCriterion: () => new NumberCriterion(this),
makeCriterion: makeCriterion
? makeCriterion
: () => new NumberCriterion(this),
});
}
}
Expand Down Expand Up @@ -780,6 +786,19 @@ export function createDurationCriterionOption(
return new DurationCriterionOption(messageID ?? value, value);
}

export class NullDurationCriterionOption extends NullNumberCriterionOption {
constructor(messageID: string, value: CriterionType) {
super(messageID, value, () => new DurationCriterion(this));
}
}

export function createNullDurationCriterionOption(
value: CriterionType,
messageID?: string
) {
return new NullDurationCriterionOption(messageID ?? value, value);
}

export class DurationCriterion extends Criterion<INumberValue> {
constructor(type: CriterionOption) {
super(type, { value: undefined, value2: undefined });
Expand Down
3 changes: 3 additions & 0 deletions ui/v2.5/src/models/list-filter/scene-markers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ import { DisplayMode } from "./types";
import {
createDateCriterionOption,
createMandatoryTimestampCriterionOption,
createNullDurationCriterionOption,
} from "./criteria/criterion";

const defaultSortBy = "title";
const sortByOptions = [
"duration",
"title",
"seconds",
"scene_id",
Expand All @@ -22,6 +24,7 @@ const criterionOptions = [
MarkersScenesCriterionOption,
SceneTagsCriterionOption,
PerformersCriterionOption,
createNullDurationCriterionOption("duration"),
createMandatoryTimestampCriterionOption("created_at"),
createMandatoryTimestampCriterionOption("updated_at"),
createDateCriterionOption("scene_date"),
Expand Down

0 comments on commit 6ad0951

Please sign in to comment.