183 lines
4.2 KiB
Go
183 lines
4.2 KiB
Go
package backend
|
|
|
|
import (
|
|
"slices"
|
|
"sync"
|
|
|
|
"github.com/dweymouth/supersonic/backend/mediaprovider"
|
|
)
|
|
|
|
type playbackCommandType int
|
|
|
|
const (
|
|
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
|
|
Arg any
|
|
Arg2 any
|
|
Arg3 any
|
|
}
|
|
|
|
type CommandQueue struct {
|
|
mutex sync.Mutex
|
|
queue []PlaybackCommand
|
|
cmdAvailable *sync.Cond
|
|
nextChan chan (PlaybackCommand)
|
|
}
|
|
|
|
func NewCommandQueue() *CommandQueue {
|
|
c := &CommandQueue{}
|
|
c.cmdAvailable = sync.NewCond(&c.mutex)
|
|
go c.chanWriter()
|
|
return c
|
|
}
|
|
|
|
func (c *CommandQueue) C() <-chan PlaybackCommand {
|
|
return c.nextChan
|
|
}
|
|
|
|
func (c *CommandQueue) Stop() {
|
|
c.filterCommandsAndAdd([]playbackCommandType{cmdContinue, cmdPause, cmdStop},
|
|
PlaybackCommand{Type: cmdStop})
|
|
}
|
|
|
|
func (c *CommandQueue) Continue() {
|
|
c.filterCommandsAndAdd([]playbackCommandType{cmdContinue, cmdPause, cmdStop},
|
|
PlaybackCommand{Type: cmdContinue})
|
|
}
|
|
|
|
func (c *CommandQueue) Pause() {
|
|
c.filterCommandsAndAdd([]playbackCommandType{cmdContinue, cmdPause, cmdStop},
|
|
PlaybackCommand{Type: cmdPause})
|
|
}
|
|
|
|
func (c *CommandQueue) StopAndClearPlayQueue() {
|
|
c.filterCommandsAndAdd([]playbackCommandType{cmdContinue, cmdPause, cmdStop, cmdStopAndClearPlayQueue},
|
|
PlaybackCommand{Type: cmdStopAndClearPlayQueue})
|
|
}
|
|
|
|
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{cmdSeekSeconds},
|
|
PlaybackCommand{Type: cmdSeekSeconds, Arg: s})
|
|
}
|
|
|
|
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()
|
|
|
|
j := 0
|
|
for _, cmd := range c.queue {
|
|
if slices.Contains(excludeTypes, cmd.Type) {
|
|
continue
|
|
}
|
|
c.queue[j] = cmd
|
|
j++
|
|
}
|
|
c.queue = c.queue[:j]
|
|
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()
|
|
for len(c.queue) == 0 {
|
|
c.cmdAvailable.Wait()
|
|
}
|
|
cmd := c.queue[0]
|
|
copy(c.queue, c.queue[1:])
|
|
c.queue = c.queue[:len(c.queue)-1]
|
|
c.mutex.Unlock()
|
|
c.nextChan <- cmd
|
|
}
|
|
}
|