From e14bb8432c81c2e01ae8fab15f457158521ab07d Mon Sep 17 00:00:00 2001 From: SmallCoccinelle <89733524+SmallCoccinelle@users.noreply.github.com> Date: Mon, 18 Oct 2021 05:12:40 +0200 Subject: [PATCH] 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 --- .golangci.yml | 14 ++- graphql/schema/types/scraper.graphql | 6 + pkg/api/resolver.go | 6 + pkg/api/resolver_model_studio.go | 2 +- pkg/api/resolver_query_scraper.go | 37 +++--- pkg/api/resolver_subscription_logging.go | 16 +-- pkg/autotag/scene_test.go | 2 +- pkg/database/transaction.go | 4 +- pkg/dlna/dms.go | 7 +- pkg/dlna/paging.go | 2 +- pkg/ffmpeg/ffprobe.go | 4 +- pkg/ffmpeg/image.go | 15 ++- pkg/file/zip.go | 2 +- pkg/gallery/export_test.go | 14 ++- pkg/gallery/scan.go | 57 ++++----- pkg/image/export_test.go | 7 +- pkg/image/thumbnail.go | 3 + pkg/job/progress.go | 7 +- pkg/logger/logger.go | 9 +- pkg/logger/plugin.go | 14 +-- pkg/manager/config/config.go | 44 ++++--- pkg/manager/downloads.go | 2 +- pkg/manager/manager.go | 8 +- pkg/manager/manager_tasks.go | 14 ++- pkg/manager/task_autotag.go | 6 +- pkg/manager/task_scan.go | 149 ++++++++++++----------- pkg/match/path.go | 10 +- pkg/models/model_file.go | 14 +-- pkg/models/transaction.go | 8 +- pkg/movie/export_test.go | 7 +- pkg/performer/export_test.go | 7 +- pkg/plugin/config.go | 2 +- pkg/scene/export_test.go | 35 +++--- pkg/scene/import.go | 8 +- pkg/scraper/autotag.go | 4 +- pkg/scraper/json.go | 4 +- pkg/scraper/mapped.go | 6 +- pkg/scraper/query_url.go | 2 +- pkg/scraper/stashbox/stash_box.go | 7 +- pkg/scraper/xpath.go | 4 +- pkg/session/authentication.go | 8 +- pkg/sqlite/filter.go | 21 ++-- pkg/sqlite/gallery.go | 9 +- pkg/sqlite/movies.go | 7 +- pkg/sqlite/performer.go | 7 +- pkg/sqlite/scene.go | 9 +- pkg/sqlite/scene_marker.go | 8 +- pkg/sqlite/sql.go | 18 +-- pkg/studio/export_test.go | 7 +- pkg/tag/export_test.go | 7 +- pkg/utils/windows.go | 2 +- 51 files changed, 372 insertions(+), 300 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index 2900aa4df..0499b6f29 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -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 diff --git a/graphql/schema/types/scraper.graphql b/graphql/schema/types/scraper.graphql index 9e35346f4..e567cbe50 100644 --- a/graphql/schema/types/scraper.graphql +++ b/graphql/schema/types/scraper.graphql @@ -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!] } diff --git a/pkg/api/resolver.go b/pkg/api/resolver.go index 85986cce1..0cbcd2db2 100644 --- a/pkg/api/resolver.go +++ b/pkg/api/resolver.go @@ -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) } diff --git a/pkg/api/resolver_model_studio.go b/pkg/api/resolver_model_studio.go index e8724c545..32c7c5399 100644 --- a/pkg/api/resolver_model_studio.go +++ b/pkg/api/resolver_model_studio.go @@ -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 diff --git a/pkg/api/resolver_query_scraper.go b/pkg/api/resolver_query_scraper.go index 0af2dc540..6bf542730 100644 --- a/pkg/api/resolver_query_scraper.go +++ b/pkg/api/resolver_query_scraper.go @@ -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 } diff --git a/pkg/api/resolver_subscription_logging.go b/pkg/api/resolver_subscription_logging.go index 6ef8e4109..d4f8706af 100644 --- a/pkg/api/resolver_subscription_logging.go +++ b/pkg/api/resolver_subscription_logging.go @@ -8,20 +8,20 @@ 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: + return models.LogLevelDebug } - - // default to debug - return models.LogLevelDebug } func logEntriesFromLogItems(logItems []logger.LogItem) []*models.LogEntry { diff --git a/pkg/autotag/scene_test.go b/pkg/autotag/scene_test.go index 5e5b88806..3bf9b33e9 100644 --- a/pkg/autotag/scene_test.go +++ b/pkg/autotag/scene_test.go @@ -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)...) } } diff --git a/pkg/database/transaction.go b/pkg/database/transaction.go index 32c1ab171..d8c23fb3b 100644 --- a/pkg/database/transaction.go +++ b/pkg/database/transaction.go @@ -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) diff --git a/pkg/dlna/dms.go b/pkg/dlna/dms.go index 5773ad4f6..a1ea8ceac 100644 --- a/pkg/dlna/dms.go +++ b/pkg/dlna/dms.go @@ -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) } } diff --git a/pkg/dlna/paging.go b/pkg/dlna/paging.go index d5485b107..42f82c7d1 100644 --- a/pkg/dlna/paging.go +++ b/pkg/dlna/paging.go @@ -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)) diff --git a/pkg/ffmpeg/ffprobe.go b/pkg/ffmpeg/ffprobe.go index b8cdfbbbe..e97db8aaf 100644 --- a/pkg/ffmpeg/ffprobe.go +++ b/pkg/ffmpeg/ffprobe.go @@ -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 } diff --git a/pkg/ffmpeg/image.go b/pkg/ffmpeg/image.go index 85fb0e646..68fb90379 100644 --- a/pkg/ffmpeg/image.go +++ b/pkg/ffmpeg/image.go @@ -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{ diff --git a/pkg/file/zip.go b/pkg/file/zip.go index 4ae69f27b..4028beea5 100644 --- a/pkg/file/zip.go +++ b/pkg/file/zip.go @@ -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) { diff --git a/pkg/gallery/export_test.go b/pkg/gallery/export_test.go index 5dad35975..379900dfd 100644 --- a/pkg/gallery/export_test.go +++ b/pkg/gallery/export_test.go @@ -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) } } diff --git a/pkg/gallery/scan.go b/pkg/gallery/scan.go index bc3a7dd27..81dedf7bf 100644 --- a/pkg/gallery/scan.go +++ b/pkg/gallery/scan.go @@ -142,38 +142,35 @@ 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) { - currentTime := time.Now() + } else if scanner.hasImages(path) { // don't create gallery if it has no images + currentTime := time.Now() - g = &models.Gallery{ - Zip: true, - Title: sql.NullString{ - String: utils.GetNameFromPath(path, scanner.StripFileExtension), - Valid: true, - }, - CreatedAt: models.SQLiteTimestamp{Timestamp: currentTime}, - UpdatedAt: models.SQLiteTimestamp{Timestamp: currentTime}, - } - - g.SetFile(*scanned) - - // only warn when creating the gallery - ok, err := utils.IsZipFileUncompressed(path) - if err == nil && !ok { - logger.Warnf("%s is using above store (0) level compression.", path) - } - - logger.Infof("%s doesn't exist. Creating new item...", path) - g, err = qb.Create(*g) - if err != nil { - return err - } - - scanImages = true - isNewGallery = true + g = &models.Gallery{ + Zip: true, + Title: sql.NullString{ + String: utils.GetNameFromPath(path, scanner.StripFileExtension), + Valid: true, + }, + CreatedAt: models.SQLiteTimestamp{Timestamp: currentTime}, + UpdatedAt: models.SQLiteTimestamp{Timestamp: currentTime}, } + + g.SetFile(*scanned) + + // only warn when creating the gallery + ok, err := utils.IsZipFileUncompressed(path) + if err == nil && !ok { + logger.Warnf("%s is using above store (0) level compression.", path) + } + + logger.Infof("%s doesn't exist. Creating new item...", path) + g, err = qb.Create(*g) + if err != nil { + return err + } + + scanImages = true + isNewGallery = true } return nil diff --git a/pkg/image/export_test.go b/pkg/image/export_test.go index 1e0a7d411..6f72ea324 100644 --- a/pkg/image/export_test.go +++ b/pkg/image/export_test.go @@ -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) } } diff --git a/pkg/image/thumbnail.go b/pkg/image/thumbnail.go index 6cc49b990..ea03d7357 100644 --- a/pkg/image/thumbnail.go +++ b/pkg/image/thumbnail.go @@ -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 diff --git a/pkg/job/progress.go b/pkg/job/progress.go index 0c1d3c5cb..3bd6c3f08 100644 --- a/pkg/job/progress.go +++ b/pkg/job/progress.go @@ -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 diff --git a/pkg/logger/logger.go b/pkg/logger/logger.go index 2cf8d10c3..96d4eba3e 100644 --- a/pkg/logger/logger.go +++ b/pkg/logger/logger.go @@ -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 } diff --git a/pkg/logger/plugin.go b/pkg/logger/plugin.go index 67e6b03e9..7b2541c1b 100644 --- a/pkg/logger/plugin.go +++ b/pkg/logger/plugin.go @@ -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 { - // don't block on this - select { - case log.ProgressChan <- p: - default: - } + } else if log.ProgressChan != nil { // only pass progress through if channel present + // don't block on this + select { + case log.ProgressChan <- p: + default: } } + } } diff --git a/pkg/manager/config/config.go b/pkg/manager/config/config.go index be6b1784b..7721413ad 100644 --- a/pkg/manager/config/config.go +++ b/pkg/manager/config/config.go @@ -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 == "" { - //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") + return &StashBoxError{msg: "API Key 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 } diff --git a/pkg/manager/downloads.go b/pkg/manager/downloads.go index 4706298f2..675a08525 100644 --- a/pkg/manager/downloads.go +++ b/pkg/manager/downloads.go @@ -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{ diff --git a/pkg/manager/manager.go b/pkg/manager/manager.go index 29519ae81..bbb7a206b 100644 --- a/pkg/manager/manager.go +++ b/pkg/manager/manager.go @@ -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 { - panic(err) - } + } 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 diff --git a/pkg/manager/manager_tasks.go b/pkg/manager/manager_tasks.go index 0dc697279..fbffa5b49 100644 --- a/pkg/manager/manager_tasks.go +++ b/pkg/manager/manager_tasks.go @@ -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 diff --git a/pkg/manager/task_autotag.go b/pkg/manager/task_autotag.go index 195f9f54a..9c62d2b3e 100644 --- a/pkg/manager/task_autotag.go +++ b/pkg/manager/task_autotag.go @@ -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 { diff --git a/pkg/manager/task_scan.go b/pkg/manager/task_scan.go index d54ad1066..66c61702e 100644 --- a/pkg/manager/task_scan.go +++ b/pkg/manager/task_scan.go @@ -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,80 +260,84 @@ 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 { - iwg := sizedwaitgroup.New(2) - - if t.GenerateSprite { - iwg.Add() - - go t.progress.ExecuteTask(fmt.Sprintf("Generating sprites for %s", path), func() { - taskSprite := GenerateSpriteTask{ - Scene: *s, - Overwrite: false, - fileNamingAlgorithm: t.fileNamingAlgorithm, - } - taskSprite.Start() - iwg.Done() - }) - } - - if t.GeneratePhash { - iwg.Add() - - go t.progress.ExecuteTask(fmt.Sprintf("Generating phash for %s", path), func() { - taskPhash := GeneratePhashTask{ - Scene: *s, - fileNamingAlgorithm: t.fileNamingAlgorithm, - txnManager: t.TxnManager, - } - taskPhash.Start() - iwg.Done() - }) - } - - if t.GeneratePreview { - iwg.Add() - - go t.progress.ExecuteTask(fmt.Sprintf("Generating preview for %s", path), func() { - config := config.GetInstance() - var previewSegmentDuration = config.GetPreviewSegmentDuration() - var previewSegments = config.GetPreviewSegments() - var previewExcludeStart = config.GetPreviewExcludeStart() - var previewExcludeEnd = config.GetPreviewExcludeEnd() - var previewPresent = config.GetPreviewPreset() - - // NOTE: the reuse of this model like this is painful. - previewOptions := models.GeneratePreviewOptionsInput{ - PreviewSegments: &previewSegments, - PreviewSegmentDuration: &previewSegmentDuration, - PreviewExcludeStart: &previewExcludeStart, - PreviewExcludeEnd: &previewExcludeEnd, - PreviewPreset: &previewPresent, - } - - taskPreview := GeneratePreviewTask{ - Scene: *s, - ImagePreview: t.GenerateImagePreview, - Options: previewOptions, - Overwrite: false, - fileNamingAlgorithm: t.fileNamingAlgorithm, - } - taskPreview.Start() - iwg.Done() - }) - } - - iwg.Wait() + if s == nil { + return } + + // Handle the case of a scene + iwg := sizedwaitgroup.New(2) + + if t.GenerateSprite { + iwg.Add() + + go t.progress.ExecuteTask(fmt.Sprintf("Generating sprites for %s", path), func() { + taskSprite := GenerateSpriteTask{ + Scene: *s, + Overwrite: false, + fileNamingAlgorithm: t.fileNamingAlgorithm, + } + taskSprite.Start() + iwg.Done() + }) + } + + if t.GeneratePhash { + iwg.Add() + + go t.progress.ExecuteTask(fmt.Sprintf("Generating phash for %s", path), func() { + taskPhash := GeneratePhashTask{ + Scene: *s, + fileNamingAlgorithm: t.fileNamingAlgorithm, + txnManager: t.TxnManager, + } + taskPhash.Start() + iwg.Done() + }) + } + + if t.GeneratePreview { + iwg.Add() + + go t.progress.ExecuteTask(fmt.Sprintf("Generating preview for %s", path), func() { + config := config.GetInstance() + var previewSegmentDuration = config.GetPreviewSegmentDuration() + var previewSegments = config.GetPreviewSegments() + var previewExcludeStart = config.GetPreviewExcludeStart() + var previewExcludeEnd = config.GetPreviewExcludeEnd() + var previewPresent = config.GetPreviewPreset() + + // NOTE: the reuse of this model like this is painful. + previewOptions := models.GeneratePreviewOptionsInput{ + PreviewSegments: &previewSegments, + PreviewSegmentDuration: &previewSegmentDuration, + PreviewExcludeStart: &previewExcludeStart, + PreviewExcludeEnd: &previewExcludeEnd, + PreviewPreset: &previewPresent, + } + + taskPreview := GeneratePreviewTask{ + Scene: *s, + ImagePreview: t.GenerateImagePreview, + Options: previewOptions, + Overwrite: false, + fileNamingAlgorithm: t.fileNamingAlgorithm, + } + taskPreview.Start() + iwg.Done() + }) + } + + iwg.Wait() } func walkFilesToScan(s *models.StashConfig, f filepath.WalkFunc) error { diff --git a/pkg/match/path.go b/pkg/match/path.go index 04f7fe58b..9cdeb84ef 100644 --- a/pkg/match/path.go +++ b/pkg/match/path.go @@ -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{ diff --git a/pkg/models/model_file.go b/pkg/models/model_file.go index 799cac7e9..21fd51bab 100644 --- a/pkg/models/model_file.go +++ b/pkg/models/model_file.go @@ -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 { diff --git a/pkg/models/transaction.go b/pkg/models/transaction.go index c30f4cb9a..291038b0c 100644 --- a/pkg/models/transaction.go +++ b/pkg/models/transaction.go @@ -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) diff --git a/pkg/movie/export_test.go b/pkg/movie/export_test.go index b03de426c..d9fed193a 100644 --- a/pkg/movie/export_test.go +++ b/pkg/movie/export_test.go @@ -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) } } diff --git a/pkg/performer/export_test.go b/pkg/performer/export_test.go index 0ec00a93b..9398e7fc1 100644 --- a/pkg/performer/export_test.go +++ b/pkg/performer/export_test.go @@ -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) } } diff --git a/pkg/plugin/config.go b/pkg/plugin/config.go index a56c5520d..05501b4e2 100644 --- a/pkg/plugin/config.go +++ b/pkg/plugin/config.go @@ -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 diff --git a/pkg/scene/export_test.go b/pkg/scene/export_test.go index b0b4f7834..258b17a11 100644 --- a/pkg/scene/export_test.go +++ b/pkg/scene/export_test.go @@ -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) } } diff --git a/pkg/scene/import.go b/pkg/scene/import.go index 5cd56ccfb..fa1e8f857 100644 --- a/pkg/scene/import.go +++ b/pkg/scene/import.go @@ -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") } diff --git a/pkg/scraper/autotag.go b/pkg/scraper/autotag.go index 1f62a4701..73a836224 100644 --- a/pkg/scraper/autotag.go +++ b/pkg/scraper/autotag.go @@ -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 { diff --git a/pkg/scraper/json.go b/pkg/scraper/json.go index b291167a5..1543e18ca 100644 --- a/pkg/scraper/json.go +++ b/pkg/scraper/json.go @@ -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) diff --git a/pkg/scraper/mapped.go b/pkg/scraper/mapped.go index cdba5b907..764cfa730 100644 --- a/pkg/scraper/mapped.go +++ b/pkg/scraper/mapped.go @@ -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"` diff --git a/pkg/scraper/query_url.go b/pkg/scraper/query_url.go index b48b2b794..2826e15e4 100644 --- a/pkg/scraper/query_url.go +++ b/pkg/scraper/query_url.go @@ -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 diff --git a/pkg/scraper/stashbox/stash_box.go b/pkg/scraper/stashbox/stash_box.go index ff7a4b520..71dc1b109 100644 --- a/pkg/scraper/stashbox/stash_box.go +++ b/pkg/scraper/stashbox/stash_box.go @@ -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) } diff --git a/pkg/scraper/xpath.go b/pkg/scraper/xpath.go index 8a8c69d40..5093bd099 100644 --- a/pkg/scraper/xpath.go +++ b/pkg/scraper/xpath.go @@ -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) diff --git a/pkg/session/authentication.go b/pkg/session/authentication.go index 7bdf0ea22..b1b155123 100644 --- a/pkg/session/authentication.go +++ b/pkg/session/authentication.go @@ -70,12 +70,10 @@ func CheckAllowPublicWithoutAuth(c *config.Instance, r *http.Request) error { return UntrustedProxyError(requestIP) } } - } else { - // request was not proxied - if !isLocalIP(requestIP) { - return ExternalAccessError(requestIP) - } + } else if !isLocalIP(requestIP) { // request was not proxied + return ExternalAccessError(requestIP) } + } return nil diff --git a/pkg/sqlite/filter.go b/pkg/sqlite/filter.go index c85b68536..75122d365 100644 --- a/pkg/sqlite/filter.go +++ b/pkg/sqlite/filter.go @@ -210,10 +210,8 @@ func (f *filterBuilder) getSubFilterClause(clause, subFilterClause string) strin var op string if len(ret) > 0 { op = " " + f.subFilterOp + " " - } else { - if f.subFilterOp == notOp { - op = "NOT " - } + } 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 // .id NOT IN (select . from where . in ) @@ -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)) } } diff --git a/pkg/sqlite/gallery.go b/pkg/sqlite/gallery.go index 27c7585ad..80a80a44c 100644 --- a/pkg/sqlite/gallery.go +++ b/pkg/sqlite/gallery.go @@ -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)) } } diff --git a/pkg/sqlite/movies.go b/pkg/sqlite/movies.go index 92936ecf5..a034ed913 100644 --- a/pkg/sqlite/movies.go +++ b/pkg/sqlite/movies.go @@ -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") } } diff --git a/pkg/sqlite/performer.go b/pkg/sqlite/performer.go index e3c06a3e6..12f74e987 100644 --- a/pkg/sqlite/performer.go +++ b/pkg/sqlite/performer.go @@ -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 } diff --git a/pkg/sqlite/scene.go b/pkg/sqlite/scene.go index c40f2f358..0748ff839 100644 --- a/pkg/sqlite/scene.go +++ b/pkg/sqlite/scene.go @@ -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)) } } diff --git a/pkg/sqlite/scene_marker.go b/pkg/sqlite/scene_marker.go index 50218738b..a343d783a 100644 --- a/pkg/sqlite/scene_marker.go +++ b/pkg/sqlite/scene_marker.go @@ -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) diff --git a/pkg/sqlite/sql.go b/pkg/sqlite/sql.go index 9834acb26..997864eec 100644 --- a/pkg/sqlite/sql.go +++ b/pkg/sqlite/sql.go @@ -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)) + ")" diff --git a/pkg/studio/export_test.go b/pkg/studio/export_test.go index e251ad52c..b4d5b312e 100644 --- a/pkg/studio/export_test.go +++ b/pkg/studio/export_test.go @@ -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) } } diff --git a/pkg/tag/export_test.go b/pkg/tag/export_test.go index 2057ccad3..e37008ab4 100644 --- a/pkg/tag/export_test.go +++ b/pkg/tag/export_test.go @@ -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) } } diff --git a/pkg/utils/windows.go b/pkg/utils/windows.go index fb25f912d..cf63e1ae4 100644 --- a/pkg/utils/windows.go +++ b/pkg/utils/windows.go @@ -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 }