mirror of
https://github.com/FloatTech/ZeroBot-Plugin.git
synced 2025-12-19 22:00:11 +08:00
取消多个API依赖以提高稳定性,优化用户本地/API歌单可读性和自定义性 (#315)
This commit is contained in:
parent
9182d214af
commit
4a1d4644ed
32
README.md
32
README.md
@ -581,22 +581,32 @@ print("run[CQ:image,file="+j["img"]+"]")
|
|||||||
`import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/guessmusic"`
|
`import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/guessmusic"`
|
||||||
|
|
||||||
- 猜歌插件(该插件依赖ffmpeg)
|
- 猜歌插件(该插件依赖ffmpeg)
|
||||||
|
|
||||||
- [x] 个人猜歌
|
|
||||||
|
|
||||||
- [x] 团队猜歌
|
|
||||||
|
|
||||||
- [x] 设置猜歌缓存歌库路径 [绝对路径]
|
- [x] 设置猜歌缓存歌库路径 [绝对路径]
|
||||||
|
|
||||||
- [x] 设置猜歌本地 [true/false]
|
|
||||||
|
|
||||||
- [x] 设置猜歌Api [true/false]
|
|
||||||
|
|
||||||
- 注:默认歌库为网易云热歌榜
|
- [x] 设置猜歌[本地/Api] [true/false]
|
||||||
|
|
||||||
- 1.可在后面添加“-动漫”进行动漫歌猜歌(这个只能猜歌名和歌手)
|
- [x] 登录网易云
|
||||||
|
|
||||||
- 2.可在后面添加“-动漫2”进行动漫歌猜歌(这个可以猜番名,但歌手经常“未知”)
|
- 注:不登陆也能用,API有几率返回400
|
||||||
|
|
||||||
|
- [x] 添加歌单 [网易云歌单ID] [歌单名称]
|
||||||
|
|
||||||
|
- 注:[歌单名称]可为空,默认原标题
|
||||||
|
|
||||||
|
- [x] 删除歌单 [网易云歌单ID/API歌单名称]
|
||||||
|
|
||||||
|
- [x] 获取歌单列表
|
||||||
|
|
||||||
|
- [x] [网易云歌单ID/API歌单名称]歌单信息
|
||||||
|
|
||||||
|
- [x] [个人/团队]猜歌
|
||||||
|
|
||||||
|
- 注:默认歌库为网易云ACG动画榜
|
||||||
|
|
||||||
|
- 可在后面添加[-歌单名称]进行指定歌单猜歌
|
||||||
|
|
||||||
|
- 歌单的歌曲命名规则为:歌名 - 歌手 - 其他(歌曲出处之类)
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
<details>
|
<details>
|
||||||
|
|||||||
@ -15,6 +15,9 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
"github.com/wdvxdr1123/ZeroBot/utils/helper"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
zero "github.com/wdvxdr1123/ZeroBot"
|
zero "github.com/wdvxdr1123/ZeroBot"
|
||||||
"github.com/wdvxdr1123/ZeroBot/message"
|
"github.com/wdvxdr1123/ZeroBot/message"
|
||||||
@ -25,6 +28,10 @@ import (
|
|||||||
"github.com/FloatTech/zbputils/file"
|
"github.com/FloatTech/zbputils/file"
|
||||||
"github.com/FloatTech/zbputils/web"
|
"github.com/FloatTech/zbputils/web"
|
||||||
"github.com/wdvxdr1123/ZeroBot/extension/single"
|
"github.com/wdvxdr1123/ZeroBot/extension/single"
|
||||||
|
|
||||||
|
// 图片输出
|
||||||
|
|
||||||
|
"github.com/FloatTech/zbputils/img/text"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -32,26 +39,31 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
cuttime = [...]string{"00:00:05", "00:00:30", "00:01:00"} // 音乐切割时间点,可自行调节时间(时:分:秒)
|
catlist = make(map[string]int64, 100)
|
||||||
cfg = config{ // 默认 config
|
filelist []string
|
||||||
MusicPath: file.BOTPATH + "/data/guessmusic/music/", // 绝对路径,歌库根目录,通过指令进行更改
|
cuttime = [...]string{"00:00:05", "00:00:30", "00:01:00"} // 音乐切割时间点,可自行调节时间(时:分:秒)
|
||||||
Local: true, // 是否使用本地音乐库
|
cfg config
|
||||||
API: true, // 是否使用 Api
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() { // 插件主体
|
func init() { // 插件主体
|
||||||
engine := control.Register("guessmusic", &ctrl.Options[*zero.Ctx]{
|
engine := control.Register("guessmusic", &ctrl.Options[*zero.Ctx]{
|
||||||
DisableOnDefault: false,
|
DisableOnDefault: false,
|
||||||
Help: "猜歌插件(该插件依赖ffmpeg)\n" +
|
Help: "猜歌插件(该插件依赖ffmpeg)\n" +
|
||||||
"- 个人猜歌\n" +
|
"------bot主人指令------\n" +
|
||||||
"- 团队猜歌\n" +
|
|
||||||
"- 设置猜歌缓存歌库路径 [绝对路径]\n" +
|
"- 设置猜歌缓存歌库路径 [绝对路径]\n" +
|
||||||
"- 设置猜歌本地 [true/false]\n" +
|
"- 设置猜歌[本地/Api] [true/false]\n" +
|
||||||
"- 设置猜歌Api [true/false]\n" +
|
"- 登录网易云\n" +
|
||||||
"注:默认歌库为网易云热歌榜\n- 本地歌榜歌库歌曲命名规格“歌名 - 歌手”\n" +
|
"- 添加歌单 [网易云歌单ID] [歌单名称]\n" +
|
||||||
"1.可在后面添加“-动漫”进行动漫歌猜歌\n- 这个只能猜歌名和歌手\n- 本地动漫歌库歌曲命名规格“歌名 - 歌手”\n" +
|
"- 删除歌单 [网易云歌单ID/API歌单名称]\n" +
|
||||||
"2.可在后面添加“-动漫2”进行动漫歌猜歌\n- 这个可以猜番名,但歌手经常“未知”\n- 本地动漫2歌库歌曲命名规格“歌名 - 歌手 - 番名”",
|
"注:\n1.不登陆也能用,API有几率返回400\n" +
|
||||||
|
"2.[歌单名称]可为空,默认原标题\n" +
|
||||||
|
"------公 用 指 令------\n" +
|
||||||
|
"- 获取歌单列表\n" +
|
||||||
|
"- [网易云歌单ID/API歌单名称]歌单信息\n" +
|
||||||
|
"- [个人/团队]猜歌\n" +
|
||||||
|
"注:默认歌库为网易云ACG动画榜\n" +
|
||||||
|
"可在后面添加[-歌单名称]进行指定歌单猜歌\n" +
|
||||||
|
"歌单的歌曲命名规则为:\n歌名 - 歌手 - 其他(歌曲出处之类)",
|
||||||
PrivateDataFolder: "guessmusic",
|
PrivateDataFolder: "guessmusic",
|
||||||
}).ApplySingle(single.New(
|
}).ApplySingle(single.New(
|
||||||
single.WithKeyFn(func(ctx *zero.Ctx) int64 { return ctx.Event.GroupID }),
|
single.WithKeyFn(func(ctx *zero.Ctx) int64 { return ctx.Event.GroupID }),
|
||||||
@ -84,11 +96,28 @@ func init() { // 插件主体
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
var plist = []listRaw{
|
||||||
|
{
|
||||||
|
Name: "动画榜",
|
||||||
|
ID: 3001835560,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
cfg = config{ // 默认 config
|
||||||
|
MusicPath: file.BOTPATH + "/data/guessmusic/music/", // 绝对路径,歌库根目录,通过指令进行更改
|
||||||
|
Local: true, // 是否使用本地音乐库
|
||||||
|
API: true, // 是否使用 Api
|
||||||
|
Cookie: "",
|
||||||
|
Playlist: plist,
|
||||||
|
}
|
||||||
err = saveConfig(cfgFile)
|
err = saveConfig(cfgFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
err = getcatlist(cfg.MusicPath)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Infof("[guessmusic2]无法获取歌单列表,[error]:%s", err)
|
||||||
|
}
|
||||||
engine.OnRegex(`^设置猜歌(缓存歌库路径|本地|Api)\s*(.*)$`, func(ctx *zero.Ctx) bool {
|
engine.OnRegex(`^设置猜歌(缓存歌库路径|本地|Api)\s*(.*)$`, func(ctx *zero.Ctx) bool {
|
||||||
if !zero.SuperUserPermission(ctx) {
|
if !zero.SuperUserPermission(ctx) {
|
||||||
ctx.SendChain(message.Text("只有bot主人可以设置!"))
|
ctx.SendChain(message.Text("只有bot主人可以设置!"))
|
||||||
@ -132,15 +161,259 @@ func init() { // 插件主体
|
|||||||
ctx.SendChain(message.Text("ERROR:", err))
|
ctx.SendChain(message.Text("ERROR:", err))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
engine.OnRegex(`^(个人|团队)猜歌(-动漫|-动漫2)?$`, zero.OnlyGroup).SetBlock(true).Limit(ctxext.LimitByGroup).
|
engine.OnFullMatch("登录网易云", zero.SuperUserPermission, func(ctx *zero.Ctx) bool {
|
||||||
|
if !zero.OnlyPrivate(ctx) {
|
||||||
|
ctx.SendChain(message.Text("为了保护登录过程,请bot主人私聊。"))
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}).SetBlock(true).
|
||||||
Handle(func(ctx *zero.Ctx) {
|
Handle(func(ctx *zero.Ctx) {
|
||||||
mode := ctx.State["regex_matched"].([]string)[2]
|
keyURL := "https://music.cyrilstudio.top/login/qr/key"
|
||||||
gid := strconv.FormatInt(ctx.Event.GroupID, 10)
|
data, err := web.GetData(keyURL)
|
||||||
if mode == "-动漫2" {
|
if err != nil {
|
||||||
ctx.SendChain(message.Text("正在准备歌曲,请稍等\n回答“-[歌曲名称|歌手|番剧|提示|取消]”\n一共3段语音,6次机会"))
|
ctx.SendChain(message.Text("获取网易云key失败, ERROR:", err))
|
||||||
} else {
|
return
|
||||||
ctx.SendChain(message.Text("正在准备歌曲,请稍等\n回答“-[歌曲名称|歌手|提示|取消]”\n一共3段语音,6次机会"))
|
|
||||||
}
|
}
|
||||||
|
var keyInfo keyInfo
|
||||||
|
err = json.Unmarshal(data, &keyInfo)
|
||||||
|
if err != nil {
|
||||||
|
ctx.SendChain(message.Text("解析网易云key失败, ERROR:", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
qrURL := "https://music.cyrilstudio.top/login/qr/create?key=" + keyInfo.Data.Unikey + "&qrimg=1"
|
||||||
|
data, err = web.GetData(qrURL)
|
||||||
|
if err != nil {
|
||||||
|
ctx.SendChain(message.Text("获取网易云二维码失败, ERROR:", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var qrInfo qrInfo
|
||||||
|
err = json.Unmarshal(data, &qrInfo)
|
||||||
|
if err != nil {
|
||||||
|
ctx.SendChain(message.Text("解析网易云二维码失败, ERROR:", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.SendChain(message.Text("[请使用手机APP扫描二维码或者进入网页扫码登录]\n", qrInfo.Data.Qrurl), message.Image("base64://"+strings.ReplaceAll(qrInfo.Data.Qrimg, "data:image/png;base64,", "")), message.Text("二维码有效时间为6分钟"))
|
||||||
|
i := 0
|
||||||
|
for range time.NewTicker(10 * time.Second).C {
|
||||||
|
apiURL := "https://music.cyrilstudio.top/login/qr/check?key=" + url.QueryEscape(keyInfo.Data.Unikey)
|
||||||
|
referer := "https://music.cyrilstudio.top"
|
||||||
|
data, err := web.RequestDataWith(web.NewDefaultClient(), apiURL, "GET", referer, ua)
|
||||||
|
if err != nil {
|
||||||
|
ctx.SendChain(message.Text("无法获取登录状态, ERROR:", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var cookiesInfo cookyInfo
|
||||||
|
err = json.Unmarshal(data, &cookiesInfo)
|
||||||
|
if err != nil {
|
||||||
|
ctx.SendChain(message.Text("解析登录状态失败, ERROR:", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch cookiesInfo.Code {
|
||||||
|
case 803:
|
||||||
|
cfg.Cookie = cookiesInfo.Cookie
|
||||||
|
err = saveConfig(cfgFile)
|
||||||
|
if err == nil {
|
||||||
|
ctx.SendChain(message.Text("成功!"))
|
||||||
|
} else {
|
||||||
|
ctx.SendChain(message.Text("ERROR:", err))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
case 801:
|
||||||
|
i++
|
||||||
|
if i%6 == 0 { // 每1分钟才提醒一次,减少提示(380/60=6次)
|
||||||
|
ctx.SendChain(message.Text("状态:", cookiesInfo.Message))
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
case 800:
|
||||||
|
ctx.SendChain(message.Text("状态:", cookiesInfo.Message))
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
ctx.SendChain(message.Text("状态:", cookiesInfo.Message))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
engine.OnRegex(`^添加歌单\s?(\d*)(\s(.*))?$`, zero.SuperUserPermission).SetBlock(true).Limit(ctxext.LimitByGroup).
|
||||||
|
Handle(func(ctx *zero.Ctx) {
|
||||||
|
listID := ctx.State["regex_matched"].([]string)[1]
|
||||||
|
listName := ctx.State["regex_matched"].([]string)[3]
|
||||||
|
apiURL := "https://music.cyrilstudio.top/playlist/detail?id=" + listID + "&cookie=" + url.QueryEscape(cfg.Cookie)
|
||||||
|
referer := "https://music.cyrilstudio.top"
|
||||||
|
data, err := web.RequestDataWith(web.NewDefaultClient(), apiURL, "GET", referer, ua)
|
||||||
|
if err != nil {
|
||||||
|
ctx.SendChain(message.Text("无法连接歌单,[error]", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var parsed topList
|
||||||
|
err = json.Unmarshal(data, &parsed)
|
||||||
|
if err != nil {
|
||||||
|
ctx.SendChain(message.Text("无法解析歌单ID内容,[error]", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if listName == "" {
|
||||||
|
listName = parsed.Playlist.Name
|
||||||
|
}
|
||||||
|
playID, _ := strconv.ParseInt(listID, 10, 64)
|
||||||
|
catlist[listName] = playID
|
||||||
|
cfg.Playlist = append(cfg.Playlist, listRaw{
|
||||||
|
Name: listName,
|
||||||
|
ID: playID,
|
||||||
|
})
|
||||||
|
err = saveConfig(cfgFile)
|
||||||
|
if err == nil {
|
||||||
|
ctx.SendChain(message.Text("成功!"))
|
||||||
|
} else {
|
||||||
|
ctx.SendChain(message.Text("ERROR:", err))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
engine.OnRegex(`^删除歌单\s?(.*)$`, zero.SuperUserPermission).SetBlock(true).Limit(ctxext.LimitByGroup).
|
||||||
|
Handle(func(ctx *zero.Ctx) {
|
||||||
|
delList := ctx.State["regex_matched"].([]string)[1]
|
||||||
|
var playlist []listRaw
|
||||||
|
var newCatList = make(map[string]int64)
|
||||||
|
var ok = false
|
||||||
|
for name, musicID := range catlist {
|
||||||
|
if delList == name || delList == strconv.FormatInt(musicID, 10) {
|
||||||
|
ok = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
newCatList[name] = musicID
|
||||||
|
playlist = append(playlist, listRaw{
|
||||||
|
Name: name,
|
||||||
|
ID: musicID,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
ctx.SendChain(message.Text("目标歌单未找到,请确认是否正确"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
catlist = newCatList
|
||||||
|
cfg.Playlist = playlist
|
||||||
|
err = saveConfig(cfgFile)
|
||||||
|
if err == nil {
|
||||||
|
ctx.SendChain(message.Text("成功!"))
|
||||||
|
} else {
|
||||||
|
ctx.SendChain(message.Text("ERROR:", err))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
engine.OnFullMatch("获取歌单列表").SetBlock(true).Limit(ctxext.LimitByGroup).
|
||||||
|
Handle(func(ctx *zero.Ctx) {
|
||||||
|
var msg []string
|
||||||
|
// 获取网易云歌单列表
|
||||||
|
if cfg.API {
|
||||||
|
catlist = make(map[string]int64, 100)
|
||||||
|
msg = append(msg, "\n当前添加的API歌单含有以下:\n")
|
||||||
|
for i, listInfo := range cfg.Playlist {
|
||||||
|
catlist[listInfo.Name] = listInfo.ID
|
||||||
|
msg = append(msg, strconv.Itoa(i)+":"+listInfo.Name)
|
||||||
|
if i%3 == 2 {
|
||||||
|
msg = append(msg, "\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 获取本地歌单列表*/
|
||||||
|
if cfg.Local {
|
||||||
|
err = os.MkdirAll(cfg.MusicPath, 0755)
|
||||||
|
if err == nil {
|
||||||
|
files, err := ioutil.ReadDir(cfg.MusicPath)
|
||||||
|
if err == nil {
|
||||||
|
msg = append(msg, "\n当前本地歌单含有以下:\n")
|
||||||
|
i := 0
|
||||||
|
for _, name := range files {
|
||||||
|
if !name.IsDir() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
filelist[i] = strconv.Itoa(i) + ":" + name.Name()
|
||||||
|
msg = append(msg, filelist[i])
|
||||||
|
if i%3 == 2 {
|
||||||
|
msg = append(msg, "\n")
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ctx.SendChain(message.Text("[读取本地列表错误]ERROR:", err))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ctx.SendChain(message.Text("[生成文件夹错误]ERROR:", err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if msg == nil {
|
||||||
|
ctx.SendChain(message.Text("本地和API均未开启!"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
msgs, err := text.RenderToBase64(strings.Join(msg, " "), text.FontFile, 400, 20)
|
||||||
|
if err != nil {
|
||||||
|
ctx.SendChain(message.Text("生成列表图片失败,请重试"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if id := ctx.SendChain(message.Image("base64://" + helper.BytesToString(msgs))); id.ID() == 0 {
|
||||||
|
ctx.SendChain(message.Text("ERROR: 可能被风控了"))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
engine.OnSuffix("歌单信息").SetBlock(true).Limit(ctxext.LimitByGroup).
|
||||||
|
Handle(func(ctx *zero.Ctx) {
|
||||||
|
list := ctx.State["args"].(string)
|
||||||
|
var listIDStr string
|
||||||
|
for listName, listID := range catlist {
|
||||||
|
if list == listName || list == strconv.FormatInt(listID, 10) {
|
||||||
|
listIDStr = strconv.FormatInt(listID, 10)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if listIDStr == "" {
|
||||||
|
_, err := strconv.ParseInt(list, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
ctx.SendChain(message.Text("仅支持歌单ID查询"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
listIDStr = list
|
||||||
|
}
|
||||||
|
apiURL := "https://music.cyrilstudio.top/playlist/detail?id=" + listIDStr + "&cookie=" + url.QueryEscape(cfg.Cookie)
|
||||||
|
referer := "https://music.cyrilstudio.top"
|
||||||
|
data, err := web.RequestDataWith(web.NewDefaultClient(), apiURL, "GET", referer, ua)
|
||||||
|
if err != nil {
|
||||||
|
ctx.SendChain(message.Text("无法连接歌单,[error]", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var parsed topList
|
||||||
|
err = json.Unmarshal(data, &parsed)
|
||||||
|
if err != nil {
|
||||||
|
ctx.SendChain(message.Text("无法解析歌单ID内容,[error]", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.SendChain(
|
||||||
|
message.Image(parsed.Playlist.CoverImgURL),
|
||||||
|
message.Text(
|
||||||
|
"歌单名称:", parsed.Playlist.Name,
|
||||||
|
"\n歌单ID:", parsed.Playlist.ID,
|
||||||
|
"\n创建人:", parsed.Playlist.Creator.Nickname,
|
||||||
|
"\n创建时间:", time.Unix(parsed.Playlist.CreateTime/1000, 0).Format("2006-01-02"),
|
||||||
|
"\n标签:", strings.Join(parsed.Playlist.Tags, ";"),
|
||||||
|
"\n歌曲数量:", parsed.Playlist.TrackCount,
|
||||||
|
"\n歌单简介:\n", parsed.Playlist.Description,
|
||||||
|
"\n更新时间:", time.Unix(parsed.Playlist.UpdateTime/1000, 0).Format("2006-01-02"),
|
||||||
|
))
|
||||||
|
})
|
||||||
|
engine.OnRegex(`^(个人|团队)猜歌(-(.*))?$`, zero.OnlyGroup).SetBlock(true).Limit(ctxext.LimitByGroup).
|
||||||
|
Handle(func(ctx *zero.Ctx) {
|
||||||
|
mode := ctx.State["regex_matched"].([]string)[3]
|
||||||
|
if mode == "" {
|
||||||
|
mode = "动画榜"
|
||||||
|
}
|
||||||
|
_, ok := catlist[mode]
|
||||||
|
switch {
|
||||||
|
// 如果API没有开,本地也不存在这个歌单
|
||||||
|
case !cfg.API && !strings.Contains(strings.Join(filelist, " "), mode):
|
||||||
|
ctx.SendChain(message.Text("歌单名称错误,可以发送“获取歌单列表”获取歌单名称"))
|
||||||
|
return
|
||||||
|
// 如果本地没有开,网易云也不存在这个歌单
|
||||||
|
case !cfg.Local && !ok:
|
||||||
|
ctx.SendChain(message.Text("歌单名称错误,可以发送“获取歌单列表”获取歌单名称"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
gid := strconv.FormatInt(ctx.Event.GroupID, 10)
|
||||||
|
ctx.SendChain(message.Text("正在准备歌曲,请稍等\n回答“-[歌曲信息(歌名歌手等)|提示|取消]”\n一共3段语音,6次机会"))
|
||||||
// 随机抽歌
|
// 随机抽歌
|
||||||
musicName, pathOfMusic, err := musicLottery(mode, cfg.MusicPath)
|
musicName, pathOfMusic, err := musicLottery(mode, cfg.MusicPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -154,9 +427,17 @@ func init() { // 插件主体
|
|||||||
ctx.SendChain(message.Text(err))
|
ctx.SendChain(message.Text(err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
// 解析歌曲信息
|
||||||
|
musicInfo := strings.Split(musicName, " - ")
|
||||||
|
infoNum := len(musicInfo)
|
||||||
|
answerString := "歌名:" + musicInfo[0] + "\n歌手:" + musicInfo[1]
|
||||||
|
musicAlia := ""
|
||||||
|
if infoNum > 2 {
|
||||||
|
musicAlia = musicInfo[2]
|
||||||
|
answerString += "\n其他信息:\n" + strings.ReplaceAll(musicAlia, "&", "\n")
|
||||||
|
}
|
||||||
// 进行猜歌环节
|
// 进行猜歌环节
|
||||||
ctx.SendChain(message.Record("file:///" + file.BOTPATH + "/" + outputPath + "0.wav"))
|
ctx.SendChain(message.Record("file:///" + file.BOTPATH + "/" + outputPath + "0.wav"))
|
||||||
answerString := strings.Split(musicName, " - ")
|
|
||||||
var next *zero.FutureEvent
|
var next *zero.FutureEvent
|
||||||
if ctx.State["regex_matched"].([]string)[1] == "个人" {
|
if ctx.State["regex_matched"].([]string)[1] == "个人" {
|
||||||
next = zero.NewFutureEvent("message", 999, false, zero.OnlyGroup, zero.RegexRule(`^-\S{1,}`), ctx.CheckSession())
|
next = zero.NewFutureEvent("message", 999, false, zero.OnlyGroup, zero.RegexRule(`^-\S{1,}`), ctx.CheckSession())
|
||||||
@ -175,15 +456,8 @@ func init() { // 插件主体
|
|||||||
case <-tick.C:
|
case <-tick.C:
|
||||||
ctx.SendChain(message.Text("猜歌游戏,你还有15s作答时间"))
|
ctx.SendChain(message.Text("猜歌游戏,你还有15s作答时间"))
|
||||||
case <-after.C:
|
case <-after.C:
|
||||||
msg := make(message.Message, 0, 3)
|
ctx.Send(message.ReplyWithMessage(ctx.Event.MessageID,
|
||||||
msg = append(msg, message.Reply(ctx.Event.MessageID))
|
message.Text("时间超时,猜歌结束,公布答案:\n", answerString)))
|
||||||
msg = append(msg, message.Text("猜歌超时,游戏结束\n答案是:",
|
|
||||||
"\n歌名:", answerString[0],
|
|
||||||
"\n歌手:", answerString[1]))
|
|
||||||
if mode == "-动漫2" {
|
|
||||||
msg = append(msg, message.Text("\n歌曲出自:", answerString[2]))
|
|
||||||
}
|
|
||||||
ctx.Send(msg)
|
|
||||||
return
|
return
|
||||||
case <-wait.C:
|
case <-wait.C:
|
||||||
wait.Reset(40 * time.Second)
|
wait.Reset(40 * time.Second)
|
||||||
@ -207,15 +481,8 @@ func init() { // 插件主体
|
|||||||
wait.Stop()
|
wait.Stop()
|
||||||
tick.Stop()
|
tick.Stop()
|
||||||
after.Stop()
|
after.Stop()
|
||||||
msg := make(message.Message, 0, 3)
|
ctx.Send(message.ReplyWithMessage(ctx.Event.MessageID,
|
||||||
msg = append(msg, message.Reply(c.Event.MessageID))
|
message.Text("游戏已取消,猜歌答案是\n", answerString)))
|
||||||
msg = append(msg, message.Text("游戏已取消,猜歌答案是",
|
|
||||||
"\n歌名:", answerString[0],
|
|
||||||
"\n歌手:", answerString[1]))
|
|
||||||
if mode == "-动漫2" {
|
|
||||||
msg = append(msg, message.Text("\n歌曲出自:", answerString[2]))
|
|
||||||
}
|
|
||||||
ctx.Send(msg)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ctx.Send(
|
ctx.Send(
|
||||||
@ -241,53 +508,28 @@ func init() { // 插件主体
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
ctx.SendChain(message.Record("file:///" + file.BOTPATH + "/" + outputPath + strconv.Itoa(musicCount) + ".wav"))
|
ctx.SendChain(message.Record("file:///" + file.BOTPATH + "/" + outputPath + strconv.Itoa(musicCount) + ".wav"))
|
||||||
case strings.Contains(answerString[0], answer) || strings.EqualFold(answerString[0], answer):
|
case strings.Contains(musicInfo[0], answer) || strings.EqualFold(musicInfo[0], answer):
|
||||||
wait.Stop()
|
wait.Stop()
|
||||||
tick.Stop()
|
tick.Stop()
|
||||||
after.Stop()
|
after.Stop()
|
||||||
msg := make(message.Message, 0, 3)
|
ctx.Send(message.ReplyWithMessage(c.Event.MessageID,
|
||||||
msg = append(msg, message.Reply(c.Event.MessageID))
|
message.Text("太棒了,你猜对歌曲名了!答案是\n", answerString)))
|
||||||
msg = append(msg, message.Text("太棒了,你猜对歌曲名了!答案是",
|
|
||||||
"\n歌名:", answerString[0],
|
|
||||||
"\n歌手:", answerString[1]))
|
|
||||||
if mode == "-动漫2" {
|
|
||||||
msg = append(msg, message.Text("\n歌曲出自:", answerString[2]))
|
|
||||||
}
|
|
||||||
ctx.Send(msg)
|
|
||||||
return
|
return
|
||||||
case answerString[1] == "未知" && answer == "未知":
|
case strings.Contains(musicInfo[1], answer) || strings.EqualFold(musicInfo[1], answer):
|
||||||
ctx.Send(
|
|
||||||
message.ReplyWithMessage(c.Event.MessageID,
|
|
||||||
message.Text("该模式禁止回答“未知”"),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
case strings.Contains(answerString[1], answer) || strings.EqualFold(answerString[1], answer):
|
|
||||||
wait.Stop()
|
wait.Stop()
|
||||||
tick.Stop()
|
tick.Stop()
|
||||||
after.Stop()
|
after.Stop()
|
||||||
msg := make(message.Message, 0, 3)
|
ctx.Send(message.ReplyWithMessage(c.Event.MessageID,
|
||||||
msg = append(msg, message.Reply(c.Event.MessageID))
|
message.Text("太棒了,你猜对歌手名了!答案是\n", answerString)))
|
||||||
msg = append(msg, message.Text("太棒了,你猜对歌手名了!答案是",
|
return
|
||||||
"\n歌名:", answerString[0],
|
case strings.Contains(musicAlia, answer) || strings.EqualFold(musicAlia, answer):
|
||||||
"\n歌手:", answerString[1]))
|
wait.Stop()
|
||||||
if mode == "-动漫2" {
|
tick.Stop()
|
||||||
msg = append(msg, message.Text("\n歌曲出自:", answerString[2]))
|
after.Stop()
|
||||||
}
|
ctx.Send(message.ReplyWithMessage(c.Event.MessageID,
|
||||||
ctx.Send(msg)
|
message.Text("太棒了,你猜对出处了!答案是\n", answerString)))
|
||||||
return
|
return
|
||||||
default:
|
default:
|
||||||
if mode == "-动漫2" && (strings.Contains(answerString[2], answer) || strings.EqualFold(answerString[2], answer)) {
|
|
||||||
wait.Stop()
|
|
||||||
tick.Stop()
|
|
||||||
after.Stop()
|
|
||||||
ctx.Send(message.ReplyWithMessage(c.Event.MessageID,
|
|
||||||
message.Text("太棒了,你猜对番剧名了!答案是:",
|
|
||||||
"\n歌名:", answerString[0],
|
|
||||||
"\n歌手:", answerString[1],
|
|
||||||
"\n歌曲出自:", answerString[2]),
|
|
||||||
))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
musicCount++
|
musicCount++
|
||||||
switch {
|
switch {
|
||||||
case musicCount > 2 && answerCount < 6:
|
case musicCount > 2 && answerCount < 6:
|
||||||
@ -302,15 +544,8 @@ func init() { // 插件主体
|
|||||||
wait.Stop()
|
wait.Stop()
|
||||||
tick.Stop()
|
tick.Stop()
|
||||||
after.Stop()
|
after.Stop()
|
||||||
msg := make(message.Message, 0, 3)
|
ctx.Send(message.ReplyWithMessage(c.Event.MessageID,
|
||||||
msg = append(msg, message.Reply(c.Event.MessageID))
|
message.Text("次数到了,没能猜出来。答案是\n", answerString)))
|
||||||
msg = append(msg, message.Text("次数到了,你没能猜出来。\n答案是:",
|
|
||||||
"\n歌名:", answerString[0],
|
|
||||||
"\n歌手:", answerString[1]))
|
|
||||||
if mode == "-动漫2" {
|
|
||||||
msg = append(msg, message.Text("\n歌曲出自:", answerString[2]))
|
|
||||||
}
|
|
||||||
ctx.Send(msg)
|
|
||||||
return
|
return
|
||||||
default:
|
default:
|
||||||
wait.Reset(40 * time.Second)
|
wait.Reset(40 * time.Second)
|
||||||
@ -340,16 +575,30 @@ func saveConfig(cfgFile string) (err error) {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getcatlist(pathOfMusic string) error {
|
||||||
|
catlist = make(map[string]int64, 100)
|
||||||
|
for _, listInfo := range cfg.Playlist {
|
||||||
|
catlist[listInfo.Name] = listInfo.ID
|
||||||
|
}
|
||||||
|
err := os.MkdirAll(pathOfMusic, 0755)
|
||||||
|
if err != nil {
|
||||||
|
err = errors.Errorf("[生成文件夹错误]ERROR:%s", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
files, err := ioutil.ReadDir(pathOfMusic)
|
||||||
|
if err != nil {
|
||||||
|
err = errors.Errorf("[读取本地列表错误]ERROR:%s", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for i, name := range files {
|
||||||
|
filelist = append(filelist, strconv.Itoa(i)+":"+name.Name())
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// 随机抽取音乐
|
// 随机抽取音乐
|
||||||
func musicLottery(mode, musicPath string) (musicName, pathOfMusic string, err error) {
|
func musicLottery(mode, musicPath string) (musicName, pathOfMusic string, err error) {
|
||||||
switch mode {
|
pathOfMusic = musicPath + mode + "/"
|
||||||
case "-动漫":
|
|
||||||
pathOfMusic = musicPath + "动漫/"
|
|
||||||
case "-动漫2":
|
|
||||||
pathOfMusic = musicPath + "动漫2/"
|
|
||||||
default:
|
|
||||||
pathOfMusic = musicPath + "歌榜/"
|
|
||||||
}
|
|
||||||
err = os.MkdirAll(pathOfMusic, 0755)
|
err = os.MkdirAll(pathOfMusic, 0755)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = errors.Errorf("[生成文件夹错误]ERROR:%s", err)
|
err = errors.Errorf("[生成文件夹错误]ERROR:%s", err)
|
||||||
@ -360,21 +609,27 @@ func musicLottery(mode, musicPath string) (musicName, pathOfMusic string, err er
|
|||||||
err = errors.Errorf("[读取本地列表错误]ERROR:%s", err)
|
err = errors.Errorf("[读取本地列表错误]ERROR:%s", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
listID, ok := catlist[mode]
|
||||||
|
listIDstr := strconv.FormatInt(listID, 10)
|
||||||
if cfg.Local && cfg.API {
|
if cfg.Local && cfg.API {
|
||||||
switch {
|
switch {
|
||||||
case len(files) == 0:
|
case len(files) == 0:
|
||||||
|
if !ok {
|
||||||
|
// 如果歌单是本地歌单
|
||||||
|
err = errors.New("本地歌单数据为0")
|
||||||
|
return
|
||||||
|
}
|
||||||
// 如果没有任何本地就下载歌曲
|
// 如果没有任何本地就下载歌曲
|
||||||
musicName, err = getAPIMusic(mode, pathOfMusic)
|
musicName, err = getListMusic(listIDstr, pathOfMusic)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = errors.Errorf("[本地数据为0,歌曲下载错误]ERROR:%s", err)
|
err = errors.Errorf("[本地数据为0,歌曲下载错误]ERROR:%s", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
case rand.Intn(2) == 0:
|
case rand.Intn(2) == 0 || !ok:
|
||||||
// [0,1)只会取到0,rand不允许的
|
// 1/2概率抽本地或者歌单只有本地有时
|
||||||
musicName = getLocalMusic(files)
|
musicName = getLocalMusic(files)
|
||||||
default:
|
default:
|
||||||
musicName, err = getAPIMusic(mode, pathOfMusic)
|
musicName, err = getListMusic(listIDstr, pathOfMusic)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// 如果下载失败就从本地抽一个歌曲
|
// 如果下载失败就从本地抽一个歌曲
|
||||||
musicName = getLocalMusic(files)
|
musicName = getLocalMusic(files)
|
||||||
@ -391,27 +646,15 @@ func musicLottery(mode, musicPath string) (musicName, pathOfMusic string, err er
|
|||||||
musicName = getLocalMusic(files)
|
musicName = getLocalMusic(files)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if cfg.API {
|
if cfg.API && ok {
|
||||||
musicName, err = getAPIMusic(mode, pathOfMusic)
|
musicName, err = getListMusic(listIDstr, pathOfMusic)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = errors.Errorf("[获取API失败,未开启本地数据] ERROR:%s", err)
|
err = errors.Errorf("[获取API失败,未开启本地数据] ERROR:%s", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err = errors.New("[未开启API以及本地数据]")
|
err = errors.New("[请确认本地和API设置已开启或歌单存在]")
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func getAPIMusic(mode string, musicPath string) (musicName string, err error) {
|
|
||||||
switch mode {
|
|
||||||
case "-动漫":
|
|
||||||
musicName, err = getPaugramData(musicPath)
|
|
||||||
case "-动漫2":
|
|
||||||
musicName, err = getAnimeData(musicPath)
|
|
||||||
default:
|
|
||||||
musicName, err = getNetEaseData(musicPath)
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -424,28 +667,43 @@ func getLocalMusic(files []fs.FileInfo) (musicName string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 下载保罗API的歌曲
|
// 下载网易云歌单音乐
|
||||||
func getPaugramData(musicPath string) (musicName string, err error) {
|
func getListMusic(listID, pathOfMusic string) (musicName string, err error) {
|
||||||
api := "https://api.paugram.com/acgm/?list=1"
|
apiURL := "https://music.cyrilstudio.top/playlist/track/all?id=" + listID + "&cookie=" + url.QueryEscape(cfg.Cookie)
|
||||||
referer := "https://api.paugram.com/"
|
referer := "https://music.cyrilstudio.top"
|
||||||
data, err := web.RequestDataWith(web.NewDefaultClient(), api, "GET", referer, ua)
|
data, err := web.RequestDataWith(web.NewDefaultClient(), apiURL, "GET", referer, ua)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var parsed paugramData
|
var parsed topMusicInfo
|
||||||
err = json.Unmarshal(data, &parsed)
|
err = json.Unmarshal(data, &parsed)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
name := parsed.Title
|
listlen := len(parsed.Songs)
|
||||||
artistsName := parsed.Artist
|
randidx := rand.Intn(listlen)
|
||||||
musicURL := parsed.Link
|
// 将"/"符号去除,不然无法生成文件
|
||||||
if name == "" || artistsName == "" {
|
name := strings.ReplaceAll(parsed.Songs[randidx].Name, "/", "·")
|
||||||
|
musicID := parsed.Songs[randidx].ID
|
||||||
|
artistName := ""
|
||||||
|
for i, ARInfo := range parsed.Songs[randidx].Ar {
|
||||||
|
if i != 0 {
|
||||||
|
artistName += "&" + ARInfo.Name
|
||||||
|
} else {
|
||||||
|
artistName += ARInfo.Name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cource := ""
|
||||||
|
if parsed.Songs[randidx].Alia != nil {
|
||||||
|
cource = strings.Join(parsed.Songs[randidx].Alia, "&")
|
||||||
|
// 将"/"符号去除,不然无法下载
|
||||||
|
cource = strings.ReplaceAll(cource, "/", "&")
|
||||||
|
}
|
||||||
|
if name == "" || musicID == 0 {
|
||||||
err = errors.New("无法获API取歌曲信息")
|
err = errors.New("无法获API取歌曲信息")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
musicName = name + " - " + artistsName
|
musicURL := "http://music.163.com/song/media/outer/url?id=" + strconv.Itoa(musicID)
|
||||||
downMusic := musicPath + "/" + musicName + ".mp3"
|
|
||||||
response, err := http.Head(musicURL)
|
response, err := http.Head(musicURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = errors.Errorf("下载音乐失败, ERROR: %s", err)
|
err = errors.Errorf("下载音乐失败, ERROR: %s", err)
|
||||||
@ -455,109 +713,12 @@ func getPaugramData(musicPath string) (musicName string, err error) {
|
|||||||
err = errors.Errorf("下载音乐失败, Status Code: %d", response.StatusCode)
|
err = errors.Errorf("下载音乐失败, Status Code: %d", response.StatusCode)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if file.IsNotExist(downMusic) {
|
if cource != "" {
|
||||||
data, err = web.GetData(musicURL)
|
musicName = name + " - " + artistName + " - " + cource
|
||||||
if err != nil {
|
} else {
|
||||||
return
|
musicName = name + " - " + artistName
|
||||||
}
|
|
||||||
err = os.WriteFile(downMusic, data, 0666)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return
|
downMusic := pathOfMusic + musicName + ".mp3"
|
||||||
}
|
|
||||||
|
|
||||||
// 下载animeMusic API的歌曲
|
|
||||||
func getAnimeData(musicPath string) (musicName string, err error) {
|
|
||||||
api := "https://anime-music.jijidown.com/api/v2/music"
|
|
||||||
referer := "https://anime-music.jijidown.com/"
|
|
||||||
data, err := web.RequestDataWith(web.NewDefaultClient(), api, "GET", referer, ua)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
var parsed animeData
|
|
||||||
err = json.Unmarshal(data, &parsed)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
name := parsed.Res.Title
|
|
||||||
artistName := parsed.Res.Author
|
|
||||||
acgName := parsed.Res.AnimeInfo.Title
|
|
||||||
//musicURL := parsed.Res.PlayURL
|
|
||||||
if name == "" || artistName == "" {
|
|
||||||
err = errors.New("无法获API取歌曲信息")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
requestURL := "https://music.cyrilstudio.top/search?keywords=" + url.QueryEscape(name+" "+artistName) + "&limit=1"
|
|
||||||
if artistName == "未知" {
|
|
||||||
requestURL = "https://music.cyrilstudio.top/search?keywords=" + url.QueryEscape(acgName+" "+name) + "&limit=1"
|
|
||||||
}
|
|
||||||
data, err = web.GetData(requestURL)
|
|
||||||
if err != nil {
|
|
||||||
err = errors.Errorf("API歌曲查询失败, ERROR: %s", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
var autumnfish autumnfishData
|
|
||||||
err = json.Unmarshal(data, &autumnfish)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if autumnfish.Code != 200 {
|
|
||||||
err = errors.Errorf("下载音乐失败, Status Code: %d", autumnfish.Code)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
musicID := strconv.Itoa(autumnfish.Result.Songs[0].ID)
|
|
||||||
if artistName == "未知" {
|
|
||||||
artistName = strings.ReplaceAll(autumnfish.Result.Songs[0].Artists[0].Name, " - ", "-")
|
|
||||||
}
|
|
||||||
musicName = name + " - " + artistName + " - " + acgName
|
|
||||||
downMusic := musicPath + "/" + musicName + ".mp3"
|
|
||||||
musicURL := "http://music.163.com/song/media/outer/url?id=" + musicID
|
|
||||||
response, err := http.Head(musicURL)
|
|
||||||
if err != nil {
|
|
||||||
err = errors.Errorf("下载音乐失败, ERROR: %s", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if response.StatusCode != 200 {
|
|
||||||
err = errors.Errorf("下载音乐失败, Status Code: %d", response.StatusCode)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if file.IsNotExist(downMusic) {
|
|
||||||
data, err = web.GetData(musicURL)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = os.WriteFile(downMusic, data, 0666)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 下载网易云热歌榜音乐
|
|
||||||
func getNetEaseData(musicPath string) (musicName string, err error) {
|
|
||||||
api := "https://api.uomg.com/api/rand.music?sort=%E7%83%AD%E6%AD%8C%E6%A6%9C&format=json"
|
|
||||||
referer := "https://api.uomg.com/api/rand.music"
|
|
||||||
data, err := web.RequestDataWith(web.NewDefaultClient(), api, "GET", referer, ua)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
var parsed netEaseData
|
|
||||||
err = json.Unmarshal(data, &parsed)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
name := parsed.Data.Name
|
|
||||||
musicURL := parsed.Data.URL
|
|
||||||
artistsName := parsed.Data.Artistsname
|
|
||||||
if name == "" || artistsName == "" {
|
|
||||||
err = errors.New("无法获API取歌曲信息")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
musicName = name + " - " + artistsName
|
|
||||||
downMusic := musicPath + "/" + musicName + ".mp3"
|
|
||||||
if file.IsNotExist(downMusic) {
|
if file.IsNotExist(downMusic) {
|
||||||
data, err = web.GetData(musicURL)
|
data, err = web.GetData(musicURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@ -1,108 +1,406 @@
|
|||||||
package guessmusic
|
package guessmusic
|
||||||
|
|
||||||
|
type listRaw struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
ID int64 `json:"id"`
|
||||||
|
}
|
||||||
|
|
||||||
type config struct {
|
type config struct {
|
||||||
MusicPath string `json:"musicPath"`
|
MusicPath string `json:"musicPath"`
|
||||||
Local bool `json:"local"`
|
Local bool `json:"local"`
|
||||||
API bool `json:"api"`
|
API bool `json:"api"`
|
||||||
|
Cookie string `json:"cookie"`
|
||||||
|
Playlist []listRaw `json:"playlist"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type paugramData struct {
|
type keyInfo struct {
|
||||||
ID int `json:"id"`
|
Data struct {
|
||||||
Title string `json:"title"`
|
Code int `json:"code"`
|
||||||
Artist string `json:"artist"`
|
Unikey string `json:"unikey"`
|
||||||
Album string `json:"album"`
|
} `json:"data"`
|
||||||
Cover string `json:"cover"`
|
|
||||||
Lyric string `json:"lyric"`
|
|
||||||
SubLyric string `json:"sub_lyric"`
|
|
||||||
Link string `json:"link"`
|
|
||||||
Cached bool `json:"cached"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type animeData struct {
|
|
||||||
Msg string `json:"msg"`
|
|
||||||
Res struct {
|
|
||||||
ID string `json:"id"`
|
|
||||||
AnimeInfo struct {
|
|
||||||
Desc string `json:"desc"`
|
|
||||||
ID string `json:"id"`
|
|
||||||
Atime int `json:"atime"`
|
|
||||||
Logo string `json:"logo"`
|
|
||||||
Year int `json:"year"`
|
|
||||||
Bg string `json:"bg"`
|
|
||||||
Title string `json:"title"`
|
|
||||||
Month int `json:"month"`
|
|
||||||
} `json:"anime_info"`
|
|
||||||
PlayURL string `json:"play_url"`
|
|
||||||
Atime int `json:"atime"`
|
|
||||||
Title string `json:"title"`
|
|
||||||
Author string `json:"author"`
|
|
||||||
Type string `json:"type"`
|
|
||||||
Recommend bool `json:"recommend"`
|
|
||||||
} `json:"res"`
|
|
||||||
Code int `json:"code"`
|
Code int `json:"code"`
|
||||||
}
|
}
|
||||||
|
type cookyInfo struct {
|
||||||
type netEaseData struct {
|
Code int `json:"code"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
Cookie string `json:"cookie"`
|
||||||
|
}
|
||||||
|
type qrInfo struct {
|
||||||
Code int `json:"code"`
|
Code int `json:"code"`
|
||||||
Data struct {
|
Data struct {
|
||||||
Name string `json:"name"`
|
Qrurl string `json:"qrurl"`
|
||||||
URL string `json:"url"`
|
Qrimg string `json:"qrimg"`
|
||||||
Picurl string `json:"picurl"`
|
|
||||||
Artistsname string `json:"artistsname"`
|
|
||||||
} `json:"data"`
|
} `json:"data"`
|
||||||
}
|
}
|
||||||
|
type topList struct {
|
||||||
|
Code int `json:"code"`
|
||||||
|
RelatedVideos interface{} `json:"relatedVideos"`
|
||||||
|
Playlist struct {
|
||||||
|
ID int64 `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
CoverImgID int64 `json:"coverImgId"`
|
||||||
|
CoverImgURL string `json:"coverImgUrl"`
|
||||||
|
CoverImgIDStr string `json:"coverImgId_str"`
|
||||||
|
AdType int `json:"adType"`
|
||||||
|
UserID int `json:"userId"`
|
||||||
|
CreateTime int64 `json:"createTime"`
|
||||||
|
Status int `json:"status"`
|
||||||
|
OpRecommend bool `json:"opRecommend"`
|
||||||
|
HighQuality bool `json:"highQuality"`
|
||||||
|
NewImported bool `json:"newImported"`
|
||||||
|
UpdateTime int64 `json:"updateTime"`
|
||||||
|
TrackCount int `json:"trackCount"`
|
||||||
|
SpecialType int `json:"specialType"`
|
||||||
|
Privacy int `json:"privacy"`
|
||||||
|
TrackUpdateTime int64 `json:"trackUpdateTime"`
|
||||||
|
CommentThreadID string `json:"commentThreadId"`
|
||||||
|
PlayCount int `json:"playCount"`
|
||||||
|
TrackNumberUpdateTime int64 `json:"trackNumberUpdateTime"`
|
||||||
|
SubscribedCount int `json:"subscribedCount"`
|
||||||
|
CloudTrackCount int `json:"cloudTrackCount"`
|
||||||
|
Ordered bool `json:"ordered"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
Tags []string `json:"tags"`
|
||||||
|
UpdateFrequency interface{} `json:"updateFrequency"`
|
||||||
|
BackgroundCoverID int `json:"backgroundCoverId"`
|
||||||
|
BackgroundCoverURL interface{} `json:"backgroundCoverUrl"`
|
||||||
|
TitleImage int `json:"titleImage"`
|
||||||
|
TitleImageURL interface{} `json:"titleImageUrl"`
|
||||||
|
EnglishTitle interface{} `json:"englishTitle"`
|
||||||
|
OfficialPlaylistType interface{} `json:"officialPlaylistType"`
|
||||||
|
Subscribers []struct {
|
||||||
|
DefaultAvatar bool `json:"defaultAvatar"`
|
||||||
|
Province int `json:"province"`
|
||||||
|
AuthStatus int `json:"authStatus"`
|
||||||
|
Followed bool `json:"followed"`
|
||||||
|
AvatarURL string `json:"avatarUrl"`
|
||||||
|
AccountStatus int `json:"accountStatus"`
|
||||||
|
Gender int `json:"gender"`
|
||||||
|
City int `json:"city"`
|
||||||
|
Birthday int `json:"birthday"`
|
||||||
|
UserID int `json:"userId"`
|
||||||
|
UserType int `json:"userType"`
|
||||||
|
Nickname string `json:"nickname"`
|
||||||
|
Signature string `json:"signature"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
DetailDescription string `json:"detailDescription"`
|
||||||
|
AvatarImgID int64 `json:"avatarImgId"`
|
||||||
|
BackgroundImgID int64 `json:"backgroundImgId"`
|
||||||
|
BackgroundURL string `json:"backgroundUrl"`
|
||||||
|
Authority int `json:"authority"`
|
||||||
|
Mutual bool `json:"mutual"`
|
||||||
|
ExpertTags interface{} `json:"expertTags"`
|
||||||
|
Experts interface{} `json:"experts"`
|
||||||
|
DjStatus int `json:"djStatus"`
|
||||||
|
VipType int `json:"vipType"`
|
||||||
|
RemarkName interface{} `json:"remarkName"`
|
||||||
|
AuthenticationTypes int `json:"authenticationTypes"`
|
||||||
|
AvatarDetail interface{} `json:"avatarDetail"`
|
||||||
|
Anchor bool `json:"anchor"`
|
||||||
|
BackgroundImgIDStr string `json:"backgroundImgIdStr"`
|
||||||
|
AvatarImgIDStr string `json:"avatarImgIdStr"`
|
||||||
|
AvatarImgIDString string `json:"AvatarImgIDString"`
|
||||||
|
} `json:"subscribers"`
|
||||||
|
Subscribed interface{} `json:"subscribed"`
|
||||||
|
Creator struct {
|
||||||
|
DefaultAvatar bool `json:"defaultAvatar"`
|
||||||
|
Province int `json:"province"`
|
||||||
|
AuthStatus int `json:"authStatus"`
|
||||||
|
Followed bool `json:"followed"`
|
||||||
|
AvatarURL string `json:"avatarUrl"`
|
||||||
|
AccountStatus int `json:"accountStatus"`
|
||||||
|
Gender int `json:"gender"`
|
||||||
|
City int `json:"city"`
|
||||||
|
Birthday int `json:"birthday"`
|
||||||
|
UserID int `json:"userId"`
|
||||||
|
UserType int `json:"userType"`
|
||||||
|
Nickname string `json:"nickname"`
|
||||||
|
Signature string `json:"signature"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
DetailDescription string `json:"detailDescription"`
|
||||||
|
AvatarImgID int64 `json:"avatarImgId"`
|
||||||
|
BackgroundImgID int64 `json:"backgroundImgId"`
|
||||||
|
BackgroundURL string `json:"backgroundUrl"`
|
||||||
|
Authority int `json:"authority"`
|
||||||
|
Mutual bool `json:"mutual"`
|
||||||
|
ExpertTags interface{} `json:"expertTags"`
|
||||||
|
Experts interface{} `json:"experts"`
|
||||||
|
DjStatus int `json:"djStatus"`
|
||||||
|
VipType int `json:"vipType"`
|
||||||
|
RemarkName interface{} `json:"remarkName"`
|
||||||
|
AuthenticationTypes int `json:"authenticationTypes"`
|
||||||
|
AvatarDetail struct {
|
||||||
|
UserType int `json:"userType"`
|
||||||
|
IdentityLevel int `json:"identityLevel"`
|
||||||
|
IdentityIconURL string `json:"identityIconUrl"`
|
||||||
|
} `json:"avatarDetail"`
|
||||||
|
Anchor bool `json:"anchor"`
|
||||||
|
BackgroundImgIDStr string `json:"backgroundImgIdStr"`
|
||||||
|
AvatarImgIDStr string `json:"avatarImgIdStr"`
|
||||||
|
AvatarImgIDString string `json:"AvatarImgIDString"`
|
||||||
|
} `json:"creator"`
|
||||||
|
Tracks []struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
ID int `json:"id"`
|
||||||
|
Pst int `json:"pst"`
|
||||||
|
T int `json:"t"`
|
||||||
|
Ar []struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Tns []interface{} `json:"tns"`
|
||||||
|
Alias []interface{} `json:"alias"`
|
||||||
|
} `json:"ar"`
|
||||||
|
Alia []string `json:"alia"`
|
||||||
|
Pop int `json:"pop"`
|
||||||
|
St int `json:"st"`
|
||||||
|
Rt string `json:"rt"`
|
||||||
|
Fee int `json:"fee"`
|
||||||
|
V int `json:"v"`
|
||||||
|
Crbt interface{} `json:"crbt"`
|
||||||
|
Cf string `json:"cf"`
|
||||||
|
Al struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
PicURL string `json:"picUrl"`
|
||||||
|
Tns []interface{} `json:"tns"`
|
||||||
|
PicStr string `json:"pic_str"`
|
||||||
|
Pic int64 `json:"pic"`
|
||||||
|
} `json:"al"`
|
||||||
|
Dt int `json:"dt"`
|
||||||
|
H struct {
|
||||||
|
Br int `json:"br"`
|
||||||
|
Fid int `json:"fid"`
|
||||||
|
Size int `json:"size"`
|
||||||
|
Vd int `json:"vd"`
|
||||||
|
Sr int `json:"sr"`
|
||||||
|
} `json:"h"`
|
||||||
|
M struct {
|
||||||
|
Br int `json:"br"`
|
||||||
|
Fid int `json:"fid"`
|
||||||
|
Size int `json:"size"`
|
||||||
|
Vd int `json:"vd"`
|
||||||
|
Sr int `json:"sr"`
|
||||||
|
} `json:"m"`
|
||||||
|
L struct {
|
||||||
|
Br int `json:"br"`
|
||||||
|
Fid int `json:"fid"`
|
||||||
|
Size int `json:"size"`
|
||||||
|
Vd int `json:"vd"`
|
||||||
|
Sr int `json:"sr"`
|
||||||
|
} `json:"l"`
|
||||||
|
Sq interface{} `json:"sq"`
|
||||||
|
Hr interface{} `json:"hr"`
|
||||||
|
A interface{} `json:"a"`
|
||||||
|
Cd string `json:"cd"`
|
||||||
|
No int `json:"no"`
|
||||||
|
RtURL interface{} `json:"rtUrl"`
|
||||||
|
Ftype int `json:"ftype"`
|
||||||
|
RtUrls []interface{} `json:"rtUrls"`
|
||||||
|
DjID int `json:"djId"`
|
||||||
|
Copyright int `json:"copyright"`
|
||||||
|
SID int `json:"s_id"`
|
||||||
|
Mark int `json:"mark"`
|
||||||
|
OriginCoverType int `json:"originCoverType"`
|
||||||
|
OriginSongSimpleData interface{} `json:"originSongSimpleData"`
|
||||||
|
TagPicList interface{} `json:"tagPicList"`
|
||||||
|
ResourceState bool `json:"resourceState"`
|
||||||
|
Version int `json:"version"`
|
||||||
|
SongJumpInfo interface{} `json:"songJumpInfo"`
|
||||||
|
EntertainmentTags interface{} `json:"entertainmentTags"`
|
||||||
|
Single int `json:"single"`
|
||||||
|
NoCopyrightRcmd interface{} `json:"noCopyrightRcmd"`
|
||||||
|
Alg interface{} `json:"alg"`
|
||||||
|
Rtype int `json:"rtype"`
|
||||||
|
Rurl interface{} `json:"rurl"`
|
||||||
|
Mst int `json:"mst"`
|
||||||
|
Cp int `json:"cp"`
|
||||||
|
Mv int `json:"mv"`
|
||||||
|
PublishTime int64 `json:"publishTime"`
|
||||||
|
Tns []string `json:"tns,omitempty"`
|
||||||
|
} `json:"tracks"`
|
||||||
|
VideoIds interface{} `json:"videoIds"`
|
||||||
|
Videos interface{} `json:"videos"`
|
||||||
|
TrackIds []struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
V int `json:"v"`
|
||||||
|
T int `json:"t"`
|
||||||
|
At int64 `json:"at"`
|
||||||
|
Alg interface{} `json:"alg"`
|
||||||
|
UID int `json:"uid"`
|
||||||
|
RcmdReason string `json:"rcmdReason"`
|
||||||
|
Sc interface{} `json:"sc"`
|
||||||
|
Lr int `json:"lr,omitempty"`
|
||||||
|
} `json:"trackIds"`
|
||||||
|
ShareCount int `json:"shareCount"`
|
||||||
|
CommentCount int `json:"commentCount"`
|
||||||
|
RemixVideo interface{} `json:"remixVideo"`
|
||||||
|
SharedUsers interface{} `json:"sharedUsers"`
|
||||||
|
HistorySharedUsers interface{} `json:"historySharedUsers"`
|
||||||
|
GradeStatus string `json:"gradeStatus"`
|
||||||
|
Score interface{} `json:"score"`
|
||||||
|
AlgTags interface{} `json:"algTags"`
|
||||||
|
} `json:"playlist"`
|
||||||
|
Urls interface{} `json:"urls"`
|
||||||
|
Privileges []struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
Fee int `json:"fee"`
|
||||||
|
Payed int `json:"payed"`
|
||||||
|
RealPayed int `json:"realPayed"`
|
||||||
|
St int `json:"st"`
|
||||||
|
Pl int `json:"pl"`
|
||||||
|
Dl int `json:"dl"`
|
||||||
|
Sp int `json:"sp"`
|
||||||
|
Cp int `json:"cp"`
|
||||||
|
Subp int `json:"subp"`
|
||||||
|
Cs bool `json:"cs"`
|
||||||
|
Maxbr int `json:"maxbr"`
|
||||||
|
Fl int `json:"fl"`
|
||||||
|
Pc interface{} `json:"pc"`
|
||||||
|
Toast bool `json:"toast"`
|
||||||
|
Flag int `json:"flag"`
|
||||||
|
PaidBigBang bool `json:"paidBigBang"`
|
||||||
|
PreSell bool `json:"preSell"`
|
||||||
|
PlayMaxbr int `json:"playMaxbr"`
|
||||||
|
DownloadMaxbr int `json:"downloadMaxbr"`
|
||||||
|
MaxBrLevel string `json:"maxBrLevel"`
|
||||||
|
PlayMaxBrLevel string `json:"playMaxBrLevel"`
|
||||||
|
DownloadMaxBrLevel string `json:"downloadMaxBrLevel"`
|
||||||
|
PlLevel string `json:"plLevel"`
|
||||||
|
DlLevel string `json:"dlLevel"`
|
||||||
|
FlLevel string `json:"flLevel"`
|
||||||
|
Rscl int `json:"rscl"`
|
||||||
|
FreeTrialPrivilege struct {
|
||||||
|
ResConsumable bool `json:"resConsumable"`
|
||||||
|
UserConsumable bool `json:"userConsumable"`
|
||||||
|
ListenType interface{} `json:"listenType"`
|
||||||
|
} `json:"freeTrialPrivilege"`
|
||||||
|
ChargeInfoList []struct {
|
||||||
|
Rate int `json:"rate"`
|
||||||
|
ChargeURL interface{} `json:"chargeUrl"`
|
||||||
|
ChargeMessage interface{} `json:"chargeMessage"`
|
||||||
|
ChargeType int `json:"chargeType"`
|
||||||
|
} `json:"chargeInfoList"`
|
||||||
|
} `json:"privileges"`
|
||||||
|
SharedPrivilege interface{} `json:"sharedPrivilege"`
|
||||||
|
ResEntrance interface{} `json:"resEntrance"`
|
||||||
|
}
|
||||||
|
|
||||||
type autumnfishData struct {
|
type topMusicInfo struct {
|
||||||
Result struct {
|
Songs []struct {
|
||||||
Songs []struct {
|
Name string `json:"name"`
|
||||||
ID int `json:"id"`
|
ID int `json:"id"`
|
||||||
Name string `json:"name"`
|
Pst int `json:"pst"`
|
||||||
Artists []struct {
|
T int `json:"t"`
|
||||||
ID int `json:"id"`
|
Ar []struct {
|
||||||
Name string `json:"name"`
|
ID int `json:"id"`
|
||||||
PicURL interface{} `json:"picUrl"`
|
Name string `json:"name"`
|
||||||
Alias []interface{} `json:"alias"`
|
Tns []interface{} `json:"tns"`
|
||||||
AlbumSize int `json:"albumSize"`
|
Alias []interface{} `json:"alias"`
|
||||||
PicID int `json:"picId"`
|
} `json:"ar"`
|
||||||
Img1V1URL string `json:"img1v1Url"`
|
Alia []string `json:"alia"`
|
||||||
Img1V1 int `json:"img1v1"`
|
Pop int `json:"pop"`
|
||||||
Trans interface{} `json:"trans"`
|
St int `json:"st"`
|
||||||
} `json:"artists"`
|
Rt string `json:"rt"`
|
||||||
Album struct {
|
Fee int `json:"fee"`
|
||||||
ID int `json:"id"`
|
V int `json:"v"`
|
||||||
Name string `json:"name"`
|
Crbt interface{} `json:"crbt"`
|
||||||
Artist struct {
|
Cf string `json:"cf"`
|
||||||
ID int `json:"id"`
|
Al struct {
|
||||||
Name string `json:"name"`
|
ID int `json:"id"`
|
||||||
PicURL interface{} `json:"picUrl"`
|
Name string `json:"name"`
|
||||||
Alias []interface{} `json:"alias"`
|
PicURL string `json:"picUrl"`
|
||||||
AlbumSize int `json:"albumSize"`
|
Tns []interface{} `json:"tns"`
|
||||||
PicID int `json:"picId"`
|
PicStr string `json:"pic_str"`
|
||||||
Img1V1URL string `json:"img1v1Url"`
|
Pic int64 `json:"pic"`
|
||||||
Img1V1 int `json:"img1v1"`
|
} `json:"al"`
|
||||||
Trans interface{} `json:"trans"`
|
Dt int `json:"dt"`
|
||||||
} `json:"artist"`
|
H struct {
|
||||||
PublishTime int64 `json:"publishTime"`
|
Br int `json:"br"`
|
||||||
Size int `json:"size"`
|
Fid int `json:"fid"`
|
||||||
CopyrightID int `json:"copyrightId"`
|
Size int `json:"size"`
|
||||||
Status int `json:"status"`
|
Vd float32 `json:"vd"`
|
||||||
PicID int64 `json:"picId"`
|
Sr int `json:"sr"`
|
||||||
Mark int `json:"mark"`
|
} `json:"h"`
|
||||||
} `json:"album"`
|
M struct {
|
||||||
Duration int `json:"duration"`
|
Br int `json:"br"`
|
||||||
CopyrightID int `json:"copyrightId"`
|
Fid int `json:"fid"`
|
||||||
Status int `json:"status"`
|
Size int `json:"size"`
|
||||||
Alias []interface{} `json:"alias"`
|
Vd float32 `json:"vd"`
|
||||||
Rtype int `json:"rtype"`
|
Sr int `json:"sr"`
|
||||||
Ftype int `json:"ftype"`
|
} `json:"m"`
|
||||||
TransNames []string `json:"transNames"`
|
L struct {
|
||||||
Mvid int `json:"mvid"`
|
Br int `json:"br"`
|
||||||
Fee int `json:"fee"`
|
Fid int `json:"fid"`
|
||||||
RURL interface{} `json:"rUrl"`
|
Size int `json:"size"`
|
||||||
Mark int `json:"mark"`
|
Vd float32 `json:"vd"`
|
||||||
} `json:"songs"`
|
Sr int `json:"sr"`
|
||||||
HasMore bool `json:"hasMore"`
|
} `json:"l"`
|
||||||
SongCount int `json:"songCount"`
|
Sq interface{} `json:"sq"`
|
||||||
} `json:"result"`
|
Hr interface{} `json:"hr"`
|
||||||
|
A interface{} `json:"a"`
|
||||||
|
Cd string `json:"cd"`
|
||||||
|
No int `json:"no"`
|
||||||
|
RtURL interface{} `json:"rtUrl"`
|
||||||
|
Ftype int `json:"ftype"`
|
||||||
|
RtUrls []interface{} `json:"rtUrls"`
|
||||||
|
DjID int `json:"djId"`
|
||||||
|
Copyright int `json:"copyright"`
|
||||||
|
SID int `json:"s_id"`
|
||||||
|
Mark int `json:"mark"`
|
||||||
|
OriginCoverType int `json:"originCoverType"`
|
||||||
|
OriginSongSimpleData interface{} `json:"originSongSimpleData"`
|
||||||
|
TagPicList interface{} `json:"tagPicList"`
|
||||||
|
ResourceState bool `json:"resourceState"`
|
||||||
|
Version int `json:"version"`
|
||||||
|
SongJumpInfo interface{} `json:"songJumpInfo"`
|
||||||
|
EntertainmentTags interface{} `json:"entertainmentTags"`
|
||||||
|
AwardTags interface{} `json:"awardTags"`
|
||||||
|
Single int `json:"single"`
|
||||||
|
NoCopyrightRcmd interface{} `json:"noCopyrightRcmd"`
|
||||||
|
Rtype int `json:"rtype"`
|
||||||
|
Rurl interface{} `json:"rurl"`
|
||||||
|
Mst int `json:"mst"`
|
||||||
|
Cp int `json:"cp"`
|
||||||
|
Mv int `json:"mv"`
|
||||||
|
PublishTime int64 `json:"publishTime"`
|
||||||
|
Tns []string `json:"tns,omitempty"`
|
||||||
|
} `json:"songs"`
|
||||||
|
Privileges []struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
Fee int `json:"fee"`
|
||||||
|
Payed int `json:"payed"`
|
||||||
|
St int `json:"st"`
|
||||||
|
Pl int `json:"pl"`
|
||||||
|
Dl int `json:"dl"`
|
||||||
|
Sp int `json:"sp"`
|
||||||
|
Cp int `json:"cp"`
|
||||||
|
Subp int `json:"subp"`
|
||||||
|
Cs bool `json:"cs"`
|
||||||
|
Maxbr int `json:"maxbr"`
|
||||||
|
Fl int `json:"fl"`
|
||||||
|
Toast bool `json:"toast"`
|
||||||
|
Flag int `json:"flag"`
|
||||||
|
PreSell bool `json:"preSell"`
|
||||||
|
PlayMaxbr int `json:"playMaxbr"`
|
||||||
|
DownloadMaxbr int `json:"downloadMaxbr"`
|
||||||
|
MaxBrLevel string `json:"maxBrLevel"`
|
||||||
|
PlayMaxBrLevel string `json:"playMaxBrLevel"`
|
||||||
|
DownloadMaxBrLevel string `json:"downloadMaxBrLevel"`
|
||||||
|
PlLevel string `json:"plLevel"`
|
||||||
|
DlLevel string `json:"dlLevel"`
|
||||||
|
FlLevel string `json:"flLevel"`
|
||||||
|
Rscl int `json:"rscl"`
|
||||||
|
FreeTrialPrivilege struct {
|
||||||
|
ResConsumable bool `json:"resConsumable"`
|
||||||
|
UserConsumable bool `json:"userConsumable"`
|
||||||
|
ListenType interface{} `json:"listenType"`
|
||||||
|
} `json:"freeTrialPrivilege"`
|
||||||
|
ChargeInfoList []struct {
|
||||||
|
Rate int `json:"rate"`
|
||||||
|
ChargeURL interface{} `json:"chargeUrl"`
|
||||||
|
ChargeMessage interface{} `json:"chargeMessage"`
|
||||||
|
ChargeType int `json:"chargeType"`
|
||||||
|
} `json:"chargeInfoList"`
|
||||||
|
} `json:"privileges"`
|
||||||
Code int `json:"code"`
|
Code int `json:"code"`
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user