2024-02-15 v1.0.3-pre

This commit is contained in:
ZxwyWebSite
2024-02-15 23:55:41 +08:00
parent 32c2617f3f
commit 2dea36c3ce
40 changed files with 1687 additions and 612 deletions

10
init.go
View File

@ -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(`文件日志初始化成功`)

View File

@ -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下载对应平台可执行文件运行

View File

@ -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
View File

@ -0,0 +1 @@
package database

59
src/database/types.go Normal file
View 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
View File

@ -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,

View File

@ -0,0 +1 @@
package auth

View File

@ -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)
// }
// }

View File

@ -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
View 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
})
}

View File

@ -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"`

View File

@ -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
}

View File

@ -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`)

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -0,0 +1,3 @@
package custom
// type CSource struct{}

View File

@ -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
})
}

View 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
}

View 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
}

View 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"`
}
)

View 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
// }

View File

@ -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", ``)
}

View File

@ -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&notrace=0`,
`&type=`, convtype,
`&br=`, infoFile.H, infoFile.E,

View File

@ -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: {

View 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
}

View 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"`
}

View 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,
// }
)

View File

@ -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)
}
})
}

View File

@ -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 {

View File

@ -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
}

View File

@ -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)
}
})
}

View 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"`
}

View File

@ -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{

View File

@ -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)
// }
// })
// }

View File

@ -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"`

View File

@ -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
}

View 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)
}
})
}

View File

@ -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:

View File

@ -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)
+ [重构请求处理逻辑]:
```