Add better process lifecycle management

This commit is contained in:
Deluan 2020-10-24 22:43:59 -04:00
parent 6d08a9446d
commit c0ec0b28b9
7 changed files with 80 additions and 49 deletions

View File

@ -33,6 +33,10 @@ testall: check_go_env test
@(cd ./ui && npm test -- --watchAll=false) @(cd ./ui && npm test -- --watchAll=false)
.PHONY: testall .PHONY: testall
lint:
golangci-lint run -v
.PHONY: lint
update-snapshots: check_go_env update-snapshots: check_go_env
UPDATE_SNAPSHOTS=true ginkgo ./server/subsonic/... UPDATE_SNAPSHOTS=true ginkgo ./server/subsonic/...
.PHONY: update-snapshots .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 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 .PHONY: buildall
pre-push: pre-push: lint test
golangci-lint run -v
@echo
make test
.PHONY: pre-push .PHONY: pre-push
release: release:

View File

@ -3,10 +3,13 @@ package cmd
import ( import (
"fmt" "fmt"
"os" "os"
"time"
"github.com/deluan/navidrome/conf" "github.com/deluan/navidrome/conf"
"github.com/deluan/navidrome/consts" "github.com/deluan/navidrome/consts"
"github.com/deluan/navidrome/db" "github.com/deluan/navidrome/db"
"github.com/deluan/navidrome/log"
"github.com/oklog/run"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/viper" "github.com/spf13/viper"
) )
@ -24,7 +27,7 @@ Complete documentation is available at https://www.navidrome.org/docs`,
preRun() preRun()
}, },
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
startServer() runNavidrome()
}, },
Version: consts.Version(), Version: consts.Version(),
} }
@ -45,20 +48,69 @@ func preRun() {
conf.Load() conf.Load()
} }
func startServer() { func runNavidrome() {
db.EnsureLatestVersion() db.EnsureLatestVersion()
subsonic, err := CreateSubsonicAPIRouter() var g run.Group
if err != nil { g.Add(startServer())
panic(fmt.Sprintf("Could not create the Subsonic API router. Aborting! err=%v", err)) 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() { func init() {
cobra.OnInitialize(func() { cobra.OnInitialize(func() {
conf.InitConfig(cfgFile) conf.InitConfig(cfgFile)

View File

@ -21,8 +21,7 @@ import (
func CreateServer(musicFolder string) *server.Server { func CreateServer(musicFolder string) *server.Server {
dataStore := persistence.New() dataStore := persistence.New()
scannerScanner := scanner.New(dataStore) serverServer := server.New(dataStore)
serverServer := server.New(scannerScanner, dataStore)
return serverServer return serverServer
} }
@ -38,7 +37,7 @@ func CreateAppRouter() *app.Router {
return router return router
} }
func CreateSubsonicAPIRouter() (*subsonic.Router, error) { func CreateSubsonicAPIRouter() *subsonic.Router {
dataStore := persistence.New() dataStore := persistence.New()
artworkCache := core.NewImageCache() artworkCache := core.NewImageCache()
artwork := core.NewArtwork(dataStore, artworkCache) artwork := core.NewArtwork(dataStore, artworkCache)
@ -54,7 +53,7 @@ func CreateSubsonicAPIRouter() (*subsonic.Router, error) {
spotifyClient := core.SpotifyNewClient() spotifyClient := core.SpotifyNewClient()
externalInfo := core.NewExternalInfo(dataStore, client, spotifyClient) externalInfo := core.NewExternalInfo(dataStore, client, spotifyClient)
router := subsonic.New(artwork, listGenerator, playlists, mediaStreamer, archiver, players, externalInfo, dataStore) router := subsonic.New(artwork, listGenerator, playlists, mediaStreamer, archiver, players, externalInfo, dataStore)
return router, nil return router
} }
// wire_injectors.go: // wire_injectors.go:

View File

@ -39,6 +39,6 @@ func CreateAppRouter() *app.Router {
panic(wire.Build(allProviders)) panic(wire.Build(allProviders))
} }
func CreateSubsonicAPIRouter() (*subsonic.Router, error) { func CreateSubsonicAPIRouter() *subsonic.Router {
panic(wire.Build(allProviders)) panic(wire.Build(allProviders))
} }

1
go.mod
View File

@ -26,6 +26,7 @@ require (
github.com/mattn/go-sqlite3 v2.0.3+incompatible github.com/mattn/go-sqlite3 v2.0.3+incompatible
github.com/microcosm-cc/bluemonday v1.0.4 github.com/microcosm-cc/bluemonday v1.0.4
github.com/mitchellh/mapstructure v1.3.2 // indirect 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/ginkgo v1.14.2
github.com/onsi/gomega v1.10.3 github.com/onsi/gomega v1.10.3
github.com/pelletier/go-toml v1.8.0 // indirect github.com/pelletier/go-toml v1.8.0 // indirect

2
go.sum
View File

@ -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/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 h1:RW6JSWSu/RkSatfcLtogGfFgpim5p7ARQ10ECk5O750=
github.com/ogier/pflag v0.0.1/go.mod h1:zkFki7tvTa0tafRvTBIZTvzYyAu6kQhPZFnshFFPE+g= 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/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.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg=

View File

@ -3,14 +3,12 @@ package server
import ( import (
"net/http" "net/http"
"path" "path"
"time"
"github.com/deluan/navidrome/assets" "github.com/deluan/navidrome/assets"
"github.com/deluan/navidrome/conf" "github.com/deluan/navidrome/conf"
"github.com/deluan/navidrome/consts" "github.com/deluan/navidrome/consts"
"github.com/deluan/navidrome/log" "github.com/deluan/navidrome/log"
"github.com/deluan/navidrome/model" "github.com/deluan/navidrome/model"
"github.com/deluan/navidrome/scanner"
"github.com/go-chi/chi" "github.com/go-chi/chi"
"github.com/go-chi/chi/middleware" "github.com/go-chi/chi/middleware"
"github.com/go-chi/cors" "github.com/go-chi/cors"
@ -22,16 +20,14 @@ type Handler interface {
} }
type Server struct { type Server struct {
Scanner *scanner.Scanner router *chi.Mux
router *chi.Mux ds model.DataStore
ds model.DataStore
} }
func New(scanner *scanner.Scanner, ds model.DataStore) *Server { func New(ds model.DataStore) *Server {
a := &Server{Scanner: scanner, ds: ds} a := &Server{ds: ds}
initialSetup(ds) initialSetup(ds)
a.initRoutes() a.initRoutes()
a.initScanner()
checkFfmpegInstallation() checkFfmpegInstallation()
checkExternalCredentials() checkExternalCredentials()
return a 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.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() { func (a *Server) initRoutes() {
@ -72,22 +68,3 @@ func (a *Server) initRoutes() {
a.router = r 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)
}
}()
}