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:
SmallCoccinelle
2021-10-18 05:12:40 +02:00
committed by GitHub
parent 3e6e830f45
commit e14bb8432c
51 changed files with 372 additions and 300 deletions

View File

@@ -17,27 +17,31 @@ linters:
- typecheck - typecheck
- unused - unused
- varcheck - varcheck
# Linters added by the stash project # Linters added by the stash project.
# - bodyclose
- dogsled - dogsled
- errorlint - errorlint
# - exhaustive # - exhaustive
- exportloopref - exportloopref
# - gocritic - gocritic
# - goerr113 # - goerr113
- gofmt - gofmt
# - gomnd # - gomnd
# - gosec
# - ifshort # - ifshort
- misspell - misspell
# - nakedret # - nakedret
- noctx - noctx
# - paralleltest
- revive - revive
- rowserrcheck - rowserrcheck
- sqlclosecheck - sqlclosecheck
# Project-specific linter overrides
linters-settings: linters-settings:
gocritic:
disabled-checks:
# Way too many errors to fix regarding comment formatting for now
- commentFormatting
- appendAssign
gofmt: gofmt:
simplify: false simplify: false

View File

@@ -175,10 +175,16 @@ type StashBoxFingerprint {
duration: Int! duration: Int!
} }
"""If neither performer_ids nor performer_names are set, tag all performers"""
input StashBoxBatchPerformerTagInput { input StashBoxBatchPerformerTagInput {
"Stash endpoint to use for the performer tagging"
endpoint: Int! endpoint: Int!
"Fields to exclude when executing the performer tagging"
exclude_fields: [String!] exclude_fields: [String!]
"Refresh performers already tagged by StashBox if true. Only tag performers with no StashBox tagging if false"
refresh: Boolean! refresh: Boolean!
"If set, only tag these performer ids"
performer_ids: [ID!] performer_ids: [ID!]
"If set, only tag these performer names"
performer_names: [String!] performer_names: [String!]
} }

View File

