mirror of
https://github.com/stashapp/stash.git
synced 2025-12-17 04:14:39 +03:00
[Files Refactor] Filter and sort by file count (#2744)
* Add filtering on file count * Add sorting by file count
This commit is contained in:
@@ -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"""
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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":
|
||||
|
||||
@@ -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":
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -10,7 +10,7 @@ interface IReleaseNotes {
|
||||
|
||||
export const releaseNotes: IReleaseNotes[] = [
|
||||
{
|
||||
date: 20220707,
|
||||
date: 20220715,
|
||||
content: v0170,
|
||||
},
|
||||
];
|
||||
|
||||
@@ -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.
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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)
|
||||
);
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -133,4 +133,5 @@ export type CriterionType =
|
||||
| "performer_favorite"
|
||||
| "performer_age"
|
||||
| "duplicated"
|
||||
| "ignore_auto_tag";
|
||||
| "ignore_auto_tag"
|
||||
| "file_count";
|
||||
|
||||
Reference in New Issue
Block a user