diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 2013c84d8c..27f949997f 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,5 +1,3 @@ /ui/v2.5/src/docs/en/Manual/ @DogmaDragon /docs/ @DogmaDragon README.md @DogmaDragon -/docker/ @feederbox826 -/.github/workflows/ @feederbox826 diff --git a/.mockery.yml b/.mockery.yml index 0dcb289e3d..ecd83eac78 100644 --- a/.mockery.yml +++ b/.mockery.yml @@ -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: diff --git a/go.mod b/go.mod index 73fbeefff9..803372383b 100644 --- a/go.mod +++ b/go.mod @@ -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 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 @@ -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 @@ -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 ) diff --git a/go.sum b/go.sum index cb54a5da9e..6882f48ff9 100644 --- a/go.sum +++ b/go.sum @@ -17,18 +17,6 @@ cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHOb cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= -cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= -cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= -cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= -cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= -cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= -cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= -cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= -cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= -cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= -cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= -cloud.google.com/go v0.98.0/go.mod h1:ua6Ush4NALrHk5QXDWnjvZHN93OuF0HfuEPq9I1X0cM= -cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= @@ -37,7 +25,6 @@ cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4g cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/firestore v1.6.1/go.mod h1:asNXNOzBdyVQmEU+ggO8UPodTkEVFW5Qx+rwHnAz+EY= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= @@ -57,8 +44,6 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60= github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= -github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= -github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/PuerkitoBio/goquery v1.10.3 h1:pFYcNSqHxBD06Fpj/KsbStFRsgRATgnf3LeXiUkhzPo= github.com/PuerkitoBio/goquery v1.10.3/go.mod h1:tMUX0zDMHXYlAQk6p35XxQMqMweEKB7iK7iLNd4RH4Y= github.com/RoaringBitmap/roaring v0.4.7/go.mod h1:8khRDP4HmeXns4xIj9oGrKSz7XTQiJx2zgh7AcNke4w= @@ -68,10 +53,6 @@ github.com/Yamashou/gqlgenc v0.32.1 h1:EHs9//xQxXlyltkSFXM+fhO2rTXcWNw6FPKRJ6t+i github.com/Yamashou/gqlgenc v0.32.1/go.mod h1:o5SxKt9d3+oUZ2i0V3CW8lHFyunfLR+KcKHubS4zf5E= github.com/agnivade/levenshtein v1.2.1 h1:EHBY3UOn1gwdy/VbFwgo4cxecRznFk7fKWN1KOX7eoM= github.com/agnivade/levenshtein v1.2.1/go.mod h1:QVVI16kDrtSuwcpd0p1+xMC6Z/VfhtCyDIjcwga4/DU= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/anacrolix/dms v1.2.2 h1:0mk2/DXNqa5KDDbaLgFPf3oMV6VCGdFNh3d/gt4oafM= github.com/anacrolix/dms v1.2.2/go.mod h1:msPKAoppoNRfrYplJqx63FZ+VipDZ4Xsj3KzIQxyU7k= github.com/anacrolix/envpprof v0.0.0-20180404065416-323002cec2fa/go.mod h1:KgHhUaQMc8cC0+cEflSgCFNFbKwi5h54gqtVn8yhP7c= @@ -90,33 +71,21 @@ github.com/antchfx/htmlquery v1.3.5/go.mod h1:5oyIPIa3ovYGtLqMPNjBF2Uf25NPCKsMjC github.com/antchfx/xpath v1.3.5/go.mod h1:i54GszH55fYfBmoZXapTHN8T8tkcHfRgLyVwwqzXNcs= github.com/antchfx/xpath v1.3.6 h1:s0y+ElRRtTQdfHP609qFu0+c6bglDv20pqOViQjjdPI= github.com/antchfx/xpath v1.3.6/go.mod h1:i54GszH55fYfBmoZXapTHN8T8tkcHfRgLyVwwqzXNcs= -github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE= -github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= -github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= -github.com/armon/go-metrics v0.3.10/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= -github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/asticode/go-astikit v0.20.0 h1:+7N+J4E4lWx2QOkRdOf6DafWJMv6O4RRfgClwQokrH8= github.com/asticode/go-astikit v0.20.0/go.mod h1:h4ly7idim1tNhaVkdVBeXQZEE3L0xblP7fCWbgwipF0= github.com/asticode/go-astisub v0.25.1 h1:RZMGfZPp7CXOkI6g+zCU7DRLuciGPGup921uKZnMXPI= github.com/asticode/go-astisub v0.25.1/go.mod h1:WTkuSzFB+Bp7wezuSf2Oxulj5A8zu2zLRVFf6bIFQK8= github.com/asticode/go-astits v1.8.0 h1:rf6aiiGn/QhlFjNON1n5plqF3Fs025XLUwiQ0NB6oZg= github.com/asticode/go-astits v1.8.0/go.mod h1:DkOWmBNQpnr9mv24KfZjq4JawCFX1FCqjLVGvO0DygQ= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= -github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bool64/dev v0.2.28 h1:6ayDfrB/jnNr2iQAZHI+uT3Qi6rErSbJYQs1y8rSrwM= github.com/bool64/dev v0.2.28/go.mod h1:iJbh1y/HkunEPhgebWRNcs8wfGq7sjvJ6W5iabL8ACg= github.com/bradfitz/iter v0.0.0-20140124041915-454541ec3da2/go.mod h1:PyRFw1Lt2wKX4ZVSQ2mk+PeDa1rxyObEDlApuIsUKuo= github.com/bradfitz/iter v0.0.0-20190303215204-33e6a9893b0c/go.mod h1:PyRFw1Lt2wKX4ZVSQ2mk+PeDa1rxyObEDlApuIsUKuo= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chigopher/pathlib v0.12.0 h1:1GM7fN/IwXXmOHbd1jkMqHD2wUhYqUvafgxTwmLT/q8= +github.com/chigopher/pathlib v0.12.0/go.mod h1:EJ5UtJ/sK8Nt6q3VWN+EwZLZ3g0afJiG8NegYiQQ/gQ= github.com/chromedp/cdproto v0.0.0-20250803210736-d308e07a266d h1:ZtA1sedVbEW7EW80Iz2GR3Ye6PwbJAJXjv7D74xG6HU= github.com/chromedp/cdproto v0.0.0-20250803210736-d308e07a266d/go.mod h1:NItd7aLkcfOA/dcMXvl8p1u+lQqioRMq/SqDp71Pb/k= github.com/chromedp/chromedp v0.14.2 h1:r3b/WtwM50RsBZHMUm9fsNhhzRStTHrKdr2zmwbZSzM= @@ -129,27 +98,15 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P github.com/chzyer/readline v1.5.0/go.mod h1:x22KAscuvRqlLoK9CsoYsmxoXZMMFVyOl86cAH8qUic= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/chzyer/test v0.0.0-20210722231415-061457976a23/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= -github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= -github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211130200136-a8f946100490/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/coder/websocket v1.8.12 h1:5bUXkEPPIbewrnkU8LTCLVaxi4N4J8ahufH2vlo4NAo= github.com/coder/websocket v1.8.12/go.mod h1:LNVeNrXQZfe5qhS9ALED3uA+l5pPqvwXg3CKoDBB2gs= -github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/corona10/goimagehash v1.1.0 h1:teNMX/1e+Wn/AYSbLHX8mj+mF9r60R1kBeqE9MkoYwI= github.com/corona10/goimagehash v1.1.0/go.mod h1:VkvE0mLn84L4aF8vCb6mafVajEb6QYMHl2ZJLn0mOGI= -github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo= github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= @@ -191,23 +148,13 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= -github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= -github.com/envoyproxy/go-control-plane v0.10.1/go.mod h1:AY7fTTXNdv/aJ2O5jwpxAPOWUZ7hQAEvzN5Pf27BkQQ= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/envoyproxy/protoc-gen-validate v0.6.2/go.mod h1:2t7qjJNvHPx8IjnBOzl9E9/baC+qXE/TeeyBRzgJDws= -github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= -github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/feederbox826/gosx-notifier v0.2.2 h1:26NkaJZ8Wzptx82R46c9pkVAcFwGSU7kxWrOKmRWlC0= github.com/feederbox826/gosx-notifier v0.2.2/go.mod h1:R6rqw7VuwuiCuvsr7EOONmWq++CRA5Ijmkmx75/C3Fs= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= -github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE= github.com/glycerine/goconvey v0.0.0-20180728074245-46e3a41ad493/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24= github.com/go-chi/chi/v5 v5.0.7/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= @@ -222,16 +169,11 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-json-experiment/json v0.0.0-20250725192818-e39067aee2d2 h1:iizUGZ9pEquQS5jTGkh4AqeeHCMbfbjeb0zMt0aEFzs= github.com/go-json-experiment/json v0.0.0-20250725192818-e39067aee2d2/go.mod h1:TiCD2a1pcmjd7YnhGH0f/zKNcCD06B029pHhzV23c2M= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-sourcemap/sourcemap v2.1.3+incompatible h1:W1iEw64niKVGogNgBN3ePyLFfuisuzeidWPMPWmECqU= github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-toast/toast v0.0.0-20190211030409-01e6764cf0a4 h1:qZNfIGkIANxGv/OqtnntR4DfOY2+BgwR60cAcu/i3SE= github.com/go-toast/toast v0.0.0-20190211030409-01e6764cf0a4/go.mod h1:kW3HQ4UdaAyrUCSSDR4xUzBKW6O2iA4uHhk7AtyYp10= github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= @@ -245,8 +187,6 @@ github.com/gobwas/ws v1.4.0/go.mod h1:G3gNqMNtPppf5XUz7O4shetPpcZ1VJ7zt18dlUeakr github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw= github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI= github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang-migrate/migrate/v4 v4.16.2 h1:8coYbMKUyInrFk1lfGfRovTLAW7PhWp8qQDT2iKfuoA= @@ -265,8 +205,6 @@ github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= -github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -281,11 +219,7 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180124185431-e89373fe6b4a/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= @@ -297,10 +231,7 @@ github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= @@ -308,7 +239,6 @@ github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/ github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= @@ -319,11 +249,6 @@ github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20230207041349-798e818bf904 h1:4/hN5RUoecvl+RmJRE2YxKWtnnQls6rQjjW5oV7qg2U= github.com/google/pprof v0.0.0-20230207041349-798e818bf904/go.mod h1:uglQLonpP8qtYCYyzA+8c/9qtqgA3qsXGYqCPKARAFg= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= @@ -332,8 +257,6 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= -github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ= @@ -342,71 +265,36 @@ github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7Fsg github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/hashicorp/consul/api v1.11.0/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M= -github.com/hashicorp/consul/api v1.12.0/go.mod h1:6pVBMo0ebnYdt2S3H87XhekM/HHrUoTD2XXb/VrZVy0= -github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= -github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= -github.com/hashicorp/go-hclog v1.0.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= -github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= -github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= -github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= -github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= -github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= -github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= -github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= -github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= -github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY= -github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc= -github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= -github.com/hashicorp/memberlist v0.3.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= -github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk= -github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4= github.com/hasura/go-graphql-client v0.13.1 h1:kKbjhxhpwz58usVl+Xvgah/TDha5K2akNTRQdsEHN6U= github.com/hasura/go-graphql-client v0.13.1/go.mod h1:k7FF7h53C+hSNFRG3++DdVZWIuHdCaTbI7siTJ//zGQ= github.com/huandu/xstrings v1.0.0/go.mod h1:4qWG/gcEcfX4z/mBDHJ++3ReCw9ibxbsNJbcucJdbSo= -github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w= -github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8= github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg= github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o= github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/kermieisinthehouse/systray v1.2.4 h1:pdH5vnl+KKjRrVCRU4g/2W1/0HVzuuJ6WXHlPPHYY6s= github.com/kermieisinthehouse/systray v1.2.4/go.mod h1:axh6C/jNuSyC0QGtidZJURc9h+h41HNoMySoLVrhVR4= -github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.18.5 h1:/h1gH5Ce+VWNLSWqPzOVn6XBO+vJbCNGvjoaGBFW2IE= github.com/klauspost/compress v1.18.5/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ= @@ -422,11 +310,8 @@ github.com/knadh/koanf/providers/posflag v1.0.1 h1:EnMxHSrPkYCFnKgBUl5KBgrjed8gV github.com/knadh/koanf/providers/posflag v1.0.1/go.mod h1:3Wn3+YG3f4ljzRyCUgIwH7G0sZ1pMjCOsNBovrbKmAk= github.com/knadh/koanf/v2 v2.2.1 h1:jaleChtw85y3UdBnI0wCqcg1sj1gPoz6D3caGNHtrNE= github.com/knadh/koanf/v2 v2.2.1/go.mod h1:PSFru3ufQgTsI7IF+95rf9s8XA1+aHxKuO/W+dPoHEY= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= @@ -442,23 +327,12 @@ github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= -github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w= -github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= -github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= -github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= -github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= @@ -467,19 +341,10 @@ github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D github.com/mattn/go-sqlite3 v1.14.7/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= -github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= -github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= -github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= @@ -487,12 +352,9 @@ github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/natefinch/pie v0.0.0-20170715172608-9a0d72014007 h1:Ohgj9L0EYOgXxkDp+bczlMBiulwmqYzQpvQNUdtt3oc= github.com/natefinch/pie v0.0.0-20170715172608-9a0d72014007/go.mod h1:wKCOWMb6iNlvKiOToY2cNuaovSXvIiv1zDi9QDR7aGQ= github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ= @@ -501,15 +363,11 @@ github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d h1:VhgPp6v9qf9Agr/ github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d/go.mod h1:YUTz3bUH2ZwIWBy3CJBeOBEugqcmXREj14T+iG/4k4U= github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde h1:x0TT0RDC7UhAVbbWWBzr41ElhJx5tXPWkIHA2HWPRuw= github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0= -github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU= github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -518,20 +376,7 @@ github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZ github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= -github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8= github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII= github.com/quic-go/quic-go v0.59.1 h1:0Gmua0HW1Tv7ANR7hUYwRyD0MG5OJfgvYSZasGZzBic= @@ -540,15 +385,12 @@ github.com/refraction-networking/utls v1.8.3-0.20260301010127-aa6edf4b11af h1:er github.com/refraction-networking/utls v1.8.3-0.20260301010127-aa6edf4b11af/go.mod h1:jkSOEkLqn+S/jtpEHPOsVv/4V4EVnelwbMQl4vCWXAM= github.com/remeh/sizedwaitgroup v1.0.0 h1:VNGGFwNo/R5+MJBf6yrsr110p0m4/OX4S3DCy7Kyl5E= github.com/remeh/sizedwaitgroup v1.0.0/go.mod h1:3j2R4OIe/SeS6YDhICBy22RWjJC5eNCJ1V+9+NVNYlo= -github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= -github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= -github.com/rs/zerolog v1.26.1/go.mod h1:/wSSJWX7lVrsOwlbyTRSOJvqRlc+WjWlfes+CiJ+tmc= github.com/rs/zerolog v1.29.1/go.mod h1:Le6ESbR7hc+DP6Lt1THiV8CQSdkkNrd3R0XbEgp3ZBU= github.com/rs/zerolog v1.30.0 h1:SymVODrcRsaRaSInD9yQtKbtWqwsfoPcRff/oRXLj4c= github.com/rs/zerolog v1.30.0/go.mod h1:/tk+P47gFdPXq4QYjvCmT5/Gsug2nagsFWBWhAiSi1w= @@ -556,33 +398,22 @@ github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd h1:CmH9+J6ZSsIjUK3dcGsnCnO41eRBOnY12zwkn5qVwgc= github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk= -github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46/go.mod h1:uAQ5PCi+MFsC7HjREoAz1BU+Mq60+05gifQSsHSDG/8= github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06 h1:OkMGxebDjyw0ULyrTYWeN0UNCCkmCWfjPnIA2W6oviI= github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06/go.mod h1:+ePHsJ1keEjQtpvf9HHw0f4ZeJ0TLRsxhunSI2hYJSs= -github.com/sagikazarmark/crypt v0.3.0/go.mod h1:uD/D+6UF4SrIR1uGEv7bBNkNqLGqUr43MRiaGWX1Nig= -github.com/sagikazarmark/crypt v0.4.0/go.mod h1:ALv2SRj7GxYV4HO9elxH9nS6M9gW+xDNxqmyJ6RfDFM= -github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s= github.com/sosodev/duration v1.3.1 h1:qtHBDMQ6lvMQsL15g4aopM4HEfOaYuhWBw3NPTtlqq4= github.com/sosodev/duration v1.3.1/go.mod h1:RQIBBX0+fMLc/D9+Jb/fwvVmo0eZvDDEERAikUR6SDg= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= -github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= -github.com/spf13/afero v1.8.0/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo= +github.com/spf13/afero v1.4.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM= github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= -github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= -github.com/spf13/cobra v1.3.0/go.mod h1:BrRVncBjOJa/eUcVVm9CE+oC6as8k+VYr4NY7WCi9V4= github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= @@ -590,8 +421,6 @@ github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0 github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.10.0/go.mod h1:SoyBPwAtKDzypXNDFKN5kzH7ppppbGZtls1UpIy5AsM= -github.com/spf13/viper v1.10.1/go.mod h1:IGlFPqhNAPKRxohIzWpI5QEy4kuI7tcl5WvR+8qy1rU= github.com/spf13/viper v1.16.0 h1:rGGH0XDZhdUOryiDWjmIvUSWpbNqisK8Wk0Vyefw8hc= github.com/spf13/viper v1.16.0/go.mod h1:yg78JgCJcbrQOvV9YLXgkLaZqUidkY9K+Dd1FofRzQg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -612,7 +441,6 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/tidwall/gjson v1.16.0 h1:SyXa+dsSPpUlcwEDuKuEBJEz5vzTvOea+9rjyYodQFg= @@ -623,7 +451,6 @@ github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhso github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= -github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= github.com/urfave/cli/v2 v2.27.6 h1:VdRdS98FNhKZ8/Az8B7MTyGQmpIr36O1EHybx/LaZ4g= github.com/urfave/cli/v2 v2.27.6/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ= github.com/vearutop/statigz v1.4.0 h1:RQL0KG3j/uyA/PFpHeZ/L6l2ta920/MxlOAIGEOuwmU= @@ -632,8 +459,8 @@ github.com/vektah/dataloaden v0.3.0 h1:ZfVN2QD6swgvp+tDqdH/OIT/wu3Dhu0cus0k5gIZS github.com/vektah/dataloaden v0.3.0/go.mod h1:/HUdMve7rvxZma+2ZELQeNh88+003LL7Pf/CZ089j8U= github.com/vektah/gqlparser/v2 v2.5.27 h1:RHPD3JOplpk5mP5JGX8RKZkt2/Vwj/PZv0HxTdwFp0s= github.com/vektah/gqlparser/v2 v2.5.27/go.mod h1:D1/VCZtV3LPnQrcPBeR/q5jkSQIPti0uYCP/RI0gIeo= -github.com/vektra/mockery/v2 v2.10.0 h1:MiiQWxwdq7/ET6dCXLaJzSGEN17k758H7JHS9kOdiks= -github.com/vektra/mockery/v2 v2.10.0/go.mod h1:m/WO2UzWzqgVX3nvqpRQq70I4Z7jbSCRhdmkgtp+Ab4= +github.com/vektra/mockery/v2 v2.21.6 h1:TgrH1vMGWVFtxX7fNxVFTgxNSg71VWekt9C0ds+QJRw= +github.com/vektra/mockery/v2 v2.21.6/go.mod h1:le9nnD50TkeV8QGwrkza7v/xd3DA7YbSZYBEyiuAlsI= github.com/willf/bitset v1.1.9/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= github.com/wzshiming/socks5 v0.7.0 h1:euJ+U48WrvVngi+opC8vAnpZ5sK12y1C2hPvb1f48Rg= github.com/wzshiming/socks5 v0.7.0/go.mod h1:BvCAqlzocQN5xwLjBZDBbvWlrx8sCYSSbHEOf2wZgT0= @@ -647,55 +474,37 @@ github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/zencoder/go-dash/v3 v3.0.2 h1:oP1+dOh+Gp57PkvdCyMfbHtrHaxfl3w4kR3KBBbuqQE= github.com/zencoder/go-dash/v3 v3.0.2/go.mod h1:30R5bKy1aUYY45yesjtZ9l8trNc2TwNqbS17WVQmCzk= -go.etcd.io/etcd/api/v3 v3.5.1/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= -go.etcd.io/etcd/client/pkg/v3 v3.5.1/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= -go.etcd.io/etcd/client/v2 v2.305.1/go.mod h1:pMEacxZW7o8pg4CrFE7pquyCJJzZvkvdD2RibOCCCGs= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= -go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= -go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko= go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o= -go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= -go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= go.yaml.in/yaml/v3 v3.0.3 h1:bXOww4E/J3f66rav3pX3m8w6jDE4knZjGOw8b5Y6iNE= go.yaml.in/yaml/v3 v3.0.3/go.mod h1:tBHosrYAkRZjRAOREWbDnBXUf08JOwYq++0QNwQiWzI= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20211215165025-cf75a172585e/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= -golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= -golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts= -golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos= +golang.org/x/crypto v0.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988= +golang.org/x/crypto v0.52.0/go.mod h1:1QgfPxDqh0T2M/elOJtp9RvuR95kVjir0e6/BvEmGbc= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -709,8 +518,8 @@ golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMk golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.38.0 h1:5l+q+Y9JDC7mBOMjo4/aPhMDcxEptsX+Tt3GgRQRPuE= -golang.org/x/image v0.38.0/go.mod h1:/3f6vaXC+6CEanU4KJxbcUZyEePbyKbaLoDOe4ehFYY= +golang.org/x/image v0.41.0 h1:8wS72eGJMJaBxK6okTzd4WaXumUlTVlb753MlsSvTCo= +golang.org/x/image v0.41.0/go.mod h1:uIc348UZMSvS5Z65CVZ7iDPaNobNFEPeJ4kbqTOszmA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -722,7 +531,6 @@ golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRu golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= @@ -733,20 +541,15 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= -golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8= -golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w= +golang.org/x/mod v0.35.0 h1:Ww1D637e6Pg+Zb2KrWfHQUnH2dQRLBQyAtpr/haaJeM= +golang.org/x/mod v0.35.0/go.mod h1:+GwiRhIInF8wPm+4AoT6L0FA1QWAad3OMdTRx4tFYlU= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -755,11 +558,9 @@ golang.org/x/net v0.0.0-20190415214537-1da14a5a36f2/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -777,18 +578,9 @@ golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= -golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= @@ -797,8 +589,8 @@ golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= -golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60= -golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM= +golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8= +golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -808,14 +600,6 @@ golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -826,7 +610,6 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= @@ -835,33 +618,21 @@ golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= -golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190415145633-3fd5a3612ccd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -879,35 +650,13 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -921,8 +670,8 @@ golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k= -golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY= +golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -932,15 +681,14 @@ golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= -golang.org/x/term v0.40.0 h1:36e4zGLqU4yhjlmxEaagx2KuYbJq3EwY8K943ZsHcvg= -golang.org/x/term v0.40.0/go.mod h1:w2P8uVp06p2iyKKuvXIm7N/y0UCRt3UfJTfZ7oOpglM= +golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4= +golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= @@ -950,8 +698,8 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= -golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8= -golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA= +golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc= +golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -971,7 +719,6 @@ golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgw golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -996,7 +743,6 @@ golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= @@ -1005,22 +751,14 @@ golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= -golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= -golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k= -golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0= +golang.org/x/tools v0.44.0 h1:UP4ajHPIcuMjT1GqzDWRlalUEoY+uzoZKnhOjbIPD2c= +golang.org/x/tools v0.44.0/go.mod h1:KA0AfVErSdxRZIsOVipbv3rQhVXTnlU6UhKxHd1seDI= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1044,20 +782,6 @@ google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz513 google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= -google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= -google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= -google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= -google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= -google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= -google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= -google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= -google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= -google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= -google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= -google.golang.org/api v0.59.0/go.mod h1:sT2boj7M9YJxZzgeZqXogmhfmRWDtPzT31xkieUbuZU= -google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= -google.golang.org/api v0.62.0/go.mod h1:dKmwPCydfsad4qCH08MSdgWjfHOyfpd4VtDGgRFdavw= -google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -1088,7 +812,6 @@ google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfG google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= @@ -1101,35 +824,7 @@ google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= -google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= -google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= -google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= -google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= -google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= -google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= -google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= -google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211008145708-270636b82663/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211028162531-8db9c33dc351/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211129164237-f09f9a12af12/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211203200212-54befc351ae9/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -1143,22 +838,9 @@ google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3Iji google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= -google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= -google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -1169,34 +851,21 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/guregu/null.v4 v4.0.0 h1:1Wm3S1WEA2I26Kq+6vcW+w0gcDo44YKYD7YIEJNHDjg= gopkg.in/guregu/null.v4 v4.0.0/go.mod h1:YoQhUrADuG3i9WqesrCmpNRwm1ypAgSHYqoOcTu/JrI= -gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.66.3/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/graphql/schema/schema.graphql b/graphql/schema/schema.graphql index 6054faea8a..15ca0ee3af 100644 --- a/graphql/schema/schema.graphql +++ b/graphql/schema/schema.graphql @@ -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 diff --git a/graphql/schema/types/group.graphql b/graphql/schema/types/group.graphql index 8610f39dce..71a1afe470 100644 --- a/graphql/schema/types/group.graphql +++ b/graphql/schema/types/group.graphql @@ -7,7 +7,7 @@ type GroupDescription { type Group { id: ID! name: String! - aliases: String + aliases: [String!] "Duration in seconds" duration: Int date: String @@ -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 @@ -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 diff --git a/graphql/schema/types/scraped-group.graphql b/graphql/schema/types/scraped-group.graphql index e490f32bbb..13e903e954 100644 --- a/graphql/schema/types/scraped-group.graphql +++ b/graphql/schema/types/scraped-group.graphql @@ -36,7 +36,7 @@ input ScrapedMovieInput { type ScrapedGroup { stored_id: ID name: String - aliases: String + aliases: [String!] duration: String date: String rating: String @@ -54,7 +54,7 @@ type ScrapedGroup { input ScrapedGroupInput { name: String - aliases: String + aliases: [String!] duration: String date: String rating: String diff --git a/graphql/schema/types/scraper.graphql b/graphql/schema/types/scraper.graphql index 009c5114c7..2ed9873098 100644 --- a/graphql/schema/types/scraper.graphql +++ b/graphql/schema/types/scraper.graphql @@ -289,6 +289,7 @@ type StashBoxFingerprint { algorithm: String! hash: String! duration: Int! + submissions: Int! } """ diff --git a/graphql/stash-box/query.graphql b/graphql/stash-box/query.graphql index ebaf05648f..039be28afe 100644 --- a/graphql/stash-box/query.graphql +++ b/graphql/stash-box/query.graphql @@ -20,6 +20,13 @@ fragment StudioFragment on Studio { parent { name id + aliases + urls { + ...URLFragment + } + images { + ...ImageFragment + } } images { ...ImageFragment @@ -97,6 +104,7 @@ fragment FingerprintFragment on Fingerprint { algorithm hash duration + submissions } fragment SceneFragment on Scene { diff --git a/internal/api/authentication.go b/internal/api/authentication.go index 770c4ea32a..5d81c884e9 100644 --- a/internal/api/authentication.go +++ b/internal/api/authentication.go @@ -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 { diff --git a/internal/api/resolver_model_movie.go b/internal/api/resolver_model_movie.go index 287d5d51a4..f1ee966d26 100644 --- a/internal/api/resolver_model_movie.go +++ b/internal/api/resolver_model_movie.go @@ -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) { + aliases, err := r.groupResolver.Aliases(ctx, obj) + if err != nil { + return nil, err + } + if len(aliases) == 0 { + return nil, nil + } + return &aliases[0], nil +} diff --git a/internal/api/resolver_mutation_group.go b/internal/api/resolver_mutation_group.go index 6c986c4da7..5813a275e8 100644 --- a/internal/api/resolver_mutation_group.go +++ b/internal/api/resolver_mutation_group.go @@ -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) @@ -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") @@ -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, diff --git a/internal/api/resolver_mutation_group_test.go b/internal/api/resolver_mutation_group_test.go new file mode 100644 index 0000000000..c32a5c8565 --- /dev/null +++ b/internal/api/resolver_mutation_group_test.go @@ -0,0 +1,196 @@ +package api + +import ( + "context" + "testing" + + "github.com/stashapp/stash/internal/api/testutil" + managermocks "github.com/stashapp/stash/internal/manager/mocks" + "github.com/stashapp/stash/pkg/models" + "github.com/stashapp/stash/pkg/models/mocks" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" +) + +func newGroupResolver(t *testing.T) (*mocks.Database, *managermocks.GroupService, *Resolver) { + t.Helper() + db := mocks.NewDatabase() + svc := managermocks.NewGroupService(t) + t.Cleanup(func() { db.AssertExpectations(t) }) + r := &Resolver{ + repository: db.Repository(), + hookExecutor: &mockHookExecutor{}, + groupService: svc, + } + return db, svc, r +} + +const ( + groupID = 1 + groupIDStr = "1" + groupName = "MyGroup" + + alias1 = "alias1" + alias1Padded = " " + alias1 + " " + aliasOld1 = "aliasOld1" + aliasOld2 = "aliasOld2" +) + +var ( + // var because need to grab pointer in tests + groupNewName = "NewName" + + // copied per-subtest so Aliases.Loaded() state isn't shared across test cases + groupData = models.Group{ID: groupID, Name: groupName} +) + +func TestGroupCreate_AliasNormalization(t *testing.T) { + tests := []struct { + name string + aliases []string + expected []string + }{ + { + name: "dedup and exclude name", + aliases: []string{alias1, groupName, alias1, "MYGROUP"}, + expected: []string{alias1}, + }, + { + name: "single valid alias", + aliases: []string{alias1}, + expected: []string{alias1}, + }, + { + name: "single alias same as group name", + aliases: []string{groupName}, + expected: []string{}, + }, + { + name: "alias with surrounding spaces trimmed", + aliases: []string{alias1Padded}, + expected: []string{alias1}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ctx := testutil.WithGQLContext(context.Background(), nil) + db, svc, r := newGroupResolver(t) + groupDataCopy := groupData + + // setup mocks + var gotInput *models.CreateGroupInput + svc.On("Create", mock.Anything, mock.Anything). + Run(func(args mock.Arguments) { + gotInput = args.Get(1).(*models.CreateGroupInput) + gotInput.Group.ID = groupID + }). + Return(nil).Once() + + db.Group.On("Find", mock.Anything, groupID).Return(&groupDataCopy, nil).Once() + + // run the mutation + result, err := r.Mutation().GroupCreate(ctx, GroupCreateInput{ + Name: groupName, + Aliases: tt.aliases, + }) + assert.Nil(t, err) + assert.NotNil(t, result) + + // validate that arguments are as expected + assert.Equal(t, tt.expected, gotInput.Group.Aliases.List()) + }) + } +} + +func TestGroupUpdate_AliasNormalization(t *testing.T) { + tests := []struct { + name string + inputAliases []string + existingAliases []string // aliases already on the group in the db + newName *string // nil = name not being updated + expected []string + }{ + // Name not being updated — normalize against existing name + { + name: "dedup and exclude name", + inputAliases: []string{alias1, groupName, alias1, "MYGROUP"}, + expected: []string{alias1}, + }, + { + name: "existing aliases are replaced not merged", + inputAliases: []string{alias1}, + existingAliases: []string{aliasOld1, aliasOld2}, + expected: []string{alias1}, + }, + { + name: "single valid alias", + inputAliases: []string{alias1}, + expected: []string{alias1}, + }, + { + name: "single alias same as group name", + inputAliases: []string{groupName}, + expected: []string{}, + }, + { + name: "alias with surrounding spaces trimmed", + inputAliases: []string{alias1Padded}, + expected: []string{alias1}, + }, + // Name being updated — normalize against the NEW name + { + name: "new name excluded from aliases", + inputAliases: []string{alias1, groupNewName}, + newName: &groupNewName, + expected: []string{alias1}, + }, + { + name: "old name kept when updating to new name", + inputAliases: []string{alias1, groupName}, + newName: &groupNewName, + expected: []string{alias1, groupName}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + inputMap := map[string]interface{}{"aliases": nil} + if tt.newName != nil { + inputMap["name"] = nil + } + ctx := testutil.WithGQLContext(context.Background(), inputMap) + + db, svc, r := newGroupResolver(t) + groupDataCopy := groupData + + // setup mocks + // Find runs twice in case of group update + db.Group.On("Find", mock.Anything, groupID).Return(&groupDataCopy, nil).Twice() + db.Group.On("GetAliases", mock.Anything, groupID).Return(tt.existingAliases, nil).Once() + + var gotPartial models.GroupPartial + svc.On("UpdatePartial", mock.Anything, groupID, mock.Anything, mock.Anything, mock.Anything). + Run(func(args mock.Arguments) { + gotPartial = args.Get(2).(models.GroupPartial) + }). + Return(&groupDataCopy, nil).Once() + + // run the mutation + result, err := r.Mutation().GroupUpdate(ctx, GroupUpdateInput{ + ID: groupIDStr, + Name: tt.newName, + Aliases: tt.inputAliases, + }) + assert.Nil(t, err) + assert.NotNil(t, result) + + // validate that arguments are as expected + assert.Equal(t, &models.UpdateStrings{ + Values: tt.expected, + Mode: models.RelationshipUpdateModeSet, + }, gotPartial.Aliases) + }) + } +} diff --git a/internal/api/resolver_mutation_movie.go b/internal/api/resolver_mutation_movie.go index cb19e540f6..0cb038a3d9 100644 --- a/internal/api/resolver_mutation_movie.go +++ b/internal/api/resolver_mutation_movie.go @@ -34,7 +34,11 @@ func (r *mutationResolver) MovieCreate(ctx context.Context, input MovieCreateInp newGroup := models.NewGroup() newGroup.Name = strings.TrimSpace(input.Name) - newGroup.Aliases = translator.string(input.Aliases) + var movieAliases []string + if input.Aliases != nil { + movieAliases = []string{strings.TrimSpace(*input.Aliases)} + } + newGroup.Aliases = models.NewRelatedStrings(stringslice.UniqueExcludeFold(movieAliases, newGroup.Name)) newGroup.Duration = input.Duration newGroup.Rating = input.Rating100 newGroup.Director = translator.string(input.Director) @@ -133,7 +137,21 @@ func (r *mutationResolver) MovieUpdate(ctx context.Context, input MovieUpdateInp updatedGroup := models.NewGroupPartial() updatedGroup.Name = translator.optionalString(input.Name, "name") - updatedGroup.Aliases = translator.optionalString(input.Aliases, "aliases") + + var aliases []string + if input.Aliases != nil { + aliases = []string{strings.TrimSpace(*input.Aliases)} + } + + // movie is deprecated, so the below case is being ignored + // when the new alias is same as old group name and old group name + // is not being updated, the deduplication wont happen, and extra alias + // would be present, not a major correctness issue though. + if updatedGroup.Name.Ptr() != nil { + aliases = stringslice.UniqueExcludeFold(aliases, updatedGroup.Name.Value) + } + + 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") diff --git a/internal/api/resolver_mutation_scene.go b/internal/api/resolver_mutation_scene.go index 70158fc6f7..0919fc539c 100644 --- a/internal/api/resolver_mutation_scene.go +++ b/internal/api/resolver_mutation_scene.go @@ -1319,11 +1319,12 @@ func (r *mutationResolver) SceneDeleteO(ctx context.Context, id string, t []*tim } func (r *mutationResolver) SceneGenerateScreenshot(ctx context.Context, id string, at *float64) (string, error) { + var jobID int if at != nil { - manager.GetInstance().GenerateScreenshot(ctx, id, *at) + jobID = manager.GetInstance().GenerateScreenshot(ctx, id, *at) } else { - manager.GetInstance().GenerateDefaultScreenshot(ctx, id) + jobID = manager.GetInstance().GenerateDefaultScreenshot(ctx, id) } - return "todo", nil + return strconv.Itoa(jobID), nil } diff --git a/internal/api/testutil/gql.go b/internal/api/testutil/gql.go new file mode 100644 index 0000000000..57c6da8e0b --- /dev/null +++ b/internal/api/testutil/gql.go @@ -0,0 +1,40 @@ +package testutil + +import ( + "context" + + "github.com/99designs/gqlgen/graphql" + "github.com/vektah/gqlparser/v2/ast" +) + +// WithGQLContext injects a fake gqlgen operation+field context so that +// changesetTranslator works in unit tests without a real GraphQL request. +// inputMap keys are the snake_case field names being treated as "set", +// values are ignored, prefer using `nil` +func WithGQLContext(ctx context.Context, inputMap map[string]interface{}) context.Context { + const varName = "_testInput" + if inputMap == nil { + inputMap = map[string]interface{}{} + } + opCtx := &graphql.OperationContext{ + Variables: map[string]interface{}{varName: inputMap}, + } + fieldCtx := &graphql.FieldContext{ + Field: graphql.CollectedField{ + Field: &ast.Field{ + Definition: &ast.FieldDefinition{ + Arguments: ast.ArgumentDefinitionList{{Name: "input"}}, + }, + Arguments: ast.ArgumentList{ + { + Name: "input", + Value: &ast.Value{Kind: ast.Variable, Raw: varName}, + }, + }, + }, + }, + } + ctx = graphql.WithOperationContext(ctx, opCtx) + ctx = graphql.WithFieldContext(ctx, fieldCtx) + return ctx +} diff --git a/internal/manager/manager_tasks.go b/internal/manager/manager_tasks.go index 518b872f6d..6afe33f0b8 100644 --- a/internal/manager/manager_tasks.go +++ b/internal/manager/manager_tasks.go @@ -145,7 +145,8 @@ func (s *Manager) Scan(ctx context.Context, input ScanMetadataInput) (int, error ZipFileExtensions: cfg.GetGalleryExtensions(), // ScanFilters is set in ScanJob.Execute // HandlerRequiredFilters is set in ScanJob.Execute - RootPaths: cfg.GetStashPaths().Paths(), + // #4425 - isRootPath compares these against the NFC paths stored during scanning + RootPaths: fsutil.NormalizePaths(cfg.GetStashPaths().Paths()), Rescan: input.Rescan, } diff --git a/internal/manager/mocks/GroupService.go b/internal/manager/mocks/GroupService.go new file mode 100644 index 0000000000..f218684df9 --- /dev/null +++ b/internal/manager/mocks/GroupService.go @@ -0,0 +1,115 @@ +// Code generated by mockery v2.21.6. DO NOT EDIT. + +package mocks + +import ( + context "context" + + group "github.com/stashapp/stash/pkg/group" + + mock "github.com/stretchr/testify/mock" + + models "github.com/stashapp/stash/pkg/models" +) + +// GroupService is an autogenerated mock type for the GroupService type +type GroupService struct { + mock.Mock +} + +// AddSubGroups provides a mock function with given fields: ctx, groupID, subGroups, insertIndex +func (_m *GroupService) AddSubGroups(ctx context.Context, groupID int, subGroups []models.GroupIDDescription, insertIndex *int) error { + ret := _m.Called(ctx, groupID, subGroups, insertIndex) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, int, []models.GroupIDDescription, *int) error); ok { + r0 = rf(ctx, groupID, subGroups, insertIndex) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Create provides a mock function with given fields: ctx, input +func (_m *GroupService) Create(ctx context.Context, input *models.CreateGroupInput) error { + ret := _m.Called(ctx, input) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, *models.CreateGroupInput) error); ok { + r0 = rf(ctx, input) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// RemoveSubGroups provides a mock function with given fields: ctx, groupID, subGroupIDs +func (_m *GroupService) RemoveSubGroups(ctx context.Context, groupID int, subGroupIDs []int) error { + ret := _m.Called(ctx, groupID, subGroupIDs) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, int, []int) error); ok { + r0 = rf(ctx, groupID, subGroupIDs) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// ReorderSubGroups provides a mock function with given fields: ctx, groupID, subGroupIDs, insertPointID, insertAfter +func (_m *GroupService) ReorderSubGroups(ctx context.Context, groupID int, subGroupIDs []int, insertPointID int, insertAfter bool) error { + ret := _m.Called(ctx, groupID, subGroupIDs, insertPointID, insertAfter) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, int, []int, int, bool) error); ok { + r0 = rf(ctx, groupID, subGroupIDs, insertPointID, insertAfter) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// UpdatePartial provides a mock function with given fields: ctx, id, updatedGroup, frontImage, backImage +func (_m *GroupService) UpdatePartial(ctx context.Context, id int, updatedGroup models.GroupPartial, frontImage group.ImageInput, backImage group.ImageInput) (*models.Group, error) { + ret := _m.Called(ctx, id, updatedGroup, frontImage, backImage) + + var r0 *models.Group + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int, models.GroupPartial, group.ImageInput, group.ImageInput) (*models.Group, error)); ok { + return rf(ctx, id, updatedGroup, frontImage, backImage) + } + if rf, ok := ret.Get(0).(func(context.Context, int, models.GroupPartial, group.ImageInput, group.ImageInput) *models.Group); ok { + r0 = rf(ctx, id, updatedGroup, frontImage, backImage) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*models.Group) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, int, models.GroupPartial, group.ImageInput, group.ImageInput) error); ok { + r1 = rf(ctx, id, updatedGroup, frontImage, backImage) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +type mockConstructorTestingTNewGroupService interface { + mock.TestingT + Cleanup(func()) +} + +// NewGroupService creates a new instance of GroupService. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewGroupService(t mockConstructorTestingTNewGroupService) *GroupService { + mock := &GroupService{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/internal/manager/task_scan.go b/internal/manager/task_scan.go index a7fd9c9dba..6c3cb970de 100644 --- a/internal/manager/task_scan.go +++ b/internal/manager/task_scan.go @@ -207,13 +207,21 @@ func (j *ScanJob) queueFileFunc(ctx context.Context, f models.FS, zipFile *file. return err } + // #4425 - store the NFC-normalized path so search matches user input. + // Filtering and filesystem access above use the original path; zip + // entries are matched byte-exact so are left unnormalized. + storedPath := path + if zipFile == nil { + storedPath = fsutil.NormalizePath(path) + } + ff := file.ScannedFile{ BaseFile: &models.BaseFile{ DirEntry: models.DirEntry{ ModTime: file.ModTime(info), }, - Path: path, - Basename: filepath.Base(path), + Path: storedPath, + Basename: filepath.Base(storedPath), Size: size, }, FS: f, diff --git a/pkg/ffmpeg/options.go b/pkg/ffmpeg/options.go index 9846192f4d..3734b14fb1 100644 --- a/pkg/ffmpeg/options.go +++ b/pkg/ffmpeg/options.go @@ -37,6 +37,11 @@ func (a Args) Seek(seconds float64) Args { return append(a, "-ss", fmt.Sprint(seconds)) } +// NoAccurateSeek adds the -noaccurate_seek flag and returns the result. +func (a Args) NoAccurateSeek() Args { + return append(a, "-noaccurate_seek") +} + // Duration sets the duration (-t) to the given seconds and returns the result. func (a Args) Duration(seconds float64) Args { return append(a, "-t", fmt.Sprint(seconds)) diff --git a/pkg/ffmpeg/stream_transcode.go b/pkg/ffmpeg/stream_transcode.go index bb701664f8..9efffce631 100644 --- a/pkg/ffmpeg/stream_transcode.go +++ b/pkg/ffmpeg/stream_transcode.go @@ -202,6 +202,10 @@ func (o TranscodeOptions) makeStreamArgs(sm *StreamManager) Args { args = append(args, extraInputArgs...) if o.StartTime != 0 { + if codec == VideoCodecCopy { + // #7103 - keep audio aligned with copied video, which can only start at a keyframe + args = args.NoAccurateSeek() + } args = args.Seek(o.StartTime) } diff --git a/pkg/file/folder_rename_detect.go b/pkg/file/folder_rename_detect.go index d45593b287..49fbd5b262 100644 --- a/pkg/file/folder_rename_detect.go +++ b/pkg/file/folder_rename_detect.go @@ -5,6 +5,7 @@ import ( "fmt" "io/fs" + "github.com/stashapp/stash/pkg/fsutil" "github.com/stashapp/stash/pkg/logger" "github.com/stashapp/stash/pkg/models" ) @@ -159,8 +160,13 @@ func (s *Scanner) detectFolderMove(ctx context.Context, file ScannedFile) (*mode // parent folder must be missing _, err = file.FS.Lstat(pf.Path) - if err == nil { + if err == nil && fsutil.NormalizePath(pf.Path) != file.Path { // parent folder exists, not a candidate + // #4425 - Lstat succeeds via a pre-normalization NFD path too, + // since macOS is normalization-insensitive; exclude that case, + // since it's the same folder as the one being scanned rather + // than a separate one that's still present, so its path should + // be updated in place instead of creating a duplicate detector.reject(parentFolderID) continue } diff --git a/pkg/file/scan.go b/pkg/file/scan.go index 4a1708caa8..760d4de89e 100644 --- a/pkg/file/scan.go +++ b/pkg/file/scan.go @@ -10,6 +10,7 @@ import ( "sync" "time" + "github.com/stashapp/stash/pkg/fsutil" "github.com/stashapp/stash/pkg/logger" "github.com/stashapp/stash/pkg/models" "github.com/stashapp/stash/pkg/txn" @@ -371,7 +372,7 @@ func (s *Scanner) ScanFile(ctx context.Context, f ScannedFile) (*ScanFileResult, // #1426 / #6326 - if file is in a case-insensitive filesystem, then try // case insensitive search // assume case sensitive if in zip - if ff == nil && f.ZipFileID != nil { + if ff == nil && f.ZipFileID == nil { caseSensitive, _ := f.FS.IsPathCaseSensitive(f.Path) if !caseSensitive { @@ -610,6 +611,11 @@ func (s *Scanner) handleRename(ctx context.Context, f models.File, fp []models.F switch { case err != nil: missing = append(missing, other) + case other.Base().Path != f.Base().Path && fsutil.NormalizePath(other.Base().Path) == fsutil.NormalizePath(f.Base().Path): + // #4425 - same file under a different normalization (e.g. NFD stored + // before NFC was introduced); treat as a move so the path is updated + // in place instead of creating a duplicate + missing = append(missing, other) case strings.EqualFold(f.Base().Path, other.Base().Path): // #1426 - if file exists but is a case-insensitive match for the // original filename, and the filesystem is case-insensitive diff --git a/pkg/file/scan_test.go b/pkg/file/scan_test.go new file mode 100644 index 0000000000..5f34c1befb --- /dev/null +++ b/pkg/file/scan_test.go @@ -0,0 +1,142 @@ +package file + +import ( + "context" + "errors" + "io/fs" + "testing" + "time" + + "github.com/stashapp/stash/pkg/models" + "github.com/stashapp/stash/pkg/models/mocks" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" +) + +type scanTestFS struct { + caseSensitive bool +} + +func (f scanTestFS) Stat(name string) (fs.FileInfo, error) { + return nil, fs.ErrNotExist +} + +func (f scanTestFS) Lstat(name string) (fs.FileInfo, error) { + return nil, fs.ErrNotExist +} + +func (f scanTestFS) Open(name string) (fs.ReadDirFile, error) { + return nil, fs.ErrNotExist +} + +func (f scanTestFS) OpenZip(name string, size int64) (models.ZipFS, error) { + return nil, errors.New("zip files not supported") +} + +func (f scanTestFS) IsPathCaseSensitive(path string) (bool, error) { + return f.caseSensitive, nil +} + +type scanTestFileInfo struct { + name string + size int64 + modTime time.Time +} + +func (i scanTestFileInfo) Name() string { return i.name } +func (i scanTestFileInfo) Size() int64 { return i.size } +func (i scanTestFileInfo) Mode() fs.FileMode { return 0644 } +func (i scanTestFileInfo) ModTime() time.Time { return i.modTime } +func (i scanTestFileInfo) IsDir() bool { return false } +func (i scanTestFileInfo) Sys() interface{} { return nil } + +type scanTestFingerprintCalculator struct { + fingerprints []models.Fingerprint +} + +func (c scanTestFingerprintCalculator) CalculateFingerprints(f *models.BaseFile, o Opener, useExisting bool) ([]models.Fingerprint, error) { + return c.fingerprints, nil +} + +func TestScannerScanFileMatchesCaseOnlyRenameOnCaseInsensitiveFilesystem(t *testing.T) { + ctx := context.Background() + db := mocks.NewDatabase() + + oldModTime := time.Date(2026, 7, 2, 12, 0, 0, 0, time.UTC) + newModTime := oldModTime.Add(time.Hour) + + existing := &models.VideoFile{ + BaseFile: &models.BaseFile{ + ID: 1, + Path: `C:\stash\ALLCAPS.mp4`, + Basename: "ALLCAPS.mp4", + ParentFolderID: 2, + Size: 100, + DirEntry: models.DirEntry{ + ModTime: oldModTime, + }, + Fingerprints: []models.Fingerprint{ + {Type: models.FingerprintTypeOshash, Fingerprint: "old-oshash"}, + {Type: models.FingerprintTypeMD5, Fingerprint: "old-md5"}, + {Type: models.FingerprintTypePhash, Fingerprint: int64(1234)}, + }, + }, + Format: "mp4", + VideoCodec: "h264", + AudioCodec: "aac", + } + + newFingerprints := []models.Fingerprint{ + {Type: models.FingerprintTypeOshash, Fingerprint: "new-oshash"}, + {Type: models.FingerprintTypeMD5, Fingerprint: "new-md5"}, + } + + scanner := Scanner{ + FS: scanTestFS{caseSensitive: false}, + Repository: Repository{ + TxnManager: db, + File: db.File, + Folder: db.Folder, + }, + FingerprintCalculator: scanTestFingerprintCalculator{fingerprints: newFingerprints}, + } + + scanned := ScannedFile{ + BaseFile: &models.BaseFile{ + Path: `C:\stash\Allcaps.mp4`, + Basename: "Allcaps.mp4", + Size: 90, + DirEntry: models.DirEntry{ + ModTime: newModTime, + }, + }, + FS: scanTestFS{caseSensitive: false}, + Info: scanTestFileInfo{ + name: "Allcaps.mp4", + size: 90, + modTime: newModTime, + }, + } + + db.File.On("FindByPath", mock.Anything, scanned.Path, true).Return(nil, nil).Once() + db.File.On("FindByPath", mock.Anything, scanned.Path, false).Return(existing, nil).Once() + db.File.On("Update", mock.Anything, mock.MatchedBy(func(f models.File) bool { + base := f.Base() + return base.ID == existing.ID && + base.Basename == scanned.Basename && + base.Size == scanned.Size && + base.Fingerprints.Get(models.FingerprintTypeOshash) == "new-oshash" && + base.Fingerprints.Get(models.FingerprintTypeMD5) == "new-md5" && + base.Fingerprints.Get(models.FingerprintTypePhash) == nil + })).Return(nil).Once() + + result, err := scanner.ScanFile(ctx, scanned) + require.NoError(t, err) + require.NotNil(t, result) + assert.False(t, result.New) + assert.True(t, result.Updated) + assert.Equal(t, existing.ID, result.File.Base().ID) + + db.File.AssertExpectations(t) +} diff --git a/pkg/file/stashignore_test.go b/pkg/file/stashignore_test.go index 41668b51b6..5c6e9f91bf 100644 --- a/pkg/file/stashignore_test.go +++ b/pkg/file/stashignore_test.go @@ -66,7 +66,7 @@ func walkAndFilter(t *testing.T, root string, filter *StashIgnoreFilter) []strin if filter.Accept(ctx, path, info, root, "") { relPath, _ := filepath.Rel(root, path) - accepted = append(accepted, relPath) + accepted = append(accepted, filepath.ToSlash(relPath)) } else if info.IsDir() { // If directory is rejected, skip it. return filepath.SkipDir diff --git a/pkg/fsutil/dir.go b/pkg/fsutil/dir.go index 554ce39a90..0b0fa9ab88 100644 --- a/pkg/fsutil/dir.go +++ b/pkg/fsutil/dir.go @@ -23,6 +23,10 @@ func DirExists(path string) (bool, error) { // IsPathInDir returns true if pathToCheck is within dir. func IsPathInDir(dir, pathToCheck string) bool { + // #4425 - normalize so NFC/NFD differences don't break containment checks + dir = NormalizePath(dir) + pathToCheck = NormalizePath(pathToCheck) + rel, err := filepath.Rel(dir, pathToCheck) if err == nil { diff --git a/pkg/fsutil/dir_test.go b/pkg/fsutil/dir_test.go index 577773fc1b..e522c4868e 100644 --- a/pkg/fsutil/dir_test.go +++ b/pkg/fsutil/dir_test.go @@ -3,11 +3,28 @@ package fsutil import ( "os" "path/filepath" + "runtime" "testing" "github.com/stretchr/testify/assert" ) +// TestIsPathInDirNormalization verifies that containment holds when the dir and +// the path use different Unicode normalization forms, as happens on macOS where +// the filesystem may report NFD while the configured/stored path is NFC. +func TestIsPathInDirNormalization(t *testing.T) { + dirNFC := filepath.Join("/library", gaNFC) + fileNFD := filepath.Join("/library", gaNFD, "video.mp4") + + // Only macOS treats NFC and NFD as equivalent; elsewhere they differ + // byte-wise and the path is considered outside the dir. + want := runtime.GOOS == "darwin" + assert.Equal(t, want, IsPathInDir(dirNFC, fileNFD)) + + // An exact (same-form) match must always hold. + assert.True(t, IsPathInDir(filepath.Join("/library", gaNFC), filepath.Join("/library", gaNFC, "video.mp4"))) +} + func TestIsPathInDir(t *testing.T) { type test struct { dir string diff --git a/pkg/fsutil/file.go b/pkg/fsutil/file.go index 05a1271295..13c97dc076 100644 --- a/pkg/fsutil/file.go +++ b/pkg/fsutil/file.go @@ -181,3 +181,12 @@ func GetExeName(base string) string { } return base } + +// NormalizePaths returns a new slice with NormalizePath applied to each path. +func NormalizePaths(paths []string) []string { + ret := make([]string, len(paths)) + for i, p := range paths { + ret[i] = NormalizePath(p) + } + return ret +} diff --git a/pkg/fsutil/file_darwin.go b/pkg/fsutil/file_darwin.go new file mode 100644 index 0000000000..b92230f4b4 --- /dev/null +++ b/pkg/fsutil/file_darwin.go @@ -0,0 +1,15 @@ +//go:build darwin +// +build darwin + +package fsutil + +import "golang.org/x/text/unicode/norm" + +// NormalizePath returns path normalized to Unicode NFC (composed) form. +// +// macOS filesystems report filenames in NFD (decomposed) form but are +// normalization-insensitive, so an NFD file on disk is still found via its +// NFC path. +func NormalizePath(path string) string { + return norm.NFC.String(path) +} diff --git a/pkg/fsutil/file_nondarwin.go b/pkg/fsutil/file_nondarwin.go new file mode 100644 index 0000000000..8bbaf774ac --- /dev/null +++ b/pkg/fsutil/file_nondarwin.go @@ -0,0 +1,13 @@ +//go:build !darwin +// +build !darwin + +package fsutil + +// NormalizePath returns path unchanged. +// +// Only macOS is normalization-insensitive; normalizing on a +// normalization-sensitive filesystem (e.g. Linux) would prevent files with +// NFD names on disk from being found. +func NormalizePath(path string) string { + return path +} diff --git a/pkg/fsutil/file_test.go b/pkg/fsutil/file_test.go index df1077df24..c128749510 100644 --- a/pkg/fsutil/file_test.go +++ b/pkg/fsutil/file_test.go @@ -1,6 +1,59 @@ package fsutil -import "testing" +import ( + "runtime" + "testing" +) + +// Unicode forms of the Japanese character "が" used to exercise NFC/NFD handling. +const ( + gaNFC = "が" // composed: が + gaNFD = "が" // decomposed: か + combining dakuten +) + +func TestNormalizePath(t *testing.T) { + // NFD input is normalized to NFC on macOS, left unchanged elsewhere. + wantNFD := gaNFD + if runtime.GOOS == "darwin" { + wantNFD = gaNFC + } + + tests := []struct { + name string + in string + want string + }{ + {"nfd", gaNFD, wantNFD}, + {"nfc unchanged", gaNFC, gaNFC}, + {"ascii unchanged", "/a/b/c.mp4", "/a/b/c.mp4"}, + {"empty", "", ""}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := NormalizePath(tt.in); got != tt.want { + t.Errorf("NormalizePath(%q) = %q, want %q (GOOS=%s)", tt.in, got, tt.want, runtime.GOOS) + } + }) + } +} + +func TestNormalizePaths(t *testing.T) { + in := []string{gaNFD, "/ascii"} + got := NormalizePaths(in) + + // must return a new slice, leaving the input untouched + if in[0] != gaNFD { + t.Errorf("NormalizePaths mutated its input: %q", in[0]) + } + + want0 := gaNFD + if runtime.GOOS == "darwin" { + want0 = gaNFC + } + if got[0] != want0 || got[1] != "/ascii" { + t.Errorf("NormalizePaths() = %q, want [%q /ascii]", got, want0) + } +} func TestSanitiseBasename(t *testing.T) { tests := []struct { diff --git a/pkg/group/export.go b/pkg/group/export.go index 0a56fbdbb4..27a2a05fc2 100644 --- a/pkg/group/export.go +++ b/pkg/group/export.go @@ -12,6 +12,7 @@ import ( ) type GroupExportReader interface { + models.AliasLoader GetFrontImage(ctx context.Context, groupID int) ([]byte, error) GetBackImage(ctx context.Context, groupID int) ([]byte, error) GetCustomFields(ctx context.Context, groupID int) (map[string]interface{}, error) @@ -21,7 +22,6 @@ type GroupExportReader interface { func ToJSON(ctx context.Context, reader GroupExportReader, studioReader models.StudioGetter, group *models.Group) (*jsonschema.Group, error) { newGroupJSON := jsonschema.Group{ Name: group.Name, - Aliases: group.Aliases, Director: group.Director, Synopsis: group.Synopsis, URLs: group.URLs.List(), @@ -50,6 +50,11 @@ func ToJSON(ctx context.Context, reader GroupExportReader, studioReader models.S } } + if err := group.LoadAliases(ctx, reader); err != nil { + return nil, fmt.Errorf("loading group aliases: %w", err) + } + newGroupJSON.Aliases = group.Aliases.List() + frontImage, err := reader.GetFrontImage(ctx, group.ID) if err != nil { logger.Errorf("Error getting movie front image: %v", err) diff --git a/pkg/group/export_test.go b/pkg/group/export_test.go index bff50de5ef..438ec4a1cc 100644 --- a/pkg/group/export_test.go +++ b/pkg/group/export_test.go @@ -31,7 +31,8 @@ const ( ) const movieName = "testMovie" -const movieAliases = "aliases" + +var movieAliases = []string{"aliases"} var ( date = "2001-01-01" @@ -73,7 +74,7 @@ func createFullMovie(id int, studioID int) models.Group { return models.Group{ ID: id, Name: movieName, - Aliases: movieAliases, + Aliases: models.NewRelatedStrings(movieAliases), Date: &dateObj, Rating: &rating, Duration: &duration, @@ -120,7 +121,8 @@ func createFullJSONMovie(studio, frontImage, backImage string, customFields map[ func createEmptyJSONMovie() *jsonschema.Group { return &jsonschema.Group{ - URLs: []string{}, + Aliases: []string{}, + URLs: []string{}, CreatedAt: json.JSONTime{ Time: createTime, }, @@ -211,6 +213,8 @@ func TestToJSON(t *testing.T) { db.Group.On("GetBackImage", testCtx, errStudioMovieID).Return(backImageBytes, nil).Maybe() db.Group.On("GetBackImage", testCtx, errCustomFieldsID).Return(nil, nil).Once() + db.Group.On("GetAliases", testCtx, emptyID).Return(nil, nil).Once() + db.Group.On("GetCustomFields", testCtx, movieID).Return(customFields, nil).Once() db.Group.On("GetCustomFields", testCtx, errCustomFieldsID).Return(nil, errors.New("error getting custom fields")).Once() db.Group.On("GetCustomFields", testCtx, mock.Anything).Return(emptyCustomFields, nil).Times(4) diff --git a/pkg/group/import.go b/pkg/group/import.go index 1a332bac22..31c9d5042c 100644 --- a/pkg/group/import.go +++ b/pkg/group/import.go @@ -143,7 +143,7 @@ func createTags(ctx context.Context, tagWriter models.TagFinderCreator, names [] func (i *Importer) groupJSONToGroup(groupJSON jsonschema.Group) models.Group { newGroup := models.Group{ Name: groupJSON.Name, - Aliases: groupJSON.Aliases, + Aliases: models.NewRelatedStrings(groupJSON.Aliases), Director: groupJSON.Director, Synopsis: groupJSON.Synopsis, CreatedAt: groupJSON.CreatedAt.GetTime(), diff --git a/pkg/group/validate.go b/pkg/group/validate.go index 255152a957..e89896b4c8 100644 --- a/pkg/group/validate.go +++ b/pkg/group/validate.go @@ -2,6 +2,7 @@ package group import ( "context" + "errors" "slices" "strings" @@ -9,11 +10,22 @@ import ( "github.com/stashapp/stash/pkg/sliceutil" ) +var ( + ErrEmptyAlias = errors.New("group alias must not be an empty string") + ErrAliasNotTrimmed = errors.New("group alias contains spaces at the beginning or the end") +) + func (s *Service) validateCreate(ctx context.Context, group *models.Group) error { if err := validateName(group.Name); err != nil { return err } + if group.Aliases.Loaded() { + if err := ValidateAliases(ctx, group.Aliases.List()); err != nil { + return err + } + } + containingIDs := group.ContainingGroups.IDs() subIDs := group.SubGroups.IDs() @@ -41,6 +53,12 @@ func (s *Service) validateUpdate(ctx context.Context, id int, partial models.Gro } } + if partial.Aliases != nil { + if err := ValidateAliases(ctx, partial.Aliases.Values); err != nil { + return err + } + } + if err := s.validateUpdateGroupHierarchy(ctx, existing, partial.ContainingGroups, partial.SubGroups); err != nil { return err } @@ -57,6 +75,26 @@ func validateName(n string) error { return nil } +// only validate if aliases are not empty and trimmed +// no requirement for uniqueness or difference from group name here +// deduplication and exclusion are ensured at resolver level +// import bypasses resolver though. +func ValidateAliases(ctx context.Context, aliases []string) error { + for _, a := range aliases { + if err := validateName(a); err != nil { + if errors.Is(err, ErrEmptyName) { + return ErrEmptyAlias + } + return err + } + if strings.TrimSpace(a) != a { + return ErrAliasNotTrimmed + } + } + + return nil +} + func (s *Service) validateGroupHierarchy(ctx context.Context, containingIDs []int, subIDs []int) error { // only need to validate if both are non-empty if len(containingIDs) == 0 || len(subIDs) == 0 { diff --git a/pkg/group/validate_test.go b/pkg/group/validate_test.go new file mode 100644 index 0000000000..fa48376972 --- /dev/null +++ b/pkg/group/validate_test.go @@ -0,0 +1,34 @@ +package group + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestValidateAliases(t *testing.T) { + tests := []struct { + name string + aliases []string + want error + }{ + {"nil aliases", nil, nil}, + {"empty aliases", []string{}, nil}, + {"valid single alias", []string{"alias one"}, nil}, + {"valid multiple aliases", []string{"alias one", "alias two"}, nil}, + {"internal spaces valid", []string{"foo bar baz"}, nil}, + {"empty string alias", []string{""}, ErrEmptyAlias}, + {"whitespace-only alias", []string{" "}, ErrEmptyAlias}, + {"leading space", []string{" alias"}, ErrAliasNotTrimmed}, + {"trailing space", []string{"alias "}, ErrAliasNotTrimmed}, + {"valid then untrimmed", []string{"good", " bad"}, ErrAliasNotTrimmed}, + {"duplicate aliases", []string{"alias", "alias"}, nil}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := ValidateAliases(testCtx, tt.aliases) + assert.Equal(t, tt.want, got) + }) + } +} diff --git a/pkg/hash/imagephash/phash.go b/pkg/hash/imagephash/phash.go index 0af5adec90..aaf685973b 100644 --- a/pkg/hash/imagephash/phash.go +++ b/pkg/hash/imagephash/phash.go @@ -40,12 +40,7 @@ func loadImage(encoder *ffmpeg.FFMpeg, imageFile *models.ImageFile) (image.Image } defer reader.Close() - buf := new(bytes.Buffer) - if _, err := buf.ReadFrom(reader); err != nil { - return nil, err - } - - img, _, err := image.Decode(buf) + img, _, err := image.Decode(reader) if errors.Is(err, image.ErrFormat) { // try ffmpeg as a fallback for unsupported formats // ffmpeg cannot read files inside zips diff --git a/pkg/models/jsonschema/group.go b/pkg/models/jsonschema/group.go index 357ac70bc6..045d5924d9 100644 --- a/pkg/models/jsonschema/group.go +++ b/pkg/models/jsonschema/group.go @@ -17,21 +17,23 @@ type SubGroupDescription struct { } type Group struct { - Name string `json:"name,omitempty"` - Aliases string `json:"aliases,omitempty"` - Duration int `json:"duration,omitempty"` - Date string `json:"date,omitempty"` - Rating int `json:"rating,omitempty"` - Director string `json:"director,omitempty"` - Synopsis string `json:"synopsis,omitempty"` - FrontImage string `json:"front_image,omitempty"` - BackImage string `json:"back_image,omitempty"` - URLs []string `json:"urls,omitempty"` - Studio string `json:"studio,omitempty"` - Tags []string `json:"tags,omitempty"` - SubGroups []SubGroupDescription `json:"sub_groups,omitempty"` - CreatedAt json.JSONTime `json:"created_at,omitempty"` - UpdatedAt json.JSONTime `json:"updated_at,omitempty"` + Name string `json:"name,omitempty"` + Duration int `json:"duration,omitempty"` + Date string `json:"date,omitempty"` + Rating int `json:"rating,omitempty"` + Director string `json:"director,omitempty"` + Synopsis string `json:"synopsis,omitempty"` + FrontImage string `json:"front_image,omitempty"` + BackImage string `json:"back_image,omitempty"` + Studio string `json:"studio,omitempty"` + + CreatedAt json.JSONTime `json:"created_at,omitempty"` + UpdatedAt json.JSONTime `json:"updated_at,omitempty"` + + Aliases StringOrStringList `json:"aliases,omitempty"` + URLs []string `json:"urls,omitempty"` + Tags []string `json:"tags,omitempty"` + SubGroups []SubGroupDescription `json:"sub_groups,omitempty"` CustomFields map[string]interface{} `json:"custom_fields,omitempty"` diff --git a/pkg/models/mocks/FileReaderWriter.go b/pkg/models/mocks/FileReaderWriter.go index 4b370459e4..e9f2ee7c7d 100644 --- a/pkg/models/mocks/FileReaderWriter.go +++ b/pkg/models/mocks/FileReaderWriter.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.10.0. DO NOT EDIT. +// Code generated by mockery v2.21.6. DO NOT EDIT. package mocks @@ -21,13 +21,16 @@ func (_m *FileReaderWriter) CountAllInPaths(ctx context.Context, p []string) (in ret := _m.Called(ctx, p) var r0 int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, []string) (int, error)); ok { + return rf(ctx, p) + } if rf, ok := ret.Get(0).(func(context.Context, []string) int); ok { r0 = rf(ctx, p) } else { r0 = ret.Get(0).(int) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, []string) error); ok { r1 = rf(ctx, p) } else { @@ -42,13 +45,16 @@ func (_m *FileReaderWriter) CountByFolderID(ctx context.Context, folderID models ret := _m.Called(ctx, folderID) var r0 int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, models.FolderID) (int, error)); ok { + return rf(ctx, folderID) + } if rf, ok := ret.Get(0).(func(context.Context, models.FolderID) int); ok { r0 = rf(ctx, folderID) } else { r0 = ret.Get(0).(int) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, models.FolderID) error); ok { r1 = rf(ctx, folderID) } else { @@ -112,6 +118,10 @@ func (_m *FileReaderWriter) Find(ctx context.Context, id ...models.FileID) ([]mo ret := _m.Called(_ca...) var r0 []models.File + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, ...models.FileID) ([]models.File, error)); ok { + return rf(ctx, id...) + } if rf, ok := ret.Get(0).(func(context.Context, ...models.FileID) []models.File); ok { r0 = rf(ctx, id...) } else { @@ -120,7 +130,6 @@ func (_m *FileReaderWriter) Find(ctx context.Context, id ...models.FileID) ([]mo } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, ...models.FileID) error); ok { r1 = rf(ctx, id...) } else { @@ -135,6 +144,10 @@ func (_m *FileReaderWriter) FindAllByPath(ctx context.Context, path string, case ret := _m.Called(ctx, path, caseSensitive) var r0 []models.File + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, bool) ([]models.File, error)); ok { + return rf(ctx, path, caseSensitive) + } if rf, ok := ret.Get(0).(func(context.Context, string, bool) []models.File); ok { r0 = rf(ctx, path, caseSensitive) } else { @@ -143,7 +156,6 @@ func (_m *FileReaderWriter) FindAllByPath(ctx context.Context, path string, case } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, string, bool) error); ok { r1 = rf(ctx, path, caseSensitive) } else { @@ -158,6 +170,10 @@ func (_m *FileReaderWriter) FindAllInPaths(ctx context.Context, p []string, incl ret := _m.Called(ctx, p, includeZipContents, limit, offset) var r0 []models.File + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, []string, bool, int, int) ([]models.File, error)); ok { + return rf(ctx, p, includeZipContents, limit, offset) + } if rf, ok := ret.Get(0).(func(context.Context, []string, bool, int, int) []models.File); ok { r0 = rf(ctx, p, includeZipContents, limit, offset) } else { @@ -166,7 +182,6 @@ func (_m *FileReaderWriter) FindAllInPaths(ctx context.Context, p []string, incl } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, []string, bool, int, int) error); ok { r1 = rf(ctx, p, includeZipContents, limit, offset) } else { @@ -181,6 +196,10 @@ func (_m *FileReaderWriter) FindByFileInfo(ctx context.Context, info fs.FileInfo ret := _m.Called(ctx, info, size) var r0 []models.File + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, fs.FileInfo, int64) ([]models.File, error)); ok { + return rf(ctx, info, size) + } if rf, ok := ret.Get(0).(func(context.Context, fs.FileInfo, int64) []models.File); ok { r0 = rf(ctx, info, size) } else { @@ -189,7 +208,6 @@ func (_m *FileReaderWriter) FindByFileInfo(ctx context.Context, info fs.FileInfo } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, fs.FileInfo, int64) error); ok { r1 = rf(ctx, info, size) } else { @@ -204,6 +222,10 @@ func (_m *FileReaderWriter) FindByFingerprint(ctx context.Context, fp models.Fin ret := _m.Called(ctx, fp) var r0 []models.File + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, models.Fingerprint) ([]models.File, error)); ok { + return rf(ctx, fp) + } if rf, ok := ret.Get(0).(func(context.Context, models.Fingerprint) []models.File); ok { r0 = rf(ctx, fp) } else { @@ -212,7 +234,6 @@ func (_m *FileReaderWriter) FindByFingerprint(ctx context.Context, fp models.Fin } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, models.Fingerprint) error); ok { r1 = rf(ctx, fp) } else { @@ -227,6 +248,10 @@ func (_m *FileReaderWriter) FindByPath(ctx context.Context, path string, caseSen ret := _m.Called(ctx, path, caseSensitive) var r0 models.File + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, bool) (models.File, error)); ok { + return rf(ctx, path, caseSensitive) + } if rf, ok := ret.Get(0).(func(context.Context, string, bool) models.File); ok { r0 = rf(ctx, path, caseSensitive) } else { @@ -235,7 +260,6 @@ func (_m *FileReaderWriter) FindByPath(ctx context.Context, path string, caseSen } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, string, bool) error); ok { r1 = rf(ctx, path, caseSensitive) } else { @@ -250,6 +274,10 @@ func (_m *FileReaderWriter) FindByZipFileID(ctx context.Context, zipFileID model ret := _m.Called(ctx, zipFileID) var r0 []models.File + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, models.FileID) ([]models.File, error)); ok { + return rf(ctx, zipFileID) + } if rf, ok := ret.Get(0).(func(context.Context, models.FileID) []models.File); ok { r0 = rf(ctx, zipFileID) } else { @@ -258,7 +286,6 @@ func (_m *FileReaderWriter) FindByZipFileID(ctx context.Context, zipFileID model } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, models.FileID) error); ok { r1 = rf(ctx, zipFileID) } else { @@ -273,6 +300,10 @@ func (_m *FileReaderWriter) GetCaptions(ctx context.Context, fileID models.FileI ret := _m.Called(ctx, fileID) var r0 []*models.VideoCaption + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, models.FileID) ([]*models.VideoCaption, error)); ok { + return rf(ctx, fileID) + } if rf, ok := ret.Get(0).(func(context.Context, models.FileID) []*models.VideoCaption); ok { r0 = rf(ctx, fileID) } else { @@ -281,7 +312,6 @@ func (_m *FileReaderWriter) GetCaptions(ctx context.Context, fileID models.FileI } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, models.FileID) error); ok { r1 = rf(ctx, fileID) } else { @@ -296,13 +326,16 @@ func (_m *FileReaderWriter) IsPrimary(ctx context.Context, fileID models.FileID) ret := _m.Called(ctx, fileID) var r0 bool + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, models.FileID) (bool, error)); ok { + return rf(ctx, fileID) + } if rf, ok := ret.Get(0).(func(context.Context, models.FileID) bool); ok { r0 = rf(ctx, fileID) } else { r0 = ret.Get(0).(bool) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, models.FileID) error); ok { r1 = rf(ctx, fileID) } else { @@ -331,6 +364,10 @@ func (_m *FileReaderWriter) Query(ctx context.Context, options models.FileQueryO ret := _m.Called(ctx, options) var r0 *models.FileQueryResult + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, models.FileQueryOptions) (*models.FileQueryResult, error)); ok { + return rf(ctx, options) + } if rf, ok := ret.Get(0).(func(context.Context, models.FileQueryOptions) *models.FileQueryResult); ok { r0 = rf(ctx, options) } else { @@ -339,7 +376,6 @@ func (_m *FileReaderWriter) Query(ctx context.Context, options models.FileQueryO } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, models.FileQueryOptions) error); ok { r1 = rf(ctx, options) } else { @@ -376,3 +412,18 @@ func (_m *FileReaderWriter) UpdateCaptions(ctx context.Context, fileID models.Fi return r0 } + +type mockConstructorTestingTNewFileReaderWriter interface { + mock.TestingT + Cleanup(func()) +} + +// NewFileReaderWriter creates a new instance of FileReaderWriter. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewFileReaderWriter(t mockConstructorTestingTNewFileReaderWriter) *FileReaderWriter { + mock := &FileReaderWriter{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/pkg/models/mocks/FolderReaderWriter.go b/pkg/models/mocks/FolderReaderWriter.go index d2230c645a..a830037308 100644 --- a/pkg/models/mocks/FolderReaderWriter.go +++ b/pkg/models/mocks/FolderReaderWriter.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.10.0. DO NOT EDIT. +// Code generated by mockery v2.21.6. DO NOT EDIT. package mocks @@ -19,13 +19,16 @@ func (_m *FolderReaderWriter) CountAllInPaths(ctx context.Context, p []string) ( ret := _m.Called(ctx, p) var r0 int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, []string) (int, error)); ok { + return rf(ctx, p) + } if rf, ok := ret.Get(0).(func(context.Context, []string) int); ok { r0 = rf(ctx, p) } else { r0 = ret.Get(0).(int) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, []string) error); ok { r1 = rf(ctx, p) } else { @@ -68,6 +71,10 @@ func (_m *FolderReaderWriter) Find(ctx context.Context, id models.FolderID) (*mo ret := _m.Called(ctx, id) var r0 *models.Folder + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, models.FolderID) (*models.Folder, error)); ok { + return rf(ctx, id) + } if rf, ok := ret.Get(0).(func(context.Context, models.FolderID) *models.Folder); ok { r0 = rf(ctx, id) } else { @@ -76,7 +83,6 @@ func (_m *FolderReaderWriter) Find(ctx context.Context, id models.FolderID) (*mo } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, models.FolderID) error); ok { r1 = rf(ctx, id) } else { @@ -91,6 +97,10 @@ func (_m *FolderReaderWriter) FindAllInPaths(ctx context.Context, p []string, in ret := _m.Called(ctx, p, includeZipContents, limit, offset) var r0 []*models.Folder + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, []string, bool, int, int) ([]*models.Folder, error)); ok { + return rf(ctx, p, includeZipContents, limit, offset) + } if rf, ok := ret.Get(0).(func(context.Context, []string, bool, int, int) []*models.Folder); ok { r0 = rf(ctx, p, includeZipContents, limit, offset) } else { @@ -99,7 +109,6 @@ func (_m *FolderReaderWriter) FindAllInPaths(ctx context.Context, p []string, in } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, []string, bool, int, int) error); ok { r1 = rf(ctx, p, includeZipContents, limit, offset) } else { @@ -114,6 +123,10 @@ func (_m *FolderReaderWriter) FindByParentFolderID(ctx context.Context, parentFo ret := _m.Called(ctx, parentFolderID) var r0 []*models.Folder + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, models.FolderID) ([]*models.Folder, error)); ok { + return rf(ctx, parentFolderID) + } if rf, ok := ret.Get(0).(func(context.Context, models.FolderID) []*models.Folder); ok { r0 = rf(ctx, parentFolderID) } else { @@ -122,7 +135,6 @@ func (_m *FolderReaderWriter) FindByParentFolderID(ctx context.Context, parentFo } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, models.FolderID) error); ok { r1 = rf(ctx, parentFolderID) } else { @@ -137,6 +149,10 @@ func (_m *FolderReaderWriter) FindByPath(ctx context.Context, path string, caseS ret := _m.Called(ctx, path, caseSensitive) var r0 *models.Folder + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, bool) (*models.Folder, error)); ok { + return rf(ctx, path, caseSensitive) + } if rf, ok := ret.Get(0).(func(context.Context, string, bool) *models.Folder); ok { r0 = rf(ctx, path, caseSensitive) } else { @@ -145,7 +161,6 @@ func (_m *FolderReaderWriter) FindByPath(ctx context.Context, path string, caseS } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, string, bool) error); ok { r1 = rf(ctx, path, caseSensitive) } else { @@ -160,6 +175,10 @@ func (_m *FolderReaderWriter) FindByZipFileID(ctx context.Context, zipFileID mod ret := _m.Called(ctx, zipFileID) var r0 []*models.Folder + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, models.FileID) ([]*models.Folder, error)); ok { + return rf(ctx, zipFileID) + } if rf, ok := ret.Get(0).(func(context.Context, models.FileID) []*models.Folder); ok { r0 = rf(ctx, zipFileID) } else { @@ -168,7 +187,6 @@ func (_m *FolderReaderWriter) FindByZipFileID(ctx context.Context, zipFileID mod } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, models.FileID) error); ok { r1 = rf(ctx, zipFileID) } else { @@ -183,6 +201,10 @@ func (_m *FolderReaderWriter) FindMany(ctx context.Context, id []models.FolderID ret := _m.Called(ctx, id) var r0 []*models.Folder + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, []models.FolderID) ([]*models.Folder, error)); ok { + return rf(ctx, id) + } if rf, ok := ret.Get(0).(func(context.Context, []models.FolderID) []*models.Folder); ok { r0 = rf(ctx, id) } else { @@ -191,7 +213,6 @@ func (_m *FolderReaderWriter) FindMany(ctx context.Context, id []models.FolderID } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, []models.FolderID) error); ok { r1 = rf(ctx, id) } else { @@ -206,6 +227,10 @@ func (_m *FolderReaderWriter) GetManyParentFolderIDs(ctx context.Context, folder ret := _m.Called(ctx, folderIDs) var r0 [][]models.FolderID + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, []models.FolderID) ([][]models.FolderID, error)); ok { + return rf(ctx, folderIDs) + } if rf, ok := ret.Get(0).(func(context.Context, []models.FolderID) [][]models.FolderID); ok { r0 = rf(ctx, folderIDs) } else { @@ -214,7 +239,6 @@ func (_m *FolderReaderWriter) GetManyParentFolderIDs(ctx context.Context, folder } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, []models.FolderID) error); ok { r1 = rf(ctx, folderIDs) } else { @@ -229,6 +253,10 @@ func (_m *FolderReaderWriter) GetManySubFolderIDs(ctx context.Context, folderIDs ret := _m.Called(ctx, folderIDs) var r0 [][]models.FolderID + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, []models.FolderID) ([][]models.FolderID, error)); ok { + return rf(ctx, folderIDs) + } if rf, ok := ret.Get(0).(func(context.Context, []models.FolderID) [][]models.FolderID); ok { r0 = rf(ctx, folderIDs) } else { @@ -237,7 +265,6 @@ func (_m *FolderReaderWriter) GetManySubFolderIDs(ctx context.Context, folderIDs } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, []models.FolderID) error); ok { r1 = rf(ctx, folderIDs) } else { @@ -252,6 +279,10 @@ func (_m *FolderReaderWriter) Query(ctx context.Context, options models.FolderQu ret := _m.Called(ctx, options) var r0 *models.FolderQueryResult + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, models.FolderQueryOptions) (*models.FolderQueryResult, error)); ok { + return rf(ctx, options) + } if rf, ok := ret.Get(0).(func(context.Context, models.FolderQueryOptions) *models.FolderQueryResult); ok { r0 = rf(ctx, options) } else { @@ -260,7 +291,6 @@ func (_m *FolderReaderWriter) Query(ctx context.Context, options models.FolderQu } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, models.FolderQueryOptions) error); ok { r1 = rf(ctx, options) } else { @@ -283,3 +313,18 @@ func (_m *FolderReaderWriter) Update(ctx context.Context, f *models.Folder) erro return r0 } + +type mockConstructorTestingTNewFolderReaderWriter interface { + mock.TestingT + Cleanup(func()) +} + +// NewFolderReaderWriter creates a new instance of FolderReaderWriter. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewFolderReaderWriter(t mockConstructorTestingTNewFolderReaderWriter) *FolderReaderWriter { + mock := &FolderReaderWriter{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/pkg/models/mocks/GalleryChapterReaderWriter.go b/pkg/models/mocks/GalleryChapterReaderWriter.go index 3adc980ac6..06c57a0744 100644 --- a/pkg/models/mocks/GalleryChapterReaderWriter.go +++ b/pkg/models/mocks/GalleryChapterReaderWriter.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.10.0. DO NOT EDIT. +// Code generated by mockery v2.21.6. DO NOT EDIT. package mocks @@ -47,6 +47,10 @@ func (_m *GalleryChapterReaderWriter) Find(ctx context.Context, id int) (*models ret := _m.Called(ctx, id) var r0 *models.GalleryChapter + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) (*models.GalleryChapter, error)); ok { + return rf(ctx, id) + } if rf, ok := ret.Get(0).(func(context.Context, int) *models.GalleryChapter); ok { r0 = rf(ctx, id) } else { @@ -55,7 +59,6 @@ func (_m *GalleryChapterReaderWriter) Find(ctx context.Context, id int) (*models } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, id) } else { @@ -70,6 +73,10 @@ func (_m *GalleryChapterReaderWriter) FindByGalleryID(ctx context.Context, galle ret := _m.Called(ctx, galleryID) var r0 []*models.GalleryChapter + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) ([]*models.GalleryChapter, error)); ok { + return rf(ctx, galleryID) + } if rf, ok := ret.Get(0).(func(context.Context, int) []*models.GalleryChapter); ok { r0 = rf(ctx, galleryID) } else { @@ -78,7 +85,6 @@ func (_m *GalleryChapterReaderWriter) FindByGalleryID(ctx context.Context, galle } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, galleryID) } else { @@ -93,6 +99,10 @@ func (_m *GalleryChapterReaderWriter) FindMany(ctx context.Context, ids []int) ( ret := _m.Called(ctx, ids) var r0 []*models.GalleryChapter + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, []int) ([]*models.GalleryChapter, error)); ok { + return rf(ctx, ids) + } if rf, ok := ret.Get(0).(func(context.Context, []int) []*models.GalleryChapter); ok { r0 = rf(ctx, ids) } else { @@ -101,7 +111,6 @@ func (_m *GalleryChapterReaderWriter) FindMany(ctx context.Context, ids []int) ( } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, []int) error); ok { r1 = rf(ctx, ids) } else { @@ -130,6 +139,10 @@ func (_m *GalleryChapterReaderWriter) UpdatePartial(ctx context.Context, id int, ret := _m.Called(ctx, id, updatedGalleryChapter) var r0 *models.GalleryChapter + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int, models.GalleryChapterPartial) (*models.GalleryChapter, error)); ok { + return rf(ctx, id, updatedGalleryChapter) + } if rf, ok := ret.Get(0).(func(context.Context, int, models.GalleryChapterPartial) *models.GalleryChapter); ok { r0 = rf(ctx, id, updatedGalleryChapter) } else { @@ -138,7 +151,6 @@ func (_m *GalleryChapterReaderWriter) UpdatePartial(ctx context.Context, id int, } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int, models.GalleryChapterPartial) error); ok { r1 = rf(ctx, id, updatedGalleryChapter) } else { @@ -147,3 +159,18 @@ func (_m *GalleryChapterReaderWriter) UpdatePartial(ctx context.Context, id int, return r0, r1 } + +type mockConstructorTestingTNewGalleryChapterReaderWriter interface { + mock.TestingT + Cleanup(func()) +} + +// NewGalleryChapterReaderWriter creates a new instance of GalleryChapterReaderWriter. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewGalleryChapterReaderWriter(t mockConstructorTestingTNewGalleryChapterReaderWriter) *GalleryChapterReaderWriter { + mock := &GalleryChapterReaderWriter{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/pkg/models/mocks/GalleryReaderWriter.go b/pkg/models/mocks/GalleryReaderWriter.go index 15a4a6b984..cc4201f45c 100644 --- a/pkg/models/mocks/GalleryReaderWriter.go +++ b/pkg/models/mocks/GalleryReaderWriter.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.10.0. DO NOT EDIT. +// Code generated by mockery v2.21.6. DO NOT EDIT. package mocks @@ -68,6 +68,10 @@ func (_m *GalleryReaderWriter) All(ctx context.Context) ([]*models.Gallery, erro ret := _m.Called(ctx) var r0 []*models.Gallery + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) ([]*models.Gallery, error)); ok { + return rf(ctx) + } if rf, ok := ret.Get(0).(func(context.Context) []*models.Gallery); ok { r0 = rf(ctx) } else { @@ -76,7 +80,6 @@ func (_m *GalleryReaderWriter) All(ctx context.Context) ([]*models.Gallery, erro } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context) error); ok { r1 = rf(ctx) } else { @@ -91,13 +94,16 @@ func (_m *GalleryReaderWriter) Count(ctx context.Context) (int, error) { ret := _m.Called(ctx) var r0 int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) (int, error)); ok { + return rf(ctx) + } if rf, ok := ret.Get(0).(func(context.Context) int); ok { r0 = rf(ctx) } else { r0 = ret.Get(0).(int) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context) error); ok { r1 = rf(ctx) } else { @@ -112,13 +118,16 @@ func (_m *GalleryReaderWriter) CountByFileID(ctx context.Context, fileID models. ret := _m.Called(ctx, fileID) var r0 int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, models.FileID) (int, error)); ok { + return rf(ctx, fileID) + } if rf, ok := ret.Get(0).(func(context.Context, models.FileID) int); ok { r0 = rf(ctx, fileID) } else { r0 = ret.Get(0).(int) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, models.FileID) error); ok { r1 = rf(ctx, fileID) } else { @@ -161,6 +170,10 @@ func (_m *GalleryReaderWriter) Find(ctx context.Context, id int) (*models.Galler ret := _m.Called(ctx, id) var r0 *models.Gallery + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) (*models.Gallery, error)); ok { + return rf(ctx, id) + } if rf, ok := ret.Get(0).(func(context.Context, int) *models.Gallery); ok { r0 = rf(ctx, id) } else { @@ -169,7 +182,6 @@ func (_m *GalleryReaderWriter) Find(ctx context.Context, id int) (*models.Galler } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, id) } else { @@ -184,6 +196,10 @@ func (_m *GalleryReaderWriter) FindByChecksum(ctx context.Context, checksum stri ret := _m.Called(ctx, checksum) var r0 []*models.Gallery + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string) ([]*models.Gallery, error)); ok { + return rf(ctx, checksum) + } if rf, ok := ret.Get(0).(func(context.Context, string) []*models.Gallery); ok { r0 = rf(ctx, checksum) } else { @@ -192,7 +208,6 @@ func (_m *GalleryReaderWriter) FindByChecksum(ctx context.Context, checksum stri } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { r1 = rf(ctx, checksum) } else { @@ -207,6 +222,10 @@ func (_m *GalleryReaderWriter) FindByChecksums(ctx context.Context, checksums [] ret := _m.Called(ctx, checksums) var r0 []*models.Gallery + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, []string) ([]*models.Gallery, error)); ok { + return rf(ctx, checksums) + } if rf, ok := ret.Get(0).(func(context.Context, []string) []*models.Gallery); ok { r0 = rf(ctx, checksums) } else { @@ -215,7 +234,6 @@ func (_m *GalleryReaderWriter) FindByChecksums(ctx context.Context, checksums [] } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, []string) error); ok { r1 = rf(ctx, checksums) } else { @@ -230,6 +248,10 @@ func (_m *GalleryReaderWriter) FindByFileID(ctx context.Context, fileID models.F ret := _m.Called(ctx, fileID) var r0 []*models.Gallery + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, models.FileID) ([]*models.Gallery, error)); ok { + return rf(ctx, fileID) + } if rf, ok := ret.Get(0).(func(context.Context, models.FileID) []*models.Gallery); ok { r0 = rf(ctx, fileID) } else { @@ -238,7 +260,6 @@ func (_m *GalleryReaderWriter) FindByFileID(ctx context.Context, fileID models.F } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, models.FileID) error); ok { r1 = rf(ctx, fileID) } else { @@ -253,6 +274,10 @@ func (_m *GalleryReaderWriter) FindByFingerprints(ctx context.Context, fp []mode ret := _m.Called(ctx, fp) var r0 []*models.Gallery + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, []models.Fingerprint) ([]*models.Gallery, error)); ok { + return rf(ctx, fp) + } if rf, ok := ret.Get(0).(func(context.Context, []models.Fingerprint) []*models.Gallery); ok { r0 = rf(ctx, fp) } else { @@ -261,7 +286,6 @@ func (_m *GalleryReaderWriter) FindByFingerprints(ctx context.Context, fp []mode } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, []models.Fingerprint) error); ok { r1 = rf(ctx, fp) } else { @@ -276,6 +300,10 @@ func (_m *GalleryReaderWriter) FindByFolderID(ctx context.Context, folderID mode ret := _m.Called(ctx, folderID) var r0 []*models.Gallery + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, models.FolderID) ([]*models.Gallery, error)); ok { + return rf(ctx, folderID) + } if rf, ok := ret.Get(0).(func(context.Context, models.FolderID) []*models.Gallery); ok { r0 = rf(ctx, folderID) } else { @@ -284,7 +312,6 @@ func (_m *GalleryReaderWriter) FindByFolderID(ctx context.Context, folderID mode } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, models.FolderID) error); ok { r1 = rf(ctx, folderID) } else { @@ -299,6 +326,10 @@ func (_m *GalleryReaderWriter) FindByImageID(ctx context.Context, imageID int) ( ret := _m.Called(ctx, imageID) var r0 []*models.Gallery + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) ([]*models.Gallery, error)); ok { + return rf(ctx, imageID) + } if rf, ok := ret.Get(0).(func(context.Context, int) []*models.Gallery); ok { r0 = rf(ctx, imageID) } else { @@ -307,7 +338,6 @@ func (_m *GalleryReaderWriter) FindByImageID(ctx context.Context, imageID int) ( } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, imageID) } else { @@ -322,6 +352,10 @@ func (_m *GalleryReaderWriter) FindByPath(ctx context.Context, path string) ([]* ret := _m.Called(ctx, path) var r0 []*models.Gallery + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string) ([]*models.Gallery, error)); ok { + return rf(ctx, path) + } if rf, ok := ret.Get(0).(func(context.Context, string) []*models.Gallery); ok { r0 = rf(ctx, path) } else { @@ -330,7 +364,6 @@ func (_m *GalleryReaderWriter) FindByPath(ctx context.Context, path string) ([]* } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { r1 = rf(ctx, path) } else { @@ -345,6 +378,10 @@ func (_m *GalleryReaderWriter) FindBySceneID(ctx context.Context, sceneID int) ( ret := _m.Called(ctx, sceneID) var r0 []*models.Gallery + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) ([]*models.Gallery, error)); ok { + return rf(ctx, sceneID) + } if rf, ok := ret.Get(0).(func(context.Context, int) []*models.Gallery); ok { r0 = rf(ctx, sceneID) } else { @@ -353,7 +390,6 @@ func (_m *GalleryReaderWriter) FindBySceneID(ctx context.Context, sceneID int) ( } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, sceneID) } else { @@ -368,6 +404,10 @@ func (_m *GalleryReaderWriter) FindMany(ctx context.Context, ids []int) ([]*mode ret := _m.Called(ctx, ids) var r0 []*models.Gallery + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, []int) ([]*models.Gallery, error)); ok { + return rf(ctx, ids) + } if rf, ok := ret.Get(0).(func(context.Context, []int) []*models.Gallery); ok { r0 = rf(ctx, ids) } else { @@ -376,7 +416,6 @@ func (_m *GalleryReaderWriter) FindMany(ctx context.Context, ids []int) ([]*mode } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, []int) error); ok { r1 = rf(ctx, ids) } else { @@ -391,6 +430,10 @@ func (_m *GalleryReaderWriter) FindUserGalleryByTitle(ctx context.Context, title ret := _m.Called(ctx, title) var r0 []*models.Gallery + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string) ([]*models.Gallery, error)); ok { + return rf(ctx, title) + } if rf, ok := ret.Get(0).(func(context.Context, string) []*models.Gallery); ok { r0 = rf(ctx, title) } else { @@ -399,7 +442,6 @@ func (_m *GalleryReaderWriter) FindUserGalleryByTitle(ctx context.Context, title } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { r1 = rf(ctx, title) } else { @@ -414,6 +456,10 @@ func (_m *GalleryReaderWriter) GetCustomFields(ctx context.Context, id int) (map ret := _m.Called(ctx, id) var r0 map[string]interface{} + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) (map[string]interface{}, error)); ok { + return rf(ctx, id) + } if rf, ok := ret.Get(0).(func(context.Context, int) map[string]interface{}); ok { r0 = rf(ctx, id) } else { @@ -422,7 +468,6 @@ func (_m *GalleryReaderWriter) GetCustomFields(ctx context.Context, id int) (map } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, id) } else { @@ -437,6 +482,10 @@ func (_m *GalleryReaderWriter) GetCustomFieldsBulk(ctx context.Context, ids []in ret := _m.Called(ctx, ids) var r0 []models.CustomFieldMap + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, []int) ([]models.CustomFieldMap, error)); ok { + return rf(ctx, ids) + } if rf, ok := ret.Get(0).(func(context.Context, []int) []models.CustomFieldMap); ok { r0 = rf(ctx, ids) } else { @@ -445,7 +494,6 @@ func (_m *GalleryReaderWriter) GetCustomFieldsBulk(ctx context.Context, ids []in } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, []int) error); ok { r1 = rf(ctx, ids) } else { @@ -460,6 +508,10 @@ func (_m *GalleryReaderWriter) GetFiles(ctx context.Context, relatedID int) ([]m ret := _m.Called(ctx, relatedID) var r0 []models.File + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) ([]models.File, error)); ok { + return rf(ctx, relatedID) + } if rf, ok := ret.Get(0).(func(context.Context, int) []models.File); ok { r0 = rf(ctx, relatedID) } else { @@ -468,7 +520,6 @@ func (_m *GalleryReaderWriter) GetFiles(ctx context.Context, relatedID int) ([]m } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, relatedID) } else { @@ -483,6 +534,10 @@ func (_m *GalleryReaderWriter) GetImageIDs(ctx context.Context, relatedID int) ( ret := _m.Called(ctx, relatedID) var r0 []int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) ([]int, error)); ok { + return rf(ctx, relatedID) + } if rf, ok := ret.Get(0).(func(context.Context, int) []int); ok { r0 = rf(ctx, relatedID) } else { @@ -491,7 +546,6 @@ func (_m *GalleryReaderWriter) GetImageIDs(ctx context.Context, relatedID int) ( } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, relatedID) } else { @@ -506,6 +560,10 @@ func (_m *GalleryReaderWriter) GetManyFileIDs(ctx context.Context, ids []int) ([ ret := _m.Called(ctx, ids) var r0 [][]models.FileID + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, []int) ([][]models.FileID, error)); ok { + return rf(ctx, ids) + } if rf, ok := ret.Get(0).(func(context.Context, []int) [][]models.FileID); ok { r0 = rf(ctx, ids) } else { @@ -514,7 +572,6 @@ func (_m *GalleryReaderWriter) GetManyFileIDs(ctx context.Context, ids []int) ([ } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, []int) error); ok { r1 = rf(ctx, ids) } else { @@ -529,6 +586,10 @@ func (_m *GalleryReaderWriter) GetManyIDsByFileIDs(ctx context.Context, fileIDs ret := _m.Called(ctx, fileIDs) var r0 [][]int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, []models.FileID) ([][]int, error)); ok { + return rf(ctx, fileIDs) + } if rf, ok := ret.Get(0).(func(context.Context, []models.FileID) [][]int); ok { r0 = rf(ctx, fileIDs) } else { @@ -537,7 +598,6 @@ func (_m *GalleryReaderWriter) GetManyIDsByFileIDs(ctx context.Context, fileIDs } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, []models.FileID) error); ok { r1 = rf(ctx, fileIDs) } else { @@ -552,6 +612,10 @@ func (_m *GalleryReaderWriter) GetPerformerIDs(ctx context.Context, relatedID in ret := _m.Called(ctx, relatedID) var r0 []int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) ([]int, error)); ok { + return rf(ctx, relatedID) + } if rf, ok := ret.Get(0).(func(context.Context, int) []int); ok { r0 = rf(ctx, relatedID) } else { @@ -560,7 +624,6 @@ func (_m *GalleryReaderWriter) GetPerformerIDs(ctx context.Context, relatedID in } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, relatedID) } else { @@ -575,6 +638,10 @@ func (_m *GalleryReaderWriter) GetSceneIDs(ctx context.Context, relatedID int) ( ret := _m.Called(ctx, relatedID) var r0 []int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) ([]int, error)); ok { + return rf(ctx, relatedID) + } if rf, ok := ret.Get(0).(func(context.Context, int) []int); ok { r0 = rf(ctx, relatedID) } else { @@ -583,7 +650,6 @@ func (_m *GalleryReaderWriter) GetSceneIDs(ctx context.Context, relatedID int) ( } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, relatedID) } else { @@ -598,6 +664,10 @@ func (_m *GalleryReaderWriter) GetTagIDs(ctx context.Context, relatedID int) ([] ret := _m.Called(ctx, relatedID) var r0 []int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) ([]int, error)); ok { + return rf(ctx, relatedID) + } if rf, ok := ret.Get(0).(func(context.Context, int) []int); ok { r0 = rf(ctx, relatedID) } else { @@ -606,7 +676,6 @@ func (_m *GalleryReaderWriter) GetTagIDs(ctx context.Context, relatedID int) ([] } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, relatedID) } else { @@ -621,6 +690,10 @@ func (_m *GalleryReaderWriter) GetURLs(ctx context.Context, relatedID int) ([]st ret := _m.Called(ctx, relatedID) var r0 []string + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) ([]string, error)); ok { + return rf(ctx, relatedID) + } if rf, ok := ret.Get(0).(func(context.Context, int) []string); ok { r0 = rf(ctx, relatedID) } else { @@ -629,7 +702,6 @@ func (_m *GalleryReaderWriter) GetURLs(ctx context.Context, relatedID int) ([]st } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, relatedID) } else { @@ -644,6 +716,11 @@ func (_m *GalleryReaderWriter) Query(ctx context.Context, galleryFilter *models. ret := _m.Called(ctx, galleryFilter, findFilter) var r0 []*models.Gallery + var r1 int + var r2 error + if rf, ok := ret.Get(0).(func(context.Context, *models.GalleryFilterType, *models.FindFilterType) ([]*models.Gallery, int, error)); ok { + return rf(ctx, galleryFilter, findFilter) + } if rf, ok := ret.Get(0).(func(context.Context, *models.GalleryFilterType, *models.FindFilterType) []*models.Gallery); ok { r0 = rf(ctx, galleryFilter, findFilter) } else { @@ -652,14 +729,12 @@ func (_m *GalleryReaderWriter) Query(ctx context.Context, galleryFilter *models. } } - var r1 int if rf, ok := ret.Get(1).(func(context.Context, *models.GalleryFilterType, *models.FindFilterType) int); ok { r1 = rf(ctx, galleryFilter, findFilter) } else { r1 = ret.Get(1).(int) } - var r2 error if rf, ok := ret.Get(2).(func(context.Context, *models.GalleryFilterType, *models.FindFilterType) error); ok { r2 = rf(ctx, galleryFilter, findFilter) } else { @@ -674,13 +749,16 @@ func (_m *GalleryReaderWriter) QueryCount(ctx context.Context, galleryFilter *mo ret := _m.Called(ctx, galleryFilter, findFilter) var r0 int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *models.GalleryFilterType, *models.FindFilterType) (int, error)); ok { + return rf(ctx, galleryFilter, findFilter) + } if rf, ok := ret.Get(0).(func(context.Context, *models.GalleryFilterType, *models.FindFilterType) int); ok { r0 = rf(ctx, galleryFilter, findFilter) } else { r0 = ret.Get(0).(int) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, *models.GalleryFilterType, *models.FindFilterType) error); ok { r1 = rf(ctx, galleryFilter, findFilter) } else { @@ -786,6 +864,10 @@ func (_m *GalleryReaderWriter) UpdatePartial(ctx context.Context, id int, update ret := _m.Called(ctx, id, updatedGallery) var r0 *models.Gallery + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int, models.GalleryPartial) (*models.Gallery, error)); ok { + return rf(ctx, id, updatedGallery) + } if rf, ok := ret.Get(0).(func(context.Context, int, models.GalleryPartial) *models.Gallery); ok { r0 = rf(ctx, id, updatedGallery) } else { @@ -794,7 +876,6 @@ func (_m *GalleryReaderWriter) UpdatePartial(ctx context.Context, id int, update } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int, models.GalleryPartial) error); ok { r1 = rf(ctx, id, updatedGallery) } else { @@ -803,3 +884,18 @@ func (_m *GalleryReaderWriter) UpdatePartial(ctx context.Context, id int, update return r0, r1 } + +type mockConstructorTestingTNewGalleryReaderWriter interface { + mock.TestingT + Cleanup(func()) +} + +// NewGalleryReaderWriter creates a new instance of GalleryReaderWriter. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewGalleryReaderWriter(t mockConstructorTestingTNewGalleryReaderWriter) *GalleryReaderWriter { + mock := &GalleryReaderWriter{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/pkg/models/mocks/GroupReaderWriter.go b/pkg/models/mocks/GroupReaderWriter.go index ac9e513f4a..5bbeb6314c 100644 --- a/pkg/models/mocks/GroupReaderWriter.go +++ b/pkg/models/mocks/GroupReaderWriter.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.10.0. DO NOT EDIT. +// Code generated by mockery v2.21.6. DO NOT EDIT. package mocks @@ -19,6 +19,10 @@ func (_m *GroupReaderWriter) All(ctx context.Context) ([]*models.Group, error) { ret := _m.Called(ctx) var r0 []*models.Group + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) ([]*models.Group, error)); ok { + return rf(ctx) + } if rf, ok := ret.Get(0).(func(context.Context) []*models.Group); ok { r0 = rf(ctx) } else { @@ -27,7 +31,6 @@ func (_m *GroupReaderWriter) All(ctx context.Context) ([]*models.Group, error) { } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context) error); ok { r1 = rf(ctx) } else { @@ -42,13 +45,16 @@ func (_m *GroupReaderWriter) Count(ctx context.Context) (int, error) { ret := _m.Called(ctx) var r0 int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) (int, error)); ok { + return rf(ctx) + } if rf, ok := ret.Get(0).(func(context.Context) int); ok { r0 = rf(ctx) } else { r0 = ret.Get(0).(int) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context) error); ok { r1 = rf(ctx) } else { @@ -63,13 +69,16 @@ func (_m *GroupReaderWriter) CountByPerformerID(ctx context.Context, performerID ret := _m.Called(ctx, performerID) var r0 int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) (int, error)); ok { + return rf(ctx, performerID) + } if rf, ok := ret.Get(0).(func(context.Context, int) int); ok { r0 = rf(ctx, performerID) } else { r0 = ret.Get(0).(int) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, performerID) } else { @@ -84,13 +93,16 @@ func (_m *GroupReaderWriter) CountByStudioID(ctx context.Context, studioID int) ret := _m.Called(ctx, studioID) var r0 int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) (int, error)); ok { + return rf(ctx, studioID) + } if rf, ok := ret.Get(0).(func(context.Context, int) int); ok { r0 = rf(ctx, studioID) } else { r0 = ret.Get(0).(int) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, studioID) } else { @@ -133,6 +145,10 @@ func (_m *GroupReaderWriter) Find(ctx context.Context, id int) (*models.Group, e ret := _m.Called(ctx, id) var r0 *models.Group + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) (*models.Group, error)); ok { + return rf(ctx, id) + } if rf, ok := ret.Get(0).(func(context.Context, int) *models.Group); ok { r0 = rf(ctx, id) } else { @@ -141,7 +157,6 @@ func (_m *GroupReaderWriter) Find(ctx context.Context, id int) (*models.Group, e } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, id) } else { @@ -156,6 +171,10 @@ func (_m *GroupReaderWriter) FindByName(ctx context.Context, name string, nocase ret := _m.Called(ctx, name, nocase) var r0 *models.Group + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, bool) (*models.Group, error)); ok { + return rf(ctx, name, nocase) + } if rf, ok := ret.Get(0).(func(context.Context, string, bool) *models.Group); ok { r0 = rf(ctx, name, nocase) } else { @@ -164,7 +183,6 @@ func (_m *GroupReaderWriter) FindByName(ctx context.Context, name string, nocase } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, string, bool) error); ok { r1 = rf(ctx, name, nocase) } else { @@ -179,6 +197,10 @@ func (_m *GroupReaderWriter) FindByNames(ctx context.Context, names []string, no ret := _m.Called(ctx, names, nocase) var r0 []*models.Group + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, []string, bool) ([]*models.Group, error)); ok { + return rf(ctx, names, nocase) + } if rf, ok := ret.Get(0).(func(context.Context, []string, bool) []*models.Group); ok { r0 = rf(ctx, names, nocase) } else { @@ -187,7 +209,6 @@ func (_m *GroupReaderWriter) FindByNames(ctx context.Context, names []string, no } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, []string, bool) error); ok { r1 = rf(ctx, names, nocase) } else { @@ -202,6 +223,10 @@ func (_m *GroupReaderWriter) FindByPerformerID(ctx context.Context, performerID ret := _m.Called(ctx, performerID) var r0 []*models.Group + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) ([]*models.Group, error)); ok { + return rf(ctx, performerID) + } if rf, ok := ret.Get(0).(func(context.Context, int) []*models.Group); ok { r0 = rf(ctx, performerID) } else { @@ -210,7 +235,6 @@ func (_m *GroupReaderWriter) FindByPerformerID(ctx context.Context, performerID } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, performerID) } else { @@ -225,6 +249,10 @@ func (_m *GroupReaderWriter) FindByStudioID(ctx context.Context, studioID int) ( ret := _m.Called(ctx, studioID) var r0 []*models.Group + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) ([]*models.Group, error)); ok { + return rf(ctx, studioID) + } if rf, ok := ret.Get(0).(func(context.Context, int) []*models.Group); ok { r0 = rf(ctx, studioID) } else { @@ -233,7 +261,6 @@ func (_m *GroupReaderWriter) FindByStudioID(ctx context.Context, studioID int) ( } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, studioID) } else { @@ -248,6 +275,10 @@ func (_m *GroupReaderWriter) FindMany(ctx context.Context, ids []int) ([]*models ret := _m.Called(ctx, ids) var r0 []*models.Group + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, []int) ([]*models.Group, error)); ok { + return rf(ctx, ids) + } if rf, ok := ret.Get(0).(func(context.Context, []int) []*models.Group); ok { r0 = rf(ctx, ids) } else { @@ -256,7 +287,6 @@ func (_m *GroupReaderWriter) FindMany(ctx context.Context, ids []int) ([]*models } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, []int) error); ok { r1 = rf(ctx, ids) } else { @@ -266,11 +296,41 @@ func (_m *GroupReaderWriter) FindMany(ctx context.Context, ids []int) ([]*models return r0, r1 } +// GetAliases provides a mock function with given fields: ctx, relatedID +func (_m *GroupReaderWriter) GetAliases(ctx context.Context, relatedID int) ([]string, error) { + ret := _m.Called(ctx, relatedID) + + var r0 []string + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) ([]string, error)); ok { + return rf(ctx, relatedID) + } + if rf, ok := ret.Get(0).(func(context.Context, int) []string); ok { + r0 = rf(ctx, relatedID) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]string) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { + r1 = rf(ctx, relatedID) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // GetBackImage provides a mock function with given fields: ctx, groupID func (_m *GroupReaderWriter) GetBackImage(ctx context.Context, groupID int) ([]byte, error) { ret := _m.Called(ctx, groupID) var r0 []byte + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) ([]byte, error)); ok { + return rf(ctx, groupID) + } if rf, ok := ret.Get(0).(func(context.Context, int) []byte); ok { r0 = rf(ctx, groupID) } else { @@ -279,7 +339,6 @@ func (_m *GroupReaderWriter) GetBackImage(ctx context.Context, groupID int) ([]b } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, groupID) } else { @@ -294,6 +353,10 @@ func (_m *GroupReaderWriter) GetContainingGroupDescriptions(ctx context.Context, ret := _m.Called(ctx, id) var r0 []models.GroupIDDescription + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) ([]models.GroupIDDescription, error)); ok { + return rf(ctx, id) + } if rf, ok := ret.Get(0).(func(context.Context, int) []models.GroupIDDescription); ok { r0 = rf(ctx, id) } else { @@ -302,7 +365,6 @@ func (_m *GroupReaderWriter) GetContainingGroupDescriptions(ctx context.Context, } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, id) } else { @@ -317,6 +379,10 @@ func (_m *GroupReaderWriter) GetCustomFields(ctx context.Context, id int) (map[s ret := _m.Called(ctx, id) var r0 map[string]interface{} + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) (map[string]interface{}, error)); ok { + return rf(ctx, id) + } if rf, ok := ret.Get(0).(func(context.Context, int) map[string]interface{}); ok { r0 = rf(ctx, id) } else { @@ -325,7 +391,6 @@ func (_m *GroupReaderWriter) GetCustomFields(ctx context.Context, id int) (map[s } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, id) } else { @@ -340,6 +405,10 @@ func (_m *GroupReaderWriter) GetCustomFieldsBulk(ctx context.Context, ids []int) ret := _m.Called(ctx, ids) var r0 []models.CustomFieldMap + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, []int) ([]models.CustomFieldMap, error)); ok { + return rf(ctx, ids) + } if rf, ok := ret.Get(0).(func(context.Context, []int) []models.CustomFieldMap); ok { r0 = rf(ctx, ids) } else { @@ -348,7 +417,6 @@ func (_m *GroupReaderWriter) GetCustomFieldsBulk(ctx context.Context, ids []int) } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, []int) error); ok { r1 = rf(ctx, ids) } else { @@ -363,6 +431,10 @@ func (_m *GroupReaderWriter) GetFrontImage(ctx context.Context, groupID int) ([] ret := _m.Called(ctx, groupID) var r0 []byte + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) ([]byte, error)); ok { + return rf(ctx, groupID) + } if rf, ok := ret.Get(0).(func(context.Context, int) []byte); ok { r0 = rf(ctx, groupID) } else { @@ -371,7 +443,6 @@ func (_m *GroupReaderWriter) GetFrontImage(ctx context.Context, groupID int) ([] } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, groupID) } else { @@ -386,6 +457,10 @@ func (_m *GroupReaderWriter) GetSubGroupDescriptions(ctx context.Context, id int ret := _m.Called(ctx, id) var r0 []models.GroupIDDescription + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) ([]models.GroupIDDescription, error)); ok { + return rf(ctx, id) + } if rf, ok := ret.Get(0).(func(context.Context, int) []models.GroupIDDescription); ok { r0 = rf(ctx, id) } else { @@ -394,7 +469,6 @@ func (_m *GroupReaderWriter) GetSubGroupDescriptions(ctx context.Context, id int } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, id) } else { @@ -409,6 +483,10 @@ func (_m *GroupReaderWriter) GetTagIDs(ctx context.Context, relatedID int) ([]in ret := _m.Called(ctx, relatedID) var r0 []int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) ([]int, error)); ok { + return rf(ctx, relatedID) + } if rf, ok := ret.Get(0).(func(context.Context, int) []int); ok { r0 = rf(ctx, relatedID) } else { @@ -417,7 +495,6 @@ func (_m *GroupReaderWriter) GetTagIDs(ctx context.Context, relatedID int) ([]in } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, relatedID) } else { @@ -432,6 +509,10 @@ func (_m *GroupReaderWriter) GetURLs(ctx context.Context, relatedID int) ([]stri ret := _m.Called(ctx, relatedID) var r0 []string + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) ([]string, error)); ok { + return rf(ctx, relatedID) + } if rf, ok := ret.Get(0).(func(context.Context, int) []string); ok { r0 = rf(ctx, relatedID) } else { @@ -440,7 +521,6 @@ func (_m *GroupReaderWriter) GetURLs(ctx context.Context, relatedID int) ([]stri } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, relatedID) } else { @@ -455,13 +535,16 @@ func (_m *GroupReaderWriter) HasBackImage(ctx context.Context, groupID int) (boo ret := _m.Called(ctx, groupID) var r0 bool + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) (bool, error)); ok { + return rf(ctx, groupID) + } if rf, ok := ret.Get(0).(func(context.Context, int) bool); ok { r0 = rf(ctx, groupID) } else { r0 = ret.Get(0).(bool) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, groupID) } else { @@ -476,13 +559,16 @@ func (_m *GroupReaderWriter) HasFrontImage(ctx context.Context, groupID int) (bo ret := _m.Called(ctx, groupID) var r0 bool + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) (bool, error)); ok { + return rf(ctx, groupID) + } if rf, ok := ret.Get(0).(func(context.Context, int) bool); ok { r0 = rf(ctx, groupID) } else { r0 = ret.Get(0).(bool) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, groupID) } else { @@ -497,6 +583,11 @@ func (_m *GroupReaderWriter) Query(ctx context.Context, groupFilter *models.Grou ret := _m.Called(ctx, groupFilter, findFilter) var r0 []*models.Group + var r1 int + var r2 error + if rf, ok := ret.Get(0).(func(context.Context, *models.GroupFilterType, *models.FindFilterType) ([]*models.Group, int, error)); ok { + return rf(ctx, groupFilter, findFilter) + } if rf, ok := ret.Get(0).(func(context.Context, *models.GroupFilterType, *models.FindFilterType) []*models.Group); ok { r0 = rf(ctx, groupFilter, findFilter) } else { @@ -505,14 +596,12 @@ func (_m *GroupReaderWriter) Query(ctx context.Context, groupFilter *models.Grou } } - var r1 int if rf, ok := ret.Get(1).(func(context.Context, *models.GroupFilterType, *models.FindFilterType) int); ok { r1 = rf(ctx, groupFilter, findFilter) } else { r1 = ret.Get(1).(int) } - var r2 error if rf, ok := ret.Get(2).(func(context.Context, *models.GroupFilterType, *models.FindFilterType) error); ok { r2 = rf(ctx, groupFilter, findFilter) } else { @@ -527,13 +616,16 @@ func (_m *GroupReaderWriter) QueryCount(ctx context.Context, groupFilter *models ret := _m.Called(ctx, groupFilter, findFilter) var r0 int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *models.GroupFilterType, *models.FindFilterType) (int, error)); ok { + return rf(ctx, groupFilter, findFilter) + } if rf, ok := ret.Get(0).(func(context.Context, *models.GroupFilterType, *models.FindFilterType) int); ok { r0 = rf(ctx, groupFilter, findFilter) } else { r0 = ret.Get(0).(int) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, *models.GroupFilterType, *models.FindFilterType) error); ok { r1 = rf(ctx, groupFilter, findFilter) } else { @@ -604,6 +696,10 @@ func (_m *GroupReaderWriter) UpdatePartial(ctx context.Context, id int, updatedG ret := _m.Called(ctx, id, updatedGroup) var r0 *models.Group + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int, models.GroupPartial) (*models.Group, error)); ok { + return rf(ctx, id, updatedGroup) + } if rf, ok := ret.Get(0).(func(context.Context, int, models.GroupPartial) *models.Group); ok { r0 = rf(ctx, id, updatedGroup) } else { @@ -612,7 +708,6 @@ func (_m *GroupReaderWriter) UpdatePartial(ctx context.Context, id int, updatedG } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int, models.GroupPartial) error); ok { r1 = rf(ctx, id, updatedGroup) } else { @@ -621,3 +716,18 @@ func (_m *GroupReaderWriter) UpdatePartial(ctx context.Context, id int, updatedG return r0, r1 } + +type mockConstructorTestingTNewGroupReaderWriter interface { + mock.TestingT + Cleanup(func()) +} + +// NewGroupReaderWriter creates a new instance of GroupReaderWriter. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewGroupReaderWriter(t mockConstructorTestingTNewGroupReaderWriter) *GroupReaderWriter { + mock := &GroupReaderWriter{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/pkg/models/mocks/ImageReaderWriter.go b/pkg/models/mocks/ImageReaderWriter.go index 06026c474e..95cb8898a4 100644 --- a/pkg/models/mocks/ImageReaderWriter.go +++ b/pkg/models/mocks/ImageReaderWriter.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.10.0. DO NOT EDIT. +// Code generated by mockery v2.21.6. DO NOT EDIT. package mocks @@ -33,6 +33,10 @@ func (_m *ImageReaderWriter) All(ctx context.Context) ([]*models.Image, error) { ret := _m.Called(ctx) var r0 []*models.Image + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) ([]*models.Image, error)); ok { + return rf(ctx) + } if rf, ok := ret.Get(0).(func(context.Context) []*models.Image); ok { r0 = rf(ctx) } else { @@ -41,7 +45,6 @@ func (_m *ImageReaderWriter) All(ctx context.Context) ([]*models.Image, error) { } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context) error); ok { r1 = rf(ctx) } else { @@ -56,13 +59,16 @@ func (_m *ImageReaderWriter) Count(ctx context.Context) (int, error) { ret := _m.Called(ctx) var r0 int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) (int, error)); ok { + return rf(ctx) + } if rf, ok := ret.Get(0).(func(context.Context) int); ok { r0 = rf(ctx) } else { r0 = ret.Get(0).(int) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context) error); ok { r1 = rf(ctx) } else { @@ -77,13 +83,16 @@ func (_m *ImageReaderWriter) CountByFileID(ctx context.Context, fileID models.Fi ret := _m.Called(ctx, fileID) var r0 int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, models.FileID) (int, error)); ok { + return rf(ctx, fileID) + } if rf, ok := ret.Get(0).(func(context.Context, models.FileID) int); ok { r0 = rf(ctx, fileID) } else { r0 = ret.Get(0).(int) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, models.FileID) error); ok { r1 = rf(ctx, fileID) } else { @@ -98,13 +107,16 @@ func (_m *ImageReaderWriter) CountByGalleryID(ctx context.Context, galleryID int ret := _m.Called(ctx, galleryID) var r0 int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) (int, error)); ok { + return rf(ctx, galleryID) + } if rf, ok := ret.Get(0).(func(context.Context, int) int); ok { r0 = rf(ctx, galleryID) } else { r0 = ret.Get(0).(int) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, galleryID) } else { @@ -119,6 +131,10 @@ func (_m *ImageReaderWriter) CoverByGalleryID(ctx context.Context, galleryId int ret := _m.Called(ctx, galleryId) var r0 *models.Image + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) (*models.Image, error)); ok { + return rf(ctx, galleryId) + } if rf, ok := ret.Get(0).(func(context.Context, int) *models.Image); ok { r0 = rf(ctx, galleryId) } else { @@ -127,7 +143,6 @@ func (_m *ImageReaderWriter) CoverByGalleryID(ctx context.Context, galleryId int } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, galleryId) } else { @@ -156,13 +171,16 @@ func (_m *ImageReaderWriter) DecrementOCounter(ctx context.Context, id int) (int ret := _m.Called(ctx, id) var r0 int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) (int, error)); ok { + return rf(ctx, id) + } if rf, ok := ret.Get(0).(func(context.Context, int) int); ok { r0 = rf(ctx, id) } else { r0 = ret.Get(0).(int) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, id) } else { @@ -191,6 +209,10 @@ func (_m *ImageReaderWriter) Find(ctx context.Context, id int) (*models.Image, e ret := _m.Called(ctx, id) var r0 *models.Image + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) (*models.Image, error)); ok { + return rf(ctx, id) + } if rf, ok := ret.Get(0).(func(context.Context, int) *models.Image); ok { r0 = rf(ctx, id) } else { @@ -199,7 +221,6 @@ func (_m *ImageReaderWriter) Find(ctx context.Context, id int) (*models.Image, e } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, id) } else { @@ -214,6 +235,10 @@ func (_m *ImageReaderWriter) FindByChecksum(ctx context.Context, checksum string ret := _m.Called(ctx, checksum) var r0 []*models.Image + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string) ([]*models.Image, error)); ok { + return rf(ctx, checksum) + } if rf, ok := ret.Get(0).(func(context.Context, string) []*models.Image); ok { r0 = rf(ctx, checksum) } else { @@ -222,7 +247,6 @@ func (_m *ImageReaderWriter) FindByChecksum(ctx context.Context, checksum string } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { r1 = rf(ctx, checksum) } else { @@ -237,6 +261,10 @@ func (_m *ImageReaderWriter) FindByFileID(ctx context.Context, fileID models.Fil ret := _m.Called(ctx, fileID) var r0 []*models.Image + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, models.FileID) ([]*models.Image, error)); ok { + return rf(ctx, fileID) + } if rf, ok := ret.Get(0).(func(context.Context, models.FileID) []*models.Image); ok { r0 = rf(ctx, fileID) } else { @@ -245,7 +273,6 @@ func (_m *ImageReaderWriter) FindByFileID(ctx context.Context, fileID models.Fil } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, models.FileID) error); ok { r1 = rf(ctx, fileID) } else { @@ -260,6 +287,10 @@ func (_m *ImageReaderWriter) FindByFingerprints(ctx context.Context, fp []models ret := _m.Called(ctx, fp) var r0 []*models.Image + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, []models.Fingerprint) ([]*models.Image, error)); ok { + return rf(ctx, fp) + } if rf, ok := ret.Get(0).(func(context.Context, []models.Fingerprint) []*models.Image); ok { r0 = rf(ctx, fp) } else { @@ -268,7 +299,6 @@ func (_m *ImageReaderWriter) FindByFingerprints(ctx context.Context, fp []models } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, []models.Fingerprint) error); ok { r1 = rf(ctx, fp) } else { @@ -283,6 +313,10 @@ func (_m *ImageReaderWriter) FindByFolderID(ctx context.Context, fileID models.F ret := _m.Called(ctx, fileID) var r0 []*models.Image + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, models.FolderID) ([]*models.Image, error)); ok { + return rf(ctx, fileID) + } if rf, ok := ret.Get(0).(func(context.Context, models.FolderID) []*models.Image); ok { r0 = rf(ctx, fileID) } else { @@ -291,7 +325,6 @@ func (_m *ImageReaderWriter) FindByFolderID(ctx context.Context, fileID models.F } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, models.FolderID) error); ok { r1 = rf(ctx, fileID) } else { @@ -306,6 +339,10 @@ func (_m *ImageReaderWriter) FindByGalleryID(ctx context.Context, galleryID int) ret := _m.Called(ctx, galleryID) var r0 []*models.Image + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) ([]*models.Image, error)); ok { + return rf(ctx, galleryID) + } if rf, ok := ret.Get(0).(func(context.Context, int) []*models.Image); ok { r0 = rf(ctx, galleryID) } else { @@ -314,7 +351,6 @@ func (_m *ImageReaderWriter) FindByGalleryID(ctx context.Context, galleryID int) } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, galleryID) } else { @@ -329,6 +365,10 @@ func (_m *ImageReaderWriter) FindByGalleryIDIndex(ctx context.Context, galleryID ret := _m.Called(ctx, galleryID, index) var r0 *models.Image + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int, uint) (*models.Image, error)); ok { + return rf(ctx, galleryID, index) + } if rf, ok := ret.Get(0).(func(context.Context, int, uint) *models.Image); ok { r0 = rf(ctx, galleryID, index) } else { @@ -337,7 +377,6 @@ func (_m *ImageReaderWriter) FindByGalleryIDIndex(ctx context.Context, galleryID } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int, uint) error); ok { r1 = rf(ctx, galleryID, index) } else { @@ -352,6 +391,10 @@ func (_m *ImageReaderWriter) FindByZipFileID(ctx context.Context, zipFileID mode ret := _m.Called(ctx, zipFileID) var r0 []*models.Image + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, models.FileID) ([]*models.Image, error)); ok { + return rf(ctx, zipFileID) + } if rf, ok := ret.Get(0).(func(context.Context, models.FileID) []*models.Image); ok { r0 = rf(ctx, zipFileID) } else { @@ -360,7 +403,6 @@ func (_m *ImageReaderWriter) FindByZipFileID(ctx context.Context, zipFileID mode } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, models.FileID) error); ok { r1 = rf(ctx, zipFileID) } else { @@ -375,6 +417,10 @@ func (_m *ImageReaderWriter) FindMany(ctx context.Context, ids []int) ([]*models ret := _m.Called(ctx, ids) var r0 []*models.Image + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, []int) ([]*models.Image, error)); ok { + return rf(ctx, ids) + } if rf, ok := ret.Get(0).(func(context.Context, []int) []*models.Image); ok { r0 = rf(ctx, ids) } else { @@ -383,7 +429,6 @@ func (_m *ImageReaderWriter) FindMany(ctx context.Context, ids []int) ([]*models } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, []int) error); ok { r1 = rf(ctx, ids) } else { @@ -398,6 +443,10 @@ func (_m *ImageReaderWriter) GetCustomFields(ctx context.Context, id int) (map[s ret := _m.Called(ctx, id) var r0 map[string]interface{} + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) (map[string]interface{}, error)); ok { + return rf(ctx, id) + } if rf, ok := ret.Get(0).(func(context.Context, int) map[string]interface{}); ok { r0 = rf(ctx, id) } else { @@ -406,7 +455,6 @@ func (_m *ImageReaderWriter) GetCustomFields(ctx context.Context, id int) (map[s } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, id) } else { @@ -421,6 +469,10 @@ func (_m *ImageReaderWriter) GetCustomFieldsBulk(ctx context.Context, ids []int) ret := _m.Called(ctx, ids) var r0 []models.CustomFieldMap + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, []int) ([]models.CustomFieldMap, error)); ok { + return rf(ctx, ids) + } if rf, ok := ret.Get(0).(func(context.Context, []int) []models.CustomFieldMap); ok { r0 = rf(ctx, ids) } else { @@ -429,7 +481,6 @@ func (_m *ImageReaderWriter) GetCustomFieldsBulk(ctx context.Context, ids []int) } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, []int) error); ok { r1 = rf(ctx, ids) } else { @@ -444,6 +495,10 @@ func (_m *ImageReaderWriter) GetFiles(ctx context.Context, relatedID int) ([]mod ret := _m.Called(ctx, relatedID) var r0 []models.File + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) ([]models.File, error)); ok { + return rf(ctx, relatedID) + } if rf, ok := ret.Get(0).(func(context.Context, int) []models.File); ok { r0 = rf(ctx, relatedID) } else { @@ -452,7 +507,6 @@ func (_m *ImageReaderWriter) GetFiles(ctx context.Context, relatedID int) ([]mod } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, relatedID) } else { @@ -467,6 +521,10 @@ func (_m *ImageReaderWriter) GetGalleryIDs(ctx context.Context, relatedID int) ( ret := _m.Called(ctx, relatedID) var r0 []int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) ([]int, error)); ok { + return rf(ctx, relatedID) + } if rf, ok := ret.Get(0).(func(context.Context, int) []int); ok { r0 = rf(ctx, relatedID) } else { @@ -475,7 +533,6 @@ func (_m *ImageReaderWriter) GetGalleryIDs(ctx context.Context, relatedID int) ( } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, relatedID) } else { @@ -490,6 +547,10 @@ func (_m *ImageReaderWriter) GetManyFileIDs(ctx context.Context, ids []int) ([][ ret := _m.Called(ctx, ids) var r0 [][]models.FileID + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, []int) ([][]models.FileID, error)); ok { + return rf(ctx, ids) + } if rf, ok := ret.Get(0).(func(context.Context, []int) [][]models.FileID); ok { r0 = rf(ctx, ids) } else { @@ -498,7 +559,6 @@ func (_m *ImageReaderWriter) GetManyFileIDs(ctx context.Context, ids []int) ([][ } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, []int) error); ok { r1 = rf(ctx, ids) } else { @@ -513,6 +573,10 @@ func (_m *ImageReaderWriter) GetManyIDsByFileIDs(ctx context.Context, fileIDs [] ret := _m.Called(ctx, fileIDs) var r0 [][]int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, []models.FileID) ([][]int, error)); ok { + return rf(ctx, fileIDs) + } if rf, ok := ret.Get(0).(func(context.Context, []models.FileID) [][]int); ok { r0 = rf(ctx, fileIDs) } else { @@ -521,7 +585,6 @@ func (_m *ImageReaderWriter) GetManyIDsByFileIDs(ctx context.Context, fileIDs [] } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, []models.FileID) error); ok { r1 = rf(ctx, fileIDs) } else { @@ -536,6 +599,10 @@ func (_m *ImageReaderWriter) GetPerformerIDs(ctx context.Context, relatedID int) ret := _m.Called(ctx, relatedID) var r0 []int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) ([]int, error)); ok { + return rf(ctx, relatedID) + } if rf, ok := ret.Get(0).(func(context.Context, int) []int); ok { r0 = rf(ctx, relatedID) } else { @@ -544,7 +611,6 @@ func (_m *ImageReaderWriter) GetPerformerIDs(ctx context.Context, relatedID int) } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, relatedID) } else { @@ -559,6 +625,10 @@ func (_m *ImageReaderWriter) GetTagIDs(ctx context.Context, relatedID int) ([]in ret := _m.Called(ctx, relatedID) var r0 []int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) ([]int, error)); ok { + return rf(ctx, relatedID) + } if rf, ok := ret.Get(0).(func(context.Context, int) []int); ok { r0 = rf(ctx, relatedID) } else { @@ -567,7 +637,6 @@ func (_m *ImageReaderWriter) GetTagIDs(ctx context.Context, relatedID int) ([]in } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, relatedID) } else { @@ -582,6 +651,10 @@ func (_m *ImageReaderWriter) GetURLs(ctx context.Context, relatedID int) ([]stri ret := _m.Called(ctx, relatedID) var r0 []string + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) ([]string, error)); ok { + return rf(ctx, relatedID) + } if rf, ok := ret.Get(0).(func(context.Context, int) []string); ok { r0 = rf(ctx, relatedID) } else { @@ -590,7 +663,6 @@ func (_m *ImageReaderWriter) GetURLs(ctx context.Context, relatedID int) ([]stri } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, relatedID) } else { @@ -605,13 +677,16 @@ func (_m *ImageReaderWriter) IncrementOCounter(ctx context.Context, id int) (int ret := _m.Called(ctx, id) var r0 int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) (int, error)); ok { + return rf(ctx, id) + } if rf, ok := ret.Get(0).(func(context.Context, int) int); ok { r0 = rf(ctx, id) } else { r0 = ret.Get(0).(int) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, id) } else { @@ -626,13 +701,16 @@ func (_m *ImageReaderWriter) OCount(ctx context.Context) (int, error) { ret := _m.Called(ctx) var r0 int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) (int, error)); ok { + return rf(ctx) + } if rf, ok := ret.Get(0).(func(context.Context) int); ok { r0 = rf(ctx) } else { r0 = ret.Get(0).(int) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context) error); ok { r1 = rf(ctx) } else { @@ -647,13 +725,16 @@ func (_m *ImageReaderWriter) OCountByPerformerID(ctx context.Context, performerI ret := _m.Called(ctx, performerID) var r0 int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) (int, error)); ok { + return rf(ctx, performerID) + } if rf, ok := ret.Get(0).(func(context.Context, int) int); ok { r0 = rf(ctx, performerID) } else { r0 = ret.Get(0).(int) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, performerID) } else { @@ -668,13 +749,16 @@ func (_m *ImageReaderWriter) OCountByStudioID(ctx context.Context, studioID int, ret := _m.Called(ctx, studioID, depth) var r0 int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int, int) (int, error)); ok { + return rf(ctx, studioID, depth) + } if rf, ok := ret.Get(0).(func(context.Context, int, int) int); ok { r0 = rf(ctx, studioID, depth) } else { r0 = ret.Get(0).(int) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int, int) error); ok { r1 = rf(ctx, studioID, depth) } else { @@ -689,6 +773,10 @@ func (_m *ImageReaderWriter) Query(ctx context.Context, options models.ImageQuer ret := _m.Called(ctx, options) var r0 *models.ImageQueryResult + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, models.ImageQueryOptions) (*models.ImageQueryResult, error)); ok { + return rf(ctx, options) + } if rf, ok := ret.Get(0).(func(context.Context, models.ImageQueryOptions) *models.ImageQueryResult); ok { r0 = rf(ctx, options) } else { @@ -697,7 +785,6 @@ func (_m *ImageReaderWriter) Query(ctx context.Context, options models.ImageQuer } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, models.ImageQueryOptions) error); ok { r1 = rf(ctx, options) } else { @@ -712,13 +799,16 @@ func (_m *ImageReaderWriter) QueryCount(ctx context.Context, imageFilter *models ret := _m.Called(ctx, imageFilter, findFilter) var r0 int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *models.ImageFilterType, *models.FindFilterType) (int, error)); ok { + return rf(ctx, imageFilter, findFilter) + } if rf, ok := ret.Get(0).(func(context.Context, *models.ImageFilterType, *models.FindFilterType) int); ok { r0 = rf(ctx, imageFilter, findFilter) } else { r0 = ret.Get(0).(int) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, *models.ImageFilterType, *models.FindFilterType) error); ok { r1 = rf(ctx, imageFilter, findFilter) } else { @@ -747,13 +837,16 @@ func (_m *ImageReaderWriter) ResetOCounter(ctx context.Context, id int) (int, er ret := _m.Called(ctx, id) var r0 int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) (int, error)); ok { + return rf(ctx, id) + } if rf, ok := ret.Get(0).(func(context.Context, int) int); ok { r0 = rf(ctx, id) } else { r0 = ret.Get(0).(int) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, id) } else { @@ -782,13 +875,16 @@ func (_m *ImageReaderWriter) Size(ctx context.Context) (float64, error) { ret := _m.Called(ctx) var r0 float64 + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) (float64, error)); ok { + return rf(ctx) + } if rf, ok := ret.Get(0).(func(context.Context) float64); ok { r0 = rf(ctx) } else { r0 = ret.Get(0).(float64) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context) error); ok { r1 = rf(ctx) } else { @@ -817,6 +913,10 @@ func (_m *ImageReaderWriter) UpdatePartial(ctx context.Context, id int, partial ret := _m.Called(ctx, id, partial) var r0 *models.Image + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int, models.ImagePartial) (*models.Image, error)); ok { + return rf(ctx, id, partial) + } if rf, ok := ret.Get(0).(func(context.Context, int, models.ImagePartial) *models.Image); ok { r0 = rf(ctx, id, partial) } else { @@ -825,7 +925,6 @@ func (_m *ImageReaderWriter) UpdatePartial(ctx context.Context, id int, partial } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int, models.ImagePartial) error); ok { r1 = rf(ctx, id, partial) } else { @@ -862,3 +961,27 @@ func (_m *ImageReaderWriter) UpdateTags(ctx context.Context, imageID int, tagIDs return r0 } + +type mockConstructorTestingTNewImageReaderWriter interface { + mock.TestingT + Cleanup(func()) +} + +// NewImageReaderWriter creates a new instance of ImageReaderWriter. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewImageReaderWriter(t mockConstructorTestingTNewImageReaderWriter) *ImageReaderWriter { + mock := &ImageReaderWriter{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} +e mock and a cleanup function to assert the mocks expectations. +func NewImageReaderWriter(t mockConstructorTestingTNewImageReaderWriter) *ImageReaderWriter { + mock := &ImageReaderWriter{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/pkg/models/mocks/PerformerReaderWriter.go b/pkg/models/mocks/PerformerReaderWriter.go index 6487bc5a5e..86ee7d53d0 100644 --- a/pkg/models/mocks/PerformerReaderWriter.go +++ b/pkg/models/mocks/PerformerReaderWriter.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.10.0. DO NOT EDIT. +// Code generated by mockery v2.21.6. DO NOT EDIT. package mocks @@ -19,6 +19,10 @@ func (_m *PerformerReaderWriter) All(ctx context.Context) ([]*models.Performer, ret := _m.Called(ctx) var r0 []*models.Performer + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) ([]*models.Performer, error)); ok { + return rf(ctx) + } if rf, ok := ret.Get(0).(func(context.Context) []*models.Performer); ok { r0 = rf(ctx) } else { @@ -27,7 +31,6 @@ func (_m *PerformerReaderWriter) All(ctx context.Context) ([]*models.Performer, } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context) error); ok { r1 = rf(ctx) } else { @@ -42,13 +45,16 @@ func (_m *PerformerReaderWriter) Count(ctx context.Context) (int, error) { ret := _m.Called(ctx) var r0 int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) (int, error)); ok { + return rf(ctx) + } if rf, ok := ret.Get(0).(func(context.Context) int); ok { r0 = rf(ctx) } else { r0 = ret.Get(0).(int) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context) error); ok { r1 = rf(ctx) } else { @@ -63,13 +69,16 @@ func (_m *PerformerReaderWriter) CountByTagID(ctx context.Context, tagID int) (i ret := _m.Called(ctx, tagID) var r0 int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) (int, error)); ok { + return rf(ctx, tagID) + } if rf, ok := ret.Get(0).(func(context.Context, int) int); ok { r0 = rf(ctx, tagID) } else { r0 = ret.Get(0).(int) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, tagID) } else { @@ -112,6 +121,10 @@ func (_m *PerformerReaderWriter) Find(ctx context.Context, id int) (*models.Perf ret := _m.Called(ctx, id) var r0 *models.Performer + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) (*models.Performer, error)); ok { + return rf(ctx, id) + } if rf, ok := ret.Get(0).(func(context.Context, int) *models.Performer); ok { r0 = rf(ctx, id) } else { @@ -120,7 +133,6 @@ func (_m *PerformerReaderWriter) Find(ctx context.Context, id int) (*models.Perf } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, id) } else { @@ -135,6 +147,10 @@ func (_m *PerformerReaderWriter) FindByGalleryID(ctx context.Context, galleryID ret := _m.Called(ctx, galleryID) var r0 []*models.Performer + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) ([]*models.Performer, error)); ok { + return rf(ctx, galleryID) + } if rf, ok := ret.Get(0).(func(context.Context, int) []*models.Performer); ok { r0 = rf(ctx, galleryID) } else { @@ -143,7 +159,6 @@ func (_m *PerformerReaderWriter) FindByGalleryID(ctx context.Context, galleryID } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, galleryID) } else { @@ -158,6 +173,10 @@ func (_m *PerformerReaderWriter) FindByImageID(ctx context.Context, imageID int) ret := _m.Called(ctx, imageID) var r0 []*models.Performer + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) ([]*models.Performer, error)); ok { + return rf(ctx, imageID) + } if rf, ok := ret.Get(0).(func(context.Context, int) []*models.Performer); ok { r0 = rf(ctx, imageID) } else { @@ -166,7 +185,6 @@ func (_m *PerformerReaderWriter) FindByImageID(ctx context.Context, imageID int) } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, imageID) } else { @@ -181,6 +199,10 @@ func (_m *PerformerReaderWriter) FindByNames(ctx context.Context, names []string ret := _m.Called(ctx, names, nocase) var r0 []*models.Performer + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, []string, bool) ([]*models.Performer, error)); ok { + return rf(ctx, names, nocase) + } if rf, ok := ret.Get(0).(func(context.Context, []string, bool) []*models.Performer); ok { r0 = rf(ctx, names, nocase) } else { @@ -189,7 +211,6 @@ func (_m *PerformerReaderWriter) FindByNames(ctx context.Context, names []string } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, []string, bool) error); ok { r1 = rf(ctx, names, nocase) } else { @@ -204,6 +225,10 @@ func (_m *PerformerReaderWriter) FindBySceneID(ctx context.Context, sceneID int) ret := _m.Called(ctx, sceneID) var r0 []*models.Performer + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) ([]*models.Performer, error)); ok { + return rf(ctx, sceneID) + } if rf, ok := ret.Get(0).(func(context.Context, int) []*models.Performer); ok { r0 = rf(ctx, sceneID) } else { @@ -212,7 +237,6 @@ func (_m *PerformerReaderWriter) FindBySceneID(ctx context.Context, sceneID int) } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, sceneID) } else { @@ -227,6 +251,10 @@ func (_m *PerformerReaderWriter) FindByStashID(ctx context.Context, stashID mode ret := _m.Called(ctx, stashID) var r0 []*models.Performer + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, models.StashID) ([]*models.Performer, error)); ok { + return rf(ctx, stashID) + } if rf, ok := ret.Get(0).(func(context.Context, models.StashID) []*models.Performer); ok { r0 = rf(ctx, stashID) } else { @@ -235,7 +263,6 @@ func (_m *PerformerReaderWriter) FindByStashID(ctx context.Context, stashID mode } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, models.StashID) error); ok { r1 = rf(ctx, stashID) } else { @@ -250,6 +277,10 @@ func (_m *PerformerReaderWriter) FindByStashIDStatus(ctx context.Context, hasSta ret := _m.Called(ctx, hasStashID, stashboxEndpoint) var r0 []*models.Performer + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, bool, string) ([]*models.Performer, error)); ok { + return rf(ctx, hasStashID, stashboxEndpoint) + } if rf, ok := ret.Get(0).(func(context.Context, bool, string) []*models.Performer); ok { r0 = rf(ctx, hasStashID, stashboxEndpoint) } else { @@ -258,7 +289,6 @@ func (_m *PerformerReaderWriter) FindByStashIDStatus(ctx context.Context, hasSta } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, bool, string) error); ok { r1 = rf(ctx, hasStashID, stashboxEndpoint) } else { @@ -273,6 +303,10 @@ func (_m *PerformerReaderWriter) FindMany(ctx context.Context, ids []int) ([]*mo ret := _m.Called(ctx, ids) var r0 []*models.Performer + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, []int) ([]*models.Performer, error)); ok { + return rf(ctx, ids) + } if rf, ok := ret.Get(0).(func(context.Context, []int) []*models.Performer); ok { r0 = rf(ctx, ids) } else { @@ -281,7 +315,6 @@ func (_m *PerformerReaderWriter) FindMany(ctx context.Context, ids []int) ([]*mo } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, []int) error); ok { r1 = rf(ctx, ids) } else { @@ -296,6 +329,10 @@ func (_m *PerformerReaderWriter) GetAliases(ctx context.Context, relatedID int) ret := _m.Called(ctx, relatedID) var r0 []string + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) ([]string, error)); ok { + return rf(ctx, relatedID) + } if rf, ok := ret.Get(0).(func(context.Context, int) []string); ok { r0 = rf(ctx, relatedID) } else { @@ -304,7 +341,6 @@ func (_m *PerformerReaderWriter) GetAliases(ctx context.Context, relatedID int) } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, relatedID) } else { @@ -319,6 +355,10 @@ func (_m *PerformerReaderWriter) GetCustomFields(ctx context.Context, id int) (m ret := _m.Called(ctx, id) var r0 map[string]interface{} + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) (map[string]interface{}, error)); ok { + return rf(ctx, id) + } if rf, ok := ret.Get(0).(func(context.Context, int) map[string]interface{}); ok { r0 = rf(ctx, id) } else { @@ -327,7 +367,6 @@ func (_m *PerformerReaderWriter) GetCustomFields(ctx context.Context, id int) (m } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, id) } else { @@ -342,6 +381,10 @@ func (_m *PerformerReaderWriter) GetCustomFieldsBulk(ctx context.Context, ids [] ret := _m.Called(ctx, ids) var r0 []models.CustomFieldMap + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, []int) ([]models.CustomFieldMap, error)); ok { + return rf(ctx, ids) + } if rf, ok := ret.Get(0).(func(context.Context, []int) []models.CustomFieldMap); ok { r0 = rf(ctx, ids) } else { @@ -350,7 +393,6 @@ func (_m *PerformerReaderWriter) GetCustomFieldsBulk(ctx context.Context, ids [] } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, []int) error); ok { r1 = rf(ctx, ids) } else { @@ -365,6 +407,10 @@ func (_m *PerformerReaderWriter) GetImage(ctx context.Context, performerID int) ret := _m.Called(ctx, performerID) var r0 []byte + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) ([]byte, error)); ok { + return rf(ctx, performerID) + } if rf, ok := ret.Get(0).(func(context.Context, int) []byte); ok { r0 = rf(ctx, performerID) } else { @@ -373,7 +419,6 @@ func (_m *PerformerReaderWriter) GetImage(ctx context.Context, performerID int) } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, performerID) } else { @@ -388,6 +433,10 @@ func (_m *PerformerReaderWriter) GetStashIDs(ctx context.Context, relatedID int) ret := _m.Called(ctx, relatedID) var r0 []models.StashID + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) ([]models.StashID, error)); ok { + return rf(ctx, relatedID) + } if rf, ok := ret.Get(0).(func(context.Context, int) []models.StashID); ok { r0 = rf(ctx, relatedID) } else { @@ -396,7 +445,6 @@ func (_m *PerformerReaderWriter) GetStashIDs(ctx context.Context, relatedID int) } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, relatedID) } else { @@ -411,6 +459,10 @@ func (_m *PerformerReaderWriter) GetTagIDs(ctx context.Context, relatedID int) ( ret := _m.Called(ctx, relatedID) var r0 []int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) ([]int, error)); ok { + return rf(ctx, relatedID) + } if rf, ok := ret.Get(0).(func(context.Context, int) []int); ok { r0 = rf(ctx, relatedID) } else { @@ -419,7 +471,6 @@ func (_m *PerformerReaderWriter) GetTagIDs(ctx context.Context, relatedID int) ( } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, relatedID) } else { @@ -434,6 +485,10 @@ func (_m *PerformerReaderWriter) GetURLs(ctx context.Context, relatedID int) ([] ret := _m.Called(ctx, relatedID) var r0 []string + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) ([]string, error)); ok { + return rf(ctx, relatedID) + } if rf, ok := ret.Get(0).(func(context.Context, int) []string); ok { r0 = rf(ctx, relatedID) } else { @@ -442,7 +497,6 @@ func (_m *PerformerReaderWriter) GetURLs(ctx context.Context, relatedID int) ([] } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, relatedID) } else { @@ -457,13 +511,16 @@ func (_m *PerformerReaderWriter) HasImage(ctx context.Context, performerID int) ret := _m.Called(ctx, performerID) var r0 bool + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) (bool, error)); ok { + return rf(ctx, performerID) + } if rf, ok := ret.Get(0).(func(context.Context, int) bool); ok { r0 = rf(ctx, performerID) } else { r0 = ret.Get(0).(bool) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, performerID) } else { @@ -492,6 +549,11 @@ func (_m *PerformerReaderWriter) Query(ctx context.Context, performerFilter *mod ret := _m.Called(ctx, performerFilter, findFilter) var r0 []*models.Performer + var r1 int + var r2 error + if rf, ok := ret.Get(0).(func(context.Context, *models.PerformerFilterType, *models.FindFilterType) ([]*models.Performer, int, error)); ok { + return rf(ctx, performerFilter, findFilter) + } if rf, ok := ret.Get(0).(func(context.Context, *models.PerformerFilterType, *models.FindFilterType) []*models.Performer); ok { r0 = rf(ctx, performerFilter, findFilter) } else { @@ -500,14 +562,12 @@ func (_m *PerformerReaderWriter) Query(ctx context.Context, performerFilter *mod } } - var r1 int if rf, ok := ret.Get(1).(func(context.Context, *models.PerformerFilterType, *models.FindFilterType) int); ok { r1 = rf(ctx, performerFilter, findFilter) } else { r1 = ret.Get(1).(int) } - var r2 error if rf, ok := ret.Get(2).(func(context.Context, *models.PerformerFilterType, *models.FindFilterType) error); ok { r2 = rf(ctx, performerFilter, findFilter) } else { @@ -522,13 +582,16 @@ func (_m *PerformerReaderWriter) QueryCount(ctx context.Context, performerFilter ret := _m.Called(ctx, performerFilter, findFilter) var r0 int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *models.PerformerFilterType, *models.FindFilterType) (int, error)); ok { + return rf(ctx, performerFilter, findFilter) + } if rf, ok := ret.Get(0).(func(context.Context, *models.PerformerFilterType, *models.FindFilterType) int); ok { r0 = rf(ctx, performerFilter, findFilter) } else { r0 = ret.Get(0).(int) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, *models.PerformerFilterType, *models.FindFilterType) error); ok { r1 = rf(ctx, performerFilter, findFilter) } else { @@ -543,6 +606,10 @@ func (_m *PerformerReaderWriter) QueryForAutoTag(ctx context.Context, words []st ret := _m.Called(ctx, words) var r0 []*models.Performer + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, []string) ([]*models.Performer, error)); ok { + return rf(ctx, words) + } if rf, ok := ret.Get(0).(func(context.Context, []string) []*models.Performer); ok { r0 = rf(ctx, words) } else { @@ -551,7 +618,6 @@ func (_m *PerformerReaderWriter) QueryForAutoTag(ctx context.Context, words []st } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, []string) error); ok { r1 = rf(ctx, words) } else { @@ -594,6 +660,10 @@ func (_m *PerformerReaderWriter) UpdatePartial(ctx context.Context, id int, upda ret := _m.Called(ctx, id, updatedPerformer) var r0 *models.Performer + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int, models.PerformerPartial) (*models.Performer, error)); ok { + return rf(ctx, id, updatedPerformer) + } if rf, ok := ret.Get(0).(func(context.Context, int, models.PerformerPartial) *models.Performer); ok { r0 = rf(ctx, id, updatedPerformer) } else { @@ -602,7 +672,6 @@ func (_m *PerformerReaderWriter) UpdatePartial(ctx context.Context, id int, upda } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int, models.PerformerPartial) error); ok { r1 = rf(ctx, id, updatedPerformer) } else { @@ -611,3 +680,18 @@ func (_m *PerformerReaderWriter) UpdatePartial(ctx context.Context, id int, upda return r0, r1 } + +type mockConstructorTestingTNewPerformerReaderWriter interface { + mock.TestingT + Cleanup(func()) +} + +// NewPerformerReaderWriter creates a new instance of PerformerReaderWriter. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewPerformerReaderWriter(t mockConstructorTestingTNewPerformerReaderWriter) *PerformerReaderWriter { + mock := &PerformerReaderWriter{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/pkg/models/mocks/SavedFilterReaderWriter.go b/pkg/models/mocks/SavedFilterReaderWriter.go index 53f5cb0e50..ca8c167439 100644 --- a/pkg/models/mocks/SavedFilterReaderWriter.go +++ b/pkg/models/mocks/SavedFilterReaderWriter.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.10.0. DO NOT EDIT. +// Code generated by mockery v2.21.6. DO NOT EDIT. package mocks @@ -19,6 +19,10 @@ func (_m *SavedFilterReaderWriter) All(ctx context.Context) ([]*models.SavedFilt ret := _m.Called(ctx) var r0 []*models.SavedFilter + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) ([]*models.SavedFilter, error)); ok { + return rf(ctx) + } if rf, ok := ret.Get(0).(func(context.Context) []*models.SavedFilter); ok { r0 = rf(ctx) } else { @@ -27,7 +31,6 @@ func (_m *SavedFilterReaderWriter) All(ctx context.Context) ([]*models.SavedFilt } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context) error); ok { r1 = rf(ctx) } else { @@ -70,6 +73,10 @@ func (_m *SavedFilterReaderWriter) Find(ctx context.Context, id int) (*models.Sa ret := _m.Called(ctx, id) var r0 *models.SavedFilter + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) (*models.SavedFilter, error)); ok { + return rf(ctx, id) + } if rf, ok := ret.Get(0).(func(context.Context, int) *models.SavedFilter); ok { r0 = rf(ctx, id) } else { @@ -78,7 +85,6 @@ func (_m *SavedFilterReaderWriter) Find(ctx context.Context, id int) (*models.Sa } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, id) } else { @@ -93,6 +99,10 @@ func (_m *SavedFilterReaderWriter) FindByMode(ctx context.Context, mode models.F ret := _m.Called(ctx, mode) var r0 []*models.SavedFilter + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, models.FilterMode) ([]*models.SavedFilter, error)); ok { + return rf(ctx, mode) + } if rf, ok := ret.Get(0).(func(context.Context, models.FilterMode) []*models.SavedFilter); ok { r0 = rf(ctx, mode) } else { @@ -101,7 +111,6 @@ func (_m *SavedFilterReaderWriter) FindByMode(ctx context.Context, mode models.F } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, models.FilterMode) error); ok { r1 = rf(ctx, mode) } else { @@ -116,6 +125,10 @@ func (_m *SavedFilterReaderWriter) FindMany(ctx context.Context, ids []int, igno ret := _m.Called(ctx, ids, ignoreNotFound) var r0 []*models.SavedFilter + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, []int, bool) ([]*models.SavedFilter, error)); ok { + return rf(ctx, ids, ignoreNotFound) + } if rf, ok := ret.Get(0).(func(context.Context, []int, bool) []*models.SavedFilter); ok { r0 = rf(ctx, ids, ignoreNotFound) } else { @@ -124,7 +137,6 @@ func (_m *SavedFilterReaderWriter) FindMany(ctx context.Context, ids []int, igno } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, []int, bool) error); ok { r1 = rf(ctx, ids, ignoreNotFound) } else { @@ -147,3 +159,18 @@ func (_m *SavedFilterReaderWriter) Update(ctx context.Context, obj *models.Saved return r0 } + +type mockConstructorTestingTNewSavedFilterReaderWriter interface { + mock.TestingT + Cleanup(func()) +} + +// NewSavedFilterReaderWriter creates a new instance of SavedFilterReaderWriter. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewSavedFilterReaderWriter(t mockConstructorTestingTNewSavedFilterReaderWriter) *SavedFilterReaderWriter { + mock := &SavedFilterReaderWriter{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/pkg/models/mocks/SceneMarkerReaderWriter.go b/pkg/models/mocks/SceneMarkerReaderWriter.go index 4b4fa6d2ce..6439717227 100644 --- a/pkg/models/mocks/SceneMarkerReaderWriter.go +++ b/pkg/models/mocks/SceneMarkerReaderWriter.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.10.0. DO NOT EDIT. +// Code generated by mockery v2.21.6. DO NOT EDIT. package mocks @@ -19,6 +19,10 @@ func (_m *SceneMarkerReaderWriter) All(ctx context.Context) ([]*models.SceneMark ret := _m.Called(ctx) var r0 []*models.SceneMarker + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) ([]*models.SceneMarker, error)); ok { + return rf(ctx) + } if rf, ok := ret.Get(0).(func(context.Context) []*models.SceneMarker); ok { r0 = rf(ctx) } else { @@ -27,7 +31,6 @@ func (_m *SceneMarkerReaderWriter) All(ctx context.Context) ([]*models.SceneMark } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context) error); ok { r1 = rf(ctx) } else { @@ -42,13 +45,16 @@ func (_m *SceneMarkerReaderWriter) Count(ctx context.Context) (int, error) { ret := _m.Called(ctx) var r0 int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) (int, error)); ok { + return rf(ctx) + } if rf, ok := ret.Get(0).(func(context.Context) int); ok { r0 = rf(ctx) } else { r0 = ret.Get(0).(int) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context) error); ok { r1 = rf(ctx) } else { @@ -63,13 +69,16 @@ func (_m *SceneMarkerReaderWriter) CountByTagID(ctx context.Context, tagID int) ret := _m.Called(ctx, tagID) var r0 int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) (int, error)); ok { + return rf(ctx, tagID) + } if rf, ok := ret.Get(0).(func(context.Context, int) int); ok { r0 = rf(ctx, tagID) } else { r0 = ret.Get(0).(int) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, tagID) } else { @@ -112,6 +121,10 @@ func (_m *SceneMarkerReaderWriter) Find(ctx context.Context, id int) (*models.Sc ret := _m.Called(ctx, id) var r0 *models.SceneMarker + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) (*models.SceneMarker, error)); ok { + return rf(ctx, id) + } if rf, ok := ret.Get(0).(func(context.Context, int) *models.SceneMarker); ok { r0 = rf(ctx, id) } else { @@ -120,7 +133,6 @@ func (_m *SceneMarkerReaderWriter) Find(ctx context.Context, id int) (*models.Sc } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, id) } else { @@ -135,6 +147,10 @@ func (_m *SceneMarkerReaderWriter) FindBySceneID(ctx context.Context, sceneID in ret := _m.Called(ctx, sceneID) var r0 []*models.SceneMarker + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) ([]*models.SceneMarker, error)); ok { + return rf(ctx, sceneID) + } if rf, ok := ret.Get(0).(func(context.Context, int) []*models.SceneMarker); ok { r0 = rf(ctx, sceneID) } else { @@ -143,7 +159,6 @@ func (_m *SceneMarkerReaderWriter) FindBySceneID(ctx context.Context, sceneID in } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, sceneID) } else { @@ -158,6 +173,10 @@ func (_m *SceneMarkerReaderWriter) FindMany(ctx context.Context, ids []int) ([]* ret := _m.Called(ctx, ids) var r0 []*models.SceneMarker + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, []int) ([]*models.SceneMarker, error)); ok { + return rf(ctx, ids) + } if rf, ok := ret.Get(0).(func(context.Context, []int) []*models.SceneMarker); ok { r0 = rf(ctx, ids) } else { @@ -166,7 +185,6 @@ func (_m *SceneMarkerReaderWriter) FindMany(ctx context.Context, ids []int) ([]* } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, []int) error); ok { r1 = rf(ctx, ids) } else { @@ -181,6 +199,10 @@ func (_m *SceneMarkerReaderWriter) GetMarkerStrings(ctx context.Context, q *stri ret := _m.Called(ctx, q, sort) var r0 []*models.MarkerStringsResultType + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *string, *string) ([]*models.MarkerStringsResultType, error)); ok { + return rf(ctx, q, sort) + } if rf, ok := ret.Get(0).(func(context.Context, *string, *string) []*models.MarkerStringsResultType); ok { r0 = rf(ctx, q, sort) } else { @@ -189,7 +211,6 @@ func (_m *SceneMarkerReaderWriter) GetMarkerStrings(ctx context.Context, q *stri } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, *string, *string) error); ok { r1 = rf(ctx, q, sort) } else { @@ -204,6 +225,10 @@ func (_m *SceneMarkerReaderWriter) GetTagIDs(ctx context.Context, relatedID int) ret := _m.Called(ctx, relatedID) var r0 []int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) ([]int, error)); ok { + return rf(ctx, relatedID) + } if rf, ok := ret.Get(0).(func(context.Context, int) []int); ok { r0 = rf(ctx, relatedID) } else { @@ -212,7 +237,6 @@ func (_m *SceneMarkerReaderWriter) GetTagIDs(ctx context.Context, relatedID int) } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, relatedID) } else { @@ -227,6 +251,11 @@ func (_m *SceneMarkerReaderWriter) Query(ctx context.Context, sceneMarkerFilter ret := _m.Called(ctx, sceneMarkerFilter, findFilter) var r0 []*models.SceneMarker + var r1 int + var r2 error + if rf, ok := ret.Get(0).(func(context.Context, *models.SceneMarkerFilterType, *models.FindFilterType) ([]*models.SceneMarker, int, error)); ok { + return rf(ctx, sceneMarkerFilter, findFilter) + } if rf, ok := ret.Get(0).(func(context.Context, *models.SceneMarkerFilterType, *models.FindFilterType) []*models.SceneMarker); ok { r0 = rf(ctx, sceneMarkerFilter, findFilter) } else { @@ -235,14 +264,12 @@ func (_m *SceneMarkerReaderWriter) Query(ctx context.Context, sceneMarkerFilter } } - var r1 int if rf, ok := ret.Get(1).(func(context.Context, *models.SceneMarkerFilterType, *models.FindFilterType) int); ok { r1 = rf(ctx, sceneMarkerFilter, findFilter) } else { r1 = ret.Get(1).(int) } - var r2 error if rf, ok := ret.Get(2).(func(context.Context, *models.SceneMarkerFilterType, *models.FindFilterType) error); ok { r2 = rf(ctx, sceneMarkerFilter, findFilter) } else { @@ -257,13 +284,16 @@ func (_m *SceneMarkerReaderWriter) QueryCount(ctx context.Context, sceneMarkerFi ret := _m.Called(ctx, sceneMarkerFilter, findFilter) var r0 int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *models.SceneMarkerFilterType, *models.FindFilterType) (int, error)); ok { + return rf(ctx, sceneMarkerFilter, findFilter) + } if rf, ok := ret.Get(0).(func(context.Context, *models.SceneMarkerFilterType, *models.FindFilterType) int); ok { r0 = rf(ctx, sceneMarkerFilter, findFilter) } else { r0 = ret.Get(0).(int) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, *models.SceneMarkerFilterType, *models.FindFilterType) error); ok { r1 = rf(ctx, sceneMarkerFilter, findFilter) } else { @@ -292,6 +322,10 @@ func (_m *SceneMarkerReaderWriter) UpdatePartial(ctx context.Context, id int, up ret := _m.Called(ctx, id, updatedSceneMarker) var r0 *models.SceneMarker + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int, models.SceneMarkerPartial) (*models.SceneMarker, error)); ok { + return rf(ctx, id, updatedSceneMarker) + } if rf, ok := ret.Get(0).(func(context.Context, int, models.SceneMarkerPartial) *models.SceneMarker); ok { r0 = rf(ctx, id, updatedSceneMarker) } else { @@ -300,7 +334,6 @@ func (_m *SceneMarkerReaderWriter) UpdatePartial(ctx context.Context, id int, up } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int, models.SceneMarkerPartial) error); ok { r1 = rf(ctx, id, updatedSceneMarker) } else { @@ -329,6 +362,10 @@ func (_m *SceneMarkerReaderWriter) Wall(ctx context.Context, q *string) ([]*mode ret := _m.Called(ctx, q) var r0 []*models.SceneMarker + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *string) ([]*models.SceneMarker, error)); ok { + return rf(ctx, q) + } if rf, ok := ret.Get(0).(func(context.Context, *string) []*models.SceneMarker); ok { r0 = rf(ctx, q) } else { @@ -337,7 +374,6 @@ func (_m *SceneMarkerReaderWriter) Wall(ctx context.Context, q *string) ([]*mode } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, *string) error); ok { r1 = rf(ctx, q) } else { @@ -346,3 +382,18 @@ func (_m *SceneMarkerReaderWriter) Wall(ctx context.Context, q *string) ([]*mode return r0, r1 } + +type mockConstructorTestingTNewSceneMarkerReaderWriter interface { + mock.TestingT + Cleanup(func()) +} + +// NewSceneMarkerReaderWriter creates a new instance of SceneMarkerReaderWriter. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewSceneMarkerReaderWriter(t mockConstructorTestingTNewSceneMarkerReaderWriter) *SceneMarkerReaderWriter { + mock := &SceneMarkerReaderWriter{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/pkg/models/mocks/SceneReaderWriter.go b/pkg/models/mocks/SceneReaderWriter.go index 6d4e788a30..cebd1e771b 100644 --- a/pkg/models/mocks/SceneReaderWriter.go +++ b/pkg/models/mocks/SceneReaderWriter.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.10.0. DO NOT EDIT. +// Code generated by mockery v2.21.6. DO NOT EDIT. package mocks @@ -49,6 +49,10 @@ func (_m *SceneReaderWriter) AddO(ctx context.Context, id int, dates []time.Time ret := _m.Called(ctx, id, dates) var r0 []time.Time + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int, []time.Time) ([]time.Time, error)); ok { + return rf(ctx, id, dates) + } if rf, ok := ret.Get(0).(func(context.Context, int, []time.Time) []time.Time); ok { r0 = rf(ctx, id, dates) } else { @@ -57,7 +61,6 @@ func (_m *SceneReaderWriter) AddO(ctx context.Context, id int, dates []time.Time } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int, []time.Time) error); ok { r1 = rf(ctx, id, dates) } else { @@ -72,6 +75,10 @@ func (_m *SceneReaderWriter) AddViews(ctx context.Context, sceneID int, dates [] ret := _m.Called(ctx, sceneID, dates) var r0 []time.Time + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int, []time.Time) ([]time.Time, error)); ok { + return rf(ctx, sceneID, dates) + } if rf, ok := ret.Get(0).(func(context.Context, int, []time.Time) []time.Time); ok { r0 = rf(ctx, sceneID, dates) } else { @@ -80,7 +87,6 @@ func (_m *SceneReaderWriter) AddViews(ctx context.Context, sceneID int, dates [] } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int, []time.Time) error); ok { r1 = rf(ctx, sceneID, dates) } else { @@ -95,6 +101,10 @@ func (_m *SceneReaderWriter) All(ctx context.Context) ([]*models.Scene, error) { ret := _m.Called(ctx) var r0 []*models.Scene + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) ([]*models.Scene, error)); ok { + return rf(ctx) + } if rf, ok := ret.Get(0).(func(context.Context) []*models.Scene); ok { r0 = rf(ctx) } else { @@ -103,7 +113,6 @@ func (_m *SceneReaderWriter) All(ctx context.Context) ([]*models.Scene, error) { } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context) error); ok { r1 = rf(ctx) } else { @@ -132,13 +141,16 @@ func (_m *SceneReaderWriter) Count(ctx context.Context) (int, error) { ret := _m.Called(ctx) var r0 int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) (int, error)); ok { + return rf(ctx) + } if rf, ok := ret.Get(0).(func(context.Context) int); ok { r0 = rf(ctx) } else { r0 = ret.Get(0).(int) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context) error); ok { r1 = rf(ctx) } else { @@ -153,13 +165,16 @@ func (_m *SceneReaderWriter) CountAllViews(ctx context.Context) (int, error) { ret := _m.Called(ctx) var r0 int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) (int, error)); ok { + return rf(ctx) + } if rf, ok := ret.Get(0).(func(context.Context) int); ok { r0 = rf(ctx) } else { r0 = ret.Get(0).(int) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context) error); ok { r1 = rf(ctx) } else { @@ -174,13 +189,16 @@ func (_m *SceneReaderWriter) CountByFileID(ctx context.Context, fileID models.Fi ret := _m.Called(ctx, fileID) var r0 int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, models.FileID) (int, error)); ok { + return rf(ctx, fileID) + } if rf, ok := ret.Get(0).(func(context.Context, models.FileID) int); ok { r0 = rf(ctx, fileID) } else { r0 = ret.Get(0).(int) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, models.FileID) error); ok { r1 = rf(ctx, fileID) } else { @@ -195,13 +213,16 @@ func (_m *SceneReaderWriter) CountByPerformerID(ctx context.Context, performerID ret := _m.Called(ctx, performerID) var r0 int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) (int, error)); ok { + return rf(ctx, performerID) + } if rf, ok := ret.Get(0).(func(context.Context, int) int); ok { r0 = rf(ctx, performerID) } else { r0 = ret.Get(0).(int) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, performerID) } else { @@ -216,13 +237,16 @@ func (_m *SceneReaderWriter) CountMissingChecksum(ctx context.Context) (int, err ret := _m.Called(ctx) var r0 int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) (int, error)); ok { + return rf(ctx) + } if rf, ok := ret.Get(0).(func(context.Context) int); ok { r0 = rf(ctx) } else { r0 = ret.Get(0).(int) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context) error); ok { r1 = rf(ctx) } else { @@ -237,13 +261,16 @@ func (_m *SceneReaderWriter) CountMissingOSHash(ctx context.Context) (int, error ret := _m.Called(ctx) var r0 int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) (int, error)); ok { + return rf(ctx) + } if rf, ok := ret.Get(0).(func(context.Context) int); ok { r0 = rf(ctx) } else { r0 = ret.Get(0).(int) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context) error); ok { r1 = rf(ctx) } else { @@ -258,13 +285,16 @@ func (_m *SceneReaderWriter) CountUniqueViews(ctx context.Context) (int, error) ret := _m.Called(ctx) var r0 int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) (int, error)); ok { + return rf(ctx) + } if rf, ok := ret.Get(0).(func(context.Context) int); ok { r0 = rf(ctx) } else { r0 = ret.Get(0).(int) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context) error); ok { r1 = rf(ctx) } else { @@ -279,13 +309,16 @@ func (_m *SceneReaderWriter) CountViews(ctx context.Context, id int) (int, error ret := _m.Called(ctx, id) var r0 int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) (int, error)); ok { + return rf(ctx, id) + } if rf, ok := ret.Get(0).(func(context.Context, int) int); ok { r0 = rf(ctx, id) } else { r0 = ret.Get(0).(int) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, id) } else { @@ -314,13 +347,16 @@ func (_m *SceneReaderWriter) DeleteAllViews(ctx context.Context, id int) (int, e ret := _m.Called(ctx, id) var r0 int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) (int, error)); ok { + return rf(ctx, id) + } if rf, ok := ret.Get(0).(func(context.Context, int) int); ok { r0 = rf(ctx, id) } else { r0 = ret.Get(0).(int) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, id) } else { @@ -335,6 +371,10 @@ func (_m *SceneReaderWriter) DeleteO(ctx context.Context, id int, dates []time.T ret := _m.Called(ctx, id, dates) var r0 []time.Time + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int, []time.Time) ([]time.Time, error)); ok { + return rf(ctx, id, dates) + } if rf, ok := ret.Get(0).(func(context.Context, int, []time.Time) []time.Time); ok { r0 = rf(ctx, id, dates) } else { @@ -343,7 +383,6 @@ func (_m *SceneReaderWriter) DeleteO(ctx context.Context, id int, dates []time.T } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int, []time.Time) error); ok { r1 = rf(ctx, id, dates) } else { @@ -358,6 +397,10 @@ func (_m *SceneReaderWriter) DeleteViews(ctx context.Context, id int, dates []ti ret := _m.Called(ctx, id, dates) var r0 []time.Time + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int, []time.Time) ([]time.Time, error)); ok { + return rf(ctx, id, dates) + } if rf, ok := ret.Get(0).(func(context.Context, int, []time.Time) []time.Time); ok { r0 = rf(ctx, id, dates) } else { @@ -366,7 +409,6 @@ func (_m *SceneReaderWriter) DeleteViews(ctx context.Context, id int, dates []ti } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int, []time.Time) error); ok { r1 = rf(ctx, id, dates) } else { @@ -395,13 +437,16 @@ func (_m *SceneReaderWriter) Duration(ctx context.Context) (float64, error) { ret := _m.Called(ctx) var r0 float64 + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) (float64, error)); ok { + return rf(ctx) + } if rf, ok := ret.Get(0).(func(context.Context) float64); ok { r0 = rf(ctx) } else { r0 = ret.Get(0).(float64) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context) error); ok { r1 = rf(ctx) } else { @@ -416,6 +461,10 @@ func (_m *SceneReaderWriter) Find(ctx context.Context, id int) (*models.Scene, e ret := _m.Called(ctx, id) var r0 *models.Scene + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) (*models.Scene, error)); ok { + return rf(ctx, id) + } if rf, ok := ret.Get(0).(func(context.Context, int) *models.Scene); ok { r0 = rf(ctx, id) } else { @@ -424,7 +473,6 @@ func (_m *SceneReaderWriter) Find(ctx context.Context, id int) (*models.Scene, e } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, id) } else { @@ -439,6 +487,10 @@ func (_m *SceneReaderWriter) FindByChecksum(ctx context.Context, checksum string ret := _m.Called(ctx, checksum) var r0 []*models.Scene + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string) ([]*models.Scene, error)); ok { + return rf(ctx, checksum) + } if rf, ok := ret.Get(0).(func(context.Context, string) []*models.Scene); ok { r0 = rf(ctx, checksum) } else { @@ -447,7 +499,6 @@ func (_m *SceneReaderWriter) FindByChecksum(ctx context.Context, checksum string } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { r1 = rf(ctx, checksum) } else { @@ -462,6 +513,10 @@ func (_m *SceneReaderWriter) FindByFileID(ctx context.Context, fileID models.Fil ret := _m.Called(ctx, fileID) var r0 []*models.Scene + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, models.FileID) ([]*models.Scene, error)); ok { + return rf(ctx, fileID) + } if rf, ok := ret.Get(0).(func(context.Context, models.FileID) []*models.Scene); ok { r0 = rf(ctx, fileID) } else { @@ -470,7 +525,6 @@ func (_m *SceneReaderWriter) FindByFileID(ctx context.Context, fileID models.Fil } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, models.FileID) error); ok { r1 = rf(ctx, fileID) } else { @@ -485,6 +539,10 @@ func (_m *SceneReaderWriter) FindByFingerprints(ctx context.Context, fp []models ret := _m.Called(ctx, fp) var r0 []*models.Scene + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, []models.Fingerprint) ([]*models.Scene, error)); ok { + return rf(ctx, fp) + } if rf, ok := ret.Get(0).(func(context.Context, []models.Fingerprint) []*models.Scene); ok { r0 = rf(ctx, fp) } else { @@ -493,7 +551,6 @@ func (_m *SceneReaderWriter) FindByFingerprints(ctx context.Context, fp []models } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, []models.Fingerprint) error); ok { r1 = rf(ctx, fp) } else { @@ -508,6 +565,10 @@ func (_m *SceneReaderWriter) FindByGalleryID(ctx context.Context, performerID in ret := _m.Called(ctx, performerID) var r0 []*models.Scene + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) ([]*models.Scene, error)); ok { + return rf(ctx, performerID) + } if rf, ok := ret.Get(0).(func(context.Context, int) []*models.Scene); ok { r0 = rf(ctx, performerID) } else { @@ -516,7 +577,6 @@ func (_m *SceneReaderWriter) FindByGalleryID(ctx context.Context, performerID in } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, performerID) } else { @@ -531,6 +591,10 @@ func (_m *SceneReaderWriter) FindByGroupID(ctx context.Context, groupID int) ([] ret := _m.Called(ctx, groupID) var r0 []*models.Scene + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) ([]*models.Scene, error)); ok { + return rf(ctx, groupID) + } if rf, ok := ret.Get(0).(func(context.Context, int) []*models.Scene); ok { r0 = rf(ctx, groupID) } else { @@ -539,7 +603,6 @@ func (_m *SceneReaderWriter) FindByGroupID(ctx context.Context, groupID int) ([] } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, groupID) } else { @@ -554,6 +617,10 @@ func (_m *SceneReaderWriter) FindByIDs(ctx context.Context, ids []int) ([]*model ret := _m.Called(ctx, ids) var r0 []*models.Scene + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, []int) ([]*models.Scene, error)); ok { + return rf(ctx, ids) + } if rf, ok := ret.Get(0).(func(context.Context, []int) []*models.Scene); ok { r0 = rf(ctx, ids) } else { @@ -562,7 +629,6 @@ func (_m *SceneReaderWriter) FindByIDs(ctx context.Context, ids []int) ([]*model } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, []int) error); ok { r1 = rf(ctx, ids) } else { @@ -577,6 +643,10 @@ func (_m *SceneReaderWriter) FindByOSHash(ctx context.Context, oshash string) ([ ret := _m.Called(ctx, oshash) var r0 []*models.Scene + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string) ([]*models.Scene, error)); ok { + return rf(ctx, oshash) + } if rf, ok := ret.Get(0).(func(context.Context, string) []*models.Scene); ok { r0 = rf(ctx, oshash) } else { @@ -585,7 +655,6 @@ func (_m *SceneReaderWriter) FindByOSHash(ctx context.Context, oshash string) ([ } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { r1 = rf(ctx, oshash) } else { @@ -600,6 +669,10 @@ func (_m *SceneReaderWriter) FindByPath(ctx context.Context, path string) ([]*mo ret := _m.Called(ctx, path) var r0 []*models.Scene + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string) ([]*models.Scene, error)); ok { + return rf(ctx, path) + } if rf, ok := ret.Get(0).(func(context.Context, string) []*models.Scene); ok { r0 = rf(ctx, path) } else { @@ -608,7 +681,6 @@ func (_m *SceneReaderWriter) FindByPath(ctx context.Context, path string) ([]*mo } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { r1 = rf(ctx, path) } else { @@ -623,6 +695,10 @@ func (_m *SceneReaderWriter) FindByPerformerID(ctx context.Context, performerID ret := _m.Called(ctx, performerID) var r0 []*models.Scene + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) ([]*models.Scene, error)); ok { + return rf(ctx, performerID) + } if rf, ok := ret.Get(0).(func(context.Context, int) []*models.Scene); ok { r0 = rf(ctx, performerID) } else { @@ -631,7 +707,6 @@ func (_m *SceneReaderWriter) FindByPerformerID(ctx context.Context, performerID } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, performerID) } else { @@ -646,6 +721,10 @@ func (_m *SceneReaderWriter) FindByPrimaryFileID(ctx context.Context, fileID mod ret := _m.Called(ctx, fileID) var r0 []*models.Scene + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, models.FileID) ([]*models.Scene, error)); ok { + return rf(ctx, fileID) + } if rf, ok := ret.Get(0).(func(context.Context, models.FileID) []*models.Scene); ok { r0 = rf(ctx, fileID) } else { @@ -654,7 +733,6 @@ func (_m *SceneReaderWriter) FindByPrimaryFileID(ctx context.Context, fileID mod } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, models.FileID) error); ok { r1 = rf(ctx, fileID) } else { @@ -669,6 +747,10 @@ func (_m *SceneReaderWriter) FindDuplicates(ctx context.Context, distance int, d ret := _m.Called(ctx, distance, durationDiff, filter) var r0 [][]*models.Scene + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int, float64, *models.SceneFilterType) ([][]*models.Scene, error)); ok { + return rf(ctx, distance, durationDiff, filter) + } if rf, ok := ret.Get(0).(func(context.Context, int, float64, *models.SceneFilterType) [][]*models.Scene); ok { r0 = rf(ctx, distance, durationDiff, filter) } else { @@ -677,7 +759,6 @@ func (_m *SceneReaderWriter) FindDuplicates(ctx context.Context, distance int, d } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int, float64, *models.SceneFilterType) error); ok { r1 = rf(ctx, distance, durationDiff, filter) } else { @@ -692,6 +773,10 @@ func (_m *SceneReaderWriter) FindMany(ctx context.Context, ids []int) ([]*models ret := _m.Called(ctx, ids) var r0 []*models.Scene + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, []int) ([]*models.Scene, error)); ok { + return rf(ctx, ids) + } if rf, ok := ret.Get(0).(func(context.Context, []int) []*models.Scene); ok { r0 = rf(ctx, ids) } else { @@ -700,7 +785,6 @@ func (_m *SceneReaderWriter) FindMany(ctx context.Context, ids []int) ([]*models } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, []int) error); ok { r1 = rf(ctx, ids) } else { @@ -715,13 +799,16 @@ func (_m *SceneReaderWriter) GetAllOCount(ctx context.Context) (int, error) { ret := _m.Called(ctx) var r0 int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) (int, error)); ok { + return rf(ctx) + } if rf, ok := ret.Get(0).(func(context.Context) int); ok { r0 = rf(ctx) } else { r0 = ret.Get(0).(int) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context) error); ok { r1 = rf(ctx) } else { @@ -736,6 +823,10 @@ func (_m *SceneReaderWriter) GetCover(ctx context.Context, sceneID int) ([]byte, ret := _m.Called(ctx, sceneID) var r0 []byte + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) ([]byte, error)); ok { + return rf(ctx, sceneID) + } if rf, ok := ret.Get(0).(func(context.Context, int) []byte); ok { r0 = rf(ctx, sceneID) } else { @@ -744,7 +835,6 @@ func (_m *SceneReaderWriter) GetCover(ctx context.Context, sceneID int) ([]byte, } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, sceneID) } else { @@ -759,6 +849,10 @@ func (_m *SceneReaderWriter) GetCustomFields(ctx context.Context, id int) (map[s ret := _m.Called(ctx, id) var r0 map[string]interface{} + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) (map[string]interface{}, error)); ok { + return rf(ctx, id) + } if rf, ok := ret.Get(0).(func(context.Context, int) map[string]interface{}); ok { r0 = rf(ctx, id) } else { @@ -767,7 +861,6 @@ func (_m *SceneReaderWriter) GetCustomFields(ctx context.Context, id int) (map[s } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, id) } else { @@ -782,6 +875,10 @@ func (_m *SceneReaderWriter) GetCustomFieldsBulk(ctx context.Context, ids []int) ret := _m.Called(ctx, ids) var r0 []models.CustomFieldMap + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, []int) ([]models.CustomFieldMap, error)); ok { + return rf(ctx, ids) + } if rf, ok := ret.Get(0).(func(context.Context, []int) []models.CustomFieldMap); ok { r0 = rf(ctx, ids) } else { @@ -790,7 +887,6 @@ func (_m *SceneReaderWriter) GetCustomFieldsBulk(ctx context.Context, ids []int) } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, []int) error); ok { r1 = rf(ctx, ids) } else { @@ -805,6 +901,10 @@ func (_m *SceneReaderWriter) GetFiles(ctx context.Context, relatedID int) ([]*mo ret := _m.Called(ctx, relatedID) var r0 []*models.VideoFile + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) ([]*models.VideoFile, error)); ok { + return rf(ctx, relatedID) + } if rf, ok := ret.Get(0).(func(context.Context, int) []*models.VideoFile); ok { r0 = rf(ctx, relatedID) } else { @@ -813,7 +913,6 @@ func (_m *SceneReaderWriter) GetFiles(ctx context.Context, relatedID int) ([]*mo } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, relatedID) } else { @@ -828,6 +927,10 @@ func (_m *SceneReaderWriter) GetGalleryIDs(ctx context.Context, relatedID int) ( ret := _m.Called(ctx, relatedID) var r0 []int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) ([]int, error)); ok { + return rf(ctx, relatedID) + } if rf, ok := ret.Get(0).(func(context.Context, int) []int); ok { r0 = rf(ctx, relatedID) } else { @@ -836,7 +939,6 @@ func (_m *SceneReaderWriter) GetGalleryIDs(ctx context.Context, relatedID int) ( } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, relatedID) } else { @@ -851,6 +953,10 @@ func (_m *SceneReaderWriter) GetGroups(ctx context.Context, id int) ([]models.Gr ret := _m.Called(ctx, id) var r0 []models.GroupsScenes + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) ([]models.GroupsScenes, error)); ok { + return rf(ctx, id) + } if rf, ok := ret.Get(0).(func(context.Context, int) []models.GroupsScenes); ok { r0 = rf(ctx, id) } else { @@ -859,7 +965,6 @@ func (_m *SceneReaderWriter) GetGroups(ctx context.Context, id int) ([]models.Gr } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, id) } else { @@ -874,6 +979,10 @@ func (_m *SceneReaderWriter) GetManyFileIDs(ctx context.Context, ids []int) ([][ ret := _m.Called(ctx, ids) var r0 [][]models.FileID + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, []int) ([][]models.FileID, error)); ok { + return rf(ctx, ids) + } if rf, ok := ret.Get(0).(func(context.Context, []int) [][]models.FileID); ok { r0 = rf(ctx, ids) } else { @@ -882,7 +991,6 @@ func (_m *SceneReaderWriter) GetManyFileIDs(ctx context.Context, ids []int) ([][ } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, []int) error); ok { r1 = rf(ctx, ids) } else { @@ -897,6 +1005,10 @@ func (_m *SceneReaderWriter) GetManyIDsByFileIDs(ctx context.Context, fileIDs [] ret := _m.Called(ctx, fileIDs) var r0 [][]int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, []models.FileID) ([][]int, error)); ok { + return rf(ctx, fileIDs) + } if rf, ok := ret.Get(0).(func(context.Context, []models.FileID) [][]int); ok { r0 = rf(ctx, fileIDs) } else { @@ -905,7 +1017,6 @@ func (_m *SceneReaderWriter) GetManyIDsByFileIDs(ctx context.Context, fileIDs [] } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, []models.FileID) error); ok { r1 = rf(ctx, fileIDs) } else { @@ -920,6 +1031,10 @@ func (_m *SceneReaderWriter) GetManyLastViewed(ctx context.Context, ids []int) ( ret := _m.Called(ctx, ids) var r0 []*time.Time + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, []int) ([]*time.Time, error)); ok { + return rf(ctx, ids) + } if rf, ok := ret.Get(0).(func(context.Context, []int) []*time.Time); ok { r0 = rf(ctx, ids) } else { @@ -928,7 +1043,6 @@ func (_m *SceneReaderWriter) GetManyLastViewed(ctx context.Context, ids []int) ( } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, []int) error); ok { r1 = rf(ctx, ids) } else { @@ -943,6 +1057,10 @@ func (_m *SceneReaderWriter) GetManyOCount(ctx context.Context, ids []int) ([]in ret := _m.Called(ctx, ids) var r0 []int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, []int) ([]int, error)); ok { + return rf(ctx, ids) + } if rf, ok := ret.Get(0).(func(context.Context, []int) []int); ok { r0 = rf(ctx, ids) } else { @@ -951,7 +1069,6 @@ func (_m *SceneReaderWriter) GetManyOCount(ctx context.Context, ids []int) ([]in } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, []int) error); ok { r1 = rf(ctx, ids) } else { @@ -966,6 +1083,10 @@ func (_m *SceneReaderWriter) GetManyODates(ctx context.Context, ids []int) ([][] ret := _m.Called(ctx, ids) var r0 [][]time.Time + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, []int) ([][]time.Time, error)); ok { + return rf(ctx, ids) + } if rf, ok := ret.Get(0).(func(context.Context, []int) [][]time.Time); ok { r0 = rf(ctx, ids) } else { @@ -974,7 +1095,6 @@ func (_m *SceneReaderWriter) GetManyODates(ctx context.Context, ids []int) ([][] } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, []int) error); ok { r1 = rf(ctx, ids) } else { @@ -989,6 +1109,10 @@ func (_m *SceneReaderWriter) GetManyViewCount(ctx context.Context, ids []int) ([ ret := _m.Called(ctx, ids) var r0 []int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, []int) ([]int, error)); ok { + return rf(ctx, ids) + } if rf, ok := ret.Get(0).(func(context.Context, []int) []int); ok { r0 = rf(ctx, ids) } else { @@ -997,7 +1121,6 @@ func (_m *SceneReaderWriter) GetManyViewCount(ctx context.Context, ids []int) ([ } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, []int) error); ok { r1 = rf(ctx, ids) } else { @@ -1012,6 +1135,10 @@ func (_m *SceneReaderWriter) GetManyViewDates(ctx context.Context, ids []int) ([ ret := _m.Called(ctx, ids) var r0 [][]time.Time + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, []int) ([][]time.Time, error)); ok { + return rf(ctx, ids) + } if rf, ok := ret.Get(0).(func(context.Context, []int) [][]time.Time); ok { r0 = rf(ctx, ids) } else { @@ -1020,7 +1147,6 @@ func (_m *SceneReaderWriter) GetManyViewDates(ctx context.Context, ids []int) ([ } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, []int) error); ok { r1 = rf(ctx, ids) } else { @@ -1035,13 +1161,16 @@ func (_m *SceneReaderWriter) GetOCount(ctx context.Context, id int) (int, error) ret := _m.Called(ctx, id) var r0 int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) (int, error)); ok { + return rf(ctx, id) + } if rf, ok := ret.Get(0).(func(context.Context, int) int); ok { r0 = rf(ctx, id) } else { r0 = ret.Get(0).(int) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, id) } else { @@ -1056,6 +1185,10 @@ func (_m *SceneReaderWriter) GetODates(ctx context.Context, relatedID int) ([]ti ret := _m.Called(ctx, relatedID) var r0 []time.Time + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) ([]time.Time, error)); ok { + return rf(ctx, relatedID) + } if rf, ok := ret.Get(0).(func(context.Context, int) []time.Time); ok { r0 = rf(ctx, relatedID) } else { @@ -1064,7 +1197,6 @@ func (_m *SceneReaderWriter) GetODates(ctx context.Context, relatedID int) ([]ti } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, relatedID) } else { @@ -1079,6 +1211,10 @@ func (_m *SceneReaderWriter) GetPerformerIDs(ctx context.Context, relatedID int) ret := _m.Called(ctx, relatedID) var r0 []int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) ([]int, error)); ok { + return rf(ctx, relatedID) + } if rf, ok := ret.Get(0).(func(context.Context, int) []int); ok { r0 = rf(ctx, relatedID) } else { @@ -1087,7 +1223,6 @@ func (_m *SceneReaderWriter) GetPerformerIDs(ctx context.Context, relatedID int) } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, relatedID) } else { @@ -1102,6 +1237,10 @@ func (_m *SceneReaderWriter) GetStashIDs(ctx context.Context, relatedID int) ([] ret := _m.Called(ctx, relatedID) var r0 []models.StashID + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) ([]models.StashID, error)); ok { + return rf(ctx, relatedID) + } if rf, ok := ret.Get(0).(func(context.Context, int) []models.StashID); ok { r0 = rf(ctx, relatedID) } else { @@ -1110,7 +1249,6 @@ func (_m *SceneReaderWriter) GetStashIDs(ctx context.Context, relatedID int) ([] } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, relatedID) } else { @@ -1125,6 +1263,10 @@ func (_m *SceneReaderWriter) GetTagIDs(ctx context.Context, relatedID int) ([]in ret := _m.Called(ctx, relatedID) var r0 []int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) ([]int, error)); ok { + return rf(ctx, relatedID) + } if rf, ok := ret.Get(0).(func(context.Context, int) []int); ok { r0 = rf(ctx, relatedID) } else { @@ -1133,7 +1275,6 @@ func (_m *SceneReaderWriter) GetTagIDs(ctx context.Context, relatedID int) ([]in } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, relatedID) } else { @@ -1148,6 +1289,10 @@ func (_m *SceneReaderWriter) GetURLs(ctx context.Context, relatedID int) ([]stri ret := _m.Called(ctx, relatedID) var r0 []string + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) ([]string, error)); ok { + return rf(ctx, relatedID) + } if rf, ok := ret.Get(0).(func(context.Context, int) []string); ok { r0 = rf(ctx, relatedID) } else { @@ -1156,7 +1301,6 @@ func (_m *SceneReaderWriter) GetURLs(ctx context.Context, relatedID int) ([]stri } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, relatedID) } else { @@ -1171,6 +1315,10 @@ func (_m *SceneReaderWriter) GetViewDates(ctx context.Context, relatedID int) ([ ret := _m.Called(ctx, relatedID) var r0 []time.Time + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) ([]time.Time, error)); ok { + return rf(ctx, relatedID) + } if rf, ok := ret.Get(0).(func(context.Context, int) []time.Time); ok { r0 = rf(ctx, relatedID) } else { @@ -1179,7 +1327,6 @@ func (_m *SceneReaderWriter) GetViewDates(ctx context.Context, relatedID int) ([ } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, relatedID) } else { @@ -1194,13 +1341,16 @@ func (_m *SceneReaderWriter) HasCover(ctx context.Context, sceneID int) (bool, e ret := _m.Called(ctx, sceneID) var r0 bool + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) (bool, error)); ok { + return rf(ctx, sceneID) + } if rf, ok := ret.Get(0).(func(context.Context, int) bool); ok { r0 = rf(ctx, sceneID) } else { r0 = ret.Get(0).(bool) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, sceneID) } else { @@ -1215,13 +1365,16 @@ func (_m *SceneReaderWriter) OCountByGroupID(ctx context.Context, groupID int) ( ret := _m.Called(ctx, groupID) var r0 int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) (int, error)); ok { + return rf(ctx, groupID) + } if rf, ok := ret.Get(0).(func(context.Context, int) int); ok { r0 = rf(ctx, groupID) } else { r0 = ret.Get(0).(int) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, groupID) } else { @@ -1236,13 +1389,16 @@ func (_m *SceneReaderWriter) OCountByPerformerID(ctx context.Context, performerI ret := _m.Called(ctx, performerID) var r0 int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) (int, error)); ok { + return rf(ctx, performerID) + } if rf, ok := ret.Get(0).(func(context.Context, int) int); ok { r0 = rf(ctx, performerID) } else { r0 = ret.Get(0).(int) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, performerID) } else { @@ -1257,13 +1413,16 @@ func (_m *SceneReaderWriter) OCountByStudioID(ctx context.Context, studioID int, ret := _m.Called(ctx, studioID, depth) var r0 int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int, int) (int, error)); ok { + return rf(ctx, studioID, depth) + } if rf, ok := ret.Get(0).(func(context.Context, int, int) int); ok { r0 = rf(ctx, studioID, depth) } else { r0 = ret.Get(0).(int) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int, int) error); ok { r1 = rf(ctx, studioID, depth) } else { @@ -1278,13 +1437,16 @@ func (_m *SceneReaderWriter) PlayDuration(ctx context.Context) (float64, error) ret := _m.Called(ctx) var r0 float64 + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) (float64, error)); ok { + return rf(ctx) + } if rf, ok := ret.Get(0).(func(context.Context) float64); ok { r0 = rf(ctx) } else { r0 = ret.Get(0).(float64) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context) error); ok { r1 = rf(ctx) } else { @@ -1299,6 +1461,10 @@ func (_m *SceneReaderWriter) Query(ctx context.Context, options models.SceneQuer ret := _m.Called(ctx, options) var r0 *models.SceneQueryResult + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, models.SceneQueryOptions) (*models.SceneQueryResult, error)); ok { + return rf(ctx, options) + } if rf, ok := ret.Get(0).(func(context.Context, models.SceneQueryOptions) *models.SceneQueryResult); ok { r0 = rf(ctx, options) } else { @@ -1307,7 +1473,6 @@ func (_m *SceneReaderWriter) Query(ctx context.Context, options models.SceneQuer } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, models.SceneQueryOptions) error); ok { r1 = rf(ctx, options) } else { @@ -1322,13 +1487,16 @@ func (_m *SceneReaderWriter) QueryCount(ctx context.Context, sceneFilter *models ret := _m.Called(ctx, sceneFilter, findFilter) var r0 int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *models.SceneFilterType, *models.FindFilterType) (int, error)); ok { + return rf(ctx, sceneFilter, findFilter) + } if rf, ok := ret.Get(0).(func(context.Context, *models.SceneFilterType, *models.FindFilterType) int); ok { r0 = rf(ctx, sceneFilter, findFilter) } else { r0 = ret.Get(0).(int) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, *models.SceneFilterType, *models.FindFilterType) error); ok { r1 = rf(ctx, sceneFilter, findFilter) } else { @@ -1343,13 +1511,16 @@ func (_m *SceneReaderWriter) ResetActivity(ctx context.Context, sceneID int, res ret := _m.Called(ctx, sceneID, resetResume, resetDuration) var r0 bool + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int, bool, bool) (bool, error)); ok { + return rf(ctx, sceneID, resetResume, resetDuration) + } if rf, ok := ret.Get(0).(func(context.Context, int, bool, bool) bool); ok { r0 = rf(ctx, sceneID, resetResume, resetDuration) } else { r0 = ret.Get(0).(bool) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int, bool, bool) error); ok { r1 = rf(ctx, sceneID, resetResume, resetDuration) } else { @@ -1364,13 +1535,16 @@ func (_m *SceneReaderWriter) ResetO(ctx context.Context, id int) (int, error) { ret := _m.Called(ctx, id) var r0 int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) (int, error)); ok { + return rf(ctx, id) + } if rf, ok := ret.Get(0).(func(context.Context, int) int); ok { r0 = rf(ctx, id) } else { r0 = ret.Get(0).(int) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, id) } else { @@ -1385,13 +1559,16 @@ func (_m *SceneReaderWriter) SaveActivity(ctx context.Context, sceneID int, resu ret := _m.Called(ctx, sceneID, resumeTime, playDuration) var r0 bool + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int, *float64, *float64) (bool, error)); ok { + return rf(ctx, sceneID, resumeTime, playDuration) + } if rf, ok := ret.Get(0).(func(context.Context, int, *float64, *float64) bool); ok { r0 = rf(ctx, sceneID, resumeTime, playDuration) } else { r0 = ret.Get(0).(bool) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int, *float64, *float64) error); ok { r1 = rf(ctx, sceneID, resumeTime, playDuration) } else { @@ -1420,13 +1597,16 @@ func (_m *SceneReaderWriter) Size(ctx context.Context) (float64, error) { ret := _m.Called(ctx) var r0 float64 + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) (float64, error)); ok { + return rf(ctx) + } if rf, ok := ret.Get(0).(func(context.Context) float64); ok { r0 = rf(ctx) } else { r0 = ret.Get(0).(float64) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context) error); ok { r1 = rf(ctx) } else { @@ -1469,6 +1649,10 @@ func (_m *SceneReaderWriter) UpdatePartial(ctx context.Context, id int, updatedS ret := _m.Called(ctx, id, updatedScene) var r0 *models.Scene + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int, models.ScenePartial) (*models.Scene, error)); ok { + return rf(ctx, id, updatedScene) + } if rf, ok := ret.Get(0).(func(context.Context, int, models.ScenePartial) *models.Scene); ok { r0 = rf(ctx, id, updatedScene) } else { @@ -1477,7 +1661,6 @@ func (_m *SceneReaderWriter) UpdatePartial(ctx context.Context, id int, updatedS } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int, models.ScenePartial) error); ok { r1 = rf(ctx, id, updatedScene) } else { @@ -1492,6 +1675,10 @@ func (_m *SceneReaderWriter) Wall(ctx context.Context, q *string) ([]*models.Sce ret := _m.Called(ctx, q) var r0 []*models.Scene + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *string) ([]*models.Scene, error)); ok { + return rf(ctx, q) + } if rf, ok := ret.Get(0).(func(context.Context, *string) []*models.Scene); ok { r0 = rf(ctx, q) } else { @@ -1500,7 +1687,6 @@ func (_m *SceneReaderWriter) Wall(ctx context.Context, q *string) ([]*models.Sce } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, *string) error); ok { r1 = rf(ctx, q) } else { @@ -1509,3 +1695,24 @@ func (_m *SceneReaderWriter) Wall(ctx context.Context, q *string) ([]*models.Sce return r0, r1 } + +type mockConstructorTestingTNewSceneReaderWriter interface { + mock.TestingT + Cleanup(func()) +} + +// NewSceneReaderWriter creates a new instance of SceneReaderWriter. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewSceneReaderWriter(t mockConstructorTestingTNewSceneReaderWriter) *SceneReaderWriter { + mock := &SceneReaderWriter{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} + + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/pkg/models/mocks/StudioReaderWriter.go b/pkg/models/mocks/StudioReaderWriter.go index f57a73aa1d..7358fb8b9e 100644 --- a/pkg/models/mocks/StudioReaderWriter.go +++ b/pkg/models/mocks/StudioReaderWriter.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.10.0. DO NOT EDIT. +// Code generated by mockery v2.21.6. DO NOT EDIT. package mocks @@ -19,6 +19,10 @@ func (_m *StudioReaderWriter) All(ctx context.Context) ([]*models.Studio, error) ret := _m.Called(ctx) var r0 []*models.Studio + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) ([]*models.Studio, error)); ok { + return rf(ctx) + } if rf, ok := ret.Get(0).(func(context.Context) []*models.Studio); ok { r0 = rf(ctx) } else { @@ -27,7 +31,6 @@ func (_m *StudioReaderWriter) All(ctx context.Context) ([]*models.Studio, error) } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context) error); ok { r1 = rf(ctx) } else { @@ -42,13 +45,16 @@ func (_m *StudioReaderWriter) Count(ctx context.Context) (int, error) { ret := _m.Called(ctx) var r0 int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) (int, error)); ok { + return rf(ctx) + } if rf, ok := ret.Get(0).(func(context.Context) int); ok { r0 = rf(ctx) } else { r0 = ret.Get(0).(int) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context) error); ok { r1 = rf(ctx) } else { @@ -63,13 +69,16 @@ func (_m *StudioReaderWriter) CountByTagID(ctx context.Context, tagID int) (int, ret := _m.Called(ctx, tagID) var r0 int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) (int, error)); ok { + return rf(ctx, tagID) + } if rf, ok := ret.Get(0).(func(context.Context, int) int); ok { r0 = rf(ctx, tagID) } else { r0 = ret.Get(0).(int) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, tagID) } else { @@ -112,6 +121,10 @@ func (_m *StudioReaderWriter) Find(ctx context.Context, id int) (*models.Studio, ret := _m.Called(ctx, id) var r0 *models.Studio + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) (*models.Studio, error)); ok { + return rf(ctx, id) + } if rf, ok := ret.Get(0).(func(context.Context, int) *models.Studio); ok { r0 = rf(ctx, id) } else { @@ -120,7 +133,6 @@ func (_m *StudioReaderWriter) Find(ctx context.Context, id int) (*models.Studio, } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, id) } else { @@ -135,6 +147,10 @@ func (_m *StudioReaderWriter) FindByName(ctx context.Context, name string, nocas ret := _m.Called(ctx, name, nocase) var r0 *models.Studio + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, bool) (*models.Studio, error)); ok { + return rf(ctx, name, nocase) + } if rf, ok := ret.Get(0).(func(context.Context, string, bool) *models.Studio); ok { r0 = rf(ctx, name, nocase) } else { @@ -143,7 +159,6 @@ func (_m *StudioReaderWriter) FindByName(ctx context.Context, name string, nocas } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, string, bool) error); ok { r1 = rf(ctx, name, nocase) } else { @@ -158,6 +173,10 @@ func (_m *StudioReaderWriter) FindBySceneID(ctx context.Context, sceneID int) (* ret := _m.Called(ctx, sceneID) var r0 *models.Studio + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) (*models.Studio, error)); ok { + return rf(ctx, sceneID) + } if rf, ok := ret.Get(0).(func(context.Context, int) *models.Studio); ok { r0 = rf(ctx, sceneID) } else { @@ -166,7 +185,6 @@ func (_m *StudioReaderWriter) FindBySceneID(ctx context.Context, sceneID int) (* } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, sceneID) } else { @@ -181,6 +199,10 @@ func (_m *StudioReaderWriter) FindByStashID(ctx context.Context, stashID models. ret := _m.Called(ctx, stashID) var r0 []*models.Studio + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, models.StashID) ([]*models.Studio, error)); ok { + return rf(ctx, stashID) + } if rf, ok := ret.Get(0).(func(context.Context, models.StashID) []*models.Studio); ok { r0 = rf(ctx, stashID) } else { @@ -189,7 +211,6 @@ func (_m *StudioReaderWriter) FindByStashID(ctx context.Context, stashID models. } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, models.StashID) error); ok { r1 = rf(ctx, stashID) } else { @@ -204,6 +225,10 @@ func (_m *StudioReaderWriter) FindByStashIDStatus(ctx context.Context, hasStashI ret := _m.Called(ctx, hasStashID, stashboxEndpoint) var r0 []*models.Studio + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, bool, string) ([]*models.Studio, error)); ok { + return rf(ctx, hasStashID, stashboxEndpoint) + } if rf, ok := ret.Get(0).(func(context.Context, bool, string) []*models.Studio); ok { r0 = rf(ctx, hasStashID, stashboxEndpoint) } else { @@ -212,7 +237,6 @@ func (_m *StudioReaderWriter) FindByStashIDStatus(ctx context.Context, hasStashI } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, bool, string) error); ok { r1 = rf(ctx, hasStashID, stashboxEndpoint) } else { @@ -227,6 +251,10 @@ func (_m *StudioReaderWriter) FindChildren(ctx context.Context, id int) ([]*mode ret := _m.Called(ctx, id) var r0 []*models.Studio + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) ([]*models.Studio, error)); ok { + return rf(ctx, id) + } if rf, ok := ret.Get(0).(func(context.Context, int) []*models.Studio); ok { r0 = rf(ctx, id) } else { @@ -235,7 +263,6 @@ func (_m *StudioReaderWriter) FindChildren(ctx context.Context, id int) ([]*mode } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, id) } else { @@ -250,6 +277,10 @@ func (_m *StudioReaderWriter) FindMany(ctx context.Context, ids []int) ([]*model ret := _m.Called(ctx, ids) var r0 []*models.Studio + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, []int) ([]*models.Studio, error)); ok { + return rf(ctx, ids) + } if rf, ok := ret.Get(0).(func(context.Context, []int) []*models.Studio); ok { r0 = rf(ctx, ids) } else { @@ -258,7 +289,6 @@ func (_m *StudioReaderWriter) FindMany(ctx context.Context, ids []int) ([]*model } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, []int) error); ok { r1 = rf(ctx, ids) } else { @@ -273,6 +303,10 @@ func (_m *StudioReaderWriter) GetAliases(ctx context.Context, relatedID int) ([] ret := _m.Called(ctx, relatedID) var r0 []string + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) ([]string, error)); ok { + return rf(ctx, relatedID) + } if rf, ok := ret.Get(0).(func(context.Context, int) []string); ok { r0 = rf(ctx, relatedID) } else { @@ -281,7 +315,6 @@ func (_m *StudioReaderWriter) GetAliases(ctx context.Context, relatedID int) ([] } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, relatedID) } else { @@ -296,6 +329,10 @@ func (_m *StudioReaderWriter) GetCustomFields(ctx context.Context, id int) (map[ ret := _m.Called(ctx, id) var r0 map[string]interface{} + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) (map[string]interface{}, error)); ok { + return rf(ctx, id) + } if rf, ok := ret.Get(0).(func(context.Context, int) map[string]interface{}); ok { r0 = rf(ctx, id) } else { @@ -304,7 +341,6 @@ func (_m *StudioReaderWriter) GetCustomFields(ctx context.Context, id int) (map[ } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, id) } else { @@ -319,6 +355,10 @@ func (_m *StudioReaderWriter) GetCustomFieldsBulk(ctx context.Context, ids []int ret := _m.Called(ctx, ids) var r0 []models.CustomFieldMap + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, []int) ([]models.CustomFieldMap, error)); ok { + return rf(ctx, ids) + } if rf, ok := ret.Get(0).(func(context.Context, []int) []models.CustomFieldMap); ok { r0 = rf(ctx, ids) } else { @@ -327,7 +367,6 @@ func (_m *StudioReaderWriter) GetCustomFieldsBulk(ctx context.Context, ids []int } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, []int) error); ok { r1 = rf(ctx, ids) } else { @@ -342,6 +381,10 @@ func (_m *StudioReaderWriter) GetImage(ctx context.Context, studioID int) ([]byt ret := _m.Called(ctx, studioID) var r0 []byte + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) ([]byte, error)); ok { + return rf(ctx, studioID) + } if rf, ok := ret.Get(0).(func(context.Context, int) []byte); ok { r0 = rf(ctx, studioID) } else { @@ -350,7 +393,6 @@ func (_m *StudioReaderWriter) GetImage(ctx context.Context, studioID int) ([]byt } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, studioID) } else { @@ -365,6 +407,10 @@ func (_m *StudioReaderWriter) GetStashIDs(ctx context.Context, relatedID int) ([ ret := _m.Called(ctx, relatedID) var r0 []models.StashID + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) ([]models.StashID, error)); ok { + return rf(ctx, relatedID) + } if rf, ok := ret.Get(0).(func(context.Context, int) []models.StashID); ok { r0 = rf(ctx, relatedID) } else { @@ -373,7 +419,6 @@ func (_m *StudioReaderWriter) GetStashIDs(ctx context.Context, relatedID int) ([ } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, relatedID) } else { @@ -388,6 +433,10 @@ func (_m *StudioReaderWriter) GetTagIDs(ctx context.Context, relatedID int) ([]i ret := _m.Called(ctx, relatedID) var r0 []int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) ([]int, error)); ok { + return rf(ctx, relatedID) + } if rf, ok := ret.Get(0).(func(context.Context, int) []int); ok { r0 = rf(ctx, relatedID) } else { @@ -396,7 +445,6 @@ func (_m *StudioReaderWriter) GetTagIDs(ctx context.Context, relatedID int) ([]i } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, relatedID) } else { @@ -411,6 +459,10 @@ func (_m *StudioReaderWriter) GetURLs(ctx context.Context, relatedID int) ([]str ret := _m.Called(ctx, relatedID) var r0 []string + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) ([]string, error)); ok { + return rf(ctx, relatedID) + } if rf, ok := ret.Get(0).(func(context.Context, int) []string); ok { r0 = rf(ctx, relatedID) } else { @@ -419,7 +471,6 @@ func (_m *StudioReaderWriter) GetURLs(ctx context.Context, relatedID int) ([]str } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, relatedID) } else { @@ -434,13 +485,16 @@ func (_m *StudioReaderWriter) HasImage(ctx context.Context, studioID int) (bool, ret := _m.Called(ctx, studioID) var r0 bool + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) (bool, error)); ok { + return rf(ctx, studioID) + } if rf, ok := ret.Get(0).(func(context.Context, int) bool); ok { r0 = rf(ctx, studioID) } else { r0 = ret.Get(0).(bool) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, studioID) } else { @@ -455,6 +509,11 @@ func (_m *StudioReaderWriter) Query(ctx context.Context, studioFilter *models.St ret := _m.Called(ctx, studioFilter, findFilter) var r0 []*models.Studio + var r1 int + var r2 error + if rf, ok := ret.Get(0).(func(context.Context, *models.StudioFilterType, *models.FindFilterType) ([]*models.Studio, int, error)); ok { + return rf(ctx, studioFilter, findFilter) + } if rf, ok := ret.Get(0).(func(context.Context, *models.StudioFilterType, *models.FindFilterType) []*models.Studio); ok { r0 = rf(ctx, studioFilter, findFilter) } else { @@ -463,14 +522,12 @@ func (_m *StudioReaderWriter) Query(ctx context.Context, studioFilter *models.St } } - var r1 int if rf, ok := ret.Get(1).(func(context.Context, *models.StudioFilterType, *models.FindFilterType) int); ok { r1 = rf(ctx, studioFilter, findFilter) } else { r1 = ret.Get(1).(int) } - var r2 error if rf, ok := ret.Get(2).(func(context.Context, *models.StudioFilterType, *models.FindFilterType) error); ok { r2 = rf(ctx, studioFilter, findFilter) } else { @@ -485,13 +542,16 @@ func (_m *StudioReaderWriter) QueryCount(ctx context.Context, studioFilter *mode ret := _m.Called(ctx, studioFilter, findFilter) var r0 int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *models.StudioFilterType, *models.FindFilterType) (int, error)); ok { + return rf(ctx, studioFilter, findFilter) + } if rf, ok := ret.Get(0).(func(context.Context, *models.StudioFilterType, *models.FindFilterType) int); ok { r0 = rf(ctx, studioFilter, findFilter) } else { r0 = ret.Get(0).(int) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, *models.StudioFilterType, *models.FindFilterType) error); ok { r1 = rf(ctx, studioFilter, findFilter) } else { @@ -506,6 +566,10 @@ func (_m *StudioReaderWriter) QueryForAutoTag(ctx context.Context, words []strin ret := _m.Called(ctx, words) var r0 []*models.Studio + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, []string) ([]*models.Studio, error)); ok { + return rf(ctx, words) + } if rf, ok := ret.Get(0).(func(context.Context, []string) []*models.Studio); ok { r0 = rf(ctx, words) } else { @@ -514,7 +578,6 @@ func (_m *StudioReaderWriter) QueryForAutoTag(ctx context.Context, words []strin } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, []string) error); ok { r1 = rf(ctx, words) } else { @@ -557,6 +620,10 @@ func (_m *StudioReaderWriter) UpdatePartial(ctx context.Context, updatedStudio m ret := _m.Called(ctx, updatedStudio) var r0 *models.Studio + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, models.StudioPartial) (*models.Studio, error)); ok { + return rf(ctx, updatedStudio) + } if rf, ok := ret.Get(0).(func(context.Context, models.StudioPartial) *models.Studio); ok { r0 = rf(ctx, updatedStudio) } else { @@ -565,7 +632,6 @@ func (_m *StudioReaderWriter) UpdatePartial(ctx context.Context, updatedStudio m } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, models.StudioPartial) error); ok { r1 = rf(ctx, updatedStudio) } else { @@ -574,3 +640,18 @@ func (_m *StudioReaderWriter) UpdatePartial(ctx context.Context, updatedStudio m return r0, r1 } + +type mockConstructorTestingTNewStudioReaderWriter interface { + mock.TestingT + Cleanup(func()) +} + +// NewStudioReaderWriter creates a new instance of StudioReaderWriter. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewStudioReaderWriter(t mockConstructorTestingTNewStudioReaderWriter) *StudioReaderWriter { + mock := &StudioReaderWriter{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/pkg/models/mocks/TagReaderWriter.go b/pkg/models/mocks/TagReaderWriter.go index 194f475c8f..a8982a4bae 100644 --- a/pkg/models/mocks/TagReaderWriter.go +++ b/pkg/models/mocks/TagReaderWriter.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.10.0. DO NOT EDIT. +// Code generated by mockery v2.21.6. DO NOT EDIT. package mocks @@ -19,6 +19,10 @@ func (_m *TagReaderWriter) All(ctx context.Context) ([]*models.Tag, error) { ret := _m.Called(ctx) var r0 []*models.Tag + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) ([]*models.Tag, error)); ok { + return rf(ctx) + } if rf, ok := ret.Get(0).(func(context.Context) []*models.Tag); ok { r0 = rf(ctx) } else { @@ -27,7 +31,6 @@ func (_m *TagReaderWriter) All(ctx context.Context) ([]*models.Tag, error) { } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context) error); ok { r1 = rf(ctx) } else { @@ -42,13 +45,16 @@ func (_m *TagReaderWriter) Count(ctx context.Context) (int, error) { ret := _m.Called(ctx) var r0 int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) (int, error)); ok { + return rf(ctx) + } if rf, ok := ret.Get(0).(func(context.Context) int); ok { r0 = rf(ctx) } else { r0 = ret.Get(0).(int) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context) error); ok { r1 = rf(ctx) } else { @@ -63,13 +69,16 @@ func (_m *TagReaderWriter) CountByChildTagID(ctx context.Context, childID int) ( ret := _m.Called(ctx, childID) var r0 int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) (int, error)); ok { + return rf(ctx, childID) + } if rf, ok := ret.Get(0).(func(context.Context, int) int); ok { r0 = rf(ctx, childID) } else { r0 = ret.Get(0).(int) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, childID) } else { @@ -84,13 +93,16 @@ func (_m *TagReaderWriter) CountByParentTagID(ctx context.Context, parentID int) ret := _m.Called(ctx, parentID) var r0 int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) (int, error)); ok { + return rf(ctx, parentID) + } if rf, ok := ret.Get(0).(func(context.Context, int) int); ok { r0 = rf(ctx, parentID) } else { r0 = ret.Get(0).(int) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, parentID) } else { @@ -133,6 +145,10 @@ func (_m *TagReaderWriter) Find(ctx context.Context, id int) (*models.Tag, error ret := _m.Called(ctx, id) var r0 *models.Tag + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) (*models.Tag, error)); ok { + return rf(ctx, id) + } if rf, ok := ret.Get(0).(func(context.Context, int) *models.Tag); ok { r0 = rf(ctx, id) } else { @@ -141,7 +157,6 @@ func (_m *TagReaderWriter) Find(ctx context.Context, id int) (*models.Tag, error } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, id) } else { @@ -156,6 +171,10 @@ func (_m *TagReaderWriter) FindAllAncestors(ctx context.Context, tagID int, excl ret := _m.Called(ctx, tagID, excludeIDs) var r0 []*models.TagPath + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int, []int) ([]*models.TagPath, error)); ok { + return rf(ctx, tagID, excludeIDs) + } if rf, ok := ret.Get(0).(func(context.Context, int, []int) []*models.TagPath); ok { r0 = rf(ctx, tagID, excludeIDs) } else { @@ -164,7 +183,6 @@ func (_m *TagReaderWriter) FindAllAncestors(ctx context.Context, tagID int, excl } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int, []int) error); ok { r1 = rf(ctx, tagID, excludeIDs) } else { @@ -179,6 +197,10 @@ func (_m *TagReaderWriter) FindAllDescendants(ctx context.Context, tagID int, ex ret := _m.Called(ctx, tagID, excludeIDs) var r0 []*models.TagPath + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int, []int) ([]*models.TagPath, error)); ok { + return rf(ctx, tagID, excludeIDs) + } if rf, ok := ret.Get(0).(func(context.Context, int, []int) []*models.TagPath); ok { r0 = rf(ctx, tagID, excludeIDs) } else { @@ -187,7 +209,6 @@ func (_m *TagReaderWriter) FindAllDescendants(ctx context.Context, tagID int, ex } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int, []int) error); ok { r1 = rf(ctx, tagID, excludeIDs) } else { @@ -202,6 +223,10 @@ func (_m *TagReaderWriter) FindByAlias(ctx context.Context, alias string, nocase ret := _m.Called(ctx, alias, nocase) var r0 *models.Tag + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, bool) (*models.Tag, error)); ok { + return rf(ctx, alias, nocase) + } if rf, ok := ret.Get(0).(func(context.Context, string, bool) *models.Tag); ok { r0 = rf(ctx, alias, nocase) } else { @@ -210,7 +235,6 @@ func (_m *TagReaderWriter) FindByAlias(ctx context.Context, alias string, nocase } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, string, bool) error); ok { r1 = rf(ctx, alias, nocase) } else { @@ -225,6 +249,10 @@ func (_m *TagReaderWriter) FindByChildTagID(ctx context.Context, childID int) ([ ret := _m.Called(ctx, childID) var r0 []*models.Tag + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) ([]*models.Tag, error)); ok { + return rf(ctx, childID) + } if rf, ok := ret.Get(0).(func(context.Context, int) []*models.Tag); ok { r0 = rf(ctx, childID) } else { @@ -233,7 +261,6 @@ func (_m *TagReaderWriter) FindByChildTagID(ctx context.Context, childID int) ([ } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, childID) } else { @@ -248,6 +275,10 @@ func (_m *TagReaderWriter) FindByGalleryID(ctx context.Context, galleryID int) ( ret := _m.Called(ctx, galleryID) var r0 []*models.Tag + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) ([]*models.Tag, error)); ok { + return rf(ctx, galleryID) + } if rf, ok := ret.Get(0).(func(context.Context, int) []*models.Tag); ok { r0 = rf(ctx, galleryID) } else { @@ -256,7 +287,6 @@ func (_m *TagReaderWriter) FindByGalleryID(ctx context.Context, galleryID int) ( } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, galleryID) } else { @@ -271,6 +301,10 @@ func (_m *TagReaderWriter) FindByGroupID(ctx context.Context, groupID int) ([]*m ret := _m.Called(ctx, groupID) var r0 []*models.Tag + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) ([]*models.Tag, error)); ok { + return rf(ctx, groupID) + } if rf, ok := ret.Get(0).(func(context.Context, int) []*models.Tag); ok { r0 = rf(ctx, groupID) } else { @@ -279,7 +313,6 @@ func (_m *TagReaderWriter) FindByGroupID(ctx context.Context, groupID int) ([]*m } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, groupID) } else { @@ -294,6 +327,10 @@ func (_m *TagReaderWriter) FindByImageID(ctx context.Context, imageID int) ([]*m ret := _m.Called(ctx, imageID) var r0 []*models.Tag + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) ([]*models.Tag, error)); ok { + return rf(ctx, imageID) + } if rf, ok := ret.Get(0).(func(context.Context, int) []*models.Tag); ok { r0 = rf(ctx, imageID) } else { @@ -302,7 +339,6 @@ func (_m *TagReaderWriter) FindByImageID(ctx context.Context, imageID int) ([]*m } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, imageID) } else { @@ -317,6 +353,10 @@ func (_m *TagReaderWriter) FindByName(ctx context.Context, name string, nocase b ret := _m.Called(ctx, name, nocase) var r0 *models.Tag + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, bool) (*models.Tag, error)); ok { + return rf(ctx, name, nocase) + } if rf, ok := ret.Get(0).(func(context.Context, string, bool) *models.Tag); ok { r0 = rf(ctx, name, nocase) } else { @@ -325,7 +365,6 @@ func (_m *TagReaderWriter) FindByName(ctx context.Context, name string, nocase b } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, string, bool) error); ok { r1 = rf(ctx, name, nocase) } else { @@ -340,6 +379,10 @@ func (_m *TagReaderWriter) FindByNames(ctx context.Context, names []string, noca ret := _m.Called(ctx, names, nocase) var r0 []*models.Tag + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, []string, bool) ([]*models.Tag, error)); ok { + return rf(ctx, names, nocase) + } if rf, ok := ret.Get(0).(func(context.Context, []string, bool) []*models.Tag); ok { r0 = rf(ctx, names, nocase) } else { @@ -348,7 +391,6 @@ func (_m *TagReaderWriter) FindByNames(ctx context.Context, names []string, noca } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, []string, bool) error); ok { r1 = rf(ctx, names, nocase) } else { @@ -363,6 +405,10 @@ func (_m *TagReaderWriter) FindByParentTagID(ctx context.Context, parentID int) ret := _m.Called(ctx, parentID) var r0 []*models.Tag + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) ([]*models.Tag, error)); ok { + return rf(ctx, parentID) + } if rf, ok := ret.Get(0).(func(context.Context, int) []*models.Tag); ok { r0 = rf(ctx, parentID) } else { @@ -371,7 +417,6 @@ func (_m *TagReaderWriter) FindByParentTagID(ctx context.Context, parentID int) } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, parentID) } else { @@ -386,6 +431,10 @@ func (_m *TagReaderWriter) FindByPerformerID(ctx context.Context, performerID in ret := _m.Called(ctx, performerID) var r0 []*models.Tag + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) ([]*models.Tag, error)); ok { + return rf(ctx, performerID) + } if rf, ok := ret.Get(0).(func(context.Context, int) []*models.Tag); ok { r0 = rf(ctx, performerID) } else { @@ -394,7 +443,6 @@ func (_m *TagReaderWriter) FindByPerformerID(ctx context.Context, performerID in } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, performerID) } else { @@ -409,6 +457,10 @@ func (_m *TagReaderWriter) FindBySceneID(ctx context.Context, sceneID int) ([]*m ret := _m.Called(ctx, sceneID) var r0 []*models.Tag + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) ([]*models.Tag, error)); ok { + return rf(ctx, sceneID) + } if rf, ok := ret.Get(0).(func(context.Context, int) []*models.Tag); ok { r0 = rf(ctx, sceneID) } else { @@ -417,7 +469,6 @@ func (_m *TagReaderWriter) FindBySceneID(ctx context.Context, sceneID int) ([]*m } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, sceneID) } else { @@ -432,6 +483,10 @@ func (_m *TagReaderWriter) FindBySceneMarkerID(ctx context.Context, sceneMarkerI ret := _m.Called(ctx, sceneMarkerID) var r0 []*models.Tag + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) ([]*models.Tag, error)); ok { + return rf(ctx, sceneMarkerID) + } if rf, ok := ret.Get(0).(func(context.Context, int) []*models.Tag); ok { r0 = rf(ctx, sceneMarkerID) } else { @@ -440,7 +495,6 @@ func (_m *TagReaderWriter) FindBySceneMarkerID(ctx context.Context, sceneMarkerI } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, sceneMarkerID) } else { @@ -455,6 +509,10 @@ func (_m *TagReaderWriter) FindByStashID(ctx context.Context, stashID models.Sta ret := _m.Called(ctx, stashID) var r0 []*models.Tag + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, models.StashID) ([]*models.Tag, error)); ok { + return rf(ctx, stashID) + } if rf, ok := ret.Get(0).(func(context.Context, models.StashID) []*models.Tag); ok { r0 = rf(ctx, stashID) } else { @@ -463,7 +521,6 @@ func (_m *TagReaderWriter) FindByStashID(ctx context.Context, stashID models.Sta } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, models.StashID) error); ok { r1 = rf(ctx, stashID) } else { @@ -478,6 +535,10 @@ func (_m *TagReaderWriter) FindByStashIDStatus(ctx context.Context, hasStashID b ret := _m.Called(ctx, hasStashID, stashboxEndpoint) var r0 []*models.Tag + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, bool, string) ([]*models.Tag, error)); ok { + return rf(ctx, hasStashID, stashboxEndpoint) + } if rf, ok := ret.Get(0).(func(context.Context, bool, string) []*models.Tag); ok { r0 = rf(ctx, hasStashID, stashboxEndpoint) } else { @@ -486,7 +547,6 @@ func (_m *TagReaderWriter) FindByStashIDStatus(ctx context.Context, hasStashID b } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, bool, string) error); ok { r1 = rf(ctx, hasStashID, stashboxEndpoint) } else { @@ -501,6 +561,10 @@ func (_m *TagReaderWriter) FindByStudioID(ctx context.Context, studioID int) ([] ret := _m.Called(ctx, studioID) var r0 []*models.Tag + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) ([]*models.Tag, error)); ok { + return rf(ctx, studioID) + } if rf, ok := ret.Get(0).(func(context.Context, int) []*models.Tag); ok { r0 = rf(ctx, studioID) } else { @@ -509,7 +573,6 @@ func (_m *TagReaderWriter) FindByStudioID(ctx context.Context, studioID int) ([] } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, studioID) } else { @@ -524,6 +587,10 @@ func (_m *TagReaderWriter) FindMany(ctx context.Context, ids []int) ([]*models.T ret := _m.Called(ctx, ids) var r0 []*models.Tag + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, []int) ([]*models.Tag, error)); ok { + return rf(ctx, ids) + } if rf, ok := ret.Get(0).(func(context.Context, []int) []*models.Tag); ok { r0 = rf(ctx, ids) } else { @@ -532,7 +599,6 @@ func (_m *TagReaderWriter) FindMany(ctx context.Context, ids []int) ([]*models.T } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, []int) error); ok { r1 = rf(ctx, ids) } else { @@ -547,6 +613,10 @@ func (_m *TagReaderWriter) GetAliases(ctx context.Context, relatedID int) ([]str ret := _m.Called(ctx, relatedID) var r0 []string + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) ([]string, error)); ok { + return rf(ctx, relatedID) + } if rf, ok := ret.Get(0).(func(context.Context, int) []string); ok { r0 = rf(ctx, relatedID) } else { @@ -555,7 +625,6 @@ func (_m *TagReaderWriter) GetAliases(ctx context.Context, relatedID int) ([]str } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, relatedID) } else { @@ -570,6 +639,10 @@ func (_m *TagReaderWriter) GetChildIDs(ctx context.Context, relatedID int) ([]in ret := _m.Called(ctx, relatedID) var r0 []int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) ([]int, error)); ok { + return rf(ctx, relatedID) + } if rf, ok := ret.Get(0).(func(context.Context, int) []int); ok { r0 = rf(ctx, relatedID) } else { @@ -578,7 +651,6 @@ func (_m *TagReaderWriter) GetChildIDs(ctx context.Context, relatedID int) ([]in } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, relatedID) } else { @@ -593,6 +665,10 @@ func (_m *TagReaderWriter) GetCustomFields(ctx context.Context, id int) (map[str ret := _m.Called(ctx, id) var r0 map[string]interface{} + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) (map[string]interface{}, error)); ok { + return rf(ctx, id) + } if rf, ok := ret.Get(0).(func(context.Context, int) map[string]interface{}); ok { r0 = rf(ctx, id) } else { @@ -601,7 +677,6 @@ func (_m *TagReaderWriter) GetCustomFields(ctx context.Context, id int) (map[str } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, id) } else { @@ -616,6 +691,10 @@ func (_m *TagReaderWriter) GetCustomFieldsBulk(ctx context.Context, ids []int) ( ret := _m.Called(ctx, ids) var r0 []models.CustomFieldMap + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, []int) ([]models.CustomFieldMap, error)); ok { + return rf(ctx, ids) + } if rf, ok := ret.Get(0).(func(context.Context, []int) []models.CustomFieldMap); ok { r0 = rf(ctx, ids) } else { @@ -624,7 +703,6 @@ func (_m *TagReaderWriter) GetCustomFieldsBulk(ctx context.Context, ids []int) ( } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, []int) error); ok { r1 = rf(ctx, ids) } else { @@ -639,6 +717,10 @@ func (_m *TagReaderWriter) GetImage(ctx context.Context, tagID int) ([]byte, err ret := _m.Called(ctx, tagID) var r0 []byte + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) ([]byte, error)); ok { + return rf(ctx, tagID) + } if rf, ok := ret.Get(0).(func(context.Context, int) []byte); ok { r0 = rf(ctx, tagID) } else { @@ -647,7 +729,6 @@ func (_m *TagReaderWriter) GetImage(ctx context.Context, tagID int) ([]byte, err } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, tagID) } else { @@ -662,6 +743,10 @@ func (_m *TagReaderWriter) GetParentIDs(ctx context.Context, relatedID int) ([]i ret := _m.Called(ctx, relatedID) var r0 []int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) ([]int, error)); ok { + return rf(ctx, relatedID) + } if rf, ok := ret.Get(0).(func(context.Context, int) []int); ok { r0 = rf(ctx, relatedID) } else { @@ -670,7 +755,6 @@ func (_m *TagReaderWriter) GetParentIDs(ctx context.Context, relatedID int) ([]i } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, relatedID) } else { @@ -685,6 +769,10 @@ func (_m *TagReaderWriter) GetStashIDs(ctx context.Context, relatedID int) ([]mo ret := _m.Called(ctx, relatedID) var r0 []models.StashID + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) ([]models.StashID, error)); ok { + return rf(ctx, relatedID) + } if rf, ok := ret.Get(0).(func(context.Context, int) []models.StashID); ok { r0 = rf(ctx, relatedID) } else { @@ -693,7 +781,6 @@ func (_m *TagReaderWriter) GetStashIDs(ctx context.Context, relatedID int) ([]mo } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, relatedID) } else { @@ -708,13 +795,16 @@ func (_m *TagReaderWriter) HasImage(ctx context.Context, tagID int) (bool, error ret := _m.Called(ctx, tagID) var r0 bool + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) (bool, error)); ok { + return rf(ctx, tagID) + } if rf, ok := ret.Get(0).(func(context.Context, int) bool); ok { r0 = rf(ctx, tagID) } else { r0 = ret.Get(0).(bool) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, tagID) } else { @@ -743,6 +833,11 @@ func (_m *TagReaderWriter) Query(ctx context.Context, tagFilter *models.TagFilte ret := _m.Called(ctx, tagFilter, findFilter) var r0 []*models.Tag + var r1 int + var r2 error + if rf, ok := ret.Get(0).(func(context.Context, *models.TagFilterType, *models.FindFilterType) ([]*models.Tag, int, error)); ok { + return rf(ctx, tagFilter, findFilter) + } if rf, ok := ret.Get(0).(func(context.Context, *models.TagFilterType, *models.FindFilterType) []*models.Tag); ok { r0 = rf(ctx, tagFilter, findFilter) } else { @@ -751,14 +846,12 @@ func (_m *TagReaderWriter) Query(ctx context.Context, tagFilter *models.TagFilte } } - var r1 int if rf, ok := ret.Get(1).(func(context.Context, *models.TagFilterType, *models.FindFilterType) int); ok { r1 = rf(ctx, tagFilter, findFilter) } else { r1 = ret.Get(1).(int) } - var r2 error if rf, ok := ret.Get(2).(func(context.Context, *models.TagFilterType, *models.FindFilterType) error); ok { r2 = rf(ctx, tagFilter, findFilter) } else { @@ -773,6 +866,10 @@ func (_m *TagReaderWriter) QueryForAutoTag(ctx context.Context, words []string) ret := _m.Called(ctx, words) var r0 []*models.Tag + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, []string) ([]*models.Tag, error)); ok { + return rf(ctx, words) + } if rf, ok := ret.Get(0).(func(context.Context, []string) []*models.Tag); ok { r0 = rf(ctx, words) } else { @@ -781,7 +878,6 @@ func (_m *TagReaderWriter) QueryForAutoTag(ctx context.Context, words []string) } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, []string) error); ok { r1 = rf(ctx, words) } else { @@ -880,6 +976,10 @@ func (_m *TagReaderWriter) UpdatePartial(ctx context.Context, id int, updateTag ret := _m.Called(ctx, id, updateTag) var r0 *models.Tag + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int, models.TagPartial) (*models.Tag, error)); ok { + return rf(ctx, id, updateTag) + } if rf, ok := ret.Get(0).(func(context.Context, int, models.TagPartial) *models.Tag); ok { r0 = rf(ctx, id, updateTag) } else { @@ -888,7 +988,6 @@ func (_m *TagReaderWriter) UpdatePartial(ctx context.Context, id int, updateTag } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int, models.TagPartial) error); ok { r1 = rf(ctx, id, updateTag) } else { @@ -897,3 +996,18 @@ func (_m *TagReaderWriter) UpdatePartial(ctx context.Context, id int, updateTag return r0, r1 } + +type mockConstructorTestingTNewTagReaderWriter interface { + mock.TestingT + Cleanup(func()) +} + +// NewTagReaderWriter creates a new instance of TagReaderWriter. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewTagReaderWriter(t mockConstructorTestingTNewTagReaderWriter) *TagReaderWriter { + mock := &TagReaderWriter{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/pkg/models/model_group.go b/pkg/models/model_group.go index 5bfb42c443..6164a73a3c 100644 --- a/pkg/models/model_group.go +++ b/pkg/models/model_group.go @@ -8,7 +8,6 @@ import ( type Group struct { ID int `json:"id"` Name string `json:"name"` - Aliases string `json:"aliases"` Duration *int `json:"duration"` Date *Date `json:"date"` // Rating expressed in 1-100 scale @@ -19,8 +18,9 @@ type Group struct { CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` - URLs RelatedStrings `json:"urls"` - TagIDs RelatedIDs `json:"tag_ids"` + Aliases RelatedStrings `json:"aliases"` + URLs RelatedStrings `json:"urls"` + TagIDs RelatedIDs `json:"tag_ids"` ContainingGroups RelatedGroupDescriptions `json:"containing_groups"` SubGroups RelatedGroupDescriptions `json:"sub_groups"` @@ -66,22 +66,29 @@ func (m *Group) LoadSubGroupIDs(ctx context.Context, l SubGroupLoader) error { }) } +func (m *Group) LoadAliases(ctx context.Context, l AliasLoader) error { + return m.Aliases.load(func() ([]string, error) { + return l.GetAliases(ctx, m.ID) + }) +} + type GroupPartial struct { Name OptionalString - Aliases OptionalString Duration OptionalInt Date OptionalDate // Rating expressed in 1-100 scale - Rating OptionalInt - StudioID OptionalInt - Director OptionalString - Synopsis OptionalString + Rating OptionalInt + StudioID OptionalInt + Director OptionalString + Synopsis OptionalString + CreatedAt OptionalTime + UpdatedAt OptionalTime + + Aliases *UpdateStrings URLs *UpdateStrings TagIDs *UpdateIDs ContainingGroups *UpdateGroupDescriptions SubGroups *UpdateGroupDescriptions - CreatedAt OptionalTime - UpdatedAt OptionalTime CustomFields CustomFieldsInput } diff --git a/pkg/models/model_scraped_item.go b/pkg/models/model_scraped_item.go index 03865ac105..249eb86a5b 100644 --- a/pkg/models/model_scraped_item.go +++ b/pkg/models/model_scraped_item.go @@ -605,7 +605,6 @@ func (m ScrapedMovie) ScrapedGroup() ScrapedGroup { ret := ScrapedGroup{ StoredID: m.StoredID, Name: m.Name, - Aliases: m.Aliases, Duration: m.Duration, Date: m.Date, Rating: m.Rating, @@ -622,6 +621,11 @@ func (m ScrapedMovie) ScrapedGroup() ScrapedGroup { ret.URLs = []string{*m.URL} } + // Movies are largely deprecated; these are just gluing fixes. + if m.Aliases != nil { + ret.Aliases = []string{*m.Aliases} + } + return ret } @@ -629,7 +633,7 @@ func (m ScrapedMovie) ScrapedGroup() ScrapedGroup { type ScrapedGroup struct { StoredID *string `json:"stored_id"` Name *string `json:"name"` - Aliases *string `json:"aliases"` + Aliases []string `json:"aliases"` Duration *string `json:"duration"` Date *string `json:"date"` Rating *string `json:"rating"` @@ -651,7 +655,6 @@ func (g ScrapedGroup) ScrapedMovie() ScrapedMovie { ret := ScrapedMovie{ StoredID: g.StoredID, Name: g.Name, - Aliases: g.Aliases, Duration: g.Duration, Date: g.Date, Rating: g.Rating, @@ -668,6 +671,11 @@ func (g ScrapedGroup) ScrapedMovie() ScrapedMovie { ret.URL = &g.URLs[0] } + // Movies are largely deprecated; these are just gluing fixes. + if len(g.Aliases) > 0 { + ret.Aliases = &g.Aliases[0] + } + return ret } diff --git a/pkg/models/repository_group.go b/pkg/models/repository_group.go index d7f74de64c..00da10c088 100644 --- a/pkg/models/repository_group.go +++ b/pkg/models/repository_group.go @@ -69,6 +69,7 @@ type GroupReader interface { ContainingGroupLoader SubGroupLoader CustomFieldsReader + AliasLoader All(ctx context.Context) ([]*Group, error) GetFrontImage(ctx context.Context, groupID int) ([]byte, error) diff --git a/pkg/models/stash_box.go b/pkg/models/stash_box.go index 6a254a3f97..9c58651cd1 100644 --- a/pkg/models/stash_box.go +++ b/pkg/models/stash_box.go @@ -1,9 +1,10 @@ package models type StashBoxFingerprint struct { - Algorithm string `json:"algorithm"` - Hash string `json:"hash"` - Duration int `json:"duration"` + Algorithm string `json:"algorithm"` + Hash string `json:"hash"` + Duration int `json:"duration"` + Submissions int `json:"submissions"` } type StashBox struct { diff --git a/pkg/scraper/mapped_result.go b/pkg/scraper/mapped_result.go index 64cc97ec7a..3962f32604 100644 --- a/pkg/scraper/mapped_result.go +++ b/pkg/scraper/mapped_result.go @@ -239,7 +239,7 @@ func (r mappedResult) scrapedMovie() *models.ScrapedMovie { func (r mappedResult) scrapedGroup() *models.ScrapedGroup { ret := &models.ScrapedGroup{ Name: r.stringPtr("Name"), - Aliases: r.stringPtr("Aliases"), + Aliases: r.stringSlice("Aliases"), URL: r.stringPtr("URL"), URLs: r.stringSlice("URLs"), Duration: r.stringPtr("Duration"), diff --git a/pkg/scraper/mapped_result_test.go b/pkg/scraper/mapped_result_test.go index db6d921bfd..8ab312c0fc 100644 --- a/pkg/scraper/mapped_result_test.go +++ b/pkg/scraper/mapped_result_test.go @@ -812,7 +812,7 @@ func TestMappedResultScrapedGroup(t *testing.T) { name: "full group", data: mappedResult{ "Name": "Group Title", - "Aliases": "Group Alias", + "Aliases": []string{"Group Alias", "Group Alias 2"}, "URL": "https://example.com/group", "URLs": []string{"url1", "url2"}, "Duration": "240 minutes", @@ -825,7 +825,7 @@ func TestMappedResultScrapedGroup(t *testing.T) { validate: func(t *testing.T, g *models.ScrapedGroup) { assert.NotNil(t, g) assert.Equal(t, "Group Title", *g.Name) - assert.Equal(t, "Group Alias", *g.Aliases) + assert.Equal(t, []string{"Group Alias", "Group Alias 2"}, g.Aliases) assert.Equal(t, "https://example.com/group", *g.URL) assert.Equal(t, []string{"url1", "url2"}, g.URLs) assert.Equal(t, "240 minutes", *g.Duration) diff --git a/pkg/sqlite/anonymise.go b/pkg/sqlite/anonymise.go index ace3061690..64c69a800e 100644 --- a/pkg/sqlite/anonymise.go +++ b/pkg/sqlite/anonymise.go @@ -904,7 +904,6 @@ func (db *Anonymiser) anonymiseGroups(ctx context.Context) error { query := dialect.From(table).Select( table.Col(idColumn), table.Col("name"), - table.Col("aliases"), table.Col("description"), table.Col("director"), ).Where(table.Col(idColumn).Gt(lastID)).Limit(1000) @@ -916,7 +915,6 @@ func (db *Anonymiser) anonymiseGroups(ctx context.Context) error { var ( id int name sql.NullString - aliases sql.NullString description sql.NullString director sql.NullString ) @@ -924,7 +922,6 @@ func (db *Anonymiser) anonymiseGroups(ctx context.Context) error { if err := rows.Scan( &id, &name, - &aliases, &description, &director, ); err != nil { @@ -933,7 +930,6 @@ func (db *Anonymiser) anonymiseGroups(ctx context.Context) error { set := goqu.Record{} db.obfuscateNullString(set, "name", name) - db.obfuscateNullString(set, "aliases", aliases) db.obfuscateNullString(set, "description", description) db.obfuscateNullString(set, "director", director) @@ -960,6 +956,10 @@ func (db *Anonymiser) anonymiseGroups(ctx context.Context) error { } } + if err := db.anonymiseAliases(ctx, goqu.T(groupAliasesTable), "group_id"); err != nil { + return err + } + if err := db.anonymiseURLs(ctx, goqu.T(groupURLsTable), "group_id"); err != nil { return err } diff --git a/pkg/sqlite/database.go b/pkg/sqlite/database.go index 7c383dc4ca..026a18c081 100644 --- a/pkg/sqlite/database.go +++ b/pkg/sqlite/database.go @@ -34,7 +34,7 @@ const ( cacheSizeEnv = "STASH_SQLITE_CACHE_SIZE" ) -var appSchemaVersion uint = 85 +var appSchemaVersion uint = 86 //go:embed migrations/*.sql var migrationsBox embed.FS diff --git a/pkg/sqlite/file.go b/pkg/sqlite/file.go index dc5ba06370..d8b1265ed4 100644 --- a/pkg/sqlite/file.go +++ b/pkg/sqlite/file.go @@ -7,7 +7,6 @@ import ( "fmt" "io/fs" "path/filepath" - "strings" "time" "github.com/doug-martin/goqu/v9" @@ -624,7 +623,7 @@ func (qb *FileStore) find(ctx context.Context, id models.FileID) (models.File, e return ret, nil } -// FindByPath returns the first file that matches the given path. Wildcard characters are supported. +// FindByPath returns the first file that matches the given path. The * wildcard is supported. func (qb *FileStore) FindByPath(ctx context.Context, p string, caseSensitive bool) (models.File, error) { ret, err := qb.FindAllByPath(ctx, p, caseSensitive) @@ -641,28 +640,29 @@ func (qb *FileStore) FindByPath(ctx context.Context, p string, caseSensitive boo } // FindAllByPath returns all the files that match the given path. -// Wildcard characters are supported. +// The * wildcard is supported. func (qb *FileStore) FindAllByPath(ctx context.Context, p string, caseSensitive bool) ([]models.File, error) { // separate basename from path basename := filepath.Base(p) dirName := filepath.Dir(p) - // replace wildcards - basename = strings.ReplaceAll(basename, "*", "%") - dirName = strings.ReplaceAll(dirName, "*", "%") - table := qb.table() folderTable := folderTableMgr.table - // like uses case-insensitive matching. Only use like if wildcards are used q := qb.selectDataset().Prepared(true) - if strings.Contains(basename, "%") || strings.Contains(dirName, "%") || !caseSensitive { + switch { + case pathHasWildcard(basename) || pathHasWildcard(dirName): + q = q.Where( + pathLike(folderTable.Col("path"), dirName), + pathLike(table.Col("basename"), basename), + ) + case !caseSensitive: q = q.Where( - folderTable.Col("path").Like(dirName), - table.Col("basename").Like(basename), + pathEqNoCase(folderTable.Col("path"), dirName), + pathEqNoCase(table.Col("basename"), basename), ) - } else { + default: q = q.Where( folderTable.Col("path").Eq(dirName), table.Col("basename").Eq(basename), diff --git a/pkg/sqlite/file_test.go b/pkg/sqlite/file_test.go index 55c41f4f7f..009da1d85d 100644 --- a/pkg/sqlite/file_test.go +++ b/pkg/sqlite/file_test.go @@ -562,6 +562,55 @@ func Test_FileStore_FindByPath(t *testing.T) { } } +func TestFileStore_FindByPathCaseInsensitiveTreatsLikeWildcardsLiterally(t *testing.T) { + qb := db.File + + runWithRollbackTxn(t, "literal wildcards", func(t *testing.T, ctx context.Context) { + now := time.Now() + makeTestFile := func(basename string) models.File { + return &models.BaseFile{ + Path: getFilePath(folderIdxWithFiles, basename), + ParentFolderID: folderIDs[folderIdxWithFiles], + Basename: basename, + Size: 1, + DirEntry: models.DirEntry{ + ModTime: now, + }, + CreatedAt: now, + UpdatedAt: now, + } + } + + ambiguous := makeTestFile("scan_lookup_AB1.mp4") + if !assert.NoError(t, qb.Create(ctx, ambiguous)) { + return + } + + got, err := qb.FindByPath(ctx, getFilePath(folderIdxWithFiles, "scan_lookup_A_1.mp4"), false) + assert.NoError(t, err) + assert.Nil(t, got) + + got, err = qb.FindByPath(ctx, getFilePath(folderIdxWithFiles, "scan_lookup_A%1.mp4"), false) + assert.NoError(t, err) + assert.Nil(t, got) + + literalUnderscore := makeTestFile("scan_lookup_A_1.mp4") + if !assert.NoError(t, qb.Create(ctx, literalUnderscore)) { + return + } + + got, err = qb.FindByPath(ctx, getFilePath(folderIdxWithFiles, "SCAN_LOOKUP_a_1.MP4"), false) + assert.NoError(t, err) + if assert.NotNil(t, got) { + assert.Equal(t, literalUnderscore.Base().ID, got.Base().ID) + } + + wildcardMatches, err := qb.FindAllByPath(ctx, getFilePath(folderIdxWithFiles, "scan_lookup_A*1.mp4"), true) + assert.NoError(t, err) + assert.Len(t, wildcardMatches, 2) + }) +} + func TestFileStore_FindByFingerprint(t *testing.T) { tests := []struct { name string diff --git a/pkg/sqlite/folder.go b/pkg/sqlite/folder.go index 957e98e0f1..f12789c383 100644 --- a/pkg/sqlite/folder.go +++ b/pkg/sqlite/folder.go @@ -296,12 +296,11 @@ func (qb *FolderStore) FindMany(ctx context.Context, ids []models.FolderID) ([]* } func (qb *FolderStore) FindByPath(ctx context.Context, p string, caseSensitive bool) (*models.Folder, error) { - // use like for case insensitive search - var criterion exp.BooleanExpression + var criterion exp.Expression if caseSensitive { criterion = qb.table().Col("path").Eq(p) } else { - criterion = qb.table().Col("path").ILike(p) + criterion = pathEqNoCase(qb.table().Col("path"), p) } q := qb.selectDataset().Prepared(true).Where(criterion) diff --git a/pkg/sqlite/folder_test.go b/pkg/sqlite/folder_test.go index 072a1167f9..408b02b0ff 100644 --- a/pkg/sqlite/folder_test.go +++ b/pkg/sqlite/folder_test.go @@ -238,6 +238,48 @@ func Test_FolderStore_FindByPath(t *testing.T) { } } +func TestFolderStore_FindByPathCaseInsensitiveTreatsLikeWildcardsLiterally(t *testing.T) { + qb := db.Folder + + runWithRollbackTxn(t, "literal wildcards", func(t *testing.T, ctx context.Context) { + now := time.Now() + makeTestFolder := func(path string) *models.Folder { + return &models.Folder{ + Path: path, + DirEntry: models.DirEntry{ + ModTime: now, + }, + CreatedAt: now, + UpdatedAt: now, + } + } + + ambiguous := makeTestFolder("folder_lookup_AB1") + if !assert.NoError(t, qb.Create(ctx, ambiguous)) { + return + } + + got, err := qb.FindByPath(ctx, "folder_lookup_A_1", false) + assert.NoError(t, err) + assert.Nil(t, got) + + got, err = qb.FindByPath(ctx, "folder_lookup_A%1", false) + assert.NoError(t, err) + assert.Nil(t, got) + + literalUnderscore := makeTestFolder("folder_lookup_A_1") + if !assert.NoError(t, qb.Create(ctx, literalUnderscore)) { + return + } + + got, err = qb.FindByPath(ctx, "FOLDER_LOOKUP_a_1", false) + assert.NoError(t, err) + if assert.NotNil(t, got) { + assert.Equal(t, literalUnderscore.ID, got.ID) + } + }) +} + func Test_FolderStore_GetManyParentFolderIDs(t *testing.T) { var empty []models.FolderID emptyResult := [][]models.FolderID{empty} diff --git a/pkg/sqlite/gallery_filter.go b/pkg/sqlite/gallery_filter.go index 6b2a604f36..355dbdce8b 100644 --- a/pkg/sqlite/gallery_filter.go +++ b/pkg/sqlite/gallery_filter.go @@ -332,7 +332,7 @@ func (qb *galleryFilterHandler) parentFolderCriterionHandler(folder *models.Hier return } - f.addWhere(fmt.Sprintf("files.parent_folder_id NOT IN (SELECT column2 FROM (%s)) OR folders.parent_folder_id IS NULL", valuesClause)) + f.addWhere(fmt.Sprintf("files.parent_folder_id NOT IN (SELECT column2 FROM (%s)) OR files.parent_folder_id IS NULL", valuesClause)) f.addWhere(fmt.Sprintf("gallery_folder.parent_folder_id NOT IN (SELECT column2 FROM (%s)) OR gallery_folder.parent_folder_id IS NULL", valuesClause)) } } diff --git a/pkg/sqlite/gallery_test.go b/pkg/sqlite/gallery_test.go index fd7f55d78b..c04b4be11b 100644 --- a/pkg/sqlite/gallery_test.go +++ b/pkg/sqlite/gallery_test.go @@ -1586,6 +1586,43 @@ func TestGalleryQueryPath(t *testing.T) { } } +func TestGalleryQueryParentFolderExcludes(t *testing.T) { + tests := []struct { + name string + folderIdx int + depth int + }{ + { + name: "exact folder", + folderIdx: folderIdxWithGalleryFiles, + depth: 0, + }, + { + name: "parent folder recursively", + folderIdx: folderIdxForObjectFiles, + depth: -1, + }, + } + + for _, tt := range tests { + runWithRollbackTxn(t, tt.name, func(t *testing.T, ctx context.Context) { + galleries, count, err := db.Gallery.Query(ctx, &models.GalleryFilterType{ + ParentFolder: &models.HierarchicalMultiCriterionInput{ + Value: []string{ + strconv.Itoa(int(folderIDs[tt.folderIdx])), + }, + Modifier: models.CriterionModifierExcludes, + Depth: &tt.depth, + }, + }, nil) + require.NoError(t, err) + require.Equal(t, 1, count) + require.Len(t, galleries, 1) + assert.Equal(t, galleryIDs[galleryIdxWithoutFile], galleries[0].ID) + }) + } +} + func verifyGalleriesPath(ctx context.Context, t *testing.T, pathCriterion models.StringCriterionInput) { galleryFilter := models.GalleryFilterType{ Path: &pathCriterion, diff --git a/pkg/sqlite/group.go b/pkg/sqlite/group.go index a20abd6ac6..00150a74c5 100644 --- a/pkg/sqlite/group.go +++ b/pkg/sqlite/group.go @@ -29,12 +29,14 @@ const ( groupURLColumn = "url" groupRelationsTable = "groups_relations" + + groupAliasesTable = "group_aliases" + groupAliasColumn = "alias" ) type groupRow struct { ID int `db:"id" goqu:"skipinsert"` Name zero.String `db:"name"` - Aliases zero.String `db:"aliases"` Duration null.Int `db:"duration"` Date NullDate `db:"date"` DatePrecision null.Int `db:"date_precision"` @@ -54,7 +56,6 @@ type groupRow struct { func (r *groupRow) fromGroup(o models.Group) { r.ID = o.ID r.Name = zero.StringFrom(o.Name) - r.Aliases = zero.StringFrom(o.Aliases) r.Duration = intFromPtr(o.Duration) r.Date = NullDateFromDatePtr(o.Date) r.DatePrecision = datePrecisionFromDatePtr(o.Date) @@ -70,7 +71,6 @@ func (r *groupRow) resolve() *models.Group { ret := &models.Group{ ID: r.ID, Name: r.Name.String, - Aliases: r.Aliases.String, Duration: nullIntPtr(r.Duration), Date: r.Date.DatePtr(r.DatePrecision), Rating: nullIntPtr(r.Rating), @@ -90,7 +90,6 @@ type groupRowRecord struct { func (r *groupRowRecord) fromPartial(o models.GroupPartial) { r.setNullString("name", o.Name) - r.setNullString("aliases", o.Aliases) r.setNullInt("duration", o.Duration) r.setNullDate("date", "date_precision", o.Date) r.setNullInt("rating", o.Rating) @@ -178,6 +177,12 @@ func (qb *GroupStore) Create(ctx context.Context, newObject *models.Group) error return err } + if newObject.Aliases.Loaded() { + if err := groupAliasesTableMgr.insertJoins(ctx, id, newObject.Aliases.List()); err != nil { + return err + } + } + if newObject.URLs.Loaded() { const startPos = 0 if err := groupsURLsTableMgr.insertJoins(ctx, id, startPos, newObject.URLs.List()); err != nil { @@ -222,6 +227,12 @@ func (qb *GroupStore) UpdatePartial(ctx context.Context, id int, partial models. } } + if partial.Aliases != nil { + if err := groupAliasesTableMgr.modifyJoins(ctx, id, partial.Aliases.Values, partial.Aliases.Mode); err != nil { + return nil, err + } + } + if partial.URLs != nil { if err := groupsURLsTableMgr.modifyJoins(ctx, id, partial.URLs.Values, partial.URLs.Mode); err != nil { return nil, err @@ -255,6 +266,12 @@ func (qb *GroupStore) Update(ctx context.Context, updatedObject *models.Group) e return err } + if updatedObject.Aliases.Loaded() { + if err := groupAliasesTableMgr.replaceJoins(ctx, updatedObject.ID, updatedObject.Aliases.List()); err != nil { + return err + } + } + if updatedObject.URLs.Loaded() { if err := groupsURLsTableMgr.replaceJoins(ctx, updatedObject.ID, updatedObject.URLs.List()); err != nil { return err @@ -441,7 +458,8 @@ func (qb *GroupStore) makeQuery(ctx context.Context, groupFilter *models.GroupFi distinctIDs(&query, groupTable) if q := findFilter.Q; q != nil && *q != "" { - searchColumns := []string{"groups.name", "groups.aliases"} + query.join(groupAliasesTable, "", "group_aliases.group_id = groups.id") + searchColumns := []string{"groups.name", "group_aliases.alias"} query.parseQueryString(searchColumns, *q) } @@ -730,3 +748,7 @@ func (qb *GroupStore) sortByOCounter(direction string) string { // need to sum the o_counter from scenes and images return " ORDER BY (" + selectGroupOCountSQL + ") " + direction } + +func (qb *GroupStore) GetAliases(ctx context.Context, groupId int) ([]string, error) { + return groupAliasesTableMgr.get(ctx, groupId) +} diff --git a/pkg/sqlite/group_filter.go b/pkg/sqlite/group_filter.go index 6054141857..f5e7a01c4d 100644 --- a/pkg/sqlite/group_filter.go +++ b/pkg/sqlite/group_filter.go @@ -131,9 +131,12 @@ func (qb *groupFilterHandler) missingCriterionHandler(isMissing *string) criteri case "tags": groupRepository.tags.leftJoin(f, "tags_join", "groups.id") f.addWhere("tags_join.group_id IS NULL") + case "aliases": + groupAliasesTableMgr.leftJoin(f, "", "groups.id") + f.addWhere("group_aliases.alias IS NULL") default: if err := validateIsMissing(*isMissing, []string{ - "aliases", "description", "director", "date", "rating", + "description", "director", "date", "rating", }); err != nil { f.setError(err) return diff --git a/pkg/sqlite/group_test.go b/pkg/sqlite/group_test.go index f4386b07a5..3f5adad797 100644 --- a/pkg/sqlite/group_test.go +++ b/pkg/sqlite/group_test.go @@ -40,6 +40,11 @@ func loadGroupRelationships(ctx context.Context, expected models.Group, actual * return err } } + if expected.Aliases.Loaded() { + if err := actual.LoadAliases(ctx, db.Group); err != nil { + return err + } + } return nil } @@ -48,7 +53,7 @@ func Test_GroupStore_Create(t *testing.T) { var ( name = "name" url = "url" - aliases = "alias1, alias2" + aliases = []string{"alias1", "alias2"} director = "director" rating = 60 duration = 34 @@ -83,7 +88,7 @@ func Test_GroupStore_Create(t *testing.T) { SubGroups: models.NewRelatedGroupDescriptions([]models.GroupIDDescription{ {GroupID: groupIDs[groupIdxWithStudio], Description: subGroupDescription}, }), - Aliases: aliases, + Aliases: models.NewRelatedStrings(aliases), CreatedAt: createdAt, UpdatedAt: updatedAt, }, @@ -170,7 +175,7 @@ func Test_groupQueryBuilder_Update(t *testing.T) { var ( name = "name" url = "url" - aliases = "alias1, alias2" + aliases = []string{"alias1", "alias2"} director = "director" rating = 60 duration = 34 @@ -206,7 +211,7 @@ func Test_groupQueryBuilder_Update(t *testing.T) { SubGroups: models.NewRelatedGroupDescriptions([]models.GroupIDDescription{ {GroupID: groupIDs[groupIdxWithStudio], Description: subGroupDescription}, }), - Aliases: aliases, + Aliases: models.NewRelatedStrings(aliases), CreatedAt: createdAt, UpdatedAt: updatedAt, }, @@ -311,7 +316,7 @@ func Test_groupQueryBuilder_Update(t *testing.T) { var clearGroupPartial = models.GroupPartial{ // leave mandatory fields - Aliases: models.OptionalString{Set: true, Null: true}, + Aliases: &models.UpdateStrings{Mode: models.RelationshipUpdateModeSet}, Synopsis: models.OptionalString{Set: true, Null: true}, Director: models.OptionalString{Set: true, Null: true}, Duration: models.OptionalInt{Set: true, Null: true}, @@ -338,7 +343,7 @@ func Test_groupQueryBuilder_UpdatePartial(t *testing.T) { var ( name = "name" url = "url" - aliases = "alias1, alias2" + aliases = []string{"alias1", "alias2"} director = "director" rating = 60 duration = 34 @@ -364,7 +369,10 @@ func Test_groupQueryBuilder_UpdatePartial(t *testing.T) { Name: models.NewOptionalString(name), Director: models.NewOptionalString(director), Synopsis: models.NewOptionalString(synopsis), - Aliases: models.NewOptionalString(aliases), + Aliases: &models.UpdateStrings{ + Values: aliases, + Mode: models.RelationshipUpdateModeSet, + }, URLs: &models.UpdateStrings{ Values: []string{url}, Mode: models.RelationshipUpdateModeSet, @@ -399,7 +407,7 @@ func Test_groupQueryBuilder_UpdatePartial(t *testing.T) { Name: name, Director: director, Synopsis: synopsis, - Aliases: aliases, + Aliases: models.NewRelatedStrings(aliases), URLs: models.NewRelatedStrings([]string{url}), Date: &date, Duration: &duration, diff --git a/pkg/sqlite/image.go b/pkg/sqlite/image.go index 5eb5e1add0..b0bcd877b0 100644 --- a/pkg/sqlite/image.go +++ b/pkg/sqlite/image.go @@ -1027,6 +1027,7 @@ var imageSortOptions = sortOptions{ "id", "o_counter", "path", + "performer_age", "performer_count", "random", "rating", @@ -1102,6 +1103,29 @@ func (qb *ImageStore) setImageSortAndPagination(q *queryBuilder, findFilter *mod addFilesJoin() addFolderJoin() sortClause = " ORDER BY COALESCE(images.title, files.basename) COLLATE NATURAL_CI " + direction + ", folders.path COLLATE NATURAL_CI " + direction + case "performer_age": + // Looking at the youngest performer by default + aggregation := "MIN" + if direction == "DESC" { + // When sorting by performer's age DESC, I should consider the oldest performer instead + aggregation = "MAX" + } + fallback := "NULL" + if direction == "ASC" { + // When sorting ascending, NULLs are first by default. Coalescing to the MAX int value supported by sqlite + fallback = "9223372036854775807" + } + sortClause = fmt.Sprintf( + " ORDER BY (SELECT COALESCE(%s(JulianDay(images.date) - JulianDay(performers.birthdate)), %s) FROM %s as performers INNER JOIN %s AS aggregation WHERE performers.id = aggregation.%s AND aggregation.%s = %s.id) %s", + aggregation, + fallback, + performerTable, + performersImagesTable, + performerIDColumn, + imageIDColumn, + imageTable, + getSortDirection(direction), + ) default: sortClause = getSort(sort, direction, "images") } diff --git a/pkg/sqlite/image_test.go b/pkg/sqlite/image_test.go index bd29e75c79..1cc585d419 100644 --- a/pkg/sqlite/image_test.go +++ b/pkg/sqlite/image_test.go @@ -3088,6 +3088,13 @@ func TestImageQuerySorting(t *testing.T) { imageIdxWithTwoGalleries, imageIdxWithGrandChildStudio, }, + { + "performer_age", + "performer_age", + models.SortDirectionEnumDesc, + -1, + -1, + }, } qb := db.Image diff --git a/pkg/sqlite/migrations/86_group_aliases.up.sql b/pkg/sqlite/migrations/86_group_aliases.up.sql new file mode 100644 index 0000000000..2474018c2d --- /dev/null +++ b/pkg/sqlite/migrations/86_group_aliases.up.sql @@ -0,0 +1,21 @@ +CREATE TABLE `group_aliases` ( + `group_id` integer NOT NULL, + `alias` varchar(255) NOT NULL, + PRIMARY KEY(`group_id`, `alias`), + foreign key(`group_id`) references `groups`(`id`) on delete CASCADE +); + +CREATE INDEX `index_group_aliases_on_alias` on `group_aliases` (`alias`); + +-- copy data from group table where aliases is just a string +-- skip NULL rows to satisfy the NOT NULL constraint +INSERT INTO `group_aliases` ( + `group_id`, + `alias` +) SELECT + `id`, + `aliases` +FROM `groups` +WHERE `aliases` IS NOT NULL; + +ALTER TABLE `groups` DROP COLUMN `aliases`; \ No newline at end of file diff --git a/pkg/sqlite/path_match.go b/pkg/sqlite/path_match.go new file mode 100644 index 0000000000..421b4d260e --- /dev/null +++ b/pkg/sqlite/path_match.go @@ -0,0 +1,39 @@ +package sqlite + +import ( + "strings" + + "github.com/doug-martin/goqu/v9" + "github.com/doug-martin/goqu/v9/exp" +) + +func pathHasWildcard(path string) bool { + return strings.Contains(path, "*") +} + +func pathToLikePattern(path string) string { + var b strings.Builder + b.Grow(len(path)) + + for _, r := range path { + switch r { + case '\\', '%', '_': + b.WriteRune('\\') + case '*': + b.WriteRune('%') + continue + } + + b.WriteRune(r) + } + + return b.String() +} + +func pathLike(col exp.IdentifierExpression, pattern string) exp.Expression { + return goqu.L(`? LIKE ? ESCAPE '\'`, col, pathToLikePattern(pattern)) +} + +func pathEqNoCase(col exp.IdentifierExpression, value string) exp.Expression { + return goqu.L("? = ? COLLATE NOCASE", col, value) +} diff --git a/pkg/sqlite/scene.go b/pkg/sqlite/scene.go index 36b5f3df35..5f60b43d12 100644 --- a/pkg/sqlite/scene.go +++ b/pkg/sqlite/scene.go @@ -721,20 +721,25 @@ func (qb *SceneStore) FindByPath(ctx context.Context, p string) ([]*models.Scene basename := filepath.Base(p) dir := filepath.Dir(p) - // replace wildcards - basename = strings.ReplaceAll(basename, "*", "%") - dir = strings.ReplaceAll(dir, "*", "%") - sq := dialect.From(scenesFilesJoinTable).InnerJoin( filesTable, goqu.On(filesTable.Col(idColumn).Eq(scenesFilesJoinTable.Col(fileIDColumn))), ).InnerJoin( foldersTable, goqu.On(foldersTable.Col(idColumn).Eq(filesTable.Col("parent_folder_id"))), - ).Select(scenesFilesJoinTable.Col(sceneIDColumn)).Where( - foldersTable.Col("path").Like(dir), - filesTable.Col("basename").Like(basename), - ) + ).Select(scenesFilesJoinTable.Col(sceneIDColumn)) + + if pathHasWildcard(basename) || pathHasWildcard(dir) { + sq = sq.Where( + pathLike(foldersTable.Col("path"), dir), + pathLike(filesTable.Col("basename"), basename), + ) + } else { + sq = sq.Where( + pathEqNoCase(foldersTable.Col("path"), dir), + pathEqNoCase(filesTable.Col("basename"), basename), + ) + } ret, err := qb.findBySubquery(ctx, sq) if err != nil && !errors.Is(err, sql.ErrNoRows) { diff --git a/pkg/sqlite/scene_filter.go b/pkg/sqlite/scene_filter.go index 40fc223e42..e1ee8ec1d1 100644 --- a/pkg/sqlite/scene_filter.go +++ b/pkg/sqlite/scene_filter.go @@ -110,7 +110,10 @@ func (qb *sceneFilterHandler) criterionHandler() criterionHandler { &phashDistanceCriterionHandler{ joinFn: func(f *filterBuilder) { - const joinType = joinTypeInner + joinType := joinTypeInner + if sceneFilter.PhashDistance.Modifier == models.CriterionModifierIsNull { + joinType = joinTypeLeft + } qb.addSceneFilesTable(f, joinType) f.addJoin(joinType, fingerprintTable, "fingerprints_phash", "scenes_files.file_id = fingerprints_phash.file_id AND fingerprints_phash.type = 'phash'") }, diff --git a/pkg/sqlite/scene_test.go b/pkg/sqlite/scene_test.go index 198ee55dc7..ce8b3d2b02 100644 --- a/pkg/sqlite/scene_test.go +++ b/pkg/sqlite/scene_test.go @@ -12,6 +12,7 @@ import ( "reflect" "regexp" "strconv" + "strings" "testing" "time" @@ -1738,6 +1739,12 @@ func Test_sceneQueryBuilder_FindByPath(t *testing.T) { []*models.Scene{makeSceneWithID(sceneIdxWithSpacedName)}, false, }, + { + "case insensitive", + strings.ToUpper(getPath(sceneIdxWithSpacedName)), + []*models.Scene{makeSceneWithID(sceneIdxWithSpacedName)}, + false, + }, { "invalid", "invalid path", @@ -3424,23 +3431,71 @@ func TestSceneQueryIsMissingRating(t *testing.T) { } func TestSceneQueryIsMissingPhash(t *testing.T) { - withTxn(func(ctx context.Context) error { - sqb := db.Scene - isMissing := "phash" - sceneFilter := models.SceneFilterType{ - IsMissing: &isMissing, - } + isMissing := "phash" - scenes := queryScene(ctx, t, sqb, &sceneFilter, nil) + tests := []struct { + name string + filter models.SceneFilterType + includeIdxs []int + excludeIdxs []int + wantErr bool + }{ + { + "is missing phash", + models.SceneFilterType{ + IsMissing: &isMissing, + }, + []int{sceneIdxMissingPhash}, + []int{sceneIdxWithGroup}, + false, + }, + { + "phash null", + models.SceneFilterType{ + Phash: &models.StringCriterionInput{ + Modifier: models.CriterionModifierIsNull, + }, + }, + []int{sceneIdxMissingPhash}, + []int{sceneIdxWithGroup}, + false, + }, + { + "phash distance null", + models.SceneFilterType{ + PhashDistance: &models.PhashDistanceCriterionInput{ + Modifier: models.CriterionModifierIsNull, + }, + }, + []int{sceneIdxMissingPhash}, + []int{sceneIdxWithGroup}, + false, + }, + } - if !assert.Len(t, scenes, 1) { - return nil - } + for _, tt := range tests { + runWithRollbackTxn(t, tt.name, func(t *testing.T, ctx context.Context) { + assert := assert.New(t) + + results, err := db.Scene.Query(ctx, models.SceneQueryOptions{ + SceneFilter: &tt.filter, + }) + if (err != nil) != tt.wantErr { + t.Errorf("SceneStore.Query() error = %v, wantErr %v", err, tt.wantErr) + return + } - assert.Equal(t, sceneIDs[sceneIdxMissingPhash], scenes[0].ID) + include := indexesToIDs(sceneIDs, tt.includeIdxs) + exclude := indexesToIDs(sceneIDs, tt.excludeIdxs) - return nil - }) + for _, i := range include { + assert.Contains(results.IDs, i) + } + for _, e := range exclude { + assert.NotContains(results.IDs, e) + } + }) + } } func TestSceneQueryPerformers(t *testing.T) { diff --git a/pkg/sqlite/tables.go b/pkg/sqlite/tables.go index 4c09113f0c..315ef665fb 100644 --- a/pkg/sqlite/tables.go +++ b/pkg/sqlite/tables.go @@ -49,6 +49,7 @@ var ( groupsTagsJoinTable = goqu.T(groupsTagsTable) groupRelationsJoinTable = goqu.T(groupRelationsTable) groupsCustomFieldsTable = goqu.T("group_custom_fields") + groupAliasesJoinTable = goqu.T(groupAliasesTable) tagsAliasesJoinTable = goqu.T(tagAliasesTable) tagRelationsJoinTable = goqu.T(tagRelationsTable) @@ -418,6 +419,14 @@ var ( groupRelationshipTableMgr = &table{ table: groupRelationsJoinTable, } + + groupAliasesTableMgr = &stringTable{ + table: table{ + table: groupAliasesJoinTable, + idColumn: groupAliasesJoinTable.Col(groupIDColumn), + }, + stringColumn: groupAliasesJoinTable.Col(groupAliasColumn), + } ) var ( diff --git a/pkg/stashbox/graphql/generated_client.go b/pkg/stashbox/graphql/generated_client.go index bc9a6ce89c..801b38b4d0 100644 --- a/pkg/stashbox/graphql/generated_client.go +++ b/pkg/stashbox/graphql/generated_client.go @@ -400,9 +400,10 @@ func (t *PerformerAppearanceFragment) GetPerformer() *PerformerFragment { } type FingerprintFragment struct { - Algorithm FingerprintAlgorithm "json:\"algorithm\" graphql:\"algorithm\"" - Hash string "json:\"hash\" graphql:\"hash\"" - Duration int "json:\"duration\" graphql:\"duration\"" + Algorithm FingerprintAlgorithm "json:\"algorithm\" graphql:\"algorithm\"" + Hash string "json:\"hash\" graphql:\"hash\"" + Duration int "json:\"duration\" graphql:\"duration\"" + Submissions int "json:\"submissions\" graphql:\"submissions\"" } func (t *FingerprintFragment) GetAlgorithm() *FingerprintAlgorithm { @@ -423,6 +424,12 @@ func (t *FingerprintFragment) GetDuration() int { } return t.Duration } +func (t *FingerprintFragment) GetSubmissions() int { + if t == nil { + t = &FingerprintFragment{} + } + return t.Submissions +} type SceneFragment struct { ID string "json:\"id\" graphql:\"id\"" @@ -520,22 +527,43 @@ func (t *SceneFragment) GetFingerprints() []*FingerprintFragment { } type StudioFragment_Parent struct { - ID string "json:\"id\" graphql:\"id\"" - Name string "json:\"name\" graphql:\"name\"" + Aliases []string "json:\"aliases\" graphql:\"aliases\"" + ID string "json:\"id\" graphql:\"id\"" + Images []*ImageFragment "json:\"images\" graphql:\"images\"" + Name string "json:\"name\" graphql:\"name\"" + Urls []*URLFragment "json:\"urls\" graphql:\"urls\"" } +func (t *StudioFragment_Parent) GetAliases() []string { + if t == nil { + t = &StudioFragment_Parent{} + } + return t.Aliases +} func (t *StudioFragment_Parent) GetID() string { if t == nil { t = &StudioFragment_Parent{} } return t.ID } +func (t *StudioFragment_Parent) GetImages() []*ImageFragment { + if t == nil { + t = &StudioFragment_Parent{} + } + return t.Images +} func (t *StudioFragment_Parent) GetName() string { if t == nil { t = &StudioFragment_Parent{} } return t.Name } +func (t *StudioFragment_Parent) GetUrls() []*URLFragment { + if t == nil { + t = &StudioFragment_Parent{} + } + return t.Urls +} type TagFragment_Category struct { Description *string "json:\"description,omitempty\" graphql:\"description\"" @@ -563,22 +591,43 @@ func (t *TagFragment_Category) GetName() string { } type SceneFragment_Studio_StudioFragment_Parent struct { - ID string "json:\"id\" graphql:\"id\"" - Name string "json:\"name\" graphql:\"name\"" + Aliases []string "json:\"aliases\" graphql:\"aliases\"" + ID string "json:\"id\" graphql:\"id\"" + Images []*ImageFragment "json:\"images\" graphql:\"images\"" + Name string "json:\"name\" graphql:\"name\"" + Urls []*URLFragment "json:\"urls\" graphql:\"urls\"" } +func (t *SceneFragment_Studio_StudioFragment_Parent) GetAliases() []string { + if t == nil { + t = &SceneFragment_Studio_StudioFragment_Parent{} + } + return t.Aliases +} func (t *SceneFragment_Studio_StudioFragment_Parent) GetID() string { if t == nil { t = &SceneFragment_Studio_StudioFragment_Parent{} } return t.ID } +func (t *SceneFragment_Studio_StudioFragment_Parent) GetImages() []*ImageFragment { + if t == nil { + t = &SceneFragment_Studio_StudioFragment_Parent{} + } + return t.Images +} func (t *SceneFragment_Studio_StudioFragment_Parent) GetName() string { if t == nil { t = &SceneFragment_Studio_StudioFragment_Parent{} } return t.Name } +func (t *SceneFragment_Studio_StudioFragment_Parent) GetUrls() []*URLFragment { + if t == nil { + t = &SceneFragment_Studio_StudioFragment_Parent{} + } + return t.Urls +} type SceneFragment_Tags_TagFragment_Category struct { Description *string "json:\"description,omitempty\" graphql:\"description\"" @@ -606,22 +655,43 @@ func (t *SceneFragment_Tags_TagFragment_Category) GetName() string { } type FindScenesBySceneFingerprints_FindScenesBySceneFingerprints_SceneFragment_Studio_StudioFragment_Parent struct { - ID string "json:\"id\" graphql:\"id\"" - Name string "json:\"name\" graphql:\"name\"" + Aliases []string "json:\"aliases\" graphql:\"aliases\"" + ID string "json:\"id\" graphql:\"id\"" + Images []*ImageFragment "json:\"images\" graphql:\"images\"" + Name string "json:\"name\" graphql:\"name\"" + Urls []*URLFragment "json:\"urls\" graphql:\"urls\"" } +func (t *FindScenesBySceneFingerprints_FindScenesBySceneFingerprints_SceneFragment_Studio_StudioFragment_Parent) GetAliases() []string { + if t == nil { + t = &FindScenesBySceneFingerprints_FindScenesBySceneFingerprints_SceneFragment_Studio_StudioFragment_Parent{} + } + return t.Aliases +} func (t *FindScenesBySceneFingerprints_FindScenesBySceneFingerprints_SceneFragment_Studio_StudioFragment_Parent) GetID() string { if t == nil { t = &FindScenesBySceneFingerprints_FindScenesBySceneFingerprints_SceneFragment_Studio_StudioFragment_Parent{} } return t.ID } +func (t *FindScenesBySceneFingerprints_FindScenesBySceneFingerprints_SceneFragment_Studio_StudioFragment_Parent) GetImages() []*ImageFragment { + if t == nil { + t = &FindScenesBySceneFingerprints_FindScenesBySceneFingerprints_SceneFragment_Studio_StudioFragment_Parent{} + } + return t.Images +} func (t *FindScenesBySceneFingerprints_FindScenesBySceneFingerprints_SceneFragment_Studio_StudioFragment_Parent) GetName() string { if t == nil { t = &FindScenesBySceneFingerprints_FindScenesBySceneFingerprints_SceneFragment_Studio_StudioFragment_Parent{} } return t.Name } +func (t *FindScenesBySceneFingerprints_FindScenesBySceneFingerprints_SceneFragment_Studio_StudioFragment_Parent) GetUrls() []*URLFragment { + if t == nil { + t = &FindScenesBySceneFingerprints_FindScenesBySceneFingerprints_SceneFragment_Studio_StudioFragment_Parent{} + } + return t.Urls +} type FindScenesBySceneFingerprints_FindScenesBySceneFingerprints_SceneFragment_Tags_TagFragment_Category struct { Description *string "json:\"description,omitempty\" graphql:\"description\"" @@ -649,22 +719,43 @@ func (t *FindScenesBySceneFingerprints_FindScenesBySceneFingerprints_SceneFragme } type SearchScene_SearchScene_SceneFragment_Studio_StudioFragment_Parent struct { - ID string "json:\"id\" graphql:\"id\"" - Name string "json:\"name\" graphql:\"name\"" + Aliases []string "json:\"aliases\" graphql:\"aliases\"" + ID string "json:\"id\" graphql:\"id\"" + Images []*ImageFragment "json:\"images\" graphql:\"images\"" + Name string "json:\"name\" graphql:\"name\"" + Urls []*URLFragment "json:\"urls\" graphql:\"urls\"" } +func (t *SearchScene_SearchScene_SceneFragment_Studio_StudioFragment_Parent) GetAliases() []string { + if t == nil { + t = &SearchScene_SearchScene_SceneFragment_Studio_StudioFragment_Parent{} + } + return t.Aliases +} func (t *SearchScene_SearchScene_SceneFragment_Studio_StudioFragment_Parent) GetID() string { if t == nil { t = &SearchScene_SearchScene_SceneFragment_Studio_StudioFragment_Parent{} } return t.ID } +func (t *SearchScene_SearchScene_SceneFragment_Studio_StudioFragment_Parent) GetImages() []*ImageFragment { + if t == nil { + t = &SearchScene_SearchScene_SceneFragment_Studio_StudioFragment_Parent{} + } + return t.Images +} func (t *SearchScene_SearchScene_SceneFragment_Studio_StudioFragment_Parent) GetName() string { if t == nil { t = &SearchScene_SearchScene_SceneFragment_Studio_StudioFragment_Parent{} } return t.Name } +func (t *SearchScene_SearchScene_SceneFragment_Studio_StudioFragment_Parent) GetUrls() []*URLFragment { + if t == nil { + t = &SearchScene_SearchScene_SceneFragment_Studio_StudioFragment_Parent{} + } + return t.Urls +} type SearchScene_SearchScene_SceneFragment_Tags_TagFragment_Category struct { Description *string "json:\"description,omitempty\" graphql:\"description\"" @@ -692,22 +783,43 @@ func (t *SearchScene_SearchScene_SceneFragment_Tags_TagFragment_Category) GetNam } type FindSceneByID_FindScene_SceneFragment_Studio_StudioFragment_Parent struct { - ID string "json:\"id\" graphql:\"id\"" - Name string "json:\"name\" graphql:\"name\"" + Aliases []string "json:\"aliases\" graphql:\"aliases\"" + ID string "json:\"id\" graphql:\"id\"" + Images []*ImageFragment "json:\"images\" graphql:\"images\"" + Name string "json:\"name\" graphql:\"name\"" + Urls []*URLFragment "json:\"urls\" graphql:\"urls\"" } +func (t *FindSceneByID_FindScene_SceneFragment_Studio_StudioFragment_Parent) GetAliases() []string { + if t == nil { + t = &FindSceneByID_FindScene_SceneFragment_Studio_StudioFragment_Parent{} + } + return t.Aliases +} func (t *FindSceneByID_FindScene_SceneFragment_Studio_StudioFragment_Parent) GetID() string { if t == nil { t = &FindSceneByID_FindScene_SceneFragment_Studio_StudioFragment_Parent{} } return t.ID } +func (t *FindSceneByID_FindScene_SceneFragment_Studio_StudioFragment_Parent) GetImages() []*ImageFragment { + if t == nil { + t = &FindSceneByID_FindScene_SceneFragment_Studio_StudioFragment_Parent{} + } + return t.Images +} func (t *FindSceneByID_FindScene_SceneFragment_Studio_StudioFragment_Parent) GetName() string { if t == nil { t = &FindSceneByID_FindScene_SceneFragment_Studio_StudioFragment_Parent{} } return t.Name } +func (t *FindSceneByID_FindScene_SceneFragment_Studio_StudioFragment_Parent) GetUrls() []*URLFragment { + if t == nil { + t = &FindSceneByID_FindScene_SceneFragment_Studio_StudioFragment_Parent{} + } + return t.Urls +} type FindSceneByID_FindScene_SceneFragment_Tags_TagFragment_Category struct { Description *string "json:\"description,omitempty\" graphql:\"description\"" @@ -735,22 +847,43 @@ func (t *FindSceneByID_FindScene_SceneFragment_Tags_TagFragment_Category) GetNam } type FindStudio_FindStudio_StudioFragment_Parent struct { - ID string "json:\"id\" graphql:\"id\"" - Name string "json:\"name\" graphql:\"name\"" + Aliases []string "json:\"aliases\" graphql:\"aliases\"" + ID string "json:\"id\" graphql:\"id\"" + Images []*ImageFragment "json:\"images\" graphql:\"images\"" + Name string "json:\"name\" graphql:\"name\"" + Urls []*URLFragment "json:\"urls\" graphql:\"urls\"" } +func (t *FindStudio_FindStudio_StudioFragment_Parent) GetAliases() []string { + if t == nil { + t = &FindStudio_FindStudio_StudioFragment_Parent{} + } + return t.Aliases +} func (t *FindStudio_FindStudio_StudioFragment_Parent) GetID() string { if t == nil { t = &FindStudio_FindStudio_StudioFragment_Parent{} } return t.ID } +func (t *FindStudio_FindStudio_StudioFragment_Parent) GetImages() []*ImageFragment { + if t == nil { + t = &FindStudio_FindStudio_StudioFragment_Parent{} + } + return t.Images +} func (t *FindStudio_FindStudio_StudioFragment_Parent) GetName() string { if t == nil { t = &FindStudio_FindStudio_StudioFragment_Parent{} } return t.Name } +func (t *FindStudio_FindStudio_StudioFragment_Parent) GetUrls() []*URLFragment { + if t == nil { + t = &FindStudio_FindStudio_StudioFragment_Parent{} + } + return t.Urls +} type FindTag_FindTag_TagFragment_Category struct { Description *string "json:\"description,omitempty\" graphql:\"description\"" @@ -1037,6 +1170,13 @@ fragment StudioFragment on Studio { parent { name id + aliases + urls { + ... URLFragment + } + images { + ... ImageFragment + } } images { ... ImageFragment @@ -1108,6 +1248,7 @@ fragment FingerprintFragment on Fingerprint { algorithm hash duration + submissions } ` @@ -1180,6 +1321,13 @@ fragment StudioFragment on Studio { parent { name id + aliases + urls { + ... URLFragment + } + images { + ... ImageFragment + } } images { ... ImageFragment @@ -1251,6 +1399,7 @@ fragment FingerprintFragment on Fingerprint { algorithm hash duration + submissions } ` @@ -1481,6 +1630,13 @@ fragment StudioFragment on Studio { parent { name id + aliases + urls { + ... URLFragment + } + images { + ... ImageFragment + } } images { ... ImageFragment @@ -1552,6 +1708,7 @@ fragment FingerprintFragment on Fingerprint { algorithm hash duration + submissions } ` @@ -1587,6 +1744,13 @@ fragment StudioFragment on Studio { parent { name id + aliases + urls { + ... URLFragment + } + images { + ... ImageFragment + } } images { ... ImageFragment diff --git a/pkg/stashbox/graphql/generated_models.go b/pkg/stashbox/graphql/generated_models.go index e52c61fb27..9e1646ffb6 100644 --- a/pkg/stashbox/graphql/generated_models.go +++ b/pkg/stashbox/graphql/generated_models.go @@ -46,7 +46,25 @@ type ActivateNewUserInput struct { Password string `json:"password"` } -type ApplyEditInput struct { +type AmendEditInput struct { + ID string `json:"id"` + Reason string `json:"reason"` + // Fields to remove from the diff (e.g., ["name", "disambiguation"]) + RemoveFields []string `json:"remove_fields,omitempty"` + // Array items to remove from added arrays + RemoveAddedItems []*AmendItemRemoval `json:"remove_added_items,omitempty"` + // Array items to remove from removed arrays + RemoveRemovedItems []*AmendItemRemoval `json:"remove_removed_items,omitempty"` +} + +type AmendItemRemoval struct { + // Field name (e.g., "aliases", "urls", "images") + Field string `json:"field"` + // Indices to remove from the array + Indices []int `json:"indices"` +} + +type ApproveEditInput struct { ID string `json:"id"` } @@ -75,6 +93,25 @@ type CancelEditInput struct { ID string `json:"id"` } +type ClusterMember struct { + Hash string `json:"hash"` + SceneSubmissions []*ClusterSceneSubmission `json:"scene_submissions"` +} + +type ClusterOshash struct { + Hash string `json:"hash"` + Submissions int `json:"submissions"` + Reports int `json:"reports"` +} + +type ClusterSceneSubmission struct { + Scene *Scene `json:"scene"` + Submissions int `json:"submissions"` + Reports int `json:"reports"` + Durations []*DurationCount `json:"durations"` + LinkedFingerprints []*ClusterOshash `json:"linked_fingerprints"` +} + type CommentCommentedEdit struct { Comment *EditComment `json:"comment"` } @@ -98,6 +135,16 @@ type DateCriterionInput struct { Modifier CriterionModifier `json:"modifier"` } +type DeleteEditInput struct { + ID string `json:"id"` + Reason string `json:"reason"` +} + +type DeleteFingerprintSubmissionsInput struct { + Fingerprints []*FingerprintQueryInput `json:"fingerprints"` + SceneID string `json:"scene_id"` +} + type DownvoteOwnEdit struct { Edit *Edit `json:"edit"` } @@ -137,6 +184,11 @@ type DraftSubmissionStatus struct { ID *string `json:"id,omitempty"` } +type DurationCount struct { + Duration int `json:"duration"` + Count int `json:"count"` +} + type Edit struct { ID string `json:"id"` User *User `json:"user,omitempty"` @@ -173,7 +225,11 @@ type EditComment struct { User *User `json:"user,omitempty"` Date time.Time `json:"date"` Comment string `json:"comment"` - Edit *Edit `json:"edit"` + // Set when a moderator has edited the comment text + Updated *time.Time `json:"updated,omitempty"` + // Whether the comment is hidden from public view. Hidden comments are only returned to moderators. + Hidden bool `json:"hidden"` + Edit *Edit `json:"edit"` } type EditCommentInput struct { @@ -283,6 +339,30 @@ type Fingerprint struct { UserReported bool `json:"user_reported"` } +type FingerprintBatchSubmission struct { + SceneID string `json:"scene_id"` + Hash string `json:"hash"` + Algorithm FingerprintAlgorithm `json:"algorithm"` + Duration int `json:"duration"` +} + +type FingerprintCluster struct { + Members []*ClusterMember `json:"members"` +} + +type FingerprintClustersInput struct { + SceneID string `json:"scene_id"` + Distance int `json:"distance"` +} + +type FingerprintClustersResult struct { + Clusters []*FingerprintCluster `json:"clusters"` + // True when BFS expansion was capped by any of its limits (scene count, + // member count, or iteration count); some related fingerprints may be + // missing from the response. + Truncated bool `json:"truncated"` +} + type FingerprintEditInput struct { UserIds []string `json:"user_ids,omitempty"` Hash string `json:"hash"` @@ -301,6 +381,14 @@ type FingerprintInput struct { Duration int `json:"duration"` } +type FingerprintMovedScene struct { + SourceScene *Scene `json:"source_scene"` + TargetScene *Scene `json:"target_scene"` + FingerprintHash string `json:"fingerprint_hash"` +} + +func (FingerprintMovedScene) IsNotificationData() {} + type FingerprintQueryInput struct { Hash string `json:"hash"` Algorithm FingerprintAlgorithm `json:"algorithm"` @@ -313,6 +401,15 @@ type FingerprintSubmission struct { Vote *FingerprintSubmissionType `json:"vote,omitempty"` } +type FingerprintSubmissionResult struct { + // The fingerprint hash that was submitted + Hash string `json:"hash"` + // The scene ID that was submitted to + SceneID string `json:"scene_id"` + // Error message if submission failed + Error *string `json:"error,omitempty"` +} + type FingerprintedSceneEdit struct { Edit *Edit `json:"edit"` } @@ -324,6 +421,11 @@ type FuzzyDate struct { Accuracy DateAccuracyEnum `json:"accuracy"` } +type GenderFacet struct { + Gender GenderEnum `json:"gender"` + Count int `json:"count"` +} + type GenerateInviteCodeInput struct { Keys *int `json:"keys,omitempty"` Uses *int `json:"uses,omitempty"` @@ -340,6 +442,14 @@ type HairColorCriterionInput struct { Modifier CriterionModifier `json:"modifier"` } +type HideEditCommentInput struct { + // ID of the comment to hide/unhide + ID string `json:"id"` + Hidden bool `json:"hidden"` + // Reason for the change, recorded in the moderation audit log + Reason *string `json:"reason,omitempty"` +} + type IDCriterionInput struct { Value []string `json:"value"` Modifier CriterionModifier `json:"modifier"` @@ -389,6 +499,30 @@ type Measurements struct { Hip *int `json:"hip,omitempty"` } +type ModAudit struct { + ID string `json:"id"` + Action ModAuditActionEnum `json:"action"` + User *User `json:"user,omitempty"` + TargetID string `json:"target_id"` + TargetType string `json:"target_type"` + Data string `json:"data"` + Reason *string `json:"reason,omitempty"` + CreatedAt time.Time `json:"created_at"` +} + +type ModAuditQueryInput struct { + Page int `json:"page"` + PerPage int `json:"per_page"` + Action *ModAuditActionEnum `json:"action,omitempty"` + UserID *string `json:"user_id,omitempty"` +} + +type MoveFingerprintSubmissionsInput struct { + Fingerprints []*FingerprintQueryInput `json:"fingerprints"` + SourceSceneID string `json:"source_scene_id"` + TargetSceneID string `json:"target_scene_id"` +} + type MultiIDCriterionInput struct { Value []string `json:"value,omitempty"` Modifier CriterionModifier `json:"modifier"` @@ -408,9 +542,10 @@ type NewUserInput struct { } type Notification struct { - Created time.Time `json:"created"` - Read bool `json:"read"` - Data NotificationData `json:"data"` + Created time.Time `json:"created"` + Read bool `json:"read"` + Level NotificationLevel `json:"level"` + Data NotificationData `json:"data"` } type Performer struct { @@ -686,6 +821,15 @@ type PerformerScenesInput struct { Tags *MultiIDCriterionInput `json:"tags,omitempty"` } +type PerformerSearchFacets struct { + Genders []*GenderFacet `json:"genders"` +} + +type PerformerSearchFilter struct { + // Filter by gender + Gender *GenderEnum `json:"gender,omitempty"` +} + type PerformerStudio struct { Studio *Studio `json:"studio"` SceneCount int `json:"scene_count"` @@ -748,6 +892,11 @@ type QueryExistingSceneResult struct { Scenes []*Scene `json:"scenes"` } +type QueryModAuditsResultType struct { + Count int `json:"count"` + Audits []*ModAudit `json:"audits"` +} + type QueryNotificationsInput struct { Page int `json:"page"` PerPage int `json:"per_page"` @@ -763,6 +912,8 @@ type QueryNotificationsResult struct { type QueryPerformersResultType struct { Count int `json:"count"` Performers []*Performer `json:"performers"` + // Search facets, only available for searchPerformer queries + Facets *PerformerSearchFacets `json:"facets,omitempty"` } type QueryScenesResultType struct { @@ -770,6 +921,11 @@ type QueryScenesResultType struct { Scenes []*Scene `json:"scenes"` } +type QuerySiteCategoriesResultType struct { + Count int `json:"count"` + SiteCategories []*SiteCategory `json:"site_categories"` +} + type QuerySitesResultType struct { Count int `json:"count"` Sites []*Site `json:"sites"` @@ -942,12 +1098,13 @@ type SceneEditInput struct { } type SceneQueryInput struct { - // Filter to search title and details - assumes like query unless quoted Text *string `json:"text,omitempty"` // Filter to search title - assumes like query unless quoted Title *string `json:"title,omitempty"` // Filter to search urls - assumes like query unless quoted URL *string `json:"url,omitempty"` + // Filter by scene code + Code *StringCriterionInput `json:"code,omitempty"` // Filter by date Date *DateCriterionInput `json:"date,omitempty"` // Filter by production date @@ -999,8 +1156,35 @@ type Site struct { Regex *string `json:"regex,omitempty"` ValidTypes []ValidSiteTypeEnum `json:"valid_types"` Icon string `json:"icon"` - Created time.Time `json:"created"` - Updated time.Time `json:"updated"` + Category *SiteCategory `json:"category,omitempty"` + // Whether links for this site are highlighted on entity pages, in addition to being listed in the links section + Highlighted bool `json:"highlighted"` + Created time.Time `json:"created"` + Updated time.Time `json:"updated"` +} + +type SiteCategory struct { + ID int `json:"id"` + Name string `json:"name"` + Description *string `json:"description,omitempty"` + SortOrder int `json:"sort_order"` +} + +type SiteCategoryCreateInput struct { + Name string `json:"name"` + Description *string `json:"description,omitempty"` + SortOrder *int `json:"sort_order,omitempty"` +} + +type SiteCategoryDestroyInput struct { + ID int `json:"id"` +} + +type SiteCategoryUpdateInput struct { + ID int `json:"id"` + Name *string `json:"name,omitempty"` + Description *string `json:"description,omitempty"` + SortOrder *int `json:"sort_order,omitempty"` } type SiteCreateInput struct { @@ -1009,12 +1193,24 @@ type SiteCreateInput struct { URL *string `json:"url,omitempty"` Regex *string `json:"regex,omitempty"` ValidTypes []ValidSiteTypeEnum `json:"valid_types"` + CategoryID *int `json:"category_id,omitempty"` + Highlighted bool `json:"highlighted"` + // Base64 data URL of the favicon to store. Empty string clears it, null leaves it unchanged. + Favicon *string `json:"favicon,omitempty"` } type SiteDestroyInput struct { ID string `json:"id"` } +// A favicon candidate discovered for a site URL. +type SiteFavicon struct { + // Source URL the icon was found at. + URL string `json:"url"` + // Base64 data URL of the downloaded icon. + Image string `json:"image"` +} + type SiteUpdateInput struct { ID string `json:"id"` Name string `json:"name"` @@ -1022,6 +1218,10 @@ type SiteUpdateInput struct { URL *string `json:"url,omitempty"` Regex *string `json:"regex,omitempty"` ValidTypes []ValidSiteTypeEnum `json:"valid_types"` + CategoryID *int `json:"category_id,omitempty"` + Highlighted bool `json:"highlighted"` + // Base64 data URL of the favicon to store. Empty string clears it, null leaves it unchanged. + Favicon *string `json:"favicon,omitempty"` } type StashBoxConfig struct { @@ -1051,6 +1251,7 @@ type Studio struct { Urls []*URL `json:"urls"` Parent *Studio `json:"parent,omitempty"` ChildStudios []*Studio `json:"child_studios"` + SubStudios *QueryStudiosResultType `json:"sub_studios"` Images []*Image `json:"images"` Deleted bool `json:"deleted"` IsFavorite bool `json:"is_favorite"` @@ -1240,6 +1441,19 @@ type URLInput struct { SiteID string `json:"site_id"` } +type UnreadNotificationCount struct { + Total int `json:"total"` + Urgent int `json:"urgent"` +} + +type UpdateEditCommentInput struct { + // ID of the comment to edit + ID string `json:"id"` + Comment string `json:"comment"` + // Reason for the edit, recorded in the moderation audit log + Reason *string `json:"reason,omitempty"` +} + type UpdatedEdit struct { Edit *Edit `json:"edit"` } @@ -1295,13 +1509,20 @@ type UserDestroyInput struct { } type UserEditCount struct { - Accepted int `json:"accepted"` - Rejected int `json:"rejected"` - Pending int `json:"pending"` - ImmediateAccepted int `json:"immediate_accepted"` - ImmediateRejected int `json:"immediate_rejected"` - Failed int `json:"failed"` - Canceled int `json:"canceled"` + Accepted int `json:"accepted"` + Rejected int `json:"rejected"` + Pending int `json:"pending"` + ImmediateAccepted int `json:"immediate_accepted"` + ImmediateRejected int `json:"immediate_rejected"` + Failed int `json:"failed"` + Canceled int `json:"canceled"` + AcceptedBot int `json:"accepted_bot"` + RejectedBot int `json:"rejected_bot"` + PendingBot int `json:"pending_bot"` + ImmediateAcceptedBot int `json:"immediate_accepted_bot"` + ImmediateRejectedBot int `json:"immediate_rejected_bot"` + FailedBot int `json:"failed_bot"` + CanceledBot int `json:"canceled_bot"` } type UserQueryInput struct { @@ -2172,6 +2393,65 @@ func (e HairColorEnum) MarshalJSON() ([]byte, error) { return buf.Bytes(), nil } +type ModAuditActionEnum string + +const ( + ModAuditActionEnumEditDelete ModAuditActionEnum = "EDIT_DELETE" + ModAuditActionEnumEditAmendment ModAuditActionEnum = "EDIT_AMENDMENT" + ModAuditActionEnumEditCommentUpdate ModAuditActionEnum = "EDIT_COMMENT_UPDATE" + ModAuditActionEnumEditCommentHide ModAuditActionEnum = "EDIT_COMMENT_HIDE" +) + +var AllModAuditActionEnum = []ModAuditActionEnum{ + ModAuditActionEnumEditDelete, + ModAuditActionEnumEditAmendment, + ModAuditActionEnumEditCommentUpdate, + ModAuditActionEnumEditCommentHide, +} + +func (e ModAuditActionEnum) IsValid() bool { + switch e { + case ModAuditActionEnumEditDelete, ModAuditActionEnumEditAmendment, ModAuditActionEnumEditCommentUpdate, ModAuditActionEnumEditCommentHide: + return true + } + return false +} + +func (e ModAuditActionEnum) String() string { + return string(e) +} + +func (e *ModAuditActionEnum) UnmarshalGQL(v any) error { + str, ok := v.(string) + if !ok { + return fmt.Errorf("enums must be strings") + } + + *e = ModAuditActionEnum(str) + if !e.IsValid() { + return fmt.Errorf("%s is not a valid ModAuditActionEnum", str) + } + return nil +} + +func (e ModAuditActionEnum) MarshalGQL(w io.Writer) { + fmt.Fprint(w, strconv.Quote(e.String())) +} + +func (e *ModAuditActionEnum) UnmarshalJSON(b []byte) error { + s, err := strconv.Unquote(string(b)) + if err != nil { + return err + } + return e.UnmarshalGQL(s) +} + +func (e ModAuditActionEnum) MarshalJSON() ([]byte, error) { + var buf bytes.Buffer + e.MarshalGQL(&buf) + return buf.Bytes(), nil +} + type NotificationEnum string const ( @@ -2186,6 +2466,7 @@ const ( NotificationEnumCommentVotedEdit NotificationEnum = "COMMENT_VOTED_EDIT" NotificationEnumUpdatedEdit NotificationEnum = "UPDATED_EDIT" NotificationEnumFingerprintedSceneEdit NotificationEnum = "FINGERPRINTED_SCENE_EDIT" + NotificationEnumFingerprintMoved NotificationEnum = "FINGERPRINT_MOVED" ) var AllNotificationEnum = []NotificationEnum{ @@ -2200,11 +2481,12 @@ var AllNotificationEnum = []NotificationEnum{ NotificationEnumCommentVotedEdit, NotificationEnumUpdatedEdit, NotificationEnumFingerprintedSceneEdit, + NotificationEnumFingerprintMoved, } func (e NotificationEnum) IsValid() bool { switch e { - case NotificationEnumFavoritePerformerScene, NotificationEnumFavoritePerformerEdit, NotificationEnumFavoriteStudioScene, NotificationEnumFavoriteStudioEdit, NotificationEnumCommentOwnEdit, NotificationEnumDownvoteOwnEdit, NotificationEnumFailedOwnEdit, NotificationEnumCommentCommentedEdit, NotificationEnumCommentVotedEdit, NotificationEnumUpdatedEdit, NotificationEnumFingerprintedSceneEdit: + case NotificationEnumFavoritePerformerScene, NotificationEnumFavoritePerformerEdit, NotificationEnumFavoriteStudioScene, NotificationEnumFavoriteStudioEdit, NotificationEnumCommentOwnEdit, NotificationEnumDownvoteOwnEdit, NotificationEnumFailedOwnEdit, NotificationEnumCommentCommentedEdit, NotificationEnumCommentVotedEdit, NotificationEnumUpdatedEdit, NotificationEnumFingerprintedSceneEdit, NotificationEnumFingerprintMoved: return true } return false @@ -2245,6 +2527,61 @@ func (e NotificationEnum) MarshalJSON() ([]byte, error) { return buf.Bytes(), nil } +type NotificationLevel string + +const ( + NotificationLevelNormal NotificationLevel = "NORMAL" + NotificationLevelUrgent NotificationLevel = "URGENT" +) + +var AllNotificationLevel = []NotificationLevel{ + NotificationLevelNormal, + NotificationLevelUrgent, +} + +func (e NotificationLevel) IsValid() bool { + switch e { + case NotificationLevelNormal, NotificationLevelUrgent: + return true + } + return false +} + +func (e NotificationLevel) String() string { + return string(e) +} + +func (e *NotificationLevel) UnmarshalGQL(v any) error { + str, ok := v.(string) + if !ok { + return fmt.Errorf("enums must be strings") + } + + *e = NotificationLevel(str) + if !e.IsValid() { + return fmt.Errorf("%s is not a valid NotificationLevel", str) + } + return nil +} + +func (e NotificationLevel) MarshalGQL(w io.Writer) { + fmt.Fprint(w, strconv.Quote(e.String())) +} + +func (e *NotificationLevel) UnmarshalJSON(b []byte) error { + s, err := strconv.Unquote(string(b)) + if err != nil { + return err + } + return e.UnmarshalGQL(s) +} + +func (e NotificationLevel) MarshalJSON() ([]byte, error) { + var buf bytes.Buffer + e.MarshalGQL(&buf) + return buf.Bytes(), nil +} + type OperationEnum string const ( @@ -2314,6 +2651,7 @@ const ( PerformerSortEnumCareerStartYear PerformerSortEnum = "CAREER_START_YEAR" PerformerSortEnumDebut PerformerSortEnum = "DEBUT" PerformerSortEnumLastScene PerformerSortEnum = "LAST_SCENE" + PerformerSortEnumPopularity PerformerSortEnum = "POPULARITY" PerformerSortEnumCreatedAt PerformerSortEnum = "CREATED_AT" PerformerSortEnumUpdatedAt PerformerSortEnum = "UPDATED_AT" ) @@ -2326,13 +2664,14 @@ var AllPerformerSortEnum = []PerformerSortEnum{ PerformerSortEnumCareerStartYear, PerformerSortEnumDebut, PerformerSortEnumLastScene, + PerformerSortEnumPopularity, PerformerSortEnumCreatedAt, PerformerSortEnumUpdatedAt, } func (e PerformerSortEnum) IsValid() bool { switch e { - case PerformerSortEnumName, PerformerSortEnumBirthdate, PerformerSortEnumDeathdate, PerformerSortEnumSceneCount, PerformerSortEnumCareerStartYear, PerformerSortEnumDebut, PerformerSortEnumLastScene, PerformerSortEnumCreatedAt, PerformerSortEnumUpdatedAt: + case PerformerSortEnumName, PerformerSortEnumBirthdate, PerformerSortEnumDeathdate, PerformerSortEnumSceneCount, PerformerSortEnumCareerStartYear, PerformerSortEnumDebut, PerformerSortEnumLastScene, PerformerSortEnumPopularity, PerformerSortEnumCreatedAt, PerformerSortEnumUpdatedAt: return true } return false @@ -2376,11 +2715,12 @@ func (e PerformerSortEnum) MarshalJSON() ([]byte, error) { type RoleEnum string const ( - RoleEnumRead RoleEnum = "READ" - RoleEnumVote RoleEnum = "VOTE" - RoleEnumEdit RoleEnum = "EDIT" - RoleEnumModify RoleEnum = "MODIFY" - RoleEnumAdmin RoleEnum = "ADMIN" + RoleEnumRead RoleEnum = "READ" + RoleEnumVote RoleEnum = "VOTE" + RoleEnumEdit RoleEnum = "EDIT" + RoleEnumModify RoleEnum = "MODIFY" + RoleEnumModerate RoleEnum = "MODERATE" + RoleEnumAdmin RoleEnum = "ADMIN" // May generate invites without tokens RoleEnumInvite RoleEnum = "INVITE" // May grant and rescind invite tokens and resind invite keys @@ -2395,6 +2735,7 @@ var AllRoleEnum = []RoleEnum{ RoleEnumVote, RoleEnumEdit, RoleEnumModify, + RoleEnumModerate, RoleEnumAdmin, RoleEnumInvite, RoleEnumManageInvites, @@ -2405,7 +2746,7 @@ var AllRoleEnum = []RoleEnum{ func (e RoleEnum) IsValid() bool { switch e { - case RoleEnumRead, RoleEnumVote, RoleEnumEdit, RoleEnumModify, RoleEnumAdmin, RoleEnumInvite, RoleEnumManageInvites, RoleEnumBot, RoleEnumReadOnly, RoleEnumEditTags: + case RoleEnumRead, RoleEnumVote, RoleEnumEdit, RoleEnumModify, RoleEnumModerate, RoleEnumAdmin, RoleEnumInvite, RoleEnumManageInvites, RoleEnumBot, RoleEnumReadOnly, RoleEnumEditTags: return true } return false @@ -2449,24 +2790,28 @@ func (e RoleEnum) MarshalJSON() ([]byte, error) { type SceneSortEnum string const ( - SceneSortEnumTitle SceneSortEnum = "TITLE" - SceneSortEnumDate SceneSortEnum = "DATE" - SceneSortEnumTrending SceneSortEnum = "TRENDING" - SceneSortEnumCreatedAt SceneSortEnum = "CREATED_AT" - SceneSortEnumUpdatedAt SceneSortEnum = "UPDATED_AT" + SceneSortEnumTitle SceneSortEnum = "TITLE" + SceneSortEnumDate SceneSortEnum = "DATE" + SceneSortEnumDuration SceneSortEnum = "DURATION" + SceneSortEnumTrending SceneSortEnum = "TRENDING" + SceneSortEnumPopularity SceneSortEnum = "POPULARITY" + SceneSortEnumCreatedAt SceneSortEnum = "CREATED_AT" + SceneSortEnumUpdatedAt SceneSortEnum = "UPDATED_AT" ) var AllSceneSortEnum = []SceneSortEnum{ SceneSortEnumTitle, SceneSortEnumDate, + SceneSortEnumDuration, SceneSortEnumTrending, + SceneSortEnumPopularity, SceneSortEnumCreatedAt, SceneSortEnumUpdatedAt, } func (e SceneSortEnum) IsValid() bool { switch e { - case SceneSortEnumTitle, SceneSortEnumDate, SceneSortEnumTrending, SceneSortEnumCreatedAt, SceneSortEnumUpdatedAt: + case SceneSortEnumTitle, SceneSortEnumDate, SceneSortEnumDuration, SceneSortEnumTrending, SceneSortEnumPopularity, SceneSortEnumCreatedAt, SceneSortEnumUpdatedAt: return true } return false diff --git a/pkg/stashbox/scene.go b/pkg/stashbox/scene.go index 9ba4d1a22f..0d0ed01df1 100644 --- a/pkg/stashbox/scene.go +++ b/pkg/stashbox/scene.go @@ -191,11 +191,7 @@ func (c Client) sceneFragmentToScrapedScene(ctx context.Context, s *graphql.Scen } if s.Studio != nil { - var err error - ss.Studio, err = c.resolveStudio(ctx, s.Studio) - if err != nil { - return nil, err - } + ss.Studio = studioFragmentToScrapedStudio(*s.Studio) } for _, p := range s.Performers { @@ -223,9 +219,10 @@ func getFingerprints(scene *graphql.SceneFragment) []*models.StashBoxFingerprint fingerprints := []*models.StashBoxFingerprint{} for _, fp := range scene.Fingerprints { fingerprint := models.StashBoxFingerprint{ - Algorithm: fp.Algorithm.String(), - Hash: fp.Hash, - Duration: fp.Duration, + Algorithm: fp.Algorithm.String(), + Hash: fp.Hash, + Duration: fp.Duration, + Submissions: fp.Submissions, } fingerprints = append(fingerprints, &fingerprint) } diff --git a/pkg/stashbox/studio.go b/pkg/stashbox/studio.go index 8934972f25..ee69bc2d50 100644 --- a/pkg/stashbox/studio.go +++ b/pkg/stashbox/studio.go @@ -9,28 +9,6 @@ import ( "github.com/stashapp/stash/pkg/stashbox/graphql" ) -func (c Client) resolveStudio(ctx context.Context, s *graphql.StudioFragment) (*models.ScrapedStudio, error) { - scraped := studioFragmentToScrapedStudio(*s) - - if s.Parent != nil { - parentStudio, err := c.client.FindStudio(ctx, &s.Parent.ID, nil) - if err != nil { - return nil, err - } - - if parentStudio.FindStudio == nil { - return scraped, nil - } - - scraped.Parent, err = c.resolveStudio(ctx, parentStudio.FindStudio) - if err != nil { - return nil, err - } - } - - return scraped, nil -} - func (c Client) FindStudio(ctx context.Context, query string) (*models.ScrapedStudio, error) { var studio *graphql.FindStudio @@ -49,10 +27,7 @@ func (c Client) FindStudio(ctx context.Context, query string) (*models.ScrapedSt var ret *models.ScrapedStudio if studio.FindStudio != nil { - ret, err = c.resolveStudio(ctx, studio.FindStudio) - if err != nil { - return nil, err - } + ret = studioFragmentToScrapedStudio(*studio.FindStudio) } return ret, nil @@ -81,5 +56,15 @@ func studioFragmentToScrapedStudio(s graphql.StudioFragment) *models.ScrapedStud st.Image = &st.Images[0] } + if s.Parent != nil { + st.Parent = studioFragmentToScrapedStudio(graphql.StudioFragment{ + Name: s.Parent.Name, + ID: s.Parent.ID, + Aliases: s.Parent.Aliases, + Urls: s.Parent.Urls, + Images: s.Parent.Images, + }) + } + return st } diff --git a/pkg/utils/image.go b/pkg/utils/image.go index b61643fac6..0ef13e9303 100644 --- a/pkg/utils/image.go +++ b/pkg/utils/image.go @@ -8,6 +8,7 @@ import ( "io" "net/http" "regexp" + "strings" "time" ) @@ -32,7 +33,32 @@ func ProcessImageInput(ctx context.Context, imageInput string) ([]byte, error) { } // assume input is a URL. Read it. - return ReadImageFromURL(ctx, imageInput) + d, err := ReadImageFromURL(ctx, imageInput) + if err != nil { + return nil, err + } + + if err := validateImageData(d); err != nil { + return nil, err + } + + return d, nil +} + +// validateImageData rejects HTML content, which is not a valid image and would +// execute as a document if served back to a browser. SVG (detected as XML or +// plain text) is still accepted and sandboxed on output by ServeImage. +func validateImageData(data []byte) error { + if len(data) == 0 { + return nil + } + + contentType := http.DetectContentType(data) + if strings.HasPrefix(contentType, "text/html") { + return fmt.Errorf("unsupported image content type %q", contentType) + } + + return nil } // ReadImageFromURL returns image data from a URL @@ -97,6 +123,10 @@ func ProcessBase64Image(imageString string) ([]byte, error) { return nil, err } + if err := validateImageData(imageData); err != nil { + return nil, err + } + return imageData, nil } @@ -112,10 +142,22 @@ func GetBase64StringFromData(data []byte) string { func ServeImage(w http.ResponseWriter, r *http.Request, image []byte) { contentType := http.DetectContentType(image) + + // SVG images are detected as XML or plain text; serve them as SVG so they + // render. The sandboxing CSP below prevents any embedded script running. if contentType == "text/xml; charset=utf-8" || contentType == "text/plain; charset=utf-8" { contentType = "image/svg+xml" + } else if strings.HasPrefix(contentType, "text/") { + // any other text type (e.g. HTML) is not a valid image - never render it + contentType = "application/octet-stream" + w.Header().Set("Content-Disposition", "attachment") } + // sandbox every image response so a stored SVG cannot execute script or + // exfiltrate data; harmless for raster images. + w.Header().Set("Content-Security-Policy", "default-src 'none'; img-src data:; style-src 'unsafe-inline'; sandbox") + w.Header().Set("X-Content-Type-Options", "nosniff") + w.Header().Set("Content-Type", contentType) ServeStaticContent(w, r, image) } diff --git a/pkg/utils/image_test.go b/pkg/utils/image_test.go new file mode 100644 index 0000000000..dba640697b --- /dev/null +++ b/pkg/utils/image_test.go @@ -0,0 +1,97 @@ +package utils + +import ( + "net/http" + "net/http/httptest" + "strings" + "testing" +) + +// a minimal valid 1x1 PNG +var pngImage = []byte{ + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, + 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, + 0x08, 0x06, 0x00, 0x00, 0x00, 0x1f, 0x15, 0xc4, 0x89, 0x00, 0x00, 0x00, + 0x0a, 0x49, 0x44, 0x41, 0x54, 0x78, 0x9c, 0x63, 0x00, 0x01, 0x00, 0x00, + 0x05, 0x00, 0x01, 0x0d, 0x0a, 0x2d, 0xb4, 0x00, 0x00, 0x00, 0x00, 0x49, + 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82, +} + +func TestServeImage(t *testing.T) { + const wantCSP = "default-src 'none'; img-src data:; style-src 'unsafe-inline'; sandbox" + + tests := []struct { + name string + image []byte + wantContentType string + wantAttachment bool + }{ + { + name: "png is served as-is", + image: pngImage, + wantContentType: "image/png", + }, + { + name: "svg is served as image/svg+xml so it renders", + image: []byte(``), + wantContentType: "image/svg+xml", + }, + { + name: "svg with xml prolog is served as image/svg+xml", + image: []byte(``), + wantContentType: "image/svg+xml", + }, + { + name: "html is never rendered - forced to an attachment", + image: []byte(``), + wantContentType: "application/octet-stream", + wantAttachment: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + w := httptest.NewRecorder() + r := httptest.NewRequest(http.MethodGet, "/image", nil) + + ServeImage(w, r, tt.image) + + if got := w.Header().Get("Content-Type"); got != tt.wantContentType { + t.Errorf("Content-Type = %q, want %q", got, tt.wantContentType) + } + if got := w.Header().Get("Content-Security-Policy"); got != wantCSP { + t.Errorf("Content-Security-Policy = %q, want %q", got, wantCSP) + } + if got := w.Header().Get("X-Content-Type-Options"); got != "nosniff" { + t.Errorf("X-Content-Type-Options = %q, want %q", got, "nosniff") + } + gotAttachment := strings.Contains(w.Header().Get("Content-Disposition"), "attachment") + if gotAttachment != tt.wantAttachment { + t.Errorf("Content-Disposition attachment = %v, want %v (got %q)", gotAttachment, tt.wantAttachment, w.Header().Get("Content-Disposition")) + } + }) + } +} + +func TestValidateImageData(t *testing.T) { + tests := []struct { + name string + data []byte + wantErr bool + }{ + {name: "empty is allowed", data: nil, wantErr: false}, + {name: "png is allowed", data: pngImage, wantErr: false}, + {name: "svg is allowed", data: []byte(``), wantErr: false}, + {name: "html is rejected", data: []byte(``), wantErr: true}, + {name: "html without doctype is rejected", data: []byte(`x`), wantErr: true}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := validateImageData(tt.data) + if (err != nil) != tt.wantErr { + t.Errorf("validateImageData() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} diff --git a/ui/v2.5/biome.jsonc b/ui/v2.5/biome.jsonc index cf2aae3af1..bf7cd6a0e2 100644 --- a/ui/v2.5/biome.jsonc +++ b/ui/v2.5/biome.jsonc @@ -29,7 +29,24 @@ "types": "recommended" }, "rules": { + // generally want to enforce recommended rules + // so change recommended rules that emit info by default to error + // "a11y": "off", + "complexity": { + "noUselessCatch": "error", + "noUselessEscapeInRegex": "error", + "noUselessFragments": "error", + "noUselessLabel": "error", + "noUselessLoneBlockStatements": "error", + "noUselessStringRaw": "error", + "noUselessSwitchCase": "error", + "noUselessTernary": "error", + "useFlatMap": "error", + "useIndexOf": "error", + "useLiteralKeys": "error", + "useOptionalChain": "error" + }, "style": { // used where the type is known to be non-null, but the compiler can't figure it out "noNonNullAssertion": "off", diff --git a/ui/v2.5/graphql/data/scrapers.graphql b/ui/v2.5/graphql/data/scrapers.graphql index 0dae3c2d5f..b71ab96317 100644 --- a/ui/v2.5/graphql/data/scrapers.graphql +++ b/ui/v2.5/graphql/data/scrapers.graphql @@ -211,6 +211,7 @@ fragment ScrapedSceneData on ScrapedScene { hash algorithm duration + submissions } } @@ -282,6 +283,7 @@ fragment ScrapedStashBoxSceneData on ScrapedScene { hash algorithm duration + submissions } studio { diff --git a/ui/v2.5/package.json b/ui/v2.5/package.json index d6b33704e7..9d6472a44f 100644 --- a/ui/v2.5/package.json +++ b/ui/v2.5/package.json @@ -13,8 +13,8 @@ "validate": "pnpm run lint && pnpm run check && pnpm run format-check", "lint": "pnpm run lint:js && pnpm run lint:css", "lint:css": "stylelint --cache \"src/**/*.scss\"", - "lint:js": "biome lint", - "lint:js:fix": "biome lint --write", + "lint:js": "biome lint --error-on-warnings", + "lint:js:fix": "biome lint --error-on-warnings --write", "check": "tsc --noEmit", "stylelint": "stylelint", "format": "biome format --write", diff --git a/ui/v2.5/src/components/Galleries/GalleryDetails/GalleryEditPanel.tsx b/ui/v2.5/src/components/Galleries/GalleryDetails/GalleryEditPanel.tsx index dccf13966f..b75f570a5e 100644 --- a/ui/v2.5/src/components/Galleries/GalleryDetails/GalleryEditPanel.tsx +++ b/ui/v2.5/src/components/Galleries/GalleryDetails/GalleryEditPanel.tsx @@ -117,10 +117,8 @@ export const GalleryEditPanel: React.FC = ({ onSubmit: submit, }); - const { tags, updateTagsStateFromScraper, tagsControl } = useTagsEdit( - gallery.tags, - (ids) => formik.setFieldValue("tag_ids", ids) - ); + const { tags, updateTagsStateFromScraper, resetTagsState, tagsControl } = + useTagsEdit(gallery.tags, (ids) => formik.setFieldValue("tag_ids", ids)); function onSetScenes(items: Scene[]) { setScenes(items); @@ -199,6 +197,12 @@ export const GalleryEditPanel: React.FC = ({ try { await onSubmit(input, andNew); formik.resetForm(); + if (andNew) { + setScenes(gallery.scenes ?? []); + setPerformers(gallery.performers ?? []); + setStudio(gallery.studio ?? null); + resetTagsState(); + } } catch (e) { Toast.error(e); } diff --git a/ui/v2.5/src/components/Galleries/GalleryList.tsx b/ui/v2.5/src/components/Galleries/GalleryList.tsx index 9caed3e052..1f06989860 100644 --- a/ui/v2.5/src/components/Galleries/GalleryList.tsx +++ b/ui/v2.5/src/components/Galleries/GalleryList.tsx @@ -496,6 +496,7 @@ export const FilteredGalleryList = PatchComponent( /> showEditFilter(c.criterionOption.type)} onRemoveCriterion={removeCriterion} diff --git a/ui/v2.5/src/components/Groups/GroupDetails/Group.tsx b/ui/v2.5/src/components/Groups/GroupDetails/Group.tsx index 30f14beefb..d153b4e42f 100644 --- a/ui/v2.5/src/components/Groups/GroupDetails/Group.tsx +++ b/ui/v2.5/src/components/Groups/GroupDetails/Group.tsx @@ -176,10 +176,7 @@ const GroupPage: React.FC = PatchComponent( const [backImage, setBackImage] = useState(); const [encodingImage, setEncodingImage] = useState(false); - const aliases = useMemo( - () => (group.aliases ? [group.aliases] : []), - [group.aliases] - ); + const aliases = useMemo(() => group.aliases ?? [], [group.aliases]); const isDefaultImage = group.front_image_path?.includes("default=true"); diff --git a/ui/v2.5/src/components/Groups/GroupDetails/GroupEditPanel.tsx b/ui/v2.5/src/components/Groups/GroupDetails/GroupEditPanel.tsx index 62abc38f6d..6152c41d5d 100644 --- a/ui/v2.5/src/components/Groups/GroupDetails/GroupEditPanel.tsx +++ b/ui/v2.5/src/components/Groups/GroupDetails/GroupEditPanel.tsx @@ -22,6 +22,7 @@ import { formikUtils } from "src/utils/form"; import { yupDateString, yupFormikValidate, + yupRequiredStringArray, yupUniqueStringList, } from "src/utils/yup"; import { Studio, StudioSelect } from "src/components/Studios/StudioSelect"; @@ -71,7 +72,7 @@ export const GroupEditPanel: React.FC = ({ const schema = yup.object({ name: yup.string().required(), - aliases: yup.string().ensure(), + aliases: yupRequiredStringArray(intl).defined(), duration: yup.number().integer().min(0).nullable().defined(), date: yupDateString(intl), studio_id: yup.string().required().nullable(), @@ -94,7 +95,7 @@ export const GroupEditPanel: React.FC = ({ const initialValues = { name: group?.name ?? "", - aliases: group?.aliases ?? "", + aliases: group?.aliases ?? [], duration: group?.duration ?? null, date: group?.date ?? "", studio_id: group?.studio?.id ?? null, @@ -127,10 +128,8 @@ export const GroupEditPanel: React.FC = ({ onSubmit: submit, }); - const { tags, updateTagsStateFromScraper, tagsControl } = useTagsEdit( - group.tags, - (ids) => formik.setFieldValue("tag_ids", ids) - ); + const { tags, updateTagsStateFromScraper, resetTagsState, tagsControl } = + useTagsEdit(group.tags, (ids) => formik.setFieldValue("tag_ids", ids)); const containingGroupEntries = useMemo(() => { return formik.values.containing_groups @@ -230,6 +229,11 @@ export const GroupEditPanel: React.FC = ({ try { await onSubmit(input, andNew); formik.resetForm(); + if (andNew) { + setStudio(group.studio ?? null); + setContainingGroups(group.containing_groups?.map((m) => m.group) ?? []); + resetTagsState(); + } } catch (e) { Toast.error(e); } @@ -400,6 +404,7 @@ export const GroupEditPanel: React.FC = ({ renderInputField, renderDateField, renderDurationField, + renderStringListField, renderURLListField, } = formikUtils(intl, formik); @@ -469,7 +474,7 @@ export const GroupEditPanel: React.FC = ({
{renderInputField("name")} - {renderInputField("aliases")} + {renderStringListField("aliases")} {renderDurationField("duration")} {renderDateField("date")} {renderContainingGroupsField()} diff --git a/ui/v2.5/src/components/Groups/GroupDetails/GroupScrapeDialog.tsx b/ui/v2.5/src/components/Groups/GroupDetails/GroupScrapeDialog.tsx index 278809cdd1..673adfa975 100644 --- a/ui/v2.5/src/components/Groups/GroupDetails/GroupScrapeDialog.tsx +++ b/ui/v2.5/src/components/Groups/GroupDetails/GroupScrapeDialog.tsx @@ -41,8 +41,13 @@ export const GroupScrapeDialog: React.FC = ({ const [name, setName] = useState>( new ScrapeResult(group.name, scraped.name) ); - const [aliases, setAliases] = useState>( - new ScrapeResult(group.aliases, scraped.aliases) + const [aliases, setAliases] = useState>( + new ScrapeResult( + group.aliases, + scraped.aliases + ? uniq((group.aliases ?? []).concat(scraped.aliases)) + : group.aliases + ) ); const [duration, setDuration] = useState>( new ScrapeResult( @@ -154,7 +159,7 @@ export const GroupScrapeDialog: React.FC = ({ result={name} onChange={(value) => setName(value)} /> - showEditFilter(c.criterionOption.type)} onRemoveCriterion={removeCriterion} diff --git a/ui/v2.5/src/components/Groups/GroupSelect.tsx b/ui/v2.5/src/components/Groups/GroupSelect.tsx index 780e23561a..0556fad4a7 100644 --- a/ui/v2.5/src/components/Groups/GroupSelect.tsx +++ b/ui/v2.5/src/components/Groups/GroupSelect.tsx @@ -47,7 +47,7 @@ function sortGroupsByRelevance(input: string, groups: FindGroupsResult) { input, groups, (m) => m.name, - (m) => (m.aliases ? [m.aliases] : []) + (m) => m.aliases ?? [] ); } @@ -111,7 +111,9 @@ export const GroupSelect: React.FC< const { inputValue } = optionProps.selectProps; let alias: string | undefined = ""; if (!title.toLowerCase().includes(inputValue.toLowerCase())) { - alias = object.aliases || undefined; + alias = object.aliases?.find((a) => + a.toLowerCase().includes(inputValue.toLowerCase()) + ); } thisOptionProps = { @@ -204,6 +206,7 @@ export const GroupSelect: React.FC< return { id, name, + aliases: [], }; }; @@ -216,7 +219,7 @@ export const GroupSelect: React.FC< options.some((o) => { return ( o.name.toLowerCase() === inputValue.toLowerCase() || - o.aliases?.toLowerCase() === inputValue.toLowerCase() + o.aliases?.some((a) => a.toLowerCase() === inputValue.toLowerCase()) ); }) ) { diff --git a/ui/v2.5/src/components/Images/ImageList.tsx b/ui/v2.5/src/components/Images/ImageList.tsx index cad80c1896..4e25829310 100644 --- a/ui/v2.5/src/components/Images/ImageList.tsx +++ b/ui/v2.5/src/components/Images/ImageList.tsx @@ -744,6 +744,7 @@ export const FilteredImageList = PatchComponent( /> showEditFilter(c.criterionOption.type)} onRemoveCriterion={removeCriterion} diff --git a/ui/v2.5/src/components/List/EditFilterDialog.tsx b/ui/v2.5/src/components/List/EditFilterDialog.tsx index a6eca5cb71..101e1ab08e 100644 --- a/ui/v2.5/src/components/List/EditFilterDialog.tsx +++ b/ui/v2.5/src/components/List/EditFilterDialog.tsx @@ -36,7 +36,11 @@ import { import { useFocusOnce } from "src/utils/focus"; import Mousetrap from "mousetrap"; import ScreenUtils from "src/utils/screen"; -import { LoadFilterDialog, SaveFilterDialog } from "./SavedFilterList"; +import { + LoadFilterDialog, + notifySavedFilterLoaded, + SaveFilterDialog, +} from "./SavedFilterList"; import { SearchTermInput } from "./ListFilter"; interface ICriterionList { @@ -480,6 +484,11 @@ export const EditFilterDialog: React.FC = ({ newFilter.randomSeed = -1; onApply(newFilter); + notifySavedFilterLoaded({ + filter: newFilter, + savedFilter: f, + source: "dialog", + }); } async function onSaveFilter(name: string, id?: string) { diff --git a/ui/v2.5/src/components/List/FilterTags.tsx b/ui/v2.5/src/components/List/FilterTags.tsx index abc2d34c72..22abd68615 100644 --- a/ui/v2.5/src/components/List/FilterTags.tsx +++ b/ui/v2.5/src/components/List/FilterTags.tsx @@ -22,6 +22,8 @@ import { CustomFieldsCriterion } from "src/models/list-filter/criteria/custom-fi import { useDebounce } from "src/hooks/debounce"; import cx from "classnames"; import { useConfigurationContext } from "src/hooks/Config"; +import { PatchContainerComponent } from "src/patch"; +import { View } from "./views"; type TagItemProps = PropsWithChildren< ReplaceProps<"span", BsPrefixProps<"span"> & BadgeProps> @@ -122,6 +124,7 @@ const MoreFilterTags: React.FC<{ interface IFilterTagsProps { searchTerm?: string; + view?: View; criteria: Criterion[]; onEditSearchTerm?: () => void; onEditCriterion: (c: Criterion) => void; @@ -131,8 +134,18 @@ interface IFilterTagsProps { truncateOnOverflow?: boolean; } +interface IFilterTagExtrasProps { + searchTerm?: string; + view?: View; + criteria: Criterion[]; +} + +const FilterTagExtras = + PatchContainerComponent("FilterTags.Extras"); + export const FilterTags: React.FC = ({ searchTerm, + view, criteria, onEditCriterion, onRemoveCriterion, @@ -302,10 +315,6 @@ export const FilterTags: React.FC = ({ ); } - if (criteria.length === 0 && !searchTerm) { - return null; - } - const className = "wrap-tags filter-tags"; const filterTags = criteria.flatMap((c) => getFilterTags(c)); @@ -331,19 +340,30 @@ export const FilterTags: React.FC = ({ cutoff !== undefined ? filterTags.slice(0, cutoff) : filterTags; const hiddenCriteria = cutoff !== undefined ? filterTags.slice(cutoff) : []; + const extras = ( + + ); + + if (filterTags.length === 0) { + return extras; + } + return ( -
- {visibleCriteria} - - {filterTags.length >= 3 && ( - - )} -
+ <> +
+ {visibleCriteria} + + {filterTags.length >= 3 && ( + + )} +
+ {extras} + ); }; diff --git a/ui/v2.5/src/components/List/Filters/LabeledIdFilter.tsx b/ui/v2.5/src/components/List/Filters/LabeledIdFilter.tsx index e67d091b36..4f5219c721 100644 --- a/ui/v2.5/src/components/List/Filters/LabeledIdFilter.tsx +++ b/ui/v2.5/src/components/List/Filters/LabeledIdFilter.tsx @@ -23,6 +23,7 @@ import { HierarchicalCountInput, ImageFilterType, InputMaybe, + IntCriterionInput, PerformerFilterType, SceneFilterType, SceneMarkerFilterType, @@ -520,21 +521,38 @@ export function makeQueryVariables(query: string, extraProps: object) { interface IFilterType { scenes_filter?: InputMaybe; - scene_count?: InputMaybe; + scene_count?: InputMaybe; performers_filter?: InputMaybe; - performer_count?: InputMaybe; + performer_count?: InputMaybe; galleries_filter?: InputMaybe; - gallery_count?: InputMaybe; + gallery_count?: InputMaybe; images_filter?: InputMaybe; - image_count?: InputMaybe; + image_count?: InputMaybe; groups_filter?: InputMaybe; - group_count?: InputMaybe; + group_count?: InputMaybe; studios_filter?: InputMaybe; - studio_count?: InputMaybe; - marker_count?: InputMaybe; + studio_count?: InputMaybe; + marker_count?: InputMaybe; markers_filter?: InputMaybe; } +type CountCriterionInput = IntCriterionInput | HierarchicalCountInput; + +interface ISetObjectFilterOptions { + hierarchicalCounts?: boolean; +} + +function makeCountCriterion( + options?: ISetObjectFilterOptions +): CountCriterionInput { + const criterion = { + modifier: CriterionModifier.GreaterThan, + value: 0, + }; + + return options?.hierarchicalCounts ? { ...criterion, depth: -1 } : criterion; +} + export function setObjectFilter( out: IFilterType, mode: FilterMode, @@ -543,7 +561,8 @@ export function setObjectFilter( | PerformerFilterType | GalleryFilterType | GroupFilterType - | StudioFilterType + | StudioFilterType, + options?: ISetObjectFilterOptions ) { const empty = Object.keys(relatedFilterOutput).length === 0; @@ -551,11 +570,7 @@ export function setObjectFilter( case FilterMode.Scenes: // if empty, only get objects with scenes if (empty) { - out.scene_count = { - modifier: CriterionModifier.GreaterThan, - value: 0, - depth: -1, - }; + out.scene_count = makeCountCriterion(options); break; } out.scenes_filter = relatedFilterOutput as SceneFilterType; @@ -563,11 +578,7 @@ export function setObjectFilter( case FilterMode.Performers: // if empty, only get objects with performers if (empty) { - out.performer_count = { - modifier: CriterionModifier.GreaterThan, - value: 0, - depth: -1, - }; + out.performer_count = makeCountCriterion(options); break; } out.performers_filter = relatedFilterOutput as PerformerFilterType; @@ -575,23 +586,15 @@ export function setObjectFilter( case FilterMode.Galleries: // if empty, only get objects with galleries if (empty) { - out.gallery_count = { - modifier: CriterionModifier.GreaterThan, - value: 0, - depth: -1, - }; + out.gallery_count = makeCountCriterion(options); break; } out.galleries_filter = relatedFilterOutput as GalleryFilterType; break; case FilterMode.Images: - // if empty, only get objects with galleries + // if empty, only get objects with images if (empty) { - out.image_count = { - modifier: CriterionModifier.GreaterThan, - value: 0, - depth: -1, - }; + out.image_count = makeCountCriterion(options); break; } out.images_filter = relatedFilterOutput as ImageFilterType; @@ -599,11 +602,7 @@ export function setObjectFilter( case FilterMode.Groups: // if empty, only get objects with groups if (empty) { - out.group_count = { - modifier: CriterionModifier.GreaterThan, - value: 0, - depth: -1, - }; + out.group_count = makeCountCriterion(options); break; } out.groups_filter = relatedFilterOutput as GroupFilterType; @@ -611,11 +610,7 @@ export function setObjectFilter( case FilterMode.Studios: // if empty, only get objects with studios if (empty) { - out.studio_count = { - modifier: CriterionModifier.GreaterThan, - value: 0, - depth: -1, - }; + out.studio_count = makeCountCriterion(options); break; } out.studios_filter = relatedFilterOutput as StudioFilterType; @@ -623,11 +618,7 @@ export function setObjectFilter( case FilterMode.SceneMarkers: // if empty, only get objects with scene markers if (empty) { - out.marker_count = { - modifier: CriterionModifier.GreaterThan, - value: 0, - depth: -1, - }; + out.marker_count = makeCountCriterion(options); break; } out.markers_filter = relatedFilterOutput as SceneMarkerFilterType; diff --git a/ui/v2.5/src/components/List/Filters/StashIDFilter.tsx b/ui/v2.5/src/components/List/Filters/StashIDFilter.tsx index 3c1f6dd10c..64782f5fb0 100644 --- a/ui/v2.5/src/components/List/Filters/StashIDFilter.tsx +++ b/ui/v2.5/src/components/List/Filters/StashIDFilter.tsx @@ -4,6 +4,8 @@ import { useIntl } from "react-intl"; import { IStashIDValue } from "../../../models/list-filter/types"; import { ModifierCriterion } from "../../../models/list-filter/criteria/criterion"; import { CriterionModifier } from "src/core/generated-graphql"; +import { useConfigurationContext } from "src/hooks/Config"; +import { stashboxDisplayName } from "src/utils/stashbox"; interface IStashIDFilterProps { criterion: ModifierCriterion; @@ -15,9 +17,12 @@ export const StashIDFilter: React.FC = ({ onValueChanged, }) => { const intl = useIntl(); + const { configuration } = useConfigurationContext(); const { value } = criterion; + const stashBoxes = configuration.general.stashBoxes; + const selectedEndpoint = value?.endpoint ?? ""; - function onEndpointChanged(event: React.ChangeEvent) { + function onEndpointChanged(event: React.ChangeEvent) { onValueChanged({ endpoint: event.target.value, stashID: criterion.value.stashID, @@ -35,11 +40,25 @@ export const StashIDFilter: React.FC = ({
+ value={selectedEndpoint} + aria-label={intl.formatMessage({ id: "stash_id_endpoint" })} + > + + {stashBoxes.map((stashBox, index) => ( + + ))} + {selectedEndpoint && + !stashBoxes.some( + (stashBox) => stashBox.endpoint === selectedEndpoint + ) && } + {criterion.modifier !== CriterionModifier.IsNull && criterion.modifier !== CriterionModifier.NotNull && ( diff --git a/ui/v2.5/src/components/List/Filters/TagsFilter.tsx b/ui/v2.5/src/components/List/Filters/TagsFilter.tsx index 446a903319..a1b8e96535 100644 --- a/ui/v2.5/src/components/List/Filters/TagsFilter.tsx +++ b/ui/v2.5/src/components/List/Filters/TagsFilter.tsx @@ -47,7 +47,9 @@ function queryVariables(query: string, f?: ListFilterModel) { // TODO - look for same in AND? } - setObjectFilter(tagFilter, f.mode, filterOutput); + setObjectFilter(tagFilter, f.mode, filterOutput, { + hierarchicalCounts: true, + }); } return makeQueryVariables(query, { tag_filter: tagFilter }); diff --git a/ui/v2.5/src/components/List/SavedFilterList.tsx b/ui/v2.5/src/components/List/SavedFilterList.tsx index 0873115c54..0c8e7df3b1 100644 --- a/ui/v2.5/src/components/List/SavedFilterList.tsx +++ b/ui/v2.5/src/components/List/SavedFilterList.tsx @@ -32,6 +32,7 @@ import cx from "classnames"; import { TruncatedInlineText } from "../Shared/TruncatedText"; import { OperationButton } from "../Shared/OperationButton"; import { createPortal } from "react-dom"; +import { PatchFunction } from "src/patch"; const ExistingSavedFilterList: React.FC<{ name: string; @@ -247,6 +248,18 @@ interface ISavedFilterListProps { menuPortalTarget?: Element | DocumentFragment; } +export interface ISavedFilterLoaded { + savedFilter: SavedFilterDataFragment; + filter: ListFilterModel; + source: "dialog" | "sidebar" | "toolbar"; + view?: View; +} + +export const notifySavedFilterLoaded = PatchFunction( + "SavedFilter.Loaded", + (event: ISavedFilterLoaded) => event +); + export const SavedFilterList: React.FC = ({ filter, onSetFilter, @@ -377,6 +390,12 @@ export const SavedFilterList: React.FC = ({ newFilter.randomSeed = -1; onSetFilter(newFilter); + notifySavedFilterLoaded({ + filter: newFilter, + savedFilter: f, + source: "toolbar", + view, + }); } interface ISavedFilterItem { @@ -762,6 +781,12 @@ export const SidebarSavedFilterList: React.FC = ({ setCurrentSavedFilter({ id: f.id, set: true }); onSetFilter(newFilter); + notifySavedFilterLoaded({ + filter: newFilter, + savedFilter: f, + source: "sidebar", + view, + }); } return ( diff --git a/ui/v2.5/src/components/Performers/PerformerDetails/PerformerEditPanel.tsx b/ui/v2.5/src/components/Performers/PerformerDetails/PerformerEditPanel.tsx index ec00620bc5..0ffa09c32a 100644 --- a/ui/v2.5/src/components/Performers/PerformerDetails/PerformerEditPanel.tsx +++ b/ui/v2.5/src/components/Performers/PerformerDetails/PerformerEditPanel.tsx @@ -178,10 +178,8 @@ export const PerformerEditPanel: React.FC = ({ onSubmit: submit, }); - const { tags, updateTagsStateFromScraper, tagsControl } = useTagsEdit( - performer.tags, - (ids) => formik.setFieldValue("tag_ids", ids) - ); + const { tags, updateTagsStateFromScraper, resetTagsState, tagsControl } = + useTagsEdit(performer.tags, (ids) => formik.setFieldValue("tag_ids", ids)); function translateScrapedGender(scrapedGender?: string) { if (!scrapedGender) { @@ -351,6 +349,9 @@ export const PerformerEditPanel: React.FC = ({ try { await onSubmit(input, andNew); formik.resetForm(); + if (andNew) { + resetTagsState(); + } } catch (e) { Toast.error(e); } diff --git a/ui/v2.5/src/components/Performers/PerformerList.tsx b/ui/v2.5/src/components/Performers/PerformerList.tsx index c2db288bcd..79e2847e6b 100644 --- a/ui/v2.5/src/components/Performers/PerformerList.tsx +++ b/ui/v2.5/src/components/Performers/PerformerList.tsx @@ -608,6 +608,7 @@ export const FilteredPerformerList = PatchComponent( /> showEditFilter(c.criterionOption.type)} onRemoveCriterion={removeCriterion} diff --git a/ui/v2.5/src/components/Performers/PerformerMergeDialog.tsx b/ui/v2.5/src/components/Performers/PerformerMergeDialog.tsx index 0c3c6d8fbb..16e330da27 100644 --- a/ui/v2.5/src/components/Performers/PerformerMergeDialog.tsx +++ b/ui/v2.5/src/components/Performers/PerformerMergeDialog.tsx @@ -46,6 +46,7 @@ import { import { PerformerSelect } from "./PerformerSelect"; import { uniq } from "lodash-es"; import { StashIDsField } from "../Shared/StashID"; +import { PatchComponent } from "src/patch"; type MergeOptions = { values: GQL.PerformerUpdateInput; @@ -728,190 +729,186 @@ interface IPerformerMergeModalProps { performers: GQL.SelectPerformerDataFragment[]; } -export const PerformerMergeModal: React.FC = ({ - show, - onClose, - performers, -}) => { - const [sourcePerformers, setSourcePerformers] = useState< - GQL.SelectPerformerDataFragment[] - >([]); - const [destPerformer, setDestPerformer] = useState< - GQL.SelectPerformerDataFragment[] - >([]); - - const [loadedSources, setLoadedSources] = useState< - GQL.PerformerDataFragment[] - >([]); - const [loadedDest, setLoadedDest] = useState(); +export const PerformerMergeModal: React.FC = + PatchComponent("PerformerMergeModal", ({ show, onClose, performers }) => { + const [sourcePerformers, setSourcePerformers] = useState< + GQL.SelectPerformerDataFragment[] + >([]); + const [destPerformer, setDestPerformer] = useState< + GQL.SelectPerformerDataFragment[] + >([]); + + const [loadedSources, setLoadedSources] = useState< + GQL.PerformerDataFragment[] + >([]); + const [loadedDest, setLoadedDest] = useState(); + + const [running, setRunning] = useState(false); + const [secondStep, setSecondStep] = useState(false); + + const intl = useIntl(); + const Toast = useToast(); + + const title = intl.formatMessage({ + id: "actions.merge", + }); + + const srcIDs = useMemo( + () => sourcePerformers.map((s) => s.id), + [sourcePerformers] + ); + const destID = useMemo( + () => (destPerformer[0] ? [destPerformer[0].id] : []), + [destPerformer] + ); - const [running, setRunning] = useState(false); - const [secondStep, setSecondStep] = useState(false); + useEffect(() => { + if (performers.length > 0) { + // set the first performer as the destination, others as source + setDestPerformer([performers[0]]); - const intl = useIntl(); - const Toast = useToast(); + if (performers.length > 1) { + setSourcePerformers(performers.slice(1)); + } + } + }, [performers]); - const title = intl.formatMessage({ - id: "actions.merge", - }); + async function loadPerformers() { + const performerIDs = sourcePerformers.map((s) => parseInt(s.id, 10)); + performerIDs.push(parseInt(destPerformer[0].id, 10)); + const query = await queryFindPerformersByID(performerIDs); + const { performers: loadedPerformers } = query.data.findPerformers; - const srcIDs = useMemo( - () => sourcePerformers.map((s) => s.id), - [sourcePerformers] - ); - const destID = useMemo( - () => (destPerformer[0] ? [destPerformer[0].id] : []), - [destPerformer] - ); - - useEffect(() => { - if (performers.length > 0) { - // set the first performer as the destination, others as source - setDestPerformer([performers[0]]); + setLoadedDest(loadedPerformers.find((s) => s.id === destPerformer[0].id)); + setLoadedSources( + loadedPerformers.filter((s) => s.id !== destPerformer[0].id) + ); + setSecondStep(true); + } - if (performers.length > 1) { - setSourcePerformers(performers.slice(1)); + async function onMerge(options: MergeOptions) { + const { values } = options; + try { + setRunning(true); + const result = await mutatePerformerMerge( + destPerformer[0].id, + sourcePerformers.map((s) => s.id), + values + ); + if (result.data?.performerMerge) { + Toast.success(intl.formatMessage({ id: "toast.merged_performers" })); + onClose(destPerformer[0].id); + } + } catch (e) { + Toast.error(e); + } finally { + setRunning(false); } } - }, [performers]); - - async function loadPerformers() { - const performerIDs = sourcePerformers.map((s) => parseInt(s.id, 10)); - performerIDs.push(parseInt(destPerformer[0].id, 10)); - const query = await queryFindPerformersByID(performerIDs); - const { performers: loadedPerformers } = query.data.findPerformers; - setLoadedDest(loadedPerformers.find((s) => s.id === destPerformer[0].id)); - setLoadedSources( - loadedPerformers.filter((s) => s.id !== destPerformer[0].id) - ); - setSecondStep(true); - } + function canMerge() { + return sourcePerformers.length > 0 && destPerformer.length !== 0; + } - async function onMerge(options: MergeOptions) { - const { values } = options; - try { - setRunning(true); - const result = await mutatePerformerMerge( - destPerformer[0].id, - sourcePerformers.map((s) => s.id), - values - ); - if (result.data?.performerMerge) { - Toast.success(intl.formatMessage({ id: "toast.merged_performers" })); - onClose(destPerformer[0].id); + function switchPerformers() { + if (sourcePerformers.length && destPerformer.length) { + const newDest = sourcePerformers[0]; + setSourcePerformers([...sourcePerformers.slice(1), destPerformer[0]]); + setDestPerformer([newDest]); } - onClose(); - } catch (e) { - Toast.error(e); - } finally { - setRunning(false); } - } - - function canMerge() { - return sourcePerformers.length > 0 && destPerformer.length !== 0; - } - function switchPerformers() { - if (sourcePerformers.length && destPerformer.length) { - const newDest = sourcePerformers[0]; - setSourcePerformers([...sourcePerformers.slice(1), destPerformer[0]]); - setDestPerformer([newDest]); + if (secondStep && destPerformer.length > 0) { + return ( + { + setSecondStep(false); + if (values) { + onMerge(values); + } else { + onClose(); + } + }} + /> + ); } - } - if (secondStep && destPerformer.length > 0) { return ( - { - setSecondStep(false); - if (values) { - onMerge(values); - } else { - onClose(); - } + loadPerformers(), }} - /> - ); - } - - return ( - loadPerformers(), - }} - disabled={!canMerge()} - cancel={{ - variant: "secondary", - onClick: () => onClose(), - }} - isRunning={running} - > -
-
- - {FormUtils.renderLabel({ - title: intl.formatMessage({ id: "dialogs.merge.source" }), - labelProps: { - column: true, - sm: 3, - xl: 12, - }, - })} - - setSourcePerformers(items)} - values={sourcePerformers} - menuPortalTarget={document.body} - excludeIds={destID} - /> - - - - - - - {FormUtils.renderLabel({ - title: intl.formatMessage({ - id: "dialogs.merge.destination", - }), - labelProps: { - column: true, - sm: 3, - xl: 12, - }, - })} - - setDestPerformer(items)} - values={destPerformer} - menuPortalTarget={document.body} - excludeIds={srcIDs} - /> - - + + + + {FormUtils.renderLabel({ + title: intl.formatMessage({ + id: "dialogs.merge.destination", + }), + labelProps: { + column: true, + sm: 3, + xl: 12, + }, + })} + + setDestPerformer(items)} + values={destPerformer} + menuPortalTarget={document.body} + excludeIds={srcIDs} + /> + + +
-
- - ); -}; + + ); + }); diff --git a/ui/v2.5/src/components/Performers/PerformerPopover.tsx b/ui/v2.5/src/components/Performers/PerformerPopover.tsx index 04cbf8e874..a84740dc32 100644 --- a/ui/v2.5/src/components/Performers/PerformerPopover.tsx +++ b/ui/v2.5/src/components/Performers/PerformerPopover.tsx @@ -8,15 +8,18 @@ import { useConfigurationContext } from "../../hooks/Config"; import { Placement } from "react-bootstrap/esm/Overlay"; interface IPeromerPopoverCardProps { - id: string; + id?: string; + cardContent?: React.ReactNode; + cardExtras?: React.ReactNode; } -export const PerformerPopoverCard: React.FC = ({ - id, -}) => { - const { data, loading, error } = useFindPerformer(id); +const PerformerPopoverCardByID: React.FC<{ + id: string; + cardExtras?: React.ReactNode; +}> = ({ id, cardExtras }) => { + const { data, loading: isLoading, error } = useFindPerformer(id); - if (loading) + if (isLoading) return (
@@ -29,25 +32,60 @@ export const PerformerPopoverCard: React.FC = ({ const performer = data.findPerformer; return ( -
- -
+ <> +
+ +
+ {cardExtras} + ); }; +export const PerformerPopoverCard: React.FC = ({ + id, + cardContent, + cardExtras, +}) => { + if (cardContent) { + return ( + <> + {cardContent} + {cardExtras} + + ); + } + + if (!id) return null; + return ; +}; + interface IPeroformerPopoverProps { - id: string; + id?: string; + cardContent?: React.ReactNode; + cardExtras?: React.ReactNode; hide?: boolean; placement?: Placement; + enterDelay?: number; + leaveDelay?: number; target?: React.RefObject; + triggerClassName?: string; + onOpen?: () => void; + onClose?: () => void; } export const PerformerPopover: React.FC = ({ id, + cardContent, + cardExtras, hide, children, placement = "top", + enterDelay = 500, + leaveDelay = 100, target, + triggerClassName, + onOpen, + onClose, }) => { const { configuration: config } = useConfigurationContext(); @@ -59,11 +97,20 @@ export const PerformerPopover: React.FC = ({ return ( } + enterDelay={enterDelay} + leaveDelay={leaveDelay} + onOpen={onOpen} + onClose={onClose} + content={ + + } > {children} diff --git a/ui/v2.5/src/components/Performers/PerformerSelect.tsx b/ui/v2.5/src/components/Performers/PerformerSelect.tsx index 5948f91296..2e2a237938 100644 --- a/ui/v2.5/src/components/Performers/PerformerSelect.tsx +++ b/ui/v2.5/src/components/Performers/PerformerSelect.tsx @@ -1,6 +1,7 @@ import React, { useEffect, useMemo, useState } from "react"; import { OptionProps, + ValueContainerProps, components as reactSelectComponents, MultiValueGenericProps, SingleValueProps, @@ -82,6 +83,10 @@ const _PerformerSelect: React.FC< hoverPlacementLabel?: Placement; hoverPlacementOptions?: Placement; excludeIds?: string[]; + wrapValueContainer?: ( + children: React.ReactNode, + performer: Performer + ) => React.ReactNode; } > = (props) => { const [createPerformer] = usePerformerCreate(); @@ -273,6 +278,19 @@ const _PerformerSelect: React.FC< return ; }; + const PerformerValueContainer = ( + vcProps: ValueContainerProps + ) => { + const selected = vcProps.getValue()[0]?.object; + const container = ; + + if (!props.wrapValueContainer || !selected) { + return container; + } + + return <>{props.wrapValueContainer(container, selected)}; + }; + const onCreate = async (name: string) => { const result = await createPerformer({ variables: { input: { name } }, @@ -330,6 +348,9 @@ const _PerformerSelect: React.FC< Option: PerformerOption, MultiValueLabel: PerformerMultiValueLabel, SingleValue: PerformerValueLabel, + ...(props.wrapValueContainer + ? { ValueContainer: PerformerValueContainer } + : undefined), }} isMulti={props.isMulti ?? false} creatable={props.creatable ?? defaultCreatable} diff --git a/ui/v2.5/src/components/Performers/styles.scss b/ui/v2.5/src/components/Performers/styles.scss index 49dc27550f..8156221196 100644 --- a/ui/v2.5/src/components/Performers/styles.scss +++ b/ui/v2.5/src/components/Performers/styles.scss @@ -158,6 +158,16 @@ } } +.performer-preview-popover { + .card { + width: 200px; + } + + .performer-card-image { + max-height: 300px; + } +} + .scrape-dialog .performer-image { display: block; margin-bottom: 10px; diff --git a/ui/v2.5/src/components/ScenePlayer/styles.scss b/ui/v2.5/src/components/ScenePlayer/styles.scss index fc143a8733..87e704cf5d 100644 --- a/ui/v2.5/src/components/ScenePlayer/styles.scss +++ b/ui/v2.5/src/components/ScenePlayer/styles.scss @@ -647,6 +647,7 @@ $sceneTabWidth: 450px; @media (min-width: 1200px) { .scene-tabs { flex: 0 0 $sceneTabWidth; + max-height: calc(100vh - 4rem); max-width: $sceneTabWidth; overflow: auto; diff --git a/ui/v2.5/src/components/Scenes/SceneDetails/Scene.tsx b/ui/v2.5/src/components/Scenes/SceneDetails/Scene.tsx index 533b57b56b..cace8cc55c 100644 --- a/ui/v2.5/src/components/Scenes/SceneDetails/Scene.tsx +++ b/ui/v2.5/src/components/Scenes/SceneDetails/Scene.tsx @@ -1,5 +1,6 @@ import { Tab, Nav, Dropdown, Button } from "react-bootstrap"; import React, { + useCallback, useEffect, useState, useMemo, @@ -57,6 +58,7 @@ import { SceneMergeModal } from "../SceneMergeDialog"; import { goBackOrReplace } from "src/utils/history"; import { FormattedDate } from "src/components/Shared/Date"; import { StudioLogo } from "src/components/Shared/StudioLogo"; +import { JobFragment, useMonitorJob } from "src/utils/job"; const SubmitStashBoxDraft = lazyComponent( () => import("src/components/Dialogs/SubmitDraft") @@ -154,6 +156,7 @@ interface IProps { collapsed: boolean; setCollapsed: (state: boolean) => void; setContinuePlaylist: (value: boolean) => void; + onRefreshScene: () => Promise; } interface ISceneParams { @@ -183,6 +186,7 @@ const ScenePage: React.FC = PatchComponent("ScenePage", (props) => { collapsed, setCollapsed, setContinuePlaylist, + onRefreshScene, } = props; const Toast = useToast(); @@ -190,6 +194,7 @@ const ScenePage: React.FC = PatchComponent("ScenePage", (props) => { const history = useHistory(); const [updateScene] = useSceneUpdate(); const [generateScreenshot] = useSceneGenerateScreenshot(); + const [screenshotJobID, setScreenshotJobID] = useState(); const { configuration } = useConfigurationContext(); const { showStudioText } = configuration?.ui ?? {}; @@ -216,6 +221,27 @@ const ScenePage: React.FC = PatchComponent("ScenePage", (props) => { const [isDeleteAlertOpen, setIsDeleteAlertOpen] = useState(false); const [isGenerateDialogOpen, setIsGenerateDialogOpen] = useState(false); + const onScreenshotJobComplete = useCallback( + async (job?: JobFragment) => { + setScreenshotJobID(undefined); + + if (job?.status === GQL.JobStatus.Failed) { + Toast.error(job.error); + return; + } + + if (job?.status === GQL.JobStatus.Cancelled) { + return; + } + + await onRefreshScene(); + Toast.success(intl.formatMessage({ id: "toast.screenshot_generated" })); + }, + [Toast, intl, onRefreshScene] + ); + + useMonitorJob(screenshotJobID, onScreenshotJobComplete); + const onIncrementOClick = async () => { try { await incrementO(); @@ -385,13 +411,21 @@ const ScenePage: React.FC = PatchComponent("ScenePage", (props) => { } async function onGenerateScreenshot(at?: number) { - await generateScreenshot({ - variables: { - id: scene.id, - at, - }, - }); - Toast.success(intl.formatMessage({ id: "toast.generating_screenshot" })); + try { + const result = await generateScreenshot({ + variables: { + id: scene.id, + at, + }, + }); + const jobID = result.data?.sceneGenerateScreenshot; + if (jobID) { + setScreenshotJobID(jobID); + } + Toast.success(intl.formatMessage({ id: "toast.generating_screenshot" })); + } catch (e) { + Toast.error(e); + } } function onDeleteDialogClosed(deleted: boolean) { @@ -468,20 +502,6 @@ const ScenePage: React.FC = PatchComponent("ScenePage", (props) => { > … - onGenerateScreenshot(getPlayerPosition())} - > - - - onGenerateScreenshot()} - > - - {boxes.length > 0 && ( = PatchComponent("ScenePage", (props) => { + onGenerateScreenshot(getPlayerPosition()) + } + onGenerateThumbDefault={() => onGenerateScreenshot()} onSubmit={onSave} onDelete={() => setIsDeleteAlertOpen(true)} /> @@ -756,10 +780,17 @@ const SceneLoader: React.FC> = ({ }) => { const { id } = match.params; const { configuration } = useConfigurationContext(); - const { data, loading, error } = useFindScene(id); + const { data, loading, error, refetch } = useFindScene(id); const [scene, setScene] = useState(); + const onRefreshScene = useCallback(async () => { + const result = await refetch(); + if (result.data?.findScene) { + setScene(result.data.findScene); + } + }, [refetch]); + // useLayoutEffect to update before paint useLayoutEffect(() => { // only update scene when loading is done @@ -1029,6 +1060,7 @@ const SceneLoader: React.FC> = ({ collapsed={collapsed} setCollapsed={setCollapsed} setContinuePlaylist={setContinuePlaylist} + onRefreshScene={onRefreshScene} />
Promise; + onGenerateThumbDefault?: () => Promise; onSubmit: (input: GQL.SceneCreateInput, andNew?: boolean) => Promise; onDelete?: () => void; } @@ -73,6 +80,8 @@ export const SceneEditPanel: React.FC = ({ initialCoverImage, isNew = false, isVisible, + onGenerateThumbFromCurrent, + onGenerateThumbDefault, onSubmit, onDelete, }) => { @@ -203,10 +212,8 @@ export const SceneEditPanel: React.FC = ({ onSubmit: submit, }); - const { tags, updateTagsStateFromScraper, tagsControl } = useTagsEdit( - scene.tags, - (ids) => formik.setFieldValue("tag_ids", ids) - ); + const { tags, updateTagsStateFromScraper, resetTagsState, tagsControl } = + useTagsEdit(scene.tags, (ids) => formik.setFieldValue("tag_ids", ids)); const coverImagePreview = useMemo(() => { const sceneImage = scene.paths?.screenshot; @@ -298,6 +305,20 @@ export const SceneEditPanel: React.FC = ({ try { await onSubmit(input, andNew); formik.resetForm(); + if (andNew) { + setGalleries( + scene.galleries?.map((g) => ({ + id: g.id, + title: galleryTitle(g), + files: g.files, + folder: g.folder, + })) ?? [] + ); + setPerformers(scene.performers ?? []); + setGroups(scene.groups?.map((m) => m.group) ?? []); + setStudio(scene.studio ?? null); + resetTagsState(); + } } catch (e) { Toast.error(e); } @@ -884,7 +905,30 @@ export const SceneEditPanel: React.FC = ({ isEditing onImageChange={onCoverImageChange} onImageURL={onImageLoad} - onReset={scene.id ? onResetCover : undefined} + // Generate-from-server actions require a saved scene. + extraActions={ + !isNew + ? [ + { + icon: faArrowsRotate, + labelId: "actions.generate_thumb_default", + onClick: () => onGenerateThumbDefault?.(), + }, + { + icon: faCameraRotate, + labelId: "actions.generate_thumb_from_current", + onClick: () => onGenerateThumbFromCurrent?.(), + }, + ] + : undefined + } + onReset={ + formik.values.cover_image || + (formik.values.cover_image !== null && + scene.paths?.screenshot) + ? () => onResetCover() + : undefined + } /> diff --git a/ui/v2.5/src/components/Scenes/SceneList.tsx b/ui/v2.5/src/components/Scenes/SceneList.tsx index 21722a1dbe..c7dfa59830 100644 --- a/ui/v2.5/src/components/Scenes/SceneList.tsx +++ b/ui/v2.5/src/components/Scenes/SceneList.tsx @@ -679,6 +679,7 @@ export const FilteredSceneList = PatchComponent( /> showEditFilter(c.criterionOption.type) diff --git a/ui/v2.5/src/components/Scenes/SceneMarkerList.tsx b/ui/v2.5/src/components/Scenes/SceneMarkerList.tsx index acb8214844..b297ddda35 100644 --- a/ui/v2.5/src/components/Scenes/SceneMarkerList.tsx +++ b/ui/v2.5/src/components/Scenes/SceneMarkerList.tsx @@ -417,6 +417,7 @@ export const FilteredSceneMarkerList = PatchComponent( /> showEditFilter(c.criterionOption.type)} onRemoveCriterion={removeCriterion} diff --git a/ui/v2.5/src/components/Scenes/SceneMergeDialog.tsx b/ui/v2.5/src/components/Scenes/SceneMergeDialog.tsx index c1dfe512d1..ef781cddb6 100644 --- a/ui/v2.5/src/components/Scenes/SceneMergeDialog.tsx +++ b/ui/v2.5/src/components/Scenes/SceneMergeDialog.tsx @@ -46,6 +46,7 @@ import { } from "../Shared/ScrapeDialog/ScrapedObjectsRow"; import { Scene, SceneSelect } from "src/components/Scenes/SceneSelect"; import { StashIDsField } from "../Shared/StashID"; +import { PatchComponent } from "src/patch"; type MergeOptions = { values: GQL.SceneUpdateInput; @@ -699,182 +700,180 @@ interface ISceneMergeModalProps { scenes: { id: string; title: string }[]; } -export const SceneMergeModal: React.FC = ({ - show, - onClose, - scenes, -}) => { - const [sourceScenes, setSourceScenes] = useState([]); - const [destScene, setDestScene] = useState([]); +export const SceneMergeModal: React.FC = PatchComponent( + "SceneMergeModal", + ({ show, onClose, scenes }) => { + const [sourceScenes, setSourceScenes] = useState([]); + const [destScene, setDestScene] = useState([]); - const [loadedSources, setLoadedSources] = useState( - [] - ); - const [loadedDest, setLoadedDest] = useState(); + const [loadedSources, setLoadedSources] = useState( + [] + ); + const [loadedDest, setLoadedDest] = useState(); - const [running, setRunning] = useState(false); - const [secondStep, setSecondStep] = useState(false); + const [running, setRunning] = useState(false); + const [secondStep, setSecondStep] = useState(false); - const intl = useIntl(); - const Toast = useToast(); + const intl = useIntl(); + const Toast = useToast(); - const title = intl.formatMessage({ - id: "actions.merge", - }); + const title = intl.formatMessage({ + id: "actions.merge", + }); - const srcIDs = useMemo(() => sourceScenes.map((s) => s.id), [sourceScenes]); - const destID = useMemo( - () => (destScene[0] ? [destScene[0].id] : []), - [destScene] - ); + const srcIDs = useMemo(() => sourceScenes.map((s) => s.id), [sourceScenes]); + const destID = useMemo( + () => (destScene[0] ? [destScene[0].id] : []), + [destScene] + ); - useEffect(() => { - if (scenes.length > 0) { - // set the first scene as the destination, others as source - setDestScene([scenes[0]]); + useEffect(() => { + if (scenes.length > 0) { + // set the first scene as the destination, others as source + setDestScene([scenes[0]]); - if (scenes.length > 1) { - setSourceScenes(scenes.slice(1)); + if (scenes.length > 1) { + setSourceScenes(scenes.slice(1)); + } } - } - }, [scenes]); + }, [scenes]); - async function loadScenes() { - const sceneIDs = sourceScenes.map((s) => parseInt(s.id, 10)); - sceneIDs.push(parseInt(destScene[0].id, 10)); - const query = await queryFindFullScenesByID(sceneIDs); - const { scenes: loadedScenes } = query.data.findScenes; + async function loadScenes() { + const sceneIDs = sourceScenes.map((s) => parseInt(s.id, 10)); + sceneIDs.push(parseInt(destScene[0].id, 10)); + const query = await queryFindFullScenesByID(sceneIDs); + const { scenes: loadedScenes } = query.data.findScenes; - setLoadedDest(loadedScenes.find((s) => s.id === destScene[0].id)); - setLoadedSources(loadedScenes.filter((s) => s.id !== destScene[0].id)); - setSecondStep(true); - } + setLoadedDest(loadedScenes.find((s) => s.id === destScene[0].id)); + setLoadedSources(loadedScenes.filter((s) => s.id !== destScene[0].id)); + setSecondStep(true); + } - async function onMerge(options: MergeOptions) { - const { values, includeViewHistory, includeOHistory } = options; - try { - setRunning(true); - const result = await mutateSceneMerge( - destScene[0].id, - sourceScenes.map((s) => s.id), - values, - includeViewHistory, - includeOHistory - ); - if (result.data?.sceneMerge) { - Toast.success(intl.formatMessage({ id: "toast.merged_scenes" })); - onClose(destScene[0].id); + async function onMerge(options: MergeOptions) { + const { values, includeViewHistory, includeOHistory } = options; + try { + setRunning(true); + const result = await mutateSceneMerge( + destScene[0].id, + sourceScenes.map((s) => s.id), + values, + includeViewHistory, + includeOHistory + ); + if (result.data?.sceneMerge) { + Toast.success(intl.formatMessage({ id: "toast.merged_scenes" })); + onClose(destScene[0].id); + } + } catch (e) { + Toast.error(e); + } finally { + setRunning(false); } - onClose(); - } catch (e) { - Toast.error(e); - } finally { - setRunning(false); } - } - function canMerge() { - return sourceScenes.length > 0 && destScene.length !== 0; - } + function canMerge() { + return sourceScenes.length > 0 && destScene.length !== 0; + } - function switchScenes() { - if (sourceScenes.length && destScene.length) { - const newDest = sourceScenes[0]; - setSourceScenes([...sourceScenes.slice(1), destScene[0]]); - setDestScene([newDest]); + function switchScenes() { + if (sourceScenes.length && destScene.length) { + const newDest = sourceScenes[0]; + setSourceScenes([...sourceScenes.slice(1), destScene[0]]); + setDestScene([newDest]); + } + } + + if (secondStep && destScene.length > 0) { + return ( + { + setSecondStep(false); + if (values) { + onMerge(values); + } else { + onClose(); + } + }} + /> + ); } - } - if (secondStep && destScene.length > 0) { return ( - { - setSecondStep(false); - if (values) { - onMerge(values); - } else { - onClose(); - } + loadScenes(), }} - /> - ); - } - - return ( - loadScenes(), - }} - disabled={!canMerge()} - cancel={{ - variant: "secondary", - onClick: () => onClose(), - }} - isRunning={running} - > -
-
- - {FormUtils.renderLabel({ - title: intl.formatMessage({ id: "dialogs.merge.source" }), - labelProps: { - column: true, - sm: 3, - xl: 12, - }, - })} - - setSourceScenes(items)} - values={sourceScenes} - menuPortalTarget={document.body} - excludeIds={destID} - /> - - - - - - - {FormUtils.renderLabel({ - title: intl.formatMessage({ - id: "dialogs.merge.destination", - }), - labelProps: { - column: true, - sm: 3, - xl: 12, - }, - })} - - setDestScene(items)} - values={destScene} - menuPortalTarget={document.body} - excludeIds={srcIDs} - /> - - + + + + {FormUtils.renderLabel({ + title: intl.formatMessage({ + id: "dialogs.merge.destination", + }), + labelProps: { + column: true, + sm: 3, + xl: 12, + }, + })} + + setDestScene(items)} + values={destScene} + menuPortalTarget={document.body} + excludeIds={srcIDs} + /> + + +
-
- - ); -}; + + ); + } +); diff --git a/ui/v2.5/src/components/Scenes/styles.scss b/ui/v2.5/src/components/Scenes/styles.scss index dda699744e..6dae3eae92 100644 --- a/ui/v2.5/src/components/Scenes/styles.scss +++ b/ui/v2.5/src/components/Scenes/styles.scss @@ -324,7 +324,6 @@ textarea.scene-description { .scene-tabs { display: flex; flex-direction: column; - max-height: calc(100vh - 4rem); overflow-wrap: break-word; word-wrap: break-word; diff --git a/ui/v2.5/src/components/Shared/GridCard/GridCard.tsx b/ui/v2.5/src/components/Shared/GridCard/GridCard.tsx index cb07f790ac..2ac512433e 100644 --- a/ui/v2.5/src/components/Shared/GridCard/GridCard.tsx +++ b/ui/v2.5/src/components/Shared/GridCard/GridCard.tsx @@ -10,6 +10,7 @@ import { faGripLines } from "@fortawesome/free-solid-svg-icons"; import { DragSide, useDragMoveSelect } from "./dragMoveSelect"; import { useDebounce } from "src/hooks/debounce"; import { PatchComponent } from "src/patch"; +import { ExternalLink } from "../ExternalLink"; interface ICardProps { className?: string; @@ -166,6 +167,27 @@ const MoveTarget: React.FC<{ dragSide: DragSide }> = ({ dragSide }) => { ); }; +function CardNavLink(props: { + url: string; + linkClassName?: string; + onClick: (event: React.MouseEvent) => void; + children: React.ReactNode; +}) { + const { url, linkClassName, onClick, children } = props; + if (/^https?:\/\//i.test(url)) { + return ( + + {children} + + ); + } + return ( + + {children} + + ); +} + export const GridCard: React.FC = PatchComponent( "GridCard", (props: ICardProps) => { @@ -250,24 +272,24 @@ export const GridCard: React.FC = PatchComponent(
- {props.image} - + {props.overlays} {maybeRenderProgressBar()}
{maybeRenderInteractiveHeatmap()}
- +
{props.pretitleIcon}
- +
{props.details}
diff --git a/ui/v2.5/src/components/Shared/HoverPopover.tsx b/ui/v2.5/src/components/Shared/HoverPopover.tsx index 7ec8137cae..9a46aa6d67 100644 --- a/ui/v2.5/src/components/Shared/HoverPopover.tsx +++ b/ui/v2.5/src/components/Shared/HoverPopover.tsx @@ -10,6 +10,7 @@ interface IHoverPopover { content: JSX.Element[] | JSX.Element | string; className?: string; placement?: OverlayProps["placement"]; + popperConfig?: OverlayProps["popperConfig"]; onOpen?: () => void; onClose?: () => void; target?: React.RefObject; @@ -24,14 +25,28 @@ export const HoverPopover: React.FC = PatchComponent( children, className, placement = "top", + popperConfig, onOpen, onClose, target, }) => { const [show, setShow] = useState(false); - const triggerRef = useRef(null); + const [triggerElement, setTriggerElement] = useState( + null + ); const enterTimer = useRef(); const leaveTimer = useRef(); + const popoverId = useRef( + `hover-popover-${Math.random().toString(36).slice(2)}` + ); + + const setTriggerRef = useCallback((element: HTMLDivElement | null) => { + setTriggerElement(element); + }, []); + + const getOverlayTarget = useCallback(() => { + return target?.current ?? triggerElement; + }, [target, triggerElement]); const handleMouseEnter = useCallback(() => { window.clearTimeout(leaveTimer.current); @@ -63,20 +78,22 @@ export const HoverPopover: React.FC = PatchComponent( className={className} onMouseEnter={handleMouseEnter} onMouseLeave={handleMouseLeave} - ref={triggerRef} + ref={setTriggerRef} > {children}
- {triggerRef.current && ( + {getOverlayTarget() && ( {content} diff --git a/ui/v2.5/src/components/Shared/ImageInput.tsx b/ui/v2.5/src/components/Shared/ImageInput.tsx index 70a2bda7f5..552fc648cb 100644 --- a/ui/v2.5/src/components/Shared/ImageInput.tsx +++ b/ui/v2.5/src/components/Shared/ImageInput.tsx @@ -7,14 +7,26 @@ import { Popover, Row, } from "react-bootstrap"; -import { useIntl } from "react-intl"; +import { FormattedMessage, useIntl } from "react-intl"; +import { IconDefinition } from "@fortawesome/fontawesome-svg-core"; import { ModalComponent } from "./Modal"; import { Icon } from "./Icon"; -import { faClipboard, faFile, faLink } from "@fortawesome/free-solid-svg-icons"; +import { + faClipboard, + faFile, + faLink, + faTrashAlt, +} from "@fortawesome/free-solid-svg-icons"; import { PatchComponent } from "src/patch"; import ImageUtils from "src/utils/image"; import { useToast } from "src/hooks/Toast"; +export interface IImageInputExtraAction { + icon: IconDefinition; + labelId: string; + onClick: () => void | Promise; +} + interface IImageInput { isEditing: boolean; text?: string; @@ -22,6 +34,7 @@ interface IImageInput { onImageURL?: (url: string) => void; onReset?: () => void; acceptSVG?: boolean; + extraActions?: IImageInputExtraAction[]; } function acceptExtensions(acceptSVG: boolean = false) { @@ -37,12 +50,12 @@ export const ImageInput: React.FC = PatchComponent( onImageURL, onReset, acceptSVG = false, + extraActions, }) => { const [isShowDialog, setIsShowDialog] = useState(false); const [url, setURL] = useState(""); const intl = useIntl(); const Toast = useToast(); - if (!isEditing) return
; if (!onImageURL) { @@ -50,7 +63,7 @@ export const ImageInput: React.FC = PatchComponent( return ( = PatchComponent(
- {intl.formatMessage({ id: "url" })} + = PatchComponent(
- - - - +
{window.isSecureContext && ( @@ -156,11 +173,37 @@ export const ImageInput: React.FC = PatchComponent(
)} + {extraActions && extraActions.length > 0 && ( +
+ )} + {extraActions?.map((action) => ( +
+ +
+ ))} + {onReset && ( + <> +
+
+ +
+ + )} ); @@ -175,14 +218,9 @@ export const ImageInput: React.FC = PatchComponent( rootClose > - {onReset && ( - - )} ); } diff --git a/ui/v2.5/src/components/Shared/ScrapeDialog/ScrapedObjectsRow.tsx b/ui/v2.5/src/components/Shared/ScrapeDialog/ScrapedObjectsRow.tsx index 5e893b2dbe..ef00cb07ea 100644 --- a/ui/v2.5/src/components/Shared/ScrapeDialog/ScrapedObjectsRow.tsx +++ b/ui/v2.5/src/components/Shared/ScrapeDialog/ScrapedObjectsRow.tsx @@ -324,7 +324,7 @@ export const ScrapedGroupsRow: React.FC< const value = resultValue ?? []; const selectValue = value.map((p) => { - const aliases: string = ""; + const aliases: string[] = []; return { id: p.stored_id ?? "", name: p.name ?? "", @@ -340,7 +340,12 @@ export const ScrapedGroupsRow: React.FC< onSelect={(items) => { if (onChangeFn) { // map the id back to stored_id - onChangeFn(items.map((p) => ({ ...p, stored_id: p.id }))); + onChangeFn( + items.map((p) => ({ + ...p, + stored_id: p.id, + })) + ); } }} values={selectValue} diff --git a/ui/v2.5/src/components/Shared/styles.scss b/ui/v2.5/src/components/Shared/styles.scss index acc8556eb8..877ab2b4f7 100644 --- a/ui/v2.5/src/components/Shared/styles.scss +++ b/ui/v2.5/src/components/Shared/styles.scss @@ -237,6 +237,7 @@ button.collapse-button { .hover-popover-content { max-width: 32rem; text-align: center; + z-index: 2000; .popover-card { padding: 0.5rem; diff --git a/ui/v2.5/src/components/Studios/StudioDetails/Studio.tsx b/ui/v2.5/src/components/Studios/StudioDetails/Studio.tsx index 4fc7072315..4ddc32831a 100644 --- a/ui/v2.5/src/components/Studios/StudioDetails/Studio.tsx +++ b/ui/v2.5/src/components/Studios/StudioDetails/Studio.tsx @@ -511,9 +511,9 @@ const StudioPage: React.FC = PatchComponent( clickToRate withoutContext /> - {showAllCounts && !!studio.o_counter_all ? ( + {showAllCounts && studio.o_counter_all ? ( - ) : !showAllCounts && !!studio.o_counter ? ( + ) : !showAllCounts && studio.o_counter ? ( ) : null}
diff --git a/ui/v2.5/src/components/Studios/StudioDetails/StudioEditPanel.tsx b/ui/v2.5/src/components/Studios/StudioDetails/StudioEditPanel.tsx index 020b66f387..42a76a1980 100644 --- a/ui/v2.5/src/components/Studios/StudioDetails/StudioEditPanel.tsx +++ b/ui/v2.5/src/components/Studios/StudioDetails/StudioEditPanel.tsx @@ -106,7 +106,7 @@ export const StudioEditPanel: React.FC = ({ onSubmit: submit, }); - const { tagsControl } = useTagsEdit(studio.tags, (ids) => + const { resetTagsState, tagsControl } = useTagsEdit(studio.tags, (ids) => formik.setFieldValue("tag_ids", ids) ); @@ -176,6 +176,26 @@ export const StudioEditPanel: React.FC = ({ try { await onSubmit(input, andNew); formik.resetForm(); + if (andNew) { + setParentStudio( + studio.parent_studio + ? { + id: studio.parent_studio.id, + name: studio.parent_studio.name, + aliases: [], + } + : null + ); + setChildStudios( + (studio.child_studios ?? []).map((childStudio) => ({ + id: childStudio.id, + name: childStudio.name, + aliases: [], + image_path: childStudio.image_path, + })) + ); + resetTagsState(); + } } catch (e) { Toast.error(e); } diff --git a/ui/v2.5/src/components/Studios/StudioList.tsx b/ui/v2.5/src/components/Studios/StudioList.tsx index 478c632c84..2bfda1afbe 100644 --- a/ui/v2.5/src/components/Studios/StudioList.tsx +++ b/ui/v2.5/src/components/Studios/StudioList.tsx @@ -417,6 +417,7 @@ export const FilteredStudioList = PatchComponent( /> showEditFilter(c.criterionOption.type)} onRemoveCriterion={removeCriterion} diff --git a/ui/v2.5/src/components/Tagger/PerformerCollisionWarningButton.tsx b/ui/v2.5/src/components/Tagger/PerformerCollisionWarningButton.tsx new file mode 100644 index 0000000000..0f3188f140 --- /dev/null +++ b/ui/v2.5/src/components/Tagger/PerformerCollisionWarningButton.tsx @@ -0,0 +1,108 @@ +import React, { useCallback, useEffect, useRef, useState } from "react"; +import { Button, Overlay, Popover } from "react-bootstrap"; +import { faTriangleExclamation } from "@fortawesome/free-solid-svg-icons"; + +import { Icon } from "../Shared/Icon"; +import { PerformerCollisionPopoverContent } from "./scenes/TaggerPerformerPopover"; + +const ENTER_DELAY = 200; +const LEAVE_DELAY = 200; + +export const PerformerCollisionWarningButton: React.FC<{ + messageIds: string[]; +}> = ({ messageIds }) => { + const buttonRef = useRef(null); + const [showPopover, setShowPopover] = useState(false); + const [pinned, setPinned] = useState(false); + const enterTimer = useRef(); + const leaveTimer = useRef(); + const popoverId = useRef( + `performer-collision-warning-${Math.random().toString(36).slice(2)}` + ); + + const dismissPopover = useCallback(() => { + window.clearTimeout(enterTimer.current); + window.clearTimeout(leaveTimer.current); + setPinned(false); + setShowPopover(false); + }, []); + + const handleMouseEnter = useCallback(() => { + window.clearTimeout(leaveTimer.current); + enterTimer.current = window.setTimeout( + () => setShowPopover(true), + ENTER_DELAY + ); + }, []); + + const handleMouseLeave = useCallback(() => { + if (pinned) return; + + window.clearTimeout(enterTimer.current); + leaveTimer.current = window.setTimeout( + () => setShowPopover(false), + LEAVE_DELAY + ); + }, [pinned]); + + const handleClick = useCallback(() => { + window.clearTimeout(enterTimer.current); + window.clearTimeout(leaveTimer.current); + setPinned(true); + setShowPopover(true); + }, []); + + useEffect( + () => () => { + window.clearTimeout(enterTimer.current); + window.clearTimeout(leaveTimer.current); + }, + [] + ); + + if (messageIds.length === 0) { + return null; + } + + return ( + <> + + + + + + + + ); +}; diff --git a/ui/v2.5/src/components/Tagger/scenes/PerformerResult.tsx b/ui/v2.5/src/components/Tagger/scenes/PerformerResult.tsx index 53caba2fff..89713f160d 100755 --- a/ui/v2.5/src/components/Tagger/scenes/PerformerResult.tsx +++ b/ui/v2.5/src/components/Tagger/scenes/PerformerResult.tsx @@ -12,6 +12,11 @@ import { getStashboxBase } from "src/utils/stashbox"; import { ExternalLink } from "src/components/Shared/ExternalLink"; import { Link } from "react-router-dom"; import { LinkButton } from "../LinkButton"; +import { PerformerCollisionWarningButton } from "../PerformerCollisionWarningButton"; +import { + getPerformerCollisionMessageIds, + TaggerPerformerPopover, +} from "./TaggerPerformerPopover"; const PerformerLink: React.FC<{ performer: GQL.ScrapedPerformer | Performer; @@ -73,9 +78,14 @@ const PerformerResult: React.FC = ({ stashID.endpoint === endpoint && stashID.stash_id === performer.remote_site_id ); - const [selectedPerformer, setSelectedPerformer] = useState(); + const { data: selectedPerformerData } = GQL.useFindPerformerQuery({ + variables: { id: selectedID ?? "" }, + skip: !selectedID, + }); + const selectedLocalPerformer = selectedPerformerData?.findPerformer; + const stashboxPerformerPrefix = endpoint ? `${getStashboxBase(endpoint)}performers/` : undefined; @@ -115,10 +125,15 @@ const PerformerResult: React.FC = ({
: - + + +
@@ -147,6 +162,13 @@ const PerformerResult: React.FC = ({ } const selectedSource = !selectedID ? "skip" : "existing"; + const collisionMessageIds = selectedLocalPerformer + ? getPerformerCollisionMessageIds( + performer, + selectedLocalPerformer, + endpoint + ) + : []; const safeBuildPerformerScraperLink = (id: string | null | undefined) => { return stashboxPerformerPrefix && id @@ -159,10 +181,15 @@ const PerformerResult: React.FC = ({
: - + + +
@@ -181,7 +208,25 @@ const PerformerResult: React.FC = ({ active={selectedSource === "existing"} isClearable={false} ageFromDate={ageFromDate} + wrapValueContainer={(container, selected) => ( + + {container} + + )} /> + {collisionMessageIds.length > 0 && ( + + )} {endpoint && onLink && ( )} diff --git a/ui/v2.5/src/components/Tagger/scenes/ScrapedPerformerCard.tsx b/ui/v2.5/src/components/Tagger/scenes/ScrapedPerformerCard.tsx new file mode 100644 index 0000000000..f9f057421a --- /dev/null +++ b/ui/v2.5/src/components/Tagger/scenes/ScrapedPerformerCard.tsx @@ -0,0 +1,145 @@ +import React from "react"; +import { useIntl } from "react-intl"; +import * as GQL from "src/core/generated-graphql"; +import TextUtils from "src/utils/text"; +import { stringToGender } from "src/utils/gender"; +import { getStashboxBase } from "src/utils/stashbox"; +import { GridCard } from "src/components/Shared/GridCard/GridCard"; +import { CountryFlag } from "src/components/Shared/CountryFlag"; +import { PatchComponent } from "src/patch"; +import GenderIcon from "src/components/Performers/GenderIcon"; + +export interface IScrapedPerformerCardProps { + scrapedPerformer: GQL.ScrapedPerformer; + endpoint: string; + cardWidth?: number; + ageFromDate?: string | null; + selecting?: boolean; + selected?: boolean; + zoomIndex?: number; + onSelectedChanged?: (selected: boolean, shiftKey: boolean) => void; +} + +const ScrapedPerformerCardOverlays: React.FC = + PatchComponent("ScrapedPerformerCard.Overlays", ({ scrapedPerformer }) => { + function maybeRenderFlag() { + if (!scrapedPerformer.country) { + return; + } + return ( + + ); + } + + return <>{maybeRenderFlag()}; + }); + +const ScrapedPerformerCardDetails: React.FC = + PatchComponent("ScrapedPerformerCard.Details", (props) => { + const { scrapedPerformer, ageFromDate } = props; + const intl = useIntl(); + const age = TextUtils.age( + scrapedPerformer.birthdate, + ageFromDate ?? scrapedPerformer.death_date + ); + const ageL10nId = ageFromDate + ? "media_info.performer_card.age_context" + : "media_info.performer_card.age"; + const ageL10String = intl.formatMessage({ + id: "years_old", + }); + const ageString = intl.formatMessage( + { id: ageL10nId }, + { age, years_old: ageL10String } + ); + + return ( + <> + {age !== 0 ? ( +
{ageString}
+ ) : ( + "" + )} + + ); + }); + +const ScrapedPerformerCardImage: React.FC = + PatchComponent("ScrapedPerformerCard.Image", ({ scrapedPerformer }) => { + const intl = useIntl(); + const unknownName = intl.formatMessage({ + id: "component_tagger.results.unnamed", + }); + const alt = scrapedPerformer.name ?? unknownName; + return ( + {alt} + ); + }); + +const ScrapedPerformerCardTitle: React.FC = + PatchComponent("ScrapedPerformerCard.Title", ({ scrapedPerformer }) => { + const intl = useIntl(); + const unknownPerformerName = intl.formatMessage({ + id: "component_tagger.results.unnamed", + }); + const name = scrapedPerformer.name ?? unknownPerformerName; + return ( +
+ {name} + {scrapedPerformer.disambiguation && ( + + {` (${scrapedPerformer.disambiguation})`} + + )} +
+ ); + }); + +export const ScrapedPerformerCard: React.FC = + PatchComponent("ScrapedPerformerCard", (props) => { + const { + scrapedPerformer, + endpoint, + cardWidth, + selecting, + selected, + onSelectedChanged, + zoomIndex, + } = props; + + const base = getStashboxBase(endpoint); + const stashboxUrl = + base && scrapedPerformer.remote_site_id + ? `${base}performers/${scrapedPerformer.remote_site_id}` + : "#"; + + return ( + + } + title={} + image={} + overlays={} + details={} + selected={selected} + selecting={selecting} + onSelectedChanged={onSelectedChanged} + /> + ); + }); diff --git a/ui/v2.5/src/components/Tagger/scenes/StashSearchResult.tsx b/ui/v2.5/src/components/Tagger/scenes/StashSearchResult.tsx index 9c33face7f..c3911fe190 100755 --- a/ui/v2.5/src/components/Tagger/scenes/StashSearchResult.tsx +++ b/ui/v2.5/src/components/Tagger/scenes/StashSearchResult.tsx @@ -133,14 +133,16 @@ const getFingerprintStatus = ( scene: IScrapedScene, stashScene: GQL.SlimSceneDataFragment ) => { - const checksumMatch = scene.fingerprints?.some((f) => - stashScene.files.some((ff) => - ff.fingerprints.some( - (fp) => - fp.value === f.hash && (fp.type === "oshash" || fp.type === "md5") - ) - ) - ); + const oshashMatches = + scene.fingerprints?.filter( + (f) => + f.algorithm === "OSHASH" && + stashScene.files.some((ff) => + ff.fingerprints.some( + (fp) => fp.type === "oshash" && fp.value === f.hash + ) + ) + ) ?? []; const allPhashes: Pick[] = []; @@ -165,7 +167,22 @@ const getFingerprintStatus = (
); - if (checksumMatch || phashMatches.length > 0) + const oshashList = ( +
+ {oshashMatches.map((fp) => ( +
+ {fp.hash} + {", "} + +
+ ))} +
+ ); + + if (oshashMatches.length > 0 || phashMatches.length > 0) return (
{phashMatches.length > 0 && ( @@ -176,33 +193,35 @@ const getFingerprintStatus = ( content={phashList} className="PHashPopover" > - {phashMatches.length > 1 ? ( - - ) : ( - , - }} - /> - )} + , + }} + />
)} - {checksumMatch && ( + {oshashMatches.length > 0 && (
- - , - }} - /> + + + sum + fp.submissions, + 0 + ), + hash_type: , + }} + /> +
)}
diff --git a/ui/v2.5/src/components/Tagger/scenes/TaggerPerformerPopover.tsx b/ui/v2.5/src/components/Tagger/scenes/TaggerPerformerPopover.tsx new file mode 100644 index 0000000000..43bfaee783 --- /dev/null +++ b/ui/v2.5/src/components/Tagger/scenes/TaggerPerformerPopover.tsx @@ -0,0 +1,173 @@ +import React, { useState } from "react"; +import * as GQL from "src/core/generated-graphql"; +import { Placement } from "react-bootstrap/esm/Overlay"; +import { FormattedMessage } from "react-intl"; +import { faTriangleExclamation } from "@fortawesome/free-solid-svg-icons"; +import { PerformerPopover } from "src/components/Performers/PerformerPopover"; +import { PerformerCard } from "src/components/Performers/PerformerCard"; +import { Icon } from "src/components/Shared/Icon"; +import { PopoverCard } from "src/components/Shared/HoverPopover"; +import { ScrapedPerformerCard } from "./ScrapedPerformerCard"; + +const normalizeValue = (value: unknown) => + (() => { + const text = String(value ?? "").trim(); + if (!text) return ""; + + const isNumericLike = /^[-+]?(?:\d+\.?\d*|\.\d+)$/.test(text); + if (isNumericLike) { + const numeric = Number(text); + if (!Number.isNaN(numeric)) { + return String(numeric); + } + } + + return text.toLowerCase(); + })(); + +const toStringOrNull = (value: unknown) => { + if (value === null || value === undefined) return null; + const text = String(value).trim(); + return text.length > 0 ? text : null; +}; + +const valuesCollide = (remoteValue: unknown, localValue: unknown) => { + const remoteText = toStringOrNull(remoteValue); + if (!remoteText) return false; + + return normalizeValue(remoteText) !== normalizeValue(localValue); +}; + +const getMismatchedStashID = ( + remote: GQL.ScrapedPerformer, + local: GQL.PerformerDataFragment, + endpoint?: string +) => { + if (!endpoint || !remote.remote_site_id) return undefined; + + return local.stash_ids.find( + (stashID) => + stashID.endpoint === endpoint && + stashID.stash_id !== remote.remote_site_id + ); +}; + +export const getPerformerCollisionMessageIds = ( + remote: GQL.ScrapedPerformer, + local: GQL.PerformerDataFragment, + endpoint?: string +): string[] => { + const messageIds: string[] = []; + + if (getMismatchedStashID(remote, local, endpoint)) { + messageIds.push("tagger.performers.stash_mismatch"); + } + if (valuesCollide(remote.birthdate, local.birthdate)) { + messageIds.push("tagger.performers.birthdate"); + } + if (valuesCollide(remote.country, local.country)) { + messageIds.push("tagger.performers.country"); + } + if (valuesCollide(remote.gender, local.gender)) { + messageIds.push("tagger.performers.gender"); + } + if (valuesCollide(remote.ethnicity, local.ethnicity)) { + messageIds.push("tagger.performers.ethnicity"); + } + + return messageIds; +}; + +export const hasPerformerCollision = ( + remote: GQL.ScrapedPerformer, + local: GQL.PerformerDataFragment, + endpoint?: string +) => getPerformerCollisionMessageIds(remote, local, endpoint).length > 0; + +export const PerformerCollisionPopoverContent: React.FC<{ + messageIds: string[]; +}> = ({ messageIds }) => ( + +
+ {messageIds.map((messageId) => ( +
+ + +
+ ))} +
+
+); + +interface ITaggerPerformerPopoverProps { + performer?: GQL.PerformerDataFragment; + performerID?: string; + scrapedPerformer?: GQL.ScrapedPerformer; + endpoint?: string; + placement?: Placement; + triggerClassName?: string; + onOpen?: () => void; + onClose?: () => void; +} + +export const TaggerPerformerPopover: React.FC< + React.PropsWithChildren +> = ({ + performer, + performerID, + scrapedPerformer, + endpoint, + placement = "bottom", + triggerClassName = "d-inline-block", + onOpen, + onClose, + children, +}) => { + const [isOpened, setIsOpened] = useState(false); + + const { data: selectedPerformerData } = GQL.useFindPerformerQuery({ + variables: { id: performerID ?? "" }, + skip: !performerID || !!performer || !isOpened, + }); + const localPerformer = performer ?? selectedPerformerData?.findPerformer; + + const wantsLocalCard = !!(performerID || performer); + + const cardContent = localPerformer ? ( +
+ +
+ ) : wantsLocalCard ? undefined : scrapedPerformer ? ( +
+ +
+ ) : undefined; + + return ( + { + setIsOpened(true); + onOpen?.(); + }} + onClose={() => { + setIsOpened(false); + onClose?.(); + }} + > + {children} + + ); +}; diff --git a/ui/v2.5/src/components/Tagger/styles.scss b/ui/v2.5/src/components/Tagger/styles.scss index 0e9db45a6d..0ca4688c20 100644 --- a/ui/v2.5/src/components/Tagger/styles.scss +++ b/ui/v2.5/src/components/Tagger/styles.scss @@ -88,6 +88,14 @@ } } + // using this model to prevent having to map the popover to the entire control + // which would include the contents of the drop down window + .performer-select-value-hover-trigger { + flex: 1 1 auto; + min-width: 0; + overflow: hidden; + } + .SceneTaggerIcon { margin-left: 0.25em; margin-right: 10px; @@ -95,6 +103,92 @@ } } +.performer-collision-popover { + text-align: left; +} + +.performer-collision-warning-button { + background-color: $warning; + border-color: $warning; + color: $text-color; + + &:hover, + &:focus, + &:active { + background-color: darken($warning, 8%); + border-color: darken($warning, 8%); + color: $text-color; + } +} + +.performer-collision-warnings { + width: 100%; +} + +.performer-collision-warning { + align-items: flex-start; + display: flex; + font-size: 0.875rem; + gap: 0.5rem; + line-height: 1.3; + + &:not(:last-child) { + margin-bottom: 0.35rem; + } +} + +.performer-collision-warning-icon { + flex-shrink: 0; + margin-top: 0.1rem; + width: var(--fa-fw-width, 1.25em); +} + +.tagger-matched-performer-popover-extra { + border-top: 1px solid $secondary; + margin-top: 0.4rem; + padding-bottom: 0.4rem; + padding-left: 0.5rem; + padding-right: 0.5rem; + padding-top: 0.5rem; +} + +.tagger-performer-delta-rows { + font-size: 0.875rem; + line-height: 1.3; + text-align: left; +} + +.tagger-performer-stashid-warning { + text-align: left; + + .tagger-performer-stashid-warning-chip { + background-color: $danger; + border-radius: 0.25rem; + color: $white; + min-width: auto; + } +} + +@media (max-height: 1049px) { + .tagger-performer-popover-card { + .card { + max-width: 160px; + width: 160px; + } + + .card-section-title { + font-size: 1rem; + } + + // Target BEM class from shared PerformerCard markup; cannot + // be renamed here without changing reusable performer card components. + // stylelint-disable-next-line selector-class-pattern + .performer-card__age { + font-size: 0.9rem; + } + } +} + .selected-result { background-color: hsl(204, 20%, 30%); border-radius: 3px; diff --git a/ui/v2.5/src/components/Tags/TagDetails/TagEditPanel.tsx b/ui/v2.5/src/components/Tags/TagDetails/TagEditPanel.tsx index 1f683e2dd5..d51fdd29d3 100644 --- a/ui/v2.5/src/components/Tags/TagDetails/TagEditPanel.tsx +++ b/ui/v2.5/src/components/Tags/TagDetails/TagEditPanel.tsx @@ -144,6 +144,10 @@ export const TagEditPanel: React.FC = ({ try { await onSubmit(input, andNew); formik.resetForm(); + if (andNew) { + setParentTags(tag.parents ?? []); + setChildTags(tag.children ?? []); + } } catch (e) { Toast.error(e); } diff --git a/ui/v2.5/src/components/Tags/TagList.tsx b/ui/v2.5/src/components/Tags/TagList.tsx index 2627504248..ac63cd4cf2 100644 --- a/ui/v2.5/src/components/Tags/TagList.tsx +++ b/ui/v2.5/src/components/Tags/TagList.tsx @@ -440,6 +440,7 @@ export const FilteredTagList = PatchComponent( /> showEditFilter(c.criterionOption.type)} onRemoveCriterion={removeCriterion} diff --git a/ui/v2.5/src/components/Tags/TagMergeDialog.tsx b/ui/v2.5/src/components/Tags/TagMergeDialog.tsx index 41e6a04efd..6e3e3786f0 100644 --- a/ui/v2.5/src/components/Tags/TagMergeDialog.tsx +++ b/ui/v2.5/src/components/Tags/TagMergeDialog.tsx @@ -30,6 +30,7 @@ import { import { ScrapedTagsRow } from "../Shared/ScrapeDialog/ScrapedObjectsRow"; import { ScrapeDialog } from "../Shared/ScrapeDialog/ScrapeDialog"; import { StashIDsField } from "../Shared/StashID"; +import { PatchComponent } from "src/patch"; interface ITagMergeDetailsProps { sources: GQL.TagDataFragment[]; @@ -406,187 +407,188 @@ interface ITagMergeModalProps { tags: Tag[]; } -export const TagMergeModal: React.FC = ({ - show, - onClose, - tags, -}) => { - const [src, setSrc] = useState([]); - const [dest, setDest] = useState(null); +export const TagMergeModal: React.FC = PatchComponent( + "TagMergeModal", + ({ show, onClose, tags }) => { + const [src, setSrc] = useState([]); + const [dest, setDest] = useState(null); - const [loadedSources, setLoadedSources] = useState([]); - const [loadedDest, setLoadedDest] = useState(); + const [loadedSources, setLoadedSources] = useState( + [] + ); + const [loadedDest, setLoadedDest] = useState(); - const [secondStep, setSecondStep] = useState(false); + const [secondStep, setSecondStep] = useState(false); - const [running, setRunning] = useState(false); + const [running, setRunning] = useState(false); - const [mergeTags] = useTagsMerge(); + const [mergeTags] = useTagsMerge(); - const intl = useIntl(); - const Toast = useToast(); + const intl = useIntl(); + const Toast = useToast(); - const title = intl.formatMessage({ - id: "actions.merge", - }); + const title = intl.formatMessage({ + id: "actions.merge", + }); - const srcIDs = useMemo(() => src.map((s) => s.id), [src]); - const destID = useMemo(() => (dest ? [dest.id] : []), [dest]); + const srcIDs = useMemo(() => src.map((s) => s.id), [src]); + const destID = useMemo(() => (dest ? [dest.id] : []), [dest]); - useEffect(() => { - if (tags.length > 0) { - setDest(tags[0]); - setSrc(tags.slice(1)); - } - }, [tags]); - - async function loadTags() { - try { - const tagIDs = src.map((s) => s.id); - tagIDs.push(dest!.id); - const query = await queryFindTagsByID(tagIDs); - const { tags: loadedTags } = query.data.findTags; - - setLoadedDest(loadedTags.find((s) => s.id === dest!.id)); - setLoadedSources(loadedTags.filter((s) => s.id !== dest!.id)); - setSecondStep(true); - } catch (e) { - Toast.error(e); - return; + useEffect(() => { + if (tags.length > 0) { + setDest(tags[0]); + setSrc(tags.slice(1)); + } + }, [tags]); + + async function loadTags() { + try { + const tagIDs = src.map((s) => s.id); + tagIDs.push(dest!.id); + const query = await queryFindTagsByID(tagIDs); + const { tags: loadedTags } = query.data.findTags; + + setLoadedDest(loadedTags.find((s) => s.id === dest!.id)); + setLoadedSources(loadedTags.filter((s) => s.id !== dest!.id)); + setSecondStep(true); + } catch (e) { + Toast.error(e); + return; + } } - } - async function onMerge(values: GQL.TagUpdateInput) { - if (!dest) return; - - const source = src.map((s) => s.id); - const destination = dest.id; - - try { - setRunning(true); - const result = await mergeTags({ - variables: { - source, - destination, - values, - }, - }); - if (result.data?.tagsMerge) { - Toast.success(intl.formatMessage({ id: "toast.merged_tags" })); - onClose(dest.id); + async function onMerge(values: GQL.TagUpdateInput) { + if (!dest) return; + + const source = src.map((s) => s.id); + const destination = dest.id; + + try { + setRunning(true); + const result = await mergeTags({ + variables: { + source, + destination, + values, + }, + }); + if (result.data?.tagsMerge) { + Toast.success(intl.formatMessage({ id: "toast.merged_tags" })); + onClose(dest.id); + } + } catch (e) { + Toast.error(e); + } finally { + setRunning(false); } - } catch (e) { - Toast.error(e); - } finally { - setRunning(false); } - } - function canMerge() { - return src.length > 0 && dest !== null; - } + function canMerge() { + return src.length > 0 && dest !== null; + } - function switchTags() { - if (src.length && dest !== null) { - const newDest = src[0]; - setSrc([...src.slice(1), dest]); - setDest(newDest); + function switchTags() { + if (src.length && dest !== null) { + const newDest = src[0]; + setSrc([...src.slice(1), dest]); + setDest(newDest); + } + } + + if (secondStep && dest) { + return ( + { + setSecondStep(false); + if (values) { + onMerge(values); + } else { + onClose(); + } + }} + /> + ); } - } - if (secondStep && dest) { return ( - { - setSecondStep(false); - if (values) { - onMerge(values); - } else { - onClose(); - } + loadTags(), }} - /> - ); - } - - return ( - loadTags(), - }} - disabled={!canMerge()} - cancel={{ - variant: "secondary", - onClick: () => onClose(), - }} - isRunning={running} - > -
-
- - {FormUtils.renderLabel({ - title: intl.formatMessage({ id: "dialogs.merge.source" }), - labelProps: { - column: true, - sm: 3, - xl: 12, - }, - })} - - setSrc(items)} - values={src} - excludeIds={destID} - menuPortalTarget={document.body} - /> - - - - - - - {FormUtils.renderLabel({ - title: intl.formatMessage({ - id: "dialogs.merge.destination", - }), - labelProps: { - column: true, - sm: 3, - xl: 12, - }, - })} - - setDest(items[0])} - values={dest ? [dest] : undefined} - excludeIds={srcIDs} - menuPortalTarget={document.body} - /> - - + + + + {FormUtils.renderLabel({ + title: intl.formatMessage({ + id: "dialogs.merge.destination", + }), + labelProps: { + column: true, + sm: 3, + xl: 12, + }, + })} + + setDest(items[0])} + values={dest ? [dest] : undefined} + excludeIds={srcIDs} + menuPortalTarget={document.body} + /> + + +
- -
- ); -}; +
+ ); + } +); diff --git a/ui/v2.5/src/docs/en/Manual/Browsing.md b/ui/v2.5/src/docs/en/Manual/Browsing.md index 525cca2222..040c25d660 100644 --- a/ui/v2.5/src/docs/en/Manual/Browsing.md +++ b/ui/v2.5/src/docs/en/Manual/Browsing.md @@ -10,7 +10,7 @@ The text field allows you to search using keywords. Keyword searching matches on |------|-----------------| | Scene | Title, Details, Path, OSHash, Checksum, Marker titles | | Image | Title, Details, Path, Checksum | -| Group | Title | +| Group | Name, Aliases | | Marker | Title, Scene title | | Gallery | Title, Path, Checksum | | Performer | Name, Aliases | diff --git a/ui/v2.5/src/docs/en/Manual/Images.md b/ui/v2.5/src/docs/en/Manual/Images.md index a280b9c32a..e1ebe7076e 100644 --- a/ui/v2.5/src/docs/en/Manual/Images.md +++ b/ui/v2.5/src/docs/en/Manual/Images.md @@ -30,3 +30,9 @@ A clip/gif will be a stillframe in the wall and grid view by default. To view th If you want the loop to be used as a preview on the wall and grid view, you will have to generate them. You can do this as you scan for the new clip file by activating **Generate previews for image clips** on the scan settings, or do it after by going to the **Generated Content** section in the task section of your settings, activating **Image clip previews** and clicking generate. This takes a while, as the files are transcoded. + +## Sorting by performer age + +Images can be sorted by **Performer Age**. This sorts by how old the image's performer(s) were on the image's own date: ascending order uses the youngest performer, descending order uses the oldest. + +Because the age is calculated from the image's date, this sort only works for images that have a date set (and performers with a birthdate). Images with no date, or with no performers, have no age to compare and are always sorted last regardless of direction. If your images appear unsorted when using this option, check that they have a date set. diff --git a/ui/v2.5/src/docs/en/Manual/UIPluginApi.md b/ui/v2.5/src/docs/en/Manual/UIPluginApi.md index 92b1f3f313..a711fde63f 100644 --- a/ui/v2.5/src/docs/en/Manual/UIPluginApi.md +++ b/ui/v2.5/src/docs/en/Manual/UIPluginApi.md @@ -239,6 +239,7 @@ Returns `void`. - `FilteredSceneMarkerList` - `FilteredStudioList` - `FilteredTagList` +- `FilterTags.Extras` - `FolderSelect` - `FrontPage` - `GalleryCard` @@ -298,6 +299,7 @@ Returns `void`. - `PerformerIDSelect` - `PerformerImagesPanel` - `PerformerList` +- `PerformerMergeModal` - `PerformerPage` - `PerformerRecommendationRow` - `PerformerScenesPanel` @@ -309,6 +311,7 @@ Returns `void`. - `RatingStars` - `RatingSystem` - `RecommendationRow` +- `SavedFilter.Loaded` - `SceneCard` - `SceneCard.Details` - `SceneCard.Image` @@ -325,6 +328,7 @@ Returns `void`. - `SceneMarkerCardsGrid` - `SceneMarkerList` - `SceneMarkerRecommendationRow` +- `SceneMergeModal` - `SceneList` - `ScenePage` - `ScenePage.TabContent` @@ -360,6 +364,7 @@ Returns `void`. - `TagIDSelect` - `TagLink` - `TagList` +- `TagMergeModal` - `TagPage` - `TagRecommendationRow` - `TagSelect` @@ -372,4 +377,4 @@ Allows plugins to listen for Stash's events. ```js PluginApi.Event.addEventListener("stash:location", (e) => console.log("Page Changed", e.detail.data.location.pathname)) -``` \ No newline at end of file +``` diff --git a/ui/v2.5/src/hooks/Lightbox/Lightbox.tsx b/ui/v2.5/src/hooks/Lightbox/Lightbox.tsx index 858f3cb8b6..d2f79c28ea 100644 --- a/ui/v2.5/src/hooks/Lightbox/Lightbox.tsx +++ b/ui/v2.5/src/hooks/Lightbox/Lightbox.tsx @@ -53,6 +53,7 @@ import { useDebounce } from "../debounce"; import { isVideo } from "src/utils/visualFile"; import { imageTitle } from "src/core/files"; import { galleryTitle } from "src/core/galleries"; +import type { LightboxHideReason } from "./context"; const CLASSNAME = "Lightbox"; const CLASSNAME_HEADER = `${CLASSNAME}-header`; @@ -98,7 +99,7 @@ interface IProps { totalCount?: number; pageCallback?: (props: { direction?: number; page?: number }) => void; chapters?: IChapter[]; - hide: () => void; + hide: (reason?: LightboxHideReason) => void; onDeleteImage?: (id: string) => void; } @@ -126,15 +127,65 @@ export const LightboxComponent: React.FC = ({ const oldIndex = useRef(null); const [instantTransition, setInstantTransition] = useState(false); const [isSwitchingPage, setIsSwitchingPage] = useState(true); + // Synchronous mirror of isSwitchingPage. The nav handlers (handleLeft/ + // handleRight, reached by the arrow keys, the on-screen chevrons and the + // image-edge clicks) fire on raw keydown/click events that can arrive faster + // than React re-renders, so the isSwitchingPage *state* they close over is + // stale. The ref reflects an in-flight page switch immediately — set true + // before pageCallback — so rapid inputs are dropped and, crucially, the + // index-range effect won't clamp a stale index while a (possibly + // synchronously-cached) page is still swapping in. The landing index stays + // under the handlers' control; this ref only gates, it never picks the index. + const isSwitchingPageRef = useRef(true); const [isFullscreen, setFullscreen] = useState(false); const [showOptions, setShowOptions] = useState(false); const [showChapters, setShowChapters] = useState(false); const [imagesLoaded, setImagesLoaded] = useState(0); - const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false); + // The image the delete dialog acts on, captured when the dialog is opened. + // Reading images[currentIndex] at confirm time instead would delete whatever + // image the index has since moved to (e.g. a page-switch settle landing + // while the dialog is open). + const [deleteTarget, setDeleteTarget] = useState(null); const lastDKeyTime = useRef(0); const [navOffset, setNavOffset] = useState(); - const oldImages = useRef([]); + // An in-flight page switch's intended landing, set synchronously by + // startPageSwitch *before* pageCallback. The landing is an explicit target — + // "last" or a concrete index (0 for a forward cross) — NOT derived from the + // page-number direction (a wrap moves the page number opposite to nav + // direction, so deriving from direction breaks first/last wraparound). It is + // resolved by the settle effect once the new page has loaded. + const pendingTarget = useRef<"last" | number | null>(null); + // The page number and images array at switch start, used by the settle + // effect to detect the new page's arrival. + const switchFromPage = useRef(page); + const switchFromImages = useRef(images); + + // The ref and the state must never disagree, so they are only written + // through this setter. + const setSwitching = useCallback((v: boolean) => { + isSwitchingPageRef.current = v; + setIsSwitchingPage(v); + }, []); + + // Begin a page switch: raise the guard and record the landing intent + // *before* pageCallback. An already-cached page can swap its images in + // synchronously, so the guard must be up first or a rapid input (and the + // index-range effect) races the swap. The index is parked at 0 — valid on + // any non-empty page — while the new page loads, so the index-range clamp + // can't fire on a shorter incoming page; the settle effect moves it to the + // target once the page arrives. + const startPageSwitch = useCallback( + (target: "last" | number, cbArg: { direction?: number; page?: number }) => { + setSwitching(true); + switchFromPage.current = page; + switchFromImages.current = images; + pendingTarget.current = target; + setIndex(0); + pageCallback?.(cbArg); + }, + [images, page, pageCallback, setSwitching] + ); const [zoom, setZoom] = useState(1); @@ -256,11 +307,41 @@ export const LightboxComponent: React.FC = ({ useState((slideshowDelay / SECONDS_TO_MS).toString()); useEffect(() => { - if (images !== oldImages.current && isSwitchingPage) { - if (index === -1) setIndex(images.length - 1); - setIsSwitchingPage(false); + const target = pendingTarget.current; + if (target !== null) { + // A page switch is in flight. It completes when the page number AND the + // images array have both changed: the parent delivers them together, and + // requiring both means a parent that updates the page a commit ahead of + // the images can't settle the switch against the outgoing page's array. + // Callers that provide pageCallback without a page prop can never change + // the page number, so for them fall back to the images identity alone. + const pageChanged = page !== switchFromPage.current; + const imagesChanged = images !== switchFromImages.current; + const settled = + page === undefined ? imagesChanged : pageChanged && imagesChanged; + if (settled) { + // Resolve the landing, clamped to the arrived page: a chapter's + // numeric target can be stale (images deleted since the chapters + // loaded), and "last" is -1 on an empty page (which the close effect + // then handles). + const resolved = + target === "last" + ? images.length - 1 + : Math.min(target, images.length - 1); + setIndex(Math.max(0, resolved)); + pendingTarget.current = null; + switchFromPage.current = page; + switchFromImages.current = images; + setSwitching(false); + } + return; + } + // No switch pending: clear the switching flag once the first page's images + // have loaded (the flag starts true on open so the loader shows until then). + if (isSwitchingPage && !isLoading && images.length > 0) { + setSwitching(false); } - }, [isSwitchingPage, images, index]); + }, [isSwitchingPage, isLoading, images, page, setSwitching]); const disableInstantTransition = useDebounce( () => setInstantTransition(false), @@ -333,13 +414,27 @@ export const LightboxComponent: React.FC = ({ }; useEffect(() => { - if (isVisible) { - if (index === null) setIndex(initialIndex); - document.body.style.overflow = "hidden"; - Mousetrap.pause(); - } + if (isVisible && index === null) setIndex(initialIndex); }, [initialIndex, isVisible, index]); + useEffect(() => { + if (!isVisible) return; + + document.body.style.overflow = "hidden"; + Mousetrap.pause(); + return () => { + const fullscreenElement = document.fullscreenElement; + if ( + fullscreenElement && + containerRef.current?.contains(fullscreenElement) + ) { + document.exitFullscreen(); + } + document.body.style.overflow = "auto"; + Mousetrap.unpause(); + }; + }, [isVisible]); + const toggleSlideshow = useCallback(() => { if (slideshowInterval) { setSlideshowInterval(null); @@ -355,13 +450,12 @@ export const LightboxComponent: React.FC = ({ } }); - const close = useCallback(() => { - if (isFullscreen) document.exitFullscreen(); - - hide(); - document.body.style.overflow = "auto"; - Mousetrap.unpause(); - }, [isFullscreen, hide]); + const close = useCallback( + (reason: LightboxHideReason = "dismiss") => { + hide(reason); + }, + [hide] + ); const handleClose = (e: React.MouseEvent) => { const { className } = e.target as Element; @@ -370,7 +464,7 @@ export const LightboxComponent: React.FC = ({ const handleLeft = useCallback( (isUserAction = true) => { - if (isSwitchingPage || index === -1) return; + if (isSwitchingPageRef.current) return; if (disableAnimation) { setInstant(); @@ -380,12 +474,10 @@ export const LightboxComponent: React.FC = ({ setMovingLeft(true); if (index === 0) { - // go to next page, or loop back if no callback is set + // go to previous page (landing on its last image), or loop back if no + // callback is set if (pageCallback) { - pageCallback({ direction: -1 }); - setIndex(-1); - oldImages.current = images; - setIsSwitchingPage(true); + startPageSwitch("last", { direction: -1 }); } else setIndex(images.length - 1); } else setIndex((index ?? 0) - 1); @@ -393,12 +485,12 @@ export const LightboxComponent: React.FC = ({ resetIntervalCallback.current(); } }, - [images, pageCallback, isSwitchingPage, index, disableAnimation, setInstant] + [images, pageCallback, index, disableAnimation, setInstant, startPageSwitch] ); const handleRight = useCallback( (isUserAction = true) => { - if (isSwitchingPage) return; + if (isSwitchingPageRef.current) return; if (disableAnimation) { setInstant(); @@ -408,12 +500,10 @@ export const LightboxComponent: React.FC = ({ setShowChapters(false); if (index === images.length - 1) { - // go to preview page, or loop back if no callback is set + // go to next page (landing on its first image), or loop back if no + // callback is set if (pageCallback) { - pageCallback({ direction: 1 }); - oldImages.current = images; - setIsSwitchingPage(true); - setIndex(0); + startPageSwitch(0, { direction: 1 }); } else setIndex(0); } else setIndex((index ?? 0) + 1); @@ -421,7 +511,7 @@ export const LightboxComponent: React.FC = ({ resetIntervalCallback.current(); } }, - [images, pageCallback, isSwitchingPage, index, disableAnimation, setInstant] + [images, pageCallback, index, disableAnimation, setInstant, startPageSwitch] ); const firstScroll = useRef(null); @@ -432,6 +522,13 @@ export const LightboxComponent: React.FC = ({ inScrollGroup.current = false; }, SCROLL_ZOOM_TIMEOUT); + useEffect(() => { + return () => { + disableInstantTransition.cancel(); + debouncedScrollReset.cancel(); + }; + }, [disableInstantTransition, debouncedScrollReset]); + const handleKey = useCallback( (e: KeyboardEvent) => { if (e.repeat && (e.key === "ArrowRight" || e.key === "ArrowLeft")) @@ -439,15 +536,17 @@ export const LightboxComponent: React.FC = ({ if (e.key === "ArrowLeft") handleLeft(); else if (e.key === "ArrowRight") handleRight(); else if (e.key === "Escape") close(); - else if ( - e.key === "d" && - images[index ?? initialIndex]?.id !== undefined - ) { - const now = Date.now(); - if (now - lastDKeyTime.current < 1000) { - setIsDeleteDialogOpen(true); + else if (e.key === "d") { + // Not while a page switch is in flight: the index is parked at 0 then, + // so the shortcut would target an image the user isn't viewing. + const image = images[index ?? initialIndex]; + if (!isSwitchingPageRef.current && image?.id !== undefined) { + const now = Date.now(); + if (now - lastDKeyTime.current < 1000) { + setDeleteTarget(image); + } + lastDKeyTime.current = now; } - lastDKeyTime.current = now; } }, [setInstant, handleLeft, handleRight, close, images, index, initialIndex] @@ -547,19 +646,31 @@ export const LightboxComponent: React.FC = ({ if (isLoading) return; if (images.length === 0) { close(); - } else if (index !== null && index >= images.length) { + } else if ( + !isSwitchingPageRef.current && + index !== null && + index >= images.length + ) { + // Same-page shrink only — e.g. the last image was deleted and findImages + // refetched a shorter list. While a page switch is in flight the index is + // transiently stale against a just-swapped, shorter page; clamping it + // here is what made a forward cross land on the page's *last* image. The + // settle effect reconciles the switch instead, so skip the clamp then. setIndex(images.length - 1); } }, [images.length, index, close, isLoading]); function gotoPage(imageIndex: number) { + // indexInPage is the chapter's explicit target; the settle effect lands on + // it once the new page loads, so a chapter jump lands on the right image of + // the new page rather than its first/last. const indexInPage = (imageIndex - 1) % pageSize; if (pageCallback) { const jumppage = Math.floor((imageIndex - 1) / pageSize) + 1; if (page !== jumppage) { - pageCallback({ page: jumppage }); - oldImages.current = images; - setIsSwitchingPage(true); + startPageSwitch(indexInPage, { page: jumppage }); + setShowChapters(false); + return; } } @@ -993,14 +1104,15 @@ export const LightboxComponent: React.FC = ({ close()} + replace + onClick={() => close("navigate")} > {title ?? ""} {currentImage.id !== undefined && (