more playback commands
This commit is contained in:
+105
-25
@@ -3,24 +3,36 @@ package backend
|
||||
import (
|
||||
"slices"
|
||||
"sync"
|
||||
|
||||
"github.com/dweymouth/supersonic/backend/mediaprovider"
|
||||
)
|
||||
|
||||
type PlaybackCommandType int
|
||||
type playbackCommandType int
|
||||
|
||||
const (
|
||||
CmdStop PlaybackCommandType = iota
|
||||
CmdContinue
|
||||
CmdPause
|
||||
CmdPlayTrackAt
|
||||
CmdSeekSeconds
|
||||
CmdSeekFwdBackN
|
||||
CmdVolume
|
||||
CmdLoopMode
|
||||
cmdStop playbackCommandType = iota
|
||||
cmdContinue
|
||||
cmdPause
|
||||
cmdPlayTrackAt // arg: int
|
||||
cmdSeekSeconds // arg: float64
|
||||
cmdSeekFwdBackN // arg: int
|
||||
cmdVolume // arg: int
|
||||
cmdLoopMode // arg: LoopMode
|
||||
cmdStopAndClearPlayQueue
|
||||
cmdUpdatePlayQueue // arg: []mediaprovider.MediaItem
|
||||
cmdRemoveTracksFromQueue // arg: []int
|
||||
// arg: []mediaprovider.MediaItem
|
||||
// arg2: InsertMode
|
||||
// arg3: bool (shuffle)
|
||||
cmdLoadItems
|
||||
cmdLoadRadioStation // arg: *mediaprovider.RadioStation, arg2: InsertQueueMode
|
||||
)
|
||||
|
||||
type PlaybackCommand struct {
|
||||
Type PlaybackCommandType
|
||||
Type playbackCommandType
|
||||
Arg any
|
||||
Arg2 any
|
||||
Arg3 any
|
||||
}
|
||||
|
||||
type CommandQueue struct {
|
||||
@@ -42,36 +54,84 @@ func (c *CommandQueue) C() <-chan PlaybackCommand {
|
||||
}
|
||||
|
||||
func (c *CommandQueue) Stop() {
|
||||
c.filterCommandsAndAdd([]PlaybackCommandType{CmdContinue, CmdPause, CmdStop},
|
||||
PlaybackCommand{Type: CmdStop})
|
||||
c.filterCommandsAndAdd([]playbackCommandType{cmdContinue, cmdPause, cmdStop},
|
||||
PlaybackCommand{Type: cmdStop})
|
||||
}
|
||||
|
||||
func (c *CommandQueue) Continue() {
|
||||
c.filterCommandsAndAdd([]PlaybackCommandType{CmdContinue, CmdPause, CmdStop},
|
||||
PlaybackCommand{Type: CmdContinue})
|
||||
c.filterCommandsAndAdd([]playbackCommandType{cmdContinue, cmdPause, cmdStop},
|
||||
PlaybackCommand{Type: cmdContinue})
|
||||
}
|
||||
|
||||
func (c *CommandQueue) Pause() {
|
||||
c.filterCommandsAndAdd([]PlaybackCommandType{CmdContinue, CmdPause, CmdStop},
|
||||
PlaybackCommand{Type: CmdPause})
|
||||
c.filterCommandsAndAdd([]playbackCommandType{cmdContinue, cmdPause, cmdStop},
|
||||
PlaybackCommand{Type: cmdPause})
|
||||
}
|
||||
|
||||
func (c *CommandQueue) Volume(vol int) {
|
||||
c.filterCommandsAndAdd([]PlaybackCommandType{CmdVolume},
|
||||
PlaybackCommand{Type: CmdVolume, Arg: vol})
|
||||
func (c *CommandQueue) StopAndClearPlayQueue() {
|
||||
c.filterCommandsAndAdd([]playbackCommandType{cmdContinue, cmdPause, cmdStop, cmdStopAndClearPlayQueue},
|
||||
PlaybackCommand{Type: cmdStopAndClearPlayQueue})
|
||||
}
|
||||
|
||||
func (c *CommandQueue) LoopMode(mode LoopMode) {
|
||||
c.filterCommandsAndAdd([]PlaybackCommandType{CmdLoopMode},
|
||||
PlaybackCommand{Type: CmdLoopMode, Arg: mode})
|
||||
func (c *CommandQueue) SetVolume(vol int) {
|
||||
c.filterCommandsAndAdd([]playbackCommandType{cmdVolume},
|
||||
PlaybackCommand{Type: cmdVolume, Arg: vol})
|
||||
}
|
||||
|
||||
func (c *CommandQueue) SetLoopMode(mode LoopMode) {
|
||||
c.filterCommandsAndAdd([]playbackCommandType{cmdLoopMode},
|
||||
PlaybackCommand{Type: cmdLoopMode, Arg: mode})
|
||||
}
|
||||
|
||||
func (c *CommandQueue) SeekSeconds(s float64) {
|
||||
c.filterCommandsAndAdd([]PlaybackCommandType{CmdVolume},
|
||||
PlaybackCommand{Type: CmdSeekSeconds, Arg: s})
|
||||
c.filterCommandsAndAdd([]playbackCommandType{cmdSeekSeconds},
|
||||
PlaybackCommand{Type: cmdSeekSeconds, Arg: s})
|
||||
}
|
||||
|
||||
func (c *CommandQueue) filterCommandsAndAdd(excludeTypes []PlaybackCommandType, command PlaybackCommand) {
|
||||
func (c *CommandQueue) SeekNext() {
|
||||
c.seekBackOrFwd(1)
|
||||
}
|
||||
|
||||
func (c *CommandQueue) SeekBackOrPrevious() {
|
||||
c.seekBackOrFwd(-1)
|
||||
}
|
||||
|
||||
func (c *CommandQueue) UpdatePlayQueue(items []mediaprovider.MediaItem) {
|
||||
c.filterCommandsAndAdd([]playbackCommandType{cmdUpdatePlayQueue},
|
||||
PlaybackCommand{Type: cmdUpdatePlayQueue, Arg: items})
|
||||
}
|
||||
|
||||
func (c *CommandQueue) RemoveItemsFromQueue(idxs []int) {
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
c.queue = append(c.queue, PlaybackCommand{
|
||||
Type: cmdRemoveTracksFromQueue,
|
||||
Arg: idxs,
|
||||
})
|
||||
}
|
||||
|
||||
func (c *CommandQueue) LoadRadioStation(radio *mediaprovider.RadioStation, insertMode InsertQueueMode) {
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
c.queue = append(c.queue, PlaybackCommand{
|
||||
Type: cmdLoadRadioStation,
|
||||
Arg: radio,
|
||||
Arg2: insertMode,
|
||||
})
|
||||
}
|
||||
|
||||
func (c *CommandQueue) LoadItems(items []mediaprovider.MediaItem, insertQueueMode InsertQueueMode, shuffle bool) {
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
c.queue = append(c.queue, PlaybackCommand{
|
||||
Type: cmdLoadItems,
|
||||
Arg: items,
|
||||
Arg2: insertQueueMode,
|
||||
Arg3: shuffle,
|
||||
})
|
||||
}
|
||||
|
||||
func (c *CommandQueue) filterCommandsAndAdd(excludeTypes []playbackCommandType, command PlaybackCommand) {
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
|
||||
@@ -87,6 +147,26 @@ func (c *CommandQueue) filterCommandsAndAdd(excludeTypes []PlaybackCommandType,
|
||||
c.queue = append(c.queue, command)
|
||||
}
|
||||
|
||||
func (c *CommandQueue) seekBackOrFwd(direction int) {
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
|
||||
j := 0
|
||||
n := 0
|
||||
for _, cmd := range c.queue {
|
||||
if cmd.Type == cmdSeekFwdBackN {
|
||||
n += cmd.Arg.(int)
|
||||
} else {
|
||||
c.queue[j] = cmd
|
||||
j++
|
||||
}
|
||||
}
|
||||
c.queue = c.queue[:j]
|
||||
c.queue = append(c.queue, PlaybackCommand{
|
||||
Type: cmdSeekFwdBackN,
|
||||
Arg: n + direction})
|
||||
}
|
||||
|
||||
func (c *CommandQueue) chanWriter() {
|
||||
for {
|
||||
c.mutex.Lock()
|
||||
|
||||
@@ -213,14 +213,14 @@ func (p *playbackEngine) Continue() error {
|
||||
// Load items into the play queue.
|
||||
// If replacing the current queue (!appendToQueue), playback will be stopped.
|
||||
func (p *playbackEngine) LoadItems(items []mediaprovider.MediaItem, insertQueueMode InsertQueueMode, shuffle bool) error {
|
||||
newItems := p.deepCopyMediaItemSlice(items)
|
||||
newItems := deepCopyMediaItemSlice(items)
|
||||
return p.doLoaditems(newItems, insertQueueMode, shuffle)
|
||||
}
|
||||
|
||||
// Load tracks into the play queue.
|
||||
// If replacing the current queue (!appendToQueue), playback will be stopped.
|
||||
func (p *playbackEngine) LoadTracks(tracks []*mediaprovider.Track, insertQueueMode InsertQueueMode, shuffle bool) error {
|
||||
newTracks := p.copyTrackSliceToMediaItemSlice(tracks)
|
||||
newTracks := copyTrackSliceToMediaItemSlice(tracks)
|
||||
return p.doLoaditems(newTracks, insertQueueMode, shuffle)
|
||||
}
|
||||
|
||||
@@ -288,7 +288,7 @@ func (p *playbackEngine) StopAndClearPlayQueue() {
|
||||
}
|
||||
|
||||
func (p *playbackEngine) GetPlayQueue() []mediaprovider.MediaItem {
|
||||
return p.deepCopyMediaItemSlice(p.playQueue)
|
||||
return deepCopyMediaItemSlice(p.playQueue)
|
||||
}
|
||||
|
||||
// Any time the user changes the favorite status of a track elsewhere in the app,
|
||||
@@ -315,7 +315,7 @@ func (p *playbackEngine) OnTrackRatingChanged(id string, rating int) {
|
||||
// Does not stop playback if the currently playing track is in the new queue,
|
||||
// but updates the now playing index to point to the first instance of the track in the new queue.
|
||||
func (p *playbackEngine) UpdatePlayQueue(items []mediaprovider.MediaItem) error {
|
||||
newQueue := p.deepCopyMediaItemSlice(items)
|
||||
newQueue := deepCopyMediaItemSlice(items)
|
||||
newNowPlayingIdx := -1
|
||||
if p.nowPlayingIdx >= 0 {
|
||||
nowPlayingID := p.playQueue[p.nowPlayingIdx].Metadata().ID
|
||||
@@ -591,7 +591,7 @@ func (p *playbackEngine) sendNowPlayingScrobble() {
|
||||
|
||||
// creates a deep copy of the track info so that we can maintain our own state
|
||||
// (play count increases, favorite, and rating) without messing up other views' track models
|
||||
func (p *playbackEngine) deepCopyMediaItemSlice(tracks []mediaprovider.MediaItem) []mediaprovider.MediaItem {
|
||||
func deepCopyMediaItemSlice(tracks []mediaprovider.MediaItem) []mediaprovider.MediaItem {
|
||||
newTracks := make([]mediaprovider.MediaItem, len(tracks))
|
||||
for i, tr := range tracks {
|
||||
newTracks[i] = tr.Copy()
|
||||
@@ -599,7 +599,7 @@ func (p *playbackEngine) deepCopyMediaItemSlice(tracks []mediaprovider.MediaItem
|
||||
return newTracks
|
||||
}
|
||||
|
||||
func (p *playbackEngine) copyTrackSliceToMediaItemSlice(tracks []*mediaprovider.Track) []mediaprovider.MediaItem {
|
||||
func copyTrackSliceToMediaItemSlice(tracks []*mediaprovider.Track) []mediaprovider.MediaItem {
|
||||
newTracks := make([]mediaprovider.MediaItem, len(tracks))
|
||||
for i, tr := range tracks {
|
||||
newTracks[i] = tr.Copy()
|
||||
|
||||
Reference in New Issue
Block a user