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/gui"
|
||||
"AynaLivePlayer/logger"
|
||||
"AynaLivePlayer/plugin/diange"
|
||||
"AynaLivePlayer/plugin/qiege"
|
||||
"fmt"
|
||||
"github.com/mitchellh/panicwrap"
|
||||
"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() {
|
||||
fmt.Printf("BiliAudioBot Revive %s\n", config.VERSION)
|
||||
logger.Logger.SetLevel(logrus.DebugLevel)
|
||||
controller.Initialize()
|
||||
controller.LoadPlugins(diange.NewDiange(), qiege.NewQiege())
|
||||
defer func() {
|
||||
controller.Destroy()
|
||||
config.SaveToConfigFile(config.CONFIG_PATH)
|
||||
|
||||
@@ -1,23 +1,18 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"image/color"
|
||||
|
||||
"fyne.io/fyne/v2/app"
|
||||
"fyne.io/fyne/v2/canvas"
|
||||
"fyne.io/fyne/v2/container"
|
||||
"fyne.io/fyne/v2/layout"
|
||||
)
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
myApp := app.New()
|
||||
myWindow := myApp.NewWindow("Form Layout")
|
||||
|
||||
label1 := canvas.NewText("Label 1", color.Black)
|
||||
value1 := canvas.NewText("Value", color.Black)
|
||||
label2 := canvas.NewText("Label 2", color.Black)
|
||||
value2 := canvas.NewText("Something", color.Black)
|
||||
grid := container.New(layout.NewFormLayout(), label1, value1, label2, value2)
|
||||
myWindow.SetContent(grid)
|
||||
myWindow.ShowAndRun()
|
||||
defer func(x int) { fmt.Println(x) }(func() int { fmt.Println("build"); return 123 }())
|
||||
fmt.Println("main")
|
||||
//myApp := app.New()
|
||||
//myWindow := myApp.NewWindow("Form Layout")
|
||||
//
|
||||
//label1 := canvas.NewText("Label 1", color.Black)
|
||||
//value1 := canvas.NewText("Value", color.Black)
|
||||
//label2 := canvas.NewText("Label 2", color.Black)
|
||||
//value2 := canvas.NewText("Something", color.Black)
|
||||
//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 (
|
||||
"fmt"
|
||||
"github.com/sirupsen/logrus"
|
||||
"gopkg.in/ini.v1"
|
||||
"path"
|
||||
)
|
||||
|
||||
const VERSION = "alpha 0.4"
|
||||
const VERSION = "alpha 0.6"
|
||||
|
||||
const CONFIG_PATH = "./config.ini"
|
||||
const Assests_PATH = "./assets"
|
||||
@@ -16,91 +15,40 @@ func GetAssetPath(name string) string {
|
||||
return path.Join(Assests_PATH, name)
|
||||
}
|
||||
|
||||
type LogConfig struct {
|
||||
Path string
|
||||
Level logrus.Level
|
||||
type Config interface {
|
||||
Name() string
|
||||
}
|
||||
|
||||
var Log = &LogConfig{
|
||||
Path: "./log.txt",
|
||||
Level: logrus.InfoLevel,
|
||||
}
|
||||
var ConfigFile *ini.File
|
||||
var Configs = make([]Config, 0)
|
||||
|
||||
type LiveRoomConfig struct {
|
||||
History []string
|
||||
}
|
||||
|
||||
var LiveRoom = &LiveRoomConfig{History: []string{"9076804", "3819533"}}
|
||||
|
||||
type PlayerConfig struct {
|
||||
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 LoadConfig(cfg Config) {
|
||||
sec, err := ConfigFile.GetSection(cfg.Name())
|
||||
if err == nil {
|
||||
_ = sec.MapTo(cfg)
|
||||
}
|
||||
Configs = append(Configs, cfg)
|
||||
return
|
||||
}
|
||||
|
||||
func init() {
|
||||
cfg, err := ini.Load(CONFIG_PATH)
|
||||
var err error
|
||||
ConfigFile, err = ini.Load(CONFIG_PATH)
|
||||
if err != nil {
|
||||
fmt.Println("config not found")
|
||||
SaveToConfigFile(CONFIG_PATH)
|
||||
return
|
||||
fmt.Println("config not found, using default config")
|
||||
ConfigFile = ini.Empty()
|
||||
}
|
||||
err = cfg.Section("Log").MapTo(Log)
|
||||
if err != nil {
|
||||
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
|
||||
for _, cfg := range []Config{Log, LiveRoom, Player, Provider} {
|
||||
LoadConfig(cfg)
|
||||
}
|
||||
}
|
||||
|
||||
func SaveToConfigFile(filename string) error {
|
||||
cfg := ini.Empty()
|
||||
err := ini.ReflectFrom(cfg, &struct {
|
||||
Log *LogConfig
|
||||
LiveRoom *LiveRoomConfig
|
||||
Player *PlayerConfig
|
||||
Provider *ProviderConfig
|
||||
}{
|
||||
Log: Log,
|
||||
LiveRoom: LiveRoom,
|
||||
Player: Player,
|
||||
Provider: Provider,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
cfgFile := ini.Empty()
|
||||
for _, cfg := range Configs {
|
||||
if err := cfgFile.Section(cfg.Name()).ReflectFrom(cfg); err != nil {
|
||||
fmt.Println(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) {
|
||||
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) {
|
||||
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,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)
|
||||
}
|
||||
|
||||
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)
|
||||
UserPlaylist.Handler.RegisterA(player.EventPlaylistInsert, "controller.playnextwhenadd", handlePlaylistAdd)
|
||||
MainPlayer.ObserveProperty("time-pos", handleLyricUpdate)
|
||||
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)
|
||||
}
|
||||
|
||||
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() {
|
||||
@@ -51,6 +129,10 @@ func SetDanmuClient(roomId string) {
|
||||
Name: "controller.commandexecutor",
|
||||
Handler: danmuCommandHandler,
|
||||
})
|
||||
LiveClient.Handler().RegisterA(
|
||||
liveclient.EventMessageReceive,
|
||||
"controller.danmu.handler",
|
||||
danmuHandler)
|
||||
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 PlaylistManager []*player.Playlist
|
||||
var CurrentLyric *player.Lyric
|
||||
var CurrentMedia *player.Media
|
||||
|
||||
func Initialize() {
|
||||
|
||||
func init() {
|
||||
MainPlayer = player.NewPlayer()
|
||||
UserPlaylist = player.NewPlaylist("user", player.PlaylistConfig{RandomNext: false})
|
||||
SystemPlaylist = player.NewPlaylist("system", player.PlaylistConfig{RandomNext: config.Player.PlaylistRandom})
|
||||
PlaylistManager = make([]*player.Playlist, 0)
|
||||
CurrentLyric = player.NewLyric("")
|
||||
loadPlaylists()
|
||||
|
||||
MainPlayer.ObserveProperty("idle-active", handleMpvIdlePlayNext)
|
||||
UserPlaylist.Handler.RegisterA(player.EventPlaylistInsert, "controller.playnextwhenadd", handlePlaylistAdd)
|
||||
MainPlayer.ObserveProperty("time-pos", handleLyricUpdate)
|
||||
MainPlayer.Start()
|
||||
}
|
||||
|
||||
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 {
|
||||
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)
|
||||
|
||||
13
go.mod
13
go.mod
@@ -4,14 +4,27 @@ go 1.16
|
||||
|
||||
require (
|
||||
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/antonfisher/nested-logrus-formatter v1.3.1
|
||||
github.com/aynakeya/blivedm v0.1.3
|
||||
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/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/spf13/cast v1.3.1
|
||||
github.com/stretchr/testify v1.5.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
|
||||
)
|
||||
|
||||
|
||||
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/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.4.1 h1:GaI7EiDXDRfa8VshkTj7Fym7ha+y8/XxIgD2okUIjLw=
|
||||
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/XiaoMengXinX/Music163Api-Go v0.1.26 h1:Nybor5okI8C0jzAiRvGfpLHdDrPqUbjx5kXWIZDX6pw=
|
||||
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/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/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/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=
|
||||
@@ -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/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-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-resty/resty/v2 v2.6.0/go.mod h1:PwvJS6hvaPkjtjNg9ph+VrSD92bi5Zq73w/BIH7cC3Q=
|
||||
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/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
||||
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/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/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/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/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/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/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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
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/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/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
|
||||
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.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
|
||||
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/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
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/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/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo=
|
||||
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-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/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.1.5 h1:ouewzE6p+/VEB31YYnTbEJdi8pFqKp4P4n85vwo3DHA=
|
||||
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-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=
|
||||
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=
|
||||
|
||||
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"
|
||||
|
||||
type ConfigLayout interface {
|
||||
Title() string
|
||||
Description() string
|
||||
CreatePanel() fyne.CanvasObject
|
||||
}
|
||||
|
||||
var App fyne.App
|
||||
var MainWindow fyne.Window
|
||||
var ConfigList = []ConfigLayout{}
|
||||
|
||||
func l() *logrus.Entry {
|
||||
return logger.Logger.WithField("Module", MODULE_GUI)
|
||||
@@ -37,6 +44,9 @@ func Initialize() {
|
||||
container.NewTabItem("Playlist",
|
||||
newPaddedBoarder(nil, nil, createPlaylists(), nil, createPlaylistMedias()),
|
||||
),
|
||||
container.NewTabItem("Config",
|
||||
newPaddedBoarder(nil, nil, nil, nil, createConfigLayout()),
|
||||
),
|
||||
)
|
||||
|
||||
tabs.SetTabLocation(container.TabLocationTop)
|
||||
@@ -46,3 +56,7 @@ func Initialize() {
|
||||
MainWindow.Resize(fyne.NewSize(960, 480))
|
||||
//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)
|
||||
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(
|
||||
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.TotalTime = widget.NewLabel("0:00")
|
||||
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 property.Data == nil {
|
||||
PlayController.Progress.SetValue(0)
|
||||
return
|
||||
PlayController.Progress.Value = 0
|
||||
} else {
|
||||
PlayController.Progress.Value = property.Data.(mpv.Node).Value.(float64) * 10
|
||||
}
|
||||
PlayController.Progress.Value = property.Data.(mpv.Node).Value.(float64) * 100
|
||||
PlayController.Progress.Refresh()
|
||||
}) != nil {
|
||||
l().Error("fail to register handler for progress bar with property percent-pos")
|
||||
@@ -134,14 +134,14 @@ func registerPlayControllerHandler() {
|
||||
//PlayController.Username.SetText("Username")
|
||||
//PlayController.SetDefaultCover()
|
||||
} else {
|
||||
PlayController.Progress.Max = 10000
|
||||
PlayController.Progress.Max = 1000
|
||||
}
|
||||
}) != nil {
|
||||
l().Error("fail to register handler for progress bar with property idle-active")
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
@@ -7,9 +7,36 @@ import (
|
||||
"fmt"
|
||||
"fyne.io/fyne/v2"
|
||||
"fyne.io/fyne/v2/container"
|
||||
"fyne.io/fyne/v2/theme"
|
||||
"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 {
|
||||
Playlist *player.Playlist
|
||||
List *widget.List
|
||||
@@ -22,28 +49,31 @@ func createPlaylist() fyne.CanvasObject {
|
||||
UserPlaylist.List = widget.NewList(
|
||||
func() int {
|
||||
//debug.PrintStack()
|
||||
// todo: @4
|
||||
//todo: @4
|
||||
return UserPlaylist.Playlist.Size()
|
||||
},
|
||||
func() fyne.CanvasObject {
|
||||
return container.NewBorder(nil, nil, widget.NewLabel("index"), widget.NewLabel("user"),
|
||||
container.NewGridWithColumns(2,
|
||||
return container.NewBorder(nil, nil, widget.NewLabel("index"), newPlaylistOperationButton(),
|
||||
container.NewGridWithColumns(3,
|
||||
newLabelWithWrapping("title", fyne.TextTruncate),
|
||||
newLabelWithWrapping("artist", fyne.TextTruncate)))
|
||||
newLabelWithWrapping("artist", fyne.TextTruncate),
|
||||
newLabelWithWrapping("user", fyne.TextTruncate)))
|
||||
},
|
||||
func(id widget.ListItemID, object fyne.CanvasObject) {
|
||||
object.(*fyne.Container).Objects[0].(*fyne.Container).Objects[0].(*widget.Label).SetText(
|
||||
UserPlaylist.Playlist.Playlist[id].Title)
|
||||
object.(*fyne.Container).Objects[0].(*fyne.Container).Objects[1].(*widget.Label).SetText(
|
||||
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[2].(*widget.Label).SetText(UserPlaylist.Playlist.Playlist[id].ToUser().Name)
|
||||
object.(*fyne.Container).Objects[2].(*playlistOperationButton).Index = id
|
||||
})
|
||||
registerPlaylistHandler()
|
||||
return container.NewBorder(
|
||||
container.NewBorder(nil, nil,
|
||||
widget.NewLabel("#"), widget.NewLabel("User"),
|
||||
container.NewGridWithColumns(2, widget.NewLabel("Title"), widget.NewLabel("Artist"))),
|
||||
widget.NewLabel("#"), widget.NewLabel("OPs"),
|
||||
container.NewGridWithColumns(3, widget.NewLabel("Title"), widget.NewLabel("Artist"), widget.NewLabel("User"))),
|
||||
widget.NewSeparator(),
|
||||
nil, nil,
|
||||
UserPlaylist.List,
|
||||
@@ -52,6 +82,9 @@ func createPlaylist() fyne.CanvasObject {
|
||||
|
||||
func registerPlaylistHandler() {
|
||||
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.Playlist.Lock.RUnlock()
|
||||
})
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ func createRoomController() fyne.CanvasObject {
|
||||
controller.SetDanmuClient(RoomController.Input.Text)
|
||||
if controller.LiveClient == nil {
|
||||
RoomController.Status.SetText("Set Failed")
|
||||
RoomController.ConnectBtn.Enable()
|
||||
RoomController.Status.Refresh()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -23,6 +23,12 @@ type PlaylistUpdateEvent struct {
|
||||
Playlist *Playlist
|
||||
}
|
||||
|
||||
func newPlaylistUpdateEvent(playlist *Playlist) PlaylistUpdateEvent {
|
||||
return PlaylistUpdateEvent{
|
||||
Playlist: playlist,
|
||||
}
|
||||
}
|
||||
|
||||
type PlayEvent struct {
|
||||
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
|
||||
}
|
||||
}
|
||||
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 {
|
||||
p.l().Warn("mpv load media failed", media)
|
||||
return err
|
||||
|
||||
@@ -26,7 +26,7 @@ type Playlist struct {
|
||||
Playlist []*Media
|
||||
Handler *event.Handler
|
||||
Meta interface{}
|
||||
lock sync.RWMutex
|
||||
Lock sync.RWMutex
|
||||
}
|
||||
|
||||
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")
|
||||
return nil
|
||||
}
|
||||
p.lock.Lock()
|
||||
p.Lock.Lock()
|
||||
media := p.Playlist[0]
|
||||
p.Playlist = p.Playlist[1:]
|
||||
p.lock.Unlock()
|
||||
p.Lock.Unlock()
|
||||
defer p.Handler.CallA(EventPlaylistUpdate, PlaylistUpdateEvent{Playlist: p})
|
||||
return media
|
||||
}
|
||||
|
||||
func (p *Playlist) Replace(medias []*Media) {
|
||||
p.lock.Lock()
|
||||
p.Lock.Lock()
|
||||
p.Playlist = medias
|
||||
p.Index = 0
|
||||
p.lock.Unlock()
|
||||
p.Lock.Unlock()
|
||||
p.Handler.CallA(EventPlaylistUpdate, PlaylistUpdateEvent{Playlist: p})
|
||||
return
|
||||
}
|
||||
|
||||
func (p *Playlist) Push(media *Media) {
|
||||
p.Insert(-1, media)
|
||||
defer p.Handler.CallA(EventPlaylistUpdate, PlaylistUpdateEvent{Playlist: p})
|
||||
return
|
||||
}
|
||||
|
||||
// Insert runtime in O(n) but i don't care
|
||||
func (p *Playlist) Insert(index int, media *Media) {
|
||||
p.l().Infof("insert new meida to index %d", index)
|
||||
p.l().Debug("media=", *media)
|
||||
p.l().Debugf("media= %s", media.Title)
|
||||
e := event.Event{
|
||||
Id: EventPlaylistPreInsert,
|
||||
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")
|
||||
return
|
||||
}
|
||||
p.lock.Lock()
|
||||
p.Lock.Lock()
|
||||
if 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[index] = media
|
||||
p.lock.Unlock()
|
||||
p.Lock.Unlock()
|
||||
defer func() {
|
||||
p.Handler.Call(&event.Event{
|
||||
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 {
|
||||
p.l().Infof("get next media with random=%t", p.Config.RandomNext)
|
||||
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
|
||||
}
|
||||
@@ -2,12 +2,12 @@ package provider
|
||||
|
||||
import (
|
||||
"AynaLivePlayer/player"
|
||||
"AynaLivePlayer/util"
|
||||
"fmt"
|
||||
"github.com/tidwall/gjson"
|
||||
"html"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Kuwo struct {
|
||||
@@ -15,6 +15,10 @@ type Kuwo struct {
|
||||
FileApi string
|
||||
SearchCookie string
|
||||
SearchApi string
|
||||
LyricApi string
|
||||
PlaylistApi string
|
||||
PlaylistRegex0 *regexp.Regexp
|
||||
PlaylistRegex1 *regexp.Regexp
|
||||
}
|
||||
|
||||
func _newKuwo() *Kuwo {
|
||||
@@ -24,6 +28,10 @@ func _newKuwo() *Kuwo {
|
||||
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",
|
||||
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 {
|
||||
var id string
|
||||
id = k.PlaylistRegex0.FindString(uri)
|
||||
if id != "" {
|
||||
return id
|
||||
}
|
||||
id = k.PlaylistRegex1.FindString(uri)
|
||||
if id != "" {
|
||||
return id[9:]
|
||||
}
|
||||
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 {
|
||||
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,
|
||||
"cookie": "kw_token=" + "95MWTYC4FP",
|
||||
"csrf": "95MWTYC4FP",
|
||||
"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 {
|
||||
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
|
||||
}
|
||||
|
||||
func (k *Kuwo) GetPlaylist(meta Meta) ([]*player.Media, error) {
|
||||
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(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,23 +75,25 @@ func (n *Netease) GetPlaylist(meta Meta) ([]*player.Media, error) {
|
||||
if cnt == 0 {
|
||||
return nil, ErrorExternalApi
|
||||
}
|
||||
|
||||
ids := make([]int, len(result.Playlist.TrackIds))
|
||||
for i := 0; i < cnt; i++ {
|
||||
ids[i] = result.Playlist.TrackIds[i].Id
|
||||
}
|
||||
medias := make([]*player.Media, 0, cnt)
|
||||
for index := 0; index < len(ids); index += 1000 {
|
||||
result2, err := neteaseApi.GetSongDetail(
|
||||
n.ReqData,
|
||||
ids)
|
||||
if err != nil || result.Code != 200 {
|
||||
return nil, ErrorExternalApi
|
||||
ids[index:util.IntMin(index+1000, len(ids))])
|
||||
if err != nil || result2.Code != 200 {
|
||||
break
|
||||
}
|
||||
cnt = len(result2.Songs)
|
||||
if cnt == 0 {
|
||||
return nil, ErrorExternalApi
|
||||
break
|
||||
}
|
||||
medias := make([]*player.Media, cnt)
|
||||
for i := 0; i < cnt; i++ {
|
||||
medias[i] = &player.Media{
|
||||
medias = append(medias, &player.Media{
|
||||
Title: result2.Songs[i].Name,
|
||||
Artist: _neteaseGetArtistNames(result2.Songs[i]),
|
||||
Cover: result2.Songs[i].Al.PicUrl,
|
||||
@@ -103,8 +105,12 @@ func (n *Netease) GetPlaylist(meta Meta) ([]*player.Media, error) {
|
||||
Name: n.GetName(),
|
||||
Id: strconv.Itoa(result2.Songs[i].Id),
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
if len(medias) == 0 {
|
||||
return nil, ErrorExternalApi
|
||||
}
|
||||
return medias, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -61,12 +61,14 @@ func TestNetease_GetPlaylist(t *testing.T) {
|
||||
var api MediaProvider = NeteaseAPI
|
||||
playlist, err := api.GetPlaylist(Meta{
|
||||
Name: api.GetName(),
|
||||
Id: "2520739691",
|
||||
//Id: "2520739691",
|
||||
Id: "2382819181",
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
fmt.Println(len(playlist))
|
||||
for _, media := range playlist {
|
||||
fmt.Println(media.Title, media.Artist, media.Album)
|
||||
}
|
||||
|
||||
11
todo.txt
11
todo.txt
@@ -2,9 +2,18 @@
|
||||
- went idle and insert new item race condition
|
||||
- @3 fix handler execution (maybe priority)
|
||||
- @4 list refresh
|
||||
- @5 delete optimization
|
||||
|
||||
- 歌词来源
|
||||
- 设置界面
|
||||
- kuwo歌单
|
||||
- 文本输出
|
||||
- web输出
|
||||
- 进入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