Skip to content

Commit

Permalink
Feat 67 hide song panel (#78)
Browse files Browse the repository at this point in the history
* Changes program exit function to properly close the asset queue. Closing the queue is not currently _required_, but if we replace the queue with an on-disk queue we may need to clean up resources; this commit ensures that doesn't get missed in the future.

* feat: #67, user can disable song info panel

* feat: #67, updates readme for how to disable the panel
  • Loading branch information
xxxserxxx committed Oct 29, 2024
1 parent cbe6b1c commit 90036e1
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 60 deletions.
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,15 @@ random-songs = 50
spinner = '▁▂▃▄▅▆▇█▇▆▅▄▃▂▁'
```

The song info panel on the queue takes up space, and memory for album art. You can disable this panel with:

```toml
[ui]
hide-info-panel = true
```

If the panel is hidden, no album art will be fetched from the server.

## Usage

### General Navigation
Expand Down
4 changes: 4 additions & 0 deletions gui_handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/gdamore/tcell/v2"
"github.com/spezifisch/stmps/mpvplayer"
"github.com/spezifisch/stmps/subsonic"
"github.com/spf13/viper"
)

func (ui *Ui) handlePageInput(event *tcell.EventKey) *tcell.EventKey {
Expand Down Expand Up @@ -136,6 +137,9 @@ func (ui *Ui) Quit() {
// bad data. Therefore, we ignore errors.
_ = ui.connection.SavePlayQueue([]string{"XXX"}, "XXX", 0)
}
if !viper.GetBool("ui.hide-song-info") {
ui.queuePage.coverArtCache.Close()
}
ui.player.Quit()
ui.app.Stop()
}
Expand Down
137 changes: 77 additions & 60 deletions page_queue.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"github.com/spezifisch/stmps/logger"
"github.com/spezifisch/stmps/mpvplayer"
"github.com/spezifisch/stmps/subsonic"
"github.com/spf13/viper"
)

// TODO show total # of entries somewhere (top?)
Expand Down Expand Up @@ -70,15 +71,21 @@ func init() {
}

func (ui *Ui) createQueuePage() *QueuePage {
tmpl := template.New("song info").Funcs(template.FuncMap{
"formatTime": func(i int) string {
return (time.Duration(i) * time.Second).String()
},
})
songInfoTemplate, err := tmpl.Parse(songInfoTemplateString)
if err != nil {
ui.logger.PrintError("createQueuePage", err)
addSongInfo := !viper.GetBool("ui.hide-song-info")

var songInfoTemplate *template.Template
if addSongInfo {
tmpl := template.New("song info").Funcs(template.FuncMap{
"formatTime": func(i int) string {
return (time.Duration(i) * time.Second).String()
},
})
var err error
if songInfoTemplate, err = tmpl.Parse(songInfoTemplateString); err != nil {
ui.logger.PrintError("createQueuePage", err)
}
}

queuePage := QueuePage{
ui: ui,
logger: ui.logger,
Expand Down Expand Up @@ -153,71 +160,81 @@ func (ui *Ui) createQueuePage() *QueuePage {

queuePage.queueList.SetSelectionChangedFunc(queuePage.changeSelection)

queuePage.coverArt = tview.NewImage()
queuePage.coverArt.SetImage(STMPS_LOGO)

infoFlex := tview.NewFlex().SetDirection(tview.FlexRow).
AddItem(queuePage.songInfo, 0, 1, false).
AddItem(queuePage.coverArt, 0, 1, false)
infoFlex.SetBorder(true)
infoFlex.SetTitle(" song info ")

// flex wrapper
queuePage.Root = tview.NewFlex().SetDirection(tview.FlexColumn).
AddItem(queuePage.queueList, 0, 2, true).
AddItem(infoFlex, 0, 1, false)

// private data
queuePage.queueData = queueData{
starIdList: ui.starIdList,
}

queuePage.coverArtCache = NewCache(
// zero value
STMPS_LOGO,
// function that loads assets; can be slow
ui.connection.GetCoverArt,
// function that gets called when the actual asset is loaded
func(imgId string, img image.Image) {
row, _ := queuePage.queueList.GetSelection()
// If nothing is selected, set the image to the logo
if row >= len(queuePage.queueData.playerQueue) || row < 0 {
// flex wrapper
queuePage.Root = tview.NewFlex().SetDirection(tview.FlexColumn).
AddItem(queuePage.queueList, 0, 2, true)

if addSongInfo {
// Song info
queuePage.songInfo = tview.NewTextView()
queuePage.songInfo.SetDynamicColors(true).SetScrollable(true)

queuePage.coverArt = tview.NewImage()
queuePage.coverArt.SetImage(STMPS_LOGO)

infoFlex := tview.NewFlex().SetDirection(tview.FlexRow).
AddItem(queuePage.songInfo, 0, 1, false).
AddItem(queuePage.coverArt, 0, 1, false)
infoFlex.SetBorder(true)
infoFlex.SetTitle(" song info ")
queuePage.Root.AddItem(infoFlex, 0, 1, false)

queuePage.coverArtCache = NewCache(
// zero value
STMPS_LOGO,
// function that loads assets; can be slow
ui.connection.GetCoverArt,
// function that gets called when the actual asset is loaded
func(imgId string, img image.Image) {
row, _ := queuePage.queueList.GetSelection()
// If nothing is selected, set the image to the logo
if row >= len(queuePage.queueData.playerQueue) || row < 0 {
ui.app.QueueUpdate(func() {
queuePage.coverArt.SetImage(STMPS_LOGO)
})
return
}
// If the fetched asset isn't the asset for the current song,
// just skip it.
currentSong := queuePage.queueData.playerQueue[row]
if currentSong.CoverArtId != imgId {
return
}
// Otherwise, the asset is for the current song, so update it
ui.app.QueueUpdate(func() {
queuePage.coverArt.SetImage(STMPS_LOGO)
queuePage.coverArt.SetImage(img)
})
return
}
// If the fetched asset isn't the asset for the current song,
// just skip it.
currentSong := queuePage.queueData.playerQueue[row]
if currentSong.CoverArtId != imgId {
return
}
// Otherwise, the asset is for the current song, so update it
ui.app.QueueUpdate(func() {
queuePage.coverArt.SetImage(img)
})
},
// function called to check if asset is invalid:
// true if it can be purged from the cache, false if it's still needed
func(assetId string) bool {
for _, song := range queuePage.queueData.playerQueue {
if song.CoverArtId == assetId {
return false
},
// function called to check if asset is invalid:
// true if it can be purged from the cache, false if it's still needed
func(assetId string) bool {
for _, song := range queuePage.queueData.playerQueue {
if song.CoverArtId == assetId {
return false
}
}
}
// Didn't find a song that needs the asset; purge it.
return true
},
// How frequently we check for invalid assets
time.Minute,
ui.logger,
)
// Didn't find a song that needs the asset; purge it.
return true
},
// How frequently we check for invalid assets
time.Minute,
ui.logger,
)
}

return &queuePage
}

func (q *QueuePage) changeSelection(row, column int) {
// If the user disabled song info, there's nothing to do
if q.songInfo == nil {
return
}
q.songInfo.Clear()
if row >= len(q.queueData.playerQueue) || row < 0 || column < 0 {
q.coverArt.SetImage(STMPS_LOGO)
Expand Down

0 comments on commit 90036e1

Please sign in to comment.