mirror of
https://github.com/AynaLivePlayer/AynaLivePlayer.git
synced 2025-12-14 05:58:17 +08:00
rewrite using IoC and DI
This commit is contained in:
@@ -1,31 +0,0 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"AynaLivePlayer/event"
|
||||
"AynaLivePlayer/liveclient"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var Commands []DanmuCommandExecutor
|
||||
|
||||
type DanmuCommandExecutor interface {
|
||||
Match(command string) bool
|
||||
Execute(command string, args []string, danmu *liveclient.DanmuMessage)
|
||||
}
|
||||
|
||||
func AddCommand(executors ...DanmuCommandExecutor) {
|
||||
Commands = append(Commands, executors...)
|
||||
}
|
||||
|
||||
func danmuCommandHandler(event *event.Event) {
|
||||
danmu := event.Data.(*liveclient.DanmuMessage)
|
||||
args := strings.Split(danmu.Message, " ")
|
||||
if len(args[0]) == 0 {
|
||||
return
|
||||
}
|
||||
for _, cmd := range Commands {
|
||||
if cmd.Match(args[0]) {
|
||||
cmd.Execute(args[0], args[1:], danmu)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,13 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"AynaLivePlayer/logger"
|
||||
)
|
||||
var Instance IController = nil
|
||||
|
||||
const MODULE_CONTROLLER = "Controller"
|
||||
|
||||
var l = logger.Logger.WithField("Module", MODULE_CONTROLLER)
|
||||
type IController interface {
|
||||
LiveRooms() ILiveRoomController
|
||||
PlayControl() IPlayController
|
||||
Playlists() IPlaylistController
|
||||
Provider() IProviderController
|
||||
Plugin() IPluginController
|
||||
LoadPlugins(plugins ...Plugin)
|
||||
CloseAndSave()
|
||||
}
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestController(t *testing.T) {
|
||||
fmt.Println(LiveClient == nil)
|
||||
}
|
||||
59
controller/core/controller.go
Normal file
59
controller/core/controller.go
Normal file
@@ -0,0 +1,59 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"AynaLivePlayer/common/logger"
|
||||
"AynaLivePlayer/controller"
|
||||
)
|
||||
|
||||
var lg = logger.Logger.WithField("Module", "CoreController")
|
||||
|
||||
type Controller struct {
|
||||
liveroom controller.ILiveRoomController `ini:"-"`
|
||||
player controller.IPlayController `ini:"-"`
|
||||
lyric controller.ILyricLoader `ini:"-"`
|
||||
playlist controller.IPlaylistController `ini:"-"`
|
||||
provider controller.IProviderController `ini:"-"`
|
||||
plugin controller.IPluginController `ini:"-"`
|
||||
}
|
||||
|
||||
func NewController(
|
||||
liveroom controller.ILiveRoomController, player controller.IPlayController,
|
||||
playlist controller.IPlaylistController,
|
||||
provider controller.IProviderController, plugin controller.IPluginController) controller.IController {
|
||||
cc := &Controller{
|
||||
liveroom: liveroom,
|
||||
player: player,
|
||||
playlist: playlist,
|
||||
provider: provider,
|
||||
plugin: plugin,
|
||||
}
|
||||
return cc
|
||||
}
|
||||
|
||||
func (c *Controller) LiveRooms() controller.ILiveRoomController {
|
||||
return c.liveroom
|
||||
}
|
||||
|
||||
func (c *Controller) PlayControl() controller.IPlayController {
|
||||
return c.player
|
||||
}
|
||||
|
||||
func (c *Controller) Playlists() controller.IPlaylistController {
|
||||
return c.playlist
|
||||
}
|
||||
|
||||
func (c *Controller) Provider() controller.IProviderController {
|
||||
return c.provider
|
||||
}
|
||||
|
||||
func (c *Controller) Plugin() controller.IPluginController {
|
||||
return c.plugin
|
||||
}
|
||||
|
||||
func (c *Controller) LoadPlugins(plugins ...controller.Plugin) {
|
||||
c.plugin.LoadPlugins(plugins...)
|
||||
}
|
||||
|
||||
func (c *Controller) CloseAndSave() {
|
||||
c.plugin.ClosePlugins()
|
||||
}
|
||||
201
controller/core/liveroom.go
Normal file
201
controller/core/liveroom.go
Normal file
@@ -0,0 +1,201 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"AynaLivePlayer/common/event"
|
||||
"AynaLivePlayer/config"
|
||||
"AynaLivePlayer/controller"
|
||||
"AynaLivePlayer/liveclient"
|
||||
"AynaLivePlayer/model"
|
||||
"errors"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type coreLiveRoom struct {
|
||||
model.LiveRoom
|
||||
client liveclient.LiveClient
|
||||
}
|
||||
|
||||
func (r *coreLiveRoom) Model() *model.LiveRoom {
|
||||
return &r.LiveRoom
|
||||
}
|
||||
|
||||
func (r *coreLiveRoom) Status() bool {
|
||||
return r.client.Status()
|
||||
}
|
||||
|
||||
func (r *coreLiveRoom) EventManager() *event.Manager {
|
||||
return r.client.EventManager()
|
||||
}
|
||||
|
||||
func (r *coreLiveRoom) init(msgHandler event.HandlerFunc) (err error) {
|
||||
if r.client != nil {
|
||||
return nil
|
||||
}
|
||||
r.client, err = liveclient.NewLiveClient(r.ClientName, r.ID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
r.client.EventManager().RegisterA(
|
||||
liveclient.EventMessageReceive,
|
||||
"controller.danmu.command",
|
||||
msgHandler)
|
||||
return nil
|
||||
}
|
||||
|
||||
type LiveRoomController struct {
|
||||
LiveRoomPath string
|
||||
liveRooms []*coreLiveRoom
|
||||
danmuCommands []controller.DanmuCommandExecutor
|
||||
}
|
||||
|
||||
func NewLiveRoomController() controller.ILiveRoomController {
|
||||
lr := &LiveRoomController{
|
||||
LiveRoomPath: "liverooms.json",
|
||||
liveRooms: []*coreLiveRoom{
|
||||
{LiveRoom: model.LiveRoom{
|
||||
ClientName: "bilibili",
|
||||
ID: "9076804",
|
||||
AutoConnect: false,
|
||||
}},
|
||||
{LiveRoom: model.LiveRoom{
|
||||
ClientName: "bilibili",
|
||||
ID: "3819533",
|
||||
AutoConnect: false,
|
||||
}},
|
||||
},
|
||||
danmuCommands: make([]controller.DanmuCommandExecutor, 0),
|
||||
}
|
||||
config.LoadConfig(lr)
|
||||
lr.initialize()
|
||||
return lr
|
||||
}
|
||||
|
||||
func (lr *LiveRoomController) danmuCommandHandler(event *event.Event) {
|
||||
danmu := event.Data.(*liveclient.DanmuMessage)
|
||||
args := strings.Split(danmu.Message, " ")
|
||||
if len(args[0]) == 0 {
|
||||
return
|
||||
}
|
||||
for _, cmd := range lr.danmuCommands {
|
||||
if cmd.Match(args[0]) {
|
||||
cmd.Execute(args[0], args[1:], danmu)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (lr *LiveRoomController) initialize() {
|
||||
for i := 0; i < len(lr.liveRooms); i++ {
|
||||
if lr.liveRooms[i].client == nil {
|
||||
_ = lr.liveRooms[i].init(lr.danmuCommandHandler)
|
||||
}
|
||||
}
|
||||
go func() {
|
||||
for i := 0; i < len(lr.liveRooms); i++ {
|
||||
if lr.liveRooms[i].AutoConnect {
|
||||
lr.liveRooms[i].client.Connect()
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (lr *LiveRoomController) Name() string {
|
||||
return "LiveRooms"
|
||||
}
|
||||
|
||||
func (lr *LiveRoomController) Size() int {
|
||||
return len(lr.liveRooms)
|
||||
}
|
||||
|
||||
func (lr *LiveRoomController) OnLoad() {
|
||||
rooms := make([]model.LiveRoom, 0)
|
||||
_ = config.LoadJson(lr.LiveRoomPath, &lr.liveRooms)
|
||||
if len(rooms) == 0 {
|
||||
return
|
||||
}
|
||||
lr.liveRooms = make([]*coreLiveRoom, len(rooms))
|
||||
for i := 0; i < len(rooms); i++ {
|
||||
lr.liveRooms[i] = &coreLiveRoom{LiveRoom: rooms[i]}
|
||||
}
|
||||
}
|
||||
|
||||
func (lr *LiveRoomController) OnSave() {
|
||||
rooms := make([]model.LiveRoom, len(lr.liveRooms))
|
||||
for i := 0; i < len(lr.liveRooms); i++ {
|
||||
rooms[i] = lr.liveRooms[i].LiveRoom
|
||||
}
|
||||
_ = config.SaveJson(lr.LiveRoomPath, &rooms)
|
||||
}
|
||||
|
||||
func (lr *LiveRoomController) Get(index int) controller.ILiveRoom {
|
||||
if index < 0 || index >= len(lr.liveRooms) {
|
||||
return nil
|
||||
}
|
||||
return lr.liveRooms[index]
|
||||
}
|
||||
|
||||
func (lr *LiveRoomController) GetRoomStatus(index int) bool {
|
||||
if index < 0 || index >= len(lr.liveRooms) {
|
||||
return false
|
||||
}
|
||||
return lr.liveRooms[index].client.Status()
|
||||
}
|
||||
|
||||
func (lr *LiveRoomController) Connect(index int) error {
|
||||
lg.Infof("[LiveRooms] Try to start LiveRooms.index=%d", index)
|
||||
if index < 0 || index >= len(lr.liveRooms) {
|
||||
lg.Errorf("[LiveRooms] LiveRooms.index=%d not found", index)
|
||||
return errors.New("index out of range")
|
||||
}
|
||||
lr.liveRooms[index].client.Connect()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (lr *LiveRoomController) Disconnect(index int) error {
|
||||
lg.Infof("[LiveRooms] Try to Disconnect LiveRooms.index=%d", index)
|
||||
if index < 0 || index >= len(lr.liveRooms) {
|
||||
lg.Errorf("[LiveRooms] LiveRooms.index=%d not found", index)
|
||||
return errors.New("index out of range")
|
||||
}
|
||||
lr.liveRooms[index].client.Disconnect()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (lr *LiveRoomController) AddRoom(clientName, roomId string) (*model.LiveRoom, error) {
|
||||
rm := &coreLiveRoom{
|
||||
LiveRoom: model.LiveRoom{
|
||||
ClientName: clientName,
|
||||
ID: roomId,
|
||||
AutoConnect: false,
|
||||
},
|
||||
}
|
||||
lg.Infof("[LiveRooms] add live room %s", &rm.LiveRoom)
|
||||
err := rm.init(lr.danmuCommandHandler)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
lg.Infof("[LiveRooms] %s init failed: %s", &rm.LiveRoom, err)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
lr.liveRooms = append(lr.liveRooms, rm)
|
||||
return &rm.LiveRoom, nil
|
||||
}
|
||||
|
||||
func (lr *LiveRoomController) DeleteRoom(index int) error {
|
||||
lg.Infof("Try to remove LiveRooms.index=%d", index)
|
||||
if index < 0 || index >= len(lr.liveRooms) {
|
||||
lg.Warnf("LiveRooms.index=%d not found", index)
|
||||
return errors.New("index out of range")
|
||||
}
|
||||
if len(lr.liveRooms) == 1 {
|
||||
return errors.New("can't delete last room")
|
||||
}
|
||||
_ = lr.liveRooms[index].client.Disconnect()
|
||||
lr.liveRooms[index].EventManager().UnregisterAll()
|
||||
lr.liveRooms = append(lr.liveRooms[:index], lr.liveRooms[index+1:]...)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (lr *LiveRoomController) AddDanmuCommand(executor controller.DanmuCommandExecutor) {
|
||||
lr.danmuCommands = append(lr.danmuCommands, executor)
|
||||
}
|
||||
56
controller/core/lyric.go
Normal file
56
controller/core/lyric.go
Normal file
@@ -0,0 +1,56 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"AynaLivePlayer/common/event"
|
||||
"AynaLivePlayer/model"
|
||||
)
|
||||
|
||||
type LyricLoader struct {
|
||||
Lyric *model.Lyric
|
||||
Handler *event.Manager
|
||||
prev float64
|
||||
}
|
||||
|
||||
func NewLyricLoader() *LyricLoader {
|
||||
return &LyricLoader{
|
||||
Lyric: model.LoadLyric(""),
|
||||
Handler: event.MainManager.NewChildManager(),
|
||||
prev: -1,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *LyricLoader) EventManager() *event.Manager {
|
||||
return l.Handler
|
||||
}
|
||||
|
||||
func (l *LyricLoader) Get() *model.Lyric {
|
||||
return l.Lyric
|
||||
}
|
||||
|
||||
func (l *LyricLoader) Reload(lyric string) {
|
||||
l.Lyric = model.LoadLyric(lyric)
|
||||
l.Handler.CallA(
|
||||
model.EventLyricReload,
|
||||
model.LyricReloadEvent{
|
||||
Lyrics: l.Lyric,
|
||||
})
|
||||
}
|
||||
|
||||
func (l *LyricLoader) Update(time float64) {
|
||||
lrc := l.Lyric.Find(time)
|
||||
if lrc == nil {
|
||||
return
|
||||
}
|
||||
if l.prev == lrc.Time {
|
||||
return
|
||||
}
|
||||
l.prev = lrc.Time
|
||||
l.Handler.CallA(
|
||||
model.EventLyricUpdate,
|
||||
model.LyricUpdateEvent{
|
||||
Lyrics: l.Lyric,
|
||||
Time: time,
|
||||
Lyric: lrc,
|
||||
})
|
||||
return
|
||||
}
|
||||
242
controller/core/playcontrol.go
Normal file
242
controller/core/playcontrol.go
Normal file
@@ -0,0 +1,242 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"AynaLivePlayer/common/event"
|
||||
"AynaLivePlayer/config"
|
||||
"AynaLivePlayer/controller"
|
||||
"AynaLivePlayer/model"
|
||||
"AynaLivePlayer/player"
|
||||
"AynaLivePlayer/provider"
|
||||
)
|
||||
|
||||
type PlayController struct {
|
||||
eventManager *event.Manager `ini:"-"`
|
||||
player player.IPlayer `ini:"-"`
|
||||
playlist controller.IPlaylistController `ini:"-"`
|
||||
provider controller.IProviderController `ini:"-"`
|
||||
lyric controller.ILyricLoader `ini:"-"`
|
||||
playing *model.Media `ini:"-"`
|
||||
AudioDevice string
|
||||
Volume float64
|
||||
SkipPlaylist bool
|
||||
}
|
||||
|
||||
func (pc *PlayController) GetSkipPlaylist() bool {
|
||||
return pc.SkipPlaylist
|
||||
}
|
||||
|
||||
func (pc *PlayController) SetSkipPlaylist(b bool) {
|
||||
pc.SkipPlaylist = b
|
||||
}
|
||||
|
||||
func (pc *PlayController) Name() string {
|
||||
return "PlayController"
|
||||
}
|
||||
|
||||
func (pc *PlayController) OnLoad() {
|
||||
return
|
||||
}
|
||||
|
||||
func (pc *PlayController) OnSave() {
|
||||
return
|
||||
}
|
||||
|
||||
func NewPlayerController(
|
||||
player player.IPlayer,
|
||||
playlist controller.IPlaylistController,
|
||||
lyric controller.ILyricLoader,
|
||||
provider controller.IProviderController) controller.IPlayController {
|
||||
pc := &PlayController{
|
||||
eventManager: event.MainManager.NewChildManager(),
|
||||
player: player,
|
||||
playlist: playlist,
|
||||
lyric: lyric,
|
||||
provider: provider,
|
||||
playing: &model.Media{},
|
||||
AudioDevice: "auto",
|
||||
Volume: 100,
|
||||
SkipPlaylist: false,
|
||||
}
|
||||
config.LoadConfig(pc)
|
||||
pc.SetVolume(pc.Volume)
|
||||
pc.SetAudioDevice(pc.AudioDevice)
|
||||
pc.player.ObserveProperty(model.PlayerPropIdleActive, "controller.playcontrol.idleplaynext", pc.handleMpvIdlePlayNext)
|
||||
pc.playlist.GetCurrent().EventManager().RegisterA(model.EventPlaylistInsert, "controller.playcontrol.playlistadd", pc.handlePlaylistAdd)
|
||||
pc.player.ObserveProperty(model.PlayerPropTimePos, "controller.playcontrol.updatelyric", pc.handleLyricUpdate)
|
||||
return pc
|
||||
}
|
||||
|
||||
func (pc *PlayController) handleMpvIdlePlayNext(event *event.Event) {
|
||||
isIdle := event.Data.(model.PlayerPropertyUpdateEvent).Value.(bool)
|
||||
if isIdle {
|
||||
lg.Info("[Controller] mpv went idle, try play next")
|
||||
pc.PlayNext()
|
||||
}
|
||||
}
|
||||
|
||||
func (pc *PlayController) handlePlaylistAdd(event *event.Event) {
|
||||
if pc.player.IsIdle() {
|
||||
pc.PlayNext()
|
||||
return
|
||||
}
|
||||
lg.Debugf("[PlayController] playlist add event, SkipPlaylist=%t", pc.SkipPlaylist)
|
||||
if pc.SkipPlaylist && pc.playing != nil && pc.playing.User == controller.PlaylistUser {
|
||||
pc.PlayNext()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (pc *PlayController) handleLyricUpdate(event *event.Event) {
|
||||
data := event.Data.(model.PlayerPropertyUpdateEvent).Value
|
||||
if data == nil {
|
||||
return
|
||||
}
|
||||
pc.lyric.Update(data.(float64))
|
||||
}
|
||||
|
||||
func (pc *PlayController) EventManager() *event.Manager {
|
||||
return pc.eventManager
|
||||
}
|
||||
|
||||
func (pc *PlayController) GetPlaying() *model.Media {
|
||||
return pc.playing
|
||||
}
|
||||
|
||||
func (pc *PlayController) GetPlayer() player.IPlayer {
|
||||
return pc.player
|
||||
}
|
||||
|
||||
func (pc *PlayController) GetLyric() controller.ILyricLoader {
|
||||
return pc.lyric
|
||||
}
|
||||
|
||||
func (pc *PlayController) PlayNext() {
|
||||
lg.Infof("[PlayController] try to play next possible media")
|
||||
if pc.playlist.GetCurrent().Size() == 0 && pc.playlist.GetDefault().Size() == 0 {
|
||||
return
|
||||
}
|
||||
var media *model.Media
|
||||
if pc.playlist.GetCurrent().Size() != 0 {
|
||||
media = pc.playlist.GetCurrent().Pop().Copy()
|
||||
} else if pc.playlist.GetDefault().Size() != 0 {
|
||||
media = pc.playlist.GetDefault().Next().Copy()
|
||||
media.User = controller.PlaylistUser
|
||||
}
|
||||
pc.Play(media)
|
||||
}
|
||||
|
||||
func (pc *PlayController) Play(media *model.Media) {
|
||||
lg.Infof("[PlayController] prepare media %s", media.Title)
|
||||
err := pc.provider.PrepareMedia(media)
|
||||
if err != nil {
|
||||
lg.Warn("[PlayController] prepare media failed. try play next")
|
||||
pc.PlayNext()
|
||||
return
|
||||
}
|
||||
pc.playing = media
|
||||
pc.playlist.AddToHistory(media)
|
||||
if err := pc.player.Play(media); err != nil {
|
||||
lg.Warn("[PlayController] play failed", err)
|
||||
return
|
||||
}
|
||||
pc.eventManager.CallA(model.EventPlay, model.PlayEvent{
|
||||
Media: media,
|
||||
})
|
||||
pc.lyric.Reload(media.Lyric)
|
||||
// reset
|
||||
media.Url = ""
|
||||
}
|
||||
|
||||
func (pc *PlayController) Add(keyword string, user interface{}) {
|
||||
media := pc.provider.MediaMatch(keyword)
|
||||
if media == nil {
|
||||
medias, err := pc.provider.Search(keyword)
|
||||
if err != nil {
|
||||
lg.Warnf("[PlayController] search for %s, got error %s", keyword, err)
|
||||
return
|
||||
}
|
||||
if len(medias) == 0 {
|
||||
lg.Info("[PlayController] search for %s, got no result", keyword)
|
||||
return
|
||||
}
|
||||
media = medias[0]
|
||||
}
|
||||
media.User = user
|
||||
lg.Infof("[PlayController] add media %s (%s)", media.Title, media.Artist)
|
||||
pc.playlist.GetCurrent().Insert(-1, media)
|
||||
}
|
||||
|
||||
func (pc *PlayController) AddWithProvider(keyword string, pname string, user interface{}) {
|
||||
media := provider.MatchMedia(pname, keyword)
|
||||
if media == nil {
|
||||
medias, err := provider.Search(pname, keyword)
|
||||
if err != nil {
|
||||
lg.Warnf("[PlayController] search for %s, got error %s", keyword, err)
|
||||
return
|
||||
}
|
||||
if len(medias) == 0 {
|
||||
lg.Infof("[PlayController] search for %s, got no result", keyword)
|
||||
return
|
||||
}
|
||||
media = medias[0]
|
||||
}
|
||||
media.User = user
|
||||
lg.Infof("[PlayController] add media %s (%s)", media.Title, media.Artist)
|
||||
pc.playlist.GetCurrent().Insert(-1, media)
|
||||
}
|
||||
|
||||
func (pc *PlayController) Seek(position float64, absolute bool) {
|
||||
if err := pc.player.Seek(position, absolute); err != nil {
|
||||
lg.Warnf("[PlayController] seek to position %f (%t) failed, %s", position, absolute, err)
|
||||
}
|
||||
}
|
||||
|
||||
func (pc *PlayController) Toggle() (b bool) {
|
||||
var err error
|
||||
if pc.player.IsPaused() {
|
||||
err = pc.player.Unpause()
|
||||
b = false
|
||||
} else {
|
||||
err = pc.player.Pause()
|
||||
b = true
|
||||
}
|
||||
if err != nil {
|
||||
lg.Warn("[PlayController] toggle failed", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (pc *PlayController) SetVolume(volume float64) {
|
||||
if pc.player.SetVolume(volume) != nil {
|
||||
lg.Warnf("[PlayController] set mpv volume to %f failed", volume)
|
||||
return
|
||||
}
|
||||
pc.Volume = volume
|
||||
}
|
||||
|
||||
func (pc *PlayController) Destroy() {
|
||||
pc.player.Stop()
|
||||
}
|
||||
|
||||
func (pc *PlayController) GetCurrentAudioDevice() string {
|
||||
return pc.AudioDevice
|
||||
}
|
||||
|
||||
func (pc *PlayController) GetAudioDevices() []model.AudioDevice {
|
||||
dl, err := pc.player.GetAudioDeviceList()
|
||||
if err != nil {
|
||||
return make([]model.AudioDevice, 0)
|
||||
}
|
||||
return dl
|
||||
}
|
||||
|
||||
func (pc *PlayController) SetAudioDevice(device string) {
|
||||
lg.Infof("[PlayController] set audio device to %s", device)
|
||||
if err := pc.player.SetAudioDevice(device); err != nil {
|
||||
lg.Warnf("[PlayController] set mpv audio device to %s failed, %s", device, err)
|
||||
_ = pc.player.SetAudioDevice("auto")
|
||||
pc.AudioDevice = "auto"
|
||||
return
|
||||
}
|
||||
pc.AudioDevice = device
|
||||
}
|
||||
397
controller/core/playlist.go
Normal file
397
controller/core/playlist.go
Normal file
@@ -0,0 +1,397 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"AynaLivePlayer/common/event"
|
||||
"AynaLivePlayer/config"
|
||||
"AynaLivePlayer/controller"
|
||||
"AynaLivePlayer/model"
|
||||
"AynaLivePlayer/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: "playlist.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.Info("[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
|
||||
}
|
||||
44
controller/core/plugin.go
Normal file
44
controller/core/plugin.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"AynaLivePlayer/controller"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type PluginController struct {
|
||||
plugins map[string]controller.Plugin
|
||||
}
|
||||
|
||||
func NewPluginController() controller.IPluginController {
|
||||
return &PluginController{
|
||||
plugins: make(map[string]controller.Plugin),
|
||||
}
|
||||
}
|
||||
|
||||
func (p *PluginController) LoadPlugin(plugin controller.Plugin) {
|
||||
lg.Info("[Plugin] Loading plugin: " + plugin.Name())
|
||||
if _, ok := p.plugins[plugin.Name()]; ok {
|
||||
logrus.Warnf("[Plugin] plugin with same name already exists, skip")
|
||||
return
|
||||
}
|
||||
if err := plugin.Enable(); err != nil {
|
||||
lg.Warnf("Failed to load plugin: %s, %s", plugin.Name(), err)
|
||||
return
|
||||
}
|
||||
p.plugins[plugin.Name()] = plugin
|
||||
}
|
||||
|
||||
func (p *PluginController) LoadPlugins(plugins ...controller.Plugin) {
|
||||
for _, plugin := range plugins {
|
||||
p.LoadPlugin(plugin)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *PluginController) ClosePlugins() {
|
||||
for _, plugin := range p.plugins {
|
||||
if err := plugin.Disable(); err != nil {
|
||||
lg.Warnf("Failed to close plugin: %s, %s", plugin.Name(), err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
114
controller/core/provider.go
Normal file
114
controller/core/provider.go
Normal file
@@ -0,0 +1,114 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"AynaLivePlayer/config"
|
||||
"AynaLivePlayer/controller"
|
||||
"AynaLivePlayer/model"
|
||||
"AynaLivePlayer/provider"
|
||||
)
|
||||
|
||||
type ProviderController struct {
|
||||
config.BaseConfig
|
||||
Priority []string
|
||||
LocalDir string
|
||||
}
|
||||
|
||||
func (pc *ProviderController) Name() string {
|
||||
return "Provider"
|
||||
}
|
||||
|
||||
func NewProviderController() controller.IProviderController {
|
||||
p := &ProviderController{
|
||||
Priority: []string{"netease", "kuwo", "bilibili", "local", "bilibili-video"},
|
||||
LocalDir: "./music",
|
||||
}
|
||||
config.LoadConfig(p)
|
||||
provider.NewLocal(p.LocalDir)
|
||||
return p
|
||||
}
|
||||
|
||||
func (pc *ProviderController) GetPriority() []string {
|
||||
return pc.Priority
|
||||
}
|
||||
|
||||
func (pc *ProviderController) PrepareMedia(media *model.Media) error {
|
||||
var err error
|
||||
if media.Title == "" || !media.Cover.Exists() {
|
||||
lg.Trace("fetching media info")
|
||||
if err = provider.UpdateMedia(media); err != nil {
|
||||
lg.Warn("fail to prepare media when fetch info", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
if media.Url == "" {
|
||||
lg.Trace("fetching media url")
|
||||
if err = provider.UpdateMediaUrl(media); err != nil {
|
||||
lg.Warn("fail to prepare media when url", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
if media.Lyric == "" {
|
||||
lg.Trace("fetching media lyric")
|
||||
if err = provider.UpdateMediaLyric(media); err != nil {
|
||||
lg.Warn("fail to prepare media when lyric", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pc *ProviderController) MediaMatch(keyword string) *model.Media {
|
||||
lg.Infof("Match media for %s", keyword)
|
||||
for _, p := range pc.Priority {
|
||||
if pr, ok := provider.Providers[p]; ok {
|
||||
m := pr.MatchMedia(keyword)
|
||||
if m == nil {
|
||||
continue
|
||||
}
|
||||
if err := provider.UpdateMedia(m); err == nil {
|
||||
return m
|
||||
}
|
||||
} else {
|
||||
lg.Warnf("Provider %s not exist", p)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pc *ProviderController) Search(keyword string) ([]*model.Media, error) {
|
||||
lg.Infof("Search for %s", keyword)
|
||||
for _, p := range pc.Priority {
|
||||
if pr, ok := provider.Providers[p]; ok {
|
||||
r, err := pr.Search(keyword)
|
||||
if err != nil {
|
||||
lg.Warn("Provider %s return err", err)
|
||||
continue
|
||||
}
|
||||
return r, err
|
||||
} else {
|
||||
lg.Warnf("Provider %s not exist", p)
|
||||
}
|
||||
}
|
||||
return nil, provider.ErrorNoSuchProvider
|
||||
}
|
||||
|
||||
func (pc *ProviderController) SearchWithProvider(keyword string, p string) ([]*model.Media, error) {
|
||||
lg.Infof("Search for %s using %s", keyword, p)
|
||||
if pr, ok := provider.Providers[p]; ok {
|
||||
r, err := pr.Search(keyword)
|
||||
return r, err
|
||||
}
|
||||
lg.Warnf("Provider %s not exist", p)
|
||||
return nil, provider.ErrorNoSuchProvider
|
||||
}
|
||||
|
||||
func (pc *ProviderController) PreparePlaylist(playlist controller.IPlaylist) error {
|
||||
lg.Debug("Prepare playlist ", playlist.Name())
|
||||
medias, err := provider.GetPlaylist(&playlist.Model().Meta)
|
||||
if err != nil {
|
||||
lg.Warn("prepare playlist failed ", err)
|
||||
return err
|
||||
}
|
||||
controller.ApplyUser(medias, controller.SystemUser)
|
||||
playlist.Replace(medias)
|
||||
return nil
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"AynaLivePlayer/event"
|
||||
"AynaLivePlayer/liveclient"
|
||||
)
|
||||
|
||||
var DanmuHandlers []DanmuHandler
|
||||
|
||||
type DanmuHandler interface {
|
||||
Execute(anmu *liveclient.DanmuMessage)
|
||||
}
|
||||
|
||||
func AddDanmuHandler(handlers ...DanmuHandler) {
|
||||
DanmuHandlers = append(DanmuHandlers, handlers...)
|
||||
}
|
||||
|
||||
func danmuHandler(event *event.Event) {
|
||||
danmu := event.Data.(*liveclient.DanmuMessage)
|
||||
for _, cmd := range DanmuHandlers {
|
||||
cmd.Execute(danmu)
|
||||
}
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"AynaLivePlayer/config"
|
||||
"AynaLivePlayer/liveclient"
|
||||
"AynaLivePlayer/player"
|
||||
"AynaLivePlayer/provider"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
var MainPlayer *player.Player
|
||||
var LiveClient liveclient.LiveClient
|
||||
var CurrentLyric *player.Lyric
|
||||
var CurrentMedia *player.Media
|
||||
|
||||
func Initialize() {
|
||||
MainPlayer = player.NewPlayer()
|
||||
|
||||
SetAudioDevice(config.Player.AudioDevice)
|
||||
SetVolume(config.Player.Volume)
|
||||
|
||||
UserPlaylist = player.NewPlaylist("user", player.PlaylistConfig{RandomNext: config.Player.UserPlaylistRandom})
|
||||
SystemPlaylist = player.NewPlaylist("system", player.PlaylistConfig{RandomNext: config.Player.PlaylistRandom})
|
||||
PlaylistManager = make([]*player.Playlist, 0)
|
||||
History = player.NewPlaylist("history", player.PlaylistConfig{RandomNext: false})
|
||||
HistoryUser = &player.User{Name: "History"}
|
||||
loadPlaylists()
|
||||
|
||||
config.LoadConfig(LiveRoomManager)
|
||||
LiveRoomManager.InitializeRooms()
|
||||
|
||||
CurrentLyric = player.NewLyric("")
|
||||
|
||||
MainPlayer.ObserveProperty("idle-active", handleMpvIdlePlayNext)
|
||||
UserPlaylist.Handler.RegisterA(player.EventPlaylistInsert, "controller.playnextwhenadd", handlePlaylistAdd)
|
||||
MainPlayer.ObserveProperty("time-pos", handleLyricUpdate)
|
||||
MainPlayer.Start()
|
||||
|
||||
}
|
||||
|
||||
func CloseAndSave() {
|
||||
// set value to global config
|
||||
config.Player.PlaylistRandom = SystemPlaylist.Config.RandomNext
|
||||
config.Player.UserPlaylistRandom = UserPlaylist.Config.RandomNext
|
||||
_ = config.SaveToConfigFile(config.ConfigPath)
|
||||
}
|
||||
|
||||
func loadPlaylists() {
|
||||
l.Info("Loading playlists ", config.Player.Playlists)
|
||||
if len(config.Player.Playlists) != len(config.Player.Playlists) {
|
||||
l.Warn("playlist id and provider does not have same length")
|
||||
return
|
||||
}
|
||||
for i := 0; i < len(config.Player.Playlists); i++ {
|
||||
pc := config.Player.Playlists[i]
|
||||
p := player.NewPlaylist(fmt.Sprintf("%s-%s", pc.Provider, pc.ID), player.PlaylistConfig{})
|
||||
p.Meta = provider.Meta{
|
||||
Name: pc.Provider,
|
||||
Id: pc.ID,
|
||||
}
|
||||
PlaylistManager = append(PlaylistManager, p)
|
||||
}
|
||||
if config.Player.PlaylistIndex < 0 || config.Player.PlaylistIndex >= len(config.Player.Playlists) {
|
||||
config.Player.PlaylistIndex = 0
|
||||
l.Warn("playlist index did not find")
|
||||
return
|
||||
}
|
||||
go func() {
|
||||
c := config.Player.PlaylistIndex
|
||||
err := PreparePlaylist(PlaylistManager[c])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
SetSystemPlaylist(c)
|
||||
}()
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"AynaLivePlayer/config"
|
||||
"AynaLivePlayer/event"
|
||||
"AynaLivePlayer/player"
|
||||
"github.com/aynakeya/go-mpv"
|
||||
)
|
||||
|
||||
func handleMpvIdlePlayNext(property *mpv.EventProperty) {
|
||||
isIdle := property.Data.(mpv.Node).Value.(bool)
|
||||
if isIdle {
|
||||
l.Info("mpv went idle, try play next")
|
||||
PlayNext()
|
||||
}
|
||||
}
|
||||
|
||||
func handlePlaylistAdd(event *event.Event) {
|
||||
if MainPlayer.IsIdle() {
|
||||
PlayNext()
|
||||
return
|
||||
}
|
||||
if config.Player.SkipPlaylist && CurrentMedia != nil && CurrentMedia.User == player.PlaylistUser {
|
||||
PlayNext()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func handleLyricUpdate(property *mpv.EventProperty) {
|
||||
if property.Data == nil {
|
||||
return
|
||||
}
|
||||
t := property.Data.(mpv.Node).Value.(float64)
|
||||
CurrentLyric.Update(t)
|
||||
}
|
||||
@@ -1,170 +1,30 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"AynaLivePlayer/config"
|
||||
"AynaLivePlayer/event"
|
||||
"AynaLivePlayer/common/event"
|
||||
"AynaLivePlayer/liveclient"
|
||||
"errors"
|
||||
"fmt"
|
||||
"AynaLivePlayer/model"
|
||||
)
|
||||
|
||||
var LiveRoomManager = &LiveRooms{
|
||||
LiveRoomPath: "liverooms.json",
|
||||
LiveRooms: []*LiveRoom{
|
||||
{
|
||||
ClientName: "bilibili",
|
||||
ID: "9076804",
|
||||
AutoConnect: false,
|
||||
},
|
||||
{
|
||||
ClientName: "bilibili",
|
||||
ID: "3819533",
|
||||
AutoConnect: false,
|
||||
},
|
||||
},
|
||||
type DanmuCommandExecutor interface {
|
||||
Match(command string) bool
|
||||
Execute(command string, args []string, danmu *liveclient.DanmuMessage)
|
||||
}
|
||||
|
||||
type LiveRooms struct {
|
||||
LiveRoomPath string
|
||||
LiveRooms []*LiveRoom `ini:"-"`
|
||||
type ILiveRoomController interface {
|
||||
Size() int
|
||||
Get(index int) ILiveRoom
|
||||
GetRoomStatus(index int) bool
|
||||
Connect(index int) error
|
||||
Disconnect(index int) error
|
||||
AddRoom(clientName, roomId string) (*model.LiveRoom, error)
|
||||
DeleteRoom(index int) error
|
||||
AddDanmuCommand(executor DanmuCommandExecutor)
|
||||
}
|
||||
|
||||
func (lr *LiveRooms) Name() string {
|
||||
return "LiveRoom"
|
||||
}
|
||||
|
||||
func (lr *LiveRooms) Size() int {
|
||||
return len(lr.LiveRooms)
|
||||
}
|
||||
|
||||
func (lr *LiveRooms) OnLoad() {
|
||||
_ = config.LoadJson(lr.LiveRoomPath, &lr.LiveRooms)
|
||||
}
|
||||
|
||||
func (lr *LiveRooms) OnSave() {
|
||||
_ = config.SaveJson(lr.LiveRoomPath, &lr.LiveRooms)
|
||||
}
|
||||
|
||||
func (lr *LiveRooms) InitializeRooms() {
|
||||
for i := 0; i < len(lr.LiveRooms); i++ {
|
||||
if lr.LiveRooms[i].client == nil {
|
||||
lr.LiveRooms[i].Init()
|
||||
}
|
||||
}
|
||||
go func() {
|
||||
for i := 0; i < len(lr.LiveRooms); i++ {
|
||||
if lr.LiveRooms[i].AutoConnect {
|
||||
go lr.LiveRooms[i].Connect()
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (lr *LiveRooms) GetRoom(index int) *LiveRoom {
|
||||
if index < 0 || index >= len(lr.LiveRooms) {
|
||||
return nil
|
||||
}
|
||||
return lr.LiveRooms[index]
|
||||
}
|
||||
|
||||
func (lr *LiveRooms) AddRoom(clientName, roomId string) (*LiveRoom, error) {
|
||||
l.Infof("add live client (%s) for %s", clientName, roomId)
|
||||
rm := &LiveRoom{
|
||||
ClientName: clientName,
|
||||
ID: roomId,
|
||||
AutoConnect: false,
|
||||
}
|
||||
err := rm.Init()
|
||||
l.Infof("live client (%s) %s init failed", clientName, roomId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
lr.LiveRooms = append(lr.LiveRooms, rm)
|
||||
return rm, nil
|
||||
}
|
||||
|
||||
func (lr *LiveRooms) ConnectRoom(index int) error {
|
||||
l.Infof("Try to start LiveRoom.index=%d", index)
|
||||
if index < 0 || index >= len(lr.LiveRooms) {
|
||||
l.Warnf("LiveRoom.index=%d not found", index)
|
||||
return errors.New("index out of range")
|
||||
}
|
||||
lr.LiveRooms[index].client.Connect()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (lr *LiveRooms) DisconnectRoom(index int) error {
|
||||
l.Infof("Try to Disconnect LiveRoom.index=%d", index)
|
||||
if index < 0 || index >= len(lr.LiveRooms) {
|
||||
l.Warnf("LiveRoom.index=%d not found", index)
|
||||
return errors.New("index out of range")
|
||||
}
|
||||
lr.LiveRooms[index].client.Disconnect()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (lr *LiveRooms) DeleteRoom(index int) error {
|
||||
l.Infof("Try to remove LiveRoom.index=%d", index)
|
||||
if index < 0 || index >= len(lr.LiveRooms) {
|
||||
l.Warnf("LiveRoom.index=%d not found", index)
|
||||
return errors.New("index out of range")
|
||||
}
|
||||
if len(lr.LiveRooms) == 1 {
|
||||
return errors.New("can't delete last room")
|
||||
}
|
||||
lr.LiveRooms[index].client.Handler().UnregisterAll()
|
||||
_ = lr.LiveRooms[index].Disconnect()
|
||||
lr.LiveRooms = append(lr.LiveRooms[:index], lr.LiveRooms[index+1:]...)
|
||||
return nil
|
||||
}
|
||||
|
||||
type LiveRoom struct {
|
||||
ClientName string
|
||||
ID string
|
||||
AutoConnect bool
|
||||
client liveclient.LiveClient
|
||||
}
|
||||
|
||||
func (r *LiveRoom) Init() (err error) {
|
||||
if r.client != nil {
|
||||
return nil
|
||||
}
|
||||
r.client, err = liveclient.NewLiveClient(r.ClientName, r.ID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
r.client.Handler().Register(&event.EventHandler{
|
||||
EventId: liveclient.EventMessageReceive,
|
||||
Name: "controller.commandexecutor",
|
||||
Handler: danmuCommandHandler,
|
||||
})
|
||||
r.client.Handler().RegisterA(
|
||||
liveclient.EventMessageReceive,
|
||||
"controller.danmu.handler",
|
||||
danmuHandler)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *LiveRoom) Connect() error {
|
||||
if r.client == nil {
|
||||
return errors.New("client hasn't initialized yet")
|
||||
}
|
||||
r.client.Connect()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *LiveRoom) Disconnect() error {
|
||||
if r.client == nil {
|
||||
return errors.New("client hasn't initialized yet")
|
||||
}
|
||||
r.client.Disconnect()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *LiveRoom) Title() string {
|
||||
return fmt.Sprintf("%s-%s", r.ClientName, r.ID)
|
||||
}
|
||||
|
||||
func (r *LiveRoom) Client() liveclient.LiveClient {
|
||||
return r.client
|
||||
type ILiveRoom interface {
|
||||
Model() *model.LiveRoom // should return mutable model (not a copy)
|
||||
Title() string // should be same as Model().Title
|
||||
Status() bool
|
||||
EventManager() *event.Manager
|
||||
}
|
||||
|
||||
34
controller/playcontrol.go
Normal file
34
controller/playcontrol.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"AynaLivePlayer/common/event"
|
||||
"AynaLivePlayer/model"
|
||||
"AynaLivePlayer/player"
|
||||
)
|
||||
|
||||
type IPlayController interface {
|
||||
EventManager() *event.Manager
|
||||
GetPlaying() *model.Media
|
||||
GetPlayer() player.IPlayer
|
||||
PlayNext()
|
||||
Play(media *model.Media)
|
||||
Add(keyword string, user interface{})
|
||||
AddWithProvider(keyword string, provider string, user interface{})
|
||||
Seek(position float64, absolute bool)
|
||||
Toggle() bool
|
||||
SetVolume(volume float64)
|
||||
Destroy()
|
||||
GetCurrentAudioDevice() string
|
||||
GetAudioDevices() []model.AudioDevice
|
||||
SetAudioDevice(device string)
|
||||
GetLyric() ILyricLoader
|
||||
GetSkipPlaylist() bool
|
||||
SetSkipPlaylist(b bool)
|
||||
}
|
||||
|
||||
type ILyricLoader interface {
|
||||
EventManager() *event.Manager
|
||||
Get() *model.Lyric
|
||||
Reload(lyric string)
|
||||
Update(time float64)
|
||||
}
|
||||
@@ -1,130 +0,0 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"AynaLivePlayer/config"
|
||||
"AynaLivePlayer/player"
|
||||
"AynaLivePlayer/provider"
|
||||
)
|
||||
|
||||
func PlayNext() {
|
||||
l.Info("try to play next possible media")
|
||||
if UserPlaylist.Size() == 0 && SystemPlaylist.Size() == 0 {
|
||||
return
|
||||
}
|
||||
var media *player.Media
|
||||
if UserPlaylist.Size() != 0 {
|
||||
media = UserPlaylist.Pop()
|
||||
} else if SystemPlaylist.Size() != 0 {
|
||||
media = SystemPlaylist.Next()
|
||||
}
|
||||
Play(media)
|
||||
}
|
||||
|
||||
func Play(media *player.Media) {
|
||||
l.Infof("prepare media %s", media.Title)
|
||||
err := PrepareMedia(media)
|
||||
if err != nil {
|
||||
l.Warn("prepare media failed. try play next")
|
||||
PlayNext()
|
||||
return
|
||||
}
|
||||
CurrentMedia = media
|
||||
AddToHistory(media)
|
||||
if err := MainPlayer.Play(media); err != nil {
|
||||
l.Warn("play failed", err)
|
||||
return
|
||||
}
|
||||
CurrentLyric.Reload(media.Lyric)
|
||||
// reset
|
||||
media.Url = ""
|
||||
}
|
||||
|
||||
func Add(keyword string, user interface{}) {
|
||||
media := MediaMatch(keyword)
|
||||
if media == nil {
|
||||
medias, err := Search(keyword)
|
||||
if err != nil {
|
||||
l.Warnf("search for %s, got error %s", keyword, err)
|
||||
return
|
||||
}
|
||||
if len(medias) == 0 {
|
||||
l.Info("search for %s, got no result", keyword)
|
||||
return
|
||||
}
|
||||
media = medias[0]
|
||||
}
|
||||
media.User = user
|
||||
l.Infof("add media %s (%s)", media.Title, media.Artist)
|
||||
UserPlaylist.Insert(-1, media)
|
||||
}
|
||||
|
||||
func AddWithProvider(keyword string, pname string, user interface{}) {
|
||||
media := provider.MatchMedia(pname, keyword)
|
||||
if media == nil {
|
||||
medias, err := provider.Search(pname, keyword)
|
||||
if err != nil {
|
||||
l.Warnf("search for %s, got error %s", keyword, err)
|
||||
return
|
||||
}
|
||||
if len(medias) == 0 {
|
||||
l.Infof("search for %s, got no result", keyword)
|
||||
return
|
||||
}
|
||||
media = medias[0]
|
||||
}
|
||||
media.User = user
|
||||
l.Infof("add media %s (%s)", media.Title, media.Artist)
|
||||
UserPlaylist.Insert(-1, media)
|
||||
}
|
||||
|
||||
func Seek(position float64, absolute bool) {
|
||||
if err := MainPlayer.Seek(position, absolute); err != nil {
|
||||
l.Warnf("seek to position %f (%t) failed, %s", position, absolute, err)
|
||||
}
|
||||
}
|
||||
|
||||
func Toggle() (b bool) {
|
||||
var err error
|
||||
if MainPlayer.IsPaused() {
|
||||
err = MainPlayer.Unpause()
|
||||
b = false
|
||||
} else {
|
||||
err = MainPlayer.Pause()
|
||||
b = true
|
||||
}
|
||||
if err != nil {
|
||||
l.Warn("toggle failed", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func SetVolume(volume float64) {
|
||||
if MainPlayer.SetVolume(volume) != nil {
|
||||
l.Warnf("set mpv volume to %f failed", volume)
|
||||
return
|
||||
}
|
||||
config.Player.Volume = volume
|
||||
}
|
||||
|
||||
func Destroy() {
|
||||
MainPlayer.Stop()
|
||||
}
|
||||
|
||||
func GetAudioDevices() []player.AudioDevice {
|
||||
dl, err := MainPlayer.GetAudioDeviceList()
|
||||
if err != nil {
|
||||
return make([]player.AudioDevice, 0)
|
||||
}
|
||||
return dl
|
||||
}
|
||||
|
||||
func SetAudioDevice(device string) {
|
||||
l.Infof("set audio device to %s", device)
|
||||
if err := MainPlayer.SetAudioDevice(device); err != nil {
|
||||
l.Warnf("set mpv audio device to %s failed, %s", device, err)
|
||||
MainPlayer.SetAudioDevice("auto")
|
||||
config.Player.AudioDevice = "auto"
|
||||
return
|
||||
}
|
||||
config.Player.AudioDevice = device
|
||||
}
|
||||
@@ -1,104 +1,34 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"AynaLivePlayer/config"
|
||||
"AynaLivePlayer/player"
|
||||
"AynaLivePlayer/provider"
|
||||
"fmt"
|
||||
"AynaLivePlayer/common/event"
|
||||
"AynaLivePlayer/model"
|
||||
)
|
||||
|
||||
var UserPlaylist *player.Playlist
|
||||
var History *player.Playlist
|
||||
var HistoryUser *player.User
|
||||
var SystemPlaylist *player.Playlist
|
||||
var PlaylistManager []*player.Playlist
|
||||
|
||||
func AddToHistory(media *player.Media) {
|
||||
l.Tracef("add media %s (%s) to history", media.Title, media.Artist)
|
||||
media = media.Copy()
|
||||
// reset url for future use
|
||||
media.Url = ""
|
||||
if History.Size() >= 1024 {
|
||||
History.Replace([]*player.Media{})
|
||||
}
|
||||
History.Push(media)
|
||||
return
|
||||
type IPlaylistController interface {
|
||||
Size() int
|
||||
GetHistory() IPlaylist
|
||||
AddToHistory(media *model.Media)
|
||||
GetDefault() IPlaylist
|
||||
GetCurrent() IPlaylist
|
||||
Get(index int) IPlaylist
|
||||
Add(pname string, uri string) IPlaylist
|
||||
Remove(index int) IPlaylist
|
||||
SetDefault(index int) error
|
||||
PreparePlaylistByIndex(index int) error
|
||||
}
|
||||
|
||||
func ToHistoryMedia(media *player.Media) *player.Media {
|
||||
media = media.Copy()
|
||||
media.User = HistoryUser
|
||||
return media
|
||||
}
|
||||
|
||||
func ToSystemMedia(media *player.Media) *player.Media {
|
||||
media = media.Copy()
|
||||
media.User = player.SystemUser
|
||||
return media
|
||||
}
|
||||
|
||||
func AddPlaylist(pname string, uri string) *player.Playlist {
|
||||
l.Infof("try add playlist %s with provider %s", uri, pname)
|
||||
id, err := provider.FormatPlaylistUrl(pname, uri)
|
||||
if err != nil || id == "" {
|
||||
l.Warnf("fail to format %s playlist id for %s", uri, pname)
|
||||
return nil
|
||||
}
|
||||
p := player.NewPlaylist(fmt.Sprintf("%s-%s", pname, id), player.PlaylistConfig{})
|
||||
p.Meta = provider.Meta{
|
||||
Name: pname,
|
||||
Id: id,
|
||||
}
|
||||
PlaylistManager = append(PlaylistManager, p)
|
||||
config.Player.Playlists = append(config.Player.Playlists, &config.PlayerPlaylist{
|
||||
ID: uri,
|
||||
Provider: pname,
|
||||
})
|
||||
return p
|
||||
}
|
||||
|
||||
func RemovePlaylist(index int) {
|
||||
l.Infof("Try to remove playlist.index=%d", index)
|
||||
if index < 0 || index >= len(PlaylistManager) {
|
||||
l.Warnf("playlist.index=%d not found", index)
|
||||
return
|
||||
}
|
||||
if index == config.Player.PlaylistIndex {
|
||||
l.Info("Delete current system playlist, reset system playlist to index = 0")
|
||||
SetSystemPlaylist(0)
|
||||
}
|
||||
if index < config.Player.PlaylistIndex {
|
||||
l.Debugf("Delete playlist before system playlist (index=%d), reduce system playlist index by 1", config.Player.PlaylistIndex)
|
||||
config.Player.PlaylistIndex = config.Player.PlaylistIndex - 1
|
||||
}
|
||||
PlaylistManager = append(PlaylistManager[:index], PlaylistManager[index+1:]...)
|
||||
config.Player.Playlists = append(config.Player.Playlists[:index], config.Player.Playlists[index+1:]...)
|
||||
}
|
||||
|
||||
func SetSystemPlaylist(index int) {
|
||||
l.Infof("try set system playlist to playlist.id=%d", index)
|
||||
if index < 0 || index >= len(PlaylistManager) {
|
||||
l.Warn("playlist.index=%d not found", index)
|
||||
return
|
||||
}
|
||||
err := PreparePlaylist(PlaylistManager[index])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
medias := PlaylistManager[index].Playlist
|
||||
config.Player.PlaylistIndex = index
|
||||
ApplyUser(medias, player.PlaylistUser)
|
||||
SystemPlaylist.Replace(medias)
|
||||
}
|
||||
|
||||
func PreparePlaylistByIndex(index int) {
|
||||
l.Infof("try prepare playlist.id=%d", index)
|
||||
if index < 0 || index >= len(PlaylistManager) {
|
||||
l.Warn("playlist.id=%d not found", index)
|
||||
return
|
||||
}
|
||||
err := PreparePlaylist(PlaylistManager[index])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
type IPlaylist interface {
|
||||
Model() *model.Playlist // mutable model (not a copy)
|
||||
EventManager() *event.Manager
|
||||
Name() string
|
||||
Size() int
|
||||
Get(index int) *model.Media
|
||||
Pop() *model.Media
|
||||
Replace(medias []*model.Media)
|
||||
Push(media *model.Media)
|
||||
Insert(index int, media *model.Media)
|
||||
Delete(index int) *model.Media
|
||||
Move(src int, dst int)
|
||||
Next() *model.Media
|
||||
}
|
||||
|
||||
@@ -6,25 +6,8 @@ type Plugin interface {
|
||||
Disable() error
|
||||
}
|
||||
|
||||
func LoadPlugin(plugin Plugin) {
|
||||
l.Info("Loading plugin: " + plugin.Name())
|
||||
if err := plugin.Enable(); err != nil {
|
||||
l.Warnf("Failed to load plugin: %s, %s", plugin.Name(), err)
|
||||
}
|
||||
}
|
||||
|
||||
func LoadPlugins(plugins ...Plugin) {
|
||||
for _, plugin := range plugins {
|
||||
LoadPlugin(plugin)
|
||||
}
|
||||
}
|
||||
|
||||
func ClosePlugins(plugins ...Plugin) {
|
||||
for _, plugin := range plugins {
|
||||
err := plugin.Disable()
|
||||
if err != nil {
|
||||
l.Warnf("Failed to close plugin: %s, %s", plugin.Name(), err)
|
||||
return
|
||||
}
|
||||
}
|
||||
type IPluginController interface {
|
||||
LoadPlugin(plugin Plugin)
|
||||
LoadPlugins(plugins ...Plugin)
|
||||
ClosePlugins()
|
||||
}
|
||||
|
||||
@@ -1,95 +1,30 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"AynaLivePlayer/config"
|
||||
"AynaLivePlayer/player"
|
||||
"AynaLivePlayer/provider"
|
||||
"AynaLivePlayer/model"
|
||||
)
|
||||
|
||||
func PrepareMedia(media *player.Media) error {
|
||||
var err error
|
||||
if media.Title == "" || !media.Cover.Exists() {
|
||||
l.Trace("fetching media info")
|
||||
if err = provider.UpdateMedia(media); err != nil {
|
||||
l.Warn("fail to prepare media when fetch info", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
if media.Url == "" {
|
||||
l.Trace("fetching media url")
|
||||
if err = provider.UpdateMediaUrl(media); err != nil {
|
||||
l.Warn("fail to prepare media when url", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
if media.Lyric == "" {
|
||||
l.Trace("fetching media lyric")
|
||||
if err = provider.UpdateMediaLyric(media); err != nil {
|
||||
l.Warn("fail to prepare media when lyric", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
var PlaylistUser = &model.User{Name: "Playlists"}
|
||||
var SystemUser = &model.User{Name: "System"}
|
||||
var HistoryUser = &model.User{Name: "History"}
|
||||
|
||||
type IProviderController interface {
|
||||
GetPriority() []string
|
||||
PrepareMedia(media *model.Media) error
|
||||
MediaMatch(keyword string) *model.Media
|
||||
Search(keyword string) ([]*model.Media, error)
|
||||
SearchWithProvider(keyword string, provider string) ([]*model.Media, error)
|
||||
PreparePlaylist(playlist IPlaylist) error
|
||||
}
|
||||
|
||||
func MediaMatch(keyword string) *player.Media {
|
||||
l.Infof("Match media for %s", keyword)
|
||||
for _, p := range config.Provider.Priority {
|
||||
if pr, ok := provider.Providers[p]; ok {
|
||||
m := pr.MatchMedia(keyword)
|
||||
if m == nil {
|
||||
continue
|
||||
}
|
||||
if err := provider.UpdateMedia(m); err == nil {
|
||||
return m
|
||||
}
|
||||
} else {
|
||||
l.Warnf("Provider %s not exist", p)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func Search(keyword string) ([]*player.Media, error) {
|
||||
l.Infof("Search for %s", keyword)
|
||||
for _, p := range config.Provider.Priority {
|
||||
if pr, ok := provider.Providers[p]; ok {
|
||||
r, err := pr.Search(keyword)
|
||||
if err != nil {
|
||||
l.Warn("Provider %s return err", err)
|
||||
continue
|
||||
}
|
||||
return r, err
|
||||
} else {
|
||||
l.Warnf("Provider %s not exist", p)
|
||||
}
|
||||
}
|
||||
return nil, provider.ErrorNoSuchProvider
|
||||
}
|
||||
|
||||
func SearchWithProvider(keyword string, p string) ([]*player.Media, error) {
|
||||
l.Infof("Search for %s using %s", keyword, p)
|
||||
if pr, ok := provider.Providers[p]; ok {
|
||||
r, err := pr.Search(keyword)
|
||||
return r, err
|
||||
}
|
||||
l.Warnf("Provider %s not exist", p)
|
||||
return nil, provider.ErrorNoSuchProvider
|
||||
}
|
||||
|
||||
func ApplyUser(medias []*player.Media, user interface{}) {
|
||||
func ApplyUser(medias []*model.Media, user interface{}) {
|
||||
for _, m := range medias {
|
||||
m.User = user
|
||||
}
|
||||
}
|
||||
|
||||
func PreparePlaylist(playlist *player.Playlist) error {
|
||||
l.Debug("Prepare playlist ", playlist.Meta.(provider.Meta))
|
||||
medias, err := provider.GetPlaylist(playlist.Meta.(provider.Meta))
|
||||
if err != nil {
|
||||
l.Warn("prepare playlist failed ", err)
|
||||
return err
|
||||
}
|
||||
ApplyUser(medias, player.SystemUser)
|
||||
playlist.Replace(medias)
|
||||
return nil
|
||||
func ToSpMedia(media *model.Media, user *model.User) *model.Media {
|
||||
media = media.Copy()
|
||||
media.User = user
|
||||
return media
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user