Files
go-mxl-player/internal/playback/playlist_navigation.go
T

133 lines
3.2 KiB
Go

package playback
import "errors"
type PlaylistCommandKind uint8
const (
PlaylistSelect PlaylistCommandKind = iota + 1
PlaylistNext
PlaylistPrevious
PlaylistPause
PlaylistResume
)
type PlaylistCommand struct {
Kind PlaylistCommandKind
Index int
}
type PlaylistState struct {
CurrentIndex int
HasSelection bool
}
var (
ErrPlaylistEmpty = errors.New("playlist is empty")
ErrPlaylistIndexOutOfRange = errors.New("playlist index is out of range")
ErrPlaylistNoSelection = errors.New("playlist has no selected entry")
ErrUnknownPlaylistCommand = errors.New("unknown playlist command")
)
func ApplyPlaylistCommand(
playlist Playlist,
current PlaylistState,
command PlaylistCommand,
) (PlaylistState, error) {
if err := playlist.Validate(); err != nil {
return current, err
}
if len(playlist.Entries) == 0 {
return current, ErrPlaylistEmpty
}
lastIndex := len(playlist.Entries) - 1
switch command.Kind {
case PlaylistSelect:
if command.Index < 0 || command.Index > lastIndex {
return current, ErrPlaylistIndexOutOfRange
}
return PlaylistState{CurrentIndex: command.Index, HasSelection: true}, nil
case PlaylistNext:
if !current.HasSelection {
return PlaylistState{CurrentIndex: 0, HasSelection: true}, nil
}
if current.CurrentIndex < 0 || current.CurrentIndex > lastIndex {
return current, ErrPlaylistIndexOutOfRange
}
if current.CurrentIndex == lastIndex {
if playlist.Loop {
return PlaylistState{CurrentIndex: 0, HasSelection: true}, nil
}
return current, nil
}
return PlaylistState{CurrentIndex: current.CurrentIndex + 1, HasSelection: true}, nil
case PlaylistPrevious:
if !current.HasSelection {
index := 0
if playlist.Loop {
index = lastIndex
}
return PlaylistState{CurrentIndex: index, HasSelection: true}, nil
}
if current.CurrentIndex < 0 || current.CurrentIndex > lastIndex {
return current, ErrPlaylistIndexOutOfRange
}
if current.CurrentIndex == 0 {
if playlist.Loop {
return PlaylistState{CurrentIndex: lastIndex, HasSelection: true}, nil
}
return current, nil
}
return PlaylistState{CurrentIndex: current.CurrentIndex - 1, HasSelection: true}, nil
default:
return current, ErrUnknownPlaylistCommand
}
}
func (s PlaylistState) Entry(playlist Playlist) (PlaylistEntry, bool) {
if !s.HasSelection || s.CurrentIndex < 0 || s.CurrentIndex >= len(playlist.Entries) {
return PlaylistEntry{}, false
}
return playlist.Entries[s.CurrentIndex], true
}
func ApplyPlaylistSelection(
playlist Playlist,
current PlaylistState,
command PlaylistCommand,
retry RetryPolicy,
) (
next PlaylistState,
sessionCommand SessionCommand,
apply bool,
err error,
) {
next, err = ApplyPlaylistCommand(playlist, current, command)
if err != nil {
return current, SessionCommand{}, false, err
}
apply = command.Kind == PlaylistSelect || next != current
if !apply {
return next, SessionCommand{}, false, nil
}
entry, ok := next.Entry(playlist)
if !ok {
return current, SessionCommand{}, false, ErrPlaylistNoSelection
}
session := entry.SessionConfig(retry)
if err := session.Validate(); err != nil {
return current, SessionCommand{}, false, err
}
return next, SessionCommand{
Kind: CommandSetSession,
Session: session,
}, true, nil
}