2024-01-13 v1.0.2-b10

This commit is contained in:
ZxwyWebSite 2024-01-14 20:18:43 +08:00
parent 0bedce3bc7
commit 8d16e3be98
14 changed files with 279 additions and 92 deletions

3
.gitignore vendored
View File

@ -5,4 +5,5 @@ data/
# conf.ini
test.go
test_test.go
# src/sources/builtin/
# src/sources/builtin/
rsrc_windows_amd64.syso

View File

@ -10,7 +10,6 @@ import (
"lx-source/src/router"
"lx-source/src/sources"
"lx-source/src/sources/builtin"
"lx-source/src/sources/custom/tx"
"math/rand"
"net/http"
"os"
@ -168,9 +167,9 @@ func main() {
}
// 载入必要模块
env.Inits.Do()
env.Loger.NewGroup(`ServStart`).Info(`服务端启动, 监听地址 %s`, env.Config.Main.Listen)
loadFileLoger()
tx.Init()
env.Defer.Add(env.Tasker.Run(env.Loger)) // wait
// 启动Http服务

36
src/env/env.go vendored
View File

@ -71,12 +71,24 @@ type (
// wy (暂未实现)
Wy_Enable bool `comment:"是否开启小芸源"`
// Wy_Cookie string `comment:"账号cookie数据"`
// mg (暂未实现)
// Mg_Enable bool `comment:"是否开启小蜜源"`
// kw
Kw_Enable bool `comment:"是否开启小蜗源"`
Kw_Enable bool `comment:"是否开启小蜗源"`
Kw_Mode string `comment:"接口模式 0: bdapi(需验证), 1: kwdes"`
// kw bdapi
Kw_Bd_Uid string `comment:"user.uid"`
Kw_Bd_Token string `comment:"user.token"`
Kw_Bd_DevId string `comment:"user.device_id"`
// kw kwdes
Kw_Des_Type string `comment:"返回格式 0: text, 1: json"`
Kw_Des_Header string `comment:"请求头 User-Agent"`
// kg (暂未实现)
// Kg_Enable bool `comment:"是否开启小枸源"`
// tx
Tx_Enable bool `comment:"是否开启小秋源"`
Tx_Ukey string `comment:"Cookie中/客户端的请求体中的comm.authst"`
@ -117,7 +129,7 @@ type (
var (
// 默认配置
defCfg = Conf{
DefCfg = Conf{
Main: Conf_Main{
Debug: false,
Listen: `127.0.0.1:1011`,
@ -148,8 +160,13 @@ var (
Proxy_Address: `{protocol}://({user}:{password})@{address}:{port}`,
},
Custom: Conf_Custom{
Wy_Enable: true,
Kw_Enable: true,
Wy_Enable: true,
Kw_Enable: true,
Kw_Mode: `kwdes`,
Kw_Des_Type: `json`,
Kw_Des_Header: `okhttp/3.10.0`,
Tx_Enable: false,
Tx_Refresh_Enable: false,
Tx_Refresh_Interval: 86000,
@ -157,9 +174,10 @@ var (
Script: Conf_Script{
Log: `发布更新 (请删除旧源后重新导入)进行了部分优化修复了部分Bug`, // 更新日志
Ver: `1.0.3`, // 自定义脚本版本
Url: `lx-custom-source.js`, // 脚本下载地址
Force: true, // 强制推送更新
Ver: `1.0.3`, // 自定义脚本版本
Force: true, // 强制推送更新
Url: `public/lx-custom-source.js`, // 脚本下载地址
},
Cache: Conf_Cache{
Mode: `local`, // 缓存模式
@ -173,7 +191,7 @@ var (
Cloud_Path: `/Lx-Source/cache`,
},
}
Config = defCfg
Config = DefCfg
// 通用对象
Loger = logs.NewLogger(`LX-SOURCE`)
Cfg, _ = conf.New(&Config, &conf.Confg{
@ -184,10 +202,10 @@ var (
})
Defer = new(ztool.Err_DeferList)
Cache = memo.NewMemoStoreConf(Loger, 300) // 内存缓存 默认每5分钟进行一次GC //memo.NewMemoStore()
Inits = new(ztool.Err_DeferList)
Tasker = task.New(time.Hour, 2) // 定时任务 (暂时没有什么快速任务,默认每小时检测一次)
)
// func init() {
// }

View File

@ -20,9 +20,9 @@ var publicEM embed.FS // 打包默认Public目录 src/router/router.go
// 载入Public目录并设置路由
func LoadPublic(r *gin.Engine) {
pf := env.Loger.NewGroup(`PublicFS`)
var httpFS http.FileSystem
dir := ztool.Str_FastConcat(env.RunPath, `/data/public`)
publicFS, err := fs.Sub(publicEM, `public`)
var httpFS http.FileSystem = http.FS(publicFS)
if err != nil {
pf.Fatal(`内置Public目录载入错误: %s, 请尝试重新编译`, err)
}
@ -38,7 +38,7 @@ func LoadPublic(r *gin.Engine) {
return fmt.Errorf(`无法创建文件[%q]: %s`, relPath, err)
}
defer out.Close()
pf.Info(`导出 [%q]...`, relPath)
pf.Debug(`导出 [%q]...`, relPath)
obj, err := publicFS.Open(relPath)
if err != nil {
return fmt.Errorf(`无法打开文件[%q]: %s`, relPath, err)
@ -50,23 +50,26 @@ func LoadPublic(r *gin.Engine) {
return nil
}
if err := fs.WalkDir(publicFS, `.`, walk); err != nil {
pf.Fatal(`无法释放静态文件: %s`, err)
pf.Error(`无法释放静态文件: %s`, err)
// pf.Warn(`正在使用内置Public目录, 将无法自定义静态文件`)
// httpFS = http.FS(publicFS)
} else {
pf.Info(`全部静态资源导出完成, 祝你使用愉快 ^_^`)
}
}
httpFS = gin.Dir(dir, false)
r.GET(`/:file`, func(c *gin.Context) {
file := c.Param(`file`)
switch file {
case `favicon.ico`:
c.FileFromFS(`icon.ico`, httpFS)
case `lx-custom-source.js`:
c.FileFromFS(`lx-custom-source.js`, http.FS(publicFS))
default:
c.FileFromFS(file, httpFS)
}
})
// httpFS = gin.Dir(dir, false)
// r.GET(`/:file`, func(c *gin.Context) {
// file := c.Param(`file`)
// switch file {
// case `favicon.ico`:
// c.FileFromFS(`icon.ico`, httpFS)
// // case `lx-custom-source.js`:
// // c.FileFromFS(`lx-custom-source.js`, http.FS(publicFS))
// default:
// c.FileFromFS(file, httpFS)
// }
// })
r.StaticFileFS(`/favicon.ico`, `icon.ico`, httpFS)
r.StaticFileFS(`/lx-custom-source.js`, `lx-custom-source.js`, httpFS)
r.StaticFS(`/public`, httpFS)
}

View File

@ -35,9 +35,7 @@ func loadQMap() [][]string {
// 1.mg
m[1] = defQuality
// 2.kw
// if env.Config.Custom.Kw_Enable {
m[2] = stdQuality
// }
m[2] = defQuality
// 3.kg
m[3] = tstQuality
// 4.tx
@ -62,12 +60,12 @@ func InitRouter() *gin.Engine {
// 源信息
r.GET(`/`, func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
`version`: env.Version, // 服务端程序版本
`name`: `lx-music-source`, // 名称
`msg`: `Hello~::^-^::~v1~`, // Api大版本
`developer`: []string{`Zxwy`}, // 开发者列表, 可在保留原作者的基础上添加你自己的名字?
`version`: env.Version, // 服务端程序版本
`name`: `lx-music-source`, // 名称
`msg`: `Hello~::^-^::~v1~`, // Api大版本
// `developer`: []string{`Zxwy`}, // 开发者列表, 可在保留原作者的基础上添加你自己的名字?
// 仓库地址
`github`: `https://github.com/ZxwyWebSite/lx-source`,
// `github`: `https://github.com/ZxwyWebSite/lx-source`,
// 可用平台
`source`: gin.H{
sources.S_wy: qmap[0], //true,
@ -81,7 +79,7 @@ func InitRouter() *gin.Engine {
sources.S_lx: qmap[5],
},
// 自定义源脚本更新
`script`: env.Config.Script,
`script`: env.DefCfg.Script, //env.Config.Script,
})
})
// 静态文件

View File

@ -27,7 +27,7 @@ var (
// 并发对象池 (用户限制在Router处实现)
wy_pool = &sync.Pool{New: func() any { return new(WyApi_Song) }}
mg_pool = &sync.Pool{New: func() any { return new(MgApi_Song) }}
kw_pool = &sync.Pool{New: func() any { return new(KwApi_Song) }}
// kw_pool = &sync.Pool{New: func() any { return new(KwApi_Song) }}
kg_pool = &sync.Pool{New: func() any { return new(KgApi_Song) }}
// tx_pool = &sync.Pool{New: func() any { return new(res_tx) }}
)
@ -56,7 +56,11 @@ func (s *Source) GetLink(c *caches.Query) (outlink string, msg string) {
resp := wy_pool.Get().(*WyApi_Song)
defer wy_pool.Put(resp)
// url := ztool.Str_FastConcat(`http://`, api_wy, `?id=`, c.MusicID, `&level=`, rquery, `&noCookie=true`)
// urls := [...]string{
// ztool.Str_FastConcat(`http://`, api_wy, `?id=`, c.MusicID, `&level=`, rquery, `&noCookie=true`),
// ztool.Str_FastConcat(`https://`, api_wy, `&id=`, c.MusicID, `&level=`, rquery, `&encodeType=`, c.Extname),
// }
// url := urls[rand.Intn(len(urls))]
url := ztool.Str_FastConcat(`https://`, api_wy, `&id=`, c.MusicID, `&level=`, rquery, `&encodeType=`, c.Extname)
// jx.Debug(`Wy, Url: %v`, url)
// wy源增加后端重试 默认3次
@ -121,25 +125,6 @@ func (s *Source) GetLink(c *caches.Query) (outlink string, msg string) {
return
}
outlink = ourl
// case s_kw:
// resp := kw_pool.Get().(*KwApi_Song)
// defer kw_pool.Put(resp)
// url := ztool.Str_FastConcat(`https://`, api_kw, `/`, c.MusicID, `?isMv=0&format=`, c.Extname, `&br=`, rquery, c.Extname, `&level=`)
// // jx.Debug(`Kw, Url: %s`, url)
// _, err := ztool.Net_HttpReq(http.MethodGet, url, nil, header_kw, &resp)
// if err != nil {
// jx.Error(`Kw, HttpReq: %s`, err)
// msg = errHttpReq //err.Error()
// return
// }
// jx.Debug(`Kw, Resp: %+v`, resp)
// if resp.Code != 200 || resp.Data.AudioInfo.Bitrate == `1` {
// // jx.Debug(`Kw, Err: %#v`, resp)
// msg = ztool.Str_FastConcat(`failed: `, resp.Msg)
// return
// }
// outlink = strings.Split(resp.Data.URL, `?`)[0]
case s_kg:
resp := kg_pool.Get().(*KgApi_Song)
defer kg_pool.Put(resp)

View File

@ -59,23 +59,23 @@ type (
} `json:"data"`
}
// 酷我音乐接口 (波点)
KwApi_Song struct {
Code int `json:"code"`
Msg string `json:"msg"`
ReqID string `json:"reqId"`
Data struct {
Duration int `json:"duration"`
AudioInfo struct {
Bitrate string `json:"bitrate"`
Format string `json:"format"`
Level string `json:"level"`
Size string `json:"size"`
} `json:"audioInfo"`
URL string `json:"url"`
} `json:"data"`
ProfileID string `json:"profileId"`
CurTime int64 `json:"curTime"`
}
// KwApi_Song struct {
// Code int `json:"code"`
// Msg string `json:"msg"`
// ReqID string `json:"reqId"`
// Data struct {
// Duration int `json:"duration"`
// AudioInfo struct {
// Bitrate string `json:"bitrate"`
// Format string `json:"format"`
// Level string `json:"level"`
// Size string `json:"size"`
// } `json:"audioInfo"`
// URL string `json:"url"`
// } `json:"data"`
// ProfileID string `json:"profileId"`
// CurTime int64 `json:"curTime"`
// }
// 酷狗试听接口
KgApi_Song struct {
Status int `json:"status"`

View File

@ -122,7 +122,7 @@ var (
15, 1, 5, 12, 3, 10, 14, 5,
8, 7, 11, 0, 4, 13, 2, 11},
}
SECRET_KEY = bytesconv.StringToBytes(`ylzsxkwm`) //[]byte("ylzsxkwm")
SECRET_KEY = []byte{0x79, 0x6c, 0x7a, 0x73, 0x78, 0x6b, 0x77, 0x6d} //bytesconv.StringToBytes(`ylzsxkwm`)
)
// 初始化arrayMask

View File

@ -5,12 +5,100 @@ import (
"lx-source/src/env"
"lx-source/src/sources"
"net/http"
"strconv"
"strings"
"sync"
"github.com/ZxwyWebSite/ztool"
)
func Url(songMid, quality string) (ourl, msg string) {
var (
kw_pool *sync.Pool
Url func(string, string) (string, string)
parsemod bool
convtype string
// desParse func([]byte, *playInfo) error
// desParse func(any) ztool.Net_ResHandlerFunc
)
func init() {
env.Inits.Add(func() {
loger := env.Loger.NewGroup(`KwInit`)
switch env.Config.Custom.Kw_Mode {
case `0`, `bdapi`:
loger.Debug(`Use bdapi`)
if ztool.Chk_IsNilStr(
env.Config.Custom.Kw_Bd_Uid,
env.Config.Custom.Kw_Bd_Token,
env.Config.Custom.Kw_Bd_DevId,
) {
loger.Fatal(`使用bdapi且验证参数为空`)
}
bdheader[`uid`] = env.Config.Custom.Kw_Bd_Uid
bdheader[`devId`] = env.Config.Custom.Kw_Bd_DevId
kw_pool = &sync.Pool{New: func() any { return new(kwApi_Song) }}
Url = bdapi
case `1`, `kwdes`:
switch env.Config.Custom.Kw_Des_Type {
case `0`, `text`:
loger.Debug(`Use kwdes_text`)
convtype = `convert_url2`
// desParse = txtParse
case `1`, `json`:
loger.Debug(`Use kwdes_json`)
convtype = `convert_url_with_sign`
// desParse = ztool.Net_ResToStruct
parsemod = true
default:
loger.Fatal(`未定义的返回格式,请检查配置 [Custom].Kw_Des_Type`)
}
desheader[`User-Agent`] = env.Config.Custom.Kw_Des_Header
kw_pool = &sync.Pool{New: func() any { return new(playInfo) }}
Url = kwdes
default:
loger.Fatal(`未定义的接口模式,请检查配置 [Custom].Kw_Mode`)
}
loger = nil
})
}
func bdapi(songMid, quality string) (ourl, msg string) {
loger := env.Loger.NewGroup(`Kw`)
info, ok := fileInfo[quality]
if !ok {
msg = sources.E_QNotSupport
return
}
resp := kw_pool.Get().(*kwApi_Song)
defer kw_pool.Put(resp)
url := ztool.Str_FastConcat(
`https://bd-api.kuwo.cn/api/service/music/downloadInfo/`, songMid,
`?isMv=0&format=`, info.E,
`&br=`, info.H, info.E, //`&level=`,
`&uin=`, env.Config.Custom.Kw_Bd_Uid,
`&token=`, env.Config.Custom.Kw_Bd_Token,
)
// jx.Debug(`Kw, Url: %s`, url)
_, err := ztool.Net_HttpReq(http.MethodGet, url, nil, bdheader, &resp)
if err != nil {
loger.Error(`HttpReq: %s`, err)
msg = sources.ErrHttpReq
return
}
loger.Debug(`Resp: %+v`, resp)
if resp.Code != 200 || resp.Data.AudioInfo.Bitrate == `1` {
// jx.Debug(`Kw, Err: %#v`, resp)
msg = ztool.Str_FastConcat(`failed: `, resp.Msg)
return
}
ourl = strings.Split(resp.Data.URL, `?`)[0]
return
}
func kwdes(songMid, quality string) (ourl, msg string) {
loger := env.Loger.NewGroup(`Kw`)
infoFile, ok := fileInfo[quality]
if !ok {
@ -20,16 +108,44 @@ func Url(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(
`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=convert_url2`,
`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,
`&format=`, infoFile.E,
`&rid=`, songMid,
`&priority=bitrate&loginUid=0&network=WIFI&loginSid=0&mode=down`,
)),
)
if parsemod {
resp := kw_pool.Get().(*playInfo)
defer kw_pool.Put(resp)
err := ztool.Net_Request(http.MethodGet, target_url, nil,
[]ztool.Net_ReqHandlerFunc{ztool.Net_ReqAddHeader(desheader)},
[]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 != http.StatusOK {
msg = ztool.Str_FastConcat(`failed: `, resp.Msg)
loger.Debug(msg)
return
}
realQuality := strconv.Itoa(resp.Data.Bitrate)
if qualityMapReverse[realQuality] != quality {
msg = sources.E_QNotMatch
return
}
ourl = resp.Data.URL[:strings.Index(resp.Data.URL, `?`)]
return
}
ztool.Net_Request(http.MethodGet, target_url, nil,
[]ztool.Net_ReqHandlerFunc{
ztool.Net_ReqAddHeader(header),
ztool.Net_ReqAddHeader(desheader),
},
[]ztool.Net_ResHandlerFunc{
func(res *http.Response) (err error) {
@ -39,8 +155,8 @@ func Url(songMid, quality string) (ourl, msg string) {
return
}
if res.StatusCode != http.StatusOK {
msg = res.Status
loger.Debug(`failed: %v`, res.Status)
msg = ztool.Str_FastConcat(`failed: `, res.Status)
loger.Debug(msg)
return
}
infoData := mkMap(data)

View File

@ -0,0 +1,39 @@
package kw
type (
// KwDES json
playInfo struct {
Code int `json:"code"`
Locationid string `json:"locationid"`
Data struct {
Bitrate int `json:"bitrate"`
User string `json:"user"`
Sig string `json:"sig"`
Type string `json:"type"`
Format string `json:"format"`
P2PAudiosourceid string `json:"p2p_audiosourceid"`
Rid int `json:"rid"`
Source string `json:"source"`
URL string `json:"url"`
} `json:"data"`
Msg string `json:"msg"`
}
// 酷我音乐接口 (波点)
kwApi_Song struct {
Code int `json:"code"`
Msg string `json:"msg"`
ReqID string `json:"reqId"`
Data struct {
Duration int `json:"duration"`
AudioInfo struct {
Bitrate string `json:"bitrate"`
Format string `json:"format"`
Level string `json:"level"`
Size string `json:"size"`
} `json:"audioInfo"`
URL string `json:"url"`
} `json:"data"`
ProfileID string `json:"profileId"`
CurTime int64 `json:"curTime"`
}
)

View File

@ -9,8 +9,8 @@ import (
var (
fileInfo = map[string]struct {
E string
H string
E string // 扩展名
H string // 专用音质
}{
sources.Q_128k: {
E: `mp3`,
@ -24,14 +24,27 @@ var (
E: sources.Q_flac,
H: `2000k`,
},
sources.Q_fl24: {
E: sources.Q_flac,
H: `4000k`,
},
}
qualityMapReverse = map[string]string{
`128`: sources.Q_128k,
`320`: sources.Q_320k,
`2000`: sources.Q_flac,
`4000`: sources.Q_fl24,
}
header = map[string]string{
`User-Agent`: `okhttp/3.10.0`,
desheader = map[string]string{
// `User-Agent`: `okhttp/3.10.0`,
}
bdheader = map[string]string{
`channel`: `qq`,
`plat`: `ar`,
`net`: `wifi`,
`ver`: `3.1.2`,
// `uid`: ``,
// `devId`: `0`,
}
)
@ -44,7 +57,7 @@ func mkMap(data []byte) map[string]string {
out[bytesconv.BytesToString(pat[0])] = bytesconv.BytesToString(pat[1])
continue
}
out[`_etc`] += bytesconv.BytesToString(pat[0]) + `; `
out[`_`] += bytesconv.BytesToString(pat[0]) + `;`
}
return out
}

View File

@ -30,11 +30,13 @@ import (
// return
// }
func Init() {
if env.Config.Custom.Tx_Refresh_Enable {
env.Tasker.Add(`refresh_login`, func(l *logs.Logger) error {
refresh(l)
return nil
}, 86000, true)
}
func init() {
env.Inits.Add(func() {
if env.Config.Custom.Tx_Refresh_Enable {
env.Tasker.Add(`refresh_login`, func(l *logs.Logger) error {
refresh(l)
return nil
}, 86000, true)
}
})
}

View File

@ -24,6 +24,10 @@ const (
E_QNotSupport = `不支持的音质`
E_QNotMatch = `实际音质不匹配`
E_NoLink = `无法获取音乐链接`
// 内置错误
ErrHttpReq = `无法连接解析接口`
ErrNoLink = `无法获取试听链接`
ErrDisable = `该音乐源已被禁用`
)
// 源查询接口

View File

@ -1,5 +1,14 @@
## Lx-Source/更新日志
#### \# 2024-01-13 v1.0.2-b10 (beta)
+ 不再支持自定义Public目录默认使用内置embedFS提供服务
+ 修改脚本更新路径为 `public/lx-custom-source.js`
+ 优化kw内置源获取方式
+ 强制使用默认Script配置
+ 隐藏服务端信息中的`developer,github`字段
+ 为Windows构建添加文件属性
<!-- + 添加wy源接口分流功能 -->
#### \# 2024-01-10 v1.0.2-b10-d1 (dev)
<!-- + 内置kw接口失效暂时禁用kw源 -->
+ 修复内置kw接口