mirror of
https://github.com/stashapp/stash.git
synced 2025-12-17 04:14:39 +03:00
Enable gocritic (#1848)
* Don't capitalize local variables
ValidCodecs -> validCodecs
* Capitalize deprecation markers
A deprecated marker should be capitalized.
* Use re.MustCompile for static regexes
If the regex fails to compile, it's a programmer error, and should be
treated as such. The regex is entirely static.
* Simplify else-if constructions
Rewrite
else { if cond {}}
to
else if cond {}
* Use a switch statement to analyze formats
Break an if-else chain. While here, simplify code flow.
Also introduce a proper static error for unsupported image formats,
paving the way for being able to check against the error.
* Rewrite ifElse chains into switch statements
The "Effective Go" https://golang.org/doc/effective_go#switch document
mentions it is more idiomatic to write if-else chains as switches when
it is possible.
Find all the plain rewrite occurrences in the code base and rewrite.
In some cases, the if-else chains are replaced by a switch scrutinizer.
That is, the code sequence
if x == 1 {
..
} else if x == 2 {
..
} else if x == 3 {
...
}
can be rewritten into
switch x {
case 1:
..
case 2:
..
case 3:
..
}
which is clearer for the compiler: it can decide if the switch is
better served by a jump-table then a branch-chain.
* Rewrite switches, introduce static errors
Introduce two new static errors:
* `ErrNotImplmented`
* `ErrNotSupported`
And use these rather than forming new generative errors whenever the
code is called. Code can now test on the errors (since they are static
and the pointers to them wont change).
Also rewrite ifElse chains into switches in this part of the code base.
* Introduce a StashBoxError in configuration
Since all stashbox errors are the same, treat them as such in the code
base. While here, rewrite an ifElse chain.
In the future, it might be beneifical to refactor configuration errors
into one error which can handle missing fields, which context the error
occurs in and so on. But for now, try to get an overview of the error
categories by hoisting them into static errors.
* Get rid of an else-block in transaction handling
If we succesfully `recover()`, we then always `panic()`. This means the
rest of the code is not reachable, so we can avoid having an else-block
here.
It also solves an ifElse-chain style check in the code base.
* Use strings.ReplaceAll
Rewrite
strings.Replace(s, o, n, -1)
into
strings.ReplaceAll(s, o, n)
To make it consistent and clear that we are doing an all-replace in the
string rather than replacing parts of it. It's more of a nitpick since
there are no implementation differences: the stdlib implementation is
just to supply -1.
* Rewrite via gocritic's assignOp
Statements of the form
x = x + e
is rewritten into
x += e
where applicable.
* Formatting
* Review comments handled
Stash-box is a proper noun.
Rewrite a switch into an if-chain which returns on the first error
encountered.
* Use context.TODO() over context.Background()
Patch in the same vein as everything else: use the TODO() marker so we
can search for it later and link it into the context tree/tentacle once
it reaches down to this level in the code base.
* Tell the linter to ignore a section in manager_tasks.go
The section is less readable, so mark it with a nolint for now. Because
the rewrite enables a ifElseChain, also mark that as nolint for now.
* Use strings.ReplaceAll over strings.Replace
* Apply an ifElse rewrite
else { if .. { .. } } rewrite into else if { .. }
* Use switch-statements over ifElseChains
Rewrite chains of if-else into switch statements. Where applicable,
add an early nil-guard to simplify case analysis. Also, in
ScanTask's Start(..), invert the logic to outdent the whole block, and
help the reader: if it's not a scene, the function flow is now far more
local to the top of the function, and it's clear that the rest of the
function has to do with scene management.
* Enable gocritic on the code base.
Disable appendAssign for now since we aren't passing that check yet.
* Document the nolint additions
* Document StashBoxBatchPerformerTagInput
This commit is contained in:
@@ -17,27 +17,31 @@ linters:
|
||||
- typecheck
|
||||
- unused
|
||||
- varcheck
|
||||
# Linters added by the stash project
|
||||
# - bodyclose
|
||||
# Linters added by the stash project.
|
||||
- dogsled
|
||||
- errorlint
|
||||
# - exhaustive
|
||||
- exportloopref
|
||||
# - gocritic
|
||||
- gocritic
|
||||
# - goerr113
|
||||
- gofmt
|
||||
# - gomnd
|
||||
# - gosec
|
||||
# - ifshort
|
||||
- misspell
|
||||
# - nakedret
|
||||
- noctx
|
||||
# - paralleltest
|
||||
- revive
|
||||
- rowserrcheck
|
||||
- sqlclosecheck
|
||||
|
||||
# Project-specific linter overrides
|
||||
linters-settings:
|
||||
gocritic:
|
||||
disabled-checks:
|
||||
# Way too many errors to fix regarding comment formatting for now
|
||||
- commentFormatting
|
||||
- appendAssign
|
||||
|
||||
gofmt:
|
||||
simplify: false
|
||||
|
||||
|
||||
@@ -175,10 +175,16 @@ type StashBoxFingerprint {
|
||||
duration: Int!
|
||||
}
|
||||
|
||||
"""If neither performer_ids nor performer_names are set, tag all performers"""
|
||||
input StashBoxBatchPerformerTagInput {
|
||||
"Stash endpoint to use for the performer tagging"
|
||||
endpoint: Int!
|
||||
"Fields to exclude when executing the performer tagging"
|
||||
exclude_fields: [String!]
|
||||
"Refresh performers already tagged by StashBox if true. Only tag performers with no StashBox tagging if false"
|
||||
refresh: Boolean!
|
||||
"If set, only tag these performer ids"
|
||||
performer_ids: [ID!]
|
||||
"If set, only tag these performer names"
|
||||
performer_names: [String!]
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"sort"
|
||||
"strconv"
|
||||
|
||||
@@ -10,6 +11,11 @@ import (
|
||||
"github.com/stashapp/stash/pkg/plugin"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrNotImplemented = errors.New("not implemented")
|
||||
ErrNotSupported = errors.New("not supported")
|
||||
)
|
||||
|
||||
type hookExecutor interface {
|
||||
ExecutePostHooks(ctx context.Context, id int, hookType plugin.HookTriggerEnum, input interface{}, inputFields []string)
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ func (r *studioResolver) ImagePath(ctx context.Context, obj *models.Studio) (*st
|
||||
|
||||
// indicate that image is missing by setting default query param to true
|
||||
if !hasImage {
|
||||
imagePath = imagePath + "?default=true"
|
||||
imagePath += "?default=true"
|
||||
}
|
||||
|
||||
return &imagePath, nil
|
||||
|
||||
@@ -164,18 +164,19 @@ func (r *queryResolver) ScrapeSingleScene(ctx context.Context, source models.Scr
|
||||
var singleScene *models.ScrapedScene
|
||||
var err error
|
||||
|
||||
if input.SceneID != nil {
|
||||
switch {
|
||||
case input.SceneID != nil:
|
||||
var sceneID int
|
||||
sceneID, err = strconv.Atoi(*input.SceneID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
singleScene, err = manager.GetInstance().ScraperCache.ScrapeScene(*source.ScraperID, sceneID)
|
||||
} else if input.SceneInput != nil {
|
||||
case input.SceneInput != nil:
|
||||
singleScene, err = manager.GetInstance().ScraperCache.ScrapeSceneFragment(*source.ScraperID, *input.SceneInput)
|
||||
} else if input.Query != nil {
|
||||
case input.Query != nil:
|
||||
return manager.GetInstance().ScraperCache.ScrapeSceneQuery(*source.ScraperID, *input.Query)
|
||||
} else {
|
||||
default:
|
||||
err = errors.New("scene_id, scene_input or query must be set")
|
||||
}
|
||||
|
||||
@@ -208,7 +209,7 @@ func (r *queryResolver) ScrapeSingleScene(ctx context.Context, source models.Scr
|
||||
|
||||
func (r *queryResolver) ScrapeMultiScenes(ctx context.Context, source models.ScraperSourceInput, input models.ScrapeMultiScenesInput) ([][]*models.ScrapedScene, error) {
|
||||
if source.ScraperID != nil {
|
||||
return nil, errors.New("not implemented")
|
||||
return nil, ErrNotImplemented
|
||||
} else if source.StashBoxIndex != nil {
|
||||
client, err := r.getStashBoxClient(*source.StashBoxIndex)
|
||||
if err != nil {
|
||||
@@ -240,7 +241,7 @@ func (r *queryResolver) ScrapeSinglePerformer(ctx context.Context, source models
|
||||
return manager.GetInstance().ScraperCache.ScrapePerformerList(*source.ScraperID, *input.Query)
|
||||
}
|
||||
|
||||
return nil, errors.New("not implemented")
|
||||
return nil, ErrNotImplemented
|
||||
} else if source.StashBoxIndex != nil {
|
||||
client, err := r.getStashBoxClient(*source.StashBoxIndex)
|
||||
if err != nil {
|
||||
@@ -248,12 +249,13 @@ func (r *queryResolver) ScrapeSinglePerformer(ctx context.Context, source models
|
||||
}
|
||||
|
||||
var ret []*models.StashBoxPerformerQueryResult
|
||||
if input.PerformerID != nil {
|
||||
switch {
|
||||
case input.PerformerID != nil:
|
||||
ret, err = client.FindStashBoxPerformersByNames([]string{*input.PerformerID})
|
||||
} else if input.Query != nil {
|
||||
case input.Query != nil:
|
||||
ret, err = client.QueryStashBoxPerformer(*input.Query)
|
||||
} else {
|
||||
return nil, errors.New("not implemented")
|
||||
default:
|
||||
return nil, ErrNotImplemented
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
@@ -272,7 +274,7 @@ func (r *queryResolver) ScrapeSinglePerformer(ctx context.Context, source models
|
||||
|
||||
func (r *queryResolver) ScrapeMultiPerformers(ctx context.Context, source models.ScraperSourceInput, input models.ScrapeMultiPerformersInput) ([][]*models.ScrapedPerformer, error) {
|
||||
if source.ScraperID != nil {
|
||||
return nil, errors.New("not implemented")
|
||||
return nil, ErrNotImplemented
|
||||
} else if source.StashBoxIndex != nil {
|
||||
client, err := r.getStashBoxClient(*source.StashBoxIndex)
|
||||
if err != nil {
|
||||
@@ -290,17 +292,18 @@ func (r *queryResolver) ScrapeSingleGallery(ctx context.Context, source models.S
|
||||
var singleGallery *models.ScrapedGallery
|
||||
var err error
|
||||
|
||||
if input.GalleryID != nil {
|
||||
switch {
|
||||
case input.GalleryID != nil:
|
||||
var galleryID int
|
||||
galleryID, err = strconv.Atoi(*input.GalleryID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
singleGallery, err = manager.GetInstance().ScraperCache.ScrapeGallery(*source.ScraperID, galleryID)
|
||||
} else if input.GalleryInput != nil {
|
||||
case input.GalleryInput != nil:
|
||||
singleGallery, err = manager.GetInstance().ScraperCache.ScrapeGalleryFragment(*source.ScraperID, *input.GalleryInput)
|
||||
} else {
|
||||
return nil, errors.New("not implemented")
|
||||
default:
|
||||
return nil, ErrNotImplemented
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
@@ -313,12 +316,12 @@ func (r *queryResolver) ScrapeSingleGallery(ctx context.Context, source models.S
|
||||
|
||||
return nil, nil
|
||||
} else if source.StashBoxIndex != nil {
|
||||
return nil, errors.New("not supported")
|
||||
return nil, ErrNotSupported
|
||||
}
|
||||
|
||||
return nil, errors.New("scraper_id must be set")
|
||||
}
|
||||
|
||||
func (r *queryResolver) ScrapeSingleMovie(ctx context.Context, source models.ScraperSourceInput, input models.ScrapeSingleMovieInput) ([]*models.ScrapedMovie, error) {
|
||||
return nil, errors.New("not supported")
|
||||
return nil, ErrNotSupported
|
||||
}
|
||||
|
||||
@@ -8,21 +8,21 @@ import (
|
||||
)
|
||||
|
||||
func getLogLevel(logType string) models.LogLevel {
|
||||
if logType == "progress" {
|
||||
switch logType {
|
||||
case "progress":
|
||||
return models.LogLevelProgress
|
||||
} else if logType == "debug" {
|
||||
case "debug":
|
||||
return models.LogLevelDebug
|
||||
} else if logType == "info" {
|
||||
case "info":
|
||||
return models.LogLevelInfo
|
||||
} else if logType == "warn" {
|
||||
case "warn":
|
||||
return models.LogLevelWarning
|
||||
} else if logType == "error" {
|
||||
case "error":
|
||||
return models.LogLevelError
|
||||
}
|
||||
|
||||
// default to debug
|
||||
default:
|
||||
return models.LogLevelDebug
|
||||
}
|
||||
}
|
||||
|
||||
func logEntriesFromLogItems(logItems []logger.LogItem) []*models.LogEntry {
|
||||
ret := make([]*models.LogEntry, len(logItems))
|
||||
|
||||
@@ -79,7 +79,7 @@ func generateTestPaths(testName, ext string) (scenePatterns []string, falseScene
|
||||
// add test cases for intra-name separators
|
||||
for _, separator := range testSeparators {
|
||||
if separator != " " {
|
||||
scenePatterns = append(scenePatterns, generateNamePatterns(strings.Replace(testName, " ", separator, -1), separator, ext)...)
|
||||
scenePatterns = append(scenePatterns, generateNamePatterns(strings.ReplaceAll(testName, " ", separator), separator, ext)...)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,9 @@ func WithTxn(fn func(tx *sqlx.Tx) error) error {
|
||||
logger.Warnf("failure when performing transaction rollback: %v", err)
|
||||
}
|
||||
panic(p)
|
||||
} else if err != nil {
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
// something went wrong, rollback
|
||||
if err := tx.Rollback(); err != nil {
|
||||
logger.Warnf("failure when performing transaction rollback: %v", err)
|
||||
|
||||
@@ -515,7 +515,8 @@ func (me *Server) contentDirectoryEventSubHandler(w http.ResponseWriter, r *http
|
||||
// the spec on eventing but hasn't been completed as I have nothing to
|
||||
// test it with.
|
||||
service := me.services["ContentDirectory"]
|
||||
if r.Method == "SUBSCRIBE" && r.Header.Get("SID") == "" {
|
||||
switch {
|
||||
case r.Method == "SUBSCRIBE" && r.Header.Get("SID") == "":
|
||||
urls := upnp.ParseCallbackURLs(r.Header.Get("CALLBACK"))
|
||||
var timeout int
|
||||
fmt.Sscanf(r.Header.Get("TIMEOUT"), "Second-%d", &timeout)
|
||||
@@ -528,9 +529,9 @@ func (me *Server) contentDirectoryEventSubHandler(w http.ResponseWriter, r *http
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
me.contentDirectoryInitialEvent(r.Context(), urls, sid)
|
||||
}()
|
||||
} else if r.Method == "SUBSCRIBE" {
|
||||
case r.Method == "SUBSCRIBE":
|
||||
http.Error(w, "meh", http.StatusPreconditionFailed)
|
||||
} else {
|
||||
default:
|
||||
logger.Debugf("unhandled event method: %s", r.Method)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ func (p *scenePager) getPages(r models.ReaderRepository, total int) ([]interface
|
||||
sceneTitle = sceneTitle[0:3]
|
||||
}
|
||||
|
||||
title = title + fmt.Sprintf(" (%s...)", sceneTitle)
|
||||
title += fmt.Sprintf(" (%s...)", sceneTitle)
|
||||
}
|
||||
|
||||
objs = append(objs, makeStorageFolder(p.getPageID(page), title, p.parentID))
|
||||
|
||||
@@ -116,7 +116,7 @@ func IsValidCodec(codecName string, supportedCodecs []string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func IsValidAudio(audio AudioCodec, ValidCodecs []AudioCodec) bool {
|
||||
func IsValidAudio(audio AudioCodec, validCodecs []AudioCodec) bool {
|
||||
|
||||
// if audio codec is missing or unsupported by ffmpeg we can't do anything about it
|
||||
// report it as valid so that the file can at least be streamed directly if the video codec is supported
|
||||
@@ -124,7 +124,7 @@ func IsValidAudio(audio AudioCodec, ValidCodecs []AudioCodec) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
for _, c := range ValidCodecs {
|
||||
for _, c := range validCodecs {
|
||||
if c == audio {
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -6,17 +6,22 @@ import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
var ErrUnsupportedFormat = errors.New("unsupported image format")
|
||||
|
||||
func (e *Encoder) ImageThumbnail(image *bytes.Buffer, format *string, maxDimensions int, path string) ([]byte, error) {
|
||||
// ffmpeg spends a long sniffing image format when data is piped through stdio, so we pass the format explicitly instead
|
||||
ffmpegformat := ""
|
||||
if format != nil && *format == "jpeg" {
|
||||
if format == nil {
|
||||
return nil, ErrUnsupportedFormat
|
||||
}
|
||||
|
||||
switch *format {
|
||||
case "jpeg":
|
||||
ffmpegformat = "mjpeg"
|
||||
} else if format != nil && *format == "png" {
|
||||
case "png":
|
||||
ffmpegformat = "png_pipe"
|
||||
} else if format != nil && *format == "webp" {
|
||||
case "webp":
|
||||
ffmpegformat = "webp_pipe"
|
||||
} else {
|
||||
return nil, errors.New("unsupported image format")
|
||||
}
|
||||
|
||||
args := []string{
|
||||
|
||||
@@ -49,7 +49,7 @@ func IsZipPath(p string) bool {
|
||||
// path separators within zip files. It returns the original provided path
|
||||
// if it does not contain the zip file separator character.
|
||||
func ZipPathDisplayName(path string) string {
|
||||
return strings.Replace(path, zipSeparator, "/", -1)
|
||||
return strings.ReplaceAll(path, zipSeparator, "/")
|
||||
}
|
||||
|
||||
func ZipFilePath(path string) (zipFilename, filename string) {
|
||||
|
||||
@@ -107,11 +107,12 @@ func TestToJSON(t *testing.T) {
|
||||
gallery := s.input
|
||||
json, err := ToBasicJSON(&gallery)
|
||||
|
||||
if !s.err && err != nil {
|
||||
switch {
|
||||
case !s.err && err != nil:
|
||||
t.Errorf("[%d] unexpected error: %s", i, err.Error())
|
||||
} else if s.err && err == nil {
|
||||
case s.err && err == nil:
|
||||
t.Errorf("[%d] expected error not returned", i)
|
||||
} else {
|
||||
default:
|
||||
assert.Equal(t, s.expected, json, "[%d]", i)
|
||||
}
|
||||
}
|
||||
@@ -162,11 +163,12 @@ func TestGetStudioName(t *testing.T) {
|
||||
gallery := s.input
|
||||
json, err := GetStudioName(mockStudioReader, &gallery)
|
||||
|
||||
if !s.err && err != nil {
|
||||
switch {
|
||||
case !s.err && err != nil:
|
||||
t.Errorf("[%d] unexpected error: %s", i, err.Error())
|
||||
} else if s.err && err == nil {
|
||||
case s.err && err == nil:
|
||||
t.Errorf("[%d] expected error not returned", i)
|
||||
} else {
|
||||
default:
|
||||
assert.Equal(t, s.expected, json, "[%d]", i)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,9 +142,7 @@ func (scanner *Scanner) ScanNew(file file.SourceFile) (retGallery *models.Galler
|
||||
|
||||
isUpdatedGallery = true
|
||||
}
|
||||
} else {
|
||||
// don't create gallery if it has no images
|
||||
if scanner.hasImages(path) {
|
||||
} else if scanner.hasImages(path) { // don't create gallery if it has no images
|
||||
currentTime := time.Now()
|
||||
|
||||
g = &models.Gallery{
|
||||
@@ -174,7 +172,6 @@ func (scanner *Scanner) ScanNew(file file.SourceFile) (retGallery *models.Galler
|
||||
scanImages = true
|
||||
isNewGallery = true
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
|
||||
@@ -165,11 +165,12 @@ func TestGetStudioName(t *testing.T) {
|
||||
image := s.input
|
||||
json, err := GetStudioName(mockStudioReader, &image)
|
||||
|
||||
if !s.err && err != nil {
|
||||
switch {
|
||||
case !s.err && err != nil:
|
||||
t.Errorf("[%d] unexpected error: %s", i, err.Error())
|
||||
} else if s.err && err == nil {
|
||||
case s.err && err == nil:
|
||||
t.Errorf("[%d] expected error not returned", i)
|
||||
} else {
|
||||
default:
|
||||
assert.Equal(t, s.expected, json, "[%d]", i)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package image
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"sync"
|
||||
@@ -13,6 +14,8 @@ import (
|
||||
var vipsPath string
|
||||
var once sync.Once
|
||||
|
||||
var ErrUnsupportedFormat = errors.New("unsupported image format")
|
||||
|
||||
type ThumbnailEncoder struct {
|
||||
ffmpeg ffmpeg.Encoder
|
||||
vips *vipsEncoder
|
||||
|
||||
@@ -61,11 +61,12 @@ func (p *Progress) SetProcessed(processed int) {
|
||||
}
|
||||
|
||||
func (p *Progress) calculatePercent() {
|
||||
if p.total <= 0 {
|
||||
switch {
|
||||
case p.total <= 0:
|
||||
p.percent = ProgressIndefinite
|
||||
} else if p.processed < 0 {
|
||||
case p.processed < 0:
|
||||
p.percent = 0
|
||||
} else {
|
||||
default:
|
||||
p.percent = float64(p.processed) / float64(p.total)
|
||||
if p.percent > 1 {
|
||||
p.percent = 1
|
||||
|
||||
@@ -80,13 +80,14 @@ func SetLogLevel(level string) {
|
||||
func logLevelFromString(level string) logrus.Level {
|
||||
ret := logrus.InfoLevel
|
||||
|
||||
if level == "Debug" {
|
||||
switch level {
|
||||
case "Debug":
|
||||
ret = logrus.DebugLevel
|
||||
} else if level == "Warning" {
|
||||
case "Warning":
|
||||
ret = logrus.WarnLevel
|
||||
} else if level == "Error" {
|
||||
case "Error":
|
||||
ret = logrus.ErrorLevel
|
||||
} else if level == "Trace" {
|
||||
case "Trace":
|
||||
ret = logrus.TraceLevel
|
||||
}
|
||||
|
||||
|
||||
@@ -160,16 +160,14 @@ func (log *PluginLogger) HandleStderrLine(line string) {
|
||||
p, err := strconv.ParseFloat(ll, 64)
|
||||
if err != nil {
|
||||
Errorf("Error parsing progress value '%s': %s", ll, err.Error())
|
||||
} else {
|
||||
// only pass progress through if channel present
|
||||
if log.ProgressChan != nil {
|
||||
} else if log.ProgressChan != nil { // only pass progress through if channel present
|
||||
// don't block on this
|
||||
select {
|
||||
case log.ProgressChan <- p:
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -175,6 +174,16 @@ func (e MissingConfigError) Error() string {
|
||||
return fmt.Sprintf("missing the following mandatory settings: %s", strings.Join(e.missingFields, ", "))
|
||||
}
|
||||
|
||||
// StashBoxError represents configuration errors of Stash-Box
|
||||
type StashBoxError struct {
|
||||
msg string
|
||||
}
|
||||
|
||||
func (s *StashBoxError) Error() string {
|
||||
// "Stash-box" is a proper noun and is therefore capitcalized
|
||||
return "Stash-box: " + s.msg
|
||||
}
|
||||
|
||||
type Instance struct {
|
||||
cpuProfilePath string
|
||||
isNewSystem bool
|
||||
@@ -680,29 +689,30 @@ func (i *Instance) ValidateCredentials(username string, password string) bool {
|
||||
return username == authUser && err == nil
|
||||
}
|
||||
|
||||
var stashBoxRe = regexp.MustCompile("^http.*graphql$")
|
||||
|
||||
func (i *Instance) ValidateStashBoxes(boxes []*models.StashBoxInput) error {
|
||||
isMulti := len(boxes) > 1
|
||||
|
||||
re, err := regexp.Compile("^http.*graphql$")
|
||||
if err != nil {
|
||||
return errors.New("failure to generate regular expression")
|
||||
for _, box := range boxes {
|
||||
// Validate each stash-box configuration field, return on error
|
||||
if box.APIKey == "" {
|
||||
return &StashBoxError{msg: "API Key cannot be blank"}
|
||||
}
|
||||
|
||||
for _, box := range boxes {
|
||||
if box.APIKey == "" {
|
||||
//lint:ignore ST1005 Stash-box is a name
|
||||
return errors.New("Stash-box API Key cannot be blank")
|
||||
} else if box.Endpoint == "" {
|
||||
//lint:ignore ST1005 Stash-box is a name
|
||||
return errors.New("Stash-box Endpoint cannot be blank")
|
||||
} else if !re.Match([]byte(box.Endpoint)) {
|
||||
//lint:ignore ST1005 Stash-box is a name
|
||||
return errors.New("Stash-box Endpoint is invalid")
|
||||
} else if isMulti && box.Name == "" {
|
||||
//lint:ignore ST1005 Stash-box is a name
|
||||
return errors.New("Stash-box Name cannot be blank")
|
||||
if box.Endpoint == "" {
|
||||
return &StashBoxError{msg: "endpoint cannot be blank"}
|
||||
}
|
||||
|
||||
if !stashBoxRe.Match([]byte(box.Endpoint)) {
|
||||
return &StashBoxError{msg: "endpoint is invalid"}
|
||||
}
|
||||
|
||||
if isMulti && box.Name == "" {
|
||||
return &StashBoxError{msg: "name cannot be blank"}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ func (s *DownloadStore) RegisterFile(fp string, contentType string, keep bool) s
|
||||
for generate && a < attempts {
|
||||
hash = utils.GenerateRandomKey(keyLength)
|
||||
_, generate = s.m[hash]
|
||||
a = a + 1
|
||||
a++
|
||||
}
|
||||
|
||||
s.m[hash] = &storeFile{
|
||||
|
||||
@@ -94,17 +94,15 @@ func Initialize() *singleton {
|
||||
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("error initializing configuration: %s", err.Error()))
|
||||
} else {
|
||||
if err := instance.PostInit(ctx); err != nil {
|
||||
} else if err := instance.PostInit(ctx); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
initSecurity(cfg)
|
||||
} else {
|
||||
cfgFile := cfg.GetConfigFile()
|
||||
if cfgFile != "" {
|
||||
cfgFile = cfgFile + " "
|
||||
cfgFile += " "
|
||||
}
|
||||
|
||||
// create temporary session store - this will be re-initialised
|
||||
|
||||
@@ -616,7 +616,13 @@ func (s *singleton) StashBoxBatchPerformerTag(ctx context.Context, input models.
|
||||
|
||||
var tasks []StashBoxPerformerTagTask
|
||||
|
||||
if len(input.PerformerIds) > 0 {
|
||||
// The gocritic linter wants to turn this ifElseChain into a switch.
|
||||
// however, such a switch would contain quite large blocks for each section
|
||||
// and would arguably be hard to read.
|
||||
//
|
||||
// This is why we mark this section nolint. In principle, we should look to
|
||||
// rewrite the section at some point, to avoid the linter warning.
|
||||
if len(input.PerformerIds) > 0 { //nolint:gocritic
|
||||
if err := s.TxnManager.WithReadTxn(context.TODO(), func(r models.ReaderRepository) error {
|
||||
performerQuery := r.Performer()
|
||||
|
||||
@@ -652,7 +658,11 @@ func (s *singleton) StashBoxBatchPerformerTag(ctx context.Context, input models.
|
||||
})
|
||||
}
|
||||
}
|
||||
} else {
|
||||
} else { //nolint:gocritic
|
||||
// The gocritic linter wants to fold this if-block into the else on the line above.
|
||||
// However, this doesn't really help with readability of the current section. Mark it
|
||||
// as nolint for now. In the future we'd like to rewrite this code by factoring some of
|
||||
// this into separate functions.
|
||||
if err := s.TxnManager.WithReadTxn(context.TODO(), func(r models.ReaderRepository) error {
|
||||
performerQuery := r.Performer()
|
||||
var performers []*models.Performer
|
||||
|
||||
@@ -330,7 +330,7 @@ func (t *autoTagFilesTask) makeSceneFilter() *models.SceneFilterType {
|
||||
|
||||
for _, p := range t.paths {
|
||||
if !strings.HasSuffix(p, sep) {
|
||||
p = p + sep
|
||||
p += sep
|
||||
}
|
||||
|
||||
if ret.Path == nil {
|
||||
@@ -360,7 +360,7 @@ func (t *autoTagFilesTask) makeImageFilter() *models.ImageFilterType {
|
||||
|
||||
for _, p := range t.paths {
|
||||
if !strings.HasSuffix(p, sep) {
|
||||
p = p + sep
|
||||
p += sep
|
||||
}
|
||||
|
||||
if ret.Path == nil {
|
||||
@@ -397,7 +397,7 @@ func (t *autoTagFilesTask) makeGalleryFilter() *models.GalleryFilterType {
|
||||
|
||||
for _, p := range t.paths {
|
||||
if !strings.HasSuffix(p, sep) {
|
||||
p = p + sep
|
||||
p += sep
|
||||
}
|
||||
|
||||
if ret.Path == nil {
|
||||
|
||||
@@ -209,17 +209,18 @@ func (j *ScanJob) doesPathExist(path string) bool {
|
||||
|
||||
ret := false
|
||||
txnErr := j.txnManager.WithReadTxn(context.TODO(), func(r models.ReaderRepository) error {
|
||||
if utils.MatchExtension(path, gExt) {
|
||||
gallery, _ := r.Gallery().FindByPath(path)
|
||||
if gallery != nil {
|
||||
switch {
|
||||
case utils.MatchExtension(path, gExt):
|
||||
g, _ := r.Gallery().FindByPath(path)
|
||||
if g != nil {
|
||||
ret = true
|
||||
}
|
||||
} else if utils.MatchExtension(path, vidExt) {
|
||||
case utils.MatchExtension(path, vidExt):
|
||||
s, _ := r.Scene().FindByPath(path)
|
||||
if s != nil {
|
||||
ret = true
|
||||
}
|
||||
} else if utils.MatchExtension(path, imgExt) {
|
||||
case utils.MatchExtension(path, imgExt):
|
||||
i, _ := r.Image().FindByPath(path)
|
||||
if i != nil {
|
||||
ret = true
|
||||
@@ -259,16 +260,21 @@ func (t *ScanTask) Start(ctx context.Context) {
|
||||
var s *models.Scene
|
||||
path := t.file.Path()
|
||||
t.progress.ExecuteTask("Scanning "+path, func() {
|
||||
if isGallery(path) {
|
||||
switch {
|
||||
case isGallery(path):
|
||||
t.scanGallery(ctx)
|
||||
} else if isVideo(path) {
|
||||
case isVideo(path):
|
||||
s = t.scanScene()
|
||||
} else if isImage(path) {
|
||||
case isImage(path):
|
||||
t.scanImage()
|
||||
}
|
||||
})
|
||||
|
||||
if s != nil {
|
||||
if s == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Handle the case of a scene
|
||||
iwg := sizedwaitgroup.New(2)
|
||||
|
||||
if t.GenerateSprite {
|
||||
@@ -333,7 +339,6 @@ func (t *ScanTask) Start(ctx context.Context) {
|
||||
|
||||
iwg.Wait()
|
||||
}
|
||||
}
|
||||
|
||||
func walkFilesToScan(s *models.StashConfig, f filepath.WalkFunc) error {
|
||||
config := config.GetInstance()
|
||||
|
||||
@@ -18,7 +18,7 @@ func getPathQueryRegex(name string) string {
|
||||
// handle path separators
|
||||
const separator = `[` + separatorChars + `]`
|
||||
|
||||
ret := strings.Replace(name, " ", separator+"*", -1)
|
||||
ret := strings.ReplaceAll(name, " ", separator+"*")
|
||||
ret = `(?:^|_|[^\w\d])` + ret + `(?:$|_|[^\w\d])`
|
||||
return ret
|
||||
}
|
||||
@@ -66,7 +66,7 @@ func nameMatchesPath(name, path string) bool {
|
||||
// handle path separators
|
||||
const separator = `[` + separatorChars + `]`
|
||||
|
||||
reStr := strings.Replace(name, " ", separator+"*", -1)
|
||||
reStr := strings.ReplaceAll(name, " ", separator+"*")
|
||||
reStr = `(?:^|_|[^\w\d])` + reStr + `(?:$|_|[^\w\d])`
|
||||
|
||||
re := regexp.MustCompile(reStr)
|
||||
@@ -185,7 +185,7 @@ func scenePathsFilter(paths []string) *models.SceneFilterType {
|
||||
or = newOr
|
||||
|
||||
if !strings.HasSuffix(p, sep) {
|
||||
p = p + sep
|
||||
p += sep
|
||||
}
|
||||
|
||||
or.Path = &models.StringCriterionInput{
|
||||
@@ -249,7 +249,7 @@ func imagePathsFilter(paths []string) *models.ImageFilterType {
|
||||
or = newOr
|
||||
|
||||
if !strings.HasSuffix(p, sep) {
|
||||
p = p + sep
|
||||
p += sep
|
||||
}
|
||||
|
||||
or.Path = &models.StringCriterionInput{
|
||||
@@ -313,7 +313,7 @@ func galleryPathsFilter(paths []string) *models.GalleryFilterType {
|
||||
or = newOr
|
||||
|
||||
if !strings.HasSuffix(p, sep) {
|
||||
p = p + sep
|
||||
p += sep
|
||||
}
|
||||
|
||||
or.Path = &models.StringCriterionInput{
|
||||
|
||||
@@ -13,16 +13,14 @@ type File struct {
|
||||
// GetHash returns the hash of the scene, based on the hash algorithm provided. If
|
||||
// hash algorithm is MD5, then Checksum is returned. Otherwise, OSHash is returned.
|
||||
func (s File) GetHash(hashAlgorithm HashAlgorithm) string {
|
||||
var ret string
|
||||
if hashAlgorithm == HashAlgorithmMd5 {
|
||||
ret = s.Checksum
|
||||
} else if hashAlgorithm == HashAlgorithmOshash {
|
||||
ret = s.OSHash
|
||||
} else {
|
||||
switch hashAlgorithm {
|
||||
case HashAlgorithmMd5:
|
||||
return s.Checksum
|
||||
case HashAlgorithmOshash:
|
||||
return s.OSHash
|
||||
default:
|
||||
panic("unknown hash algorithm")
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func (s File) Equal(o File) bool {
|
||||
|
||||
@@ -38,7 +38,9 @@ func WithTxn(txn Transaction, fn func(r Repository) error) error {
|
||||
logger.Warnf("error while trying to roll back transaction: %v", err)
|
||||
}
|
||||
panic(p)
|
||||
} else if err != nil {
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
// something went wrong, rollback
|
||||
if err := txn.Rollback(); err != nil {
|
||||
logger.Warnf("error while trying to roll back transaction: %v", err)
|
||||
@@ -66,7 +68,9 @@ func WithROTxn(txn ReadTransaction, fn func(r ReaderRepository) error) error {
|
||||
logger.Warnf("error while trying to roll back RO transaction: %v", err)
|
||||
}
|
||||
panic(p)
|
||||
} else if err != nil {
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
// something went wrong, rollback
|
||||
if err := txn.Rollback(); err != nil {
|
||||
logger.Warnf("error while trying to roll back RO transaction: %v", err)
|
||||
|
||||
@@ -213,11 +213,12 @@ func TestToJSON(t *testing.T) {
|
||||
movie := s.movie
|
||||
json, err := ToJSON(mockMovieReader, mockStudioReader, &movie)
|
||||
|
||||
if !s.err && err != nil {
|
||||
switch {
|
||||
case !s.err && err != nil:
|
||||
t.Errorf("[%d] unexpected error: %s", i, err.Error())
|
||||
} else if s.err && err == nil {
|
||||
case s.err && err == nil:
|
||||
t.Errorf("[%d] expected error not returned", i)
|
||||
} else {
|
||||
default:
|
||||
assert.Equal(t, s.expected, json, "[%d]", i)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -201,11 +201,12 @@ func TestToJSON(t *testing.T) {
|
||||
tag := s.input
|
||||
json, err := ToJSON(mockPerformerReader, &tag)
|
||||
|
||||
if !s.err && err != nil {
|
||||
switch {
|
||||
case !s.err && err != nil:
|
||||
t.Errorf("[%d] unexpected error: %s", i, err.Error())
|
||||
} else if s.err && err == nil {
|
||||
case s.err && err == nil:
|
||||
t.Errorf("[%d] expected error not returned", i)
|
||||
} else {
|
||||
default:
|
||||
assert.Equal(t, s.expected, json, "[%d]", i)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -173,7 +173,7 @@ func (c Config) getExecCommand(task *OperationConfig) []string {
|
||||
continue
|
||||
}
|
||||
|
||||
ret[i] = strings.Replace(arg, "{pluginDir}", dir, -1)
|
||||
ret[i] = strings.ReplaceAll(arg, "{pluginDir}", dir)
|
||||
}
|
||||
|
||||
return ret
|
||||
|
||||
@@ -226,11 +226,12 @@ func TestToJSON(t *testing.T) {
|
||||
scene := s.input
|
||||
json, err := ToBasicJSON(mockSceneReader, &scene)
|
||||
|
||||
if !s.err && err != nil {
|
||||
switch {
|
||||
case !s.err && err != nil:
|
||||
t.Errorf("[%d] unexpected error: %s", i, err.Error())
|
||||
} else if s.err && err == nil {
|
||||
case s.err && err == nil:
|
||||
t.Errorf("[%d] expected error not returned", i)
|
||||
} else {
|
||||
default:
|
||||
assert.Equal(t, s.expected, json, "[%d]", i)
|
||||
}
|
||||
}
|
||||
@@ -283,11 +284,12 @@ func TestGetStudioName(t *testing.T) {
|
||||
scene := s.input
|
||||
json, err := GetStudioName(mockStudioReader, &scene)
|
||||
|
||||
if !s.err && err != nil {
|
||||
switch {
|
||||
case !s.err && err != nil:
|
||||
t.Errorf("[%d] unexpected error: %s", i, err.Error())
|
||||
} else if s.err && err == nil {
|
||||
case s.err && err == nil:
|
||||
t.Errorf("[%d] expected error not returned", i)
|
||||
} else {
|
||||
default:
|
||||
assert.Equal(t, s.expected, json, "[%d]", i)
|
||||
}
|
||||
}
|
||||
@@ -343,11 +345,12 @@ func TestGetTagNames(t *testing.T) {
|
||||
scene := s.input
|
||||
json, err := GetTagNames(mockTagReader, &scene)
|
||||
|
||||
if !s.err && err != nil {
|
||||
switch {
|
||||
case !s.err && err != nil:
|
||||
t.Errorf("[%d] unexpected error: %s", i, err.Error())
|
||||
} else if s.err && err == nil {
|
||||
case s.err && err == nil:
|
||||
t.Errorf("[%d] expected error not returned", i)
|
||||
} else {
|
||||
default:
|
||||
assert.Equal(t, s.expected, json, "[%d]", i)
|
||||
}
|
||||
}
|
||||
@@ -435,11 +438,12 @@ func TestGetSceneMoviesJSON(t *testing.T) {
|
||||
scene := s.input
|
||||
json, err := GetSceneMoviesJSON(mockMovieReader, mockSceneReader, &scene)
|
||||
|
||||
if !s.err && err != nil {
|
||||
switch {
|
||||
case !s.err && err != nil:
|
||||
t.Errorf("[%d] unexpected error: %s", i, err.Error())
|
||||
} else if s.err && err == nil {
|
||||
case s.err && err == nil:
|
||||
t.Errorf("[%d] expected error not returned", i)
|
||||
} else {
|
||||
default:
|
||||
assert.Equal(t, s.expected, json, "[%d]", i)
|
||||
}
|
||||
}
|
||||
@@ -617,11 +621,12 @@ func TestGetSceneMarkersJSON(t *testing.T) {
|
||||
scene := s.input
|
||||
json, err := GetSceneMarkersJSON(mockMarkerReader, mockTagReader, &scene)
|
||||
|
||||
if !s.err && err != nil {
|
||||
switch {
|
||||
case !s.err && err != nil:
|
||||
t.Errorf("[%d] unexpected error: %s", i, err.Error())
|
||||
} else if s.err && err == nil {
|
||||
case s.err && err == nil:
|
||||
t.Errorf("[%d] expected error not returned", i)
|
||||
} else {
|
||||
default:
|
||||
assert.Equal(t, s.expected, json, "[%d]", i)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -396,11 +396,13 @@ func (i *Importer) Name() string {
|
||||
func (i *Importer) FindExistingID() (*int, error) {
|
||||
var existing *models.Scene
|
||||
var err error
|
||||
if i.FileNamingAlgorithm == models.HashAlgorithmMd5 {
|
||||
|
||||
switch i.FileNamingAlgorithm {
|
||||
case models.HashAlgorithmMd5:
|
||||
existing, err = i.ReaderWriter.FindByChecksum(i.Input.Checksum)
|
||||
} else if i.FileNamingAlgorithm == models.HashAlgorithmOshash {
|
||||
case models.HashAlgorithmOshash:
|
||||
existing, err = i.ReaderWriter.FindByOSHash(i.Input.OSHash)
|
||||
} else {
|
||||
default:
|
||||
panic("unknown file naming algorithm")
|
||||
}
|
||||
|
||||
|
||||
@@ -97,7 +97,7 @@ func (c *autotagSceneScraper) scrapeByScene(scene *models.Scene) (*models.Scrape
|
||||
var ret *models.ScrapedScene
|
||||
|
||||
// populate performers, studio and tags based on scene path
|
||||
if err := c.txnManager.WithReadTxn(context.Background(), func(r models.ReaderRepository) error {
|
||||
if err := c.txnManager.WithReadTxn(context.TODO(), func(r models.ReaderRepository) error {
|
||||
path := scene.Path
|
||||
performers, err := c.matchPerformers(path, r.Performer())
|
||||
if err != nil {
|
||||
@@ -150,7 +150,7 @@ func (c *autotagGalleryScraper) scrapeByGallery(gallery *models.Gallery) (*model
|
||||
var ret *models.ScrapedGallery
|
||||
|
||||
// populate performers, studio and tags based on scene path
|
||||
if err := c.txnManager.WithReadTxn(context.Background(), func(r models.ReaderRepository) error {
|
||||
if err := c.txnManager.WithReadTxn(context.TODO(), func(r models.ReaderRepository) error {
|
||||
path := gallery.Path.String
|
||||
performers, err := c.matchPerformers(path, r.Performer())
|
||||
if err != nil {
|
||||
|
||||
@@ -128,7 +128,7 @@ func (s *jsonScraper) scrapePerformersByName(name string) ([]*models.ScrapedPerf
|
||||
escapedName := url.QueryEscape(name)
|
||||
|
||||
url := s.scraper.QueryURL
|
||||
url = strings.Replace(url, placeholder, escapedName, -1)
|
||||
url = strings.ReplaceAll(url, placeholder, escapedName)
|
||||
|
||||
doc, err := s.loadURL(context.TODO(), url)
|
||||
|
||||
@@ -157,7 +157,7 @@ func (s *jsonScraper) scrapeScenesByName(name string) ([]*models.ScrapedScene, e
|
||||
escapedName := url.QueryEscape(name)
|
||||
|
||||
url := s.scraper.QueryURL
|
||||
url = strings.Replace(url, placeholder, escapedName, -1)
|
||||
url = strings.ReplaceAll(url, placeholder, escapedName)
|
||||
|
||||
doc, err := s.loadURL(context.TODO(), url)
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ func (s mappedConfig) applyCommon(c commonMappedConfig, src string) string {
|
||||
|
||||
ret := src
|
||||
for commonKey, commonVal := range c {
|
||||
ret = strings.Replace(ret, commonKey, commonVal, -1)
|
||||
ret = strings.ReplaceAll(ret, commonKey, commonVal)
|
||||
}
|
||||
|
||||
return ret
|
||||
@@ -486,7 +486,7 @@ func (p *postProcessLbToKg) Apply(value string, q mappedQuery) string {
|
||||
const lb_in_kg = 0.45359237
|
||||
w, err := strconv.ParseFloat(value, 64)
|
||||
if err == nil {
|
||||
w = w * lb_in_kg
|
||||
w *= lb_in_kg
|
||||
value = strconv.Itoa(int(math.Round(w)))
|
||||
}
|
||||
return value
|
||||
@@ -576,7 +576,7 @@ type mappedScraperAttrConfig struct {
|
||||
|
||||
postProcessActions []postProcessAction
|
||||
|
||||
// deprecated: use PostProcess instead
|
||||
// Deprecated: use PostProcess instead
|
||||
ParseDate string `yaml:"parseDate"`
|
||||
Replace mappedRegexConfigs `yaml:"replace"`
|
||||
SubScraper *mappedScraperAttrConfig `yaml:"subScraper"`
|
||||
|
||||
@@ -69,7 +69,7 @@ func (p queryURLParameters) applyReplacements(r queryURLReplacements) {
|
||||
func (p queryURLParameters) constructURL(url string) string {
|
||||
ret := url
|
||||
for k, v := range p {
|
||||
ret = strings.Replace(ret, "{"+k+"}", v, -1)
|
||||
ret = strings.ReplaceAll(ret, "{"+k+"}", v)
|
||||
}
|
||||
|
||||
return ret
|
||||
|
||||
@@ -479,11 +479,12 @@ func formatCareerLength(start, end *int) *string {
|
||||
}
|
||||
|
||||
var ret string
|
||||
if end == nil {
|
||||
switch {
|
||||
case end == nil:
|
||||
ret = fmt.Sprintf("%d -", *start)
|
||||
} else if start == nil {
|
||||
case start == nil:
|
||||
ret = fmt.Sprintf("- %d", *end)
|
||||
} else {
|
||||
default:
|
||||
ret = fmt.Sprintf("%d - %d", *start, *end)
|
||||
}
|
||||
|
||||
|
||||
@@ -109,7 +109,7 @@ func (s *xpathScraper) scrapePerformersByName(name string) ([]*models.ScrapedPer
|
||||
escapedName := url.QueryEscape(name)
|
||||
|
||||
url := s.scraper.QueryURL
|
||||
url = strings.Replace(url, placeholder, escapedName, -1)
|
||||
url = strings.ReplaceAll(url, placeholder, escapedName)
|
||||
|
||||
doc, err := s.loadURL(context.TODO(), url)
|
||||
|
||||
@@ -138,7 +138,7 @@ func (s *xpathScraper) scrapeScenesByName(name string) ([]*models.ScrapedScene,
|
||||
escapedName := url.QueryEscape(name)
|
||||
|
||||
url := s.scraper.QueryURL
|
||||
url = strings.Replace(url, placeholder, escapedName, -1)
|
||||
url = strings.ReplaceAll(url, placeholder, escapedName)
|
||||
|
||||
doc, err := s.loadURL(context.TODO(), url)
|
||||
|
||||
|
||||
@@ -70,12 +70,10 @@ func CheckAllowPublicWithoutAuth(c *config.Instance, r *http.Request) error {
|
||||
return UntrustedProxyError(requestIP)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// request was not proxied
|
||||
if !isLocalIP(requestIP) {
|
||||
} else if !isLocalIP(requestIP) { // request was not proxied
|
||||
return ExternalAccessError(requestIP)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -210,11 +210,9 @@ func (f *filterBuilder) getSubFilterClause(clause, subFilterClause string) strin
|
||||
var op string
|
||||
if len(ret) > 0 {
|
||||
op = " " + f.subFilterOp + " "
|
||||
} else {
|
||||
if f.subFilterOp == notOp {
|
||||
} else if f.subFilterOp == notOp {
|
||||
op = "NOT "
|
||||
}
|
||||
}
|
||||
|
||||
ret += op + "(" + subFilterClause + ")"
|
||||
}
|
||||
@@ -436,16 +434,18 @@ func (m *joinedMultiCriterionHandlerBuilder) handler(criterion *models.MultiCrit
|
||||
|
||||
whereClause := ""
|
||||
havingClause := ""
|
||||
if criterion.Modifier == models.CriterionModifierIncludes {
|
||||
|
||||
switch criterion.Modifier {
|
||||
case models.CriterionModifierIncludes:
|
||||
// includes any of the provided ids
|
||||
m.addJoinTable(f)
|
||||
whereClause = fmt.Sprintf("%s.%s IN %s", joinAlias, m.foreignFK, getInBinding(len(criterion.Value)))
|
||||
} else if criterion.Modifier == models.CriterionModifierIncludesAll {
|
||||
case models.CriterionModifierIncludesAll:
|
||||
// includes all of the provided ids
|
||||
m.addJoinTable(f)
|
||||
whereClause = fmt.Sprintf("%s.%s IN %s", joinAlias, m.foreignFK, getInBinding(len(criterion.Value)))
|
||||
havingClause = fmt.Sprintf("count(distinct %s.%s) IS %d", joinAlias, m.foreignFK, len(criterion.Value))
|
||||
} else if criterion.Modifier == models.CriterionModifierExcludes {
|
||||
case models.CriterionModifierExcludes:
|
||||
// excludes all of the provided ids
|
||||
// need to use actual join table name for this
|
||||
// <primaryTable>.id NOT IN (select <joinTable>.<primaryFK> from <joinTable> where <joinTable>.<foreignFK> in <values>)
|
||||
@@ -620,12 +620,13 @@ WHERE id in {inBinding}
|
||||
}
|
||||
|
||||
func addHierarchicalConditionClauses(f *filterBuilder, criterion *models.HierarchicalMultiCriterionInput, table, idColumn string) {
|
||||
if criterion.Modifier == models.CriterionModifierIncludes {
|
||||
switch criterion.Modifier {
|
||||
case models.CriterionModifierIncludes:
|
||||
f.addWhere(fmt.Sprintf("%s.%s IS NOT NULL", table, idColumn))
|
||||
} else if criterion.Modifier == models.CriterionModifierIncludesAll {
|
||||
case models.CriterionModifierIncludesAll:
|
||||
f.addWhere(fmt.Sprintf("%s.%s IS NOT NULL", table, idColumn))
|
||||
f.addHaving(fmt.Sprintf("count(distinct %s.%s) IS %d", table, idColumn, len(criterion.Value)))
|
||||
} else if criterion.Modifier == models.CriterionModifierExcludes {
|
||||
case models.CriterionModifierExcludes:
|
||||
f.addWhere(fmt.Sprintf("%s.%s IS NULL", table, idColumn))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -418,13 +418,14 @@ func galleryAverageResolutionCriterionHandler(qb *galleryQueryBuilder, resolutio
|
||||
|
||||
const widthHeight = "avg(MIN(images.width, images.height))"
|
||||
|
||||
if resolution.Modifier == models.CriterionModifierEquals {
|
||||
switch resolution.Modifier {
|
||||
case models.CriterionModifierEquals:
|
||||
f.addHaving(fmt.Sprintf("%s BETWEEN %d AND %d", widthHeight, min, max))
|
||||
} else if resolution.Modifier == models.CriterionModifierNotEquals {
|
||||
case models.CriterionModifierNotEquals:
|
||||
f.addHaving(fmt.Sprintf("%s NOT BETWEEN %d AND %d", widthHeight, min, max))
|
||||
} else if resolution.Modifier == models.CriterionModifierLessThan {
|
||||
case models.CriterionModifierLessThan:
|
||||
f.addHaving(fmt.Sprintf("%s < %d", widthHeight, min))
|
||||
} else if resolution.Modifier == models.CriterionModifierGreaterThan {
|
||||
case models.CriterionModifierGreaterThan:
|
||||
f.addHaving(fmt.Sprintf("%s > %d", widthHeight, max))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -225,12 +225,13 @@ func moviePerformersCriterionHandler(qb *movieQueryBuilder, performers *models.M
|
||||
)`, args...)
|
||||
f.addJoin("movies_performers", "", "movies.id = movies_performers.movie_id")
|
||||
|
||||
if performers.Modifier == models.CriterionModifierIncludes {
|
||||
switch performers.Modifier {
|
||||
case models.CriterionModifierIncludes:
|
||||
f.addWhere("movies_performers.performer_id IS NOT NULL")
|
||||
} else if performers.Modifier == models.CriterionModifierIncludesAll {
|
||||
case models.CriterionModifierIncludesAll:
|
||||
f.addWhere("movies_performers.performer_id IS NOT NULL")
|
||||
f.addHaving("COUNT(DISTINCT movies_performers.performer_id) = ?", len(performers.Value))
|
||||
} else if performers.Modifier == models.CriterionModifierExcludes {
|
||||
case models.CriterionModifierExcludes:
|
||||
f.addWhere("movies_performers.performer_id IS NULL")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -443,13 +443,14 @@ func performerStudiosCriterionHandler(qb *performerQueryBuilder, studios *models
|
||||
if studios != nil {
|
||||
var clauseCondition string
|
||||
|
||||
if studios.Modifier == models.CriterionModifierIncludes {
|
||||
switch studios.Modifier {
|
||||
case models.CriterionModifierIncludes:
|
||||
// return performers who appear in scenes/images/galleries with any of the given studios
|
||||
clauseCondition = "NOT"
|
||||
} else if studios.Modifier == models.CriterionModifierExcludes {
|
||||
case models.CriterionModifierExcludes:
|
||||
// exclude performers who appear in scenes/images/galleries with any of the given studios
|
||||
clauseCondition = ""
|
||||
} else {
|
||||
default:
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -482,13 +482,14 @@ func resolutionCriterionHandler(resolution *models.ResolutionCriterionInput, hei
|
||||
|
||||
widthHeight := fmt.Sprintf("MIN(%s, %s)", widthColumn, heightColumn)
|
||||
|
||||
if resolution.Modifier == models.CriterionModifierEquals {
|
||||
switch resolution.Modifier {
|
||||
case models.CriterionModifierEquals:
|
||||
f.addWhere(fmt.Sprintf("%s BETWEEN %d AND %d", widthHeight, min, max))
|
||||
} else if resolution.Modifier == models.CriterionModifierNotEquals {
|
||||
case models.CriterionModifierNotEquals:
|
||||
f.addWhere(fmt.Sprintf("%s NOT BETWEEN %d AND %d", widthHeight, min, max))
|
||||
} else if resolution.Modifier == models.CriterionModifierLessThan {
|
||||
case models.CriterionModifierLessThan:
|
||||
f.addWhere(fmt.Sprintf("%s < %d", widthHeight, min))
|
||||
} else if resolution.Modifier == models.CriterionModifierGreaterThan {
|
||||
case models.CriterionModifierGreaterThan:
|
||||
f.addWhere(fmt.Sprintf("%s > %d", widthHeight, max))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,13 +106,13 @@ func (qb *sceneMarkerQueryBuilder) CountByTagID(tagID int) (int, error) {
|
||||
func (qb *sceneMarkerQueryBuilder) GetMarkerStrings(q *string, sort *string) ([]*models.MarkerStringsResultType, error) {
|
||||
query := "SELECT count(*) as `count`, scene_markers.id as id, scene_markers.title as title FROM scene_markers"
|
||||
if q != nil {
|
||||
query = query + " WHERE title LIKE '%" + *q + "%'"
|
||||
query += " WHERE title LIKE '%" + *q + "%'"
|
||||
}
|
||||
query = query + " GROUP BY title"
|
||||
query += " GROUP BY title"
|
||||
if sort != nil && *sort == "count" {
|
||||
query = query + " ORDER BY `count` DESC"
|
||||
query += " ORDER BY `count` DESC"
|
||||
} else {
|
||||
query = query + " ORDER BY title ASC"
|
||||
query += " ORDER BY title ASC"
|
||||
}
|
||||
var args []interface{}
|
||||
return qb.queryMarkerStringsResultType(query, args)
|
||||
|
||||
@@ -58,14 +58,15 @@ func getSort(sort string, direction string, tableName string) string {
|
||||
|
||||
const randomSeedPrefix = "random_"
|
||||
|
||||
if strings.HasSuffix(sort, "_count") {
|
||||
switch {
|
||||
case strings.HasSuffix(sort, "_count"):
|
||||
var relationTableName = strings.TrimSuffix(sort, "_count") // TODO: pluralize?
|
||||
colName := getColumn(relationTableName, "id")
|
||||
return " ORDER BY COUNT(distinct " + colName + ") " + direction
|
||||
} else if strings.Compare(sort, "filesize") == 0 {
|
||||
case strings.Compare(sort, "filesize") == 0:
|
||||
colName := getColumn(tableName, "size")
|
||||
return " ORDER BY cast(" + colName + " as integer) " + direction
|
||||
} else if strings.HasPrefix(sort, randomSeedPrefix) {
|
||||
case strings.HasPrefix(sort, randomSeedPrefix):
|
||||
// seed as a parameter from the UI
|
||||
// turn the provided seed into a float
|
||||
seedStr := "0." + sort[len(randomSeedPrefix):]
|
||||
@@ -75,9 +76,9 @@ func getSort(sort string, direction string, tableName string) string {
|
||||
seed = randomSortFloat
|
||||
}
|
||||
return getRandomSort(tableName, direction, seed)
|
||||
} else if strings.Compare(sort, "random") == 0 {
|
||||
case strings.Compare(sort, "random") == 0:
|
||||
return getRandomSort(tableName, direction, randomSortFloat)
|
||||
} else {
|
||||
default:
|
||||
colName := getColumn(tableName, sort)
|
||||
var additional string
|
||||
if tableName == "scenes" {
|
||||
@@ -202,14 +203,15 @@ func getIntCriterionWhereClause(column string, input models.IntCriterionInput) (
|
||||
func getMultiCriterionClause(primaryTable, foreignTable, joinTable, primaryFK, foreignFK string, criterion *models.MultiCriterionInput) (string, string) {
|
||||
whereClause := ""
|
||||
havingClause := ""
|
||||
if criterion.Modifier == models.CriterionModifierIncludes {
|
||||
switch criterion.Modifier {
|
||||
case models.CriterionModifierIncludes:
|
||||
// includes any of the provided ids
|
||||
whereClause = foreignTable + ".id IN " + getInBinding(len(criterion.Value))
|
||||
} else if criterion.Modifier == models.CriterionModifierIncludesAll {
|
||||
case models.CriterionModifierIncludesAll:
|
||||
// includes all of the provided ids
|
||||
whereClause = foreignTable + ".id IN " + getInBinding(len(criterion.Value))
|
||||
havingClause = "count(distinct " + foreignTable + ".id) IS " + strconv.Itoa(len(criterion.Value))
|
||||
} else if criterion.Modifier == models.CriterionModifierExcludes {
|
||||
case models.CriterionModifierExcludes:
|
||||
// excludes all of the provided ids
|
||||
if joinTable != "" {
|
||||
whereClause = primaryTable + ".id not in (select " + joinTable + "." + primaryFK + " from " + joinTable + " where " + joinTable + "." + foreignFK + " in " + getInBinding(len(criterion.Value)) + ")"
|
||||
|
||||
@@ -184,11 +184,12 @@ func TestToJSON(t *testing.T) {
|
||||
studio := s.input
|
||||
json, err := ToJSON(mockStudioReader, &studio)
|
||||
|
||||
if !s.err && err != nil {
|
||||
switch {
|
||||
case !s.err && err != nil:
|
||||
t.Errorf("[%d] unexpected error: %s", i, err.Error())
|
||||
} else if s.err && err == nil {
|
||||
case s.err && err == nil:
|
||||
t.Errorf("[%d] expected error not returned", i)
|
||||
} else {
|
||||
default:
|
||||
assert.Equal(t, s.expected, json, "[%d]", i)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,11 +130,12 @@ func TestToJSON(t *testing.T) {
|
||||
tag := s.tag
|
||||
json, err := ToJSON(mockTagReader, &tag)
|
||||
|
||||
if !s.err && err != nil {
|
||||
switch {
|
||||
case !s.err && err != nil:
|
||||
t.Errorf("[%d] unexpected error: %s", i, err.Error())
|
||||
} else if s.err && err == nil {
|
||||
case s.err && err == nil:
|
||||
t.Errorf("[%d] expected error not returned", i)
|
||||
} else {
|
||||
default:
|
||||
assert.Equal(t, s.expected, json, "[%d]", i)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
// FixWindowsPath replaces \ with / in the given path because sometimes the \ isn't recognized as valid on windows
|
||||
func FixWindowsPath(str string) string {
|
||||
if runtime.GOOS == "windows" {
|
||||
return strings.Replace(str, "\\", "/", -1)
|
||||
return strings.ReplaceAll(str, "\\", "/")
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user