mirror of
https://github.com/AynaLivePlayer/AynaLivePlayer.git
synced 2025-12-12 13:18:13 +08:00
408 lines
9.6 KiB
Go
408 lines
9.6 KiB
Go
package core
|
|
|
|
import (
|
|
"AynaLivePlayer/common/event"
|
|
"AynaLivePlayer/config"
|
|
"AynaLivePlayer/controller"
|
|
"AynaLivePlayer/model"
|
|
"AynaLivePlayer/repo/provider"
|
|
"errors"
|
|
"fmt"
|
|
"math/rand"
|
|
"sync"
|
|
)
|
|
|
|
type PlaylistController struct {
|
|
PlaylistPath string
|
|
provider controller.IProviderController
|
|
History controller.IPlaylist `ini:"-"`
|
|
Current controller.IPlaylist `ini:"-"`
|
|
Default controller.IPlaylist `ini:"-"`
|
|
Playlists []controller.IPlaylist `ini:"-"`
|
|
DefaultIndex int
|
|
CurrentPlaylistRandom bool
|
|
DefaultPlaylistRandom bool
|
|
}
|
|
|
|
func NewPlaylistController(
|
|
provider controller.IProviderController) controller.IPlaylistController {
|
|
pc := &PlaylistController{
|
|
PlaylistPath: "playlists.json",
|
|
provider: provider,
|
|
History: NewPlaylist("history"),
|
|
Default: NewPlaylist("default"),
|
|
Current: NewPlaylist("current"),
|
|
Playlists: make([]controller.IPlaylist, 0),
|
|
DefaultIndex: 0,
|
|
CurrentPlaylistRandom: false,
|
|
DefaultPlaylistRandom: true,
|
|
}
|
|
config.LoadConfig(pc)
|
|
if pc.DefaultIndex < 0 || pc.DefaultIndex >= len(pc.Playlists) {
|
|
pc.DefaultIndex = 0
|
|
lg.Warn("playlist index did not find")
|
|
}
|
|
go func() {
|
|
_ = pc.SetDefault(pc.DefaultIndex)
|
|
}()
|
|
return pc
|
|
}
|
|
|
|
func (pc *PlaylistController) Name() string {
|
|
return "Playlists"
|
|
}
|
|
|
|
func (pc *PlaylistController) OnLoad() {
|
|
var metas = []model.Meta{
|
|
{
|
|
"netease",
|
|
"2382819181",
|
|
},
|
|
{"netease",
|
|
"4987059624",
|
|
},
|
|
{"local",
|
|
"list1",
|
|
},
|
|
}
|
|
_ = config.LoadJson(pc.PlaylistPath, &metas)
|
|
for _, m := range metas {
|
|
p := NewPlaylist(fmt.Sprintf("%s-%s", m.Name, m.Id))
|
|
p.Model().Meta = m
|
|
pc.Playlists = append(pc.Playlists, p)
|
|
}
|
|
if pc.CurrentPlaylistRandom {
|
|
pc.Current.Model().Mode = model.PlaylistModeRandom
|
|
}
|
|
if pc.DefaultPlaylistRandom {
|
|
pc.Default.Model().Mode = model.PlaylistModeRandom
|
|
}
|
|
}
|
|
|
|
func (pc *PlaylistController) OnSave() {
|
|
var metas = make([]model.Meta, 0)
|
|
for _, pl := range pc.Playlists {
|
|
metas = append(metas, pl.Model().Meta)
|
|
}
|
|
_ = config.SaveJson(pc.PlaylistPath, &metas)
|
|
|
|
if pc.Current.Model().Mode == model.PlaylistModeRandom {
|
|
pc.CurrentPlaylistRandom = true
|
|
} else {
|
|
pc.CurrentPlaylistRandom = false
|
|
}
|
|
if pc.Default.Model().Mode == model.PlaylistModeRandom {
|
|
pc.DefaultPlaylistRandom = true
|
|
} else {
|
|
pc.DefaultPlaylistRandom = false
|
|
}
|
|
}
|
|
|
|
func (pc *PlaylistController) Size() int {
|
|
return len(pc.Playlists)
|
|
}
|
|
|
|
func (pc *PlaylistController) GetHistory() controller.IPlaylist {
|
|
return pc.History
|
|
}
|
|
|
|
func (pc *PlaylistController) GetDefault() controller.IPlaylist {
|
|
return pc.Default
|
|
}
|
|
|
|
func (pc *PlaylistController) GetCurrent() controller.IPlaylist {
|
|
return pc.Current
|
|
}
|
|
|
|
func (pc *PlaylistController) AddToHistory(media *model.Media) {
|
|
lg.Tracef("add media %s (%s) to history", media.Title, media.Artist)
|
|
media = media.Copy()
|
|
// reset url for future use
|
|
media.Url = ""
|
|
if pc.History.Size() >= 1024 {
|
|
pc.History.Replace([]*model.Media{})
|
|
}
|
|
media.User = controller.HistoryUser
|
|
pc.History.Push(media)
|
|
return
|
|
}
|
|
|
|
func (pc *PlaylistController) Get(index int) controller.IPlaylist {
|
|
if index < 0 || index >= len(pc.Playlists) {
|
|
lg.Warnf("playlist.index=%d not found", index)
|
|
return nil
|
|
}
|
|
return pc.Playlists[index]
|
|
}
|
|
|
|
func (pc *PlaylistController) Add(pname string, uri string) controller.IPlaylist {
|
|
lg.Infof("try add playlist %s with provider %s", uri, pname)
|
|
id, err := provider.FormatPlaylistUrl(pname, uri)
|
|
if err != nil || id == "" {
|
|
lg.Warnf("fail to format %s playlist id for %s", uri, pname)
|
|
return nil
|
|
}
|
|
p := NewPlaylist(fmt.Sprintf("%s-%s", pname, id))
|
|
p.Model().Meta = model.Meta{
|
|
Name: pname,
|
|
Id: id,
|
|
}
|
|
pc.Playlists = append(pc.Playlists, p)
|
|
return p
|
|
}
|
|
|
|
func (pc *PlaylistController) Remove(index int) controller.IPlaylist {
|
|
lg.Infof("Try to remove playlist.index=%d", index)
|
|
if index < 0 || index >= len(pc.Playlists) {
|
|
lg.Warnf("playlist.index=%d not found", index)
|
|
return nil
|
|
}
|
|
if index == pc.DefaultIndex {
|
|
lg.Info("Delete current system playlist, reset system playlist to index = 0")
|
|
_ = pc.SetDefault(0)
|
|
}
|
|
if index < pc.DefaultIndex {
|
|
lg.Debugf("Delete playlist before system playlist (index=%d), reduce system playlist index by 1", pc.DefaultIndex)
|
|
pc.DefaultIndex = pc.DefaultIndex - 1
|
|
}
|
|
pl := pc.Playlists[index]
|
|
pc.Playlists = append(pc.Playlists[:index], pc.Playlists[index+1:]...)
|
|
return pl
|
|
}
|
|
|
|
func (pc *PlaylistController) SetDefault(index int) error {
|
|
lg.Infof("try set system playlist to playlist.id=%d", index)
|
|
if index < 0 || index >= len(pc.Playlists) {
|
|
lg.Warn("playlist.index=%d not found", index)
|
|
return errors.New("playlist.index not found")
|
|
}
|
|
err := pc.provider.PreparePlaylist(pc.Playlists[index])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
pl := pc.Playlists[index].Model().Copy()
|
|
pc.DefaultIndex = index
|
|
controller.ApplyUser(pl.Medias, controller.PlaylistUser)
|
|
pc.Default.Replace(pl.Medias)
|
|
pc.Default.Model().Name = pl.Name
|
|
return nil
|
|
}
|
|
|
|
func (pc *PlaylistController) PreparePlaylistByIndex(index int) error {
|
|
lg.Infof("try prepare playlist.id=%d", index)
|
|
if index < 0 || index >= len(pc.Playlists) {
|
|
lg.Warn("playlist.id=%d not found", index)
|
|
return nil
|
|
}
|
|
return pc.provider.PreparePlaylist(pc.Playlists[index])
|
|
}
|
|
|
|
type corePlaylist struct {
|
|
model.Playlist
|
|
Index int
|
|
Lock sync.RWMutex
|
|
eventManager *event.Manager
|
|
}
|
|
|
|
func NewPlaylist(name string) controller.IPlaylist {
|
|
return &corePlaylist{
|
|
Index: 0,
|
|
Playlist: model.Playlist{
|
|
Name: name,
|
|
Medias: make([]*model.Media, 0),
|
|
Mode: model.PlaylistModeNormal,
|
|
Meta: model.Meta{},
|
|
},
|
|
eventManager: event.MainManager.NewChildManager(),
|
|
}
|
|
}
|
|
|
|
func (p *corePlaylist) Model() *model.Playlist {
|
|
return &p.Playlist
|
|
}
|
|
|
|
func (p *corePlaylist) EventManager() *event.Manager {
|
|
return p.eventManager
|
|
}
|
|
|
|
func (p *corePlaylist) Name() string {
|
|
return p.Playlist.Name
|
|
}
|
|
|
|
func (p *corePlaylist) Size() int {
|
|
return p.Playlist.Size()
|
|
}
|
|
|
|
func (p *corePlaylist) Get(index int) *model.Media {
|
|
if index < 0 || index >= p.Playlist.Size() {
|
|
return nil
|
|
}
|
|
return p.Playlist.Medias[index]
|
|
}
|
|
|
|
func (p *corePlaylist) Pop() *model.Media {
|
|
lg.Debugf("[Playlists] %s pop first media", p.Playlist)
|
|
if p.Size() == 0 {
|
|
return nil
|
|
}
|
|
p.Lock.Lock()
|
|
index := 0
|
|
if p.Mode == model.PlaylistModeRandom {
|
|
index = rand.Intn(p.Size())
|
|
}
|
|
m := p.Medias[index]
|
|
for i := index; i > 0; i-- {
|
|
p.Medias[i] = p.Medias[i-1]
|
|
}
|
|
p.Medias = p.Medias[1:]
|
|
p.Lock.Unlock()
|
|
if m == nil {
|
|
lg.Warn("[Playlists] pop first media failed, no media left in the playlist")
|
|
return nil
|
|
}
|
|
p.eventManager.CallA(
|
|
model.EventPlaylistUpdate,
|
|
model.PlaylistUpdateEvent{
|
|
Playlist: p.Playlist.Copy(),
|
|
},
|
|
)
|
|
return m
|
|
}
|
|
|
|
func (p *corePlaylist) Replace(medias []*model.Media) {
|
|
lg.Infof("[Playlists] %s replace all media", &p.Playlist)
|
|
p.Lock.Lock()
|
|
p.Playlist.Medias = medias
|
|
p.Index = 0
|
|
p.Lock.Unlock()
|
|
p.eventManager.CallA(
|
|
model.EventPlaylistUpdate,
|
|
model.PlaylistUpdateEvent{
|
|
Playlist: p.Playlist.Copy(),
|
|
},
|
|
)
|
|
}
|
|
|
|
func (p *corePlaylist) Push(media *model.Media) {
|
|
p.Insert(-1, media)
|
|
}
|
|
|
|
func (p *corePlaylist) Insert(index int, media *model.Media) {
|
|
lg.Infof("[Playlists]insert media into new index %d at %s ", index, p.Playlist)
|
|
lg.Debugf("media=%s %v", media.Title, media.Meta)
|
|
e := event.Event{
|
|
Id: model.EventPlaylistPreInsert,
|
|
Cancelled: false,
|
|
Data: model.PlaylistInsertEvent{
|
|
Playlist: p.Playlist.Copy(),
|
|
Index: index,
|
|
Media: media,
|
|
},
|
|
}
|
|
p.eventManager.Call(&e)
|
|
if e.Cancelled {
|
|
lg.Info("[Playlists] media insertion 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.Medias = append(p.Medias, nil)
|
|
for i := p.Size() - 1; i > index; i-- {
|
|
p.Medias[i] = p.Medias[i-1]
|
|
}
|
|
p.Medias[index] = media
|
|
p.Lock.Unlock()
|
|
p.eventManager.CallA(
|
|
model.EventPlaylistUpdate,
|
|
model.PlaylistUpdateEvent{
|
|
Playlist: p.Playlist.Copy(),
|
|
},
|
|
)
|
|
p.eventManager.CallA(
|
|
model.EventPlaylistInsert,
|
|
model.PlaylistInsertEvent{
|
|
Playlist: p.Playlist.Copy(),
|
|
Index: index,
|
|
Media: media,
|
|
},
|
|
)
|
|
}
|
|
|
|
func (p *corePlaylist) Delete(index int) *model.Media {
|
|
lg.Infof("from media at index %d from %s", index, p.Playlist)
|
|
if index >= p.Size() || index < 0 {
|
|
p.Lock.Unlock()
|
|
return nil
|
|
}
|
|
m := p.Medias[index]
|
|
p.Lock.Lock()
|
|
// todo: @5 delete optimization
|
|
p.Medias = append(p.Medias[:index], p.Medias[index+1:]...)
|
|
p.Lock.Unlock()
|
|
if m == nil {
|
|
lg.Warnf("media at index %d does not exist", index)
|
|
}
|
|
p.eventManager.CallA(
|
|
model.EventPlaylistUpdate,
|
|
model.PlaylistUpdateEvent{
|
|
Playlist: p.Playlist.Copy(),
|
|
})
|
|
return m
|
|
}
|
|
|
|
func (p *corePlaylist) Move(src int, dst int) {
|
|
lg.Infof("from media from index %d to %d", src, dst)
|
|
if src >= p.Size() || src < 0 {
|
|
lg.Warnf("media at index %d does not exist", src)
|
|
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()
|
|
p.eventManager.CallA(
|
|
model.EventPlaylistUpdate,
|
|
model.PlaylistUpdateEvent{
|
|
Playlist: p.Playlist.Copy(),
|
|
})
|
|
}
|
|
|
|
func (p *corePlaylist) Next() *model.Media {
|
|
lg.Infof("[Playlists] %s get next media with random=%t", p, p.Mode == model.PlaylistModeRandom)
|
|
if p.Size() == 0 {
|
|
lg.Warn("[Playlists] get next media failed, no media left in the playlist")
|
|
return nil
|
|
}
|
|
var index int
|
|
index = p.Index
|
|
if p.Mode == model.PlaylistModeRandom {
|
|
p.Index = rand.Intn(p.Size())
|
|
} else {
|
|
p.Index = (p.Index + 1) % p.Size()
|
|
}
|
|
m := p.Medias[index]
|
|
return m
|
|
}
|