save and load play queue on exit/startup (partial impl)
This commit is contained in:
@@ -25,6 +25,7 @@ const (
|
|||||||
sessionDir = "session"
|
sessionDir = "session"
|
||||||
sessionLockFile = ".lock"
|
sessionLockFile = ".lock"
|
||||||
sessionActivateFile = ".activate"
|
sessionActivateFile = ".activate"
|
||||||
|
savedQueueFile = "saved_queue.json"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -300,6 +301,9 @@ func (a *App) DeleteServerCacheDir(serverID uuid.UUID) error {
|
|||||||
func (a *App) Shutdown() {
|
func (a *App) Shutdown() {
|
||||||
a.MPRISHandler.Shutdown()
|
a.MPRISHandler.Shutdown()
|
||||||
a.PlaybackManager.DisableCallbacks()
|
a.PlaybackManager.DisableCallbacks()
|
||||||
|
if a.Config.Application.SavePlayQueue {
|
||||||
|
SavePlayQueue(a.ServerManager.ServerID.String(), a.PlaybackManager, configdir.LocalConfig(a.appName, savedQueueFile))
|
||||||
|
}
|
||||||
a.PlaybackManager.Stop() // will trigger scrobble check
|
a.PlaybackManager.Stop() // will trigger scrobble check
|
||||||
a.Config.LocalPlayback.Volume = a.LocalPlayer.GetVolume()
|
a.Config.LocalPlayback.Volume = a.LocalPlayer.GetVolume()
|
||||||
a.cancel()
|
a.cancel()
|
||||||
@@ -308,6 +312,14 @@ func (a *App) Shutdown() {
|
|||||||
os.RemoveAll(configdir.LocalConfig(a.appName, sessionDir))
|
os.RemoveAll(configdir.LocalConfig(a.appName, sessionDir))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *App) LoadSavedPlayQueue() error {
|
||||||
|
queue, err := LoadPlayQueue(configdir.LocalConfig(a.appName, savedQueueFile), a.ServerManager)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return a.PlaybackManager.LoadTracks(queue.Tracks, false, false)
|
||||||
|
}
|
||||||
|
|
||||||
func (a *App) SaveConfigFile() {
|
func (a *App) SaveConfigFile() {
|
||||||
a.Config.WriteConfigFile(a.configPath())
|
a.Config.WriteConfigFile(a.configPath())
|
||||||
a.lastWrittenCfg = *a.Config
|
a.lastWrittenCfg = *a.Config
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ type AppConfig struct {
|
|||||||
SettingsTab string
|
SettingsTab string
|
||||||
AllowMultiInstance bool
|
AllowMultiInstance bool
|
||||||
MaxImageCacheSizeMB int
|
MaxImageCacheSizeMB int
|
||||||
|
SavePlayQueue bool
|
||||||
|
|
||||||
// Experimental - may be removed in future
|
// Experimental - may be removed in future
|
||||||
FontNormalTTF string
|
FontNormalTTF string
|
||||||
@@ -147,6 +148,7 @@ func DefaultConfig(appVersionTag string) *Config {
|
|||||||
AllowMultiInstance: false,
|
AllowMultiInstance: false,
|
||||||
MaxImageCacheSizeMB: 50,
|
MaxImageCacheSizeMB: 50,
|
||||||
UIScaleSize: "Normal",
|
UIScaleSize: "Normal",
|
||||||
|
SavePlayQueue: false,
|
||||||
},
|
},
|
||||||
AlbumPage: AlbumPageConfig{
|
AlbumPage: AlbumPageConfig{
|
||||||
TracklistColumns: []string{"Artist", "Time", "Plays", "Favorite", "Rating"},
|
TracklistColumns: []string{"Artist", "Time", "Plays", "Favorite", "Rating"},
|
||||||
|
|||||||
@@ -0,0 +1,83 @@
|
|||||||
|
package backend
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/dweymouth/supersonic/backend/mediaprovider"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SavedPlayQueue struct {
|
||||||
|
Tracks []*mediaprovider.Track
|
||||||
|
TrackIndex int
|
||||||
|
TimePos float64
|
||||||
|
}
|
||||||
|
|
||||||
|
type serializedSavedPlayQueue struct {
|
||||||
|
ServerID string `json:"serverID"`
|
||||||
|
TrackIDs []string `json:"trackIDs"`
|
||||||
|
TrackIndex int `json:"trackIndex"`
|
||||||
|
TimePos float64 `json:"timePos"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// SavePlayQueue saves the current play queue and playback position to a JSON file.
|
||||||
|
func SavePlayQueue(serverID string, pm *PlaybackManager, filepath string) error {
|
||||||
|
queue := pm.GetPlayQueue()
|
||||||
|
stats := pm.PlayerStatus()
|
||||||
|
trackIdx := pm.NowPlayingIndex()
|
||||||
|
|
||||||
|
trackIDs := make([]string, len(queue))
|
||||||
|
for i, tr := range queue {
|
||||||
|
trackIDs[i] = tr.ID
|
||||||
|
}
|
||||||
|
|
||||||
|
saved := serializedSavedPlayQueue{
|
||||||
|
ServerID: serverID,
|
||||||
|
TrackIDs: trackIDs,
|
||||||
|
TrackIndex: trackIdx,
|
||||||
|
TimePos: stats.TimePos,
|
||||||
|
}
|
||||||
|
b, err := json.Marshal(saved)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return os.WriteFile(filepath, b, 0644)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loads the saved play queue from the given filepath using the current server.
|
||||||
|
// Returns an error if the queue could not be loaded for any reason, including the
|
||||||
|
// currently logged in server being different than the server from which the queue was saved.
|
||||||
|
func LoadPlayQueue(filepath string, sm *ServerManager) (*SavedPlayQueue, error) {
|
||||||
|
b, err := os.ReadFile(filepath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var savedData serializedSavedPlayQueue
|
||||||
|
if err := json.Unmarshal(b, &savedData); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if sm.ServerID.String() != savedData.ServerID {
|
||||||
|
return nil, errors.New("saved play queue was from a different server")
|
||||||
|
}
|
||||||
|
|
||||||
|
tracks := make([]*mediaprovider.Track, len(savedData.TrackIDs))
|
||||||
|
mp := sm.Server
|
||||||
|
for i, id := range savedData.TrackIDs {
|
||||||
|
if tr, err := mp.GetTrack(id); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else {
|
||||||
|
tracks[i] = tr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
savedQueue := &SavedPlayQueue{
|
||||||
|
Tracks: tracks,
|
||||||
|
TrackIndex: savedData.TrackIndex,
|
||||||
|
TimePos: savedData.TimePos,
|
||||||
|
}
|
||||||
|
return savedQueue, nil
|
||||||
|
}
|
||||||
+37
-25
@@ -2,6 +2,7 @@ package ui
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/20after4/configdir"
|
"github.com/20after4/configdir"
|
||||||
@@ -92,31 +93,7 @@ func NewMainWindow(fyneApp fyne.App, appName, displayAppName, appVersion string,
|
|||||||
m.Window.SetTitle(fmt.Sprintf("%s – %s · %s", song.Name, strings.Join(song.ArtistNames, ", "), displayAppName))
|
m.Window.SetTitle(fmt.Sprintf("%s – %s · %s", song.Name, strings.Join(song.ArtistNames, ", "), displayAppName))
|
||||||
})
|
})
|
||||||
app.ServerManager.OnServerConnected(func() {
|
app.ServerManager.OnServerConnected(func() {
|
||||||
m.BrowsingPane.EnableNavigationButtons()
|
m.RunOnServerConnectedTasks(app, displayAppName)
|
||||||
m.Router.NavigateTo(m.StartupPage())
|
|
||||||
_, canRate := m.App.ServerManager.Server.(mediaprovider.SupportsRating)
|
|
||||||
m.BottomPanel.NowPlaying.DisableRating = !canRate
|
|
||||||
// check if launching new version, else if found available update on startup
|
|
||||||
if l := app.Config.Application.LastLaunchedVersion; app.VersionTag() != l {
|
|
||||||
if !app.IsFirstLaunch() {
|
|
||||||
m.ShowWhatsNewDialog()
|
|
||||||
}
|
|
||||||
m.App.Config.Application.LastLaunchedVersion = app.VersionTag()
|
|
||||||
} else if t := app.UpdateChecker.VersionTagFound(); t != "" && t != app.Config.Application.LastCheckedVersion {
|
|
||||||
if t != app.VersionTag() {
|
|
||||||
m.ShowNewVersionDialog(displayAppName, t)
|
|
||||||
}
|
|
||||||
m.App.Config.Application.LastCheckedVersion = t
|
|
||||||
}
|
|
||||||
// register callback for the ongoing periodic update check
|
|
||||||
m.App.UpdateChecker.OnUpdatedVersionFound = func() {
|
|
||||||
t := m.App.UpdateChecker.VersionTagFound()
|
|
||||||
if t != app.VersionTag() {
|
|
||||||
m.ShowNewVersionDialog(displayAppName, t)
|
|
||||||
}
|
|
||||||
m.App.Config.Application.LastCheckedVersion = t
|
|
||||||
}
|
|
||||||
m.App.SaveConfigFile()
|
|
||||||
})
|
})
|
||||||
app.ServerManager.OnLogout(func() {
|
app.ServerManager.OnLogout(func() {
|
||||||
m.BrowsingPane.DisableNavigationButtons()
|
m.BrowsingPane.DisableNavigationButtons()
|
||||||
@@ -158,6 +135,41 @@ func (m *MainWindow) StartupPage() controller.Route {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *MainWindow) RunOnServerConnectedTasks(app *backend.App, displayAppName string) {
|
||||||
|
m.BrowsingPane.EnableNavigationButtons()
|
||||||
|
m.Router.NavigateTo(m.StartupPage())
|
||||||
|
_, canRate := m.App.ServerManager.Server.(mediaprovider.SupportsRating)
|
||||||
|
m.BottomPanel.NowPlaying.DisableRating = !canRate
|
||||||
|
|
||||||
|
if app.Config.Application.SavePlayQueue {
|
||||||
|
if err := app.LoadSavedPlayQueue(); err != nil {
|
||||||
|
log.Printf("failed to load saved play queue: %s", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if launching new version, else if found available update on startup
|
||||||
|
if l := app.Config.Application.LastLaunchedVersion; app.VersionTag() != l {
|
||||||
|
if !app.IsFirstLaunch() {
|
||||||
|
m.ShowWhatsNewDialog()
|
||||||
|
}
|
||||||
|
m.App.Config.Application.LastLaunchedVersion = app.VersionTag()
|
||||||
|
} else if t := app.UpdateChecker.VersionTagFound(); t != "" && t != app.Config.Application.LastCheckedVersion {
|
||||||
|
if t != app.VersionTag() {
|
||||||
|
m.ShowNewVersionDialog(displayAppName, t)
|
||||||
|
}
|
||||||
|
m.App.Config.Application.LastCheckedVersion = t
|
||||||
|
}
|
||||||
|
// register callback for the ongoing periodic update check
|
||||||
|
m.App.UpdateChecker.OnUpdatedVersionFound = func() {
|
||||||
|
t := m.App.UpdateChecker.VersionTagFound()
|
||||||
|
if t != app.VersionTag() {
|
||||||
|
m.ShowNewVersionDialog(displayAppName, t)
|
||||||
|
}
|
||||||
|
m.App.Config.Application.LastCheckedVersion = t
|
||||||
|
}
|
||||||
|
m.App.SaveConfigFile()
|
||||||
|
}
|
||||||
|
|
||||||
func (m *MainWindow) SetupSystemTrayMenu(appName string, fyneApp fyne.App) {
|
func (m *MainWindow) SetupSystemTrayMenu(appName string, fyneApp fyne.App) {
|
||||||
if desk, ok := fyneApp.(desktop.App); ok {
|
if desk, ok := fyneApp.(desktop.App); ok {
|
||||||
menu := fyne.NewMenu(appName,
|
menu := fyne.NewMenu(appName,
|
||||||
|
|||||||
Reference in New Issue
Block a user