diff --git a/Makefile b/Makefile index 5f19e037..9d6dde39 100644 --- a/Makefile +++ b/Makefile @@ -33,6 +33,10 @@ testall: check_go_env test @(cd ./ui && npm test -- --watchAll=false) .PHONY: testall +lint: + golangci-lint run -v +.PHONY: lint + update-snapshots: check_go_env UPDATE_SNAPSHOTS=true ginkgo ./server/subsonic/... .PHONY: update-snapshots @@ -87,11 +91,7 @@ buildall: check_env go build -ldflags="-X github.com/deluan/navidrome/consts.gitSha=$(GIT_SHA) -X github.com/deluan/navidrome/consts.gitTag=$(GIT_TAG)-SNAPSHOT" -tags=embed .PHONY: buildall -pre-push: - golangci-lint run -v - - @echo - make test +pre-push: lint test .PHONY: pre-push release: diff --git a/cmd/root.go b/cmd/root.go index 9bed2286..eb8996ce 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -3,10 +3,13 @@ package cmd import ( "fmt" "os" + "time" "github.com/deluan/navidrome/conf" "github.com/deluan/navidrome/consts" "github.com/deluan/navidrome/db" + "github.com/deluan/navidrome/log" + "github.com/oklog/run" "github.com/spf13/cobra" "github.com/spf13/viper" ) @@ -24,7 +27,7 @@ Complete documentation is available at https://www.navidrome.org/docs`, preRun() }, Run: func(cmd *cobra.Command, args []string) { - startServer() + runNavidrome() }, Version: consts.Version(), } @@ -45,20 +48,69 @@ func preRun() { conf.Load() } -func startServer() { +func runNavidrome() { db.EnsureLatestVersion() - subsonic, err := CreateSubsonicAPIRouter() - if err != nil { - panic(fmt.Sprintf("Could not create the Subsonic API router. Aborting! err=%v", err)) + var g run.Group + g.Add(startServer()) + if conf.Server.ScanInterval != 0 { + g.Add(startScanner()) + } else { + log.Warn("Scanner is disabled", "interval", conf.Server.ScanInterval) + } + if err := g.Run(); err != nil { + log.Error("Fatal error in Navidrome. Aborting", err) } - a := CreateServer(conf.Server.MusicFolder) - a.MountRouter(consts.URLPathSubsonicAPI, subsonic) - a.MountRouter(consts.URLPathUI, CreateAppRouter()) - a.Run(fmt.Sprintf("%s:%d", conf.Server.Address, conf.Server.Port)) } -// TODO: Implemement some struct tags to map flags to viper +func startServer() (func() error, func(err error)) { + return func() error { + a := CreateServer(conf.Server.MusicFolder) + a.MountRouter(consts.URLPathSubsonicAPI, CreateSubsonicAPIRouter()) + a.MountRouter(consts.URLPathUI, CreateAppRouter()) + return a.Run(fmt.Sprintf("%s:%d", conf.Server.Address, conf.Server.Port)) + }, func(err error) { + if err != nil { + log.Error("Fatal error executing Scanner", err) + } else { + log.Info("Shutting down Scanner") + } + } +} + +func startScanner() (func() error, func(err error)) { + interval := conf.Server.ScanInterval + log.Info("Starting scanner", "interval", interval.String()) + scanner := CreateScanner(conf.Server.MusicFolder) + + ticker := time.NewTicker(interval) + done := make(chan bool) + + return func() error { + time.Sleep(2 * time.Second) // Wait 2 seconds before the first scan + for { + if err := scanner.RescanAll(false); err != nil { + log.Error("Error scanning media folder", "folder", conf.Server.MusicFolder, err) + } + select { + case <-ticker.C: + continue + case <-done: + return nil + } + } + }, func(err error) { + ticker.Stop() + done <- true + if err != nil { + log.Error("Fatal error executing Scanner", err) + } else { + log.Info("Shutting down Scanner") + } + } +} + +// TODO: Implement some struct tags to map flags to viper func init() { cobra.OnInitialize(func() { conf.InitConfig(cfgFile) diff --git a/cmd/wire_gen.go b/cmd/wire_gen.go index dbef00de..9c3c7ff7 100644 --- a/cmd/wire_gen.go +++ b/cmd/wire_gen.go @@ -21,8 +21,7 @@ import ( func CreateServer(musicFolder string) *server.Server { dataStore := persistence.New() - scannerScanner := scanner.New(dataStore) - serverServer := server.New(scannerScanner, dataStore) + serverServer := server.New(dataStore) return serverServer } @@ -38,7 +37,7 @@ func CreateAppRouter() *app.Router { return router } -func CreateSubsonicAPIRouter() (*subsonic.Router, error) { +func CreateSubsonicAPIRouter() *subsonic.Router { dataStore := persistence.New() artworkCache := core.NewImageCache() artwork := core.NewArtwork(dataStore, artworkCache) @@ -54,7 +53,7 @@ func CreateSubsonicAPIRouter() (*subsonic.Router, error) { spotifyClient := core.SpotifyNewClient() externalInfo := core.NewExternalInfo(dataStore, client, spotifyClient) router := subsonic.New(artwork, listGenerator, playlists, mediaStreamer, archiver, players, externalInfo, dataStore) - return router, nil + return router } // wire_injectors.go: diff --git a/cmd/wire_injectors.go b/cmd/wire_injectors.go index 62b283cf..3b0199b4 100644 --- a/cmd/wire_injectors.go +++ b/cmd/wire_injectors.go @@ -39,6 +39,6 @@ func CreateAppRouter() *app.Router { panic(wire.Build(allProviders)) } -func CreateSubsonicAPIRouter() (*subsonic.Router, error) { +func CreateSubsonicAPIRouter() *subsonic.Router { panic(wire.Build(allProviders)) } diff --git a/go.mod b/go.mod index c55db5fc..d111ba6b 100644 --- a/go.mod +++ b/go.mod @@ -26,6 +26,7 @@ require ( github.com/mattn/go-sqlite3 v2.0.3+incompatible github.com/microcosm-cc/bluemonday v1.0.4 github.com/mitchellh/mapstructure v1.3.2 // indirect + github.com/oklog/run v1.1.0 github.com/onsi/ginkgo v1.14.2 github.com/onsi/gomega v1.10.3 github.com/pelletier/go-toml v1.8.0 // indirect diff --git a/go.sum b/go.sum index 591bd3ec..d0766c25 100644 --- a/go.sum +++ b/go.sum @@ -382,6 +382,8 @@ github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/ogier/pflag v0.0.1 h1:RW6JSWSu/RkSatfcLtogGfFgpim5p7ARQ10ECk5O750= github.com/ogier/pflag v0.0.1/go.mod h1:zkFki7tvTa0tafRvTBIZTvzYyAu6kQhPZFnshFFPE+g= +github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= +github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= diff --git a/server/server.go b/server/server.go index a5869dc6..d04744f6 100644 --- a/server/server.go +++ b/server/server.go @@ -3,14 +3,12 @@ package server import ( "net/http" "path" - "time" "github.com/deluan/navidrome/assets" "github.com/deluan/navidrome/conf" "github.com/deluan/navidrome/consts" "github.com/deluan/navidrome/log" "github.com/deluan/navidrome/model" - "github.com/deluan/navidrome/scanner" "github.com/go-chi/chi" "github.com/go-chi/chi/middleware" "github.com/go-chi/cors" @@ -22,16 +20,14 @@ type Handler interface { } type Server struct { - Scanner *scanner.Scanner - router *chi.Mux - ds model.DataStore + router *chi.Mux + ds model.DataStore } -func New(scanner *scanner.Scanner, ds model.DataStore) *Server { - a := &Server{Scanner: scanner, ds: ds} +func New(ds model.DataStore) *Server { + a := &Server{ds: ds} initialSetup(ds) a.initRoutes() - a.initScanner() checkFfmpegInstallation() checkExternalCredentials() return a @@ -47,9 +43,9 @@ func (a *Server) MountRouter(urlPath string, subRouter Handler) { }) } -func (a *Server) Run(addr string) { +func (a *Server) Run(addr string) error { log.Info("Navidrome server is accepting requests", "address", addr) - log.Error(http.ListenAndServe(addr, a.router)) + return http.ListenAndServe(addr, a.router) } func (a *Server) initRoutes() { @@ -72,22 +68,3 @@ func (a *Server) initRoutes() { a.router = r } - -func (a *Server) initScanner() { - interval := conf.Server.ScanInterval - if interval == 0 { - log.Warn("Scanner is disabled", "interval", conf.Server.ScanInterval) - return - } - log.Info("Starting scanner", "interval", interval.String()) - go func() { - time.Sleep(2 * time.Second) - for { - err := a.Scanner.RescanAll(false) - if err != nil { - log.Error("Error scanning media folder", "folder", conf.Server.MusicFolder, err) - } - time.Sleep(interval) - } - }() -}