diff --git a/pkg/sqlite/performer.go b/pkg/sqlite/performer.go index 1b1f103da..c5943b182 100644 --- a/pkg/sqlite/performer.go +++ b/pkg/sqlite/performer.go @@ -726,6 +726,28 @@ func (qb *PerformerStore) sortByLastPlayedAt(direction string) string { return " ORDER BY (" + selectPerformerLastPlayedAtSQL + ") " + direction } +// used for sorting by total scene duration +var selectPerformerScenesDurationSQL = utils.StrFormat( + "SELECT COALESCE(SUM(video_files.duration), 0) FROM {performers_scenes} s "+ + "LEFT JOIN {scenes} ON {scenes}.id = s.{scene_id} "+ + "LEFT JOIN {scenes_files} ON {scenes_files}.{scene_id} = {scenes}.id "+ + "LEFT JOIN video_files ON video_files.file_id = {scenes_files}.file_id "+ + "WHERE s.{performer_id} = {performers}.id", + map[string]interface{}{ + "performer_id": performerIDColumn, + "performers": performerTable, + "performers_scenes": performersScenesTable, + "scenes": sceneTable, + "scene_id": sceneIDColumn, + "scenes_files": scenesFilesTable, + }, +) + +func (qb *PerformerStore) sortByScenesDuration(direction string) string { + // need to sum duration from all scenes for this performer + return " ORDER BY (" + selectPerformerScenesDurationSQL + ") " + direction +} + var performerSortOptions = sortOptions{ "birthdate", "career_length", @@ -744,6 +766,7 @@ var performerSortOptions = sortOptions{ "random", "rating", "scenes_count", + "scenes_duration", "tag_count", "updated_at", "weight", @@ -771,6 +794,8 @@ func (qb *PerformerStore) getPerformerSort(findFilter *models.FindFilterType) (s sortQuery += getCountSort(performerTable, performersTagsTable, performerIDColumn, direction) case "scenes_count": sortQuery += getCountSort(performerTable, performersScenesTable, performerIDColumn, direction) + case "scenes_duration": + sortQuery += qb.sortByScenesDuration(direction) case "images_count": sortQuery += getCountSort(performerTable, performersImagesTable, performerIDColumn, direction) case "galleries_count": diff --git a/pkg/sqlite/studio.go b/pkg/sqlite/studio.go index 7d93eee28..5affb73d6 100644 --- a/pkg/sqlite/studio.go +++ b/pkg/sqlite/studio.go @@ -576,6 +576,16 @@ func (qb *StudioStore) QueryCount(ctx context.Context, studioFilter *models.Stud return query.executeCount(ctx) } +func (qb *StudioStore) sortByScenesDuration(direction string) string { + return fmt.Sprintf(` ORDER BY ( + SELECT COALESCE(SUM(video_files.duration), 0) + FROM %s + LEFT JOIN %s ON %s.%s = %s.id + LEFT JOIN video_files ON video_files.file_id = %s.file_id + WHERE %s.%s = %s.id + ) %s`, sceneTable, scenesFilesTable, scenesFilesTable, sceneIDColumn, sceneTable, scenesFilesTable, sceneTable, studioIDColumn, studioTable, getSortDirection(direction)) +} + var studioSortOptions = sortOptions{ "child_count", "created_at", @@ -584,6 +594,7 @@ var studioSortOptions = sortOptions{ "images_count", "name", "scenes_count", + "scenes_duration", "random", "rating", "tag_count", @@ -612,6 +623,8 @@ func (qb *StudioStore) getStudioSort(findFilter *models.FindFilterType) (string, sortQuery += getCountSort(studioTable, studiosTagsTable, studioIDColumn, direction) case "scenes_count": sortQuery += getCountSort(studioTable, sceneTable, studioIDColumn, direction) + case "scenes_duration": + sortQuery += qb.sortByScenesDuration(direction) case "images_count": sortQuery += getCountSort(studioTable, imageTable, studioIDColumn, direction) case "galleries_count": diff --git a/pkg/sqlite/tag.go b/pkg/sqlite/tag.go index ede1fcc2e..87ec01f5d 100644 --- a/pkg/sqlite/tag.go +++ b/pkg/sqlite/tag.go @@ -651,9 +651,21 @@ var tagSortOptions = sortOptions{ "random", "scene_markers_count", "scenes_count", + "scenes_duration", "updated_at", } +func (qb *TagStore) sortByScenesDuration(direction string) string { + return fmt.Sprintf(` ORDER BY ( + SELECT COALESCE(SUM(video_files.duration), 0) + FROM %s + LEFT JOIN %s ON %s.id = %s.%s + LEFT JOIN %s ON %s.%s = %s.id + LEFT JOIN video_files ON video_files.file_id = %s.file_id + WHERE %s.%s = %s.id + ) %s`, scenesTagsTable, sceneTable, sceneTable, scenesTagsTable, sceneIDColumn, scenesFilesTable, scenesFilesTable, sceneIDColumn, sceneTable, scenesFilesTable, scenesTagsTable, tagIDColumn, tagTable, getSortDirection(direction)) +} + func (qb *TagStore) getDefaultTagSort() string { return getSort("name", "ASC", "tags") } @@ -680,6 +692,8 @@ func (qb *TagStore) getTagSort(query *queryBuilder, findFilter *models.FindFilte sortQuery += fmt.Sprintf(" ORDER BY COALESCE(tags.sort_name, tags.name) COLLATE NATURAL_CI %s", getSortDirection(direction)) case "scenes_count": sortQuery += getCountSort(tagTable, scenesTagsTable, tagIDColumn, direction) + case "scenes_duration": + sortQuery += qb.sortByScenesDuration(direction) case "scene_markers_count": sortQuery += fmt.Sprintf(" ORDER BY (SELECT COUNT(*) FROM scene_markers_tags WHERE tags.id = scene_markers_tags.tag_id)+(SELECT COUNT(*) FROM scene_markers WHERE tags.id = scene_markers.primary_tag_id) %s", getSortDirection(direction)) case "images_count": diff --git a/ui/v2.5/src/locales/en-GB.json b/ui/v2.5/src/locales/en-GB.json index a8d731b32..48cf2f6d3 100644 --- a/ui/v2.5/src/locales/en-GB.json +++ b/ui/v2.5/src/locales/en-GB.json @@ -1308,6 +1308,7 @@ "sceneTagger": "Scene Tagger", "scene_code": "Studio Code", "scene_count": "Scene Count", + "scenes_duration": "Scene Duration", "scene_created_at": "Scene Created At", "scene_date": "Date of Scene", "scene_id": "Scene ID", diff --git a/ui/v2.5/src/models/list-filter/performers.ts b/ui/v2.5/src/models/list-filter/performers.ts index a8d3c9096..2cb3ef216 100644 --- a/ui/v2.5/src/models/list-filter/performers.ts +++ b/ui/v2.5/src/models/list-filter/performers.ts @@ -35,6 +35,7 @@ const sortByOptions = [ "career_length", "weight", "measurements", + "scenes_duration", ] .map(ListFilterOptions.createSortBy) .concat([ diff --git a/ui/v2.5/src/models/list-filter/studios.ts b/ui/v2.5/src/models/list-filter/studios.ts index a25fd9e22..02dfae2f6 100644 --- a/ui/v2.5/src/models/list-filter/studios.ts +++ b/ui/v2.5/src/models/list-filter/studios.ts @@ -15,7 +15,13 @@ import { ListFilterOptions } from "./filter-options"; import { DisplayMode } from "./types"; const defaultSortBy = "name"; -const sortByOptions = ["name", "tag_count", "random", "rating"] +const sortByOptions = [ + "name", + "tag_count", + "random", + "rating", + "scenes_duration", +] .map(ListFilterOptions.createSortBy) .concat([ { diff --git a/ui/v2.5/src/models/list-filter/tags.ts b/ui/v2.5/src/models/list-filter/tags.ts index db3a84666..c664f218e 100644 --- a/ui/v2.5/src/models/list-filter/tags.ts +++ b/ui/v2.5/src/models/list-filter/tags.ts @@ -16,7 +16,7 @@ import { import { FavoriteTagCriterionOption } from "./criteria/favorite"; const defaultSortBy = "name"; -const sortByOptions = ["name", "random"] +const sortByOptions = ["name", "random", "scenes_duration"] .map(ListFilterOptions.createSortBy) .concat([ {