mirror of
https://github.com/AynaLivePlayer/AynaLivePlayer.git
synced 2026-03-15 22:13:17 +08:00
231 lines
5.6 KiB
Go
231 lines
5.6 KiB
Go
package playlist
|
|
|
|
import (
|
|
"AynaLivePlayer/core/events"
|
|
"AynaLivePlayer/core/model"
|
|
"AynaLivePlayer/global"
|
|
"AynaLivePlayer/pkg/event"
|
|
"math/rand"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
var rng *rand.Rand
|
|
|
|
func init() {
|
|
rng = rand.New(rand.NewSource(time.Now().Unix()))
|
|
}
|
|
|
|
type playlist struct {
|
|
Index int
|
|
playlistId model.PlaylistID
|
|
mode model.PlaylistMode
|
|
Medias []model.Media
|
|
randomIndex []int
|
|
Lock sync.RWMutex
|
|
}
|
|
|
|
// resetRandomIndex reset the random index
|
|
// this function is not locked, should be called in a locked context
|
|
func (p *playlist) resetRandomIndex() {
|
|
p.randomIndex = make([]int, p.Size())
|
|
for i := 0; i < p.Size(); i++ {
|
|
p.randomIndex[i] = i
|
|
}
|
|
rand.Shuffle(p.Size(), func(i, j int) {
|
|
p.randomIndex[i], p.randomIndex[j] = p.randomIndex[j], p.randomIndex[i]
|
|
})
|
|
}
|
|
|
|
func newPlaylist(id model.PlaylistID) *playlist {
|
|
pl := &playlist{
|
|
playlistId: id,
|
|
Medias: make([]model.Media, 0),
|
|
Lock: sync.RWMutex{},
|
|
Index: 0,
|
|
}
|
|
global.EventManager.RegisterA(events.PlaylistMoveCmd(id), "internal.playlist.move", func(event *event.Event) {
|
|
e := event.Data.(events.PlaylistMoveCmdEvent)
|
|
pl.Move(e.From, e.To)
|
|
})
|
|
global.EventManager.RegisterA(events.PlaylistInsertCmd(id), "internal.playlist.insert", func(event *event.Event) {
|
|
e := event.Data.(events.PlaylistInsertCmdEvent)
|
|
pl.Insert(e.Position, e.Media)
|
|
})
|
|
global.EventManager.RegisterA(events.PlaylistDeleteCmd(id), "internal.playlist.delete", func(event *event.Event) {
|
|
e := event.Data.(events.PlaylistDeleteCmdEvent)
|
|
pl.Delete(e.Index)
|
|
})
|
|
global.EventManager.RegisterA(events.PlaylistNextCmd(id), "internal.playlist.next", func(event *event.Event) {
|
|
log.Infof("Playlist %s recieve next", id)
|
|
pl.Next(event.Data.(events.PlaylistNextCmdEvent).Remove)
|
|
})
|
|
global.EventManager.RegisterA(events.PlaylistModeChangeCmd(id), "internal.playlist.mode", func(event *event.Event) {
|
|
pl.Lock.Lock()
|
|
pl.mode = event.Data.(events.PlaylistModeChangeCmdEvent).Mode
|
|
pl.Index = 0
|
|
pl.resetRandomIndex()
|
|
pl.Lock.Unlock()
|
|
log.Infof("Playlist %s mode changed to %d", id, pl.mode)
|
|
global.EventManager.CallA(events.PlaylistModeChangeUpdate(id), events.PlaylistModeChangeUpdateEvent{
|
|
Mode: pl.mode,
|
|
})
|
|
})
|
|
global.EventManager.RegisterA(events.PlaylistSetIndexCmd(id), "internal.playlist.setindex", func(event *event.Event) {
|
|
index := event.Data.(events.PlaylistSetIndexCmdEvent).Index
|
|
if index >= pl.Size() || index < 0 {
|
|
index = 0
|
|
}
|
|
pl.Index = index
|
|
})
|
|
pl.resetRandomIndex()
|
|
return pl
|
|
}
|
|
|
|
func (p *playlist) CopyMedia() []model.Media {
|
|
medias := make([]model.Media, len(p.Medias))
|
|
copy(medias, p.Medias)
|
|
return medias
|
|
}
|
|
|
|
func (p *playlist) Size() int {
|
|
return len(p.Medias)
|
|
}
|
|
|
|
func (p *playlist) Replace(medias []model.Media) {
|
|
p.Lock.Lock()
|
|
p.Medias = medias
|
|
p.Index = 0
|
|
p.resetRandomIndex()
|
|
p.Lock.Unlock()
|
|
global.EventManager.CallA(events.PlaylistDetailUpdate(p.playlistId), events.PlaylistDetailUpdateEvent{
|
|
Medias: p.CopyMedia(),
|
|
})
|
|
}
|
|
|
|
func (p *playlist) Insert(index int, media model.Media) {
|
|
p.Lock.Lock()
|
|
if index > p.Size() {
|
|
index = p.Size()
|
|
}
|
|
if index < 0 {
|
|
index = p.Size() + index + 1
|
|
}
|
|
p.Medias = append(p.Medias, model.Media{})
|
|
for i := p.Size() - 1; i > index; i-- {
|
|
p.Medias[i] = p.Medias[i-1]
|
|
}
|
|
p.Medias[index] = media
|
|
p.resetRandomIndex()
|
|
p.Lock.Unlock()
|
|
global.EventManager.CallA(events.PlaylistInsertUpdate(p.playlistId), events.PlaylistInsertUpdateEvent{
|
|
Position: index,
|
|
Media: media,
|
|
})
|
|
global.EventManager.CallA(events.PlaylistDetailUpdate(p.playlistId), events.PlaylistDetailUpdateEvent{
|
|
Medias: p.CopyMedia(),
|
|
})
|
|
}
|
|
|
|
func (p *playlist) Delete(index int) {
|
|
p.Lock.Lock()
|
|
if index >= p.Size() || index < 0 {
|
|
p.Lock.Unlock()
|
|
return
|
|
}
|
|
// todo: @5 delete optimization
|
|
p.Medias = append(p.Medias[:index], p.Medias[index+1:]...)
|
|
if p.Index >= p.Size() {
|
|
p.Index = 0
|
|
}
|
|
p.resetRandomIndex()
|
|
p.Lock.Unlock()
|
|
global.EventManager.CallA(events.PlaylistDetailUpdate(p.playlistId), events.PlaylistDetailUpdateEvent{
|
|
Medias: p.CopyMedia(),
|
|
})
|
|
}
|
|
|
|
func (p *playlist) Move(src int, dst int) {
|
|
if src >= p.Size() || src < 0 {
|
|
return
|
|
}
|
|
p.Lock.Lock()
|
|
if dst >= p.Size() {
|
|
dst = p.Size() - 1
|
|
}
|
|
if dst < 0 {
|
|
dst = 0
|
|
}
|
|
if dst == src {
|
|
p.Lock.Unlock()
|
|
return
|
|
}
|
|
step := 1
|
|
if dst < src {
|
|
step = -1
|
|
}
|
|
tmp := p.Medias[src]
|
|
for i := src; i != dst; i += step {
|
|
p.Medias[i] = p.Medias[i+step]
|
|
}
|
|
p.Medias[dst] = tmp
|
|
p.Lock.Unlock()
|
|
global.EventManager.CallA(events.PlaylistDetailUpdate(p.playlistId), events.PlaylistDetailUpdateEvent{
|
|
Medias: p.CopyMedia(),
|
|
})
|
|
}
|
|
|
|
func (p *playlist) Next(delete bool) {
|
|
p.Lock.Lock()
|
|
if p.Size() == 0 {
|
|
// no media in the playlist
|
|
// do not dispatch any event
|
|
p.Lock.Unlock()
|
|
return
|
|
}
|
|
var index int
|
|
index = p.Index
|
|
// add guard, i don't know why this is needed
|
|
// but it seems to fix a bug
|
|
if index >= p.Size() {
|
|
index = 0
|
|
}
|
|
if (index == 0) && p.mode == model.PlaylistModeRandom {
|
|
p.resetRandomIndex()
|
|
}
|
|
var m model.Media
|
|
if p.mode == model.PlaylistModeRandom {
|
|
m = p.Medias[p.randomIndex[index]]
|
|
} else {
|
|
m = p.Medias[index]
|
|
}
|
|
//// fix race condition
|
|
//currentSize := p.Size() - 1
|
|
//if delete {
|
|
// if p.mode == model.PlaylistModeRandom {
|
|
// if currentSize == 0 {
|
|
// p.Index = 0
|
|
// } else {
|
|
// p.Index = rand.Intn(currentSize)
|
|
// }
|
|
// } else if p.mode == model.PlaylistModeNormal {
|
|
// p.Index = index
|
|
// } else {
|
|
// p.Index = index
|
|
// }
|
|
//}
|
|
p.Lock.Unlock()
|
|
global.EventManager.CallA(events.PlaylistNextUpdate(p.playlistId), events.PlaylistNextUpdateEvent{
|
|
Media: m,
|
|
})
|
|
if delete {
|
|
if p.mode == model.PlaylistModeRandom {
|
|
p.Delete(p.randomIndex[index])
|
|
} else {
|
|
p.Delete(index)
|
|
}
|
|
} else {
|
|
p.Index = (p.Index + 1) % p.Size()
|
|
}
|
|
}
|