mirror of
https://github.com/AynaLivePlayer/AynaLivePlayer.git
synced 2025-12-06 18:32:50 +08:00
config panel, kuwo source, playlist operation, bug fix @6, panic handling
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
.idea
|
||||||
@@ -5,14 +5,30 @@ import (
|
|||||||
"AynaLivePlayer/controller"
|
"AynaLivePlayer/controller"
|
||||||
"AynaLivePlayer/gui"
|
"AynaLivePlayer/gui"
|
||||||
"AynaLivePlayer/logger"
|
"AynaLivePlayer/logger"
|
||||||
|
"AynaLivePlayer/plugin/diange"
|
||||||
|
"AynaLivePlayer/plugin/qiege"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/mitchellh/panicwrap"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
exitStatus, _ := panicwrap.BasicWrap(func(s string) {
|
||||||
|
logger.Logger.Panic(s)
|
||||||
|
os.Exit(1)
|
||||||
|
return
|
||||||
|
})
|
||||||
|
if exitStatus >= 0 {
|
||||||
|
os.Exit(exitStatus)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
fmt.Printf("BiliAudioBot Revive %s\n", config.VERSION)
|
fmt.Printf("BiliAudioBot Revive %s\n", config.VERSION)
|
||||||
logger.Logger.SetLevel(logrus.DebugLevel)
|
logger.Logger.SetLevel(logrus.DebugLevel)
|
||||||
controller.Initialize()
|
controller.Initialize()
|
||||||
|
controller.LoadPlugins(diange.NewDiange(), qiege.NewQiege())
|
||||||
defer func() {
|
defer func() {
|
||||||
controller.Destroy()
|
controller.Destroy()
|
||||||
config.SaveToConfigFile(config.CONFIG_PATH)
|
config.SaveToConfigFile(config.CONFIG_PATH)
|
||||||
|
|||||||
@@ -1,23 +1,18 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import "fmt"
|
||||||
"image/color"
|
|
||||||
|
|
||||||
"fyne.io/fyne/v2/app"
|
|
||||||
"fyne.io/fyne/v2/canvas"
|
|
||||||
"fyne.io/fyne/v2/container"
|
|
||||||
"fyne.io/fyne/v2/layout"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
myApp := app.New()
|
defer func(x int) { fmt.Println(x) }(func() int { fmt.Println("build"); return 123 }())
|
||||||
myWindow := myApp.NewWindow("Form Layout")
|
fmt.Println("main")
|
||||||
|
//myApp := app.New()
|
||||||
label1 := canvas.NewText("Label 1", color.Black)
|
//myWindow := myApp.NewWindow("Form Layout")
|
||||||
value1 := canvas.NewText("Value", color.Black)
|
//
|
||||||
label2 := canvas.NewText("Label 2", color.Black)
|
//label1 := canvas.NewText("Label 1", color.Black)
|
||||||
value2 := canvas.NewText("Something", color.Black)
|
//value1 := canvas.NewText("Value", color.Black)
|
||||||
grid := container.New(layout.NewFormLayout(), label1, value1, label2, value2)
|
//label2 := canvas.NewText("Label 2", color.Black)
|
||||||
myWindow.SetContent(grid)
|
//value2 := canvas.NewText("Something", color.Black)
|
||||||
myWindow.ShowAndRun()
|
//grid := container.New(layout.NewFormLayout(), label1, value1, label2, value2)
|
||||||
|
//myWindow.SetContent(grid)
|
||||||
|
//myWindow.ShowAndRun()
|
||||||
}
|
}
|
||||||
|
|||||||
17
config.ini
17
config.ini
@@ -1,17 +0,0 @@
|
|||||||
[Log]
|
|
||||||
Path = ./log.txt
|
|
||||||
Level = 4
|
|
||||||
|
|
||||||
[LiveRoom]
|
|
||||||
History = 9076804,3819533
|
|
||||||
|
|
||||||
[Player]
|
|
||||||
Playlists = 116746576,467824586
|
|
||||||
PlaylistsProvider = netease,netease
|
|
||||||
PlaylistIndex = 0
|
|
||||||
PlaylistRandom = true
|
|
||||||
|
|
||||||
[Provider]
|
|
||||||
Priority = local,netease,kuwo,bilibili
|
|
||||||
LocalDir = ./music
|
|
||||||
|
|
||||||
100
config/config.go
100
config/config.go
@@ -2,12 +2,11 @@ package config
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
"gopkg.in/ini.v1"
|
"gopkg.in/ini.v1"
|
||||||
"path"
|
"path"
|
||||||
)
|
)
|
||||||
|
|
||||||
const VERSION = "alpha 0.4"
|
const VERSION = "alpha 0.6"
|
||||||
|
|
||||||
const CONFIG_PATH = "./config.ini"
|
const CONFIG_PATH = "./config.ini"
|
||||||
const Assests_PATH = "./assets"
|
const Assests_PATH = "./assets"
|
||||||
@@ -16,91 +15,40 @@ func GetAssetPath(name string) string {
|
|||||||
return path.Join(Assests_PATH, name)
|
return path.Join(Assests_PATH, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
type LogConfig struct {
|
type Config interface {
|
||||||
Path string
|
Name() string
|
||||||
Level logrus.Level
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var Log = &LogConfig{
|
var ConfigFile *ini.File
|
||||||
Path: "./log.txt",
|
var Configs = make([]Config, 0)
|
||||||
Level: logrus.InfoLevel,
|
|
||||||
}
|
|
||||||
|
|
||||||
type LiveRoomConfig struct {
|
func LoadConfig(cfg Config) {
|
||||||
History []string
|
sec, err := ConfigFile.GetSection(cfg.Name())
|
||||||
}
|
if err == nil {
|
||||||
|
_ = sec.MapTo(cfg)
|
||||||
var LiveRoom = &LiveRoomConfig{History: []string{"9076804", "3819533"}}
|
}
|
||||||
|
Configs = append(Configs, cfg)
|
||||||
type PlayerConfig struct {
|
return
|
||||||
Playlists []string
|
|
||||||
PlaylistsProvider []string
|
|
||||||
PlaylistIndex int
|
|
||||||
PlaylistRandom bool
|
|
||||||
}
|
|
||||||
|
|
||||||
var Player = &PlayerConfig{
|
|
||||||
Playlists: []string{"116746576", "646548465"},
|
|
||||||
PlaylistsProvider: []string{"netease", "netease"},
|
|
||||||
PlaylistIndex: 0,
|
|
||||||
PlaylistRandom: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
type ProviderConfig struct {
|
|
||||||
Priority []string
|
|
||||||
LocalDir string
|
|
||||||
}
|
|
||||||
|
|
||||||
var Provider = &ProviderConfig{
|
|
||||||
Priority: []string{"local", "netease", "kuwo", "bilibili"},
|
|
||||||
LocalDir: "./music",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
cfg, err := ini.Load(CONFIG_PATH)
|
var err error
|
||||||
|
ConfigFile, err = ini.Load(CONFIG_PATH)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("config not found")
|
fmt.Println("config not found, using default config")
|
||||||
SaveToConfigFile(CONFIG_PATH)
|
ConfigFile = ini.Empty()
|
||||||
return
|
|
||||||
}
|
}
|
||||||
err = cfg.Section("Log").MapTo(Log)
|
for _, cfg := range []Config{Log, LiveRoom, Player, Provider} {
|
||||||
if err != nil {
|
LoadConfig(cfg)
|
||||||
fmt.Println(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = cfg.Section("LiveRoom").MapTo(LiveRoom)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
fmt.Println(cfg.Section("Player").GetKey("Playlists"))
|
|
||||||
err = cfg.Section("Player").MapTo(Player)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = cfg.Section("Provider").MapTo(Provider)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func SaveToConfigFile(filename string) error {
|
func SaveToConfigFile(filename string) error {
|
||||||
cfg := ini.Empty()
|
cfgFile := ini.Empty()
|
||||||
err := ini.ReflectFrom(cfg, &struct {
|
for _, cfg := range Configs {
|
||||||
Log *LogConfig
|
if err := cfgFile.Section(cfg.Name()).ReflectFrom(cfg); err != nil {
|
||||||
LiveRoom *LiveRoomConfig
|
fmt.Println(err)
|
||||||
Player *PlayerConfig
|
}
|
||||||
Provider *ProviderConfig
|
|
||||||
}{
|
|
||||||
Log: Log,
|
|
||||||
LiveRoom: LiveRoom,
|
|
||||||
Player: Player,
|
|
||||||
Provider: Provider,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
return cfg.SaveTo(filename)
|
return cfgFile.SaveTo(filename)
|
||||||
}
|
}
|
||||||
|
|||||||
11
config/config_liveroom.go
Normal file
11
config/config_liveroom.go
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
type _LiveRoomConfig struct {
|
||||||
|
History []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *_LiveRoomConfig) Name() string {
|
||||||
|
return "LiveRoom"
|
||||||
|
}
|
||||||
|
|
||||||
|
var LiveRoom = &_LiveRoomConfig{History: []string{"9076804", "3819533"}}
|
||||||
17
config/config_log.go
Normal file
17
config/config_log.go
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
import "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
type _LogConfig struct {
|
||||||
|
Path string
|
||||||
|
Level logrus.Level
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *_LogConfig) Name() string {
|
||||||
|
return "Log"
|
||||||
|
}
|
||||||
|
|
||||||
|
var Log = &_LogConfig{
|
||||||
|
Path: "./log.txt",
|
||||||
|
Level: logrus.InfoLevel,
|
||||||
|
}
|
||||||
19
config/config_player.go
Normal file
19
config/config_player.go
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
type _PlayerConfig struct {
|
||||||
|
Playlists []string
|
||||||
|
PlaylistsProvider []string
|
||||||
|
PlaylistIndex int
|
||||||
|
PlaylistRandom bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *_PlayerConfig) Name() string {
|
||||||
|
return "Player"
|
||||||
|
}
|
||||||
|
|
||||||
|
var Player = &_PlayerConfig{
|
||||||
|
Playlists: []string{"2382819181", "116746576", "646548465"},
|
||||||
|
PlaylistsProvider: []string{"netease", "netease", "netease"},
|
||||||
|
PlaylistIndex: 0,
|
||||||
|
PlaylistRandom: true,
|
||||||
|
}
|
||||||
15
config/config_provider.go
Normal file
15
config/config_provider.go
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
type _ProviderConfig struct {
|
||||||
|
Priority []string
|
||||||
|
LocalDir string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *_ProviderConfig) Name() string {
|
||||||
|
return "Provider"
|
||||||
|
}
|
||||||
|
|
||||||
|
var Provider = &_ProviderConfig{
|
||||||
|
Priority: []string{"local", "netease", "kuwo", "bilibili"},
|
||||||
|
LocalDir: "./music",
|
||||||
|
}
|
||||||
@@ -11,4 +11,5 @@ func TestCreate(t *testing.T) {
|
|||||||
|
|
||||||
func TestLoad(t *testing.T) {
|
func TestLoad(t *testing.T) {
|
||||||
fmt.Println(Log.Path)
|
fmt.Println(Log.Path)
|
||||||
|
fmt.Println(Player.Playlists)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,94 +0,0 @@
|
|||||||
package controller
|
|
||||||
|
|
||||||
import (
|
|
||||||
"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().Info("prepare media")
|
|
||||||
err := PrepareMedia(media)
|
|
||||||
if err != nil {
|
|
||||||
l().Warn("prepare media failed. try play next")
|
|
||||||
PlayNext()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if err := MainPlayer.Play(media); err != nil {
|
|
||||||
l().Warn("play failed", err)
|
|
||||||
}
|
|
||||||
CurrentLyric.Reload(media.Lyric)
|
|
||||||
// reset
|
|
||||||
media.Url = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func Add(keyword string, user interface{}) {
|
|
||||||
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{}) {
|
|
||||||
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().Info("search for %s, got no result", keyword)
|
|
||||||
}
|
|
||||||
media := medias[0]
|
|
||||||
media.User = user
|
|
||||||
l().Info("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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -20,6 +20,9 @@ func AddCommand(executors ...DanmuCommandExecutor) {
|
|||||||
func danmuCommandHandler(event *event.Event) {
|
func danmuCommandHandler(event *event.Event) {
|
||||||
danmu := event.Data.(*liveclient.DanmuMessage)
|
danmu := event.Data.(*liveclient.DanmuMessage)
|
||||||
args := strings.Split(danmu.Message, " ")
|
args := strings.Split(danmu.Message, " ")
|
||||||
|
if len(args[0]) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
for _, cmd := range Commands {
|
for _, cmd := range Commands {
|
||||||
if cmd.Match(args[0]) {
|
if cmd.Match(args[0]) {
|
||||||
cmd.Execute(args[0], args[1:], danmu)
|
cmd.Execute(args[0], args[1:], danmu)
|
||||||
|
|||||||
@@ -1,25 +0,0 @@
|
|||||||
package controller
|
|
||||||
|
|
||||||
import (
|
|
||||||
"AynaLivePlayer/liveclient"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
var CommandDiange = &Diange{}
|
|
||||||
|
|
||||||
type Diange struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d Diange) Match(command string) bool {
|
|
||||||
for _, c := range []string{"点歌"} {
|
|
||||||
if command == c {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d Diange) Execute(command string, args []string, danmu *liveclient.DanmuMessage) {
|
|
||||||
keyword := strings.Join(args, " ")
|
|
||||||
Add(keyword, &danmu.User)
|
|
||||||
}
|
|
||||||
@@ -19,15 +19,93 @@ func l() *logrus.Entry {
|
|||||||
return logger.Logger.WithField("Module", MODULE_CONTROLLER)
|
return logger.Logger.WithField("Module", MODULE_CONTROLLER)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Initialize() {
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
AddCommand(CommandDiange)
|
func Play(media *player.Media) {
|
||||||
|
l().Info("prepare media")
|
||||||
|
err := PrepareMedia(media)
|
||||||
|
if err != nil {
|
||||||
|
l().Warn("prepare media failed. try play next")
|
||||||
|
PlayNext()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
CurrentMedia = media
|
||||||
|
if err := MainPlayer.Play(media); err != nil {
|
||||||
|
l().Warn("play failed", err)
|
||||||
|
}
|
||||||
|
CurrentLyric.Reload(media.Lyric)
|
||||||
|
// reset
|
||||||
|
media.Url = ""
|
||||||
|
}
|
||||||
|
|
||||||
MainPlayer.ObserveProperty("idle-active", handleMpvIdlePlayNext)
|
func Add(keyword string, user interface{}) {
|
||||||
UserPlaylist.Handler.RegisterA(player.EventPlaylistInsert, "controller.playnextwhenadd", handlePlaylistAdd)
|
medias, err := Search(keyword)
|
||||||
MainPlayer.ObserveProperty("time-pos", handleLyricUpdate)
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
MainPlayer.Start()
|
func AddWithProvider(keyword string, pname string, user interface{}) {
|
||||||
|
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().Info("search for %s, got no result", keyword)
|
||||||
|
}
|
||||||
|
media := medias[0]
|
||||||
|
media.User = user
|
||||||
|
l().Info("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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Destroy() {
|
func Destroy() {
|
||||||
@@ -51,6 +129,10 @@ func SetDanmuClient(roomId string) {
|
|||||||
Name: "controller.commandexecutor",
|
Name: "controller.commandexecutor",
|
||||||
Handler: danmuCommandHandler,
|
Handler: danmuCommandHandler,
|
||||||
})
|
})
|
||||||
|
LiveClient.Handler().RegisterA(
|
||||||
|
liveclient.EventMessageReceive,
|
||||||
|
"controller.danmu.handler",
|
||||||
|
danmuHandler)
|
||||||
l().Infof("setting live client for %s success", roomId)
|
l().Infof("setting live client for %s success", roomId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
23
controller/danmu.go
Normal file
23
controller/danmu.go
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,14 +14,21 @@ var SystemPlaylist *player.Playlist
|
|||||||
var LiveClient liveclient.LiveClient
|
var LiveClient liveclient.LiveClient
|
||||||
var PlaylistManager []*player.Playlist
|
var PlaylistManager []*player.Playlist
|
||||||
var CurrentLyric *player.Lyric
|
var CurrentLyric *player.Lyric
|
||||||
|
var CurrentMedia *player.Media
|
||||||
|
|
||||||
|
func Initialize() {
|
||||||
|
|
||||||
func init() {
|
|
||||||
MainPlayer = player.NewPlayer()
|
MainPlayer = player.NewPlayer()
|
||||||
UserPlaylist = player.NewPlaylist("user", player.PlaylistConfig{RandomNext: false})
|
UserPlaylist = player.NewPlaylist("user", player.PlaylistConfig{RandomNext: false})
|
||||||
SystemPlaylist = player.NewPlaylist("system", player.PlaylistConfig{RandomNext: config.Player.PlaylistRandom})
|
SystemPlaylist = player.NewPlaylist("system", player.PlaylistConfig{RandomNext: config.Player.PlaylistRandom})
|
||||||
PlaylistManager = make([]*player.Playlist, 0)
|
PlaylistManager = make([]*player.Playlist, 0)
|
||||||
CurrentLyric = player.NewLyric("")
|
CurrentLyric = player.NewLyric("")
|
||||||
loadPlaylists()
|
loadPlaylists()
|
||||||
|
|
||||||
|
MainPlayer.ObserveProperty("idle-active", handleMpvIdlePlayNext)
|
||||||
|
UserPlaylist.Handler.RegisterA(player.EventPlaylistInsert, "controller.playnextwhenadd", handlePlaylistAdd)
|
||||||
|
MainPlayer.ObserveProperty("time-pos", handleLyricUpdate)
|
||||||
|
MainPlayer.Start()
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadPlaylists() {
|
func loadPlaylists() {
|
||||||
|
|||||||
19
controller/plugin.go
Normal file
19
controller/plugin.go
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
package controller
|
||||||
|
|
||||||
|
type Plugin interface {
|
||||||
|
Name() string
|
||||||
|
Enable() 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -65,6 +65,7 @@ func ApplyUser(medias []*player.Media, user interface{}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func PreparePlaylist(playlist *player.Playlist) error {
|
func PreparePlaylist(playlist *player.Playlist) error {
|
||||||
|
l().Debug("Prepare playlist ", playlist.Meta.(provider.Meta))
|
||||||
medias, err := provider.GetPlaylist(playlist.Meta.(provider.Meta))
|
medias, err := provider.GetPlaylist(playlist.Meta.(provider.Meta))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l().Warn("prepare playlist failed ", err)
|
l().Warn("prepare playlist failed ", err)
|
||||||
|
|||||||
13
go.mod
13
go.mod
@@ -4,14 +4,27 @@ go 1.16
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
fyne.io/fyne/v2 v2.1.4
|
fyne.io/fyne/v2 v2.1.4
|
||||||
|
github.com/BurntSushi/toml v0.4.1
|
||||||
|
github.com/Kodeworks/golang-image-ico v0.0.0-20141118225523-73f0f4cfade9
|
||||||
github.com/XiaoMengXinX/Music163Api-Go v0.1.26
|
github.com/XiaoMengXinX/Music163Api-Go v0.1.26
|
||||||
github.com/antonfisher/nested-logrus-formatter v1.3.1
|
github.com/antonfisher/nested-logrus-formatter v1.3.1
|
||||||
github.com/aynakeya/blivedm v0.1.3
|
github.com/aynakeya/blivedm v0.1.3
|
||||||
github.com/aynakeya/go-mpv v0.0.4
|
github.com/aynakeya/go-mpv v0.0.4
|
||||||
|
github.com/go-ole/go-ole v1.2.6
|
||||||
github.com/go-resty/resty/v2 v2.7.0
|
github.com/go-resty/resty/v2 v2.7.0
|
||||||
|
github.com/jackmordaunt/icns v0.0.0-20181231085925-4f16af745526
|
||||||
|
github.com/josephspurrier/goversioninfo v0.0.0-20200309025242-14b0ab84c6ca
|
||||||
|
github.com/lucor/goinfo v0.0.0-20210802170112-c078a2b0f08b
|
||||||
|
github.com/mitchellh/panicwrap v1.0.0
|
||||||
|
github.com/pkg/errors v0.9.1
|
||||||
github.com/sirupsen/logrus v1.8.1
|
github.com/sirupsen/logrus v1.8.1
|
||||||
github.com/spf13/cast v1.3.1
|
github.com/spf13/cast v1.3.1
|
||||||
|
github.com/stretchr/testify v1.5.1
|
||||||
github.com/tidwall/gjson v1.14.1
|
github.com/tidwall/gjson v1.14.1
|
||||||
|
github.com/urfave/cli/v2 v2.3.0
|
||||||
|
golang.org/x/mod v0.4.2
|
||||||
|
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c
|
||||||
|
golang.org/x/tools v0.1.5
|
||||||
gopkg.in/ini.v1 v1.66.4
|
gopkg.in/ini.v1 v1.66.4
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
18
go.sum
18
go.sum
@@ -1,13 +1,17 @@
|
|||||||
fyne.io/fyne/v2 v2.1.4 h1:bt1+28++kAzRzPB0GM2EuSV4cnl8rXNX4cjfd8G06Rc=
|
fyne.io/fyne/v2 v2.1.4 h1:bt1+28++kAzRzPB0GM2EuSV4cnl8rXNX4cjfd8G06Rc=
|
||||||
fyne.io/fyne/v2 v2.1.4/go.mod h1:p+E/Dh+wPW8JwR2DVcsZ9iXgR9ZKde80+Y+40Is54AQ=
|
fyne.io/fyne/v2 v2.1.4/go.mod h1:p+E/Dh+wPW8JwR2DVcsZ9iXgR9ZKde80+Y+40Is54AQ=
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
|
github.com/BurntSushi/toml v0.4.1 h1:GaI7EiDXDRfa8VshkTj7Fym7ha+y8/XxIgD2okUIjLw=
|
||||||
github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||||
|
github.com/Kodeworks/golang-image-ico v0.0.0-20141118225523-73f0f4cfade9 h1:1ltqoej5GtaWF8jaiA49HwsZD459jqm9YFz9ZtMFpQA=
|
||||||
github.com/Kodeworks/golang-image-ico v0.0.0-20141118225523-73f0f4cfade9/go.mod h1:7uhhqiBaR4CpN0k9rMjOtjpcfGd6DG2m04zQxKnWQ0I=
|
github.com/Kodeworks/golang-image-ico v0.0.0-20141118225523-73f0f4cfade9/go.mod h1:7uhhqiBaR4CpN0k9rMjOtjpcfGd6DG2m04zQxKnWQ0I=
|
||||||
github.com/XiaoMengXinX/Music163Api-Go v0.1.26 h1:Nybor5okI8C0jzAiRvGfpLHdDrPqUbjx5kXWIZDX6pw=
|
github.com/XiaoMengXinX/Music163Api-Go v0.1.26 h1:Nybor5okI8C0jzAiRvGfpLHdDrPqUbjx5kXWIZDX6pw=
|
||||||
github.com/XiaoMengXinX/Music163Api-Go v0.1.26/go.mod h1:kLU/CkLxKnEJFCge0URvQ0lHt6ImoG1/2aVeNbgV2RQ=
|
github.com/XiaoMengXinX/Music163Api-Go v0.1.26/go.mod h1:kLU/CkLxKnEJFCge0URvQ0lHt6ImoG1/2aVeNbgV2RQ=
|
||||||
|
github.com/akavel/rsrc v0.8.0 h1:zjWn7ukO9Kc5Q62DOJCcxGpXC18RawVtYAGdz2aLlfw=
|
||||||
github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c=
|
github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c=
|
||||||
github.com/antonfisher/nested-logrus-formatter v1.3.1 h1:NFJIr+pzwv5QLHTPyKz9UMEoHck02Q9L0FP13b/xSbQ=
|
github.com/antonfisher/nested-logrus-formatter v1.3.1 h1:NFJIr+pzwv5QLHTPyKz9UMEoHck02Q9L0FP13b/xSbQ=
|
||||||
github.com/antonfisher/nested-logrus-formatter v1.3.1/go.mod h1:6WTfyWFkBc9+zyBaKIqRrg/KwMqBbodBjgbHjDz7zjA=
|
github.com/antonfisher/nested-logrus-formatter v1.3.1/go.mod h1:6WTfyWFkBc9+zyBaKIqRrg/KwMqBbodBjgbHjDz7zjA=
|
||||||
|
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
@@ -20,6 +24,7 @@ github.com/go-gl/gl v0.0.0-20210813123233-e4099ee2221f h1:s0O46d8fPwk9kU4k1jj76w
|
|||||||
github.com/go-gl/gl v0.0.0-20210813123233-e4099ee2221f/go.mod h1:wjpnOv6ONl2SuJSxqCPVaPZibGFdSci9HFocT9qtVYM=
|
github.com/go-gl/gl v0.0.0-20210813123233-e4099ee2221f/go.mod h1:wjpnOv6ONl2SuJSxqCPVaPZibGFdSci9HFocT9qtVYM=
|
||||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20211024062804-40e447a793be h1:Z28GdQBfKOL8tNHjvaDn3wHDO7AzTRkmAXvHvnopp98=
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20211024062804-40e447a793be h1:Z28GdQBfKOL8tNHjvaDn3wHDO7AzTRkmAXvHvnopp98=
|
||||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20211024062804-40e447a793be/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20211024062804-40e447a793be/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||||
|
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
||||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||||
github.com/go-resty/resty/v2 v2.6.0/go.mod h1:PwvJS6hvaPkjtjNg9ph+VrSD92bi5Zq73w/BIH7cC3Q=
|
github.com/go-resty/resty/v2 v2.6.0/go.mod h1:PwvJS6hvaPkjtjNg9ph+VrSD92bi5Zq73w/BIH7cC3Q=
|
||||||
github.com/go-resty/resty/v2 v2.7.0 h1:me+K9p3uhSmXtrBZ4k9jcEAfJmuC8IivWHwaLZwPrFY=
|
github.com/go-resty/resty/v2 v2.7.0 h1:me+K9p3uhSmXtrBZ4k9jcEAfJmuC8IivWHwaLZwPrFY=
|
||||||
@@ -32,20 +37,29 @@ github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
|||||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
||||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
|
github.com/jackmordaunt/icns v0.0.0-20181231085925-4f16af745526 h1:NfuKjkj/Xc2z1xZIj+EmNCm5p1nKJPyw3F4E20usXvg=
|
||||||
github.com/jackmordaunt/icns v0.0.0-20181231085925-4f16af745526/go.mod h1:UQkeMHVoNcyXYq9otUupF7/h/2tmHlhrS2zw7ZVvUqc=
|
github.com/jackmordaunt/icns v0.0.0-20181231085925-4f16af745526/go.mod h1:UQkeMHVoNcyXYq9otUupF7/h/2tmHlhrS2zw7ZVvUqc=
|
||||||
|
github.com/josephspurrier/goversioninfo v0.0.0-20200309025242-14b0ab84c6ca h1:ozPUX9TKQZVek4lZWYRsQo7uS8vJ+q4OOHvRhHiCLfU=
|
||||||
github.com/josephspurrier/goversioninfo v0.0.0-20200309025242-14b0ab84c6ca/go.mod h1:eJTEwMjXb7kZ633hO3Ln9mBUCOjX2+FlTljvpl9SYdE=
|
github.com/josephspurrier/goversioninfo v0.0.0-20200309025242-14b0ab84c6ca/go.mod h1:eJTEwMjXb7kZ633hO3Ln9mBUCOjX2+FlTljvpl9SYdE=
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
|
github.com/lucor/goinfo v0.0.0-20210802170112-c078a2b0f08b h1:tLSDWcFhT0WRlnsFszh4iaFTexWF8mmccGTk88Siq7Q=
|
||||||
github.com/lucor/goinfo v0.0.0-20210802170112-c078a2b0f08b/go.mod h1:PRq09yoB+Q2OJReAmwzKivcYyremnibWGbK7WfftHzc=
|
github.com/lucor/goinfo v0.0.0-20210802170112-c078a2b0f08b/go.mod h1:PRq09yoB+Q2OJReAmwzKivcYyremnibWGbK7WfftHzc=
|
||||||
|
github.com/mitchellh/panicwrap v1.0.0 h1:67zIyVakCIvcs69A0FGfZjBdPleaonSgGlXRSRlb6fE=
|
||||||
|
github.com/mitchellh/panicwrap v1.0.0/go.mod h1:pKvZHwWrZowLUzftuFq7coarnxbBXU4aQh3N0BJOeeA=
|
||||||
|
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
|
||||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
|
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
|
||||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
|
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
|
||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||||
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
|
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
|
||||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||||
@@ -70,6 +84,7 @@ github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JT
|
|||||||
github.com/tidwall/pretty v1.1.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
|
github.com/tidwall/pretty v1.1.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
|
||||||
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
|
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
|
||||||
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||||
|
github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M=
|
||||||
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
|
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
|
||||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
github.com/yuin/goldmark v1.3.8 h1:Nw158Q8QN+CPgTmVRByhVwapp8Mm1e2blinhmx4wx5E=
|
github.com/yuin/goldmark v1.3.8 h1:Nw158Q8QN+CPgTmVRByhVwapp8Mm1e2blinhmx4wx5E=
|
||||||
@@ -78,6 +93,7 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
|
|||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/image v0.0.0-20200430140353-33d19683fad8 h1:6WW6V3x1P/jokJBpRQYUJnMHRP6isStQwCozxnU7XQw=
|
golang.org/x/image v0.0.0-20200430140353-33d19683fad8 h1:6WW6V3x1P/jokJBpRQYUJnMHRP6isStQwCozxnU7XQw=
|
||||||
golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||||
|
golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo=
|
||||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
@@ -104,9 +120,11 @@ golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
|
|||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.1.5 h1:ouewzE6p+/VEB31YYnTbEJdi8pFqKp4P4n85vwo3DHA=
|
||||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
||||||
|
|||||||
20
gui/config_basic.go
Normal file
20
gui/config_basic.go
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
package gui
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fyne.io/fyne/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
type bascicConfig struct{}
|
||||||
|
|
||||||
|
func (b bascicConfig) Title() string {
|
||||||
|
return "Basic"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b bascicConfig) Description() string {
|
||||||
|
return "Basic configuration"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b bascicConfig) Create() fyne.CanvasObject {
|
||||||
|
//TODO implement me
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
55
gui/config_layout.go
Normal file
55
gui/config_layout.go
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
package gui
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fyne.io/fyne/v2"
|
||||||
|
"fyne.io/fyne/v2/container"
|
||||||
|
"fyne.io/fyne/v2/widget"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TestConfig struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TestConfig) Title() string {
|
||||||
|
return "Test Title"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (T *TestConfig) Description() string {
|
||||||
|
return "Test Description"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TestConfig) CreatePanel() fyne.CanvasObject {
|
||||||
|
return widget.NewLabel("asdf")
|
||||||
|
}
|
||||||
|
|
||||||
|
func createConfigLayout() fyne.CanvasObject {
|
||||||
|
content := container.NewMax()
|
||||||
|
entryList := widget.NewList(
|
||||||
|
func() int {
|
||||||
|
return len(ConfigList)
|
||||||
|
},
|
||||||
|
func() fyne.CanvasObject {
|
||||||
|
return widget.NewLabel("AAAAAAAAAAAAAAAA")
|
||||||
|
},
|
||||||
|
func(id widget.ListItemID, object fyne.CanvasObject) {
|
||||||
|
object.(*widget.Label).SetText(ConfigList[id].Title())
|
||||||
|
})
|
||||||
|
entryList.OnSelected = func(id widget.ListItemID) {
|
||||||
|
desc := widget.NewRichTextFromMarkdown("## " + ConfigList[id].Title() + " \n\n" + ConfigList[id].Description())
|
||||||
|
for i := range desc.Segments {
|
||||||
|
if seg, ok := desc.Segments[i].(*widget.TextSegment); ok {
|
||||||
|
seg.Style.Alignment = fyne.TextAlignCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
a := container.NewVScroll(ConfigList[id].CreatePanel())
|
||||||
|
content.Objects = []fyne.CanvasObject{
|
||||||
|
container.NewBorder(container.NewVBox(desc, widget.NewSeparator()), nil, nil, nil,
|
||||||
|
a),
|
||||||
|
}
|
||||||
|
|
||||||
|
content.Refresh()
|
||||||
|
}
|
||||||
|
return container.NewBorder(
|
||||||
|
nil, nil,
|
||||||
|
container.NewHBox(entryList, widget.NewSeparator()), nil,
|
||||||
|
content)
|
||||||
|
}
|
||||||
14
gui/gui.go
14
gui/gui.go
@@ -12,8 +12,15 @@ import (
|
|||||||
|
|
||||||
const MODULE_GUI = "GUI"
|
const MODULE_GUI = "GUI"
|
||||||
|
|
||||||
|
type ConfigLayout interface {
|
||||||
|
Title() string
|
||||||
|
Description() string
|
||||||
|
CreatePanel() fyne.CanvasObject
|
||||||
|
}
|
||||||
|
|
||||||
var App fyne.App
|
var App fyne.App
|
||||||
var MainWindow fyne.Window
|
var MainWindow fyne.Window
|
||||||
|
var ConfigList = []ConfigLayout{}
|
||||||
|
|
||||||
func l() *logrus.Entry {
|
func l() *logrus.Entry {
|
||||||
return logger.Logger.WithField("Module", MODULE_GUI)
|
return logger.Logger.WithField("Module", MODULE_GUI)
|
||||||
@@ -37,6 +44,9 @@ func Initialize() {
|
|||||||
container.NewTabItem("Playlist",
|
container.NewTabItem("Playlist",
|
||||||
newPaddedBoarder(nil, nil, createPlaylists(), nil, createPlaylistMedias()),
|
newPaddedBoarder(nil, nil, createPlaylists(), nil, createPlaylistMedias()),
|
||||||
),
|
),
|
||||||
|
container.NewTabItem("Config",
|
||||||
|
newPaddedBoarder(nil, nil, nil, nil, createConfigLayout()),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
tabs.SetTabLocation(container.TabLocationTop)
|
tabs.SetTabLocation(container.TabLocationTop)
|
||||||
@@ -46,3 +56,7 @@ func Initialize() {
|
|||||||
MainWindow.Resize(fyne.NewSize(960, 480))
|
MainWindow.Resize(fyne.NewSize(960, 480))
|
||||||
//MainWindow.SetFixedSize(true)
|
//MainWindow.SetFixedSize(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func AddConfigLayout(cfgs ...ConfigLayout) {
|
||||||
|
ConfigList = append(ConfigList, cfgs...)
|
||||||
|
}
|
||||||
|
|||||||
@@ -31,3 +31,46 @@ func createAsyncButton(btn *widget.Button, tapped func()) *widget.Button {
|
|||||||
btn.OnTapped = createAsyncOnTapped(btn, tapped)
|
btn.OnTapped = createAsyncOnTapped(btn, tapped)
|
||||||
return btn
|
return btn
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ContextMenuButton struct {
|
||||||
|
widget.Button
|
||||||
|
menu *fyne.Menu
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *ContextMenuButton) Tapped(e *fyne.PointEvent) {
|
||||||
|
widget.ShowPopUpMenuAtPosition(b.menu, fyne.CurrentApp().Driver().CanvasForObject(b), e.AbsolutePosition)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newContextMenuButton(label string, menu *fyne.Menu) *ContextMenuButton {
|
||||||
|
b := &ContextMenuButton{menu: menu}
|
||||||
|
b.Text = label
|
||||||
|
|
||||||
|
b.ExtendBaseWidget(b)
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
type FixedSplitContainer struct {
|
||||||
|
*container.Split
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FixedSplitContainer) Dragged(event *fyne.DragEvent) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FixedSplitContainer) DragEnd() {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
func newFixedSplitContainer(horizontal bool, leading, trailing fyne.CanvasObject) *FixedSplitContainer {
|
||||||
|
s := &container.Split{
|
||||||
|
Offset: 0.5, // Sensible default, can be overridden with SetOffset
|
||||||
|
Horizontal: horizontal,
|
||||||
|
Leading: leading,
|
||||||
|
Trailing: trailing,
|
||||||
|
}
|
||||||
|
fs := &FixedSplitContainer{
|
||||||
|
s,
|
||||||
|
}
|
||||||
|
fs.Split.BaseWidget.ExtendBaseWidget(s)
|
||||||
|
return fs
|
||||||
|
}
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ func createPlayController() fyne.CanvasObject {
|
|||||||
buttonsBox := container.NewCenter(
|
buttonsBox := container.NewCenter(
|
||||||
container.NewHBox(PlayController.ButtonPrev, PlayController.ButtonSwitch, PlayController.ButtonNext))
|
container.NewHBox(PlayController.ButtonPrev, PlayController.ButtonSwitch, PlayController.ButtonNext))
|
||||||
|
|
||||||
PlayController.Progress = widget.NewSlider(0, 10000)
|
PlayController.Progress = widget.NewSlider(0, 1000)
|
||||||
PlayController.CurrentTime = widget.NewLabel("0:00")
|
PlayController.CurrentTime = widget.NewLabel("0:00")
|
||||||
PlayController.TotalTime = widget.NewLabel("0:00")
|
PlayController.TotalTime = widget.NewLabel("0:00")
|
||||||
progressItem := container.NewBorder(nil, nil, PlayController.CurrentTime, PlayController.TotalTime, PlayController.Progress)
|
progressItem := container.NewBorder(nil, nil, PlayController.CurrentTime, PlayController.TotalTime, PlayController.Progress)
|
||||||
@@ -113,10 +113,10 @@ func registerPlayControllerHandler() {
|
|||||||
|
|
||||||
if controller.MainPlayer.ObserveProperty("percent-pos", func(property *mpv.EventProperty) {
|
if controller.MainPlayer.ObserveProperty("percent-pos", func(property *mpv.EventProperty) {
|
||||||
if property.Data == nil {
|
if property.Data == nil {
|
||||||
PlayController.Progress.SetValue(0)
|
PlayController.Progress.Value = 0
|
||||||
return
|
} else {
|
||||||
|
PlayController.Progress.Value = property.Data.(mpv.Node).Value.(float64) * 10
|
||||||
}
|
}
|
||||||
PlayController.Progress.Value = property.Data.(mpv.Node).Value.(float64) * 100
|
|
||||||
PlayController.Progress.Refresh()
|
PlayController.Progress.Refresh()
|
||||||
}) != nil {
|
}) != nil {
|
||||||
l().Error("fail to register handler for progress bar with property percent-pos")
|
l().Error("fail to register handler for progress bar with property percent-pos")
|
||||||
@@ -134,14 +134,14 @@ func registerPlayControllerHandler() {
|
|||||||
//PlayController.Username.SetText("Username")
|
//PlayController.Username.SetText("Username")
|
||||||
//PlayController.SetDefaultCover()
|
//PlayController.SetDefaultCover()
|
||||||
} else {
|
} else {
|
||||||
PlayController.Progress.Max = 10000
|
PlayController.Progress.Max = 1000
|
||||||
}
|
}
|
||||||
}) != nil {
|
}) != nil {
|
||||||
l().Error("fail to register handler for progress bar with property idle-active")
|
l().Error("fail to register handler for progress bar with property idle-active")
|
||||||
}
|
}
|
||||||
|
|
||||||
PlayController.Progress.OnChanged = func(f float64) {
|
PlayController.Progress.OnChanged = func(f float64) {
|
||||||
controller.Seek(f/100, false)
|
controller.Seek(f/10, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
if controller.MainPlayer.ObserveProperty("time-pos", func(property *mpv.EventProperty) {
|
if controller.MainPlayer.ObserveProperty("time-pos", func(property *mpv.EventProperty) {
|
||||||
|
|||||||
@@ -7,9 +7,36 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"fyne.io/fyne/v2"
|
"fyne.io/fyne/v2"
|
||||||
"fyne.io/fyne/v2/container"
|
"fyne.io/fyne/v2/container"
|
||||||
|
"fyne.io/fyne/v2/theme"
|
||||||
"fyne.io/fyne/v2/widget"
|
"fyne.io/fyne/v2/widget"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type playlistOperationButton struct {
|
||||||
|
widget.Button
|
||||||
|
Index int
|
||||||
|
menu *fyne.Menu
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *playlistOperationButton) Tapped(e *fyne.PointEvent) {
|
||||||
|
widget.ShowPopUpMenuAtPosition(b.menu, fyne.CurrentApp().Driver().CanvasForObject(b), e.AbsolutePosition)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newPlaylistOperationButton() *playlistOperationButton {
|
||||||
|
b := &playlistOperationButton{Index: 0}
|
||||||
|
deleteItem := fyne.NewMenuItem("Delete", func() {
|
||||||
|
fmt.Println("delete", b.Index)
|
||||||
|
})
|
||||||
|
topItem := fyne.NewMenuItem("Top", func() {
|
||||||
|
controller.UserPlaylist.Move(b.Index, 0)
|
||||||
|
})
|
||||||
|
m := fyne.NewMenu("", deleteItem, topItem)
|
||||||
|
b.menu = m
|
||||||
|
b.Text = ""
|
||||||
|
b.Icon = theme.MoreHorizontalIcon()
|
||||||
|
b.ExtendBaseWidget(b)
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
type PlaylistContainer struct {
|
type PlaylistContainer struct {
|
||||||
Playlist *player.Playlist
|
Playlist *player.Playlist
|
||||||
List *widget.List
|
List *widget.List
|
||||||
@@ -22,28 +49,31 @@ func createPlaylist() fyne.CanvasObject {
|
|||||||
UserPlaylist.List = widget.NewList(
|
UserPlaylist.List = widget.NewList(
|
||||||
func() int {
|
func() int {
|
||||||
//debug.PrintStack()
|
//debug.PrintStack()
|
||||||
// todo: @4
|
//todo: @4
|
||||||
return UserPlaylist.Playlist.Size()
|
return UserPlaylist.Playlist.Size()
|
||||||
},
|
},
|
||||||
func() fyne.CanvasObject {
|
func() fyne.CanvasObject {
|
||||||
return container.NewBorder(nil, nil, widget.NewLabel("index"), widget.NewLabel("user"),
|
return container.NewBorder(nil, nil, widget.NewLabel("index"), newPlaylistOperationButton(),
|
||||||
container.NewGridWithColumns(2,
|
container.NewGridWithColumns(3,
|
||||||
newLabelWithWrapping("title", fyne.TextTruncate),
|
newLabelWithWrapping("title", fyne.TextTruncate),
|
||||||
newLabelWithWrapping("artist", fyne.TextTruncate)))
|
newLabelWithWrapping("artist", fyne.TextTruncate),
|
||||||
|
newLabelWithWrapping("user", fyne.TextTruncate)))
|
||||||
},
|
},
|
||||||
func(id widget.ListItemID, object fyne.CanvasObject) {
|
func(id widget.ListItemID, object fyne.CanvasObject) {
|
||||||
object.(*fyne.Container).Objects[0].(*fyne.Container).Objects[0].(*widget.Label).SetText(
|
object.(*fyne.Container).Objects[0].(*fyne.Container).Objects[0].(*widget.Label).SetText(
|
||||||
UserPlaylist.Playlist.Playlist[id].Title)
|
UserPlaylist.Playlist.Playlist[id].Title)
|
||||||
object.(*fyne.Container).Objects[0].(*fyne.Container).Objects[1].(*widget.Label).SetText(
|
object.(*fyne.Container).Objects[0].(*fyne.Container).Objects[1].(*widget.Label).SetText(
|
||||||
UserPlaylist.Playlist.Playlist[id].Artist)
|
UserPlaylist.Playlist.Playlist[id].Artist)
|
||||||
|
object.(*fyne.Container).Objects[0].(*fyne.Container).Objects[2].(*widget.Label).SetText(
|
||||||
|
UserPlaylist.Playlist.Playlist[id].ToUser().Name)
|
||||||
object.(*fyne.Container).Objects[1].(*widget.Label).SetText(fmt.Sprintf("%d", id))
|
object.(*fyne.Container).Objects[1].(*widget.Label).SetText(fmt.Sprintf("%d", id))
|
||||||
object.(*fyne.Container).Objects[2].(*widget.Label).SetText(UserPlaylist.Playlist.Playlist[id].ToUser().Name)
|
object.(*fyne.Container).Objects[2].(*playlistOperationButton).Index = id
|
||||||
})
|
})
|
||||||
registerPlaylistHandler()
|
registerPlaylistHandler()
|
||||||
return container.NewBorder(
|
return container.NewBorder(
|
||||||
container.NewBorder(nil, nil,
|
container.NewBorder(nil, nil,
|
||||||
widget.NewLabel("#"), widget.NewLabel("User"),
|
widget.NewLabel("#"), widget.NewLabel("OPs"),
|
||||||
container.NewGridWithColumns(2, widget.NewLabel("Title"), widget.NewLabel("Artist"))),
|
container.NewGridWithColumns(3, widget.NewLabel("Title"), widget.NewLabel("Artist"), widget.NewLabel("User"))),
|
||||||
widget.NewSeparator(),
|
widget.NewSeparator(),
|
||||||
nil, nil,
|
nil, nil,
|
||||||
UserPlaylist.List,
|
UserPlaylist.List,
|
||||||
@@ -52,6 +82,9 @@ func createPlaylist() fyne.CanvasObject {
|
|||||||
|
|
||||||
func registerPlaylistHandler() {
|
func registerPlaylistHandler() {
|
||||||
UserPlaylist.Playlist.Handler.RegisterA(player.EventPlaylistUpdate, "gui.playlist.update", func(event *event.Event) {
|
UserPlaylist.Playlist.Handler.RegisterA(player.EventPlaylistUpdate, "gui.playlist.update", func(event *event.Event) {
|
||||||
|
// @6 Read lock Playlist when updating free after updating.
|
||||||
|
UserPlaylist.Playlist.Lock.RLock()
|
||||||
UserPlaylist.List.Refresh()
|
UserPlaylist.List.Refresh()
|
||||||
|
UserPlaylist.Playlist.Lock.RUnlock()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ func createRoomController() fyne.CanvasObject {
|
|||||||
controller.SetDanmuClient(RoomController.Input.Text)
|
controller.SetDanmuClient(RoomController.Input.Text)
|
||||||
if controller.LiveClient == nil {
|
if controller.LiveClient == nil {
|
||||||
RoomController.Status.SetText("Set Failed")
|
RoomController.Status.SetText("Set Failed")
|
||||||
|
RoomController.ConnectBtn.Enable()
|
||||||
RoomController.Status.Refresh()
|
RoomController.Status.Refresh()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,12 @@ type PlaylistUpdateEvent struct {
|
|||||||
Playlist *Playlist
|
Playlist *Playlist
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newPlaylistUpdateEvent(playlist *Playlist) PlaylistUpdateEvent {
|
||||||
|
return PlaylistUpdateEvent{
|
||||||
|
Playlist: playlist,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type PlayEvent struct {
|
type PlayEvent struct {
|
||||||
Media *Media
|
Media *Media
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
player/mpv-2.dll
BIN
player/mpv-2.dll
Binary file not shown.
@@ -90,7 +90,7 @@ func (p *Player) Play(media *Media) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
p.l().Debug("mpv command load file", media)
|
p.l().Debugf("mpv command load file %s %s", media.Title, media.Url)
|
||||||
if err := p.libmpv.Command([]string{"loadfile", media.Url}); err != nil {
|
if err := p.libmpv.Command([]string{"loadfile", media.Url}); err != nil {
|
||||||
p.l().Warn("mpv load media failed", media)
|
p.l().Warn("mpv load media failed", media)
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ type Playlist struct {
|
|||||||
Playlist []*Media
|
Playlist []*Media
|
||||||
Handler *event.Handler
|
Handler *event.Handler
|
||||||
Meta interface{}
|
Meta interface{}
|
||||||
lock sync.RWMutex
|
Lock sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPlaylist(name string, config PlaylistConfig) *Playlist {
|
func NewPlaylist(name string, config PlaylistConfig) *Playlist {
|
||||||
@@ -57,33 +57,32 @@ func (p *Playlist) Pop() *Media {
|
|||||||
p.l().Warn("pop first media failed, no media left in the playlist")
|
p.l().Warn("pop first media failed, no media left in the playlist")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
p.lock.Lock()
|
p.Lock.Lock()
|
||||||
media := p.Playlist[0]
|
media := p.Playlist[0]
|
||||||
p.Playlist = p.Playlist[1:]
|
p.Playlist = p.Playlist[1:]
|
||||||
p.lock.Unlock()
|
p.Lock.Unlock()
|
||||||
defer p.Handler.CallA(EventPlaylistUpdate, PlaylistUpdateEvent{Playlist: p})
|
defer p.Handler.CallA(EventPlaylistUpdate, PlaylistUpdateEvent{Playlist: p})
|
||||||
return media
|
return media
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Playlist) Replace(medias []*Media) {
|
func (p *Playlist) Replace(medias []*Media) {
|
||||||
p.lock.Lock()
|
p.Lock.Lock()
|
||||||
p.Playlist = medias
|
p.Playlist = medias
|
||||||
p.Index = 0
|
p.Index = 0
|
||||||
p.lock.Unlock()
|
p.Lock.Unlock()
|
||||||
p.Handler.CallA(EventPlaylistUpdate, PlaylistUpdateEvent{Playlist: p})
|
p.Handler.CallA(EventPlaylistUpdate, PlaylistUpdateEvent{Playlist: p})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Playlist) Push(media *Media) {
|
func (p *Playlist) Push(media *Media) {
|
||||||
p.Insert(-1, media)
|
p.Insert(-1, media)
|
||||||
defer p.Handler.CallA(EventPlaylistUpdate, PlaylistUpdateEvent{Playlist: p})
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insert runtime in O(n) but i don't care
|
// Insert runtime in O(n) but i don't care
|
||||||
func (p *Playlist) Insert(index int, media *Media) {
|
func (p *Playlist) Insert(index int, media *Media) {
|
||||||
p.l().Infof("insert new meida to index %d", index)
|
p.l().Infof("insert new meida to index %d", index)
|
||||||
p.l().Debug("media=", *media)
|
p.l().Debugf("media= %s", media.Title)
|
||||||
e := event.Event{
|
e := event.Event{
|
||||||
Id: EventPlaylistPreInsert,
|
Id: EventPlaylistPreInsert,
|
||||||
Cancelled: false,
|
Cancelled: false,
|
||||||
@@ -98,7 +97,7 @@ func (p *Playlist) Insert(index int, media *Media) {
|
|||||||
p.l().Info("insert new media has been cancelled by handler")
|
p.l().Info("insert new media has been cancelled by handler")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
p.lock.Lock()
|
p.Lock.Lock()
|
||||||
if index > p.Size() {
|
if index > p.Size() {
|
||||||
index = p.Size()
|
index = p.Size()
|
||||||
}
|
}
|
||||||
@@ -110,7 +109,7 @@ func (p *Playlist) Insert(index int, media *Media) {
|
|||||||
p.Playlist[i] = p.Playlist[i-1]
|
p.Playlist[i] = p.Playlist[i-1]
|
||||||
}
|
}
|
||||||
p.Playlist[index] = media
|
p.Playlist[index] = media
|
||||||
p.lock.Unlock()
|
p.Lock.Unlock()
|
||||||
defer func() {
|
defer func() {
|
||||||
p.Handler.Call(&event.Event{
|
p.Handler.Call(&event.Event{
|
||||||
Id: EventPlaylistInsert,
|
Id: EventPlaylistInsert,
|
||||||
@@ -125,6 +124,52 @@ func (p *Playlist) Insert(index int, media *Media) {
|
|||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {
|
func (p *Playlist) Next() *Media {
|
||||||
p.l().Infof("get next media with random=%t", p.Config.RandomNext)
|
p.l().Infof("get next media with random=%t", p.Config.RandomNext)
|
||||||
if p.Size() == 0 {
|
if p.Size() == 0 {
|
||||||
|
|||||||
125
plugin/diange/diange.go
Normal file
125
plugin/diange/diange.go
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
package diange
|
||||||
|
|
||||||
|
import (
|
||||||
|
"AynaLivePlayer/config"
|
||||||
|
"AynaLivePlayer/controller"
|
||||||
|
"AynaLivePlayer/gui"
|
||||||
|
"AynaLivePlayer/liveclient"
|
||||||
|
"AynaLivePlayer/logger"
|
||||||
|
"fyne.io/fyne/v2"
|
||||||
|
"fyne.io/fyne/v2/container"
|
||||||
|
"fyne.io/fyne/v2/data/binding"
|
||||||
|
"fyne.io/fyne/v2/widget"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const MODULE_CMD_DIANGE = "CMD.DianGe"
|
||||||
|
|
||||||
|
func l() *logrus.Entry {
|
||||||
|
return logger.Logger.WithField("Module", MODULE_CMD_DIANGE)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Diange struct {
|
||||||
|
UserPermission bool
|
||||||
|
PrivilegePermission bool
|
||||||
|
AdminPermission bool
|
||||||
|
QueueMax int
|
||||||
|
UserCoolDown int
|
||||||
|
CustomCMD string
|
||||||
|
cooldowns map[string]int
|
||||||
|
panel fyne.CanvasObject
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDiange() *Diange {
|
||||||
|
return &Diange{
|
||||||
|
UserPermission: true,
|
||||||
|
PrivilegePermission: true,
|
||||||
|
AdminPermission: true,
|
||||||
|
QueueMax: 128,
|
||||||
|
UserCoolDown: -1,
|
||||||
|
CustomCMD: "add",
|
||||||
|
cooldowns: make(map[string]int),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Diange) Name() string {
|
||||||
|
return "Diange"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Diange) Enable() error {
|
||||||
|
config.LoadConfig(d)
|
||||||
|
controller.AddCommand(d)
|
||||||
|
gui.AddConfigLayout(d)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Diange) Match(command string) bool {
|
||||||
|
for _, c := range []string{"点歌", d.CustomCMD} {
|
||||||
|
if command == c {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Diange) Execute(command string, args []string, danmu *liveclient.DanmuMessage) {
|
||||||
|
// if queue is full, return
|
||||||
|
if controller.UserPlaylist.Size() >= d.QueueMax {
|
||||||
|
l().Info("Queue is full, ignore diange")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// if in user cool down, return
|
||||||
|
ct := int(time.Now().Unix())
|
||||||
|
if (ct - d.cooldowns[danmu.User.Uid]) <= d.UserCoolDown {
|
||||||
|
l().Infof("User %s(%s) still in cool down period, diange failed", danmu.User.Username, danmu.User.Uid)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
keyword := strings.Join(args, " ")
|
||||||
|
perm := d.UserPermission
|
||||||
|
l().Trace("user permission check: ", perm)
|
||||||
|
perm = perm || (d.PrivilegePermission && (danmu.User.Privilege > 0))
|
||||||
|
l().Trace("privilege permission check: ", perm)
|
||||||
|
perm = perm || (d.AdminPermission && (danmu.User.Admin))
|
||||||
|
l().Trace("admin permission check: ", perm)
|
||||||
|
if perm {
|
||||||
|
// reset cool down
|
||||||
|
d.cooldowns[danmu.User.Uid] = ct
|
||||||
|
controller.Add(keyword, &danmu.User)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Diange) Title() string {
|
||||||
|
return "Diange"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Diange) Description() string {
|
||||||
|
return "Basic diange configuration"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Diange) CreatePanel() fyne.CanvasObject {
|
||||||
|
if d.panel != nil {
|
||||||
|
return d.panel
|
||||||
|
}
|
||||||
|
dgPerm := container.NewHBox(
|
||||||
|
widget.NewLabel("点歌权限"),
|
||||||
|
widget.NewCheckWithData("用户", binding.BindBool(&d.UserPermission)),
|
||||||
|
widget.NewCheckWithData("舰长", binding.BindBool(&d.PrivilegePermission)),
|
||||||
|
widget.NewCheckWithData("管理员", binding.BindBool(&d.AdminPermission)),
|
||||||
|
)
|
||||||
|
dgQueue := container.NewBorder(nil, nil,
|
||||||
|
widget.NewLabel("最大点歌数"), nil,
|
||||||
|
widget.NewEntryWithData(binding.IntToString(binding.BindInt(&d.QueueMax))),
|
||||||
|
)
|
||||||
|
dgCoolDown := container.NewBorder(nil, nil,
|
||||||
|
widget.NewLabel("点歌冷却时间"), nil,
|
||||||
|
widget.NewEntryWithData(binding.IntToString(binding.BindInt(&d.UserCoolDown))),
|
||||||
|
)
|
||||||
|
dgShortCut := container.NewBorder(nil, nil,
|
||||||
|
widget.NewLabel("自定义命令 (默认的依然可用)"), nil,
|
||||||
|
widget.NewEntryWithData(binding.BindString(&d.CustomCMD)),
|
||||||
|
)
|
||||||
|
d.panel = container.NewVBox(dgPerm, dgQueue, dgCoolDown, dgShortCut)
|
||||||
|
return d.panel
|
||||||
|
}
|
||||||
101
plugin/qiege/qiege.go
Normal file
101
plugin/qiege/qiege.go
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
package qiege
|
||||||
|
|
||||||
|
import (
|
||||||
|
"AynaLivePlayer/config"
|
||||||
|
"AynaLivePlayer/controller"
|
||||||
|
"AynaLivePlayer/gui"
|
||||||
|
"AynaLivePlayer/liveclient"
|
||||||
|
"AynaLivePlayer/logger"
|
||||||
|
"fyne.io/fyne/v2"
|
||||||
|
"fyne.io/fyne/v2/container"
|
||||||
|
"fyne.io/fyne/v2/data/binding"
|
||||||
|
"fyne.io/fyne/v2/widget"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
const MODULE_CMD_QieGE = "CMD.QieGe"
|
||||||
|
|
||||||
|
func l() *logrus.Entry {
|
||||||
|
return logger.Logger.WithField("Module", MODULE_CMD_QieGE)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Qiege struct {
|
||||||
|
UserPermission bool
|
||||||
|
PrivilegePermission bool
|
||||||
|
AdminPermission bool
|
||||||
|
CustomCMD string
|
||||||
|
panel fyne.CanvasObject
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewQiege() *Qiege {
|
||||||
|
return &Qiege{
|
||||||
|
UserPermission: true,
|
||||||
|
PrivilegePermission: true,
|
||||||
|
AdminPermission: true,
|
||||||
|
CustomCMD: "skip",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Qiege) Name() string {
|
||||||
|
return "Qiege"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Qiege) Enable() error {
|
||||||
|
config.LoadConfig(d)
|
||||||
|
controller.AddCommand(d)
|
||||||
|
gui.AddConfigLayout(d)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Qiege) Match(command string) bool {
|
||||||
|
for _, c := range []string{"切歌", d.CustomCMD} {
|
||||||
|
if command == c {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Qiege) Execute(command string, args []string, danmu *liveclient.DanmuMessage) {
|
||||||
|
if d.UserPermission && (controller.CurrentMedia != nil) {
|
||||||
|
if controller.CurrentMedia.DanmuUser() != nil && controller.CurrentMedia.DanmuUser().Uid == danmu.User.Uid {
|
||||||
|
controller.PlayNext()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if d.PrivilegePermission && danmu.User.Privilege > 0 {
|
||||||
|
controller.PlayNext()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if d.AdminPermission && danmu.User.Admin {
|
||||||
|
controller.PlayNext()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Qiege) Title() string {
|
||||||
|
return "Qiege"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Qiege) Description() string {
|
||||||
|
return "Basic Qiege configuration"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Qiege) CreatePanel() fyne.CanvasObject {
|
||||||
|
if d.panel != nil {
|
||||||
|
return d.panel
|
||||||
|
}
|
||||||
|
|
||||||
|
dgPerm := container.NewHBox(
|
||||||
|
widget.NewLabel("切歌权限"),
|
||||||
|
widget.NewCheckWithData("切自己", binding.BindBool(&d.UserPermission)),
|
||||||
|
widget.NewCheckWithData("舰长", binding.BindBool(&d.PrivilegePermission)),
|
||||||
|
widget.NewCheckWithData("管理员", binding.BindBool(&d.AdminPermission)),
|
||||||
|
)
|
||||||
|
qgShortCut := container.NewBorder(nil, nil,
|
||||||
|
widget.NewLabel("自定义命令 (默认的依然可用)"), nil,
|
||||||
|
widget.NewEntryWithData(binding.BindString(&d.CustomCMD)),
|
||||||
|
)
|
||||||
|
d.panel = container.NewVBox(dgPerm, qgShortCut)
|
||||||
|
return d.panel
|
||||||
|
}
|
||||||
114
provider/kuwo.go
114
provider/kuwo.go
@@ -2,28 +2,36 @@ package provider
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"AynaLivePlayer/player"
|
"AynaLivePlayer/player"
|
||||||
"AynaLivePlayer/util"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/tidwall/gjson"
|
"github.com/tidwall/gjson"
|
||||||
"html"
|
"html"
|
||||||
"net/url"
|
"net/url"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Kuwo struct {
|
type Kuwo struct {
|
||||||
InfoApi string
|
InfoApi string
|
||||||
FileApi string
|
FileApi string
|
||||||
SearchCookie string
|
SearchCookie string
|
||||||
SearchApi string
|
SearchApi string
|
||||||
|
LyricApi string
|
||||||
|
PlaylistApi string
|
||||||
|
PlaylistRegex0 *regexp.Regexp
|
||||||
|
PlaylistRegex1 *regexp.Regexp
|
||||||
}
|
}
|
||||||
|
|
||||||
func _newKuwo() *Kuwo {
|
func _newKuwo() *Kuwo {
|
||||||
return &Kuwo{
|
return &Kuwo{
|
||||||
InfoApi: "http://www.kuwo.cn/api/www/music/musicInfo?mid=%s&httpsStatus=1",
|
InfoApi: "http://www.kuwo.cn/api/www/music/musicInfo?mid=%s&httpsStatus=1",
|
||||||
//FileApi: "http://www.kuwo.cn/api/v1/www/music/playUrl?mid=%d&type=music&httpsStatus=1",
|
//FileApi: "http://www.kuwo.cn/api/v1/www/music/playUrl?mid=%d&type=music&httpsStatus=1",
|
||||||
FileApi: "http://antiserver.kuwo.cn/anti.s?type=convert_url&format=mp3&response=url&rid=MUSIC_%s",
|
FileApi: "http://antiserver.kuwo.cn/anti.s?type=convert_url&format=mp3&response=url&rid=MUSIC_%s",
|
||||||
SearchCookie: "http://kuwo.cn/search/list?key=%s",
|
SearchCookie: "http://kuwo.cn/search/list?key=%s",
|
||||||
SearchApi: "http://www.kuwo.cn/api/www/search/searchMusicBykeyWord?key=%s&pn=%d&rn=%d",
|
SearchApi: "http://www.kuwo.cn/api/www/search/searchMusicBykeyWord?key=%s&pn=%d&rn=%d",
|
||||||
|
LyricApi: "http://m.kuwo.cn/newh5/singles/songinfoandlrc?musicId=%s",
|
||||||
|
PlaylistApi: "http://www.kuwo.cn/api/www/playlist/playListInfo?pid=%s&pn=%d&rn=%d&httpsStatus=1",
|
||||||
|
PlaylistRegex0: regexp.MustCompile("[0-9]+"),
|
||||||
|
PlaylistRegex1: regexp.MustCompile("playlist/[0-9]+"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,21 +47,38 @@ func (k *Kuwo) GetName() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (k *Kuwo) FormatPlaylistUrl(uri string) string {
|
func (k *Kuwo) FormatPlaylistUrl(uri string) string {
|
||||||
|
var id string
|
||||||
|
id = k.PlaylistRegex0.FindString(uri)
|
||||||
|
if id != "" {
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
id = k.PlaylistRegex1.FindString(uri)
|
||||||
|
if id != "" {
|
||||||
|
return id[9:]
|
||||||
|
}
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//func (k *Kuwo) _kuwoGet(url string) string {
|
||||||
|
// searchCookie, err := httpHead(fmt.Sprintf(k.SearchCookie, "any"), nil)
|
||||||
|
// if err != nil {
|
||||||
|
// return ""
|
||||||
|
// }
|
||||||
|
// kwToken, ok := util.SliceString(regexp.MustCompile("kw_token=([^;])*;").FindString(searchCookie.Header().Get("set-cookie")), 9, -1)
|
||||||
|
// if !ok {
|
||||||
|
// return ""
|
||||||
|
// }
|
||||||
|
// return httpGetString(url, map[string]string{
|
||||||
|
// "cookie": "kw_token=" + kwToken,
|
||||||
|
// "csrf": kwToken,
|
||||||
|
// "referer": "http://www.kuwo.cn/",
|
||||||
|
// })
|
||||||
|
//}
|
||||||
|
|
||||||
func (k *Kuwo) _kuwoGet(url string) string {
|
func (k *Kuwo) _kuwoGet(url string) string {
|
||||||
searchCookie, err := httpHead(fmt.Sprintf(k.SearchCookie, "any"), nil)
|
|
||||||
if err != nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
kwToken, ok := util.SliceString(regexp.MustCompile("kw_token=([^;])*;").FindString(searchCookie.Header().Get("set-cookie")), 9, -1)
|
|
||||||
if !ok {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return httpGetString(url, map[string]string{
|
return httpGetString(url, map[string]string{
|
||||||
"cookie": "kw_token=" + kwToken,
|
"cookie": "kw_token=" + "95MWTYC4FP",
|
||||||
"csrf": kwToken,
|
"csrf": "95MWTYC4FP",
|
||||||
"referer": "http://www.kuwo.cn/",
|
"referer": "http://www.kuwo.cn/",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -106,10 +131,59 @@ func (k *Kuwo) UpdateMediaUrl(media *player.Media) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (k *Kuwo) UpdateMediaLyric(media *player.Media) error {
|
func (k *Kuwo) UpdateMediaLyric(media *player.Media) error {
|
||||||
fmt.Println(k._kuwoGet("https://player.kuwo.cn/webmusic/st/getNewMuiseByRid?rid=MUSIC_22804772"))
|
result := httpGetString(fmt.Sprintf(k.LyricApi, media.Meta.(Meta).Id), nil)
|
||||||
|
if result == "" {
|
||||||
|
return ErrorExternalApi
|
||||||
|
}
|
||||||
|
lrcs := make([]string, 0)
|
||||||
|
gjson.Parse(result).Get("data.lrclist").ForEach(func(key, value gjson.Result) bool {
|
||||||
|
lrcs = append(lrcs, fmt.Sprintf("[00:%s]%s", value.Get("time").String(), value.Get("lineLyric").String()))
|
||||||
|
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
media.Lyric = strings.Join(lrcs, "\n")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *Kuwo) GetPlaylist(meta Meta) ([]*player.Media, error) {
|
func (k *Kuwo) GetPlaylist(meta Meta) ([]*player.Media, error) {
|
||||||
return nil, ErrorExternalApi
|
medias := make([]*player.Media, 0)
|
||||||
|
var resp string
|
||||||
|
var jresp gjson.Result
|
||||||
|
for i := 1; i <= 20; i++ {
|
||||||
|
resp = k._kuwoGet(fmt.Sprintf(k.PlaylistApi, meta.Id, i, 128))
|
||||||
|
if resp == "" {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
//fmt.Println(resp[:100])
|
||||||
|
jresp = gjson.Parse(resp)
|
||||||
|
//fmt.Println(jresp.Get("code").String())
|
||||||
|
if jresp.Get("code").String() != "200" {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
cnt := int(jresp.Get("data.total").Int())
|
||||||
|
//fmt.Println(cnt)
|
||||||
|
//fmt.Println(len(jresp.Get("data.musicList").Array()))
|
||||||
|
jresp.Get("data.musicList").ForEach(func(key, value gjson.Result) bool {
|
||||||
|
medias = append(
|
||||||
|
medias,
|
||||||
|
&player.Media{
|
||||||
|
Title: html.UnescapeString(value.Get("name").String()),
|
||||||
|
Artist: value.Get("artist").String(),
|
||||||
|
Cover: value.Get("pic").String(),
|
||||||
|
Album: value.Get("album").String(),
|
||||||
|
Meta: Meta{
|
||||||
|
Name: k.GetName(),
|
||||||
|
Id: value.Get("rid").String(),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
if cnt <= i*100 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(medias) == 0 {
|
||||||
|
return nil, ErrorExternalApi
|
||||||
|
}
|
||||||
|
return medias, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,3 +68,21 @@ func TestKuwo_UpdateMediaLyric(t *testing.T) {
|
|||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
fmt.Println(media.Lyric)
|
fmt.Println(media.Lyric)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestKuwo_GetPlaylist(t *testing.T) {
|
||||||
|
var api MediaProvider = KuwoAPI
|
||||||
|
playlist, err := api.GetPlaylist(Meta{
|
||||||
|
Name: api.GetName(),
|
||||||
|
//Id: "1082685104",
|
||||||
|
Id: "2959147566",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Println(len(playlist))
|
||||||
|
for _, media := range playlist {
|
||||||
|
fmt.Println(media.Title, media.Artist, media.Album)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@@ -75,35 +75,41 @@ func (n *Netease) GetPlaylist(meta Meta) ([]*player.Media, error) {
|
|||||||
if cnt == 0 {
|
if cnt == 0 {
|
||||||
return nil, ErrorExternalApi
|
return nil, ErrorExternalApi
|
||||||
}
|
}
|
||||||
|
|
||||||
ids := make([]int, len(result.Playlist.TrackIds))
|
ids := make([]int, len(result.Playlist.TrackIds))
|
||||||
for i := 0; i < cnt; i++ {
|
for i := 0; i < cnt; i++ {
|
||||||
ids[i] = result.Playlist.TrackIds[i].Id
|
ids[i] = result.Playlist.TrackIds[i].Id
|
||||||
}
|
}
|
||||||
result2, err := neteaseApi.GetSongDetail(
|
medias := make([]*player.Media, 0, cnt)
|
||||||
n.ReqData,
|
for index := 0; index < len(ids); index += 1000 {
|
||||||
ids)
|
result2, err := neteaseApi.GetSongDetail(
|
||||||
if err != nil || result.Code != 200 {
|
n.ReqData,
|
||||||
return nil, ErrorExternalApi
|
ids[index:util.IntMin(index+1000, len(ids))])
|
||||||
}
|
if err != nil || result2.Code != 200 {
|
||||||
cnt = len(result2.Songs)
|
break
|
||||||
if cnt == 0 {
|
|
||||||
return nil, ErrorExternalApi
|
|
||||||
}
|
|
||||||
medias := make([]*player.Media, cnt)
|
|
||||||
for i := 0; i < cnt; i++ {
|
|
||||||
medias[i] = &player.Media{
|
|
||||||
Title: result2.Songs[i].Name,
|
|
||||||
Artist: _neteaseGetArtistNames(result2.Songs[i]),
|
|
||||||
Cover: result2.Songs[i].Al.PicUrl,
|
|
||||||
Album: result2.Songs[i].Al.Name,
|
|
||||||
Url: "",
|
|
||||||
Header: nil,
|
|
||||||
User: nil,
|
|
||||||
Meta: Meta{
|
|
||||||
Name: n.GetName(),
|
|
||||||
Id: strconv.Itoa(result2.Songs[i].Id),
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
cnt = len(result2.Songs)
|
||||||
|
if cnt == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
for i := 0; i < cnt; i++ {
|
||||||
|
medias = append(medias, &player.Media{
|
||||||
|
Title: result2.Songs[i].Name,
|
||||||
|
Artist: _neteaseGetArtistNames(result2.Songs[i]),
|
||||||
|
Cover: result2.Songs[i].Al.PicUrl,
|
||||||
|
Album: result2.Songs[i].Al.Name,
|
||||||
|
Url: "",
|
||||||
|
Header: nil,
|
||||||
|
User: nil,
|
||||||
|
Meta: Meta{
|
||||||
|
Name: n.GetName(),
|
||||||
|
Id: strconv.Itoa(result2.Songs[i].Id),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(medias) == 0 {
|
||||||
|
return nil, ErrorExternalApi
|
||||||
}
|
}
|
||||||
return medias, nil
|
return medias, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,12 +61,14 @@ func TestNetease_GetPlaylist(t *testing.T) {
|
|||||||
var api MediaProvider = NeteaseAPI
|
var api MediaProvider = NeteaseAPI
|
||||||
playlist, err := api.GetPlaylist(Meta{
|
playlist, err := api.GetPlaylist(Meta{
|
||||||
Name: api.GetName(),
|
Name: api.GetName(),
|
||||||
Id: "2520739691",
|
//Id: "2520739691",
|
||||||
|
Id: "2382819181",
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
fmt.Println(len(playlist))
|
||||||
for _, media := range playlist {
|
for _, media := range playlist {
|
||||||
fmt.Println(media.Title, media.Artist, media.Album)
|
fmt.Println(media.Title, media.Artist, media.Album)
|
||||||
}
|
}
|
||||||
|
|||||||
13
todo.txt
13
todo.txt
@@ -2,9 +2,18 @@
|
|||||||
- went idle and insert new item race condition
|
- went idle and insert new item race condition
|
||||||
- @3 fix handler execution (maybe priority)
|
- @3 fix handler execution (maybe priority)
|
||||||
- @4 list refresh
|
- @4 list refresh
|
||||||
|
- @5 delete optimization
|
||||||
|
|
||||||
- 歌词来源
|
- 歌词来源
|
||||||
- 设置界面
|
- kuwo歌单
|
||||||
- 文本输出
|
- 文本输出
|
||||||
- web输出
|
- web输出
|
||||||
- 进入beta版本
|
- 进入beta版本
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
Finished
|
||||||
|
- 2022.6.25: 设置界面
|
||||||
|
- 2022.6.25: @6 bug, race condition, playlist size changed during playlist update.
|
||||||
|
- 2022.6.23: 用户歌单操作
|
||||||
8
util/integer.go
Normal file
8
util/integer.go
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
package util
|
||||||
|
|
||||||
|
func IntMin(x, y int) int {
|
||||||
|
if x < y {
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
return y
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user