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
- 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

View File

@@ -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!]
}

View File

@@ -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)
}

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
if !hasImage {
imagePath = imagePath + "?default=true"
imagePath += "?default=true"
}
return &imagePath, nil

View File

@@ -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
}

View File

@@ -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))

View File

@@ -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)...)
}
}

View File

@@ -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)

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
// 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)
}
}

View File

@@ -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))

View File

@@ -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
}

View File

@@ -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{

View File

@@ -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) {

View File

@@ -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)
}
}

View File

@@ -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 {

View File

@@ -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)
}
}

View File

@@ -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

View File

@@ -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

View File

@@ -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
}

View File

@@ -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:
}
}
}
}
}

View File

@@ -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
}

View File

@@ -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{

View File

@@ -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

View File

@@ -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

View File

@@ -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 {

View File

@@ -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()

View File

@@ -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{

View File

@@ -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 {

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)
}
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)

View File

@@ -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)
}
}

View File

@@ -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)
}
}

View File

@@ -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

View File

@@ -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)
}
}

View File

@@ -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")
}

View File

@@ -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 {

View File

@@ -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)

View File

@@ -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"`

View File

@@ -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

View File

@@ -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)
}

View File

@@ -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)

View File

@@ -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

View File

@@ -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))
}
}

View File

@@ -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))
}
}

View File

@@ -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")
}
}

View File

@@ -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
}

View File

@@ -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))
}
}

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) {
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)

View File

@@ -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)) + ")"

View File

@@ -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)
}
}

View File

@@ -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)
}
}

View File

@@ -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
}