Sort tags by name while scraping or merging scenes (#5752)

* Sort tags by name while scraping scenes
* TagStore.All should sort by sort_name first
* Sort tag by sort name/name in TagIDSelect
---------
Co-authored-by: WithoutPants <53250216+WithoutPants@users.noreply.github.com>
This commit is contained in:
its-josh4 2025-04-01 20:26:14 -07:00 committed by GitHub
parent 0f2bc3e01d
commit db06eae7cb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 42 additions and 5 deletions

View file

@ -62,7 +62,11 @@ func (r *queryResolver) FindTags(ctx context.Context, tagFilter *models.TagFilte
func (r *queryResolver) AllTags(ctx context.Context) (ret []*models.Tag, err error) {
if err := r.withReadTxn(ctx, func(ctx context.Context) error {
ret, err = r.repository.Tag.All(ctx)
return err
if err != nil {
return err
}
return nil
}); err != nil {
return nil, err
}

View file

@ -4,6 +4,7 @@ import (
"context"
"errors"
"fmt"
"slices"
"strconv"
"github.com/stashapp/stash/pkg/match"
@ -207,6 +208,10 @@ func (r *queryResolver) ScrapeSingleScene(ctx context.Context, source scraper.So
return nil, fmt.Errorf("%w: scraper_id or stash_box_index must be set", ErrInput)
}
for i := range ret {
slices.SortFunc(ret[i].Tags, models.ScrapedTagSortFunction)
}
return ret, nil
}

View file

@ -3,6 +3,7 @@ package models
import (
"context"
"strconv"
"strings"
"time"
"github.com/stashapp/stash/pkg/sliceutil/stringslice"
@ -400,6 +401,10 @@ type ScrapedTag struct {
func (ScrapedTag) IsScrapedContent() {}
func ScrapedTagSortFunction(a, b *ScrapedTag) int {
return strings.Compare(strings.ToLower(a.Name), strings.ToLower(b.Name))
}
// A movie from a scraping operation...
type ScrapedMovie struct {
StoredID *string `json:"stored_id"`

View file

@ -32,6 +32,8 @@ func FilterTags(excludeRegexps []*regexp.Regexp, tags []*models.ScrapedTag) (new
return tags, nil
}
newTags = make([]*models.ScrapedTag, 0, len(tags))
for _, t := range tags {
ignore := false
for _, reg := range excludeRegexps {

View file

@ -562,7 +562,7 @@ func (qb *TagStore) All(ctx context.Context) ([]*models.Tag, error) {
table := qb.table()
return qb.getMany(ctx, qb.selectDataset().Order(
table.Col("name").Asc(),
goqu.L("COALESCE(tags.sort_name, tags.name) COLLATE NATURAL_CI").Asc(),
table.Col(idColumn).Asc(),
))
}

View file

@ -1018,6 +1018,7 @@ type StashBoxConfig struct {
GuidelinesURL string `json:"guidelines_url"`
RequireSceneDraft bool `json:"require_scene_draft"`
EditUpdateLimit int `json:"edit_update_limit"`
RequireTagRole bool `json:"require_tag_role"`
}
type StringCriterionInput struct {
@ -2143,6 +2144,8 @@ const (
// May grant and rescind invite tokens and resind invite keys
RoleEnumManageInvites RoleEnum = "MANAGE_INVITES"
RoleEnumBot RoleEnum = "BOT"
RoleEnumReadOnly RoleEnum = "READ_ONLY"
RoleEnumEditTags RoleEnum = "EDIT_TAGS"
)
var AllRoleEnum = []RoleEnum{
@ -2154,11 +2157,13 @@ var AllRoleEnum = []RoleEnum{
RoleEnumInvite,
RoleEnumManageInvites,
RoleEnumBot,
RoleEnumReadOnly,
RoleEnumEditTags,
}
func (e RoleEnum) IsValid() bool {
switch e {
case RoleEnumRead, RoleEnumVote, RoleEnumEdit, RoleEnumModify, RoleEnumAdmin, RoleEnumInvite, RoleEnumManageInvites, RoleEnumBot:
case RoleEnumRead, RoleEnumVote, RoleEnumEdit, RoleEnumModify, RoleEnumAdmin, RoleEnumInvite, RoleEnumManageInvites, RoleEnumBot, RoleEnumReadOnly, RoleEnumEditTags:
return true
}
return false

View file

@ -36,7 +36,10 @@ export type SelectObject = {
title?: string | null;
};
export type Tag = Pick<GQL.Tag, "id" | "name" | "aliases" | "image_path">;
export type Tag = Pick<
GQL.Tag,
"id" | "name" | "sort_name" | "aliases" | "image_path"
>;
type Option = SelectOption<Tag>;
type FindTagsResult = Awaited<
@ -293,7 +296,20 @@ const _TagIDSelect: React.FC<IFilterProps & IFilterIDProps<Tag>> = (props) => {
const load = async () => {
const items = await loadObjectsByID(ids);
setValues(items);
// #4684 - sort items by sort name/name
const sortedItems = [...items];
sortedItems.sort((a, b) => {
const aName = a.sort_name || a.name;
const bName = b.sort_name || b.name;
if (aName && bName) {
return aName.localeCompare(bName);
}
return 0;
});
setValues(sortedItems);
};
load();