Compare commits

...

4 Commits

Author SHA1 Message Date
Deluan
af4b2bb4c9 go mod tidy 2023-05-13 21:30:05 -04:00
Deluan
4773adba00 Add aliases for playlists and service commands 2023-05-13 21:28:13 -04:00
Deluan
7bbf4cbaea Fix lint errors 2023-05-13 21:28:13 -04:00
Deluan
cff19445ba Add service management 2023-05-13 21:28:11 -04:00
5 changed files with 202 additions and 6 deletions

View File

@@ -27,9 +27,10 @@ func init() {
}
var plsCmd = &cobra.Command{
Use: "pls",
Short: "Export playlists",
Long: "Export Navidrome playlists to M3U files",
Use: "playlists",
Aliases: []string{"pls", "playlist"},
Short: "Export playlists",
Long: "Export Navidrome playlists to M3U files",
Run: func(cmd *cobra.Command, args []string) {
runExporter()
},

View File

@@ -39,7 +39,7 @@ Complete documentation is available at https://www.navidrome.org/docs`,
preRun()
},
Run: func(cmd *cobra.Command, args []string) {
runNavidrome()
runNavidrome(context.Background())
},
Version: consts.Version,
}
@@ -60,7 +60,7 @@ func preRun() {
conf.Load()
}
func runNavidrome() {
func runNavidrome(ctx context.Context) {
db.Init()
defer func() {
if err := db.Close(); err != nil {
@@ -69,7 +69,7 @@ func runNavidrome() {
log.Info("Navidrome stopped, bye.")
}()
g, ctx := errgroup.WithContext(context.Background())
g, ctx := errgroup.WithContext(ctx)
g.Go(startServer(ctx))
g.Go(startSignaler(ctx))
g.Go(startScheduler(ctx))

191
cmd/svc.go Normal file
View File

@@ -0,0 +1,191 @@
package cmd
import (
"context"
"fmt"
"os"
"path/filepath"
"sync"
"github.com/kardianos/service"
"github.com/navidrome/navidrome/conf"
"github.com/navidrome/navidrome/log"
"github.com/spf13/cobra"
)
var (
svcStatusLabels = map[service.Status]string{
service.StatusUnknown: "Unknown",
service.StatusStopped: "Stopped",
service.StatusRunning: "Running",
}
)
func init() {
svcCmd.AddCommand(buildInstallCmd())
svcCmd.AddCommand(buildUninstallCmd())
svcCmd.AddCommand(buildStartCmd())
svcCmd.AddCommand(buildStopCmd())
svcCmd.AddCommand(buildStatusCmd())
rootCmd.AddCommand(svcCmd)
}
var svcCmd = &cobra.Command{
Use: "service",
Aliases: []string{"svc"},
Short: "Manage Navidrome as a service",
Long: fmt.Sprintf("Manage Navidrome as a service, using the OS service manager (%s)", service.Platform()),
Run: runServiceCmd,
}
type svcControl struct {
ctx context.Context
cancel context.CancelFunc
}
func (p *svcControl) Start(_ service.Service) error {
p.ctx, p.cancel = context.WithCancel(context.Background())
go p.run()
return nil
}
func (p *svcControl) run() {
runNavidrome(p.ctx)
}
func (p *svcControl) Stop(_ service.Service) error {
log.Info("Stopping service")
p.cancel()
return nil
}
var (
svc service.Service
svcOnce = sync.Once{}
)
func svcInstance() service.Service {
svcOnce.Do(func() {
options := make(service.KeyValue)
options["Restart"] = "on-success"
options["SuccessExitStatus"] = "1 2 8 SIGKILL"
options["UserService"] = true
options["LogDirectory"] = conf.Server.DataFolder
svcConfig := &service.Config{
Name: "Navidrome",
DisplayName: "Navidrome",
Description: "Navidrome is a self-hosted music server and streamer",
Dependencies: []string{
"Requires=network.target",
"After=network-online.target syslog.target"},
WorkingDirectory: executablePath(),
Option: options,
}
if conf.Server.ConfigFile != "" {
svcConfig.Arguments = []string{"-c", conf.Server.ConfigFile}
}
prg := &svcControl{}
var err error
svc, err = service.New(prg, svcConfig)
if err != nil {
log.Fatal(err)
}
})
return svc
}
func runServiceCmd(cmd *cobra.Command, _ []string) {
_ = cmd.Help()
}
func executablePath() string {
ex, err := os.Executable()
if err != nil {
log.Fatal(err)
}
return filepath.Dir(ex)
}
func buildInstallCmd() *cobra.Command {
runInstallCmd := func(_ *cobra.Command, _ []string) {
var err error
println("Installing service with:")
println(" working directory: " + executablePath())
println(" music folder: " + conf.Server.MusicFolder)
println(" data folder: " + conf.Server.DataFolder)
if cfgFile != "" {
conf.Server.ConfigFile, err = filepath.Abs(cfgFile)
if err != nil {
log.Fatal(err)
}
println(" config file: " + conf.Server.ConfigFile)
}
err = svcInstance().Install()
if err != nil {
log.Fatal(err)
}
println("Service installed. Use 'navidrome svc start' to start it.")
}
return &cobra.Command{
Use: "install",
Short: "Install Navidrome service.",
Run: runInstallCmd,
}
}
func buildUninstallCmd() *cobra.Command {
return &cobra.Command{
Use: "uninstall",
Short: "Uninstall Navidrome service. Does not delete the music or data folders",
Run: func(cmd *cobra.Command, args []string) {
err := svcInstance().Uninstall()
if err != nil {
log.Fatal(err)
}
println("Service uninstalled. Music and data folders are still intact.")
},
}
}
func buildStartCmd() *cobra.Command {
return &cobra.Command{
Use: "start",
Short: "Start Navidrome service",
Run: func(cmd *cobra.Command, args []string) {
err := svcInstance().Start()
if err != nil {
log.Fatal(err)
}
println("Service started. Use 'navidrome svc status' to check its status.")
},
}
}
func buildStopCmd() *cobra.Command {
return &cobra.Command{
Use: "stop",
Short: "Stop Navidrome service",
Run: func(cmd *cobra.Command, args []string) {
err := svcInstance().Stop()
if err != nil {
log.Fatal(err)
}
println("Service stopped. Use 'navidrome svc status' to check its status.")
},
}
}
func buildStatusCmd() *cobra.Command {
return &cobra.Command{
Use: "status",
Short: "Show Navidrome service status",
Run: func(cmd *cobra.Command, args []string) {
status, err := svcInstance().Status()
if err != nil {
log.Fatal(err)
}
fmt.Printf("Navidrome is %s.\n", svcStatusLabels[status])
},
}
}

1
go.mod
View File

@@ -24,6 +24,7 @@ require (
github.com/google/uuid v1.3.0
github.com/google/wire v0.5.0
github.com/hashicorp/go-multierror v1.1.1
github.com/kardianos/service v1.2.2
github.com/kr/pretty v0.3.1
github.com/lestrrat-go/jwx/v2 v2.0.9
github.com/matoous/go-nanoid/v2 v2.0.0

3
go.sum
View File

@@ -208,6 +208,8 @@ github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/kardianos/service v1.2.2 h1:ZvePhAHfvo0A7Mftk/tEzqEZ7Q4lgnR8sGz4xu1YX60=
github.com/kardianos/service v1.2.2/go.mod h1:CIMRFEJVL+0DS1a3Nx06NaMn4Dz63Ng6O7dl0qH0zVM=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
@@ -500,6 +502,7 @@ golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=