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)
.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:

View File

@ -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)

View File

@ -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:

View File

@ -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))
}

1
go.mod
View File

@ -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

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/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=

View File

@ -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)
}
}()
}