diff --git a/server/ctrladmin/ctrl.go b/server/ctrladmin/ctrl.go index c8bd41c..1d542e4 100644 --- a/server/ctrladmin/ctrl.go +++ b/server/ctrladmin/ctrl.go @@ -1,7 +1,9 @@ +// Package ctrladmin provides HTTP handlers for admin UI package ctrladmin import ( "encoding/gob" + "errors" "fmt" "html/template" "log" @@ -150,7 +152,6 @@ type ( handlerAdminRaw func(w http.ResponseWriter, r *http.Request) ) -//nolint:gocognit func (c *Controller) H(h handlerAdmin) http.Handler { // TODO: break this up a bit return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -272,26 +273,33 @@ func sessLogSave(s *sessions.Session, w http.ResponseWriter, r *http.Request) { // ## begin validation // ## begin validation +var ( + errValiNoUsername = errors.New("please enter the password twice") + errValiPasswordAllFields = errors.New("please enter the password twice") + errValiPasswordsNotSame = errors.New("passwords entered were not the same") + errValiKeysAllFields = errors.New("please enter the api key and secret") +) + func validateUsername(username string) error { if username == "" { - return fmt.Errorf("please enter the username") + return errValiNoUsername } return nil } func validatePasswords(pOne, pTwo string) error { if pOne == "" || pTwo == "" { - return fmt.Errorf("please enter the password twice") + return errValiPasswordAllFields } if !(pOne == pTwo) { - return fmt.Errorf("the two passwords entered were not the same") + return errValiPasswordsNotSame } return nil } func validateAPIKey(apiKey, secret string) error { if apiKey == "" || secret == "" { - return fmt.Errorf("please enter both the api key and secret") + return errValiKeysAllFields } return nil } diff --git a/server/ctrladmin/handlers_playlist.go b/server/ctrladmin/handlers_playlist.go index b7a633e..6d3c53b 100644 --- a/server/ctrladmin/handlers_playlist.go +++ b/server/ctrladmin/handlers_playlist.go @@ -2,6 +2,7 @@ package ctrladmin import ( "bufio" + "errors" "fmt" "mime/multipart" "net/http" @@ -12,6 +13,10 @@ import ( "go.senan.xyz/gonic/server/db" ) +var ( + errPlaylistNoMatch = errors.New("couldn't match track") +) + func playlistParseLine(c *Controller, path string) (int, error) { if strings.HasPrefix(path, "#") || strings.TrimSpace(path) == "" { return 0, nil @@ -25,7 +30,7 @@ func playlistParseLine(c *Controller, path string) (int, error) { err := query.First(&track).Error switch { case gorm.IsRecordNotFoundError(err): - return 0, fmt.Errorf("couldn't match track %q", path) + return 0, fmt.Errorf("%v: %w", err, errPlaylistNoMatch) case err != nil: return 0, fmt.Errorf("while matching: %w", err) default: diff --git a/server/ctrlsubsonic/ctrl.go b/server/ctrlsubsonic/ctrl.go index cc5bc16..9822fa1 100644 --- a/server/ctrlsubsonic/ctrl.go +++ b/server/ctrlsubsonic/ctrl.go @@ -1,3 +1,4 @@ +// Package ctrlsubsonic provides HTTP handlers for subsonic api package ctrlsubsonic import ( diff --git a/server/ctrlsubsonic/handlers_by_folder.go b/server/ctrlsubsonic/handlers_by_folder.go index 5f82c8b..b05561d 100644 --- a/server/ctrlsubsonic/handlers_by_folder.go +++ b/server/ctrlsubsonic/handlers_by_folder.go @@ -92,6 +92,7 @@ func (c *Controller) ServeGetMusicDirectory(r *http.Request) *spec.Response { return sub } +// ServeGetAlbumList handles the getAlbumList view. // changes to this function should be reflected in in _by_tags.go's // getAlbumListTwo() function func (c *Controller) ServeGetAlbumList(r *http.Request) *spec.Response { diff --git a/server/ctrlsubsonic/handlers_by_tags.go b/server/ctrlsubsonic/handlers_by_tags.go index 06300cf..49538ea 100644 --- a/server/ctrlsubsonic/handlers_by_tags.go +++ b/server/ctrlsubsonic/handlers_by_tags.go @@ -92,6 +92,7 @@ func (c *Controller) ServeGetAlbum(r *http.Request) *spec.Response { return sub } +// ServeGetAlbumListTwo handles the getAlbumList2 view. // changes to this function should be reflected in in _by_folder.go's // getAlbumList() function func (c *Controller) ServeGetAlbumListTwo(r *http.Request) *spec.Response { diff --git a/server/ctrlsubsonic/spec/spec.go b/server/ctrlsubsonic/spec/spec.go index 12cfc61..7b8c93b 100644 --- a/server/ctrlsubsonic/spec/spec.go +++ b/server/ctrlsubsonic/spec/spec.go @@ -55,7 +55,7 @@ func NewResponse() *Response { } } -// spec errors: +// Error represents a typed error // 0 a generic error // 10 required parameter is missing // 20 incompatible subsonic rest protocol version. client must upgrade diff --git a/server/db/migrations.go b/server/db/migrations.go index d19f395..b777777 100644 --- a/server/db/migrations.go +++ b/server/db/migrations.go @@ -1,4 +1,3 @@ -//nolint:deadcode,varcheck package db import ( diff --git a/server/db/model.go b/server/db/model.go index b6a1cec..9320cfe 100644 --- a/server/db/model.go +++ b/server/db/model.go @@ -1,3 +1,4 @@ +// Package db provides database helpers and models //nolint:lll package db diff --git a/server/encode/encode.go b/server/encode/encode.go index 2d2cff6..bad408e 100644 --- a/server/encode/encode.go +++ b/server/encode/encode.go @@ -49,7 +49,7 @@ func writeCmdOutput(out, cache io.Writer, pipeReader io.ReadCloser) { for { n, err := pipeReader.Read(buffer) if err != nil { - pipeReader.Close() + _ = pipeReader.Close() break } data := buffer[0:n] @@ -91,7 +91,7 @@ func ffmpegCommand(filePath string, profile *Profile, bitrate string) *exec.Cmd ) } args = append(args, "-f", profile.Format, "-") - return exec.Command("/usr/bin/ffmpeg", args...) + return exec.Command("/usr/bin/ffmpeg", args...) //nolint:gosec } func Encode(out io.Writer, trackPath, cachePath string, profile *Profile, bitrate string) error { @@ -116,22 +116,22 @@ func Encode(out io.Writer, trackPath, cachePath string, profile *Profile, bitrat return fmt.Errorf("running ffmpeg: %w", err) } // close all pipes and flush cache file - pipeWriter.Close() + _ = pipeWriter.Close() if err := cacheFile.Sync(); err != nil { return fmt.Errorf("flushing %q: %w", cachePath, err) } - cacheFile.Close() + _ = cacheFile.Close() return nil } -// generate cache key (file name). for, you know, encoded tracks cache +// CacheKey generates the filename for the new transcode save func CacheKey(sourcePath string, profile, bitrate string) string { format := Profiles[profile].Format hash := xxhash.Sum64String(sourcePath) return fmt.Sprintf("%x-%s-%s.%s", hash, profile, bitrate, format) } -// check if client forces bitrate lower than set in profile +// GetBitrate checks if the client forces bitrate lower than set in profile func GetBitrate(clientBitrate int, profile *Profile) string { bitrate := profile.Bitrate if clientBitrate != 0 && clientBitrate < bitrate { diff --git a/server/jukebox/jukebox.go b/server/jukebox/jukebox.go index 03390a2..be9a959 100644 --- a/server/jukebox/jukebox.go +++ b/server/jukebox/jukebox.go @@ -4,6 +4,7 @@ package jukebox import ( "fmt" + "log" "os" "path" "sync" @@ -89,7 +90,9 @@ func (j *Jukebox) Listen() error { case update := <-j.updates: j.doUpdate(update) case speaker := <-j.speaker: - j.doUpdateSpeaker(speaker) + if err := j.doUpdateSpeaker(speaker); err != nil { + log.Printf("error in jukebox: %v", err) + } } } } diff --git a/server/lastfm/lastfm.go b/server/lastfm/lastfm.go index 5a414ef..809f2b5 100644 --- a/server/lastfm/lastfm.go +++ b/server/lastfm/lastfm.go @@ -4,6 +4,7 @@ import ( "crypto/md5" "encoding/hex" "encoding/xml" + "errors" "fmt" "net/http" "net/url" @@ -19,6 +20,7 @@ var ( client = &http.Client{ Timeout: 10 * time.Second, } + ErrLastFM = errors.New("last.fm error") ) // TODO: remove this package's dependency on models/db @@ -54,7 +56,7 @@ func makeRequest(method string, params url.Values) (LastFM, error) { return LastFM{}, fmt.Errorf("decoding: %w", err) } if lastfm.Error.Code != 0 { - return LastFM{}, fmt.Errorf("parsing: %v", lastfm.Error.Value) + return LastFM{}, fmt.Errorf("%v: %w", lastfm.Error.Value, ErrLastFM) } return lastfm, nil } diff --git a/server/scanner/scanner.go b/server/scanner/scanner.go index c0c4e67..574ff0b 100644 --- a/server/scanner/scanner.go +++ b/server/scanner/scanner.go @@ -23,17 +23,18 @@ import ( ) var ( - ErrStatingItem = errors.New("stating item") - ErrReadingTags = errors.New("reading tags") + ErrAlreadyScanning = errors.New("already scanning") + ErrStatingItem = errors.New("could not stat item") + ErrReadingTags = errors.New("could not read tags") ) func durSince(t time.Time) time.Duration { return time.Since(t).Truncate(10 * time.Microsecond) } -// decoded converts a string to it's latin equivalent. it will -// be used by the model's *UDec fields, and is only set if it -// differs from the original. the fields are used for searching +// decoded converts a string to it's latin equivalent. +// it will be used by the model's *UDec fields, and is only set if it +// differs from the original. the fields are used for searching. func decoded(in string) string { if u := unidecode.Unidecode(in); u != in { return u @@ -236,7 +237,7 @@ var coverFilenames = map[string]struct{}{ func (s *Scanner) callbackItem(fullPath string, info *godirwalk.Dirent) error { stat, err := os.Stat(fullPath) if err != nil { - return fmt.Errorf("stating: %w", err) + return fmt.Errorf("%w: %v", ErrStatingItem, err) } relPath, err := filepath.Rel(s.musicPath, fullPath) if err != nil { diff --git a/server/scanner/scanner_test.go b/server/scanner/scanner_test.go index aee0e06..24708d4 100644 --- a/server/scanner/scanner_test.go +++ b/server/scanner/scanner_test.go @@ -40,17 +40,17 @@ func resetTablesPause(db *db.DB, b *testing.B) { func BenchmarkScanFresh(b *testing.B) { for n := 0; n < b.N; n++ { resetTablesPause(testScanner.db, b) - testScanner.Start(ScanOptions{}) + _ = testScanner.Start(ScanOptions{}) } } func BenchmarkScanIncremental(b *testing.B) { // do a full scan and reset - testScanner.Start(ScanOptions{}) + _ = testScanner.Start(ScanOptions{}) b.ResetTimer() // do the inc scans for n := 0; n < b.N; n++ { - testScanner.Start(ScanOptions{}) + _ = testScanner.Start(ScanOptions{}) } } diff --git a/server/server.go b/server/server.go index 2464723..1d68d40 100644 --- a/server/server.go +++ b/server/server.go @@ -179,11 +179,11 @@ func setupSubsonic(r *mux.Router, ctrl *ctrlsubsonic.Controller) { } type ( - funcExecute func() error - funcInterrupt func(error) + FuncExecute func() error + FuncInterrupt func(error) ) -func (s *Server) StartHTTP(listenAddr string) (funcExecute, funcInterrupt) { +func (s *Server) StartHTTP(listenAddr string) (FuncExecute, FuncInterrupt) { list := &http.Server{ Addr: listenAddr, Handler: s.router, @@ -196,11 +196,11 @@ func (s *Server) StartHTTP(listenAddr string) (funcExecute, funcInterrupt) { return list.ListenAndServe() }, func(_ error) { // stop job - list.Close() + _ = list.Close() } } -func (s *Server) StartScanTicker(dur time.Duration) (funcExecute, funcInterrupt) { +func (s *Server) StartScanTicker(dur time.Duration) (FuncExecute, FuncInterrupt) { ticker := time.NewTicker(dur) done := make(chan struct{}) waitFor := func() error { @@ -225,7 +225,7 @@ func (s *Server) StartScanTicker(dur time.Duration) (funcExecute, funcInterrupt) } } -func (s *Server) StartJukebox() (funcExecute, funcInterrupt) { +func (s *Server) StartJukebox() (FuncExecute, FuncInterrupt) { return func() error { log.Printf("starting job 'jukebox'\n") return s.jukebox.Listen()