@@ -2,6 +2,7 @@ package api
import ( import (
"context" "context"
"errors"
"sort" "sort"
"strconv" "strconv"
@@ -10,6 +11,11 @@ import (
"github.com/stashapp/stash/pkg/plugin" "github.com/stashapp/stash/pkg/plugin"
) )
var (
ErrNotImplemented = errors.New("not implemented")
ErrNotSupported = errors.New("not supported")
)
type hookExecutor interface { type hookExecutor interface {
ExecutePostHooks(ctx context.Context, id int, hookType plugin.HookTriggerEnum, input interface{}, inputFields []string) ExecutePostHooks(ctx context.Context, id int, hookType plugin.HookTriggerEnum, input interface{}, inputFields []string)
} }

View File

@@ -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 // indicate that image is missing by setting default query param to true
if !hasImage { if !hasImage {
imagePath = imagePath + "?default=true" imagePath += "?default=true"
} }
return &imagePath, nil return &imagePath, nil

View File

@@ -164,18 +164,19 @@ func (r *queryResolver) ScrapeSingleScene(ctx context.Context, source models.Scr
var singleScene *models.ScrapedScene var singleScene *models.ScrapedScene
var err error var err error
if input.SceneID != nil { switch {
case input.SceneID != nil:
var sceneID int var sceneID int
sceneID, err = strconv.Atoi(*input.SceneID) sceneID, err = strconv.Atoi(*input.SceneID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
singleScene, err = manager.GetInstance().ScraperCache.ScrapeScene(*source.ScraperID, sceneID) 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) 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) return manager.GetInstance().ScraperCache.ScrapeSceneQuery(*source.ScraperID, *input.Query)
} else { default:
err = errors.New("scene_id, scene_input or query must be set") 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) { func (r *queryResolver) ScrapeMultiScenes(ctx context.Context, source models.ScraperSourceInput, input models.ScrapeMultiScenesInput) ([][]*models.ScrapedScene, error) {
if source.ScraperID != nil { if source.ScraperID != nil {
return nil, errors.New("not implemented") return nil, ErrNotImplemented
} else if source.StashBoxIndex != nil { } else if source.StashBoxIndex != nil {
client, err := r.getStashBoxClient(*source.StashBoxIndex) client, err := r.getStashBoxClient(*source.StashBoxIndex)
if err != nil { 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 manager.GetInstance().ScraperCache.ScrapePerformerList(*source.ScraperID, *input.Query)
} }
return nil, errors.New("not implemented") return nil, ErrNotImplemented
} else if source.StashBoxIndex != nil { } else if source.StashBoxIndex != nil {
client, err := r.getStashBoxClient(*source.StashBoxIndex) client, err := r.getStashBoxClient(*source.StashBoxIndex)
if err != nil { if err != nil {
@@ -248,12 +249,13 @@ func (r *queryResolver) ScrapeSinglePerformer(ctx context.Context, source models
} }
var ret []*models.StashBoxPerformerQueryResult var ret []*models.StashBoxPerformerQueryResult
if input.PerformerID != nil { switch {
case input.PerformerID != nil:
ret, err = client.FindStashBoxPerformersByNames([]string{*input.PerformerID}) ret, err = client.FindStashBoxPerformersByNames([]string{*input.PerformerID})
} else if input.Query != nil { case input.Query != nil:
ret, err = client.QueryStashBoxPerformer(*input.Query) ret, err = client.QueryStashBoxPerformer(*input.Query)
} else { default:
return nil, errors.New("not implemented") return nil, ErrNotImplemented
} }
if err != nil { 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) { func (r *queryResolver) ScrapeMultiPerformers(ctx context.Context, source models.ScraperSourceInput, input models.ScrapeMultiPerformersInput) ([][]*models.ScrapedPerformer, error) {
if source.ScraperID != nil { if source.ScraperID != nil {
return nil, errors.New("not implemented") return nil, ErrNotImplemented
} else if source.StashBoxIndex != nil { } else if source.StashBoxIndex != nil {
client, err := r.getStashBoxClient(*source.StashBoxIndex) client, err := r.getStashBoxClient(*source.StashBoxIndex)
if err != nil { if err != nil {
@@ -290,17 +292,18 @@ func (r *queryResolver) ScrapeSingleGallery(ctx context.Context, source models.S
var singleGallery *models.ScrapedGallery var singleGallery *models.ScrapedGallery
var err error var err error
if input.GalleryID != nil { switch {
case input.GalleryID != nil:
var galleryID int var galleryID int
galleryID, err = strconv.Atoi(*input.GalleryID) galleryID, err = strconv.Atoi(*input.GalleryID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
singleGallery, err = manager.GetInstance().ScraperCache.ScrapeGallery(*source.ScraperID, galleryID) 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) singleGallery, err = manager.GetInstance().ScraperCache.ScrapeGalleryFragment(*source.ScraperID, *input.GalleryInput)
} else { default:
return nil, errors.New("not implemented") return nil, ErrNotImplemented
} }
if err != nil { if err != nil {
@@ -313,12 +316,12 @@ func (r *queryResolver) ScrapeSingleGallery(ctx context.Context, source models.S
return nil, nil return nil, nil
} else if source.StashBoxIndex != nil { } else if source.StashBoxIndex != nil {
return nil, errors.New("not supported") return nil, ErrNotSupported
} }
return nil, errors.New("scraper_id must be set") 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) { 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
} }

View File

@@ -8,20 +8,20 @@ import (
) )
func getLogLevel(logType string) models.LogLevel { func getLogLevel(logType string) models.LogLevel {
if logType == "progress" { switch logType {
case "progress":
return models.LogLevelProgress return models.LogLevelProgress
} else if logType == "debug" { case "debug":
return models.LogLevelDebug return models.LogLevelDebug
} else if logType == "info" { case "info":
return models.LogLevelInfo return models.LogLevelInfo
} else if logType == "warn" { case "warn":
return models.LogLevelWarning return models.LogLevelWarning
} else if logType == "error" { case "error":
return models.LogLevelError return models.LogLevelError
} default:
// default to debug
return models.LogLevelDebug return models.LogLevelDebug
}
} }
func logEntriesFromLogItems(logItems []logger.LogItem) []*models.LogEntry { func logEntriesFromLogItems(logItems []logger.LogItem) []*models.LogEntry {

View File

@@ -79,7 +79,7 @@ func generateTestPaths(testName, ext string) (scenePatterns []string, falseScene
// add test cases for intra-name separators // add test cases for intra-name separators
for _, separator := range testSeparators { for _, separator := range testSeparators {
if separator != " " { if separator != " " {
scenePatterns = append(scenePatterns, generateNamePatterns(strings.Replace(testName, " ", separator, -1), separator, ext)...) scenePatterns = append(scenePatterns, generateNamePatterns(strings.ReplaceAll(testName, " ", separator), separator, ext)...)
} }
} }

View File

@@ -22,7 +22,9 @@ func WithTxn(fn func(tx *sqlx.Tx) error) error {
logger.Warnf("failure when performing transaction rollback: %v", err) logger.Warnf("failure when performing transaction rollback: %v", err)
} }
panic(p) panic(p)
} else if err != nil { }
if err != nil {
// something went wrong, rollback // something went wrong, rollback
if err := tx.Rollback(); err != nil { if err := tx.Rollback(); err != nil {
logger.Warnf("failure when performing transaction rollback: %v", err) logger.Warnf("failure when performing transaction rollback: %v", err)

View File

@@ -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 // the spec on eventing but hasn't been completed as I have nothing to
// test it with. // test it with.
service := me.services["ContentDirectory"] 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")) urls := upnp.ParseCallbackURLs(r.Header.Get("CALLBACK"))
var timeout int var timeout int
fmt.Sscanf(r.Header.Get("TIMEOUT"), "Second-%d", &timeout) 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) time.Sleep(100 * time.Millisecond)
me.contentDirectoryInitialEvent(r.Context(), urls, sid) me.contentDirectoryInitialEvent(r.Context(), urls, sid)
}() }()
} else if r.Method == "SUBSCRIBE" { case r.Method == "SUBSCRIBE":
http.Error(w, "meh", http.StatusPreconditionFailed) http.Error(w, "meh", http.StatusPreconditionFailed)
} else { default:
logger.Debugf("unhandled event method: %s", r.Method) logger.Debugf("unhandled event method: %s", r.Method)
} }
} }

View File

@@ -48,7 +48,7 @@ func (p *scenePager) getPages(r models.ReaderRepository, total int) ([]interface
sceneTitle = sceneTitle[0:3] 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)) objs = append(objs, makeStorageFolder(p.getPageID(page), title, p.parentID))

View File

@@ -116,7 +116,7 @@ func IsValidCodec(codecName string, supportedCodecs []string) bool {
return false 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 // 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 // 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 return true
} }
for _, c := range ValidCodecs { for _, c := range validCodecs {
if c == audio { if c == audio {
return true return true
} }

View File

@@ -6,17 +6,22 @@ import (
"fmt" "fmt"
) )
var ErrUnsupportedFormat = errors.New("unsupported image format")
func (e *Encoder) ImageThumbnail(image *bytes.Buffer, format *string, maxDimensions int, path string) ([]byte, error) { 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 // ffmpeg spends a long sniffing image format when data is piped through stdio, so we pass the format explicitly instead
ffmpegformat := "" ffmpegformat := ""
if format != nil && *format == "jpeg" { if format == nil {
return nil, ErrUnsupportedFormat
}
switch *format {
case "jpeg":
ffmpegformat = "mjpeg" ffmpegformat = "mjpeg"
} else if format != nil && *format == "png" { case "png":
ffmpegformat = "png_pipe" ffmpegformat = "png_pipe"
} else if format != nil && *format == "webp" { case "webp":
ffmpegformat = "webp_pipe" ffmpegformat = "webp_pipe"
} else {
return nil, errors.New("unsupported image format")
} }
args := []string{ args := []string{

View File

@@ -49,7 +49,7 @@ func IsZipPath(p string) bool {
// path separators within zip files. It returns the original provided path // path separators within zip files. It returns the original provided path
// if it does not contain the zip file separator character. // if it does not contain the zip file separator character.
func ZipPathDisplayName(path string) string { func ZipPathDisplayName(path string) string {
return strings.Replace(path, zipSeparator, "/", -1) return strings.ReplaceAll(path, zipSeparator, "/")
} }
func ZipFilePath(path string) (zipFilename, filename string) { func ZipFilePath(path string) (zipFilename, filename string) {

View File

@@ -107,11 +107,12 @@ func TestToJSON(t *testing.T) {
gallery := s.input gallery := s.input
json, err := ToBasicJSON(&gallery) json, err := ToBasicJSON(&gallery)
if !s.err && err != nil { switch {
case !s.err && err != nil:
t.Errorf("[%d] unexpected error: %s", i, err.Error()) 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) t.Errorf("[%d] expected error not returned", i)
} else { default:
assert.Equal(t, s.expected, json, "[%d]", i) assert.Equal(t, s.expected, json, "[%d]", i)
} }
} }
@@ -162,11 +163,12 @@ func TestGetStudioName(t *testing.T) {
gallery := s.input gallery := s.input
json, err := GetStudioName(mockStudioReader, &gallery) 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()) 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) t.Errorf("[%d] expected error not returned", i)
} else { default:
assert.Equal(t, s.expected, json, "[%d]", i) assert.Equal(t, s.expected, json, "[%d]", i)
} }
} }

View File

@@ -142,9 +142,7 @@ func (scanner *Scanner) ScanNew(file file.SourceFile) (retGallery *models.Galler
isUpdatedGallery = true isUpdatedGallery = true
} }
} else { } else if scanner.hasImages(path) { // don't create gallery if it has no images
// don't create gallery if it has no images
if scanner.hasImages(path) {
currentTime := time.Now() currentTime := time.Now()
g = &models.Gallery{ g = &models.Gallery{
@@ -174,7 +172,6 @@ func (scanner *Scanner) ScanNew(file file.SourceFile) (retGallery *models.Galler
scanImages = true scanImages = true
isNewGallery = true isNewGallery = true
} }
}
return nil return nil
}); err != nil { }); err != nil {

View File

@@ -165,11 +165,12 @@ func TestGetStudioName(t *testing.T) {
image := s.input image := s.input
json, err := GetStudioName(mockStudioReader, &image) 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()) 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) t.Errorf("[%d] expected error not returned", i)
} else { default:
assert.Equal(t, s.expected, json, "[%d]", i) assert.Equal(t, s.expected, json, "[%d]", i)
} }
} }

