diff --git a/gui.go b/gui.go index bd33dd8..0c00275 100644 --- a/gui.go +++ b/gui.go @@ -71,7 +71,6 @@ const ( ) func InitGui(indexes *[]subsonic.SubsonicIndex, - playlists *[]subsonic.SubsonicPlaylist, connection *subsonic.SubsonicConnection, player *mpvplayer.Player, logger *logger.Logger, @@ -82,7 +81,7 @@ func InitGui(indexes *[]subsonic.SubsonicIndex, eventLoop: nil, // initialized by initEventLoops() mpvEvents: make(chan mpvplayer.UiEvent, 5), - playlists: *playlists, + playlists: []subsonic.SubsonicPlaylist{}, connection: connection, player: player, logger: logger, @@ -176,6 +175,8 @@ func InitGui(indexes *[]subsonic.SubsonicIndex, SetFocus(rootFlex). EnableMouse(true) + ui.playlistPage.UpdatePlaylists() + return ui } diff --git a/page_playlist.go b/page_playlist.go index 9af4246..af960b2 100644 --- a/page_playlist.go +++ b/page_playlist.go @@ -4,6 +4,10 @@ package main import ( + "fmt" + "sync" + "time" + "github.com/gdamore/tcell/v2" "github.com/rivo/tview" "github.com/spezifisch/stmps/logger" @@ -22,12 +26,16 @@ type PlaylistPage struct { // external refs ui *Ui logger logger.LoggerInterface + + updatingMutex sync.Locker + isUpdating bool } func (ui *Ui) createPlaylistPage() *PlaylistPage { playlistPage := PlaylistPage{ - ui: ui, - logger: ui.logger, + ui: ui, + logger: ui.logger, + updatingMutex: &sync.Mutex{}, } // left half: playlists @@ -180,19 +188,77 @@ func (p *PlaylistPage) GetCount() int { } func (p *PlaylistPage) UpdatePlaylists() { - response, err := p.ui.connection.GetPlaylists() - if err != nil { - p.logger.PrintError("GetPlaylists", err) + // There's a potential race condition here and, albeit highly unlikely to ever get hit, + // we'll put in some protection + p.updatingMutex.Lock() + defer p.updatingMutex.Unlock() + if p.isUpdating { + return } - p.ui.playlists = response.Playlists.Playlists - - p.playlistList.Clear() - p.ui.addToPlaylistList.Clear() + p.isUpdating = true + + var spinnerText []rune = []rune("⠁⠂⠄⡀⢀⠠⠐⠈") + playlistsButton := buttonOrder[2] + stop := make(chan bool) + go func() { + var idx int + timer := time.NewTicker(500 * time.Millisecond) + defer timer.Stop() + for { + select { + case <-timer.C: + p.ui.app.QueueUpdateDraw(func() { + var format string + if playlistsButton == p.ui.menuWidget.activeButton { + format = "%d: [::b][red]%c[white]%s[::-]" + } else { + format = "%d: [red]%c[white]%s" + } + label := fmt.Sprintf(format, 3, spinnerText[idx], playlistsButton) + p.ui.menuWidget.buttons[playlistsButton].SetLabel(label) + idx++ + if idx > 7 { + idx = 0 + } + }) + case <-stop: + p.ui.app.QueueUpdateDraw(func() { + var format string + if playlistsButton == p.ui.menuWidget.activeButton { + format = "%d: [::b]%s[::-]" + } else { + format = "%d: %s" + } + label := fmt.Sprintf(format, 3, playlistsButton) + p.ui.menuWidget.buttons[playlistsButton].SetLabel(label) + }) + close(stop) + return + } + } + }() - for _, playlist := range p.ui.playlists { - p.playlistList.AddItem(tview.Escape(playlist.Name), "", 0, nil) - p.ui.addToPlaylistList.AddItem(tview.Escape(playlist.Name), "", 0, nil) - } + go func() { + response, err := p.ui.connection.GetPlaylists() + if err != nil { + p.logger.PrintError("GetPlaylists", err) + } + p.updatingMutex.Lock() + defer p.updatingMutex.Unlock() + p.ui.playlists = response.Playlists.Playlists + p.ui.app.QueueUpdateDraw(func() { + p.playlistList.Clear() + p.ui.addToPlaylistList.Clear() + + for _, playlist := range p.ui.playlists { + p.playlistList.AddItem(tview.Escape(playlist.Name), "", 0, nil) + p.ui.addToPlaylistList.AddItem(tview.Escape(playlist.Name), "", 0, nil) + } + + p.isUpdating = false + }) + stop <- true + }() } func (p *PlaylistPage) handleAddPlaylistSongToQueue() { diff --git a/stmps.go b/stmps.go index 0d5532c..5a6cc3b 100644 --- a/stmps.go +++ b/stmps.go @@ -113,13 +113,6 @@ func main() { os.Exit(1) } - // TODO (B) loading playlists can take a long time on e.g. gonic if there are a lot of them; can it be done in the background? - playlistResponse, err := connection.GetPlaylists() - if err != nil { - fmt.Printf("Error fetching indexes from server: %s\n", err) - os.Exit(1) - } - if *list { fmt.Printf("Index response:\n") fmt.Printf(" Directory: %s\n", indexResponse.Directory.Name) @@ -134,7 +127,12 @@ func main() { for _, pl := range indexResponse.Indexes.Index { fmt.Printf(" %s\n", pl.Name) } - fmt.Printf("Playlist response:\n") + fmt.Printf("Playlist response: (this can take a while)\n") + playlistResponse, err := connection.GetPlaylists() + if err != nil { + fmt.Printf("Error fetching indexes from server: %s\n", err) + os.Exit(1) + } fmt.Printf(" Directory: %s\n", playlistResponse.Directory.Name) fmt.Printf(" Status: %s\n", playlistResponse.Status) fmt.Printf(" Error: %s\n", playlistResponse.Error.Message) @@ -181,7 +179,6 @@ func main() { } ui := InitGui(&indexResponse.Indexes.Index, - &playlistResponse.Playlists.Playlists, connection, player, logger, diff --git a/widget_help.go b/widget_help.go index 2b1c8c9..a3d00ae 100644 --- a/widget_help.go +++ b/widget_help.go @@ -9,7 +9,6 @@ import ( "github.com/rivo/tview" ) -// FIXME (A) invoking help and the dismissing it ('q') dismisses it forever (it can't be called back up) type HelpWidget struct { Root *tview.Flex