Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
0f45dc0
Convert group aliases to list
babechan233 May 21, 2026
845e0ba
Merge remote-tracking branch 'origin/develop' into develop
babechan233 May 28, 2026
fd198af
Address review comments
babechan233 May 28, 2026
bc90011
Minor fix
babechan233 May 28, 2026
6e208b4
Merge remote-tracking branch 'upstream/develop' into develop
babechan233 Jun 5, 2026
2782b5d
Fix merge changes
babechan233 Jun 5, 2026
5f15a7d
Small update
babechan233 Jun 15, 2026
c12fff8
Add resolver tests, update mockery
babechan233 Jun 15, 2026
f9f8ee3
Merge remote-tracking branch 'upstream/develop' into develop
babechan233 Jun 15, 2026
4daaa07
Small changes
babechan233 Jun 15, 2026
5a02f7c
Updates
babechan233 Jun 15, 2026
57e5985
Update
babechan233 Jun 15, 2026
b01695e
Fix docs
babechan233 Jun 16, 2026
71a0d95
Fix tests
babechan233 Jun 16, 2026
bcd33e3
Comment
babechan233 Jun 16, 2026
f83923e
Merge remote-tracking branch 'upstream/develop' into develop
babechan233 Jun 26, 2026
d6b7d10
Merge remote-tracking branch 'upstream/develop' into develop
babechan233 Jul 13, 2026
920b3d3
New: Add Stash ID Endpoint Dropdown (#7113)
mrx-31415 Jul 17, 2026
b04b4d0
New: Update OSHASH/MD5 UI Check (#6972)
Gykes Jul 17, 2026
dd5df5b
Fix: Stashignore Tests on Windows (#7099)
CynicalAtropos Jul 17, 2026
ac695eb
Fix A/V desync after seeking when video is stream-copied (#7104)
dpusceddu Jul 18, 2026
4895a2b
Use LEFT JOIN when filtering for NULL phash (#7121)
gregpetersonanon Jul 21, 2026
3e66369
Fix: Case Insensitive Scan File Lookup (#7098)
CynicalAtropos Jul 21, 2026
9ebc217
Fix lightbox jumping to a page's last image when crossing a page boun…
void-function865 Jul 21, 2026
2692042
[Feature] Consolidate scene cover buttons (#6924)
Stash-KennyG Jul 21, 2026
050fc71
Fix sidebar select count filter depth (#7122)
CynicalAtropos Jul 22, 2026
1642b8d
Bump golang.org/x/net from 0.50.0 to 0.55.0 (#7100)
dependabot[bot] Jul 22, 2026
bbb670d
Bump golang.org/x/image from 0.38.0 to 0.41.0 (#7101)
dependabot[bot] Jul 22, 2026
d0d7fea
Fix: Gallery Parent Folder Exclusion SQL Error (#7123)
CynicalAtropos Jul 22, 2026
88d526b
Bump golang.org/x/crypto from 0.51.0 to 0.52.0 (#7110)
dependabot[bot] Jul 23, 2026
62e7d38
New: Add Additional Plugin UI Extension Hooks (#7117)
CynicalAtropos Jul 26, 2026
c74276a
Error on biome lint recommended rules and fix biome issues.
WithoutPants Jul 31, 2026
ee852b5
[Feature] Tagger performer match improvements (#6854)
Stash-KennyG Jul 31, 2026
735c6c2
Removed unnecessary buffer when generating image phash (#7128)
81hvac1k02 Jul 31, 2026
c5cf544
Fix Save & New relationship field resets (#7125)
CynicalAtropos Jul 31, 2026
6533f7b
Fix: Lightbox Back Button Behavior (#7094)
CynicalAtropos Jul 31, 2026
ba0780f
fix: normalize macOS NFD filenames to NFC when scanning (#7072)
suzuhiroruri Jul 31, 2026
c627c32
Update CODEOWNERS
DogmaDragon Aug 5, 2026
49e1545
Fix: Mobile Scene File Info Overflow (#7141)
hyper440 Aug 9, 2026
ae64cf0
New: Sort Images by Performer Age (#6936)
eugene-shovel Aug 9, 2026
9558fcd
Fix: Make the Merge Modals Patchable + onClose Modal Fix (#7151)
Maista6969 Aug 17, 2026
eff7eac
Fix: Declare Context After Setting Local Request (#7163)
gregpetersonanon Aug 17, 2026
758f6d2
Fix: Fetch Stash-Box Studio Parents in the Scene Query (#7169)
InfiniteStash Aug 19, 2026
1c03ecc
Fix: Add Sandbox to Image Headers (#7174)
Gykes Aug 19, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
/ui/v2.5/src/docs/en/Manual/ @DogmaDragon
/docs/ @DogmaDragon
README.md @DogmaDragon
/docker/ @feederbox826
/.github/workflows/ @feederbox826
31 changes: 27 additions & 4 deletions .mockery.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,27 @@
dir: ./pkg/models
name: ".*ReaderWriter"
outpkg: mocks
output: ./pkg/models/mocks
with-expecter: false
packages:
github.com/stashapp/stash/pkg/models:
config:
dir: ./pkg/models/mocks
filename: "{{.InterfaceName}}.go"
outpkg: mocks
interfaces:
FileReaderWriter:
FolderReaderWriter:
GalleryReaderWriter:
GalleryChapterReaderWriter:
ImageReaderWriter:
GroupReaderWriter:
PerformerReaderWriter:
SceneReaderWriter:
SceneMarkerReaderWriter:
StudioReaderWriter:
TagReaderWriter:
SavedFilterReaderWriter:
github.com/stashapp/stash/internal/manager:
config:
dir: ./internal/manager/mocks
filename: "{{.InterfaceName}}.go"
outpkg: mocks
interfaces:
GroupService:

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More service interfaces exist but mocks for those can be generated as and when required.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These changes should largely not be necessary if we remove the unit test from the api package.

19 changes: 10 additions & 9 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -55,15 +55,15 @@ require (
github.com/vearutop/statigz v1.4.0
github.com/vektah/dataloaden v0.3.0
github.com/vektah/gqlparser/v2 v2.5.27
github.com/vektra/mockery/v2 v2.10.0
github.com/vektra/mockery/v2 v2.21.6

@babechan233 babechan233 Jun 15, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated to use packages field in .mockery.yml

github.com/xWTF/chardet v0.0.0-20230208095535-c780f2ac244e
github.com/zencoder/go-dash/v3 v3.0.2
golang.org/x/crypto v0.48.0
golang.org/x/image v0.38.0
golang.org/x/net v0.50.0
golang.org/x/sys v0.41.0
golang.org/x/term v0.40.0
golang.org/x/text v0.35.0
golang.org/x/crypto v0.52.0
golang.org/x/image v0.41.0
golang.org/x/net v0.55.0
golang.org/x/sys v0.45.0
golang.org/x/term v0.43.0
golang.org/x/text v0.37.0
golang.org/x/time v0.10.0
gopkg.in/guregu/null.v4 v4.0.0
gopkg.in/natefinch/lumberjack.v2 v2.2.1
Expand All @@ -76,6 +76,7 @@ require (
github.com/antchfx/xpath v1.3.6 // indirect
github.com/asticode/go-astikit v0.20.0 // indirect
github.com/asticode/go-astits v1.8.0 // indirect
github.com/chigopher/pathlib v0.12.0 // indirect
github.com/chromedp/sysutil v1.1.0 // indirect
github.com/coder/websocket v1.8.12 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect
Expand Down Expand Up @@ -133,9 +134,9 @@ require (
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect
go.uber.org/atomic v1.11.0 // indirect
go.yaml.in/yaml/v3 v3.0.3 // indirect
golang.org/x/mod v0.33.0 // indirect
golang.org/x/mod v0.35.0 // indirect
golang.org/x/sync v0.20.0 // indirect
golang.org/x/tools v0.42.0 // indirect
golang.org/x/tools v0.44.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
373 changes: 21 additions & 352 deletions go.sum

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion graphql/schema/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,7 @@ type Mutation {
sceneResetPlayCount(id: ID!): Int!

"Generates screenshot at specified time in seconds. Leave empty to generate default screenshot"
sceneGenerateScreenshot(id: ID!, at: Float): String!
sceneGenerateScreenshot(id: ID!, at: Float): ID!

sceneMarkerCreate(input: SceneMarkerCreateInput!): SceneMarker
sceneMarkerUpdate(input: SceneMarkerUpdateInput!): SceneMarker
Expand Down
8 changes: 5 additions & 3 deletions graphql/schema/types/group.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ type GroupDescription {
type Group {
id: ID!
name: String!
aliases: String
aliases: [String!]

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change breaks compatibility with existing external systems. You will need to deprecate the existing aliases field and add a new alias_list field instead. The existing field will need a resolver to build the string value.

We did the same with performer aliases before we removed the deprecated field. See https://github.com/WithoutPants/stash/blob/f65e87773c4b9fb053f4c5f23bcb77c03adbbccd/internal/api/resolver_model_performer.go#L21

Will need to apply this to the other alias field changes.

"Duration in seconds"
duration: Int
date: String
Expand Down Expand Up @@ -41,7 +41,8 @@ input GroupDescriptionInput {

input GroupCreateInput {
name: String!
aliases: String
"Duplicate aliases and those equal to name will be ignored (case-insensitive)"
aliases: [String!]
"Duration in seconds"
duration: Int
date: String
Expand All @@ -67,7 +68,8 @@ input GroupCreateInput {
input GroupUpdateInput {
id: ID!
name: String
aliases: String
"Duplicate aliases and those equal to name will be ignored (case-insensitive)"
aliases: [String!]
duration: Int
date: String
# rating expressed as 1-100
Expand Down
4 changes: 2 additions & 2 deletions graphql/schema/types/scraped-group.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ input ScrapedMovieInput {
type ScrapedGroup {
stored_id: ID
name: String
aliases: String
aliases: [String!]
duration: String
date: String
rating: String
Expand All @@ -54,7 +54,7 @@ type ScrapedGroup {

input ScrapedGroupInput {
name: String
aliases: String
aliases: [String!]
duration: String
date: String
rating: String
Expand Down
1 change: 1 addition & 0 deletions graphql/schema/types/scraper.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,7 @@ type StashBoxFingerprint {
algorithm: String!
hash: String!
duration: Int!
submissions: Int!
}

"""
Expand Down
8 changes: 8 additions & 0 deletions graphql/stash-box/query.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,13 @@ fragment StudioFragment on Studio {
parent {
name
id
aliases
urls {
...URLFragment
}
images {
...ImageFragment
}
}
images {
...ImageFragment
Expand Down Expand Up @@ -97,6 +104,7 @@ fragment FingerprintFragment on Fingerprint {
algorithm
hash
duration
submissions
}

fragment SceneFragment on Scene {
Expand Down
2 changes: 1 addition & 1 deletion internal/api/authentication.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,9 @@ func authenticateHandler() func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
c := config.GetInstance()
ctx := r.Context()

r = session.SetLocalRequest(r)
ctx := r.Context()

// Check for signed media requests
if username, ok := authenticateSignedRequest(r); ok {
Expand Down
25 changes: 25 additions & 0 deletions internal/api/resolver_model_movie.go
Original file line number Diff line number Diff line change
Expand Up @@ -228,3 +228,28 @@ func (r *groupResolver) CustomFields(ctx context.Context, obj *models.Group) (ma

return m, nil
}

func (r *groupResolver) Aliases(ctx context.Context, obj *models.Group) ([]string, error) {
if !obj.Aliases.Loaded() {
if err := r.withReadTxn(ctx, func(ctx context.Context) error {
return obj.LoadAliases(ctx, r.repository.Group)
}); err != nil {
return nil, err
}
}

return obj.Aliases.List(), nil
}

// movieResolver.Aliases overrides groupResolver.Aliases to return a single string
// for backward compatibility with the deprecated Movie type.
func (r *movieResolver) Aliases(ctx context.Context, obj *models.Group) (*string, error) {

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only basic changes done to movie related codebase to ensure existing stuff doesn't break & code compiles correctly.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be repurposed to be on groupResolver when you update the graphql fields.

aliases, err := r.groupResolver.Aliases(ctx, obj)
if err != nil {
return nil, err
}
if len(aliases) == 0 {
return nil, nil
}
return &aliases[0], nil
}
32 changes: 30 additions & 2 deletions internal/api/resolver_mutation_group.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ func groupFromGroupCreateInput(ctx context.Context, input GroupCreateInput) (*mo
newGroup := newGroupInput.Group

newGroup.Name = strings.TrimSpace(input.Name)
newGroup.Aliases = translator.string(input.Aliases)
newGroup.Aliases = models.NewRelatedStrings(stringslice.UniqueExcludeFold(stringslice.TrimSpace(input.Aliases), newGroup.Name))
newGroup.Duration = input.Duration
newGroup.Rating = input.Rating100
newGroup.Director = translator.string(input.Director)
Expand Down Expand Up @@ -118,7 +118,9 @@ func groupPartialFromGroupUpdateInput(translator changesetTranslator, input Grou
updatedGroup := models.NewGroupPartial()

updatedGroup.Name = translator.optionalString(input.Name, "name")
updatedGroup.Aliases = translator.optionalString(input.Aliases, "aliases")

aliases := stringslice.TrimSpace(input.Aliases)
updatedGroup.Aliases = translator.updateStrings(aliases, "aliases")
updatedGroup.Duration = translator.optionalInt(input.Duration, "duration")
updatedGroup.Rating = translator.optionalInt(input.Rating100, "rating100")
updatedGroup.Director = translator.optionalString(input.Director, "director")
Expand Down Expand Up @@ -198,6 +200,32 @@ func (r *mutationResolver) GroupUpdate(ctx context.Context, input GroupUpdateInp
}

if err := r.withTxn(ctx, func(ctx context.Context) error {
qb := r.repository.Group

// make sure to deduplicate and exclude name from aliases
// in update flow
if updatedGroup.Aliases != nil {
g, err := qb.Find(ctx, groupID)
if err != nil {
return err
}
if g != nil {
if err := g.LoadAliases(ctx, qb); err != nil {
return err
}

effectiveAliases := updatedGroup.Aliases.Apply(g.Aliases.List())
name := g.Name
if updatedGroup.Name.Set {
name = updatedGroup.Name.Value
}

sanitized := stringslice.UniqueExcludeFold(effectiveAliases, name)
updatedGroup.Aliases.Values = sanitized
updatedGroup.Aliases.Mode = models.RelationshipUpdateModeSet
}
}

frontImage := group.ImageInput{
Image: frontimageData,
Set: frontImageIncluded,
Expand Down
Loading