create UpdateChecker background task + constants refactoring
This commit is contained in:
+28
-23
@@ -10,15 +10,14 @@ import (
|
||||
"supersonic/backend/util"
|
||||
"supersonic/player"
|
||||
"supersonic/sharedutil"
|
||||
"time"
|
||||
|
||||
"github.com/20after4/configdir"
|
||||
"github.com/zalando/go-keyring"
|
||||
)
|
||||
|
||||
const (
|
||||
AppName = "supersonic"
|
||||
configFile = "config.toml"
|
||||
LatestReleaseURL = "https://github.com/dweymouth/supersonic/releases/latest"
|
||||
configFile = "config.toml"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -32,21 +31,27 @@ type App struct {
|
||||
LibraryManager *LibraryManager
|
||||
PlaybackManager *PlaybackManager
|
||||
Player *player.Player
|
||||
UpdateChecker UpdateChecker
|
||||
|
||||
bgrndCtx context.Context
|
||||
cancel context.CancelFunc
|
||||
appName string
|
||||
appVersionTag string
|
||||
bgrndCtx context.Context
|
||||
cancel context.CancelFunc
|
||||
}
|
||||
|
||||
func StartupApp() (*App, error) {
|
||||
a := &App{}
|
||||
func StartupApp(appName, appVersionTag, latestReleaseURL string) (*App, error) {
|
||||
a := &App{appName: appName, appVersionTag: appVersionTag}
|
||||
a.bgrndCtx, a.cancel = context.WithCancel(context.Background())
|
||||
|
||||
log.Printf("Starting %s...", AppName)
|
||||
log.Printf("Using config dir: %s", configdir.LocalConfig(AppName))
|
||||
log.Printf("Using cache dir: %s", configdir.LocalCache(AppName))
|
||||
log.Printf("Starting %s...", appName)
|
||||
log.Printf("Using config dir: %s", configdir.LocalConfig(appName))
|
||||
log.Printf("Using cache dir: %s", configdir.LocalCache(appName))
|
||||
|
||||
a.readConfig()
|
||||
|
||||
a.UpdateChecker = NewUpdateChecker(appVersionTag, latestReleaseURL, &a.Config.Application.LastCheckedVersion)
|
||||
a.UpdateChecker.Start(a.bgrndCtx, 24*time.Hour)
|
||||
|
||||
if err := a.initMPV(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -63,10 +68,10 @@ func StartupApp() (*App, error) {
|
||||
PreampGain: a.Config.ReplayGain.PreampGainDB,
|
||||
})
|
||||
|
||||
a.ServerManager = NewServerManager()
|
||||
a.ServerManager = NewServerManager(appName)
|
||||
a.PlaybackManager = NewPlaybackManager(a.bgrndCtx, a.ServerManager, a.Player, &a.Config.Scrobbling)
|
||||
a.LibraryManager = NewLibraryManager(a.ServerManager)
|
||||
a.ImageManager = NewImageManager(a.bgrndCtx, a.ServerManager, configdir.LocalCache(AppName))
|
||||
a.ImageManager = NewImageManager(a.bgrndCtx, a.ServerManager, configdir.LocalCache(a.appName))
|
||||
a.LibraryManager.PreCacheCoverFn = func(coverID string) {
|
||||
_, _ = a.ImageManager.GetAlbumThumbnail(coverID)
|
||||
}
|
||||
@@ -75,23 +80,23 @@ func StartupApp() (*App, error) {
|
||||
}
|
||||
|
||||
func (a *App) readConfig() {
|
||||
configdir.MakePath(configdir.LocalConfig(AppName))
|
||||
cfgPath := configPath()
|
||||
cfg, err := ReadConfigFile(cfgPath)
|
||||
configdir.MakePath(configdir.LocalConfig(a.appName))
|
||||
cfgPath := a.configPath()
|
||||
cfg, err := ReadConfigFile(cfgPath, a.appVersionTag)
|
||||
if err != nil {
|
||||
log.Printf("Error reading app config file: %v", err)
|
||||
cfg = DefaultConfig()
|
||||
cfg = DefaultConfig(a.appVersionTag)
|
||||
if _, err := os.Stat(cfgPath); err == nil {
|
||||
backupCfgName := fmt.Sprintf("%s.bak", configFile)
|
||||
log.Printf("Config file may be malformed: copying to %s", backupCfgName)
|
||||
_ = util.CopyFile(cfgPath, path.Join(configdir.LocalConfig(AppName), backupCfgName))
|
||||
_ = util.CopyFile(cfgPath, path.Join(configdir.LocalConfig(a.appName), backupCfgName))
|
||||
}
|
||||
}
|
||||
a.Config = cfg
|
||||
}
|
||||
|
||||
func (a *App) initMPV() error {
|
||||
p := player.NewWithClientName(AppName)
|
||||
p := player.NewWithClientName(a.appName)
|
||||
c := a.Config.LocalPlayback
|
||||
c.InMemoryCacheSizeMB = clamp(c.InMemoryCacheSizeMB, 10, 500)
|
||||
if err := p.Init(c.AudioExclusive, c.InMemoryCacheSizeMB); err != nil {
|
||||
@@ -101,12 +106,12 @@ func (a *App) initMPV() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *App) LoginToDefaultServer() error {
|
||||
func (a *App) LoginToDefaultServer(string) error {
|
||||
serverCfg := a.Config.GetDefaultServer()
|
||||
if serverCfg == nil {
|
||||
return ErrNoServers
|
||||
}
|
||||
pass, err := keyring.Get(AppName, serverCfg.ID.String())
|
||||
pass, err := keyring.Get(a.appName, serverCfg.ID.String())
|
||||
if err != nil {
|
||||
return fmt.Errorf("error reading keyring credentials: %v", err)
|
||||
}
|
||||
@@ -118,11 +123,11 @@ func (a *App) Shutdown() {
|
||||
a.Config.LocalPlayback.Volume = a.Player.GetVolume()
|
||||
a.cancel()
|
||||
a.Player.Destroy()
|
||||
a.Config.WriteConfigFile(configPath())
|
||||
a.Config.WriteConfigFile(a.configPath())
|
||||
}
|
||||
|
||||
func configPath() string {
|
||||
return path.Join(configdir.LocalConfig(AppName), configFile)
|
||||
func (a *App) configPath() string {
|
||||
return path.Join(configdir.LocalConfig(a.appName), configFile)
|
||||
}
|
||||
|
||||
func clamp(i, min, max int) int {
|
||||
|
||||
+9
-7
@@ -17,8 +17,9 @@ type ServerConfig struct {
|
||||
}
|
||||
|
||||
type AppConfig struct {
|
||||
WindowWidth int
|
||||
WindowHeight int
|
||||
WindowWidth int
|
||||
WindowHeight int
|
||||
LastCheckedVersion string
|
||||
}
|
||||
|
||||
type AlbumPageConfig struct {
|
||||
@@ -79,11 +80,12 @@ type Config struct {
|
||||
ReplayGain ReplayGainConfig
|
||||
}
|
||||
|
||||
func DefaultConfig() *Config {
|
||||
func DefaultConfig(appVersionTag string) *Config {
|
||||
return &Config{
|
||||
Application: AppConfig{
|
||||
WindowWidth: 1000,
|
||||
WindowHeight: 800,
|
||||
WindowWidth: 1000,
|
||||
WindowHeight: 800,
|
||||
LastCheckedVersion: appVersionTag,
|
||||
},
|
||||
AlbumPage: AlbumPageConfig{
|
||||
TracklistColumns: []string{"Artist", "Time", "Plays", "Favorite"},
|
||||
@@ -123,14 +125,14 @@ func DefaultConfig() *Config {
|
||||
}
|
||||
}
|
||||
|
||||
func ReadConfigFile(filepath string) (*Config, error) {
|
||||
func ReadConfigFile(filepath, appVersionTag string) (*Config, error) {
|
||||
f, err := os.Open(filepath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
c := DefaultConfig()
|
||||
c := DefaultConfig(appVersionTag)
|
||||
if err := toml.NewDecoder(f).Decode(c); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -14,14 +14,15 @@ type ServerManager struct {
|
||||
ServerID uuid.UUID
|
||||
Server *subsonic.Client
|
||||
|
||||
appName string
|
||||
onServerConnected []func()
|
||||
onLogout []func()
|
||||
}
|
||||
|
||||
var ErrUnreachable = errors.New("server is unreachable")
|
||||
|
||||
func NewServerManager() *ServerManager {
|
||||
return &ServerManager{}
|
||||
func NewServerManager(appName string) *ServerManager {
|
||||
return &ServerManager{appName: appName}
|
||||
}
|
||||
|
||||
func (s *ServerManager) ConnectToServer(conf *ServerConfig, password string) error {
|
||||
@@ -75,7 +76,7 @@ func (s *ServerManager) testConnectionAndCreateClient(hostname, username, passwo
|
||||
|
||||
func (s *ServerManager) Logout() {
|
||||
if s.Server != nil {
|
||||
keyring.Delete(AppName, s.ServerID.String())
|
||||
keyring.Delete(s.appName, s.ServerID.String())
|
||||
for _, cb := range s.onLogout {
|
||||
cb()
|
||||
}
|
||||
@@ -93,9 +94,9 @@ func (s *ServerManager) OnLogout(cb func()) {
|
||||
}
|
||||
|
||||
func (s *ServerManager) GetServerPassword(server *ServerConfig) (string, error) {
|
||||
return keyring.Get(AppName, server.ID.String())
|
||||
return keyring.Get(s.appName, server.ID.String())
|
||||
}
|
||||
|
||||
func (s *ServerManager) SetServerPassword(server *ServerConfig, password string) error {
|
||||
return keyring.Set(AppName, server.ID.String(), password)
|
||||
return keyring.Set(s.appName, server.ID.String(), password)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
package backend
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type UpdateChecker struct {
|
||||
OnUpdatedVersionFound func(releaseURL string)
|
||||
|
||||
foundUpdate bool
|
||||
latestReleaseURL string
|
||||
appVersionTag string
|
||||
lastCheckedTag *string
|
||||
}
|
||||
|
||||
func NewUpdateChecker(appVersionTag, latestReleaseURL string, lastCheckedTag *string) UpdateChecker {
|
||||
return UpdateChecker{
|
||||
appVersionTag: appVersionTag,
|
||||
latestReleaseURL: latestReleaseURL,
|
||||
lastCheckedTag: lastCheckedTag,
|
||||
}
|
||||
}
|
||||
|
||||
func (u *UpdateChecker) Start(ctx context.Context, interval time.Duration) {
|
||||
go func() {
|
||||
u.checkForUpdate() // check once at startup
|
||||
t := time.NewTicker(interval)
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-t.C:
|
||||
u.checkForUpdate()
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (u *UpdateChecker) UpdateAvailable() bool {
|
||||
return u.foundUpdate
|
||||
}
|
||||
|
||||
func (u *UpdateChecker) checkForUpdate() {
|
||||
t := u.latestVersionTag()
|
||||
if t != "" && t != *u.lastCheckedTag {
|
||||
u.foundUpdate = true
|
||||
*u.lastCheckedTag = t
|
||||
if u.OnUpdatedVersionFound != nil {
|
||||
u.OnUpdatedVersionFound(u.latestReleaseURL)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (u *UpdateChecker) latestVersionTag() string {
|
||||
resp, err := http.Head(u.latestReleaseURL)
|
||||
if err != nil {
|
||||
log.Printf("failed to check for newest version: %s", err.Error())
|
||||
return ""
|
||||
}
|
||||
url := resp.Request.URL.String()
|
||||
url = strings.TrimSuffix(url, "/")
|
||||
idx := strings.LastIndex(url, "/")
|
||||
if idx >= len(url)-1 {
|
||||
return ""
|
||||
}
|
||||
return url[idx+1:]
|
||||
}
|
||||
@@ -2,11 +2,8 @@ package util
|
||||
|
||||
import (
|
||||
"io"
|
||||
"log"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -40,18 +37,3 @@ func CopyFile(srcPath, dstPath string) error {
|
||||
_, err = io.Copy(fout, fin)
|
||||
return err
|
||||
}
|
||||
|
||||
func LatestVersionTag(latestReleaseURL string) string {
|
||||
resp, err := http.Head(latestReleaseURL)
|
||||
if err != nil {
|
||||
log.Printf("failed to check for newest version: %s", err.Error())
|
||||
return ""
|
||||
}
|
||||
url := resp.Request.URL.String()
|
||||
url = strings.TrimSuffix(url, "/")
|
||||
idx := strings.LastIndex(url, "/")
|
||||
if idx >= len(url)-1 {
|
||||
return ""
|
||||
}
|
||||
return url[idx+1:]
|
||||
}
|
||||
|
||||
@@ -14,10 +14,12 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
appname = "supersonic"
|
||||
displayName = "Supersonic"
|
||||
appVersion = "0.0.1-alpha2"
|
||||
configFile = "config.toml"
|
||||
appname = "supersonic"
|
||||
displayName = "Supersonic"
|
||||
appVersion = "0.0.1-alpha2"
|
||||
appVersionTag = "v" + appVersion
|
||||
configFile = "config.toml"
|
||||
latestReleaseURL = "https://github.com/dweymouth/supersonic/releases/latest"
|
||||
)
|
||||
|
||||
func configPath() string {
|
||||
@@ -25,7 +27,7 @@ func configPath() string {
|
||||
}
|
||||
|
||||
func main() {
|
||||
myApp, err := backend.StartupApp()
|
||||
myApp, err := backend.StartupApp(appname, appVersionTag, latestReleaseURL)
|
||||
if err != nil {
|
||||
log.Fatalf("fatal startup error: %v", err.Error())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user