Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/issue-54-save-queue-on-exit'
Browse files Browse the repository at this point in the history
  • Loading branch information
spezifisch committed Oct 22, 2024
2 parents 2fbe000 + d827e3d commit 110465c
Show file tree
Hide file tree
Showing 8 changed files with 78 additions and 4 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,9 @@ These controls are accessible from any view:
- `j`: Move song down in queue
- `s`: Save the queue as a playlist
- `S`: Shuffle the songs in the queue
- `l`: Load a queue previously saved to the server

When stmps exits, the queue is automatically recorded to the server, including the position in the song being played. There is a *single* queue per user that can be thusly saved. Because empty queues can not be stored on Subsonic servers, this queue is not automatically loaded; the `l` binding on the queue page will load the previous queue and seek to the last position in the top song.

If the currently playing song is moved, the music is stopped before the move, and must be re-started manually.

Expand Down
18 changes: 17 additions & 1 deletion gui_handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
package main

import (
"log"

"github.com/gdamore/tcell/v2"
"github.com/spezifisch/stmps/mpvplayer"
"github.com/spezifisch/stmps/subsonic"
Expand Down Expand Up @@ -119,7 +121,21 @@ func (ui *Ui) ShowPage(name string) {
}

func (ui *Ui) Quit() {
// TODO savePlayQueue/getPlayQueue
if len(ui.queuePage.queueData.playerQueue) > 0 {
ids := make([]string, len(ui.queuePage.queueData.playerQueue))
for i, it := range ui.queuePage.queueData.playerQueue {
ids[i] = it.Id
}
// stmps always only ever plays the first song in the queue
pos := ui.player.GetTimePos()
if err := ui.connection.SavePlayQueue(ids, ids[0], int(pos)); err != nil {
log.Printf("error stashing play queue: %s", err)
}
} else {
// The only way to purge a saved play queue is to force an error by providing
// bad data. Therefore, we ignore errors.
_ = ui.connection.SavePlayQueue([]string{"XXX"}, "XXX", 0)
}
ui.player.Quit()
ui.app.Stop()
}
Expand Down
1 change: 1 addition & 0 deletions help_text.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ k move selected song up in queue
j move selected song down in queue
s save queue as a playlist
S shuffle the current queue
l load last queue from server
`

const helpPagePlaylists = `
Expand Down
4 changes: 2 additions & 2 deletions mpvplayer/player.go
Original file line number Diff line number Diff line change
Expand Up @@ -376,8 +376,8 @@ func (p *Player) IsSeeking() (bool, error) {
return false, nil
}

func (p *Player) SeekAbsolute(float64) error {
return nil
func (p *Player) SeekAbsolute(position int) error {
return p.instance.Command([]string{"seek", strconv.Itoa(position), "absolute"})
}

func (p *Player) Play() error {
Expand Down
5 changes: 5 additions & 0 deletions page_playlist.go
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,11 @@ func (p *PlaylistPage) UpdatePlaylists() {
if err != nil {
p.logger.PrintError("GetPlaylists", err)
}
if response == nil {
p.logger.Printf("no error from GetPlaylists, but also no response!")
stop <- true
return
}
p.updatingMutex.Lock()
defer p.updatingMutex.Unlock()
p.ui.playlists = response.Playlists.Playlists
Expand Down
24 changes: 24 additions & 0 deletions page_queue.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,30 @@ func (ui *Ui) createQueuePage() *QueuePage {
queuePage.ui.ShowSelectPlaylist()
case 'S':
queuePage.shuffle()
case 'l':
go func() {
ssr, err := queuePage.ui.connection.LoadPlayQueue()
if err != nil {
queuePage.logger.Printf("unable to load play queue from server: %s", err)
return
}
queuePage.queueList.Clear()
queuePage.queueData.Clear()
if ssr.PlayQueue.Entries != nil {
for _, ent := range ssr.PlayQueue.Entries {
ui.addSongToQueue(&ent)
}
ui.queuePage.UpdateQueue()
if err := ui.player.Play(); err != nil {
queuePage.logger.Printf("error playing: %s", err)
}
if err = ui.player.Seek(ssr.PlayQueue.Position); err != nil {
queuePage.logger.Printf("unable to seek to position %s: %s", time.Duration(ssr.PlayQueue.Position)*time.Second, err)
}
_ = ui.player.Pause()
}
}()

default:
return event
}
Expand Down
2 changes: 1 addition & 1 deletion remote/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ type ControlledPlayer interface {
Play() error
Pause() error
Stop() error
SeekAbsolute(float64) error
SeekAbsolute(int) error
NextTrack() error
PreviousTrack() error

Expand Down
25 changes: 25 additions & 0 deletions subsonic/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,12 @@ type ScanStatus struct {
Count int `json:"count"`
}

type PlayQueue struct {
Current string `json:"current"`
Position int `json:"position"`
Entries SubsonicEntities `json:"entry"`
}

type Artist struct {
Id string `json:"id"`
Name string `json:"name"`
Expand Down Expand Up @@ -271,6 +277,7 @@ type SubsonicResponse struct {
Album Album `json:"album"`
SearchResults SubsonicResults `json:"searchResult3"`
ScanStatus ScanStatus `json:"scanStatus"`
PlayQueue PlayQueue `json:"playQueue"`
}

type responseWrapper struct {
Expand Down Expand Up @@ -663,3 +670,21 @@ func (connection *SubsonicConnection) StartScan() error {
}
return nil
}

func (connection *SubsonicConnection) SavePlayQueue(queueIds []string, current string, position int) error {
query := defaultQuery(connection)
for _, songId := range queueIds {
query.Add("id", songId)
}
query.Set("current", current)
query.Set("position", fmt.Sprintf("%d", position))
requestUrl := fmt.Sprintf("%s/rest/savePlayQueue?%s", connection.Host, query.Encode())
_, err := connection.getResponse("SavePlayQueue", requestUrl)
return err
}

func (connection *SubsonicConnection) LoadPlayQueue() (*SubsonicResponse, error) {
query := defaultQuery(connection)
requestUrl := fmt.Sprintf("%s/rest/getPlayQueue?%s", connection.Host, query.Encode())
return connection.getResponse("GetPlayQueue", requestUrl)
}

0 comments on commit 110465c

Please sign in to comment.