mirror of
https://github.com/ZxwyWebSite/lx-source.git
synced 2025-07-10 08:28:14 +08:00
2024-02-15 v1.0.3-pre
This commit is contained in:
10
init.go
10
init.go
@ -12,6 +12,7 @@ import (
|
||||
"path/filepath"
|
||||
|
||||
"github.com/ZxwyWebSite/ztool"
|
||||
"github.com/ZxwyWebSite/ztool/logs"
|
||||
"github.com/ZxwyWebSite/ztool/zcypt"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
@ -43,9 +44,14 @@ func loadFileLoger() {
|
||||
if env.Config.Main.LogPath != `` {
|
||||
lg := env.Loger.NewGroup(`FileLoger`)
|
||||
printout := env.Config.Main.Print // || env.Config.Main.Debug
|
||||
_, do, err := env.Loger.SetOutFile(ztool.Str_FastConcat(env.RunPath, env.Config.Main.LogPath), printout)
|
||||
f, do, err := env.Loger.SetOutFile(ztool.Str_FastConcat(env.RunPath, env.Config.Main.LogPath), printout)
|
||||
if err == nil {
|
||||
env.Defer.Add(do)
|
||||
// env.Defer.Add(do)
|
||||
env.Defer.Add(func() { do(); f.Close() })
|
||||
env.Tasker.Add(`flog_flush`, func(loger *logs.Logger, now int64) error {
|
||||
loger.Debug(`已写入文件并清理日志缓存`)
|
||||
return do()
|
||||
}, 3600, false)
|
||||
gin.DefaultWriter = env.Loger.GetOutput()
|
||||
gin.ForceConsoleColor()
|
||||
// lg.Info(`文件日志初始化成功`)
|
||||
|
@ -17,6 +17,10 @@
|
||||
+ 视频教程:[使用教程.mp4](https://r2eu.zxwy.link/gh/lx-source/v1.0.2-b0.1/%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B.mp4)
|
||||
<!-- + *手持两把锟斤拷,口中疾呼烫烫烫。 脚踏千朵屯屯屯,笑看万物锘锘锘。* -->
|
||||
|
||||
### 注意
|
||||
+ 不保证内置源可用性,如需长期使用请自备会员账号
|
||||
+ 音源最好留着自己用,你非要当大好人当我没说(由于传播导致的账号被封与本项目无关)
|
||||
|
||||
### 使用
|
||||
#### 服务端
|
||||
+ 到Release下载对应平台可执行文件运行
|
||||
|
@ -85,7 +85,7 @@ func (c *Query) Query() string {
|
||||
return c.query
|
||||
}
|
||||
|
||||
// 分割查询字符串
|
||||
// 分割查询字符串 (已弃用)
|
||||
/*
|
||||
kg: 分割 Hash-Album 如 "6DC276334F56E22BE2A0E8254D332B45-13097991"
|
||||
tx: 分割 songmid-strMediaMid 如 "002fktJg3cmSpC-000V6uuv35Cwnh"
|
||||
|
1
src/database/driver.go
Normal file
1
src/database/driver.go
Normal file
@ -0,0 +1 @@
|
||||
package database
|
59
src/database/types.go
Normal file
59
src/database/types.go
Normal file
@ -0,0 +1,59 @@
|
||||
package database
|
||||
|
||||
// 结构表
|
||||
// type (
|
||||
// PublicKeys struct {
|
||||
// ID string `json:"id" gorm:"primaryKey"`
|
||||
// }
|
||||
// // 音乐
|
||||
// MusicItem struct {
|
||||
// ID string `json:"id" gorm:"primaryKey"` // 唯一ID
|
||||
// Name string `json:"name" gorm:"column:name"` //
|
||||
// // Source string `json:"source" gorm:"-:all"`
|
||||
// }
|
||||
// // 作者
|
||||
// ArtistItem struct {
|
||||
// ID string `json:"id" gorm:"primaryKey"`
|
||||
// Name string `json:"name" gorm:"column:name"`
|
||||
// }
|
||||
// // 歌词
|
||||
// LyricItem struct {
|
||||
// ID string `json:"id" gorm:"primaryKey"`
|
||||
// Lyric string `json:"lyric" gorm:"column:lyric"` // 歌曲歌词
|
||||
// TLyric string `json:"tlyric" gorm:"column:tlyric"` // 翻译歌词,没有可为 null
|
||||
// RLyric string `json:"rlyric" gorm:"column:rlyric"` // 罗马音歌词,没有可为 null
|
||||
// LxLyric string `json:"lxlyric" gorm:"column:lxlyric"` // lx 逐字歌词,没有可为 null
|
||||
// // 歌词格式为 [分钟:秒.毫秒]<开始时间(基于该句),持续时间>歌词文字
|
||||
// // 例如: [00:00.000]<0,36>测<36,36>试<50,60>歌<80,75>词
|
||||
// }
|
||||
// // 视频
|
||||
// MovieItem struct {
|
||||
// ID string `json:"id" gorm:"primaryKey"`
|
||||
// Name string `json:"name" gorm:"column:name"`
|
||||
// }
|
||||
// )
|
||||
|
||||
// 分源表
|
||||
// type (
|
||||
// // Music
|
||||
// WyMusic MusicItem
|
||||
// MgMusic MusicItem
|
||||
// KwMusic MusicItem
|
||||
// KgMusic MusicItem
|
||||
// TxMusic MusicItem
|
||||
// LxMusic MusicItem
|
||||
// // Artist
|
||||
// WyArtist ArtistItem
|
||||
// MgArtist ArtistItem
|
||||
// KwArtist ArtistItem
|
||||
// KgArtist ArtistItem
|
||||
// TxArtist ArtistItem
|
||||
// LxArtist ArtistItem
|
||||
// // Lyric
|
||||
// WyLyric LyricItem
|
||||
// MgLyric LyricItem
|
||||
// KwLyric LyricItem
|
||||
// KgLyric LyricItem
|
||||
// TxLyric LyricItem
|
||||
// LxLyric LyricItem
|
||||
// )
|
30
src/env/env.go
vendored
30
src/env/env.go
vendored
@ -12,7 +12,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
Version = `1.0.2-dev`
|
||||
Version = `1.0.3-pre`
|
||||
)
|
||||
|
||||
var (
|
||||
@ -78,7 +78,7 @@ type (
|
||||
Proxy_Address string `comment:"代理地址 (支持http, socks)"`
|
||||
// 验证
|
||||
MusicIdVerify bool `comment:"(beta) 验证音乐ID可用性"`
|
||||
ForceFallback bool `comment:"忽略音质限制,强制获取试听音频"`
|
||||
ForceFallback bool `comment:"忽略音质限制,强制返回链接(部分支持)"`
|
||||
// 总开关(解决部分源无法彻底禁用问题)?
|
||||
Enable_Wy bool `comment:"是否开启小芸源"`
|
||||
Enable_Mg bool `comment:"是否开启小蜜源"`
|
||||
@ -97,11 +97,17 @@ type (
|
||||
Wy_Api_Address string `comment:"NeteaseCloudMusicApi项目地址"`
|
||||
Wy_Api_Cookie string `comment:"账号cookie数据"`
|
||||
// wy refresh
|
||||
// Wy_Refresh_Enable bool `comment:"是否启用刷新登录"`
|
||||
// Wy_Refresh_Interval int64 `comment:"下次刷新时间 (由程序维护)"`
|
||||
Wy_Refresh_Enable bool `comment:"是否启用刷新登录"`
|
||||
Wy_Refresh_Interval int64 `comment:"下次刷新时间 (由程序维护)"`
|
||||
|
||||
// mg (暂未实现)
|
||||
// mg
|
||||
Mg_Enable bool `comment:"是否启用小蜜源"`
|
||||
Mg_Mode string `comment:"获取方式 0: builtin, 1: custom"`
|
||||
// mg custom
|
||||
Mg_Usr_VerId string `comment:"field user.aversionid"`
|
||||
Mg_Usr_Token string `comment:"field user.token"`
|
||||
Mg_Usr_OSVer string `comment:"field user.osversion"`
|
||||
Mg_Usr_ReqUA string `comment:"field user.useragent"`
|
||||
|
||||
// kw
|
||||
Kw_Enable bool `comment:"是否启用小蜗源"`
|
||||
@ -114,8 +120,10 @@ type (
|
||||
Kw_Des_Type string `comment:"返回格式 0: text, 1: json"`
|
||||
Kw_Des_Header string `comment:"请求头 User-Agent"`
|
||||
|
||||
// kg (暂未实现)
|
||||
// kg
|
||||
Kg_Enable bool `comment:"是否启用小枸源"`
|
||||
Kg_token string `comment:"field user.token"`
|
||||
Kg_userId string `comment:"field user.userid"`
|
||||
|
||||
// tx
|
||||
Tx_Enable bool `comment:"是否启用小秋源"`
|
||||
@ -201,22 +209,26 @@ var (
|
||||
Enable_Kw: true,
|
||||
Enable_Kg: true,
|
||||
Enable_Tx: true,
|
||||
Enable_Lx: true,
|
||||
Enable_Lx: false,
|
||||
},
|
||||
Custom: Conf_Custom{
|
||||
Wy_Enable: true,
|
||||
Wy_Mode: `builtin`,
|
||||
Wy_Api_Type: `native`,
|
||||
// Wy_Refresh_Interval: 1633622400,
|
||||
Wy_Refresh_Interval: 1633622400,
|
||||
|
||||
Mg_Enable: true,
|
||||
Mg_Mode: `builtin`,
|
||||
Mg_Usr_OSVer: `10`,
|
||||
Mg_Usr_ReqUA: `Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.82 Safari/537.36`,
|
||||
|
||||
Kw_Enable: true,
|
||||
Kw_Mode: `kwdes`,
|
||||
Kw_Des_Type: `json`,
|
||||
Kw_Des_Header: `okhttp/3.10.0`,
|
||||
|
||||
Kg_Enable: true,
|
||||
Kg_Enable: false,
|
||||
Kg_userId: `0`,
|
||||
|
||||
Tx_Enable: false,
|
||||
Tx_Refresh_Enable: false,
|
||||
|
1
src/middleware/auth/ratelimit.go
Normal file
1
src/middleware/auth/ratelimit.go
Normal file
@ -0,0 +1 @@
|
||||
package auth
|
@ -9,7 +9,7 @@ import (
|
||||
|
||||
// 统一输出
|
||||
/*
|
||||
返回码对应表:
|
||||
返回码对应表 (参考Python版):
|
||||
0: http.StatusOK, // [200] 成功
|
||||
1: http.StatusForbidden, // [403] IP被封禁
|
||||
2: http.StatusServiceUnavailable, // [503] 获取失败
|
||||
@ -21,21 +21,11 @@ import (
|
||||
type Resp struct {
|
||||
Code int `json:"code"` // 状态码 为兼容内置源设置 暂无实际作用 (1.0.2后已兼容Python版定义)
|
||||
Msg string `json:"msg"` // 提示or报错信息
|
||||
Data string `json:"data"` // 音乐URL
|
||||
Ext string `json:"ext,omitempty"` // 其它信息
|
||||
}
|
||||
|
||||
// 返回码对应列表 (参考Python版)
|
||||
var statusMap = map[int]int{
|
||||
0: http.StatusOK, // 成功
|
||||
1: http.StatusForbidden, // IP被封禁
|
||||
2: http.StatusServiceUnavailable, // 获取失败
|
||||
3: http.StatusUnauthorized, // 验证失败
|
||||
4: http.StatusInternalServerError, // 服务器内部错误
|
||||
5: http.StatusTooManyRequests, // 请求过于频繁
|
||||
6: http.StatusBadRequest, // 参数错误
|
||||
Data any `json:"data"` // 音乐URL
|
||||
Ext any `json:"ext,omitempty"` // 其它信息
|
||||
}
|
||||
|
||||
// 获取失败默认音频
|
||||
var ErrMp3 = `https://r2eu.zxwy.link/gh/lx-source/static/error.mp3`
|
||||
|
||||
// 返回请求
|
||||
@ -43,16 +33,35 @@ var ErrMp3 = `https://r2eu.zxwy.link/gh/lx-source/static/error.mp3`
|
||||
注:Code不为0时调用c.Abort()终止Handler
|
||||
*/
|
||||
func (o *Resp) Execute(c *gin.Context) {
|
||||
status, ok := statusMap[o.Code]
|
||||
if !ok {
|
||||
// StatusCode转换 (小分支switch快, 大分支map快)
|
||||
var status int
|
||||
switch o.Code {
|
||||
case 0:
|
||||
status = http.StatusOK
|
||||
}
|
||||
if o.Code != 0 {
|
||||
if o.Code == 2 /*&& o.Data == ``*/ {
|
||||
case 1:
|
||||
status = http.StatusForbidden
|
||||
case 2:
|
||||
status = http.StatusServiceUnavailable
|
||||
if o.Data == nil || o.Data == `` {
|
||||
o.Data = ErrMp3
|
||||
}
|
||||
c.Abort()
|
||||
case 3:
|
||||
status = http.StatusUnauthorized
|
||||
case 4:
|
||||
status = http.StatusInternalServerError
|
||||
case 5:
|
||||
status = http.StatusTooManyRequests
|
||||
case 6:
|
||||
status = http.StatusBadRequest
|
||||
default:
|
||||
status = http.StatusOK
|
||||
}
|
||||
// if o.Code != 0 {
|
||||
// if o.Code == 2 /*&& o.Data == ``*/ {
|
||||
// o.Data = ErrMp3
|
||||
// }
|
||||
// c.Abort()
|
||||
// }
|
||||
c.JSON(status, o)
|
||||
}
|
||||
|
||||
@ -65,3 +74,9 @@ func Wrap(c *gin.Context, f func() *Resp) {
|
||||
r.Execute(c)
|
||||
}
|
||||
}
|
||||
|
||||
// func Wrap2(c *gin.Context, p []string, f func([]string) *Resp) {
|
||||
// if r := f(util.ParaArr(c)); r != nil {
|
||||
// r.Execute(c)
|
||||
// }
|
||||
// }
|
||||
|
@ -25,6 +25,29 @@ func ParaMap(c *gin.Context) map[string]string {
|
||||
return parms
|
||||
}
|
||||
|
||||
// 将路由参数转为Array
|
||||
/*
|
||||
ParaArr(c, `id`, `s`, `xxx`) => [
|
||||
`musicId`,
|
||||
`source`,
|
||||
``,
|
||||
]
|
||||
*/
|
||||
func ParaArr(c *gin.Context, s ...string) []string {
|
||||
parmlen := len(c.Params)
|
||||
parslen := len(s)
|
||||
out := make([]string, parslen)
|
||||
for im := 0; im < parmlen; im++ {
|
||||
obj := c.Params[im]
|
||||
for is := 0; is < parslen; is++ {
|
||||
if s[is] == obj.Key {
|
||||
out[is] = obj.Value
|
||||
}
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
var pathCache string
|
||||
|
||||
func init() {
|
||||
|
117
src/server/api_music.go
Normal file
117
src/server/api_music.go
Normal file
@ -0,0 +1,117 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"lx-source/src/caches"
|
||||
"lx-source/src/env"
|
||||
"lx-source/src/middleware/resp"
|
||||
"lx-source/src/middleware/util"
|
||||
"lx-source/src/sources"
|
||||
"lx-source/src/sources/custom"
|
||||
"strings"
|
||||
|
||||
"github.com/ZxwyWebSite/ztool"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// type (
|
||||
// Context struct {
|
||||
// Parms map[string]string
|
||||
// }
|
||||
// Source interface {
|
||||
// Link(c *Context) (string, error)
|
||||
// Lyric(c *Context)
|
||||
// }
|
||||
// )
|
||||
|
||||
func loadMusic(api *gin.RouterGroup) {
|
||||
// /{method}/{source}/{musicId}/{?quality}
|
||||
api.GET(`/:m/:s/:id/*q`, musicHandler)
|
||||
|
||||
}
|
||||
|
||||
func musicHandler(c *gin.Context) {
|
||||
resp.Wrap(c, func() *resp.Resp {
|
||||
// 获取请求参数 (测试用Array会不会提升性能)
|
||||
arr := util.ParaArr(c, `s`, `m`, `id`, `q`)
|
||||
ps, pm, pid, pq := arr[0], arr[1], arr[2], strings.TrimPrefix(arr[3], `/`)
|
||||
out := &resp.Resp{Code: 0} // 默认Code:6 (参数错误)
|
||||
loger := env.Loger.NewGroup(`MusicHandler`)
|
||||
defer loger.Free()
|
||||
loger.Debug(`s:'%v', m:'%v', id:'%v', q:'%v'`, ps, pm, pid, pq)
|
||||
// 查询内存缓存
|
||||
cquery := strings.Join([]string{pm, ps, pid, pq}, `/`)
|
||||
loger.Debug(cquery)
|
||||
if clink, ok := env.Cache.Get(cquery); ok {
|
||||
if cstr, ok := clink.(string); ok {
|
||||
loger.Debug(`MemHIT [%q]=>[%q]`, cquery, cstr)
|
||||
if cstr == `` {
|
||||
out.Code = 2
|
||||
out.Msg = `Memory Reject`
|
||||
} else {
|
||||
out.Msg = `Memory HIT`
|
||||
out.Data = cstr
|
||||
}
|
||||
return out
|
||||
}
|
||||
}
|
||||
// 定位音乐源
|
||||
var source custom.Source
|
||||
switch ps {
|
||||
case sources.S_wy:
|
||||
source = custom.WySource
|
||||
case sources.S_mg:
|
||||
source = custom.MgSource
|
||||
case sources.S_kw:
|
||||
source = custom.KwSource
|
||||
case sources.S_kg:
|
||||
source = custom.KgSource
|
||||
case sources.S_tx:
|
||||
source = custom.TxSource
|
||||
case sources.S_lx:
|
||||
source = custom.LxSource
|
||||
default:
|
||||
out.Code = 6
|
||||
out.Msg = ztool.Str_FastConcat(`无效源参数:'`, ps, `'`)
|
||||
return out
|
||||
}
|
||||
if source == nil {
|
||||
out.Code = 6
|
||||
out.Msg = sources.ErrDisable
|
||||
return out
|
||||
}
|
||||
// 定位源方法
|
||||
if !source.Vef(pid) {
|
||||
out.Code = 6
|
||||
out.Msg = sources.E_VefMusicId
|
||||
return out
|
||||
}
|
||||
switch pm {
|
||||
case `url`, `link`:
|
||||
// 查询文件缓存
|
||||
if caches.UseCache.Stat() {
|
||||
uquery := caches.NewQuery(ps, pid, pq)
|
||||
defer uquery.Free()
|
||||
if olink := caches.UseCache.Get(uquery); olink != `` {
|
||||
env.Cache.Set(cquery, olink, 3600)
|
||||
out.Msg = `Cache HIT`
|
||||
out.Data = olink
|
||||
return out
|
||||
}
|
||||
}
|
||||
out.Msg = `No Link`
|
||||
// out.Data, out.Msg = source.Url(pid, pq)
|
||||
case `lrc`, `lyric`:
|
||||
out.Data, out.Msg = source.Lrc(pid)
|
||||
case `pic`, `cover`:
|
||||
out.Data, out.Msg = source.Pic(pid)
|
||||
default:
|
||||
out.Code = 6
|
||||
out.Msg = ztool.Str_FastConcat(`无效源方法:'`, pm, `'`)
|
||||
// return
|
||||
}
|
||||
// 缓存并获取直链
|
||||
if out.Data != `` {
|
||||
}
|
||||
return out
|
||||
})
|
||||
}
|
@ -12,7 +12,7 @@ import (
|
||||
func loadMusicFree(mf *gin.RouterGroup) {
|
||||
// 插件订阅
|
||||
mf.GET(`/subscribe`, func(c *gin.Context) {
|
||||
slist := []string{sources.S_wy, sources.S_mg, sources.S_kw, sources.S_kg, sources.S_tx, sources.S_lx}
|
||||
slist := sources.S_al
|
||||
type plugins struct {
|
||||
Name string `json:"name"`
|
||||
Url string `json:"url"`
|
||||
|
@ -1,6 +1,9 @@
|
||||
package server
|
||||
|
||||
import "lx-source/src/env"
|
||||
import (
|
||||
"lx-source/src/env"
|
||||
"lx-source/src/sources"
|
||||
)
|
||||
|
||||
var (
|
||||
// 默认音质
|
||||
@ -17,40 +20,44 @@ func loadQMap() [][]string {
|
||||
// 0.wy
|
||||
if env.Config.Source.Enable_Wy {
|
||||
if env.Config.Custom.Wy_Enable {
|
||||
m[0] = defQuality
|
||||
m[sources.I_wy] = defQuality
|
||||
} else {
|
||||
m[0] = tstQuality
|
||||
m[sources.I_wy] = tstQuality
|
||||
}
|
||||
}
|
||||
// 1.mg
|
||||
if env.Config.Source.Enable_Mg {
|
||||
if env.Config.Custom.Mg_Enable {
|
||||
m[1] = defQuality
|
||||
m[sources.I_mg] = defQuality
|
||||
} else {
|
||||
m[sources.I_mg] = tstQuality
|
||||
}
|
||||
}
|
||||
// 2.kw
|
||||
if env.Config.Source.Enable_Kw {
|
||||
if env.Config.Custom.Kw_Enable {
|
||||
m[2] = stdQuality
|
||||
m[sources.I_kw] = stdQuality
|
||||
}
|
||||
}
|
||||
// 3.kg
|
||||
if env.Config.Source.Enable_Kg {
|
||||
if env.Config.Custom.Kg_Enable {
|
||||
m[3] = tstQuality
|
||||
m[sources.I_kg] = defQuality
|
||||
} else {
|
||||
m[sources.I_kg] = tstQuality
|
||||
}
|
||||
}
|
||||
// 4.tx
|
||||
if env.Config.Source.Enable_Tx {
|
||||
if env.Config.Custom.Tx_Enable {
|
||||
m[4] = stdQuality
|
||||
m[sources.I_tx] = stdQuality
|
||||
} else {
|
||||
m[4] = tstQuality
|
||||
m[sources.I_tx] = tstQuality
|
||||
}
|
||||
}
|
||||
// 5.lx
|
||||
// if env.Config.Source.Enable_Lx {
|
||||
// m[5] = defQuality
|
||||
// }
|
||||
if env.Config.Source.Enable_Lx {
|
||||
m[sources.I_lx] = defQuality
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ const devmode = true // 调试模式
|
||||
// const timeout = 60 * 1000 // 请求超时(ms)
|
||||
|
||||
// 常量 & 默认值
|
||||
const { EVENT_NAMES, request, on, send } = window.lx ?? globalThis.lx
|
||||
const { EVENT_NAMES, request, on, send } = globalThis.lx
|
||||
const defs = { type: 'music', actions: ['musicUrl'] }
|
||||
const defheaders = {
|
||||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.84 Safari/537.36 HBPC/12.1.2.300',
|
||||
@ -45,8 +45,8 @@ const httpRequest = (url, options) => new Promise((resolve, reject) => {
|
||||
const musicUrl = async (source, info, quality) => {
|
||||
const start = new Date().getTime();
|
||||
const id = info.hash ?? info.copyrightId ?? info.songmid // 音乐id kg源为hash, mg源为copyrightId
|
||||
const ext = source == 'kg' ? info.albumId : '' //source == 'tx' ? info.strMediaMid
|
||||
const query = `${source}/${id}${(ext != '' && ext != void 0) ? '-' + ext : ''}/${quality}`
|
||||
//const ext = source == 'kg' ? info.albumId : '' //source == 'tx' ? info.strMediaMid
|
||||
const query = `${source}/${id}/${quality}` //${(ext != '' && ext != void 0) ? '-' + ext : ''}
|
||||
console.log('创建任务: %s, 音乐信息: %O', query, info)
|
||||
const body = await httpRequest(`${apiaddr}link/${query}`, { method: 'get' });
|
||||
console.log('返回数据: %O', body, `, 耗时 ${new Date().getTime() - start} ms`)
|
||||
|
@ -9,12 +9,19 @@ import (
|
||||
"lx-source/src/middleware/util"
|
||||
"lx-source/src/sources"
|
||||
"net/http"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/ZxwyWebSite/ztool"
|
||||
"github.com/gin-contrib/gzip"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
var (
|
||||
reqnum int64
|
||||
secnum int64
|
||||
)
|
||||
|
||||
// 载入路由
|
||||
func InitRouter() *gin.Engine {
|
||||
r := gin.Default()
|
||||
@ -23,6 +30,7 @@ func InitRouter() *gin.Engine {
|
||||
if env.Config.Main.Gzip {
|
||||
r.Use(gzip.Gzip(gzip.DefaultCompression, gzip.WithExcludedPaths([]string{"/file/"})))
|
||||
}
|
||||
startime := time.Now().Unix()
|
||||
// 源信息
|
||||
r.GET(`/`, func(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
@ -34,15 +42,21 @@ func InitRouter() *gin.Engine {
|
||||
// `github`: `https://github.com/ZxwyWebSite/lx-source`,
|
||||
// 可用平台
|
||||
`source`: gin.H{
|
||||
sources.S_wy: qmap[0],
|
||||
sources.S_mg: qmap[1],
|
||||
sources.S_kw: qmap[2],
|
||||
sources.S_kg: qmap[3],
|
||||
sources.S_tx: qmap[4],
|
||||
sources.S_lx: qmap[5],
|
||||
sources.S_wy: qmap[sources.I_wy],
|
||||
sources.S_mg: qmap[sources.I_mg],
|
||||
sources.S_kw: qmap[sources.I_kw],
|
||||
sources.S_kg: qmap[sources.I_kg],
|
||||
sources.S_tx: qmap[sources.I_tx],
|
||||
sources.S_lx: qmap[sources.I_lx],
|
||||
},
|
||||
// 自定义源脚本更新
|
||||
`script`: env.DefCfg.Script, //env.Config.Script,
|
||||
// 数据统计
|
||||
`summary`: gin.H{
|
||||
`StartAt`: startime, // 启动时间
|
||||
`Request`: reqnum, // 解析次数
|
||||
`Success`: secnum, // 成功次数
|
||||
},
|
||||
})
|
||||
})
|
||||
// 静态文件
|
||||
@ -61,6 +75,7 @@ func InitRouter() *gin.Engine {
|
||||
// r.Static(`/file`, env.Config.Cache.Local_Path)
|
||||
// }
|
||||
// 功能接口
|
||||
// loadMusic(r.Group(`/api`))
|
||||
// api := r.Group(`/api`)
|
||||
// {
|
||||
// api.GET(`/:s/:m/:q`) // {source}/{method}/{query}
|
||||
@ -99,11 +114,13 @@ const (
|
||||
func linkHandler(c *gin.Context) {
|
||||
resp.Wrap(c, func() *resp.Resp {
|
||||
// 获取传入参数 检查合法性
|
||||
parms := util.ParaMap(c)
|
||||
arr := util.ParaArr(c, `s`, `id`, `q`)
|
||||
s, id, q := arr[0], arr[1], arr[2]
|
||||
// parms := util.ParaMap(c)
|
||||
// getParam := func(p string) string { return strings.TrimSuffix(strings.TrimPrefix(c.Param(p), `/`), `/`) } //strings.Trim(c.Param(p), `/`)
|
||||
s := parms[`s`] //c.Param(`s`) //getParam(`s`) // source 平台 wy, mg, kw
|
||||
id := parms[`id`] //c.Param(`id`) //getParam(`id`) // sid 音乐ID wy: songmid, mg: copyrightId
|
||||
q := parms[`q`] //c.Param(`q`) //getParam(`q`) // quality 音质 128k / 320k / flac / flac24bit
|
||||
// s := parms[`s`] //c.Param(`s`) //getParam(`s`) // source 平台 wy, mg, kw
|
||||
// id := parms[`id`] //c.Param(`id`) //getParam(`id`) // sid 音乐ID wy: songmid, mg: copyrightId
|
||||
// q := parms[`q`] //c.Param(`q`) //getParam(`q`) // quality 音质 128k / 320k / flac / flac24bit
|
||||
env.Loger.NewGroup(`LinkQuery`).Debug(`s: %v, id: %v, q: %v`, s, id, q).Free()
|
||||
if ztool.Chk_IsNilStr(s, q, id) {
|
||||
return &resp.Resp{Code: 6, Msg: `参数不全`} // http.StatusBadRequest
|
||||
@ -118,8 +135,7 @@ func linkHandler(c *gin.Context) {
|
||||
// }
|
||||
|
||||
// 查询内存
|
||||
clink, ok := env.Cache.Get(cquery.Query())
|
||||
if ok {
|
||||
if clink, ok := env.Cache.Get(cquery.Query()); ok {
|
||||
if str, ok := clink.(string); ok {
|
||||
env.Loger.NewGroup(`MemCache`).Debug(`MemHIT [%q]=>[%q]`, cquery.Query(), str).Free()
|
||||
if str == `` {
|
||||
@ -144,15 +160,17 @@ func linkHandler(c *gin.Context) {
|
||||
} else {
|
||||
sc.Debug(`Disabled`)
|
||||
}
|
||||
atomic.AddInt64(&reqnum, 1)
|
||||
// 解析歌曲外链
|
||||
outlink, emsg := sources.UseSource.GetLink(cquery)
|
||||
if emsg != `` {
|
||||
if emsg == sources.Err_Verify { // Verify Failed: 不支持的平台或音质
|
||||
return &resp.Resp{Code: 6, Msg: ztool.Str_FastConcat(emsg, `: 不支持的平台或音质`)}
|
||||
}
|
||||
env.Cache.Set(cquery.Query(), ``, 600) // 发生错误的10分钟内禁止再次查询
|
||||
return &resp.Resp{Code: 2, Msg: emsg}
|
||||
env.Cache.Set(cquery.Query(), outlink, 600) // 发生错误的10分钟内禁止再次查询
|
||||
return &resp.Resp{Code: 2, Msg: emsg, Data: outlink}
|
||||
}
|
||||
atomic.AddInt64(&secnum, 1)
|
||||
// 缓存并获取直链 !(s == `kg` || (s == `tx` && !tx_en)) => (s != `kg` && (s != `tx` || tx_en))
|
||||
if outlink != `` && cstat && cquery.Source != sources.S_kg && (cquery.Source != sources.S_tx || env.Config.Custom.Tx_Enable) {
|
||||
sc.Debug(`Method: Set, Link: %v`, outlink)
|
||||
|
@ -5,9 +5,12 @@ import (
|
||||
"lx-source/src/caches"
|
||||
"lx-source/src/env"
|
||||
"lx-source/src/sources"
|
||||
"lx-source/src/sources/custom/kg"
|
||||
"lx-source/src/sources/custom/kw"
|
||||
"lx-source/src/sources/custom/mg"
|
||||
"lx-source/src/sources/custom/tx"
|
||||
"lx-source/src/sources/custom/wy"
|
||||
wm "lx-source/src/sources/custom/wy/modules"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"sync"
|
||||
@ -26,18 +29,21 @@ func (s *Source) Verify(c *caches.Query) (rquery string, ok bool) {
|
||||
|
||||
var (
|
||||
// 并发对象池 (用户限制在Router处实现)
|
||||
wy_pool = &sync.Pool{New: func() any { return new(WyApi_Song) }}
|
||||
wy_pool *sync.Pool
|
||||
mg_pool = &sync.Pool{New: func() any { return new(MgApi_Song) }}
|
||||
// kw_pool = &sync.Pool{New: func() any { return new(KwApi_Song) }}
|
||||
kg_pool = &sync.Pool{New: func() any { return new(KgApi_Song) }}
|
||||
// kg_pool = &sync.Pool{New: func() any { return new(KgApi_Song) }}
|
||||
// tx_pool = &sync.Pool{New: func() any { return new(res_tx) }}
|
||||
wv_pool *sync.Pool
|
||||
)
|
||||
|
||||
func init() {
|
||||
env.Inits.Add(func() {
|
||||
if env.Config.Source.Enable_Wy {
|
||||
wy_pool = &sync.Pool{New: func() any { return new(wm.PlayInfo) }}
|
||||
if env.Config.Source.MusicIdVerify {
|
||||
wv_pool = &sync.Pool{New: func() any { return new(WyApi_Vef) }}
|
||||
wv_pool = &sync.Pool{New: func() any { return new(wm.VerifyInfo) }}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -54,22 +60,17 @@ func (s *Source) GetLink(c *caches.Query) (outlink string, msg string) {
|
||||
defer jx.Free()
|
||||
switch c.Source {
|
||||
case sources.S_wy:
|
||||
if !env.Config.Custom.Wy_Enable {
|
||||
if !env.Config.Source.Enable_Wy {
|
||||
msg = sources.ErrDisable
|
||||
return
|
||||
}
|
||||
if wy.Url != nil {
|
||||
ourl, emsg := wy.Url(c.MusicID, c.Quality)
|
||||
if emsg != `` {
|
||||
msg = emsg
|
||||
return
|
||||
}
|
||||
outlink = ourl
|
||||
outlink, msg = wy.Url(c.MusicID, c.Quality)
|
||||
break
|
||||
}
|
||||
// 可用性验证
|
||||
if env.Config.Source.MusicIdVerify {
|
||||
vef := wv_pool.Get().(*WyApi_Vef)
|
||||
vef := wv_pool.Get().(*wm.VerifyInfo)
|
||||
defer wv_pool.Put(vef)
|
||||
vurl := ztool.Str_FastConcat(`https://`, vef_wy, `&id=`, c.MusicID)
|
||||
_, err := ztool.Net_HttpReq(http.MethodGet, vurl, nil, header_wy, &vef)
|
||||
@ -85,7 +86,7 @@ func (s *Source) GetLink(c *caches.Query) (outlink string, msg string) {
|
||||
}
|
||||
}
|
||||
// 获取外链
|
||||
resp := wy_pool.Get().(*WyApi_Song)
|
||||
resp := wy_pool.Get().(*wm.PlayInfo)
|
||||
defer wy_pool.Put(resp)
|
||||
// 分流逻辑 (暂无其它节点)
|
||||
// urls := [...]string{
|
||||
@ -121,20 +122,30 @@ func (s *Source) GetLink(c *caches.Query) (outlink string, msg string) {
|
||||
var data = resp.Data[0]
|
||||
if data.Code != 200 || data.FreeTrialInfo != nil {
|
||||
// jx.Error("发生错误, 返回数据:\n%#v", resp)
|
||||
msg = `触发风控或专辑单独收费`
|
||||
msg = `触发风控或专辑单独收费: ` + strconv.Itoa(data.Code)
|
||||
return
|
||||
}
|
||||
if data.Level != rquery {
|
||||
msg = ztool.Str_FastConcat(`实际音质不匹配: `, rquery, ` <= `, data.Level) // 实际音质不匹配: exhigh <= standard
|
||||
if !env.Config.Source.ForceFallback {
|
||||
return
|
||||
}
|
||||
}
|
||||
// jx.Info(`WyLink, RealQuality: %v`, data.Level)
|
||||
outlink = data.URL
|
||||
case sources.S_mg:
|
||||
if !env.Config.Custom.Mg_Enable {
|
||||
if !env.Config.Source.Enable_Mg {
|
||||
msg = sources.ErrDisable
|
||||
return
|
||||
}
|
||||
if len(c.MusicID) != 11 {
|
||||
msg = sources.E_VefMusicId
|
||||
return
|
||||
}
|
||||
if mg.Url != nil {
|
||||
outlink, msg = mg.Url(c.MusicID, c.Quality)
|
||||
break
|
||||
}
|
||||
resp := mg_pool.Get().(*MgApi_Song)
|
||||
defer mg_pool.Put(resp)
|
||||
|
||||
@ -153,71 +164,89 @@ func (s *Source) GetLink(c *caches.Query) (outlink string, msg string) {
|
||||
msg = ztool.Str_FastConcat(resp.Code, `: `, resp.Msg)
|
||||
}
|
||||
case sources.S_kw:
|
||||
if !env.Config.Custom.Kw_Enable {
|
||||
if !env.Config.Source.Enable_Kw {
|
||||
msg = sources.ErrDisable
|
||||
return
|
||||
}
|
||||
ourl, emsg := kw.Url(c.MusicID, c.Quality)
|
||||
if emsg != `` {
|
||||
msg = emsg
|
||||
return
|
||||
}
|
||||
outlink = ourl
|
||||
case sources.S_kg:
|
||||
if !env.Config.Custom.Kg_Enable {
|
||||
msg = sources.ErrDisable
|
||||
return
|
||||
}
|
||||
resp := kg_pool.Get().(*KgApi_Song)
|
||||
defer kg_pool.Put(resp)
|
||||
|
||||
// sep := strings.Split(c.MusicID, `-`) // 分割 Hash-Album 如 6DC276334F56E22BE2A0E8254D332B45-13097991
|
||||
// alb := func() string {
|
||||
// if len(sep) >= 2 {
|
||||
// return sep[1]
|
||||
outlink, msg = kw.Url(c.MusicID, c.Quality)
|
||||
// if emsg != `` {
|
||||
// msg = emsg
|
||||
// return
|
||||
// }
|
||||
// return ``
|
||||
// }()
|
||||
sep := c.Split()
|
||||
url := ztool.Str_FastConcat(api_kg, `&hash=`, sep[0], `&album_id=`, sep[1], `&_=`, strconv.FormatInt(time.Now().UnixMilli(), 10))
|
||||
// jx.Debug(`Kg, Url: %s`, url)
|
||||
_, err := ztool.Net_HttpReq(http.MethodGet, url, nil, nil, &resp)
|
||||
if err != nil {
|
||||
jx.Error(`Kg, HttpReq: %s`, err)
|
||||
msg = sources.ErrHttpReq
|
||||
// outlink = ourl
|
||||
case sources.S_kg:
|
||||
if !env.Config.Source.Enable_Kg {
|
||||
msg = sources.ErrDisable
|
||||
return
|
||||
}
|
||||
jx.Debug(`Kg, Resp: %+v`, resp)
|
||||
if resp.ErrCode != 0 {
|
||||
msg = ztool.Str_FastConcat(`Error: `, strconv.Itoa(resp.ErrCode))
|
||||
if len(c.MusicID) != 32 {
|
||||
msg = sources.E_VefMusicId
|
||||
return
|
||||
}
|
||||
var data KgApi_Data
|
||||
err = ztool.Val_MapToStruct(resp.Data, &data)
|
||||
if err != nil {
|
||||
msg = err.Error()
|
||||
return
|
||||
}
|
||||
if data.PlayBackupURL == `` {
|
||||
if data.PlayURL == `` {
|
||||
msg = sources.ErrNoLink
|
||||
return
|
||||
}
|
||||
outlink = data.PlayURL
|
||||
}
|
||||
outlink = data.PlayBackupURL
|
||||
case sources.S_tx:
|
||||
outlink, msg = kg.Url(c.MusicID, c.Quality)
|
||||
// if emsg != `` {
|
||||
// msg = emsg
|
||||
// return
|
||||
// }
|
||||
// outlink = ourl
|
||||
// case sources.S_kg:
|
||||
// if !env.Config.Custom.Kg_Enable {
|
||||
// msg = sources.ErrDisable
|
||||
// return
|
||||
// }
|
||||
// resp := kg_pool.Get().(*KgApi_Song)
|
||||
// defer kg_pool.Put(resp)
|
||||
|
||||
// // sep := strings.Split(c.MusicID, `-`) // 分割 Hash-Album 如 6DC276334F56E22BE2A0E8254D332B45-13097991
|
||||
// // alb := func() string {
|
||||
// // if len(sep) >= 2 {
|
||||
// // return sep[1]
|
||||
// // }
|
||||
// // return ``
|
||||
// // }()
|
||||
// sep := c.Split()
|
||||
// url := ztool.Str_FastConcat(api_kg, `&hash=`, sep[0], `&album_id=`, sep[1], `&_=`, strconv.FormatInt(time.Now().UnixMilli(), 10))
|
||||
// // jx.Debug(`Kg, Url: %s`, url)
|
||||
// _, err := ztool.Net_HttpReq(http.MethodGet, url, nil, nil, &resp)
|
||||
// if err != nil {
|
||||
// jx.Error(`Kg, HttpReq: %s`, err)
|
||||
// msg = sources.ErrHttpReq
|
||||
// return
|
||||
// }
|
||||
// jx.Debug(`Kg, Resp: %+v`, resp)
|
||||
// if resp.ErrCode != 0 {
|
||||
// msg = ztool.Str_FastConcat(`Error: `, strconv.Itoa(resp.ErrCode))
|
||||
// return
|
||||
// }
|
||||
// var data KgApi_Data
|
||||
// err = ztool.Val_MapToStruct(resp.Data, &data)
|
||||
// if err != nil {
|
||||
// msg = err.Error()
|
||||
// return
|
||||
// }
|
||||
// if data.PlayBackupURL == `` {
|
||||
// if data.PlayURL == `` {
|
||||
// msg = sources.ErrNoLink
|
||||
// return
|
||||
// }
|
||||
// outlink = data.PlayURL
|
||||
// }
|
||||
// outlink = data.PlayBackupURL
|
||||
case sources.S_tx:
|
||||
if !env.Config.Source.Enable_Tx {
|
||||
msg = sources.ErrDisable
|
||||
return
|
||||
}
|
||||
if len(c.MusicID) != 14 {
|
||||
msg = sources.E_VefMusicId
|
||||
return
|
||||
}
|
||||
ourl, emsg := tx.Url(c.MusicID, c.Quality)
|
||||
if emsg != `` {
|
||||
msg = emsg
|
||||
return
|
||||
}
|
||||
outlink = ourl
|
||||
outlink, msg = tx.Url(c.MusicID, c.Quality)
|
||||
// if emsg != `` {
|
||||
// msg = emsg
|
||||
// return
|
||||
// }
|
||||
// outlink = ourl
|
||||
// case `otx`:
|
||||
// resp := tx_pool.Get().(*res_tx)
|
||||
// defer tx_pool.Put(resp)
|
||||
|
@ -10,52 +10,52 @@ import (
|
||||
|
||||
type (
|
||||
// 网易音乐接口 (方格/简繁)
|
||||
WyApi_Song struct {
|
||||
Data []struct {
|
||||
ID int `json:"id"`
|
||||
URL string `json:"url"`
|
||||
Br int `json:"br"`
|
||||
Size int `json:"size"`
|
||||
Md5 string `json:"md5"`
|
||||
Code int `json:"code"`
|
||||
Expi int `json:"expi"`
|
||||
Type string `json:"type"`
|
||||
Gain float64 `json:"gain"`
|
||||
Peak float64 `json:"peak"`
|
||||
Fee int `json:"fee"`
|
||||
Uf interface{} `json:"uf"`
|
||||
Payed int `json:"payed"`
|
||||
Flag int `json:"flag"`
|
||||
CanExtend bool `json:"canExtend"`
|
||||
FreeTrialInfo interface{} `json:"freeTrialInfo"`
|
||||
Level string `json:"level"`
|
||||
EncodeType string `json:"encodeType"`
|
||||
FreeTrialPrivilege struct {
|
||||
ResConsumable bool `json:"resConsumable"`
|
||||
UserConsumable bool `json:"userConsumable"`
|
||||
ListenType interface{} `json:"listenType"`
|
||||
CannotListenReason interface{} `json:"cannotListenReason"`
|
||||
PlayReason interface{} `json:"playReason"`
|
||||
} `json:"freeTrialPrivilege"`
|
||||
FreeTimeTrialPrivilege struct {
|
||||
ResConsumable bool `json:"resConsumable"`
|
||||
UserConsumable bool `json:"userConsumable"`
|
||||
Type int `json:"type"`
|
||||
RemainTime int `json:"remainTime"`
|
||||
} `json:"freeTimeTrialPrivilege"`
|
||||
URLSource int `json:"urlSource"`
|
||||
RightSource int `json:"rightSource"`
|
||||
PodcastCtrp interface{} `json:"podcastCtrp"`
|
||||
EffectTypes interface{} `json:"effectTypes"`
|
||||
Time int `json:"time"`
|
||||
} `json:"data"`
|
||||
Code int `json:"code"`
|
||||
}
|
||||
WyApi_Vef struct {
|
||||
Code int16 `json:"code"`
|
||||
Success bool `json:"success"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
// WyApi_Song struct {
|
||||
// Data []struct {
|
||||
// ID int `json:"id"`
|
||||
// URL string `json:"url"`
|
||||
// Br int `json:"br"`
|
||||
// Size int `json:"size"`
|
||||
// Md5 string `json:"md5"`
|
||||
// Code int `json:"code"`
|
||||
// Expi int `json:"expi"`
|
||||
// Type string `json:"type"`
|
||||
// Gain float64 `json:"gain"`
|
||||
// Peak float64 `json:"peak"`
|
||||
// Fee int `json:"fee"`
|
||||
// Uf interface{} `json:"uf"`
|
||||
// Payed int `json:"payed"`
|
||||
// Flag int `json:"flag"`
|
||||
// CanExtend bool `json:"canExtend"`
|
||||
// FreeTrialInfo interface{} `json:"freeTrialInfo"`
|
||||
// Level string `json:"level"`
|
||||
// EncodeType string `json:"encodeType"`
|
||||
// FreeTrialPrivilege struct {
|
||||
// ResConsumable bool `json:"resConsumable"`
|
||||
// UserConsumable bool `json:"userConsumable"`
|
||||
// ListenType interface{} `json:"listenType"`
|
||||
// CannotListenReason interface{} `json:"cannotListenReason"`
|
||||
// PlayReason interface{} `json:"playReason"`
|
||||
// } `json:"freeTrialPrivilege"`
|
||||
// FreeTimeTrialPrivilege struct {
|
||||
// ResConsumable bool `json:"resConsumable"`
|
||||
// UserConsumable bool `json:"userConsumable"`
|
||||
// Type int `json:"type"`
|
||||
// RemainTime int `json:"remainTime"`
|
||||
// } `json:"freeTimeTrialPrivilege"`
|
||||
// URLSource int `json:"urlSource"`
|
||||
// RightSource int `json:"rightSource"`
|
||||
// PodcastCtrp interface{} `json:"podcastCtrp"`
|
||||
// EffectTypes interface{} `json:"effectTypes"`
|
||||
// Time int `json:"time"`
|
||||
// } `json:"data"`
|
||||
// Code int `json:"code"`
|
||||
// }
|
||||
// WyApi_Vef struct {
|
||||
// Code int16 `json:"code"`
|
||||
// Success bool `json:"success"`
|
||||
// Message string `json:"message"`
|
||||
// }
|
||||
// 咪咕音乐接口
|
||||
MgApi_Song struct {
|
||||
Code string `json:"code"`
|
||||
@ -88,52 +88,52 @@ type (
|
||||
// CurTime int64 `json:"curTime"`
|
||||
// }
|
||||
// 酷狗试听接口
|
||||
KgApi_Song struct {
|
||||
Status int `json:"status"`
|
||||
ErrCode int `json:"err_code"`
|
||||
Data any `json:"data"`
|
||||
}
|
||||
KgApi_Data struct {
|
||||
// Hash string `json:"hash"`
|
||||
// Timelength int `json:"timelength"`
|
||||
// Filesize int `json:"filesize"`
|
||||
// AudioName string `json:"audio_name"`
|
||||
// HaveAlbum int `json:"have_album"`
|
||||
// AlbumName string `json:"album_name"`
|
||||
// AlbumID any `json:"album_id"`
|
||||
// Img string `json:"img"`
|
||||
// HaveMv int `json:"have_mv"`
|
||||
// VideoID any `json:"video_id"`
|
||||
// AuthorName string `json:"author_name"`
|
||||
// SongName string `json:"song_name"`
|
||||
// Lyrics string `json:"lyrics"`
|
||||
// AuthorID any `json:"author_id"`
|
||||
// Privilege int `json:"privilege"`
|
||||
// Privilege2 string `json:"privilege2"`
|
||||
PlayURL string `json:"play_url"`
|
||||
// Authors []struct {
|
||||
// AuthorID any `json:"author_id"`
|
||||
// AuthorName string `json:"author_name"`
|
||||
// IsPublish string `json:"is_publish"`
|
||||
// SizableAvatar string `json:"sizable_avatar"`
|
||||
// EAuthorID string `json:"e_author_id"`
|
||||
// Avatar string `json:"avatar"`
|
||||
// } `json:"authors"`
|
||||
// IsFreePart int `json:"is_free_part"`
|
||||
// Bitrate int `json:"bitrate"`
|
||||
// RecommendAlbumID string `json:"recommend_album_id"`
|
||||
// StoreType string `json:"store_type"`
|
||||
// AlbumAudioID int `json:"album_audio_id"`
|
||||
// IsPublish int `json:"is_publish"`
|
||||
// EAuthorID string `json:"e_author_id"`
|
||||
// AudioID any `json:"audio_id"`
|
||||
// HasPrivilege bool `json:"has_privilege"`
|
||||
PlayBackupURL string `json:"play_backup_url"`
|
||||
// SmallLibrarySong int `json:"small_library_song"`
|
||||
// EncodeAlbumID string `json:"encode_album_id"`
|
||||
// EncodeAlbumAudioID string `json:"encode_album_audio_id"`
|
||||
// EVideoID string `json:"e_video_id"`
|
||||
}
|
||||
// KgApi_Song struct {
|
||||
// Status int `json:"status"`
|
||||
// ErrCode int `json:"err_code"`
|
||||
// Data any `json:"data"`
|
||||
// }
|
||||
// KgApi_Data struct {
|
||||
// // Hash string `json:"hash"`
|
||||
// // Timelength int `json:"timelength"`
|
||||
// // Filesize int `json:"filesize"`
|
||||
// // AudioName string `json:"audio_name"`
|
||||
// // HaveAlbum int `json:"have_album"`
|
||||
// // AlbumName string `json:"album_name"`
|
||||
// // AlbumID any `json:"album_id"`
|
||||
// // Img string `json:"img"`
|
||||
// // HaveMv int `json:"have_mv"`
|
||||
// // VideoID any `json:"video_id"`
|
||||
// // AuthorName string `json:"author_name"`
|
||||
// // SongName string `json:"song_name"`
|
||||
// // Lyrics string `json:"lyrics"`
|
||||
// // AuthorID any `json:"author_id"`
|
||||
// // Privilege int `json:"privilege"`
|
||||
// // Privilege2 string `json:"privilege2"`
|
||||
// PlayURL string `json:"play_url"`
|
||||
// // Authors []struct {
|
||||
// // AuthorID any `json:"author_id"`
|
||||
// // AuthorName string `json:"author_name"`
|
||||
// // IsPublish string `json:"is_publish"`
|
||||
// // SizableAvatar string `json:"sizable_avatar"`
|
||||
// // EAuthorID string `json:"e_author_id"`
|
||||
// // Avatar string `json:"avatar"`
|
||||
// // } `json:"authors"`
|
||||
// // IsFreePart int `json:"is_free_part"`
|
||||
// // Bitrate int `json:"bitrate"`
|
||||
// // RecommendAlbumID string `json:"recommend_album_id"`
|
||||
// // StoreType string `json:"store_type"`
|
||||
// // AlbumAudioID int `json:"album_audio_id"`
|
||||
// // IsPublish int `json:"is_publish"`
|
||||
// // EAuthorID string `json:"e_author_id"`
|
||||
// // AudioID any `json:"audio_id"`
|
||||
// // HasPrivilege bool `json:"has_privilege"`
|
||||
// PlayBackupURL string `json:"play_backup_url"`
|
||||
// // SmallLibrarySong int `json:"small_library_song"`
|
||||
// // EncodeAlbumID string `json:"encode_album_id"`
|
||||
// // EncodeAlbumAudioID string `json:"encode_album_audio_id"`
|
||||
// // EVideoID string `json:"e_video_id"`
|
||||
// }
|
||||
// 腾讯试听接口
|
||||
// res_tx struct {
|
||||
// Code int `json:"code"`
|
||||
@ -265,7 +265,7 @@ var (
|
||||
api_wy string
|
||||
api_mg string
|
||||
// api_kw string
|
||||
api_kg string = `https://wwwapi.kugou.com/yy/index.php?r=play/getdata&platid=4&mid=1`
|
||||
// api_kg string = `https://wwwapi.kugou.com/yy/index.php?r=play/getdata&platid=4&mid=1`
|
||||
// api_tx string = `https://u.y.qq.com/cgi-bin/musicu.fcg?data=`
|
||||
vef_wy string
|
||||
// Headers
|
||||
|
3
src/sources/custom/custom.go
Normal file
3
src/sources/custom/custom.go
Normal file
@ -0,0 +1,3 @@
|
||||
package custom
|
||||
|
||||
// type CSource struct{}
|
@ -1,2 +1,101 @@
|
||||
// 账号解析源
|
||||
package custom
|
||||
|
||||
import (
|
||||
"lx-source/src/env"
|
||||
"lx-source/src/sources/custom/kg"
|
||||
"lx-source/src/sources/custom/kw"
|
||||
"lx-source/src/sources/custom/mg"
|
||||
"lx-source/src/sources/custom/tx"
|
||||
"lx-source/src/sources/custom/wy"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type (
|
||||
// 源定义
|
||||
UrlFunc func(string, string) (string, string)
|
||||
LrcFunc func(string) (string, string)
|
||||
PicFunc func(string) (string, string)
|
||||
VefFunc func(string) bool
|
||||
// 源接口
|
||||
Source interface {
|
||||
Url(string, string) (string, string) // 外链
|
||||
Lrc(string) (string, string) // 歌词
|
||||
Pic(string) (string, string) // 封面
|
||||
Vef(string) bool // 验证
|
||||
}
|
||||
)
|
||||
|
||||
func notSupport(string) (string, string) { return ``, `不支持的平台或功能` }
|
||||
|
||||
// 接口封装
|
||||
type WrapSource struct {
|
||||
UrlFunc
|
||||
LrcFunc
|
||||
PicFunc
|
||||
VefFunc
|
||||
}
|
||||
|
||||
func (ws *WrapSource) Url(songMid, quality string) (string, string) {
|
||||
return ws.UrlFunc(songMid, quality)
|
||||
}
|
||||
func (ws *WrapSource) Lrc(songMid string) (string, string) {
|
||||
return ws.LrcFunc(songMid)
|
||||
}
|
||||
func (ws *WrapSource) Pic(songMid string) (string, string) {
|
||||
return ws.PicFunc(songMid)
|
||||
}
|
||||
func (ws *WrapSource) Vef(songMid string) bool {
|
||||
return ws.VefFunc(songMid)
|
||||
}
|
||||
|
||||
var (
|
||||
WySource Source
|
||||
MgSource Source
|
||||
KwSource Source
|
||||
KgSource Source
|
||||
TxSource Source
|
||||
LxSource Source
|
||||
)
|
||||
|
||||
func init() {
|
||||
env.Inits.Add(func() {
|
||||
WySource = &WrapSource{
|
||||
UrlFunc: wy.Url,
|
||||
LrcFunc: notSupport,
|
||||
PicFunc: notSupport,
|
||||
VefFunc: func(songMid string) bool {
|
||||
_, err := strconv.ParseUint(songMid, 10, 0)
|
||||
return err == nil
|
||||
},
|
||||
}
|
||||
MgSource = &WrapSource{
|
||||
UrlFunc: mg.Url,
|
||||
LrcFunc: notSupport,
|
||||
PicFunc: notSupport,
|
||||
VefFunc: func(songMid string) bool { return len(songMid) == 11 },
|
||||
}
|
||||
KwSource = &WrapSource{
|
||||
UrlFunc: kw.Url,
|
||||
LrcFunc: notSupport,
|
||||
PicFunc: notSupport,
|
||||
VefFunc: func(songMid string) bool {
|
||||
_, err := strconv.ParseUint(songMid, 10, 0)
|
||||
return err == nil
|
||||
},
|
||||
}
|
||||
KgSource = &WrapSource{
|
||||
UrlFunc: kg.Url,
|
||||
LrcFunc: notSupport,
|
||||
PicFunc: notSupport,
|
||||
VefFunc: func(songMid string) bool { return len(songMid) == 32 },
|
||||
}
|
||||
TxSource = &WrapSource{
|
||||
UrlFunc: tx.Url,
|
||||
LrcFunc: notSupport,
|
||||
PicFunc: notSupport,
|
||||
VefFunc: func(songMid string) bool { return len(songMid) == 14 },
|
||||
}
|
||||
LxSource = nil
|
||||
})
|
||||
}
|
||||
|
53
src/sources/custom/kg/musicinfo.go
Normal file
53
src/sources/custom/kg/musicinfo.go
Normal file
@ -0,0 +1,53 @@
|
||||
package kg
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/ZxwyWebSite/ztool"
|
||||
)
|
||||
|
||||
func getMusicInfo(hash_ string) (info musicInfo, emsg string) {
|
||||
body := ztool.Str_FastConcat(
|
||||
`{"area_code":"1","show_privilege":"1","show_album_info":"1","is_publish":"","appid":1005,"clientver":11451,"mid":"211008","dfid":"-","clienttime":"`,
|
||||
strconv.FormatInt(time.Now().Unix(), 10),
|
||||
`","key":"OIlwlieks28dk2k092lksi2UIkp","data":[{"hash":"`,
|
||||
hash_,
|
||||
`"}]}`,
|
||||
)
|
||||
var infoResp struct {
|
||||
Status int `json:"status"`
|
||||
ErrorCode int `json:"error_code"`
|
||||
Errmsg string `json:"errmsg"`
|
||||
Data [][]musicInfo `json:"data"`
|
||||
}
|
||||
err := ztool.Net_Request(
|
||||
http.MethodPost,
|
||||
`http://gateway.kugou.com/v3/album_audio/audio`,
|
||||
strings.NewReader(body),
|
||||
[]ztool.Net_ReqHandlerFunc{ztool.Net_ReqAddHeader(map[string]string{
|
||||
`KG-THash`: `13a3164`,
|
||||
`KG-RC`: `1`,
|
||||
`KG-Fake`: `0`,
|
||||
`KG-RF`: `00869891`,
|
||||
`User-Agent`: `Android712-AndroidPhone-11451-376-0-FeeCacheUpdate-wifi`,
|
||||
`x-router`: `kmr.service.kugou.com`,
|
||||
})},
|
||||
[]ztool.Net_ResHandlerFunc{ztool.Net_ResToStruct(&infoResp)},
|
||||
)
|
||||
if err != nil {
|
||||
emsg = err.Error()
|
||||
return
|
||||
}
|
||||
if len(infoResp.Data) == 0 {
|
||||
if infoResp.Errmsg != `` {
|
||||
emsg = infoResp.Errmsg
|
||||
} else {
|
||||
emsg = `No Data`
|
||||
}
|
||||
return
|
||||
}
|
||||
return infoResp.Data[0][0], infoResp.Errmsg
|
||||
}
|
100
src/sources/custom/kg/player.go
Normal file
100
src/sources/custom/kg/player.go
Normal file
@ -0,0 +1,100 @@
|
||||
package kg
|
||||
|
||||
import (
|
||||
"lx-source/src/env"
|
||||
"lx-source/src/sources"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func Url(songMid, quality string) (ourl, msg string) {
|
||||
loger := env.Loger.NewGroup(`Kg`)
|
||||
defer loger.Free()
|
||||
rquality, ok := qualityMap[quality]
|
||||
if !ok {
|
||||
msg = sources.E_QNotSupport
|
||||
return
|
||||
}
|
||||
info, emsg := getMusicInfo(strings.ToLower(songMid))
|
||||
if emsg != `` {
|
||||
loger.Error(`GetInfo: %v`, emsg)
|
||||
msg = emsg
|
||||
return
|
||||
}
|
||||
loger.Debug(`Info: %+v`, info)
|
||||
var tHash string
|
||||
switch quality {
|
||||
case sources.Q_128k:
|
||||
tHash = info.AudioInfo.Hash128
|
||||
case sources.Q_320k:
|
||||
tHash = info.AudioInfo.Hash320
|
||||
case sources.Q_flac:
|
||||
tHash = info.AudioInfo.HashFlac
|
||||
case sources.Q_fl24:
|
||||
tHash = info.AudioInfo.HashHigh
|
||||
}
|
||||
if tHash == `` {
|
||||
msg = sources.E_QNotMatch
|
||||
return
|
||||
}
|
||||
tHash = strings.ToLower(tHash)
|
||||
now := time.Now()
|
||||
params := map[string]string{
|
||||
`album_id`: info.AlbumInfo.AlbumID,
|
||||
`userid`: env.Config.Custom.Kg_userId,
|
||||
`area_code`: `1`,
|
||||
`hash`: tHash,
|
||||
`module`: ``,
|
||||
`mid`: mid,
|
||||
`appid`: appid,
|
||||
`ssa_flag`: `is_fromtrack`,
|
||||
`clientver`: clientver,
|
||||
`open_time`: now.Format(`20060102`),
|
||||
`vipType`: `6`,
|
||||
`ptype`: `0`,
|
||||
`token`: env.Config.Custom.Kg_token,
|
||||
`auth`: ``,
|
||||
`mtype`: `0`,
|
||||
`album_audio_id`: info.AlbumAudioID,
|
||||
`behavior`: `play`,
|
||||
`clienttime`: strconv.FormatInt(now.Unix(), 10),
|
||||
`pid`: `2`,
|
||||
`key`: getKey(tHash),
|
||||
`dfid`: `-`,
|
||||
`pidversion`: `3001`,
|
||||
|
||||
`quality`: rquality,
|
||||
`IsFreePart`: `1`,
|
||||
}
|
||||
headers := map[string]string{
|
||||
`User-Agent`: `Android712-AndroidPhone-8983-18-0-NetMusic-wifi`,
|
||||
`KG-THash`: `3e5ec6b`,
|
||||
`KG-Rec`: `1`,
|
||||
`KG-RC`: `1`,
|
||||
|
||||
`x-router`: `tracker.kugou.com`,
|
||||
}
|
||||
var resp playInfo
|
||||
err := signRequest(url, params, headers, &resp)
|
||||
if err != nil {
|
||||
loger.Error(`Request: %s`, err)
|
||||
msg = sources.ErrHttpReq
|
||||
return
|
||||
}
|
||||
loger.Debug(`Resp: %+v`, resp)
|
||||
switch resp.Status {
|
||||
case 3:
|
||||
msg = `该歌曲在酷狗没有版权,请换源播放`
|
||||
case 2:
|
||||
msg = `链接获取失败:请检查账号是否有会员或数字专辑是否购买`
|
||||
}
|
||||
if resp.Status != 1 {
|
||||
if msg == `` {
|
||||
msg = `链接获取失败,可能是数字专辑或者api失效,Status: ` + strconv.Itoa(resp.Status)
|
||||
}
|
||||
return
|
||||
}
|
||||
ourl = resp.URL[len(resp.URL)-1]
|
||||
return
|
||||
}
|
158
src/sources/custom/kg/types.go
Normal file
158
src/sources/custom/kg/types.go
Normal file
@ -0,0 +1,158 @@
|
||||
package kg
|
||||
|
||||
type (
|
||||
musicInfo struct {
|
||||
AlbumAudioID string `json:"album_audio_id"`
|
||||
AudioID string `json:"audio_id"`
|
||||
AuthorName string `json:"author_name"`
|
||||
OriAudioName string `json:"ori_audio_name"`
|
||||
OfficialSongname string `json:"official_songname"`
|
||||
Songname string `json:"songname"`
|
||||
Remark string `json:"remark"`
|
||||
SuffixAudioName string `json:"suffix_audio_name"`
|
||||
IsSearch string `json:"is_search"`
|
||||
IsOriginal string `json:"is_original"`
|
||||
IsPublish string `json:"is_publish"`
|
||||
MixsongType string `json:"mixsong_type"`
|
||||
Version string `json:"version"`
|
||||
Language string `json:"language"`
|
||||
Bpm string `json:"bpm"`
|
||||
BpmType string `json:"bpm_type"`
|
||||
BpmDesc string `json:"bpm_desc"`
|
||||
PublishDate string `json:"publish_date"`
|
||||
Extname string `json:"extname"`
|
||||
AlbumInfo struct {
|
||||
AlbumID string `json:"album_id"`
|
||||
AlbumName string `json:"album_name"`
|
||||
PublishDate string `json:"publish_date"`
|
||||
Category string `json:"category"`
|
||||
IsPublish string `json:"is_publish"`
|
||||
SizableCover string `json:"sizable_cover"`
|
||||
} `json:"album_info"`
|
||||
Classification []struct {
|
||||
Usage string `json:"usage"`
|
||||
Status string `json:"status"`
|
||||
Level string `json:"level"`
|
||||
ResType string `json:"res_type"`
|
||||
Type string `json:"type"`
|
||||
ID string `json:"id"`
|
||||
ResID string `json:"res_id"`
|
||||
} `json:"classification"`
|
||||
AudioInfo struct {
|
||||
IsFileHead string `json:"is_file_head"`
|
||||
IsFileHead320 string `json:"is_file_head_320"`
|
||||
AudioID string `json:"audio_id"`
|
||||
Hash string `json:"hash"`
|
||||
Filesize string `json:"filesize"`
|
||||
Timelength string `json:"timelength"`
|
||||
Bitrate string `json:"bitrate"`
|
||||
Hash128 string `json:"hash_128"`
|
||||
Filesize128 string `json:"filesize_128"`
|
||||
Timelength128 string `json:"timelength_128"`
|
||||
Hash320 string `json:"hash_320"`
|
||||
Filesize320 string `json:"filesize_320"`
|
||||
Timelength320 string `json:"timelength_320"`
|
||||
HashFlac string `json:"hash_flac"`
|
||||
FilesizeFlac string `json:"filesize_flac"`
|
||||
TimelengthFlac string `json:"timelength_flac"`
|
||||
BitrateFlac string `json:"bitrate_flac"`
|
||||
HashHigh string `json:"hash_high"`
|
||||
FilesizeHigh string `json:"filesize_high"`
|
||||
TimelengthHigh string `json:"timelength_high"`
|
||||
BitrateHigh string `json:"bitrate_high"`
|
||||
HashSuper string `json:"hash_super"`
|
||||
FilesizeSuper string `json:"filesize_super"`
|
||||
TimelengthSuper string `json:"timelength_super"`
|
||||
BitrateSuper string `json:"bitrate_super"`
|
||||
HashVinylrecord string `json:"hash_vinylrecord"`
|
||||
FilesizeVinylrecord string `json:"filesize_vinylrecord"`
|
||||
TimelengthVinylrecord string `json:"timelength_vinylrecord"`
|
||||
BitrateVinylrecord string `json:"bitrate_vinylrecord"`
|
||||
HashMultichannel string `json:"hash_multichannel"`
|
||||
FilesizeMultichannel string `json:"filesize_multichannel"`
|
||||
TimelengthMultichannel string `json:"timelength_multichannel"`
|
||||
BitrateMultichannel string `json:"bitrate_multichannel"`
|
||||
HashDolby448 string `json:"hash_dolby_448"`
|
||||
FilesizeDolby448 string `json:"filesize_dolby_448"`
|
||||
TimelengthDolby448 string `json:"timelength_dolby_448"`
|
||||
BitrateDolby448 string `json:"bitrate_dolby_448"`
|
||||
HashDolby640 string `json:"hash_dolby_640"`
|
||||
FilesizeDolby640 string `json:"filesize_dolby_640"`
|
||||
TimelengthDolby640 string `json:"timelength_dolby_640"`
|
||||
BitrateDolby640 string `json:"bitrate_dolby_640"`
|
||||
HashDolby768 string `json:"hash_dolby_768"`
|
||||
FilesizeDolby768 string `json:"filesize_dolby_768"`
|
||||
TimelengthDolby768 string `json:"timelength_dolby_768"`
|
||||
BitrateDolby768 string `json:"bitrate_dolby_768"`
|
||||
AudioGroupID string `json:"audio_group_id"`
|
||||
ExtnameSuper string `json:"extname_super"`
|
||||
Extname string `json:"extname"`
|
||||
FailProcess int `json:"fail_process"`
|
||||
PayType int `json:"pay_type"`
|
||||
Type string `json:"type"`
|
||||
OldCpy int `json:"old_cpy"`
|
||||
Privilege string `json:"privilege"`
|
||||
Privilege128 string `json:"privilege_128"`
|
||||
Privilege320 string `json:"privilege_320"`
|
||||
PrivilegeFlac string `json:"privilege_flac"`
|
||||
PrivilegeHigh string `json:"privilege_high"`
|
||||
PrivilegeSuper string `json:"privilege_super"`
|
||||
PrivilegeVinylrecord string `json:"privilege_vinylrecord"`
|
||||
PrivilegeMultichannel string `json:"privilege_multichannel"`
|
||||
PrivilegeDolby448 string `json:"privilege_dolby_448"`
|
||||
PrivilegeDolby640 string `json:"privilege_dolby_640"`
|
||||
PrivilegeDolby768 string `json:"privilege_dolby_768"`
|
||||
TransParam struct {
|
||||
HashOffset struct {
|
||||
StartByte int `json:"start_byte"`
|
||||
EndByte int `json:"end_byte"`
|
||||
StartMs int `json:"start_ms"`
|
||||
EndMs int `json:"end_ms"`
|
||||
OffsetHash string `json:"offset_hash"`
|
||||
FileType int `json:"file_type"`
|
||||
ClipHash string `json:"clip_hash"`
|
||||
} `json:"hash_offset"`
|
||||
MusicpackAdvance int `json:"musicpack_advance"`
|
||||
PayBlockTpl int `json:"pay_block_tpl"`
|
||||
Display int `json:"display"`
|
||||
DisplayRate int `json:"display_rate"`
|
||||
CpyGrade int `json:"cpy_grade"`
|
||||
CpyLevel int `json:"cpy_level"`
|
||||
Cid int `json:"cid"`
|
||||
CpyAttr0 int `json:"cpy_attr0"`
|
||||
Classmap struct {
|
||||
Attr0 int `json:"attr0"`
|
||||
} `json:"classmap"`
|
||||
InitPubDay int `json:"init_pub_day"`
|
||||
Qualitymap struct {
|
||||
Attr0 int `json:"attr0"`
|
||||
} `json:"qualitymap"`
|
||||
Language string `json:"language"`
|
||||
} `json:"trans_param"`
|
||||
} `json:"audio_info"`
|
||||
}
|
||||
playInfo struct {
|
||||
Hash string `json:"hash"`
|
||||
Classmap struct {
|
||||
Attr0 int `json:"attr0"`
|
||||
} `json:"classmap"`
|
||||
Status int `json:"status"`
|
||||
Volume float64 `json:"volume"`
|
||||
StdHashTime int `json:"std_hash_time"`
|
||||
URL []string `json:"url"`
|
||||
StdHash string `json:"std_hash"`
|
||||
TransParam struct {
|
||||
Display int `json:"display"`
|
||||
DisplayRate int `json:"display_rate"`
|
||||
} `json:"trans_param"`
|
||||
FileHead int `json:"fileHead"`
|
||||
VolumePeak float64 `json:"volume_peak"`
|
||||
BitRate int `json:"bitRate"`
|
||||
TimeLength int `json:"timeLength"`
|
||||
VolumeGain int `json:"volume_gain"`
|
||||
Q int `json:"q"`
|
||||
FileName string `json:"fileName"`
|
||||
ExtName string `json:"extName"`
|
||||
FileSize int `json:"fileSize"`
|
||||
}
|
||||
)
|
118
src/sources/custom/kg/utils.go
Normal file
118
src/sources/custom/kg/utils.go
Normal file
@ -0,0 +1,118 @@
|
||||
package kg
|
||||
|
||||
import (
|
||||
"lx-source/src/env"
|
||||
"lx-source/src/sources"
|
||||
"net/http"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/ZxwyWebSite/ztool"
|
||||
"github.com/ZxwyWebSite/ztool/zcypt"
|
||||
)
|
||||
|
||||
var (
|
||||
// qualityHashMap = map[string]string{
|
||||
// sources.Q_128k: `hash_128`,
|
||||
// sources.Q_320k: `hash_320`,
|
||||
// sources.Q_flac: `hash_flac`,
|
||||
// sources.Q_fl24: `hash_high`,
|
||||
// }
|
||||
qualityMap = map[string]string{
|
||||
sources.Q_128k: `128`,
|
||||
sources.Q_320k: `320`,
|
||||
sources.Q_flac: sources.Q_flac,
|
||||
sources.Q_fl24: `high`,
|
||||
`master`: `viper_atmos`,
|
||||
}
|
||||
)
|
||||
|
||||
const (
|
||||
signkey = `OIlwieks28dk2k092lksi2UIkp`
|
||||
pidversec = `57ae12eb6890223e355ccfcb74edf70d`
|
||||
clientver = `12029`
|
||||
url = `https://gateway.kugou.com/v5/url`
|
||||
appid = `1005`
|
||||
mid = `211008`
|
||||
)
|
||||
|
||||
func sortDict(dictionary map[string]string) ([]string, int) {
|
||||
length := len(dictionary)
|
||||
var keys = make([]string, 0, length)
|
||||
for k := range dictionary {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
slices.Sort(keys)
|
||||
return keys, length
|
||||
}
|
||||
|
||||
// func sign(params map[string]string, body string) string {
|
||||
// keys, lens := sortDict(params)
|
||||
// var b strings.Builder
|
||||
// for i := 0; i < lens; i++ {
|
||||
// b.WriteString(keys[i])
|
||||
// b.WriteByte('=')
|
||||
// b.WriteString(params[keys[i]])
|
||||
// }
|
||||
// // b.WriteString(body)
|
||||
// return zcypt.MD5EncStr(ztool.Str_FastConcat(signkey, b.String(), signkey))
|
||||
// }
|
||||
|
||||
func signRequest(url string, params, headers map[string]string, out any) error {
|
||||
// buildSignatureParams
|
||||
keys, lens := sortDict(params)
|
||||
var b strings.Builder
|
||||
for i := 0; i < lens; i++ {
|
||||
b.WriteString(keys[i])
|
||||
b.WriteByte('=')
|
||||
b.WriteString(params[keys[i]])
|
||||
}
|
||||
// buildRequestParams
|
||||
var c strings.Builder
|
||||
for j := 0; j < lens; j++ {
|
||||
c.WriteString(keys[j])
|
||||
c.WriteByte('=')
|
||||
c.WriteString(params[keys[j]])
|
||||
c.WriteByte('&')
|
||||
}
|
||||
c.WriteString(`signature`)
|
||||
c.WriteByte('=')
|
||||
c.WriteString(zcypt.MD5EncStr(ztool.Str_FastConcat(signkey, b.String(), signkey)))
|
||||
|
||||
url = ztool.Str_FastConcat(url, `?`, c.String())
|
||||
// ztool.Cmd_FastPrintln(url)
|
||||
return ztool.Net_Request(
|
||||
http.MethodGet, url, nil,
|
||||
[]ztool.Net_ReqHandlerFunc{ztool.Net_ReqAddHeader(headers)},
|
||||
[]ztool.Net_ResHandlerFunc{ //func(res *http.Response) error {
|
||||
// body, err := io.ReadAll(res.Body)
|
||||
// fmt.Printf("%s, %s, %s\n", body, err, res.Status)
|
||||
// return ztool.Err_EsContinue
|
||||
// },
|
||||
ztool.Net_ResToStruct(out),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func getKey(hash_ string) string {
|
||||
return zcypt.MD5EncStr(ztool.Str_FastConcat(
|
||||
strings.ToLower(hash_), pidversec, appid, mid, env.Config.Custom.Kg_userId,
|
||||
))
|
||||
}
|
||||
|
||||
// 解析版权字段 (Copilot)
|
||||
/*
|
||||
// 定义一个函数,接受一个整数作为参数,返回一个布尔值的切片,表示每一位的状态
|
||||
依次为: 下载是否付费 \ 下载是否禁止 \ 播放是否付费 \ 播放是否禁止
|
||||
https://open.kugou.com/docs/open-player/#/android-sdk?v=1&id=%e6%ad%8c%e6%9b%b2%e5%ad%97%e6%ae%b5%e8%a7%a3%e6%9e%90
|
||||
*/
|
||||
// func parsePrivilege(privilege int) []bool {
|
||||
// result := make([]bool, 4) // 创建一个长度为4的切片
|
||||
// for i := 0; i < 4; i++ {
|
||||
// // 用位运算符&来判断每一位是否为1,如果是则将对应的切片元素设为true
|
||||
// if privilege&(1<<i) != 0 {
|
||||
// result[i] = true
|
||||
// }
|
||||
// }
|
||||
// return result
|
||||
// }
|
@ -9,6 +9,7 @@ import (
|
||||
"encoding/base64"
|
||||
|
||||
"github.com/ZxwyWebSite/ztool/x/bytesconv"
|
||||
"github.com/ZxwyWebSite/ztool/zcypt"
|
||||
)
|
||||
|
||||
// 常量和数组
|
||||
@ -250,8 +251,9 @@ func encrypt(msg []byte, key []byte) []byte {
|
||||
}
|
||||
|
||||
// base64编码函数
|
||||
func Base64_encrypt(msg string) string {
|
||||
func base64_encrypt(msg string) string {
|
||||
b1 := encrypt(bytesconv.StringToBytes(msg), SECRET_KEY)
|
||||
s := base64.StdEncoding.EncodeToString(b1)
|
||||
return s //strings.ReplaceAll(s, "\n", ``)
|
||||
return zcypt.Base64ToString(base64.StdEncoding, b1)
|
||||
// s := base64.StdEncoding.EncodeToString(b1)
|
||||
// return s //strings.ReplaceAll(s, "\n", ``)
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ func init() {
|
||||
loger := env.Loger.NewGroup(`KwInit`)
|
||||
switch env.Config.Custom.Kw_Mode {
|
||||
case `0`, `bdapi`:
|
||||
loger.Debug(`Use bdapi`)
|
||||
loger.Debug(`use bdapi`)
|
||||
if ztool.Chk_IsNilStr(
|
||||
env.Config.Custom.Kw_Bd_Uid,
|
||||
env.Config.Custom.Kw_Bd_Token,
|
||||
@ -43,11 +43,11 @@ func init() {
|
||||
case `1`, `kwdes`:
|
||||
switch env.Config.Custom.Kw_Des_Type {
|
||||
case `0`, `text`:
|
||||
loger.Debug(`Use kwdes_text`)
|
||||
loger.Debug(`use kwdes text`)
|
||||
convtype = `convert_url2`
|
||||
// desParse = txtParse
|
||||
case `1`, `json`:
|
||||
loger.Debug(`Use kwdes_json`)
|
||||
loger.Debug(`use kwdes json`)
|
||||
convtype = `convert_url_with_sign`
|
||||
// desParse = ztool.Net_ResToStruct
|
||||
parsemod = true
|
||||
@ -109,7 +109,7 @@ func kwdes(songMid, quality string) (ourl, msg string) {
|
||||
}
|
||||
target_url := ztool.Str_FastConcat(
|
||||
`https://mobi.kuwo.cn/mobi.s?f=kuwo&q=`,
|
||||
Base64_encrypt(ztool.Str_FastConcat(
|
||||
base64_encrypt(ztool.Str_FastConcat(
|
||||
`user=0&android_id=0&prod=kwplayer_ar_8.5.5.0&corp=kuwo&newver=3&vipver=8.5.5.0&source=kwplayer_ar_8.5.5.0_apk_keluze.apk&p2p=1¬race=0`,
|
||||
`&type=`, convtype,
|
||||
`&br=`, infoFile.H, infoFile.E,
|
||||
|
@ -13,11 +13,11 @@ var (
|
||||
H string // 专用音质
|
||||
}{
|
||||
sources.Q_128k: {
|
||||
E: `mp3`,
|
||||
E: sources.X_mp3,
|
||||
H: sources.Q_128k,
|
||||
},
|
||||
sources.Q_320k: {
|
||||
E: `mp3`,
|
||||
E: sources.X_mp3,
|
||||
H: sources.Q_320k,
|
||||
},
|
||||
sources.Q_flac: {
|
||||
|
105
src/sources/custom/mg/player.go
Normal file
105
src/sources/custom/mg/player.go
Normal file
@ -0,0 +1,105 @@
|
||||
package mg
|
||||
|
||||
import (
|
||||
"lx-source/src/env"
|
||||
"lx-source/src/sources"
|
||||
"lx-source/src/sources/custom/utils"
|
||||
"net/http"
|
||||
"sync"
|
||||
|
||||
"github.com/ZxwyWebSite/ztool"
|
||||
)
|
||||
|
||||
var (
|
||||
Url func(string, string) (string, string)
|
||||
|
||||
mg_pool *sync.Pool
|
||||
)
|
||||
|
||||
func init() {
|
||||
env.Inits.Add(func() {
|
||||
if !env.Config.Source.Enable_Mg {
|
||||
return
|
||||
}
|
||||
loger := env.Loger.NewGroup(`MgInit`)
|
||||
switch env.Config.Custom.Mg_Mode {
|
||||
case `0`, `builtin`:
|
||||
loger.Debug(`use builtin`)
|
||||
// Url = builtin
|
||||
case `1`, `custom`:
|
||||
loger.Debug(`use custom`)
|
||||
if ztool.Chk_IsNilStr(
|
||||
// env.Config.Custom.Mg_Usr_VerId,
|
||||
// env.Config.Custom.Mg_Usr_Token,
|
||||
env.Config.Custom.Mg_Usr_OSVer,
|
||||
env.Config.Custom.Mg_Usr_ReqUA,
|
||||
) {
|
||||
loger.Fatal(`使用自定义账号且用户参数为空`)
|
||||
}
|
||||
mg_pool = &sync.Pool{New: func() any { return new(playInfo) }}
|
||||
Url = mcustom
|
||||
default:
|
||||
loger.Fatal(`未定义的接口模式,请检查配置 [Custom].Mg_Mode`)
|
||||
}
|
||||
loger.Free()
|
||||
})
|
||||
}
|
||||
|
||||
// func builtin(songMid, quality string) (ourl, msg string) {
|
||||
// loger := env.Loger.NewGroup(`Mg`)
|
||||
// defer loger.Free()
|
||||
// return
|
||||
// }
|
||||
|
||||
func mcustom(songMid, quality string) (ourl, msg string) {
|
||||
loger := env.Loger.NewGroup(`Mg`)
|
||||
defer loger.Free()
|
||||
rquality, ok := qualityMap[quality]
|
||||
if !ok {
|
||||
msg = sources.E_QNotSupport
|
||||
return
|
||||
}
|
||||
url := ztool.Str_FastConcat(
|
||||
`https://app.c.nf.migu.cn/MIGUM2.0/strategy/listen-url/v2.4?toneFlag=`, rquality,
|
||||
`&songId=`, songMid,
|
||||
`&resourceType=2`,
|
||||
)
|
||||
resp := mg_pool.Get().(*playInfo)
|
||||
defer mg_pool.Put(resp)
|
||||
err := ztool.Net_Request(
|
||||
http.MethodGet, url, nil,
|
||||
[]ztool.Net_ReqHandlerFunc{ztool.Net_ReqAddHeaders(map[string]string{
|
||||
`User-Agent`: env.Config.Custom.Mg_Usr_ReqUA,
|
||||
`aversionid`: env.Config.Custom.Mg_Usr_VerId,
|
||||
`token`: env.Config.Custom.Mg_Usr_Token,
|
||||
`channel`: `0146832`,
|
||||
`language`: `Chinese`,
|
||||
`ua`: `Android_migu`,
|
||||
`mode`: `android`,
|
||||
`os`: `Android ` + env.Config.Custom.Mg_Usr_OSVer,
|
||||
})},
|
||||
[]ztool.Net_ResHandlerFunc{ztool.Net_ResToStruct(&resp)},
|
||||
)
|
||||
if err != nil {
|
||||
loger.Error(`Request: %s`, err)
|
||||
msg = sources.ErrHttpReq
|
||||
return
|
||||
}
|
||||
loger.Debug(`Resp: %+v`, resp)
|
||||
if resp.Code != `000000` {
|
||||
msg = resp.Info
|
||||
return
|
||||
}
|
||||
if resp.Data.URL == `` {
|
||||
msg = `No Data: 无返回链接`
|
||||
return
|
||||
}
|
||||
if resp.Data.AudioFormatType != rquality {
|
||||
msg = ztool.Str_FastConcat(`实际音质不匹配: `, rquality, ` <= `, resp.Data.AudioFormatType)
|
||||
if !env.Config.Source.ForceFallback {
|
||||
return
|
||||
}
|
||||
}
|
||||
ourl = utils.DelQuery(resp.Data.URL)
|
||||
return
|
||||
}
|
44
src/sources/custom/mg/types.go
Normal file
44
src/sources/custom/mg/types.go
Normal file
@ -0,0 +1,44 @@
|
||||
package mg
|
||||
|
||||
type playInfo struct {
|
||||
Code string `json:"code"`
|
||||
Data struct {
|
||||
AudioFormatType string `json:"audioFormatType"`
|
||||
FreeListenType string `json:"freeListenType"`
|
||||
HaveVisualMv bool `json:"haveVisualMv"`
|
||||
LrcURL string `json:"lrcUrl"`
|
||||
MrcURL string `json:"mrcUrl"`
|
||||
Song struct {
|
||||
Album string `json:"album"`
|
||||
AlbumID string `json:"albumId"`
|
||||
AlbumPinyin string `json:"albumPinyin"`
|
||||
ContentID string `json:"contentId"`
|
||||
CopyrightID string `json:"copyrightId"`
|
||||
CopyrightType int `json:"copyrightType"`
|
||||
Duration int `json:"duration"`
|
||||
ForeverListen bool `json:"foreverListen"`
|
||||
HaveShockRing int `json:"haveShockRing"`
|
||||
Img1 string `json:"img1"`
|
||||
Img2 string `json:"img2"`
|
||||
Img3 string `json:"img3"`
|
||||
MvCopyrightType int `json:"mvCopyrightType"`
|
||||
ResourceType string `json:"resourceType"`
|
||||
RestrictType int `json:"restrictType"`
|
||||
RingToneID string `json:"ringToneId"`
|
||||
ShowTags []string `json:"showTags"`
|
||||
SingerList []struct {
|
||||
ID string `json:"id"`
|
||||
Img string `json:"img"`
|
||||
Name string `json:"name"`
|
||||
NameSpelling string `json:"nameSpelling"`
|
||||
} `json:"singerList"`
|
||||
SongID string `json:"songId"`
|
||||
SongName string `json:"songName"`
|
||||
SongPinyin string `json:"songPinyin"`
|
||||
} `json:"song"`
|
||||
TrcURL string `json:"trcUrl"`
|
||||
URL string `json:"url"`
|
||||
Version string `json:"version"`
|
||||
} `json:"data"`
|
||||
Info string `json:"info"`
|
||||
}
|
25
src/sources/custom/mg/utils.go
Normal file
25
src/sources/custom/mg/utils.go
Normal file
@ -0,0 +1,25 @@
|
||||
package mg
|
||||
|
||||
import "lx-source/src/sources"
|
||||
|
||||
// const (
|
||||
// q_128k = `PQ`
|
||||
// q_320k = `HQ`
|
||||
// q_flac = `SQ`
|
||||
// q_fl24 = `ZQ`
|
||||
// )
|
||||
|
||||
var (
|
||||
qualityMap = map[string]string{
|
||||
sources.Q_128k: `PQ`,
|
||||
sources.Q_320k: `HQ`,
|
||||
sources.Q_flac: `SQ`,
|
||||
sources.Q_fl24: `ZQ`,
|
||||
}
|
||||
// qualityMapReverse = map[string]string{
|
||||
// q_128k: sources.Q_128k,
|
||||
// q_320k: sources.Q_320k,
|
||||
// q_flac: sources.Q_flac,
|
||||
// q_fl24: sources.Q_fl24,
|
||||
// }
|
||||
)
|
@ -1,11 +1,5 @@
|
||||
package tx
|
||||
|
||||
import (
|
||||
"lx-source/src/env"
|
||||
|
||||
"github.com/ZxwyWebSite/ztool/logs"
|
||||
)
|
||||
|
||||
// func Info(songMid string) (info any, msg string) {
|
||||
// req, emsg := getMusicInfo(songMid)
|
||||
// if emsg != `` {
|
||||
@ -29,14 +23,3 @@ import (
|
||||
// }
|
||||
// return
|
||||
// }
|
||||
|
||||
func init() {
|
||||
env.Inits.Add(func() {
|
||||
if env.Config.Custom.Tx_Refresh_Enable {
|
||||
env.Tasker.Add(`tx_refresh`, func(l *logs.Logger) error {
|
||||
refresh(l)
|
||||
return nil
|
||||
}, 86000, true)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -5,195 +5,6 @@ import (
|
||||
"github.com/ZxwyWebSite/ztool/x/bytesconv"
|
||||
)
|
||||
|
||||
type musicInfo struct {
|
||||
// Info struct {
|
||||
// Company struct {
|
||||
// Title string `json:"title"`
|
||||
// Type string `json:"type"`
|
||||
// Content []struct {
|
||||
// ID int `json:"id"`
|
||||
// Value string `json:"value"`
|
||||
// Mid string `json:"mid"`
|
||||
// Type int `json:"type"`
|
||||
// ShowType int `json:"show_type"`
|
||||
// IsParent int `json:"is_parent"`
|
||||
// Picurl string `json:"picurl"`
|
||||
// ReadCnt int `json:"read_cnt"`
|
||||
// Author string `json:"author"`
|
||||
// Jumpurl string `json:"jumpurl"`
|
||||
// OriPicurl string `json:"ori_picurl"`
|
||||
// } `json:"content"`
|
||||
// Pos int `json:"pos"`
|
||||
// More int `json:"more"`
|
||||
// Selected string `json:"selected"`
|
||||
// UsePlatform int `json:"use_platform"`
|
||||
// } `json:"company"`
|
||||
// Genre struct {
|
||||
// Title string `json:"title"`
|
||||
// Type string `json:"type"`
|
||||
// Content []struct {
|
||||
// ID int `json:"id"`
|
||||
// Value string `json:"value"`
|
||||
// Mid string `json:"mid"`
|
||||
// Type int `json:"type"`
|
||||
// ShowType int `json:"show_type"`
|
||||
// IsParent int `json:"is_parent"`
|
||||
// Picurl string `json:"picurl"`
|
||||
// ReadCnt int `json:"read_cnt"`
|
||||
// Author string `json:"author"`
|
||||
// Jumpurl string `json:"jumpurl"`
|
||||
// OriPicurl string `json:"ori_picurl"`
|
||||
// } `json:"content"`
|
||||
// Pos int `json:"pos"`
|
||||
// More int `json:"more"`
|
||||
// Selected string `json:"selected"`
|
||||
// UsePlatform int `json:"use_platform"`
|
||||
// } `json:"genre"`
|
||||
// Lan struct {
|
||||
// Title string `json:"title"`
|
||||
// Type string `json:"type"`
|
||||
// Content []struct {
|
||||
// ID int `json:"id"`
|
||||
// Value string `json:"value"`
|
||||
// Mid string `json:"mid"`
|
||||
// Type int `json:"type"`
|
||||
// ShowType int `json:"show_type"`
|
||||
// IsParent int `json:"is_parent"`
|
||||
// Picurl string `json:"picurl"`
|
||||
// ReadCnt int `json:"read_cnt"`
|
||||
// Author string `json:"author"`
|
||||
// Jumpurl string `json:"jumpurl"`
|
||||
// OriPicurl string `json:"ori_picurl"`
|
||||
// } `json:"content"`
|
||||
// Pos int `json:"pos"`
|
||||
// More int `json:"more"`
|
||||
// Selected string `json:"selected"`
|
||||
// UsePlatform int `json:"use_platform"`
|
||||
// } `json:"lan"`
|
||||
// } `json:"info"`
|
||||
// Extras struct {
|
||||
// Name string `json:"name"`
|
||||
// Transname string `json:"transname"`
|
||||
// Subtitle string `json:"subtitle"`
|
||||
// From string `json:"from"`
|
||||
// Wikiurl string `json:"wikiurl"`
|
||||
// } `json:"extras"`
|
||||
TrackInfo struct {
|
||||
ID int `json:"id"`
|
||||
Type int `json:"type"`
|
||||
Mid string `json:"mid"`
|
||||
Name string `json:"name"`
|
||||
Title string `json:"title"`
|
||||
Subtitle string `json:"subtitle"`
|
||||
Singer []struct {
|
||||
ID int `json:"id"`
|
||||
Mid string `json:"mid"`
|
||||
Name string `json:"name"`
|
||||
Title string `json:"title"`
|
||||
Type int `json:"type"`
|
||||
Uin int `json:"uin"`
|
||||
} `json:"singer"`
|
||||
Album struct {
|
||||
ID int `json:"id"`
|
||||
Mid string `json:"mid"`
|
||||
Name string `json:"name"`
|
||||
Title string `json:"title"`
|
||||
Subtitle string `json:"subtitle"`
|
||||
TimePublic string `json:"time_public"`
|
||||
Pmid string `json:"pmid"`
|
||||
} `json:"album"`
|
||||
Mv struct {
|
||||
ID int `json:"id"`
|
||||
Vid string `json:"vid"`
|
||||
Name string `json:"name"`
|
||||
Title string `json:"title"`
|
||||
Vt int `json:"vt"`
|
||||
} `json:"mv"`
|
||||
Interval int `json:"interval"`
|
||||
Isonly int `json:"isonly"`
|
||||
Language int `json:"language"`
|
||||
Genre int `json:"genre"`
|
||||
IndexCd int `json:"index_cd"`
|
||||
IndexAlbum int `json:"index_album"`
|
||||
TimePublic string `json:"time_public"`
|
||||
Status int `json:"status"`
|
||||
Fnote int `json:"fnote"`
|
||||
File struct {
|
||||
MediaMid string `json:"media_mid"`
|
||||
Size24Aac int `json:"size_24aac"`
|
||||
Size48Aac int `json:"size_48aac"`
|
||||
Size96Aac int `json:"size_96aac"`
|
||||
Size192Ogg int `json:"size_192ogg"`
|
||||
Size192Aac int `json:"size_192aac"`
|
||||
Size128Mp3 int `json:"size_128mp3"`
|
||||
Size320Mp3 int `json:"size_320mp3"`
|
||||
SizeApe int `json:"size_ape"`
|
||||
SizeFlac int `json:"size_flac"`
|
||||
SizeDts int `json:"size_dts"`
|
||||
SizeTry int `json:"size_try"`
|
||||
TryBegin int `json:"try_begin"`
|
||||
TryEnd int `json:"try_end"`
|
||||
URL string `json:"url"`
|
||||
SizeHires int `json:"size_hires"`
|
||||
HiresSample int `json:"hires_sample"`
|
||||
HiresBitdepth int `json:"hires_bitdepth"`
|
||||
B30S int `json:"b_30s"`
|
||||
E30S int `json:"e_30s"`
|
||||
Size96Ogg int `json:"size_96ogg"`
|
||||
Size360Ra []interface{} `json:"size_360ra"`
|
||||
SizeDolby int `json:"size_dolby"`
|
||||
SizeNew []int `json:"size_new"`
|
||||
} `json:"file"`
|
||||
Pay struct {
|
||||
PayMonth int `json:"pay_month"`
|
||||
PriceTrack int `json:"price_track"`
|
||||
PriceAlbum int `json:"price_album"`
|
||||
PayPlay int `json:"pay_play"`
|
||||
PayDown int `json:"pay_down"`
|
||||
PayStatus int `json:"pay_status"`
|
||||
TimeFree int `json:"time_free"`
|
||||
} `json:"pay"`
|
||||
Action struct {
|
||||
Switch int `json:"switch"`
|
||||
Msgid int `json:"msgid"`
|
||||
Alert int `json:"alert"`
|
||||
Icons int `json:"icons"`
|
||||
Msgshare int `json:"msgshare"`
|
||||
Msgfav int `json:"msgfav"`
|
||||
Msgdown int `json:"msgdown"`
|
||||
Msgpay int `json:"msgpay"`
|
||||
Switch2 int `json:"switch2"`
|
||||
Icon2 int `json:"icon2"`
|
||||
} `json:"action"`
|
||||
Ksong struct {
|
||||
ID int `json:"id"`
|
||||
Mid string `json:"mid"`
|
||||
} `json:"ksong"`
|
||||
Volume struct {
|
||||
Gain float64 `json:"gain"`
|
||||
Peak float64 `json:"peak"`
|
||||
Lra float64 `json:"lra"`
|
||||
} `json:"volume"`
|
||||
Label string `json:"label"`
|
||||
URL string `json:"url"`
|
||||
Bpm int `json:"bpm"`
|
||||
Version int `json:"version"`
|
||||
Trace string `json:"trace"`
|
||||
DataType int `json:"data_type"`
|
||||
ModifyStamp int `json:"modify_stamp"`
|
||||
Pingpong string `json:"pingpong"`
|
||||
Ppurl string `json:"ppurl"`
|
||||
Tid int `json:"tid"`
|
||||
Ov int `json:"ov"`
|
||||
Sa int `json:"sa"`
|
||||
Es string `json:"es"`
|
||||
Vs []string `json:"vs"`
|
||||
Vi []int `json:"vi"`
|
||||
Ktag string `json:"ktag"`
|
||||
Vf []float64 `json:"vf"`
|
||||
} `json:"track_info"`
|
||||
}
|
||||
|
||||
func getMusicInfo(songMid string) (infoBody musicInfo, emsg string) {
|
||||
infoReqBody := ztool.Str_FastConcat(`{"comm":{"ct":"19","cv":"1859","uin":"0"},"req":{"method":"get_song_detail_yqq","module":"music.pf_song_detail_svr","param":{"song_mid":"`, songMid, `","song_type":0}}}`)
|
||||
var infoResp struct {
|
||||
|
@ -8,62 +8,6 @@ import (
|
||||
"github.com/ZxwyWebSite/ztool/x/bytesconv"
|
||||
)
|
||||
|
||||
type playInfo struct {
|
||||
Code int `json:"code"`
|
||||
Data struct {
|
||||
Uin string `json:"uin"`
|
||||
Retcode int `json:"retcode"`
|
||||
VerifyType int `json:"verify_type"`
|
||||
LoginKey string `json:"login_key"`
|
||||
Msg string `json:"msg"`
|
||||
Sip []string `json:"sip"`
|
||||
Thirdip []string `json:"thirdip"`
|
||||
Testfile2G string `json:"testfile2g"`
|
||||
Testfilewifi string `json:"testfilewifi"`
|
||||
Midurlinfo []struct {
|
||||
Songmid string `json:"songmid"`
|
||||
Filename string `json:"filename"`
|
||||
Purl string `json:"purl"`
|
||||
Errtype string `json:"errtype"`
|
||||
P2Pfromtag int `json:"p2pfromtag"`
|
||||
Qmdlfromtag int `json:"qmdlfromtag"`
|
||||
CommonDownfromtag int `json:"common_downfromtag"`
|
||||
VipDownfromtag int `json:"vip_downfromtag"`
|
||||
Pdl int `json:"pdl"`
|
||||
Premain int `json:"premain"`
|
||||
Hisdown int `json:"hisdown"`
|
||||
Hisbuy int `json:"hisbuy"`
|
||||
UIAlert int `json:"uiAlert"`
|
||||
Isbuy int `json:"isbuy"`
|
||||
Pneedbuy int `json:"pneedbuy"`
|
||||
Pneed int `json:"pneed"`
|
||||
Isonly int `json:"isonly"`
|
||||
Onecan int `json:"onecan"`
|
||||
Result int `json:"result"`
|
||||
Tips string `json:"tips"`
|
||||
Opi48Kurl string `json:"opi48kurl"`
|
||||
Opi96Kurl string `json:"opi96kurl"`
|
||||
Opi192Kurl string `json:"opi192kurl"`
|
||||
Opiflackurl string `json:"opiflackurl"`
|
||||
Opi128Kurl string `json:"opi128kurl"`
|
||||
Opi192Koggurl string `json:"opi192koggurl"`
|
||||
Wififromtag string `json:"wififromtag"`
|
||||
Flowfromtag string `json:"flowfromtag"`
|
||||
Wifiurl string `json:"wifiurl"`
|
||||
Flowurl string `json:"flowurl"`
|
||||
Vkey string `json:"vkey"`
|
||||
Opi30Surl string `json:"opi30surl"`
|
||||
Ekey string `json:"ekey"`
|
||||
AuthSwitch int `json:"auth_switch"`
|
||||
Subcode int `json:"subcode"`
|
||||
Opi96Koggurl string `json:"opi96koggurl"`
|
||||
AuthSwitch2 int `json:"auth_switch2"`
|
||||
} `json:"midurlinfo"`
|
||||
Servercheck string `json:"servercheck"`
|
||||
Expiration int `json:"expiration"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
/*
|
||||
音乐URL获取逻辑:
|
||||
if需要付费播放and无账号信息:
|
||||
@ -98,11 +42,12 @@ func Url(songMid, quality string) (ourl, msg string) {
|
||||
return
|
||||
}
|
||||
infoBody, emsg := getMusicInfo(songMid)
|
||||
loger.Debug(`infoBody: %+v`, infoBody)
|
||||
if emsg != `` {
|
||||
loger.Error(`GetInfo: %v`, emsg)
|
||||
msg = emsg
|
||||
return
|
||||
}
|
||||
loger.Debug(`infoBody: %+v`, infoBody)
|
||||
var uauthst, uuin string = env.Config.Custom.Tx_Ukey, env.Config.Custom.Tx_Uuin
|
||||
if uuin == `` || !env.Config.Custom.Tx_Enable {
|
||||
uuin = `1535153710`
|
||||
@ -110,11 +55,23 @@ func Url(songMid, quality string) (ourl, msg string) {
|
||||
var strFileName string
|
||||
tryLink := infoBody.TrackInfo.Pay.PayPlay == 1 && /*uauthst == ``&&*/ !env.Config.Custom.Tx_Enable
|
||||
if tryLink {
|
||||
strFileName = ztool.Str_FastConcat(`RS02`, infoBody.TrackInfo.Vs[0], `.mp3`)
|
||||
strFileName = ztool.Str_FastConcat(`RS02`, infoBody.TrackInfo.Vs[0], `.`, sources.X_mp3)
|
||||
} else {
|
||||
strFileName = ztool.Str_FastConcat(infoFile.H, infoBody.TrackInfo.File.MediaMid, infoFile.E)
|
||||
strFileName = ztool.Str_FastConcat(infoFile.H, infoBody.TrackInfo.File.MediaMid, `.`, infoFile.E)
|
||||
}
|
||||
requestBody := ztool.Str_FastConcat(`{"comm":{"authst":"`, uauthst, `","ct":"26","cv":"2010101","qq":"`, uuin, `","v":"2010101"},"req_0":{"method":"CgiGetVkey","module":"vkey.GetVkeyServer","param":{"filename":["`, strFileName, `"],"guid":"114514","loginflag":1,"platform":"20","songmid":["`, songMid, `"],"songtype":[0],"uin":"10086"}}}`)
|
||||
requestBody := ztool.Str_FastConcat(
|
||||
`{"comm":{"authst":"`,
|
||||
uauthst,
|
||||
`","ct":"26","cv":"2010101","qq":"`,
|
||||
uuin,
|
||||
`","v":"2010101"},"req_0":{"method":"CgiGetVkey","module":"vkey.GetVkeyServer","param":{"filename":["`,
|
||||
strFileName,
|
||||
`"],"guid":"20211008","loginflag":1,"platform":"20","songmid":["`,
|
||||
songMid,
|
||||
`"],"songtype":[0],"uin":"`,
|
||||
uuin,
|
||||
`"}}}`,
|
||||
)
|
||||
var infoResp struct {
|
||||
Code int `json:"code"`
|
||||
// Ts int64 `json:"ts"`
|
||||
@ -124,6 +81,7 @@ func Url(songMid, quality string) (ourl, msg string) {
|
||||
}
|
||||
err := signRequest(bytesconv.StringToBytes(requestBody), &infoResp)
|
||||
if err != nil {
|
||||
loger.Error(`Request: %s`, err)
|
||||
msg = err.Error()
|
||||
return
|
||||
}
|
||||
|
@ -1,10 +1,11 @@
|
||||
package tx
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"lx-source/src/env"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/ZxwyWebSite/ztool"
|
||||
"github.com/ZxwyWebSite/ztool/logs"
|
||||
@ -78,25 +79,15 @@ type refreshData struct {
|
||||
第一次载入时会刷新一次测试可用性&同步过期时间 (默认7天)
|
||||
*/
|
||||
|
||||
func refresh(loger *logs.Logger) {
|
||||
// loger := env.Loger.NewGroup(`refresh_login`)
|
||||
if env.Config.Custom.Tx_Ukey == `` || !env.Config.Custom.Tx_Refresh_Enable {
|
||||
return
|
||||
}
|
||||
if time.Now().Unix() < env.Config.Custom.Tx_Refresh_Interval {
|
||||
func refresh(loger *logs.Logger, now int64) error {
|
||||
// 前置检测
|
||||
if now < env.Config.Custom.Tx_Refresh_Interval {
|
||||
loger.Debug(`Key未过期,跳过...`)
|
||||
return
|
||||
return nil
|
||||
}
|
||||
// 刷新逻辑 (注:QQ登录最常用所以先判断QHL开头)
|
||||
var body, surl string
|
||||
if strings.HasPrefix(env.Config.Custom.Tx_Ukey, `W_X`) {
|
||||
body = ztool.Str_FastConcat(
|
||||
`{"comm":{"authst":"","ct":"11","cv":"12080008","fPersonality":"0","qq":"","tmeAppID":"qqmusic","tmeLoginMethod":"1","tmeLoginType":"1","v":"12080008"},"req1":{"method":"Login","module":"music.login.LoginServer","param":{"code":"","loginMode":2,"musickey":"`,
|
||||
env.Config.Custom.Tx_Ukey,
|
||||
`","openid":"","refresh_key":"","refresh_token":"","str_musicid":"`,
|
||||
env.Config.Custom.Tx_Uuin,
|
||||
`","unionid":""}}}`,
|
||||
)
|
||||
} else if strings.HasPrefix(env.Config.Custom.Tx_Ukey, `Q_H_L`) {
|
||||
if strings.HasPrefix(env.Config.Custom.Tx_Ukey, `Q_H_L`) {
|
||||
body = ztool.Str_FastConcat(
|
||||
`{"req1":{"method":"QQLogin","module":"QQConnectLogin.LoginServer","param":{"expired_in":7776000,"musicid":`,
|
||||
env.Config.Custom.Tx_Uuin,
|
||||
@ -105,43 +96,58 @@ func refresh(loger *logs.Logger) {
|
||||
`"}}}`,
|
||||
)
|
||||
surl = `6`
|
||||
} else if strings.HasPrefix(env.Config.Custom.Tx_Ukey, `W_X`) {
|
||||
body = ztool.Str_FastConcat(
|
||||
`{"comm":{"authst":"","ct":"11","cv":"12080008","fPersonality":"0","qq":"","tmeAppID":"qqmusic","tmeLoginMethod":"1","tmeLoginType":"1","v":"12080008"},"req1":{"method":"Login","module":"music.login.LoginServer","param":{"code":"","loginMode":2,"musickey":"`,
|
||||
env.Config.Custom.Tx_Ukey,
|
||||
`","openid":"","refresh_key":"","refresh_token":"","str_musicid":"`,
|
||||
env.Config.Custom.Tx_Uuin,
|
||||
`","unionid":""}}}`,
|
||||
)
|
||||
} else {
|
||||
loger.Error(`未知的qqmusic_key格式`)
|
||||
env.Config.Custom.Tx_Refresh_Enable = false // 本次启动阻止继续执行
|
||||
return
|
||||
// 致命错误(删除任务)
|
||||
panic(`未知的 qqmusic_key 格式, 请检查配置 [Custom].Tx_Ukey`)
|
||||
}
|
||||
loger.Debug(`Body: %v`, body)
|
||||
var resp refreshData
|
||||
signature := sign(bytesconv.StringToBytes(body))
|
||||
err := ztool.Net_Request(http.MethodPost,
|
||||
err := ztool.Net_Request(
|
||||
http.MethodPost,
|
||||
ztool.Str_FastConcat(`https://u`, surl, `.y.qq.com/cgi-bin/musics.fcg?sign=`, signature),
|
||||
strings.NewReader(body),
|
||||
[]ztool.Net_ReqHandlerFunc{
|
||||
ztool.Net_ReqAddHeaders(header),
|
||||
},
|
||||
[]ztool.Net_ResHandlerFunc{
|
||||
ztool.Net_ResToStruct(&resp),
|
||||
},
|
||||
[]ztool.Net_ReqHandlerFunc{ztool.Net_ReqAddHeaders(header)},
|
||||
[]ztool.Net_ResHandlerFunc{ztool.Net_ResToStruct(&resp)},
|
||||
)
|
||||
if err != nil {
|
||||
loger.Error(`请求Api失败: %s`, err)
|
||||
return
|
||||
// loger.Error(`请求Api失败: %s`, err)
|
||||
return errors.New(`请求Api失败: ` + err.Error())
|
||||
}
|
||||
if resp.Req1.Code != 0 {
|
||||
loger.Warn("刷新登录失败, code: %v\n响应体: %+v", resp.Req1.Code, resp)
|
||||
return
|
||||
// loger.Warn("刷新登录失败, code: %v\n响应体: %+v", resp.Req1.Code, resp)
|
||||
return fmt.Errorf("刷新登录失败, code: %v\n响应体: %+v", resp.Req1.Code, resp)
|
||||
}
|
||||
loger.Info(`刷新登录成功`)
|
||||
env.Config.Custom.Tx_Uuin = resp.Req1.Data.StrMusicId
|
||||
env.Config.Custom.Tx_Ukey = resp.Req1.Data.MusicKey
|
||||
env.Config.Custom.Tx_Refresh_Interval = resp.Req1.Data.ExpiredAt - 86000 // 提前一天
|
||||
env.Config.Custom.Tx_Refresh_Interval = now + 518400 //(每6天刷新一次) //1209600 - 86000 // 14天提前一天
|
||||
loger.Debug(`Resp: %+v`, resp)
|
||||
loger.Debug(`Uuin: %v, Ukey: %v`, resp.Req1.Data.StrMusicId, resp.Req1.Data.MusicKey)
|
||||
loger.Debug(`ExpiresAt: %v`, resp.Req1.Data.ExpiredAt)
|
||||
loger.Debug(`ExpiresAt: %v, Real: %v`, resp.Req1.Data.ExpiredAt, env.Config.Custom.Tx_Refresh_Interval)
|
||||
err = env.Cfg.Save(``)
|
||||
if err != nil {
|
||||
loger.Error(`%s`, err)
|
||||
return
|
||||
}
|
||||
// if err != nil {
|
||||
// loger.Error(`%s`, err)
|
||||
// return
|
||||
// }
|
||||
if err == nil {
|
||||
loger.Info(`数据更新成功`) // 已通过相应数据更新uin和qqmusic_key
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func init() {
|
||||
env.Inits.Add(func() {
|
||||
if env.Config.Custom.Tx_Refresh_Enable && env.Config.Custom.Tx_Ukey != `` && env.Config.Custom.Tx_Uuin != `` {
|
||||
env.Tasker.Add(`tx_refresh`, refresh, 86000, true)
|
||||
}
|
||||
})
|
||||
}
|
246
src/sources/custom/tx/types.go
Normal file
246
src/sources/custom/tx/types.go
Normal file
@ -0,0 +1,246 @@
|
||||
package tx
|
||||
|
||||
type musicInfo struct {
|
||||
// Info struct {
|
||||
// Company struct {
|
||||
// Title string `json:"title"`
|
||||
// Type string `json:"type"`
|
||||
// Content []struct {
|
||||
// ID int `json:"id"`
|
||||
// Value string `json:"value"`
|
||||
// Mid string `json:"mid"`
|
||||
// Type int `json:"type"`
|
||||
// ShowType int `json:"show_type"`
|
||||
// IsParent int `json:"is_parent"`
|
||||
// Picurl string `json:"picurl"`
|
||||
// ReadCnt int `json:"read_cnt"`
|
||||
// Author string `json:"author"`
|
||||
// Jumpurl string `json:"jumpurl"`
|
||||
// OriPicurl string `json:"ori_picurl"`
|
||||
// } `json:"content"`
|
||||
// Pos int `json:"pos"`
|
||||
// More int `json:"more"`
|
||||
// Selected string `json:"selected"`
|
||||
// UsePlatform int `json:"use_platform"`
|
||||
// } `json:"company"`
|
||||
// Genre struct {
|
||||
// Title string `json:"title"`
|
||||
// Type string `json:"type"`
|
||||
// Content []struct {
|
||||
// ID int `json:"id"`
|
||||
// Value string `json:"value"`
|
||||
// Mid string `json:"mid"`
|
||||
// Type int `json:"type"`
|
||||
// ShowType int `json:"show_type"`
|
||||
// IsParent int `json:"is_parent"`
|
||||
// Picurl string `json:"picurl"`
|
||||
// ReadCnt int `json:"read_cnt"`
|
||||
// Author string `json:"author"`
|
||||
// Jumpurl string `json:"jumpurl"`
|
||||
// OriPicurl string `json:"ori_picurl"`
|
||||
// } `json:"content"`
|
||||
// Pos int `json:"pos"`
|
||||
// More int `json:"more"`
|
||||
// Selected string `json:"selected"`
|
||||
// UsePlatform int `json:"use_platform"`
|
||||
// } `json:"genre"`
|
||||
// Lan struct {
|
||||
// Title string `json:"title"`
|
||||
// Type string `json:"type"`
|
||||
// Content []struct {
|
||||
// ID int `json:"id"`
|
||||
// Value string `json:"value"`
|
||||
// Mid string `json:"mid"`
|
||||
// Type int `json:"type"`
|
||||
// ShowType int `json:"show_type"`
|
||||
// IsParent int `json:"is_parent"`
|
||||
// Picurl string `json:"picurl"`
|
||||
// ReadCnt int `json:"read_cnt"`
|
||||
// Author string `json:"author"`
|
||||
// Jumpurl string `json:"jumpurl"`
|
||||
// OriPicurl string `json:"ori_picurl"`
|
||||
// } `json:"content"`
|
||||
// Pos int `json:"pos"`
|
||||
// More int `json:"more"`
|
||||
// Selected string `json:"selected"`
|
||||
// UsePlatform int `json:"use_platform"`
|
||||
// } `json:"lan"`
|
||||
// } `json:"info"`
|
||||
// Extras struct {
|
||||
// Name string `json:"name"`
|
||||
// Transname string `json:"transname"`
|
||||
// Subtitle string `json:"subtitle"`
|
||||
// From string `json:"from"`
|
||||
// Wikiurl string `json:"wikiurl"`
|
||||
// } `json:"extras"`
|
||||
TrackInfo struct {
|
||||
ID int `json:"id"`
|
||||
Type int `json:"type"`
|
||||
Mid string `json:"mid"`
|
||||
Name string `json:"name"`
|
||||
Title string `json:"title"`
|
||||
Subtitle string `json:"subtitle"`
|
||||
Singer []struct {
|
||||
ID int `json:"id"`
|
||||
Mid string `json:"mid"`
|
||||
Name string `json:"name"`
|
||||
Title string `json:"title"`
|
||||
Type int `json:"type"`
|
||||
Uin int `json:"uin"`
|
||||
} `json:"singer"`
|
||||
Album struct {
|
||||
ID int `json:"id"`
|
||||
Mid string `json:"mid"`
|
||||
Name string `json:"name"`
|
||||
Title string `json:"title"`
|
||||
Subtitle string `json:"subtitle"`
|
||||
TimePublic string `json:"time_public"`
|
||||
Pmid string `json:"pmid"`
|
||||
} `json:"album"`
|
||||
Mv struct {
|
||||
ID int `json:"id"`
|
||||
Vid string `json:"vid"`
|
||||
Name string `json:"name"`
|
||||
Title string `json:"title"`
|
||||
Vt int `json:"vt"`
|
||||
} `json:"mv"`
|
||||
Interval int `json:"interval"`
|
||||
Isonly int `json:"isonly"`
|
||||
Language int `json:"language"`
|
||||
Genre int `json:"genre"`
|
||||
IndexCd int `json:"index_cd"`
|
||||
IndexAlbum int `json:"index_album"`
|
||||
TimePublic string `json:"time_public"`
|
||||
Status int `json:"status"`
|
||||
Fnote int `json:"fnote"`
|
||||
File struct {
|
||||
MediaMid string `json:"media_mid"`
|
||||
Size24Aac int `json:"size_24aac"`
|
||||
Size48Aac int `json:"size_48aac"`
|
||||
Size96Aac int `json:"size_96aac"`
|
||||
Size192Ogg int `json:"size_192ogg"`
|
||||
Size192Aac int `json:"size_192aac"`
|
||||
Size128Mp3 int `json:"size_128mp3"`
|
||||
Size320Mp3 int `json:"size_320mp3"`
|
||||
SizeApe int `json:"size_ape"`
|
||||
SizeFlac int `json:"size_flac"`
|
||||
SizeDts int `json:"size_dts"`
|
||||
SizeTry int `json:"size_try"`
|
||||
TryBegin int `json:"try_begin"`
|
||||
TryEnd int `json:"try_end"`
|
||||
URL string `json:"url"`
|
||||
SizeHires int `json:"size_hires"`
|
||||
HiresSample int `json:"hires_sample"`
|
||||
HiresBitdepth int `json:"hires_bitdepth"`
|
||||
B30S int `json:"b_30s"`
|
||||
E30S int `json:"e_30s"`
|
||||
Size96Ogg int `json:"size_96ogg"`
|
||||
Size360Ra []interface{} `json:"size_360ra"`
|
||||
SizeDolby int `json:"size_dolby"`
|
||||
SizeNew []int `json:"size_new"`
|
||||
} `json:"file"`
|
||||
Pay struct {
|
||||
PayMonth int `json:"pay_month"`
|
||||
PriceTrack int `json:"price_track"`
|
||||
PriceAlbum int `json:"price_album"`
|
||||
PayPlay int `json:"pay_play"`
|
||||
PayDown int `json:"pay_down"`
|
||||
PayStatus int `json:"pay_status"`
|
||||
TimeFree int `json:"time_free"`
|
||||
} `json:"pay"`
|
||||
Action struct {
|
||||
Switch int `json:"switch"`
|
||||
Msgid int `json:"msgid"`
|
||||
Alert int `json:"alert"`
|
||||
Icons int `json:"icons"`
|
||||
Msgshare int `json:"msgshare"`
|
||||
Msgfav int `json:"msgfav"`
|
||||
Msgdown int `json:"msgdown"`
|
||||
Msgpay int `json:"msgpay"`
|
||||
Switch2 int `json:"switch2"`
|
||||
Icon2 int `json:"icon2"`
|
||||
} `json:"action"`
|
||||
Ksong struct {
|
||||
ID int `json:"id"`
|
||||
Mid string `json:"mid"`
|
||||
} `json:"ksong"`
|
||||
Volume struct {
|
||||
Gain float64 `json:"gain"`
|
||||
Peak float64 `json:"peak"`
|
||||
Lra float64 `json:"lra"`
|
||||
} `json:"volume"`
|
||||
Label string `json:"label"`
|
||||
URL string `json:"url"`
|
||||
Bpm int `json:"bpm"`
|
||||
Version int `json:"version"`
|
||||
Trace string `json:"trace"`
|
||||
DataType int `json:"data_type"`
|
||||
ModifyStamp int `json:"modify_stamp"`
|
||||
Pingpong string `json:"pingpong"`
|
||||
Ppurl string `json:"ppurl"`
|
||||
Tid int `json:"tid"`
|
||||
Ov int `json:"ov"`
|
||||
Sa int `json:"sa"`
|
||||
Es string `json:"es"`
|
||||
Vs []string `json:"vs"`
|
||||
Vi []int `json:"vi"`
|
||||
Ktag string `json:"ktag"`
|
||||
Vf []float64 `json:"vf"`
|
||||
} `json:"track_info"`
|
||||
}
|
||||
|
||||
type playInfo struct {
|
||||
Code int `json:"code"`
|
||||
Data struct {
|
||||
Uin string `json:"uin"`
|
||||
Retcode int `json:"retcode"`
|
||||
VerifyType int `json:"verify_type"`
|
||||
LoginKey string `json:"login_key"`
|
||||
Msg string `json:"msg"`
|
||||
Sip []string `json:"sip"`
|
||||
Thirdip []string `json:"thirdip"`
|
||||
Testfile2G string `json:"testfile2g"`
|
||||
Testfilewifi string `json:"testfilewifi"`
|
||||
Midurlinfo []struct {
|
||||
Songmid string `json:"songmid"`
|
||||
Filename string `json:"filename"`
|
||||
Purl string `json:"purl"`
|
||||
Errtype string `json:"errtype"`
|
||||
P2Pfromtag int `json:"p2pfromtag"`
|
||||
Qmdlfromtag int `json:"qmdlfromtag"`
|
||||
CommonDownfromtag int `json:"common_downfromtag"`
|
||||
VipDownfromtag int `json:"vip_downfromtag"`
|
||||
Pdl int `json:"pdl"`
|
||||
Premain int `json:"premain"`
|
||||
Hisdown int `json:"hisdown"`
|
||||
Hisbuy int `json:"hisbuy"`
|
||||
UIAlert int `json:"uiAlert"`
|
||||
Isbuy int `json:"isbuy"`
|
||||
Pneedbuy int `json:"pneedbuy"`
|
||||
Pneed int `json:"pneed"`
|
||||
Isonly int `json:"isonly"`
|
||||
Onecan int `json:"onecan"`
|
||||
Result int `json:"result"`
|
||||
Tips string `json:"tips"`
|
||||
Opi48Kurl string `json:"opi48kurl"`
|
||||
Opi96Kurl string `json:"opi96kurl"`
|
||||
Opi192Kurl string `json:"opi192kurl"`
|
||||
Opiflackurl string `json:"opiflackurl"`
|
||||
Opi128Kurl string `json:"opi128kurl"`
|
||||
Opi192Koggurl string `json:"opi192koggurl"`
|
||||
Wififromtag string `json:"wififromtag"`
|
||||
Flowfromtag string `json:"flowfromtag"`
|
||||
Wifiurl string `json:"wifiurl"`
|
||||
Flowurl string `json:"flowurl"`
|
||||
Vkey string `json:"vkey"`
|
||||
Opi30Surl string `json:"opi30surl"`
|
||||
Ekey string `json:"ekey"`
|
||||
AuthSwitch int `json:"auth_switch"`
|
||||
Subcode int `json:"subcode"`
|
||||
Opi96Koggurl string `json:"opi96koggurl"`
|
||||
AuthSwitch2 int `json:"auth_switch2"`
|
||||
} `json:"midurlinfo"`
|
||||
Servercheck string `json:"servercheck"`
|
||||
Expiration int `json:"expiration"`
|
||||
} `json:"data"`
|
||||
}
|
@ -14,28 +14,28 @@ var (
|
||||
H string // 专用音质
|
||||
}{
|
||||
sources.Q_128k: {
|
||||
E: `.mp3`,
|
||||
E: sources.X_mp3,
|
||||
H: `M500`,
|
||||
},
|
||||
sources.Q_320k: {
|
||||
E: `.mp3`,
|
||||
E: sources.X_mp3,
|
||||
H: `M800`,
|
||||
},
|
||||
sources.Q_flac: {
|
||||
E: `.flac`,
|
||||
E: sources.Q_flac,
|
||||
H: `F000`,
|
||||
},
|
||||
sources.Q_fl24: {
|
||||
E: `.flac`,
|
||||
E: sources.Q_flac,
|
||||
H: `RS01`,
|
||||
},
|
||||
`dolby`: {
|
||||
E: `.flac`,
|
||||
E: sources.Q_flac,
|
||||
H: `Q000`,
|
||||
},
|
||||
`master`: {
|
||||
E: `.flac`,
|
||||
H: `AI00`,
|
||||
E: sources.Q_flac,
|
||||
H: `AI00`, // (~~母带音质大部分都是AI提上去的~~)
|
||||
},
|
||||
}
|
||||
// qualityMapReverse = map[string]string{
|
||||
|
@ -1,62 +0,0 @@
|
||||
package wy
|
||||
|
||||
// import (
|
||||
// "lx-source/src/env"
|
||||
// wy "lx-source/src/sources/custom/wy/modules"
|
||||
// "maps"
|
||||
// "time"
|
||||
|
||||
// "github.com/ZxwyWebSite/ztool/logs"
|
||||
// "github.com/ZxwyWebSite/ztool/x/cookie"
|
||||
// )
|
||||
|
||||
/*
|
||||
刷新登录模块 (来自 NeteaseCloudMusicApi)
|
||||
逻辑:
|
||||
检测返回结果中是否含有"MUSIC_U":
|
||||
如果有则为正常刷新,延时30天
|
||||
否则延时1天
|
||||
注:
|
||||
原代码未提供详细描述,无法确定有效结果判断条件,暂时先这么写
|
||||
*/
|
||||
|
||||
// func init() {
|
||||
// env.Inits.Add(func() {
|
||||
// if env.Config.Custom.Wy_Refresh_Enable && env.Config.Custom.Wy_Api_Cookie != `` {
|
||||
// env.Tasker.Add(`wy_refresh`, func(loger *logs.Logger) error {
|
||||
// // 前置检测
|
||||
// now := time.Now().Unix()
|
||||
// if now < env.Config.Custom.Wy_Refresh_Interval {
|
||||
// loger.Debug(`Key未过期,跳过...`)
|
||||
// return nil
|
||||
// }
|
||||
// // 刷新逻辑
|
||||
// cookies := cookie.ToMap(cookie.Parse(env.Config.Custom.Wy_Api_Cookie))
|
||||
// res, err := wy.LoginRefresh(wy.ReqQuery{
|
||||
// Cookie: cookies,
|
||||
// })
|
||||
// loger.Debug(`Resp: %+v`, res)
|
||||
// if err == nil {
|
||||
// if out, ok := res.Body[`cookie`].(string); ok {
|
||||
// loger.Info(`获取数据成功`)
|
||||
// cmap := cookie.ToMap(cookie.Parse(out))
|
||||
// maps.Copy(cookies, cmap)
|
||||
// env.Config.Custom.Wy_Api_Cookie = cookie.Marshal(cookies)
|
||||
// loger.Debug(`Cookie: %#v`, cookies)
|
||||
// if cmap[`MUSIC_U`] != `` {
|
||||
// env.Config.Custom.Wy_Refresh_Interval = now + 2147483647 - 86000
|
||||
// } else {
|
||||
// env.Config.Custom.Wy_Refresh_Interval = now + 86000
|
||||
// loger.Warn(`未发现有效结果,将在下次检测时再次尝试`)
|
||||
// }
|
||||
// err = env.Cfg.Save(``)
|
||||
// if err == nil {
|
||||
// loger.Info(`配置更新成功`)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// return err
|
||||
// }, 86000, true)
|
||||
// }
|
||||
// })
|
||||
// }
|
@ -19,7 +19,7 @@ type (
|
||||
Payed int `json:"payed"`
|
||||
Flag int `json:"flag"`
|
||||
CanExtend bool `json:"canExtend"`
|
||||
FreeTrialInfo struct {
|
||||
FreeTrialInfo *struct {
|
||||
AlgData interface{} `json:"algData"`
|
||||
End int `json:"end"`
|
||||
FragmentType int `json:"fragmentType"`
|
||||
|
@ -6,6 +6,7 @@ import (
|
||||
"lx-source/src/sources/custom/utils"
|
||||
wy "lx-source/src/sources/custom/wy/modules"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
"github.com/ZxwyWebSite/ztool"
|
||||
@ -94,10 +95,16 @@ func nmModule(songMid, quality string) (ourl, msg string) {
|
||||
return
|
||||
}
|
||||
data := body.Data[0]
|
||||
if data.Code != 200 {
|
||||
msg = `触发风控或专辑单独收费: ` + strconv.Itoa(data.Code)
|
||||
return
|
||||
}
|
||||
if data.Level != rquality {
|
||||
msg = ztool.Str_FastConcat(`实际音质不匹配: `, rquality, ` <= `, data.Level)
|
||||
if !env.Config.Source.ForceFallback {
|
||||
return
|
||||
}
|
||||
}
|
||||
// br := strconv.Itoa(data.Br) // 注:由于flac返回br值不固定,暂无法进行比较
|
||||
// if br != rquality && !ztool.Chk_IsMatch(br, sources.Q_flac, sources.Q_fl24) {
|
||||
// msg = sources.E_QNotMatch
|
||||
@ -139,10 +146,16 @@ func nmCustom(songMid, quality string) (ourl, msg string) {
|
||||
return
|
||||
}
|
||||
data := body.Data[0]
|
||||
if data.Code != 200 {
|
||||
msg = `触发风控或专辑单独收费: ` + strconv.Itoa(data.Code)
|
||||
return
|
||||
}
|
||||
if data.Level != rquality {
|
||||
msg = ztool.Str_FastConcat(`实际音质不匹配: `, rquality, ` <= `, data.Level)
|
||||
if !env.Config.Source.ForceFallback {
|
||||
return
|
||||
}
|
||||
}
|
||||
ourl = utils.DelQuery(data.URL)
|
||||
return
|
||||
}
|
||||
|
71
src/sources/custom/wy/refresh.go
Normal file
71
src/sources/custom/wy/refresh.go
Normal file
@ -0,0 +1,71 @@
|
||||
package wy
|
||||
|
||||
import (
|
||||
"lx-source/src/env"
|
||||
wy "lx-source/src/sources/custom/wy/modules"
|
||||
"maps"
|
||||
|
||||
// "time"
|
||||
|
||||
"github.com/ZxwyWebSite/ztool/logs"
|
||||
"github.com/ZxwyWebSite/ztool/x/cookie"
|
||||
)
|
||||
|
||||
/*
|
||||
刷新登录模块 (来自 NeteaseCloudMusicApi)
|
||||
逻辑:
|
||||
检测返回结果中是否含有"MUSIC_U":
|
||||
如果有则为正常刷新,延时30天
|
||||
否则延时1天
|
||||
注:
|
||||
原代码未提供详细描述,无法确定有效结果判断条件,暂时先这么写
|
||||
2024-02-15:
|
||||
MUSIC_U 改变 则 6天 后 继续执行
|
||||
MUSIC_U 不变 则 1天 后 继续执行
|
||||
*/
|
||||
|
||||
func refresh(loger *logs.Logger, now int64) error {
|
||||
// 前置检测
|
||||
// now := time.Now().Unix() //(执行时间已改为从参数获取)
|
||||
if now < env.Config.Custom.Wy_Refresh_Interval {
|
||||
loger.Debug(`Key未过期,跳过...`)
|
||||
return nil
|
||||
}
|
||||
// 刷新逻辑
|
||||
cookies := cookie.ToMap(cookie.Parse(env.Config.Custom.Wy_Api_Cookie))
|
||||
res, err := wy.LoginRefresh(wy.ReqQuery{
|
||||
Cookie: cookies,
|
||||
})
|
||||
loger.Debug(`Resp: %+v`, res)
|
||||
if err == nil {
|
||||
if out, ok := res.Body[`cookie`].(string); ok {
|
||||
loger.Info(`获取数据成功`)
|
||||
cmap := cookie.ToMap(cookie.Parse(out))
|
||||
maps.Copy(cookies, cmap)
|
||||
env.Config.Custom.Wy_Api_Cookie = cookie.Marshal(cookies)
|
||||
loger.Debug(`Cookie: %#v`, cookies)
|
||||
if _, ok := cmap[`MUSIC_U`]; ok {
|
||||
// MUSIC_U 改变 则 6天 后 继续执行
|
||||
env.Config.Custom.Wy_Refresh_Interval = now + 518400 //2147483647 - 86000
|
||||
loger.Debug(`MUSIC_U 改变, 6天 后 继续执行`)
|
||||
} else {
|
||||
// MUSIC_U 不变 则 1天 后 继续执行
|
||||
env.Config.Custom.Wy_Refresh_Interval = now + 86000
|
||||
loger.Debug(`MUSIC_U 不变, 1天 后 继续执行`) //`未发现有效结果,将在下次检测时再次尝试`
|
||||
}
|
||||
err = env.Cfg.Save(``)
|
||||
if err == nil {
|
||||
loger.Info(`配置更新成功`)
|
||||
}
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func init() {
|
||||
env.Inits.Add(func() {
|
||||
if env.Config.Custom.Wy_Refresh_Enable && env.Config.Custom.Wy_Api_Cookie != `` {
|
||||
env.Tasker.Add(`wy_refresh`, refresh, 86000, true)
|
||||
}
|
||||
})
|
||||
}
|
@ -13,6 +13,10 @@ const (
|
||||
Q_320k = `320k`
|
||||
Q_flac = `flac`
|
||||
Q_fl24 = `flac24bit`
|
||||
// 文件扩展
|
||||
// X_aac = `aac`
|
||||
X_mp3 = `mp3`
|
||||
// X_flac = Q_flac
|
||||
// 通用平台
|
||||
S_wy = `wy` // 小芸
|
||||
S_mg = `mg` // 小蜜
|
||||
@ -31,6 +35,19 @@ const (
|
||||
ErrDisable = `该音乐源已被禁用`
|
||||
)
|
||||
|
||||
const (
|
||||
I_wy = iota
|
||||
I_mg
|
||||
I_kw
|
||||
I_kg
|
||||
I_tx
|
||||
I_lx
|
||||
)
|
||||
|
||||
var (
|
||||
S_al = []string{S_wy, S_mg, S_kw, S_kg, S_tx, S_lx} // 全部平台
|
||||
)
|
||||
|
||||
// 源查询接口
|
||||
/*
|
||||
Origin:
|
||||
|
33
update.md
33
update.md
@ -1,5 +1,38 @@
|
||||
## Lx-Source/更新日志
|
||||
|
||||
<!-- #### \# 2024-02-14 v1.0.3-rel (release)
|
||||
+ **停止更新:感谢这三个月的陪伴,现因无力维护,停止后续更新,发布最后版本,大家有缘再见** -->
|
||||
|
||||
#### \# 2024-02-15 v1.0.3-pre (pre)
|
||||
+ Wy源刷新登录模式确定:每天执行一次合并Cookie
|
||||
+ zTool: task: 增加传参 now(int64): 执行时间(Unix)
|
||||
+ 优化Tx源刷新登录函数,兼容计划任务错误处理模式
|
||||
+ 对源脚本进行部分更改,建议重新下载导入(http://127.0.0.1:1011/lx-custom-source.js)
|
||||
+ 简单优化旧版LinkHandler
|
||||
+ 支持Mg源自定义账号
|
||||
|
||||
#### \# 2024-02-12 v1.0.3-pre-d1 (pre)
|
||||
<!-- + 预留小洛源(lx)更名为local -->
|
||||
+ 确定tx刷新登录间隔6天
|
||||
+ 更新源接口定义
|
||||
|
||||
#### \# 2024-02-09 v1.0.3-pre-d1 (pre)
|
||||
+ 数据表结构规划中,暂时推迟
|
||||
+ 修复wy源builtin接口√
|
||||
+ 使用计划任务定期清理日志缓存√
|
||||
|
||||
#### \# 2024-02-07 v1.0.3-pre-d1 (pre)
|
||||
<!-- + 支持为本地缓存接口设置速率限制 -->
|
||||
+ 修复Tx源刷新登录到期时间问题(默认14天)
|
||||
+ Kg源使用新版接口,彻底弃用MusicId传参
|
||||
+ 优化linkrouter
|
||||
|
||||
#### \# 2024-02-03 v1.0.3-pre-d1 (dev)
|
||||
+ ~~更新一个大版本~~
|
||||
+ 源脚本:**兼容**洛雪手机端v1.2.0版本
|
||||
+ 计划:引入SQLite支持(GORM),支持缓存请求,重写Router/Caches逻辑
|
||||
+ 修复linux环境非root用户启动无法创建配置目录提示权限不够问题
|
||||
|
||||
#### \# 2024-01-31 v1.0.2-b12-d3 (dev)
|
||||
+ [重构请求处理逻辑]:
|
||||
```
|
||||
|
Reference in New Issue
Block a user