From 5453f179ac5389507a6011a44d2957a8a2ec3129 Mon Sep 17 00:00:00 2001 From: Gykes <24581046+Gykes@users.noreply.github.com> Date: Sat, 29 Nov 2025 13:35:40 -0800 Subject: [PATCH] sort all urls alphabetically --- pkg/sqlite/gallery.go | 18 ++++++++++++++++-- pkg/sqlite/group.go | 18 ++++++++++++++++-- pkg/sqlite/image.go | 18 ++++++++++++++++-- pkg/sqlite/performer.go | 17 +++++++++++++++-- pkg/sqlite/scene.go | 17 +++++++++++++++-- pkg/sqlite/studio.go | 18 ++++++++++++++++-- pkg/utils/url.go | 24 +++++++++++++++++++++++- 7 files changed, 117 insertions(+), 13 deletions(-) diff --git a/pkg/sqlite/gallery.go b/pkg/sqlite/gallery.go index 9cfe38b1f..04b7be9a1 100644 --- a/pkg/sqlite/gallery.go +++ b/pkg/sqlite/gallery.go @@ -12,6 +12,7 @@ import ( "github.com/doug-martin/goqu/v9/exp" "github.com/jmoiron/sqlx" "github.com/stashapp/stash/pkg/models" + "github.com/stashapp/stash/pkg/utils" "gopkg.in/guregu/null.v4" "gopkg.in/guregu/null.v4/zero" ) @@ -246,8 +247,10 @@ func (qb *GalleryStore) Create(ctx context.Context, newObject *models.Gallery, f } if newObject.URLs.Loaded() { + urls := newObject.URLs.List() + utils.SortURLs(urls) const startPos = 0 - if err := galleriesURLsTableMgr.insertJoins(ctx, id, startPos, newObject.URLs.List()); err != nil { + if err := galleriesURLsTableMgr.insertJoins(ctx, id, startPos, urls); err != nil { return err } } @@ -286,7 +289,9 @@ func (qb *GalleryStore) Update(ctx context.Context, updatedObject *models.Galler } if updatedObject.URLs.Loaded() { - if err := galleriesURLsTableMgr.replaceJoins(ctx, updatedObject.ID, updatedObject.URLs.List()); err != nil { + urls := updatedObject.URLs.List() + utils.SortURLs(urls) + if err := galleriesURLsTableMgr.replaceJoins(ctx, updatedObject.ID, urls); err != nil { return err } } @@ -339,6 +344,15 @@ func (qb *GalleryStore) UpdatePartial(ctx context.Context, id int, partial model if err := galleriesURLsTableMgr.modifyJoins(ctx, id, partial.URLs.Values, partial.URLs.Mode); err != nil { return nil, err } + // Re-sort URLs after modification + urls, err := galleriesURLsTableMgr.get(ctx, id) + if err != nil { + return nil, err + } + utils.SortURLs(urls) + if err := galleriesURLsTableMgr.replaceJoins(ctx, id, urls); err != nil { + return nil, err + } } if partial.PerformerIDs != nil { if err := galleriesPerformersTableMgr.modifyJoins(ctx, id, partial.PerformerIDs.IDs, partial.PerformerIDs.Mode); err != nil { diff --git a/pkg/sqlite/group.go b/pkg/sqlite/group.go index f0f8d6b40..fc3feb7d1 100644 --- a/pkg/sqlite/group.go +++ b/pkg/sqlite/group.go @@ -14,6 +14,7 @@ import ( "gopkg.in/guregu/null.v4/zero" "github.com/stashapp/stash/pkg/models" + "github.com/stashapp/stash/pkg/utils" ) const ( @@ -172,8 +173,10 @@ func (qb *GroupStore) Create(ctx context.Context, newObject *models.Group) error } if newObject.URLs.Loaded() { + urls := newObject.URLs.List() + utils.SortURLs(urls) const startPos = 0 - if err := groupsURLsTableMgr.insertJoins(ctx, id, startPos, newObject.URLs.List()); err != nil { + if err := groupsURLsTableMgr.insertJoins(ctx, id, startPos, urls); err != nil { return err } } @@ -219,6 +222,15 @@ func (qb *GroupStore) UpdatePartial(ctx context.Context, id int, partial models. if err := groupsURLsTableMgr.modifyJoins(ctx, id, partial.URLs.Values, partial.URLs.Mode); err != nil { return nil, err } + // Re-sort URLs after modification + urls, err := groupsURLsTableMgr.get(ctx, id) + if err != nil { + return nil, err + } + utils.SortURLs(urls) + if err := groupsURLsTableMgr.replaceJoins(ctx, id, urls); err != nil { + return nil, err + } } if err := qb.tagRelationshipStore.modifyRelationships(ctx, id, partial.TagIDs); err != nil { @@ -245,7 +257,9 @@ func (qb *GroupStore) Update(ctx context.Context, updatedObject *models.Group) e } if updatedObject.URLs.Loaded() { - if err := groupsURLsTableMgr.replaceJoins(ctx, updatedObject.ID, updatedObject.URLs.List()); err != nil { + urls := updatedObject.URLs.List() + utils.SortURLs(urls) + if err := groupsURLsTableMgr.replaceJoins(ctx, updatedObject.ID, urls); err != nil { return err } } diff --git a/pkg/sqlite/image.go b/pkg/sqlite/image.go index 1588fa415..bdad885e4 100644 --- a/pkg/sqlite/image.go +++ b/pkg/sqlite/image.go @@ -11,6 +11,7 @@ import ( "github.com/jmoiron/sqlx" "github.com/stashapp/stash/pkg/models" "github.com/stashapp/stash/pkg/sliceutil" + "github.com/stashapp/stash/pkg/utils" "gopkg.in/guregu/null.v4" "gopkg.in/guregu/null.v4/zero" @@ -251,8 +252,10 @@ func (qb *ImageStore) Create(ctx context.Context, newObject *models.Image, fileI } if newObject.URLs.Loaded() { + urls := newObject.URLs.List() + utils.SortURLs(urls) const startPos = 0 - if err := imagesURLsTableMgr.insertJoins(ctx, id, startPos, newObject.URLs.List()); err != nil { + if err := imagesURLsTableMgr.insertJoins(ctx, id, startPos, urls); err != nil { return err } } @@ -309,6 +312,15 @@ func (qb *ImageStore) UpdatePartial(ctx context.Context, id int, partial models. if err := imagesURLsTableMgr.modifyJoins(ctx, id, partial.URLs.Values, partial.URLs.Mode); err != nil { return nil, err } + // Re-sort URLs after modification + urls, err := imagesURLsTableMgr.get(ctx, id) + if err != nil { + return nil, err + } + utils.SortURLs(urls) + if err := imagesURLsTableMgr.replaceJoins(ctx, id, urls); err != nil { + return nil, err + } } if partial.PerformerIDs != nil { if err := imagesPerformersTableMgr.modifyJoins(ctx, id, partial.PerformerIDs.IDs, partial.PerformerIDs.Mode); err != nil { @@ -339,7 +351,9 @@ func (qb *ImageStore) Update(ctx context.Context, updatedObject *models.Image) e } if updatedObject.URLs.Loaded() { - if err := imagesURLsTableMgr.replaceJoins(ctx, updatedObject.ID, updatedObject.URLs.List()); err != nil { + urls := updatedObject.URLs.List() + utils.SortURLs(urls) + if err := imagesURLsTableMgr.replaceJoins(ctx, updatedObject.ID, urls); err != nil { return err } } diff --git a/pkg/sqlite/performer.go b/pkg/sqlite/performer.go index c5943b182..821bbabe3 100644 --- a/pkg/sqlite/performer.go +++ b/pkg/sqlite/performer.go @@ -269,8 +269,10 @@ func (qb *PerformerStore) Create(ctx context.Context, newObject *models.CreatePe } if newObject.URLs.Loaded() { + urls := newObject.URLs.List() + utils.SortURLs(urls) const startPos = 0 - if err := performersURLsTableMgr.insertJoins(ctx, id, startPos, newObject.URLs.List()); err != nil { + if err := performersURLsTableMgr.insertJoins(ctx, id, startPos, urls); err != nil { return err } } @@ -327,6 +329,15 @@ func (qb *PerformerStore) UpdatePartial(ctx context.Context, id int, partial mod if err := performersURLsTableMgr.modifyJoins(ctx, id, partial.URLs.Values, partial.URLs.Mode); err != nil { return nil, err } + // Re-sort URLs after modification + urls, err := performersURLsTableMgr.get(ctx, id) + if err != nil { + return nil, err + } + utils.SortURLs(urls) + if err := performersURLsTableMgr.replaceJoins(ctx, id, urls); err != nil { + return nil, err + } } if partial.TagIDs != nil { @@ -362,7 +373,9 @@ func (qb *PerformerStore) Update(ctx context.Context, updatedObject *models.Upda } if updatedObject.URLs.Loaded() { - if err := performersURLsTableMgr.replaceJoins(ctx, updatedObject.ID, updatedObject.URLs.List()); err != nil { + urls := updatedObject.URLs.List() + utils.SortURLs(urls) + if err := performersURLsTableMgr.replaceJoins(ctx, updatedObject.ID, urls); err != nil { return err } } diff --git a/pkg/sqlite/scene.go b/pkg/sqlite/scene.go index 40feb5847..f4dd9537e 100644 --- a/pkg/sqlite/scene.go +++ b/pkg/sqlite/scene.go @@ -315,8 +315,10 @@ func (qb *SceneStore) Create(ctx context.Context, newObject *models.Scene, fileI } if newObject.URLs.Loaded() { + urls := newObject.URLs.List() + utils.SortURLs(urls) const startPos = 0 - if err := scenesURLsTableMgr.insertJoins(ctx, id, startPos, newObject.URLs.List()); err != nil { + if err := scenesURLsTableMgr.insertJoins(ctx, id, startPos, urls); err != nil { return err } } @@ -379,6 +381,15 @@ func (qb *SceneStore) UpdatePartial(ctx context.Context, id int, partial models. if err := scenesURLsTableMgr.modifyJoins(ctx, id, partial.URLs.Values, partial.URLs.Mode); err != nil { return nil, err } + // Re-sort URLs after modification + urls, err := scenesURLsTableMgr.get(ctx, id) + if err != nil { + return nil, err + } + utils.SortURLs(urls) + if err := scenesURLsTableMgr.replaceJoins(ctx, id, urls); err != nil { + return nil, err + } } if partial.PerformerIDs != nil { if err := scenesPerformersTableMgr.modifyJoins(ctx, id, partial.PerformerIDs.IDs, partial.PerformerIDs.Mode); err != nil { @@ -423,7 +434,9 @@ func (qb *SceneStore) Update(ctx context.Context, updatedObject *models.Scene) e } if updatedObject.URLs.Loaded() { - if err := scenesURLsTableMgr.replaceJoins(ctx, updatedObject.ID, updatedObject.URLs.List()); err != nil { + urls := updatedObject.URLs.List() + utils.SortURLs(urls) + if err := scenesURLsTableMgr.replaceJoins(ctx, updatedObject.ID, urls); err != nil { return err } } diff --git a/pkg/sqlite/studio.go b/pkg/sqlite/studio.go index 1a05be6f3..149360d98 100644 --- a/pkg/sqlite/studio.go +++ b/pkg/sqlite/studio.go @@ -15,6 +15,7 @@ import ( "github.com/stashapp/stash/pkg/models" "github.com/stashapp/stash/pkg/studio" + "github.com/stashapp/stash/pkg/utils" ) const ( @@ -191,8 +192,10 @@ func (qb *StudioStore) Create(ctx context.Context, newObject *models.Studio) err } if newObject.URLs.Loaded() { + urls := newObject.URLs.List() + utils.SortURLs(urls) const startPos = 0 - if err := studiosURLsTableMgr.insertJoins(ctx, id, startPos, newObject.URLs.List()); err != nil { + if err := studiosURLsTableMgr.insertJoins(ctx, id, startPos, urls); err != nil { return err } } @@ -241,6 +244,15 @@ func (qb *StudioStore) UpdatePartial(ctx context.Context, input models.StudioPar if err := studiosURLsTableMgr.modifyJoins(ctx, input.ID, input.URLs.Values, input.URLs.Mode); err != nil { return nil, err } + // Re-sort URLs after modification + urls, err := studiosURLsTableMgr.get(ctx, input.ID) + if err != nil { + return nil, err + } + utils.SortURLs(urls) + if err := studiosURLsTableMgr.replaceJoins(ctx, input.ID, urls); err != nil { + return nil, err + } } if err := qb.tagRelationshipStore.modifyRelationships(ctx, input.ID, input.TagIDs); err != nil { @@ -272,7 +284,9 @@ func (qb *StudioStore) Update(ctx context.Context, updatedObject *models.Studio) } if updatedObject.URLs.Loaded() { - if err := studiosURLsTableMgr.replaceJoins(ctx, updatedObject.ID, updatedObject.URLs.List()); err != nil { + urls := updatedObject.URLs.List() + utils.SortURLs(urls) + if err := studiosURLsTableMgr.replaceJoins(ctx, updatedObject.ID, urls); err != nil { return err } } diff --git a/pkg/utils/url.go b/pkg/utils/url.go index e4d2df237..632b64e56 100644 --- a/pkg/utils/url.go +++ b/pkg/utils/url.go @@ -1,6 +1,10 @@ package utils -import "regexp" +import ( + "regexp" + "sort" + "strings" +) // URLFromHandle adds the site URL to the input if the input is not already a URL // siteURL must not end with a slash @@ -13,3 +17,21 @@ func URLFromHandle(input string, siteURL string) string { return siteURL + "/" + input } + +// urlSortKey extracts the sortable portion of a URL by removing the protocol and www. prefix +func urlSortKey(url string) string { + // Remove http:// or https:// + key := strings.TrimPrefix(url, "https://") + key = strings.TrimPrefix(key, "http://") + // Remove www. prefix + key = strings.TrimPrefix(key, "www.") + return strings.ToLower(key) +} + +// SortURLs sorts a slice of URLs alphabetically by their base URL, +// excluding the protocol (http/https) and www. prefix +func SortURLs(urls []string) { + sort.SliceStable(urls, func(i, j int) bool { + return urlSortKey(urls[i]) < urlSortKey(urls[j]) + }) +}