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 }