Files
AynaLivePlayer/player/playlist.go
2022-06-29 00:42:59 -07:00

197 lines
4.2 KiB
Go

package player
import (
"AynaLivePlayer/event"
"AynaLivePlayer/logger"
"github.com/sirupsen/logrus"
"math/rand"
"sync"
"time"
)
const MODULE_PLAYLIST = "Player.Playlist"
func init() {
rand.Seed(time.Now().UnixNano())
}
type PlaylistConfig struct {
RandomNext bool
}
type Playlist struct {
Index int
Name string
Config PlaylistConfig
Playlist []*Media
Handler *event.Handler
Meta interface{}
Lock sync.RWMutex
}
func NewPlaylist(name string, config PlaylistConfig) *Playlist {
return &Playlist{
Index: 0,
Name: name,
Config: config,
Playlist: make([]*Media, 0),
Handler: event.NewHandler(),
}
}
func (p *Playlist) l() *logrus.Entry {
return logger.Logger.WithFields(logrus.Fields{
"Module": MODULE_PLAYLIST,
"Name": p.Name,
})
}
func (p *Playlist) Size() int {
p.l().Tracef("getting size=%d", len(p.Playlist))
return len(p.Playlist)
}
func (p *Playlist) Pop() *Media {
p.l().Infof("pop first media")
if p.Size() == 0 {
p.l().Warn("pop first media failed, no media left in the playlist")
return nil
}
p.Lock.Lock()
index := 0
if p.Config.RandomNext {
index = rand.Intn(p.Size())
}
media := p.Playlist[index]
for i := index; i > 0; i-- {
p.Playlist[i] = p.Playlist[i-1]
}
p.Playlist = p.Playlist[1:]
p.Lock.Unlock()
defer p.Handler.CallA(EventPlaylistUpdate, PlaylistUpdateEvent{Playlist: p})
return media
}
func (p *Playlist) Replace(medias []*Media) {
p.Lock.Lock()
p.Playlist = medias
p.Index = 0
p.Lock.Unlock()
p.Handler.CallA(EventPlaylistUpdate, PlaylistUpdateEvent{Playlist: p})
return
}
func (p *Playlist) Push(media *Media) {
p.Insert(-1, media)
return
}
// Insert runtime in O(n) but i don't care
func (p *Playlist) Insert(index int, media *Media) {
p.l().Infof("insert new meida to index %d", index)
p.l().Debugf("media= %s", media.Title)
e := event.Event{
Id: EventPlaylistPreInsert,
Cancelled: false,
Data: PlaylistInsertEvent{
Playlist: p,
Index: index,
Media: media,
},
}
p.Handler.Call(&e)
if e.Cancelled {
p.l().Info("insert new media has been cancelled by handler")
return
}
p.Lock.Lock()
if index > p.Size() {
index = p.Size()
}
if index < 0 {
index = p.Size() + index + 1
}
p.Playlist = append(p.Playlist, nil)
for i := p.Size() - 1; i > index; i-- {
p.Playlist[i] = p.Playlist[i-1]
}
p.Playlist[index] = media
p.Lock.Unlock()
defer func() {
p.Handler.Call(&event.Event{
Id: EventPlaylistInsert,
Cancelled: false,
Data: PlaylistInsertEvent{
Playlist: p,
Index: index,
Media: media,
},
})
p.Handler.CallA(EventPlaylistUpdate, PlaylistUpdateEvent{Playlist: p})
}()
}
func (p *Playlist) Delete(index int) {
p.l().Infof("from media at index %d", index)
p.Lock.Lock()
if index >= p.Size() || index < 0 {
p.l().Warnf("media at index %d does not exist", index)
p.Lock.Unlock()
return
}
// todo: @5 delete optimization
p.Playlist = append(p.Playlist[:index], p.Playlist[index+1:]...)
p.Lock.Unlock()
defer p.Handler.CallA(EventPlaylistUpdate, PlaylistUpdateEvent{Playlist: p})
}
func (p *Playlist) Move(src int, dest int) {
p.l().Infof("from media from index %d to %d", src, dest)
p.Lock.Lock()
if src >= p.Size() || src < 0 {
p.l().Warnf("media at index %d does not exist", src)
p.Lock.Unlock()
return
}
if dest >= p.Size() {
dest = p.Size() - 1
}
if dest < 0 {
dest = 0
}
if dest == src {
p.l().Warn("src and dest are same, operation not perform")
p.Lock.Unlock()
return
}
step := 1
if dest < src {
step = -1
}
tmp := p.Playlist[src]
for i := src; i != dest; i += step {
p.Playlist[i] = p.Playlist[i+step]
}
p.Playlist[dest] = tmp
p.Lock.Unlock()
defer p.Handler.CallA(EventPlaylistUpdate, PlaylistUpdateEvent{Playlist: p})
}
func (p *Playlist) Next() *Media {
p.l().Infof("get next media with random=%t", p.Config.RandomNext)
if p.Size() == 0 {
p.l().Info("get next media failed, no media left in the playlist")
return nil
}
var index int
index = p.Index
if p.Config.RandomNext {
p.Index = rand.Intn(p.Size())
} else {
p.Index = (p.Index + 1) % p.Size()
}
p.l().Tracef("return index %d, new index %d", index, p.Index)
defer p.Handler.CallA(EventPlaylistUpdate, PlaylistUpdateEvent{Playlist: p})
return p.Playlist[index]
}