[Files Refactor] Filter and sort by file count (#2744)

* Add filtering on file count
* Add sorting by file count
This commit is contained in:
WithoutPants
2022-07-15 09:29:03 +10:00
parent 5495d72849
commit 461068462c
17 changed files with 85 additions and 14 deletions

View File

@@ -132,6 +132,8 @@ input SceneFilterType {
phash: StringCriterionInput
"""Filter by path"""
path: StringCriterionInput
"""Filter by file count"""
file_count: IntCriterionInput
"""Filter by rating"""
rating: IntCriterionInput
"""Filter by organized"""
@@ -239,6 +241,8 @@ input GalleryFilterType {
checksum: StringCriterionInput
"""Filter by path"""
path: StringCriterionInput
"""Filter by zip-file count"""
file_count: IntCriterionInput
"""Filter to only include galleries missing this property"""
is_missing: String
"""Filter to include/exclude galleries that were created from zip"""
@@ -327,6 +331,8 @@ input ImageFilterType {
checksum: StringCriterionInput
"""Filter by path"""
path: StringCriterionInput
"""Filter by file count"""
file_count: IntCriterionInput
"""Filter by rating"""
rating: IntCriterionInput
"""Filter by organized"""

View File

@@ -16,6 +16,8 @@ type GalleryFilterType struct {
Checksum *StringCriterionInput `json:"checksum"`
// Filter by path
Path *StringCriterionInput `json:"path"`
// Filter by zip file count
FileCount *IntCriterionInput `json:"file_count"`
// Filter to only include galleries missing this property
IsMissing *string `json:"is_missing"`
// Filter to include/exclude galleries that were created from zip

View File

@@ -11,6 +11,8 @@ type ImageFilterType struct {
Checksum *StringCriterionInput `json:"checksum"`
// Filter by path
Path *StringCriterionInput `json:"path"`
// Filter by file count
FileCount *IntCriterionInput `json:"file_count"`
// Filter by rating
Rating *IntCriterionInput `json:"rating"`
// Filter by organized

View File

@@ -26,6 +26,8 @@ type SceneFilterType struct {
Phash *StringCriterionInput `json:"phash"`
// Filter by path
Path *StringCriterionInput `json:"path"`
// Filter by file count
FileCount *IntCriterionInput `json:"file_count"`
// Filter by rating
Rating *IntCriterionInput `json:"rating"`
// Filter by organized

View File

@@ -596,6 +596,7 @@ func (qb *GalleryStore) makeFilter(ctx context.Context, galleryFilter *models.Ga
}))
query.handleCriterion(ctx, pathCriterionHandler(galleryFilter.Path, "galleries_query.parent_folder_path", "galleries_query.basename"))
query.handleCriterion(ctx, galleryFileCountCriterionHandler(qb, galleryFilter.FileCount))
query.handleCriterion(ctx, intCriterionHandler(galleryFilter.Rating, "galleries.rating"))
query.handleCriterion(ctx, stringCriterionHandler(galleryFilter.URL, "galleries.url"))
query.handleCriterion(ctx, boolCriterionHandler(galleryFilter.Organized, "galleries.organized"))
@@ -683,6 +684,16 @@ func (qb *GalleryStore) QueryCount(ctx context.Context, galleryFilter *models.Ga
return query.executeCount(ctx)
}
func galleryFileCountCriterionHandler(qb *GalleryStore, fileCount *models.IntCriterionInput) criterionHandlerFunc {
h := countCriterionHandlerBuilder{
primaryTable: galleryTable,
joinTable: galleriesFilesTable,
primaryFK: galleryIDColumn,
}
return h.handler(fileCount)
}
func galleryIsMissingCriterionHandler(qb *GalleryStore, isMissing *string) criterionHandlerFunc {
return func(ctx context.Context, f *filterBuilder) {
if isMissing != nil && *isMissing != "" {
@@ -897,6 +908,8 @@ func (qb *GalleryStore) getGallerySort(findFilter *models.FindFilterType) string
}
switch sort {
case "file_count":
return getCountSort(galleryTable, galleriesFilesTable, galleryIDColumn, direction)
case "images_count":
return getCountSort(galleryTable, galleriesImagesTable, galleryIDColumn, direction)
case "tag_count":

View File

@@ -564,6 +564,7 @@ func (qb *ImageStore) makeFilter(ctx context.Context, imageFilter *models.ImageF
query.handleCriterion(ctx, stringCriterionHandler(imageFilter.Title, "images.title"))
query.handleCriterion(ctx, pathCriterionHandler(imageFilter.Path, "images_query.parent_folder_path", "images_query.basename"))
query.handleCriterion(ctx, imageFileCountCriterionHandler(qb, imageFilter.FileCount))
query.handleCriterion(ctx, intCriterionHandler(imageFilter.Rating, "images.rating"))
query.handleCriterion(ctx, intCriterionHandler(imageFilter.OCounter, "images.o_counter"))
query.handleCriterion(ctx, boolCriterionHandler(imageFilter.Organized, "images.organized"))
@@ -689,6 +690,16 @@ func (qb *ImageStore) QueryCount(ctx context.Context, imageFilter *models.ImageF
return query.executeCount(ctx)
}
func imageFileCountCriterionHandler(qb *ImageStore, fileCount *models.IntCriterionInput) criterionHandlerFunc {
h := countCriterionHandlerBuilder{
primaryTable: imageTable,
joinTable: imagesFilesTable,
primaryFK: imageIDColumn,
}
return h.handler(fileCount)
}
func imageIsMissingCriterionHandler(qb *ImageStore, isMissing *string) criterionHandlerFunc {
return func(ctx context.Context, f *filterBuilder) {
if isMissing != nil && *isMissing != "" {
@@ -869,6 +880,8 @@ func (qb *ImageStore) getImageSort(findFilter *models.FindFilterType) string {
switch sort {
case "path":
return " ORDER BY images_query.parent_folder_path " + direction + ", images_query.basename " + direction
case "file_count":
return getCountSort(imageTable, imagesFilesTable, imageIDColumn, direction)
case "tag_count":
return getCountSort(imageTable, imagesTagsTable, imageIDColumn, direction)
case "performer_count":

View File

@@ -756,6 +756,7 @@ func (qb *SceneStore) makeFilter(ctx context.Context, sceneFilter *models.SceneF
}
query.handleCriterion(ctx, pathCriterionHandler(sceneFilter.Path, "scenes_query.parent_folder_path", "scenes_query.basename"))
query.handleCriterion(ctx, sceneFileCountCriterionHandler(qb, sceneFilter.FileCount))
query.handleCriterion(ctx, stringCriterionHandler(sceneFilter.Title, "scenes.title"))
query.handleCriterion(ctx, stringCriterionHandler(sceneFilter.Details, "scenes.details"))
query.handleCriterion(ctx, criterionHandlerFunc(func(ctx context.Context, f *filterBuilder) {
@@ -916,6 +917,16 @@ func (qb *SceneStore) queryGroupedFields(ctx context.Context, options models.Sce
return ret, nil
}
func sceneFileCountCriterionHandler(qb *SceneStore, fileCount *models.IntCriterionInput) criterionHandlerFunc {
h := countCriterionHandlerBuilder{
primaryTable: sceneTable,
joinTable: scenesFilesTable,
primaryFK: sceneIDColumn,
}
return h.handler(fileCount)
}
func scenePhashDuplicatedCriterionHandler(duplicatedFilter *models.PHashDuplicationCriterionInput) criterionHandlerFunc {
return func(ctx context.Context, f *filterBuilder) {
// TODO: Wishlist item: Implement Distance matching
@@ -1204,6 +1215,8 @@ func (qb *SceneStore) setSceneSort(query *queryBuilder, findFilter *models.FindF
query.sortAndPagination += getCountSort(sceneTable, scenesTagsTable, sceneIDColumn, direction)
case "performer_count":
query.sortAndPagination += getCountSort(sceneTable, performersScenesTable, sceneIDColumn, direction)
case "file_count":
query.sortAndPagination += getCountSort(sceneTable, scenesFilesTable, sceneIDColumn, direction)
case "path":
// special handling for path
query.sortAndPagination += fmt.Sprintf(" ORDER BY scenes_query.parent_folder_path %s, scenes_query.basename %[1]s", direction)

View File

@@ -7,14 +7,15 @@ After migrating, please run a scan on your entire library to populate missing da
Please report all issues to the following Github issue: https://github.com/stashapp/stash/issues/2737
### 💥 Known issues
* import/export functionality is currently disabled. Needs further design.
* missing covers are not currently regenerated. Need to consider further, especially around scene cover redesign.
* deleting galleries is currently slow.
* Import/export functionality is currently disabled. Needs further design.
* Missing covers are not currently regenerated. Need to consider further, especially around scene cover redesign.
* Deleting galleries is currently slow.
* Don't include file extension as part of the title scan flag is not supported.
* Set name, date, details from embedded file metadata scan flag is not supported.
### ✨ New Features
* Added support for identical files. Identical files are assigned to the same scene/gallery/image and can be viewed in File Info. ([#2676](https://github.com/stashapp/stash/pull/2676))
* Added support for filtering and sorting by file count. ([#2744](https://github.com/stashapp/stash/pull/2744))
* Added release notes dialog. ([#2726](https://github.com/stashapp/stash/pull/2726))
### 🎨 Improvements

View File

@@ -10,7 +10,7 @@ interface IReleaseNotes {
export const releaseNotes: IReleaseNotes[] = [
{
date: 20220707,
date: 20220715,
content: v0170,
},
];

View File

@@ -7,12 +7,13 @@ After migrating, please run a scan on your entire library to populate missing da
Please report all issues to the following Github issue: https://github.com/stashapp/stash/issues/2737
### 💥 Known issues
* import/export functionality is currently disabled. Needs further design.
* missing covers are not currently regenerated. Need to consider further, especially around scene cover redesign.
* deleting galleries is currently slow.
* Import/export functionality is currently disabled. Needs further design.
* Missing covers are not currently regenerated. Need to consider further, especially around scene cover redesign.
* Deleting galleries is currently slow.
* Don't include file extension as part of the title scan flag is not supported.
* Set name, date, details from embedded file metadata scan flag is not supported.
### Other changes:
* Added support for filtering and sorting by file count. ([#2744](https://github.com/stashapp/stash/pull/2744))
* Changelog has been moved from the stats page to a section in the Settings page.

View File

@@ -728,6 +728,7 @@
"false": "False",
"favourite": "Favourite",
"file": "file",
"file_count": "File Count",
"file_info": "File Info",
"file_mod_time": "File Modification Time",
"files": "files",
@@ -1024,5 +1025,6 @@
"videos": "Videos",
"view_all": "View All",
"weight": "Weight",
"years_old": "years old"
"years_old": "years old",
"zip_file_count": "Zip File Count"
}

View File

@@ -497,8 +497,11 @@ export class MandatoryNumberCriterionOption extends CriterionOption {
}
}
export function createMandatoryNumberCriterionOption(value: CriterionType) {
return new MandatoryNumberCriterionOption(value, value, value);
export function createMandatoryNumberCriterionOption(
value: CriterionType,
messageID?: string
) {
return new MandatoryNumberCriterionOption(messageID ?? value, value, value);
}
export class DurationCriterion extends Criterion<INumberValue> {

View File

@@ -75,6 +75,7 @@ export function makeCriteria(type: CriterionType = "none") {
case "performer_count":
case "performer_age":
case "tag_count":
case "file_count":
return new NumberCriterion(
new MandatoryNumberCriterionOption(type, type)
);

View File

@@ -25,6 +25,10 @@ const sortByOptions = ["date", ...MediaSortByOptions]
messageID: "image_count",
value: "images_count",
},
{
messageID: "zip_file_count",
value: "file_count",
},
]);
const displayModeOptions = [
@@ -56,6 +60,7 @@ const criterionOptions = [
createStringCriterionOption("image_count"),
StudiosCriterionOption,
createStringCriterionOption("url"),
createMandatoryNumberCriterionOption("file_count", "zip_file_count"),
];
export const GalleryListFilterOptions = new ListFilterOptions(

View File

@@ -19,9 +19,12 @@ import { DisplayMode } from "./types";
const defaultSortBy = "path";
const sortByOptions = ["o_counter", "filesize", ...MediaSortByOptions].map(
ListFilterOptions.createSortBy
);
const sortByOptions = [
"o_counter",
"filesize",
"file_count",
...MediaSortByOptions,
].map(ListFilterOptions.createSortBy);
const displayModeOptions = [DisplayMode.Grid, DisplayMode.Wall];
const criterionOptions = [
@@ -41,6 +44,7 @@ const criterionOptions = [
createMandatoryNumberCriterionOption("performer_age"),
PerformerFavoriteCriterionOption,
StudiosCriterionOption,
createMandatoryNumberCriterionOption("file_count"),
];
export const ImageListFilterOptions = new ListFilterOptions(
defaultSortBy,

View File

@@ -30,6 +30,7 @@ const sortByOptions = [
"organized",
"o_counter",
"date",
"file_count",
"filesize",
"duration",
"framerate",
@@ -81,6 +82,7 @@ const criterionOptions = [
InteractiveCriterionOption,
CaptionsCriterionOption,
createMandatoryNumberCriterionOption("interactive_speed"),
createMandatoryNumberCriterionOption("file_count"),
];
export const SceneListFilterOptions = new ListFilterOptions(

View File

@@ -133,4 +133,5 @@ export type CriterionType =
| "performer_favorite"
| "performer_age"
| "duplicated"
| "ignore_auto_tag";
| "ignore_auto_tag"
| "file_count";