View File

@@ -2,6 +2,7 @@ package image
import ( import (
"bytes" "bytes"
"errors"
"os/exec" "os/exec"
"runtime" "runtime"
"sync" "sync"
@@ -13,6 +14,8 @@ import (
var vipsPath string var vipsPath string
var once sync.Once var once sync.Once
var ErrUnsupportedFormat = errors.New("unsupported image format")
type ThumbnailEncoder struct { type ThumbnailEncoder struct {
ffmpeg ffmpeg.Encoder ffmpeg ffmpeg.Encoder
vips *vipsEncoder vips *vipsEncoder

View File

@@ -61,11 +61,12 @@ func (p *Progress) SetProcessed(processed int) {
} }
func (p *Progress) calculatePercent() { func (p *Progress) calculatePercent() {
if p.total <= 0 { switch {
case p.total <= 0:
p.percent = ProgressIndefinite p.percent = ProgressIndefinite
} else if p.processed < 0 { case p.processed < 0:
p.percent = 0 p.percent = 0
} else { default:
p.percent = float64(p.processed) / float64(p.total) p.percent = float64(p.processed) / float64(p.total)
if p.percent > 1 { if p.percent > 1 {
p.percent = 1 p.percent = 1

View File

@@ -80,13 +80,14 @@ func SetLogLevel(level string) {
func logLevelFromString(level string) logrus.Level { func logLevelFromString(level string) logrus.Level {
ret := logrus.InfoLevel ret := logrus.InfoLevel
if level == "Debug" { switch level {
case "Debug":
ret = logrus.DebugLevel ret = logrus.DebugLevel
} else if level == "Warning" { case "Warning":
ret = logrus.WarnLevel ret = logrus.WarnLevel
} else if level == "Error" { case "Error":
ret = logrus.ErrorLevel ret = logrus.ErrorLevel
} else if level == "Trace" { case "Trace":
ret = logrus.TraceLevel ret = logrus.TraceLevel
} }

View File

@@ -160,16 +160,14 @@ func (log *PluginLogger) HandleStderrLine(line string) {
p, err := strconv.ParseFloat(ll, 64) p, err := strconv.ParseFloat(ll, 64)
if err != nil { if err != nil {
Errorf("Error parsing progress value '%s': %s", ll, err.Error()) Errorf("Error parsing progress value '%s': %s", ll, err.Error())
} else { } else if log.ProgressChan != nil { // only pass progress through if channel present
// only pass progress through if channel present
if log.ProgressChan != nil {
// don't block on this // don't block on this
select { select {
case log.ProgressChan <- p: case log.ProgressChan <- p:
default: default:
} }
} }
}
} }
} }

View File

@@ -1,7 +1,6 @@
package config package config
import ( import (
"errors"
"fmt" "fmt"
"os" "os"
"path/filepath" "path/filepath"
@@ -175,6 +174,16 @@ func (e MissingConfigError) Error() string {
return fmt.Sprintf("missing the following mandatory settings: %s", strings.Join(e.missingFields, ", ")) 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 { type Instance struct {
cpuProfilePath string cpuProfilePath string
isNewSystem bool isNewSystem bool
@@ -680,29 +689,30 @@ func (i *Instance) ValidateCredentials(username string, password string) bool {
return username == authUser && err == nil return username == authUser && err == nil
} }
var stashBoxRe = regexp.MustCompile("^http.*graphql$")
func (i *Instance) ValidateStashBoxes(boxes []*models.StashBoxInput) error { func (i *Instance) ValidateStashBoxes(boxes []*models.StashBoxInput) error {
isMulti := len(boxes) > 1 isMulti := len(boxes) > 1
re, err := regexp.Compile("^http.*graphql$") for _, box := range boxes {
if err != nil { // Validate each stash-box configuration field, return on error
return errors.New("failure to generate regular expression") if box.APIKey == "" {
return &StashBoxError{msg: "API Key cannot be blank"}
} }
for _, box := range boxes { if box.Endpoint == "" {
if box.APIKey == "" { return &StashBoxError{msg: "endpoint cannot be blank"}
//lint:ignore ST1005 Stash-box is a name }
return errors.New("Stash-box API Key cannot be blank")
} else if box.Endpoint == "" { if !stashBoxRe.Match([]byte(box.Endpoint)) {
//lint:ignore ST1005 Stash-box is a name return &StashBoxError{msg: "endpoint is invalid"}
return errors.New("Stash-box Endpoint cannot be blank") }
} else if !re.Match([]byte(box.Endpoint)) {
//lint:ignore ST1005 Stash-box is a name if isMulti && box.Name == "" {
return errors.New("Stash-box Endpoint is invalid") return &StashBoxError{msg: "name cannot be blank"}
} else if isMulti && box.Name == "" {
//lint:ignore ST1005 Stash-box is a name
return errors.New("Stash-box Name cannot be blank")
} }
} }
return nil return nil
} }

View File

@@ -44,7 +44,7 @@ func (s *DownloadStore) RegisterFile(fp string, contentType string, keep bool) s
for generate && a < attempts { for generate && a < attempts {
hash = utils.GenerateRandomKey(keyLength) hash = utils.GenerateRandomKey(keyLength)
_, generate = s.m[hash] _, generate = s.m[hash]
a = a + 1 a++
} }
s.m[hash] = &storeFile{ s.m[hash] = &storeFile{

View File

@@ -94,17 +94,15 @@ func Initialize() *singleton {
if err != nil { if err != nil {
panic(fmt.Sprintf("error initializing configuration: %s", err.Error())) panic(fmt.Sprintf("error initializing configuration: %s", err.Error()))
} else { } else if err := instance.PostInit(ctx); err != nil {
if err := instance.PostInit(ctx); err != nil {
panic(err) panic(err)
} }
}
initSecurity(cfg) initSecurity(cfg)
} else { } else {
cfgFile := cfg.GetConfigFile() cfgFile := cfg.GetConfigFile()
if cfgFile != "" { if cfgFile != "" {
cfgFile = cfgFile + " " cfgFile += " "
} }
// create temporary session store - this will be re-initialised // create temporary session store - this will be re-initialised

View File

@@ -616,7 +616,13 @@ func (s *singleton) StashBoxBatchPerformerTag(ctx context.Context, input models.
var tasks []StashBoxPerformerTagTask 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 { if err := s.TxnManager.WithReadTxn(context.TODO(), func(r models.ReaderRepository) error {
performerQuery := r.Performer() 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 { if err := s.TxnManager.WithReadTxn(context.TODO(), func(r models.ReaderRepository) error {
performerQuery := r.Performer() performerQuery := r.Performer()
var performers []*models.Performer var performers []*models.Performer

View File

@@ -330,7 +330,7 @@ func (t *autoTagFilesTask) makeSceneFilter() *models.SceneFilterType {
for _, p := range t.paths { for _, p := range t.paths {
if !strings.HasSuffix(p, sep) { if !strings.HasSuffix(p, sep) {
p = p + sep p += sep
} }
if ret.Path == nil { if ret.Path == nil {
@@ -360,7 +360,7 @@ func (t *autoTagFilesTask) makeImageFilter() *models.ImageFilterType {
for _, p := range t.paths { for _, p := range t.paths {
if !strings.HasSuffix(p, sep) { if !strings.HasSuffix(p, sep) {
p = p + sep p += sep
} }
if ret.Path == nil { if ret.Path == nil {
@@ -397,7 +397,7 @@ func (t *autoTagFilesTask) makeGalleryFilter() *models.GalleryFilterType {
for _, p := range t.paths { for _, p := range t.paths {
if !strings.HasSuffix(p, sep) { if !strings.HasSuffix(p, sep) {
p = p + sep p += sep
} }
if ret.Path == nil { if ret.Path == nil {

View File

@@ -209,17 +209,18 @@ func (j *ScanJob) doesPathExist(path string) bool {
ret := false ret := false
txnErr := j.txnManager.WithReadTxn(context.TODO(), func(r models.ReaderRepository) error { txnErr := j.txnManager.WithReadTxn(context.TODO(), func(r models.ReaderRepository) error {
if utils.MatchExtension(path, gExt) { switch {
gallery, _ := r.Gallery().FindByPath(path) case utils.MatchExtension(path, gExt):
if gallery != nil { g, _ := r.Gallery().FindByPath(path)
if g != nil {
ret = true ret = true
} }
} else if utils.MatchExtension(path, vidExt) { case utils.MatchExtension(path, vidExt):
s, _ := r.Scene().FindByPath(path) s, _ := r.Scene().FindByPath(path)
if s != nil { if s != nil {
ret = true ret = true
} }
} else if utils.MatchExtension(path, imgExt) { case utils.MatchExtension(path, imgExt):
i, _ := r.Image().FindByPath(path) i, _ := r.Image().FindByPath(path)
if i != nil { if i != nil {
ret = true ret = true
@@ -259,16 +260,21 @@ func (t *ScanTask) Start(ctx context.Context) {
var s *models.Scene var s *models.Scene
path := t.file.Path() path := t.file.Path()
t.progress.ExecuteTask("Scanning "+path, func() { t.progress.ExecuteTask("Scanning "+path, func() {
if isGallery(path) { switch {
case isGallery(path):
t.scanGallery(ctx) t.scanGallery(ctx)
} else if isVideo(path) { case isVideo(path):
s = t.scanScene() s = t.scanScene()
} else if isImage(path) { case isImage(path):
t.scanImage() t.scanImage()
} }
}) })
if s != nil { if s == nil {
return
}
// Handle the case of a scene
iwg := sizedwaitgroup.New(2) iwg := sizedwaitgroup.New(2)
if t.GenerateSprite { if t.GenerateSprite {
@@ -332,7 +338,6 @@ func (t *ScanTask) Start(ctx context.Context) {
} }
iwg.Wait() iwg.Wait()
}
} }
func walkFilesToScan(s *models.StashConfig, f filepath.WalkFunc) error { func walkFilesToScan(s *models.StashConfig, f filepath.WalkFunc) error {

View File

@@ -18,7 +18,7 @@ func getPathQueryRegex(name string) string {
// handle path separators // handle path separators
const separator = `[` + separatorChars + `]` const separator = `[` + separatorChars + `]`
ret := strings.Replace(name, " ", separator+"*", -1) ret := strings.ReplaceAll(name, " ", separator+"*")
ret = `(?:^|_|[^\w\d])` + ret + `(?:$|_|[^\w\d])` ret = `(?:^|_|[^\w\d])` + ret + `(?:$|_|[^\w\d])`
return ret return ret
} }
@@ -66,7 +66,7 @@ func nameMatchesPath(name, path string) bool {
// handle path separators // handle path separators
const separator = `[` + separatorChars + `]` const separator = `[` + separatorChars + `]`
reStr := strings.Replace(name, " ", separator+"*", -1) reStr := strings.ReplaceAll(name, " ", separator+"*")
reStr = `(?:^|_|[^\w\d])` + reStr + `(?:$|_|[^\w\d])` reStr = `(?:^|_|[^\w\d])` + reStr + `(?:$|_|[^\w\d])`
re := regexp.MustCompile(reStr) re := regexp.MustCompile(reStr)
@@ -185,7 +185,7 @@ func scenePathsFilter(paths []string) *models.SceneFilterType {
or = newOr or = newOr
if !strings.HasSuffix(p, sep) { if !strings.HasSuffix(p, sep) {
p = p + sep p += sep
} }
or.Path = &models.StringCriterionInput{ or.Path = &models.StringCriterionInput{
@@ -249,7 +249,7 @@ func imagePathsFilter(paths []string) *models.ImageFilterType {
or = newOr or = newOr
if !strings.HasSuffix(p, sep) { if !strings.HasSuffix(p, sep) {
p = p + sep p += sep
} }
or.Path = &models.StringCriterionInput{ or.Path = &models.StringCriterionInput{
@@ -313,7 +313,7 @@ func galleryPathsFilter(paths []string) *models.GalleryFilterType {
or = newOr or = newOr
if !strings.HasSuffix(p, sep) { if !strings.HasSuffix(p, sep) {
p = p + sep p += sep
} }
or.Path = &models.StringCriterionInput{ or.Path = &models.StringCriterionInput{

View File

@@ -13,16 +13,14 @@ type File struct {
// GetHash returns the hash of the scene, based on the hash algorithm provided. If // 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. // hash algorithm is MD5, then Checksum is returned. Otherwise, OSHash is returned.
func (s File) GetHash(hashAlgorithm HashAlgorithm) string { func (s File) GetHash(hashAlgorithm HashAlgorithm) string {
var ret string switch hashAlgorithm {
if hashAlgorithm == HashAlgorithmMd5 { case HashAlgorithmMd5:
ret = s.Checksum return s.Checksum
} else if hashAlgorithm == HashAlgorithmOshash { case HashAlgorithmOshash:
ret = s.OSHash return s.OSHash
} else { default:
panic("unknown hash algorithm") panic("unknown hash algorithm")
} }
return ret
} }
func (s File) Equal(o File) bool { func (s File) Equal(o File) bool {

View File

@@ -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) logger.Warnf("error while trying to roll back transaction: %v", err)
} }
panic(p) panic(p)
} else if err != nil { }
if err != nil {
// something went wrong, rollback // something went wrong, rollback
if err := txn.Rollback(); err != nil { if err := txn.Rollback(); err != nil {
logger.Warnf("error while trying to roll back transaction: %v", err) 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) logger.Warnf("error while trying to roll back RO transaction: %v", err)
} }
panic(p) panic(p)
} else if err != nil { }
if err != nil {
// something went wrong, rollback // something went wrong, rollback
if err := txn.Rollback(); err != nil { if err := txn.Rollback(); err != nil {
logger.Warnf("error while trying to roll back RO transaction: %v", err) logger.Warnf("error while trying to roll back RO transaction: %v", err)

View File

@@ -213,11 +213,12 @@ func TestToJSON(t *testing.T) {
movie := s.movie movie := s.movie
json, err := ToJSON(mockMovieReader, mockStudioReader, &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()) 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) t.Errorf("[%d] expected error not returned", i)
} else { default:
assert.Equal(t, s.expected, json, "[%d]", i) assert.Equal(t, s.expected, json, "[%d]", i)
} }
} }

View File

@@ -201,11 +201,12 @@ func TestToJSON(t *testing.T) {
tag := s.input tag := s.input
json, err := ToJSON(mockPerformerReader, &tag) 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()) 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) t.Errorf("[%d] expected error not returned", i)
} else { default:
assert.Equal(t, s.expected, json, "[%d]", i) assert.Equal(t, s.expected, json, "[%d]", i)
} }
} }

View File

@@ -173,7 +173,7 @@ func (c Config) getExecCommand(task *OperationConfig) []string {
continue continue
} }
ret[i] = strings.Replace(arg, "{pluginDir}", dir, -1) ret[i] = strings.ReplaceAll(arg, "{pluginDir}", dir)
} }
return ret return ret

View File

@@ -226,11 +226,12 @@ func TestToJSON(t *testing.T) {
scene := s.input scene := s.input
json, err := ToBasicJSON(mockSceneReader, &scene) 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()) 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) t.Errorf("[%d] expected error not returned", i)
} else { default:
assert.Equal(t, s.expected, json, "[%d]", i) assert.Equal(t, s.expected, json, "[%d]", i)
} }
} }
@@ -283,11 +284,12 @@ func TestGetStudioName(t *testing.T) {
scene := s.input scene := s.input
json, err := GetStudioName(mockStudioReader, &scene) 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()) 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) t.Errorf("[%d] expected error not returned", i)
} else { default:
assert.Equal(t, s.expected, json, "[%d]", i) assert.Equal(t, s.expected, json, "[%d]", i)
} }
} }
@@ -343,11 +345,12 @@ func TestGetTagNames(t *testing.T) {
scene := s.input scene := s.input
json, err := GetTagNames(mockTagReader, &scene) 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()) 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) t.Errorf("[%d] expected error not returned", i)
} else { default:
assert.Equal(t, s.expected, json, "[%d]", i) assert.Equal(t, s.expected, json, "[%d]", i)
} }
} }
@@ -435,11 +438,12 @@ func TestGetSceneMoviesJSON(t *testing.T) {
scene := s.input scene := s.input
json, err := GetSceneMoviesJSON(mockMovieReader, mockSceneReader, &scene) 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()) 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) t.Errorf("[%d] expected error not returned", i)
} else { default:
assert.Equal(t, s.expected, json, "[%d]", i) assert.Equal(t, s.expected, json, "[%d]", i)
} }
} }
@@ -617,11 +621,12 @@ func TestGetSceneMarkersJSON(t *testing.T) {
scene := s.input scene := s.input
json, err := GetSceneMarkersJSON(mockMarkerReader, mockTagReader, &scene) 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()) 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) t.Errorf("[%d] expected error not returned", i)
} else { default:
assert.Equal(t, s.expected, json, "[%d]", i) assert.Equal(t, s.expected, json, "[%d]", i)
} }
} }

View File

@@ -396,11 +396,13 @@ func (i *Importer) Name() string {
func (i *Importer) FindExistingID() (*int, error) { func (i *Importer) FindExistingID() (*int, error) {
var existing *models.Scene var existing *models.Scene
var err error var err error
if i.FileNamingAlgorithm == models.HashAlgorithmMd5 {
switch i.FileNamingAlgorithm {
case models.HashAlgorithmMd5:
existing, err = i.ReaderWriter.FindByChecksum(i.Input.Checksum) 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) existing, err = i.ReaderWriter.FindByOSHash(i.Input.OSHash)
} else { default:
panic("unknown file naming algorithm") panic("unknown file naming algorithm")
} }

View File

@@ -97,7 +97,7 @@ func (c *autotagSceneScraper) scrapeByScene(scene *models.Scene) (*models.Scrape
var ret *models.ScrapedScene var ret *models.ScrapedScene
// populate performers, studio and tags based on scene path // 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 path := scene.Path
performers, err := c.matchPerformers(path, r.Performer()) performers, err := c.matchPerformers(path, r.Performer())
if err != nil { if err != nil {
@@ -150,7 +150,7 @@ func (c *autotagGalleryScraper) scrapeByGallery(gallery *models.Gallery) (*model
var ret *models.ScrapedGallery var ret *models.ScrapedGallery
// populate performers, studio and tags based on scene path // 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 path := gallery.Path.String
performers, err := c.matchPerformers(path, r.Performer()) performers, err := c.matchPerformers(path, r.Performer())
if err != nil { if err != nil {

View File

@@ -128,7 +128,7 @@ func (s *jsonScraper) scrapePerformersByName(name string) ([]*models.ScrapedPerf
escapedName := url.QueryEscape(name) escapedName := url.QueryEscape(name)
url := s.scraper.QueryURL url := s.scraper.QueryURL
url = strings.Replace(url, placeholder, escapedName, -1) url = strings.ReplaceAll(url, placeholder, escapedName)
doc, err := s.loadURL(context.TODO(), url) doc, err := s.loadURL(context.TODO(), url)
@@ -157,7 +157,7 @@ func (s *jsonScraper) scrapeScenesByName(name string) ([]*models.ScrapedScene, e
escapedName := url.QueryEscape(name) escapedName := url.QueryEscape(name)
url := s.scraper.QueryURL url := s.scraper.QueryURL
url = strings.Replace(url, placeholder, escapedName, -1) url = strings.ReplaceAll(url, placeholder, escapedName)
doc, err := s.loadURL(context.TODO(), url) doc, err := s.loadURL(context.TODO(), url)

View File

@@ -32,7 +32,7 @@ func (s mappedConfig) applyCommon(c commonMappedConfig, src string) string {
ret := src ret := src
for commonKey, commonVal := range c { for commonKey, commonVal := range c {
ret = strings.Replace(ret, commonKey, commonVal, -1) ret = strings.ReplaceAll(ret, commonKey, commonVal)
} }
return ret return ret
@@ -486,7 +486,7 @@ func (p *postProcessLbToKg) Apply(value string, q mappedQuery) string {
const lb_in_kg = 0.45359237 const lb_in_kg = 0.45359237
w, err := strconv.ParseFloat(value, 64) w, err := strconv.ParseFloat(value, 64)
if err == nil { if err == nil {
w = w * lb_in_kg w *= lb_in_kg
value = strconv.Itoa(int(math.Round(w))) value = strconv.Itoa(int(math.Round(w)))
} }
return value return value
@@ -576,7 +576,7 @@ type mappedScraperAttrConfig struct {
postProcessActions []postProcessAction postProcessActions []postProcessAction
// deprecated: use PostProcess instead // Deprecated: use PostProcess instead
ParseDate string `yaml:"parseDate"` ParseDate string `yaml:"parseDate"`
Replace mappedRegexConfigs `yaml:"replace"` Replace mappedRegexConfigs `yaml:"replace"`
SubScraper *mappedScraperAttrConfig `yaml:"subScraper"` SubScraper *mappedScraperAttrConfig `yaml:"subScraper"`

View File

@@ -69,7 +69,7 @@ func (p queryURLParameters) applyReplacements(r queryURLReplacements) {
func (p queryURLParameters) constructURL(url string) string { func (p queryURLParameters) constructURL(url string) string {
ret := url ret := url
for k, v := range p { for k, v := range p {
ret = strings.Replace(ret, "{"+k+"}", v, -1) ret = strings.ReplaceAll(ret, "{"+k+"}", v)
} }
return ret return ret

View File

@@ -479,11 +479,12 @@ func formatCareerLength(start, end *int) *string {
} }
var ret string var ret string
if end == nil { switch {
case end == nil:
ret = fmt.Sprintf("%d -", *start) ret = fmt.Sprintf("%d -", *start)
} else if start == nil { case start == nil:
ret = fmt.Sprintf("- %d", *end) ret = fmt.Sprintf("- %d", *end)
} else { default:
ret = fmt.Sprintf("%d - %d", *start, *end) ret = fmt.Sprintf("%d - %d", *start, *end)
} }

View File

@@ -109,7 +109,7 @@ func (s *xpathScraper) scrapePerformersByName(name string) ([]*models.ScrapedPer
escapedName := url.QueryEscape(name) escapedName := url.QueryEscape(name)
url := s.scraper.QueryURL url := s.scraper.QueryURL
url = strings.Replace(url, placeholder, escapedName, -1) url = strings.ReplaceAll(url, placeholder, escapedName)
doc, err := s.loadURL(context.TODO(), url) doc, err := s.loadURL(context.TODO(), url)
@@ -138,7 +138,7 @@ func (s *xpathScraper) scrapeScenesByName(name string) ([]*models.ScrapedScene,
escapedName := url.QueryEscape(name) escapedName := url.QueryEscape(name)
url := s.scraper.QueryURL url := s.scraper.QueryURL
url = strings.Replace(url, placeholder, escapedName, -1) url = strings.ReplaceAll(url, placeholder, escapedName)
doc, err := s.loadURL(context.TODO(), url) doc, err := s.loadURL(context.TODO(), url)

View File

@@ -70,12 +70,10 @@ func CheckAllowPublicWithoutAuth(c *config.Instance, r *http.Request) error {
return UntrustedProxyError(requestIP) return UntrustedProxyError(requestIP)
} }
} }
} else { } else if !isLocalIP(requestIP) { // request was not proxied
// request was not proxied
if !isLocalIP(requestIP) {
return ExternalAccessError(requestIP) return ExternalAccessError(requestIP)
} }
}
} }
return nil return nil

View File

@@ -210,11 +210,9 @@ func (f *filterBuilder) getSubFilterClause(clause, subFilterClause string) strin
var op string var op string
if len(ret) > 0 { if len(ret) > 0 {
op = " " + f.subFilterOp + " " op = " " + f.subFilterOp + " "
} else { } else if f.subFilterOp == notOp {
if f.subFilterOp == notOp {
op = "NOT " op = "NOT "
} }
}
ret += op + "(" + subFilterClause + ")" ret += op + "(" + subFilterClause + ")"
} }
@@ -436,16 +434,18 @@ func (m *joinedMultiCriterionHandlerBuilder) handler(criterion *models.MultiCrit
whereClause := "" whereClause := ""
havingClause := "" havingClause := ""
if criterion.Modifier == models.CriterionModifierIncludes {
switch criterion.Modifier {
case models.CriterionModifierIncludes:
// includes any of the provided ids // includes any of the provided ids
m.addJoinTable(f) m.addJoinTable(f)
whereClause = fmt.Sprintf("%s.%s IN %s", joinAlias, m.foreignFK, getInBinding(len(criterion.Value))) 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 // includes all of the provided ids
m.addJoinTable(f) m.addJoinTable(f)
whereClause = fmt.Sprintf("%s.%s IN %s", joinAlias, m.foreignFK, getInBinding(len(criterion.Value))) 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)) 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 // excludes all of the provided ids
// need to use actual join table name for this // need to use actual join table name for this
// <primaryTable>.id NOT IN (select <joinTable>.<primaryFK> from <joinTable> where <joinTable>.<foreignFK> in <values>) // <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) { 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)) 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.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))) 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)) f.addWhere(fmt.Sprintf("%s.%s IS NULL", table, idColumn))
} }
} }

View File

@@ -418,13 +418,14 @@ func galleryAverageResolutionCriterionHandler(qb *galleryQueryBuilder, resolutio
const widthHeight = "avg(MIN(images.width, images.height))" 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)) 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)) 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)) 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)) f.addHaving(fmt.Sprintf("%s > %d", widthHeight, max))
} }
} }

View File

@@ -225,12 +225,13 @@ func moviePerformersCriterionHandler(qb *movieQueryBuilder, performers *models.M
)`, args...) )`, args...)
f.addJoin("movies_performers", "", "movies.id = movies_performers.movie_id") 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") 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.addWhere("movies_performers.performer_id IS NOT NULL")
f.addHaving("COUNT(DISTINCT movies_performers.performer_id) = ?", len(performers.Value)) 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") f.addWhere("movies_performers.performer_id IS NULL")
} }
} }

View File

@@ -443,13 +443,14 @@ func performerStudiosCriterionHandler(qb *performerQueryBuilder, studios *models
if studios != nil { if studios != nil {
var clauseCondition string 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 // return performers who appear in scenes/images/galleries with any of the given studios
clauseCondition = "NOT" 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 // exclude performers who appear in scenes/images/galleries with any of the given studios
clauseCondition = "" clauseCondition = ""
} else { default:
return return
} }

View File

@@ -482,13 +482,14 @@ func resolutionCriterionHandler(resolution *models.ResolutionCriterionInput, hei
widthHeight := fmt.Sprintf("MIN(%s, %s)", widthColumn, heightColumn) 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)) 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)) 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)) 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)) f.addWhere(fmt.Sprintf("%s > %d", widthHeight, max))
} }
} }

View File

@@ -106,13 +106,13 @@ func (qb *sceneMarkerQueryBuilder) CountByTagID(tagID int) (int, error) {
func (qb *sceneMarkerQueryBuilder) GetMarkerStrings(q *string, sort *string) ([]*models.MarkerStringsResultType, 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" query := "SELECT count(*) as `count`, scene_markers.id as id, scene_markers.title as title FROM scene_markers"
if q != nil { 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" { if sort != nil && *sort == "count" {
query = query + " ORDER BY `count` DESC" query += " ORDER BY `count` DESC"
} else { } else {
query = query + " ORDER BY title ASC" query += " ORDER BY title ASC"
} }
var args []interface{} var args []interface{}
return qb.queryMarkerStringsResultType(query, args) return qb.queryMarkerStringsResultType(query, args)

View File

@@ -58,14 +58,15 @@ func getSort(sort string, direction string, tableName string) string {
const randomSeedPrefix = "random_" const randomSeedPrefix = "random_"
if strings.HasSuffix(sort, "_count") { switch {
case strings.HasSuffix(sort, "_count"):
var relationTableName = strings.TrimSuffix(sort, "_count") // TODO: pluralize? var relationTableName = strings.TrimSuffix(sort, "_count") // TODO: pluralize?
colName := getColumn(relationTableName, "id") colName := getColumn(relationTableName, "id")
return " ORDER BY COUNT(distinct " + colName + ") " + direction return " ORDER BY COUNT(distinct " + colName + ") " + direction
} else if strings.Compare(sort, "filesize") == 0 { case strings.Compare(sort, "filesize") == 0:
colName := getColumn(tableName, "size") colName := getColumn(tableName, "size")
return " ORDER BY cast(" + colName + " as integer) " + direction 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 // seed as a parameter from the UI
// turn the provided seed into a float // turn the provided seed into a float
seedStr := "0." + sort[len(randomSeedPrefix):] seedStr := "0." + sort[len(randomSeedPrefix):]
@@ -75,9 +76,9 @@ func getSort(sort string, direction string, tableName string) string {
seed = randomSortFloat seed = randomSortFloat
} }
return getRandomSort(tableName, direction, seed) return getRandomSort(tableName, direction, seed)
} else if strings.Compare(sort, "random") == 0 { case strings.Compare(sort, "random") == 0:
return getRandomSort(tableName, direction, randomSortFloat) return getRandomSort(tableName, direction, randomSortFloat)
} else { default:
colName := getColumn(tableName, sort) colName := getColumn(tableName, sort)
var additional string var additional string
if tableName == "scenes" { 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) { func getMultiCriterionClause(primaryTable, foreignTable, joinTable, primaryFK, foreignFK string, criterion *models.MultiCriterionInput) (string, string) {
whereClause := "" whereClause := ""
havingClause := "" havingClause := ""
if criterion.Modifier == models.CriterionModifierIncludes { switch criterion.Modifier {
case models.CriterionModifierIncludes:
// includes any of the provided ids // includes any of the provided ids
whereClause = foreignTable + ".id IN " + getInBinding(len(criterion.Value)) whereClause = foreignTable + ".id IN " + getInBinding(len(criterion.Value))
} else if criterion.Modifier == models.CriterionModifierIncludesAll { case models.CriterionModifierIncludesAll:
// includes all of the provided ids // includes all of the provided ids
whereClause = foreignTable + ".id IN " + getInBinding(len(criterion.Value)) whereClause = foreignTable + ".id IN " + getInBinding(len(criterion.Value))
havingClause = "count(distinct " + foreignTable + ".id) IS " + strconv.Itoa(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 // excludes all of the provided ids
if joinTable != "" { if joinTable != "" {
whereClause = primaryTable + ".id not in (select " + joinTable + "." + primaryFK + " from " + joinTable + " where " + joinTable + "." + foreignFK + " in " + getInBinding(len(criterion.Value)) + ")" whereClause = primaryTable + ".id not in (select " + joinTable + "." + primaryFK + " from " + joinTable + " where " + joinTable + "." + foreignFK + " in " + getInBinding(len(criterion.Value)) + ")"

View File

@@ -184,11 +184,12 @@ func TestToJSON(t *testing.T) {
studio := s.input studio := s.input
json, err := ToJSON(mockStudioReader, &studio) 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()) 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) t.Errorf("[%d] expected error not returned", i)
} else { default:
assert.Equal(t, s.expected, json, "[%d]", i) assert.Equal(t, s.expected, json, "[%d]", i)
} }
} }

View File

@@ -130,11 +130,12 @@ func TestToJSON(t *testing.T) {
tag := s.tag tag := s.tag
json, err := ToJSON(mockTagReader, &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()) 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) t.Errorf("[%d] expected error not returned", i)
} else { default:
assert.Equal(t, s.expected, json, "[%d]", i) assert.Equal(t, s.expected, json, "[%d]", i)
} }
} }

View File

@@ -8,7 +8,7 @@ import (
// FixWindowsPath replaces \ with / in the given path because sometimes the \ isn't recognized as valid on windows // FixWindowsPath replaces \ with / in the given path because sometimes the \ isn't recognized as valid on windows
func FixWindowsPath(str string) string { func FixWindowsPath(str string) string {
if runtime.GOOS == "windows" { if runtime.GOOS == "windows" {
return strings.Replace(str, "\\", "/", -1) return strings.ReplaceAll(str, "\\", "/")
} }
return str return str
} }