mirror of
https://github.com/ZxwyWebSite/lx-source.git
synced 2025-05-23 21:37:42 +08:00
2024-01-07 v1.0.2-b0.9
This commit is contained in:
parent
b500fca79a
commit
d086e2934b
7
main.go
7
main.go
@ -10,6 +10,7 @@ import (
|
|||||||
"lx-source/src/router"
|
"lx-source/src/router"
|
||||||
"lx-source/src/sources"
|
"lx-source/src/sources"
|
||||||
"lx-source/src/sources/builtin"
|
"lx-source/src/sources/builtin"
|
||||||
|
"lx-source/src/sources/custom/tx"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
@ -166,9 +167,13 @@ func main() {
|
|||||||
ise.Error(`未定义的音乐源,请检查配置 [Source].Mode,本次启动禁用内置源`)
|
ise.Error(`未定义的音乐源,请检查配置 [Source].Mode,本次启动禁用内置源`)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 启动Http服务
|
// 载入必要模块
|
||||||
env.Loger.NewGroup(`ServStart`).Info(`服务端启动, 监听地址 %s`, env.Config.Main.Listen)
|
env.Loger.NewGroup(`ServStart`).Info(`服务端启动, 监听地址 %s`, env.Config.Main.Listen)
|
||||||
loadFileLoger()
|
loadFileLoger()
|
||||||
|
tx.Init()
|
||||||
|
env.Defer.Add(env.Tasker.Run(env.Loger)) // wait
|
||||||
|
|
||||||
|
// 启动Http服务
|
||||||
r := router.InitRouter() //InitRouter()
|
r := router.InitRouter() //InitRouter()
|
||||||
server := &http.Server{
|
server := &http.Server{
|
||||||
Addr: env.Config.Main.Listen,
|
Addr: env.Config.Main.Listen,
|
||||||
|
33
src/env/env.go
vendored
33
src/env/env.go
vendored
@ -2,14 +2,17 @@
|
|||||||
package env
|
package env
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/ZxwyWebSite/ztool"
|
"github.com/ZxwyWebSite/ztool"
|
||||||
"github.com/ZxwyWebSite/ztool/cache/memo"
|
"github.com/ZxwyWebSite/ztool/cache/memo"
|
||||||
"github.com/ZxwyWebSite/ztool/conf"
|
"github.com/ZxwyWebSite/ztool/conf"
|
||||||
"github.com/ZxwyWebSite/ztool/logs"
|
"github.com/ZxwyWebSite/ztool/logs"
|
||||||
|
"github.com/ZxwyWebSite/ztool/task"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Version = `1.0.2-b0.8`
|
Version = `1.0.2-b0.9`
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -46,7 +49,8 @@ type (
|
|||||||
RateLimit_Block uint32 `comment:"检测范围,每分区为x秒"` // 每x秒一个分区
|
RateLimit_Block uint32 `comment:"检测范围,每分区为x秒"` // 每x秒一个分区
|
||||||
RateLimit_Global uint32 `comment:"全局速率限制,单位次每x秒(暂未开放)"`
|
RateLimit_Global uint32 `comment:"全局速率限制,单位次每x秒(暂未开放)"`
|
||||||
RateLimit_Single uint32 `comment:"单IP速率限制,单位次每x秒"`
|
RateLimit_Single uint32 `comment:"单IP速率限制,单位次每x秒"`
|
||||||
// RateLimit_BanNum uint32 `commemt:"容忍限度,超出限制N倍后封禁"`
|
RateLimit_BanNum uint32 `comment:"容忍限度,超出限制N次后封禁"`
|
||||||
|
RateLimit_BanTim uint32 `comment:"封禁后每次延长时间"`
|
||||||
// 黑白名单
|
// 黑白名单
|
||||||
BanList_Mode string `comment:"名单模式 0: off(关闭), 1: white(白名单), 2: black(黑名单)"`
|
BanList_Mode string `comment:"名单模式 0: off(关闭), 1: white(白名单), 2: black(黑名单)"`
|
||||||
BanList_White []string `comment:"host白名单"`
|
BanList_White []string `comment:"host白名单"`
|
||||||
@ -64,17 +68,20 @@ type (
|
|||||||
// ...(待实现)
|
// ...(待实现)
|
||||||
} // `comment:""`
|
} // `comment:""`
|
||||||
Conf_Custom struct {
|
Conf_Custom struct {
|
||||||
|
// wy (暂未实现)
|
||||||
|
Wy_Enable bool `comment:"是否开启小芸源"`
|
||||||
|
// Wy_Cookie string `comment:"账号cookie数据"`
|
||||||
|
// mg (暂未实现)
|
||||||
|
// Mg_Enable bool `comment:"是否开启小蜜源"`
|
||||||
// kg (暂未实现)
|
// kg (暂未实现)
|
||||||
// Kg_Enable bool `comment:"是否开启小枸源"`
|
// Kg_Enable bool `comment:"是否开启小枸源"`
|
||||||
// tx
|
// tx
|
||||||
Tx_Enable bool `comment:"是否开启小秋源"`
|
Tx_Enable bool `comment:"是否开启小秋源"`
|
||||||
Tx_Ukey string `comment:"Cookie中/客户端的请求体中的(comm.authst)"`
|
Tx_Ukey string `comment:"Cookie中/客户端的请求体中的(comm.authst)"`
|
||||||
Tx_Uuin string `comment:"key对应的QQ号"`
|
Tx_Uuin string `comment:"key对应的QQ号"`
|
||||||
// wy (暂未实现)
|
// tx refresh_login
|
||||||
// Wy_Enable bool `comment:"是否开启小芸源"`
|
Tx_Refresh_Enable bool `comment:"是否启动刷新登录"`
|
||||||
// Wy_Cookie string `comment:"账号cookie数据"`
|
Tx_Refresh_Interval int64 `comment:"刷新间隔 (由程序维护,非必要无需修改)"`
|
||||||
// mg (暂未实现)
|
|
||||||
// Mg_Enable bool `comment:"是否开启小蜜源"`
|
|
||||||
}
|
}
|
||||||
Conf_Script struct {
|
Conf_Script struct {
|
||||||
Ver string `comment:"自定义脚本版本" json:"ver"`
|
Ver string `comment:"自定义脚本版本" json:"ver"`
|
||||||
@ -111,7 +118,7 @@ var (
|
|||||||
defCfg = Conf{
|
defCfg = Conf{
|
||||||
Main: Conf_Main{
|
Main: Conf_Main{
|
||||||
Debug: false,
|
Debug: false,
|
||||||
Listen: `0.0.0.0:1011`,
|
Listen: `127.0.0.1:1011`,
|
||||||
Gzip: false,
|
Gzip: false,
|
||||||
LogPath: `/data/logfile.log`,
|
LogPath: `/data/logfile.log`,
|
||||||
Print: true,
|
Print: true,
|
||||||
@ -126,6 +133,8 @@ var (
|
|||||||
RateLimit_Block: 30,
|
RateLimit_Block: 30,
|
||||||
RateLimit_Global: 1,
|
RateLimit_Global: 1,
|
||||||
RateLimit_Single: 15,
|
RateLimit_Single: 15,
|
||||||
|
RateLimit_BanNum: 5,
|
||||||
|
RateLimit_BanTim: 10,
|
||||||
BanList_Mode: `off`,
|
BanList_Mode: `off`,
|
||||||
BanList_White: []string{`127.0.0.1`},
|
BanList_White: []string{`127.0.0.1`},
|
||||||
},
|
},
|
||||||
@ -136,6 +145,12 @@ var (
|
|||||||
Proxy_Enable: false,
|
Proxy_Enable: false,
|
||||||
Proxy_Address: `{protocol}://({user}:{password})@{address}:{port}`,
|
Proxy_Address: `{protocol}://({user}:{password})@{address}:{port}`,
|
||||||
},
|
},
|
||||||
|
Custom: Conf_Custom{
|
||||||
|
Wy_Enable: true,
|
||||||
|
Tx_Enable: false,
|
||||||
|
Tx_Refresh_Enable: false,
|
||||||
|
Tx_Refresh_Interval: 86000,
|
||||||
|
},
|
||||||
Script: Conf_Script{
|
Script: Conf_Script{
|
||||||
Log: `发布更新 (请删除旧源后重新导入):进行了部分优化,修复了部分Bug`, // 更新日志
|
Log: `发布更新 (请删除旧源后重新导入):进行了部分优化,修复了部分Bug`, // 更新日志
|
||||||
|
|
||||||
@ -166,6 +181,8 @@ var (
|
|||||||
})
|
})
|
||||||
Defer = new(ztool.Err_DeferList)
|
Defer = new(ztool.Err_DeferList)
|
||||||
Cache = memo.NewMemoStoreConf(Loger, 300) // 内存缓存 默认每5分钟进行一次GC //memo.NewMemoStore()
|
Cache = memo.NewMemoStoreConf(Loger, 300) // 内存缓存 默认每5分钟进行一次GC //memo.NewMemoStore()
|
||||||
|
|
||||||
|
Tasker = task.New(time.Hour, 2) // 定时任务 (暂时没有什么快速任务,默认每小时检测一次)
|
||||||
)
|
)
|
||||||
|
|
||||||
// func init() {
|
// func init() {
|
||||||
|
@ -12,7 +12,7 @@ import (
|
|||||||
|
|
||||||
type (
|
type (
|
||||||
RateLimit struct {
|
RateLimit struct {
|
||||||
Tim int64 // 创建时间
|
Tim int64 // 创建时间 (注:原子操作64位数据需放在结构体第一位或保证8字节对齐,否则不兼容32位平台 https://pkg.go.dev/sync/atomic#pkg-note-BUG)
|
||||||
Num uint32 // 请求次数
|
Num uint32 // 请求次数
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -40,6 +40,7 @@ func InitHandler(h gin.HandlerFunc) (out []gin.HandlerFunc) {
|
|||||||
// 判断ip是否在白名单内,是则直接放行
|
// 判断ip是否在白名单内,是则直接放行
|
||||||
判断请求数+1是否大于限制,True: 429 请求过快,请稍后重试
|
判断请求数+1是否大于限制,True: 429 请求过快,请稍后重试
|
||||||
// 判断是否超出容忍限度,是则封禁ip (暂未实现)
|
// 判断是否超出容忍限度,是则封禁ip (暂未实现)
|
||||||
|
// 超过容忍限度每次请求增加一个Block的时间
|
||||||
继续执行后续Handler
|
继续执行后续Handler
|
||||||
*/
|
*/
|
||||||
if env.Config.Auth.RateLimit_Enable {
|
if env.Config.Auth.RateLimit_Enable {
|
||||||
@ -47,7 +48,9 @@ func InitHandler(h gin.HandlerFunc) (out []gin.HandlerFunc) {
|
|||||||
loger.Info(`已启用速率限制,当前配置 %v/%v`, env.Config.Auth.RateLimit_Single, env.Config.Auth.RateLimit_Block)
|
loger.Info(`已启用速率限制,当前配置 %v/%v`, env.Config.Auth.RateLimit_Single, env.Config.Auth.RateLimit_Block)
|
||||||
newRateLimit := func() *RateLimit { return &RateLimit{Tim: time.Now().Unix(), Num: 1} }
|
newRateLimit := func() *RateLimit { return &RateLimit{Tim: time.Now().Unix(), Num: 1} }
|
||||||
block_int64 := int64(env.Config.Auth.RateLimit_Block)
|
block_int64 := int64(env.Config.Auth.RateLimit_Block)
|
||||||
block_int := int(env.Config.Auth.RateLimit_Block)
|
block_mem := int(env.Config.Auth.RateLimit_Block * env.Config.Auth.RateLimit_BanNum)
|
||||||
|
bannum := env.Config.Auth.RateLimit_Single + env.Config.Auth.RateLimit_BanNum
|
||||||
|
bantim := int64(env.Config.Auth.RateLimit_BanTim)
|
||||||
out = append(out, func(c *gin.Context) {
|
out = append(out, func(c *gin.Context) {
|
||||||
resp.Wrap(c, func() *resp.Resp {
|
resp.Wrap(c, func() *resp.Resp {
|
||||||
rip := c.RemoteIP()
|
rip := c.RemoteIP()
|
||||||
@ -62,6 +65,9 @@ func InitHandler(h gin.HandlerFunc) (out []gin.HandlerFunc) {
|
|||||||
if oip.Tim+block_int64 > time.Now().Unix() {
|
if oip.Tim+block_int64 > time.Now().Unix() {
|
||||||
oi := atomic.AddUint32(&oip.Num, 1)
|
oi := atomic.AddUint32(&oip.Num, 1)
|
||||||
if oi > env.Config.Auth.RateLimit_Single {
|
if oi > env.Config.Auth.RateLimit_Single {
|
||||||
|
if oi > bannum {
|
||||||
|
atomic.AddInt64(&oip.Tim, bantim)
|
||||||
|
}
|
||||||
return &resp.Resp{Code: 5, Msg: `请求过快,请稍后重试`}
|
return &resp.Resp{Code: 5, Msg: `请求过快,请稍后重试`}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@ -69,7 +75,7 @@ func InitHandler(h gin.HandlerFunc) (out []gin.HandlerFunc) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
val := newRateLimit()
|
val := newRateLimit()
|
||||||
if err := env.Cache.Set(rip, val, block_int); err != nil {
|
if err := env.Cache.Set(rip, val, block_mem); err != nil {
|
||||||
loger.Error(`写入内存: %s`, err)
|
loger.Error(`写入内存: %s`, err)
|
||||||
return &resp.Resp{Code: 4, Msg: `速率限制内部异常,请联系网站管理员`}
|
return &resp.Resp{Code: 4, Msg: `速率限制内部异常,请联系网站管理员`}
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,7 @@ const httpRequest = (url, options) => new Promise((resolve, reject) => {
|
|||||||
const musicUrl = async (source, info, quality) => {
|
const musicUrl = async (source, info, quality) => {
|
||||||
const start = new Date().getTime();
|
const start = new Date().getTime();
|
||||||
const id = info.hash ?? info.copyrightId ?? info.songmid // 音乐id kg源为hash, mg源为copyrightId
|
const id = info.hash ?? info.copyrightId ?? info.songmid // 音乐id kg源为hash, mg源为copyrightId
|
||||||
const ext = source == 'kg' ? info.albumId : source == 'tx' ? info.strMediaMid : ''
|
const ext = source == 'kg' ? info.albumId : '' //source == 'tx' ? info.strMediaMid
|
||||||
const query = `${source}/${id}${(ext != '' && ext != void 0) ? '-' + ext : ''}/${quality}`
|
const query = `${source}/${id}${(ext != '' && ext != void 0) ? '-' + ext : ''}/${quality}`
|
||||||
console.log('创建任务: %s, 音乐信息: %O', query, info)
|
console.log('创建任务: %s, 音乐信息: %O', query, info)
|
||||||
const body = await httpRequest(`${apiaddr}link/${query}`, { method: 'get' });
|
const body = await httpRequest(`${apiaddr}link/${query}`, { method: 'get' });
|
||||||
|
@ -21,22 +21,26 @@ var (
|
|||||||
defQuality = []string{`128k`, `320k`, `flac`, `flac24bit`}
|
defQuality = []string{`128k`, `320k`, `flac`, `flac24bit`}
|
||||||
// 试听音质
|
// 试听音质
|
||||||
tstQuality = []string{`128k`}
|
tstQuality = []string{`128k`}
|
||||||
|
// 标准音质
|
||||||
|
stdQuality = []string{`128k`, `320k`, `flac`}
|
||||||
)
|
)
|
||||||
|
|
||||||
// 自动生成支持的音质表
|
// 自动生成支持的音质表
|
||||||
func loadQMap() [][]string {
|
func loadQMap() [][]string {
|
||||||
m := make([][]string, 6)
|
m := make([][]string, 6)
|
||||||
// 0.wy
|
// 0.wy
|
||||||
m[0] = defQuality
|
if env.Config.Custom.Wy_Enable {
|
||||||
|
m[0] = defQuality
|
||||||
|
}
|
||||||
// 1.mg
|
// 1.mg
|
||||||
m[1] = defQuality
|
m[1] = defQuality
|
||||||
// 2.kw
|
// 2.kw
|
||||||
m[2] = []string{`128k`, `320k`, `flac`}
|
m[2] = stdQuality
|
||||||
// 3.kg
|
// 3.kg
|
||||||
m[3] = tstQuality
|
m[3] = tstQuality
|
||||||
// 4.tx
|
// 4.tx
|
||||||
if env.Config.Custom.Tx_Enable {
|
if env.Config.Custom.Tx_Enable {
|
||||||
m[4] = defQuality
|
m[4] = stdQuality
|
||||||
} else {
|
} else {
|
||||||
m[4] = tstQuality
|
m[4] = tstQuality
|
||||||
}
|
}
|
||||||
@ -64,11 +68,11 @@ func InitRouter() *gin.Engine {
|
|||||||
`github`: `https://github.com/ZxwyWebSite/lx-source`,
|
`github`: `https://github.com/ZxwyWebSite/lx-source`,
|
||||||
// 可用平台
|
// 可用平台
|
||||||
`source`: gin.H{
|
`source`: gin.H{
|
||||||
`wy`: qmap[0], //true,
|
sources.S_wy: qmap[0], //true,
|
||||||
`mg`: qmap[1], //true,
|
sources.S_mg: qmap[1], //true,
|
||||||
`kw`: qmap[2], //true,
|
sources.S_kw: qmap[2], //true,
|
||||||
`kg`: qmap[3], //[]string{`128k`, `320k`}, // 测试结构2, 启用时返回音质列表, 禁用为false
|
sources.S_kg: qmap[3], //[]string{`128k`, `320k`}, // 测试结构2, 启用时返回音质列表, 禁用为false
|
||||||
`tx`: qmap[4], //gin.H{ // "测试结构 不代表最终方式"
|
sources.S_tx: qmap[4], //gin.H{ // "测试结构 不代表最终方式"
|
||||||
// `enable`: false,
|
// `enable`: false,
|
||||||
// `qualitys`: []string{`128k`, `320k`, `flac`, `flac24bit`},
|
// `qualitys`: []string{`128k`, `320k`, `flac`, `flac24bit`},
|
||||||
// },
|
// },
|
||||||
@ -177,8 +181,8 @@ func linkHandler(c *gin.Context) {
|
|||||||
env.Cache.Set(cquery.Query(), ``, 600) // 发生错误的10分钟内禁止再次查询
|
env.Cache.Set(cquery.Query(), ``, 600) // 发生错误的10分钟内禁止再次查询
|
||||||
return &resp.Resp{Code: 2, Msg: emsg}
|
return &resp.Resp{Code: 2, Msg: emsg}
|
||||||
}
|
}
|
||||||
// 缓存并获取直链
|
// 缓存并获取直链 !(s == `kg` || (s == `tx` && !tx_en)) => (s != `kg` && (s != `tx` || tx_en))
|
||||||
if outlink != `` && cstat && !ztool.Chk_IsMatch(cquery.Source, `kg`, `tx`) {
|
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)
|
sc.Debug(`Method: Set, Link: %v`, outlink)
|
||||||
if link := caches.UseCache.Set(cquery, outlink); link != `` {
|
if link := caches.UseCache.Set(cquery, outlink); link != `` {
|
||||||
env.Cache.Set(cquery.Query(), link, 3600)
|
env.Cache.Set(cquery.Query(), link, 3600)
|
||||||
|
@ -25,7 +25,7 @@ func (s *Source) Verify(c *caches.Query) (rquery string, ok bool) {
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
// 并发对象池 (用户限制在Router处实现)
|
// 并发对象池 (用户限制在Router处实现)
|
||||||
wy_pool = &sync.Pool{New: func() any { return new(FyApi_Song) }}
|
wy_pool = &sync.Pool{New: func() any { return new(WyApi_Song) }}
|
||||||
mg_pool = &sync.Pool{New: func() any { return new(MgApi_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) }}
|
kg_pool = &sync.Pool{New: func() any { return new(KgApi_Song) }}
|
||||||
@ -35,12 +35,13 @@ var (
|
|||||||
const (
|
const (
|
||||||
errHttpReq = `无法连接解析接口`
|
errHttpReq = `无法连接解析接口`
|
||||||
errNoLink = `无法获取试听链接`
|
errNoLink = `无法获取试听链接`
|
||||||
|
errDisable = `该音乐源已被禁用`
|
||||||
)
|
)
|
||||||
|
|
||||||
// 查询
|
// 查询
|
||||||
func (s *Source) GetLink(c *caches.Query) (outlink string, msg string) {
|
func (s *Source) GetLink(c *caches.Query) (outlink string, msg string) {
|
||||||
rquery, ok := s.Verify(c)
|
rquery, ok := s.Verify(c)
|
||||||
if !ok {
|
if !ok /*&& c.Source != `tx`*/ {
|
||||||
msg = sources.Err_Verify //`Verify Failed`
|
msg = sources.Err_Verify //`Verify Failed`
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -48,14 +49,20 @@ func (s *Source) GetLink(c *caches.Query) (outlink string, msg string) {
|
|||||||
jx := env.Loger.NewGroup(`Sources`) //sources.Loger.AppGroup(`builtin`) //env.Loger.NewGroup(`JieXiApis`)
|
jx := env.Loger.NewGroup(`Sources`) //sources.Loger.AppGroup(`builtin`) //env.Loger.NewGroup(`JieXiApis`)
|
||||||
switch c.Source {
|
switch c.Source {
|
||||||
case s_wy:
|
case s_wy:
|
||||||
resp := wy_pool.Get().(*FyApi_Song)
|
if !env.Config.Custom.Wy_Enable {
|
||||||
|
msg = errDisable
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resp := wy_pool.Get().(*WyApi_Song)
|
||||||
defer wy_pool.Put(resp)
|
defer wy_pool.Put(resp)
|
||||||
|
|
||||||
url := ztool.Str_FastConcat(`http://`, api_wy, `?id=`, c.MusicID, `&level=`, rquery, `&noCookie=true`)
|
// url := ztool.Str_FastConcat(`http://`, api_wy, `?id=`, c.MusicID, `&level=`, rquery, `&noCookie=true`)
|
||||||
|
url := ztool.Str_FastConcat(`https://`, api_wy, `&id=`, c.MusicID, `&level=`, rquery, `&encodeType=`, c.Extname)
|
||||||
// jx.Debug(`Wy, Url: %v`, url)
|
// jx.Debug(`Wy, Url: %v`, url)
|
||||||
// wy源增加后端重试 默认3次
|
// wy源增加后端重试 默认3次
|
||||||
for i := 0; true; i++ {
|
for i := 0; true; i++ {
|
||||||
_, err := ztool.Net_HttpReq(http.MethodGet, url, nil, header_wy, &resp)
|
// _, err := ztool.Net_HttpReq(http.MethodGet, url, nil, header_wy, &resp)
|
||||||
|
_, err := ztool.Net_HttpReq(http.MethodGet, url, nil, nil, &resp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
jx.Error(`HttpReq, Err: %s, ReTry: %v`, err, i)
|
jx.Error(`HttpReq, Err: %s, ReTry: %v`, err, i)
|
||||||
if i > 3 {
|
if i > 3 {
|
||||||
|
File diff suppressed because one or more lines are too long
@ -1,5 +1,11 @@
|
|||||||
package tx
|
package tx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"lx-source/src/env"
|
||||||
|
|
||||||
|
"github.com/ZxwyWebSite/ztool/logs"
|
||||||
|
)
|
||||||
|
|
||||||
// func Info(songMid string) (info any, msg string) {
|
// func Info(songMid string) (info any, msg string) {
|
||||||
// req, emsg := getMusicInfo(songMid)
|
// req, emsg := getMusicInfo(songMid)
|
||||||
// if emsg != `` {
|
// if emsg != `` {
|
||||||
@ -23,3 +29,12 @@ package tx
|
|||||||
// }
|
// }
|
||||||
// return
|
// 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -218,30 +218,3 @@ func getMusicInfo(songMid string) (infoBody musicInfo, emsg string) {
|
|||||||
infoBody = infoResp.Req.Data
|
infoBody = infoResp.Req.Data
|
||||||
return //infoBody.Req.Data, ``
|
return //infoBody.Req.Data, ``
|
||||||
}
|
}
|
||||||
|
|
||||||
// func (infoBody *musicInfo) GetLink(songMid, strFileName string) (ourl, msg string) {
|
|
||||||
// var uauthst, uuin string = env.Config.Custom.Tx_Ukey, env.Config.Custom.Tx_Uuin
|
|
||||||
// if uuin == `` {
|
|
||||||
// uuin = `1535153710`
|
|
||||||
// }
|
|
||||||
// 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"}}}`)
|
|
||||||
// var infoResp struct {
|
|
||||||
// Code int `json:"code"`
|
|
||||||
// // Ts int64 `json:"ts"`
|
|
||||||
// // StartTs int64 `json:"start_ts"`
|
|
||||||
// // Traceid string `json:"traceid"`
|
|
||||||
// Req0 playInfo `json:"req_0"`
|
|
||||||
// }
|
|
||||||
// err := signRequest(bytesconv.StringToBytes(requestBody), &infoResp)
|
|
||||||
// if err != nil {
|
|
||||||
// msg = err.Error()
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
// infoData := infoResp.Req0.Data.Midurlinfo[0]
|
|
||||||
// if infoData.Purl == `` {
|
|
||||||
// msg = `无法获取音乐链接`
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
// ourl = infoData.Purl
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
|
@ -2,6 +2,7 @@ package tx
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"lx-source/src/env"
|
"lx-source/src/env"
|
||||||
|
"lx-source/src/sources"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/ZxwyWebSite/ztool"
|
"github.com/ZxwyWebSite/ztool"
|
||||||
@ -85,21 +86,25 @@ type playInfo struct {
|
|||||||
if没有链接:
|
if没有链接:
|
||||||
报错
|
报错
|
||||||
返回结果
|
返回结果
|
||||||
|
更新:
|
||||||
|
可通过 goto loop 实现,但可能会导致逻辑混乱 (想使用账号获取正常链接却返回试听链接)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
func Url(songMid, quality string) (ourl, msg string) {
|
func Url(songMid, quality string) (ourl, msg string) {
|
||||||
|
loger := env.Loger.NewGroup(`Tx`)
|
||||||
infoFile, ok := fileInfo[quality]
|
infoFile, ok := fileInfo[quality]
|
||||||
if !ok {
|
if !ok || (!env.Config.Custom.Tx_Enable && quality != sources.Q_128k) {
|
||||||
msg = `不支持的音质`
|
msg = `不支持的音质`
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
infoBody, emsg := getMusicInfo(songMid)
|
infoBody, emsg := getMusicInfo(songMid)
|
||||||
|
loger.Debug(`infoBody: %+v`, infoBody)
|
||||||
if emsg != `` {
|
if emsg != `` {
|
||||||
msg = emsg
|
msg = emsg
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var uauthst, uuin string = env.Config.Custom.Tx_Ukey, env.Config.Custom.Tx_Uuin
|
var uauthst, uuin string = env.Config.Custom.Tx_Ukey, env.Config.Custom.Tx_Uuin
|
||||||
if uuin == `` {
|
if uuin == `` || !env.Config.Custom.Tx_Enable {
|
||||||
uuin = `1535153710`
|
uuin = `1535153710`
|
||||||
}
|
}
|
||||||
var strFileName string
|
var strFileName string
|
||||||
@ -122,6 +127,7 @@ func Url(songMid, quality string) (ourl, msg string) {
|
|||||||
msg = err.Error()
|
msg = err.Error()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
loger.Debug(`infoResp: %+v`, infoResp)
|
||||||
infoData := infoResp.Req0.Data.Midurlinfo[0]
|
infoData := infoResp.Req0.Data.Midurlinfo[0]
|
||||||
if infoData.Purl == `` {
|
if infoData.Purl == `` {
|
||||||
msg = `无法获取音乐链接`
|
msg = `无法获取音乐链接`
|
||||||
|
147
src/sources/custom/tx/refresh_login.go
Normal file
147
src/sources/custom/tx/refresh_login.go
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
package tx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"lx-source/src/env"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ZxwyWebSite/ztool"
|
||||||
|
"github.com/ZxwyWebSite/ztool/logs"
|
||||||
|
"github.com/ZxwyWebSite/ztool/x/bytesconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*type AutoGenerated struct {
|
||||||
|
Code int `json:"code"`
|
||||||
|
Ts int64 `json:"ts"`
|
||||||
|
StartTs int64 `json:"start_ts"`
|
||||||
|
Traceid string `json:"traceid"`
|
||||||
|
Req1 struct {
|
||||||
|
Code int `json:"code"`
|
||||||
|
Data struct {
|
||||||
|
Openid string `json:"openid"`
|
||||||
|
RefreshToken string `json:"refresh_token"`
|
||||||
|
AccessToken string `json:"access_token"`
|
||||||
|
ExpiredAt int `json:"expired_at"`
|
||||||
|
Musicid int `json:"musicid"`
|
||||||
|
MusicKey string `json:"musickey"`
|
||||||
|
MusickeyCreateTime int `json:"musickeyCreateTime"`
|
||||||
|
FirstLogin int `json:"first_login"`
|
||||||
|
ErrMsg string `json:"errMsg"`
|
||||||
|
SessionKey string `json:"sessionKey"`
|
||||||
|
Unionid string `json:"unionid"`
|
||||||
|
StrMusicId string `json:"str_musicid"`
|
||||||
|
Errtip string `json:"errtip"`
|
||||||
|
Nick string `json:"nick"`
|
||||||
|
Logo string `json:"logo"`
|
||||||
|
FeedbackURL string `json:"feedbackURL"`
|
||||||
|
EncryptUin string `json:"encryptUin"`
|
||||||
|
Userip string `json:"userip"`
|
||||||
|
LastLoginTime int `json:"lastLoginTime"`
|
||||||
|
KeyExpiresIn int `json:"keyExpiresIn"`
|
||||||
|
RefreshKey string `json:"refresh_key"`
|
||||||
|
LoginType int `json:"loginType"`
|
||||||
|
Prompt2Bind int `json:"prompt2bind"`
|
||||||
|
LogoffStatus int `json:"logoffStatus"`
|
||||||
|
OtherAccounts []interface{} `json:"otherAccounts"`
|
||||||
|
OtherPhoneNo string `json:"otherPhoneNo"`
|
||||||
|
Token string `json:"token"`
|
||||||
|
IsPrized int `json:"isPrized"`
|
||||||
|
IsShowDevManage int `json:"isShowDevManage"`
|
||||||
|
ErrTip2 string `json:"errTip2"`
|
||||||
|
Tip3 string `json:"tip3"`
|
||||||
|
EncryptedPhoneNo string `json:"encryptedPhoneNo"`
|
||||||
|
PhoneNo string `json:"phoneNo"`
|
||||||
|
} `json:"data"`
|
||||||
|
} `json:"req1"`
|
||||||
|
}*/
|
||||||
|
|
||||||
|
type refreshData struct {
|
||||||
|
Req1 struct {
|
||||||
|
Code int `json:"code"`
|
||||||
|
Data struct {
|
||||||
|
ExpiredAt int64 `json:"expired_at"` // 过期时间 (Unix)
|
||||||
|
// MusicId int `json:"musicid"` // 数字uid
|
||||||
|
MusicKey string `json:"musickey"` // 账号Key
|
||||||
|
StrMusicId string `json:"str_musicid"` // 字符串uid
|
||||||
|
// KeyExpiresIn int `json:"keyExpiresIn"` // 过期时间 (秒)
|
||||||
|
} `json:"data"`
|
||||||
|
} `json:"req1"`
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
刷新登录模块 (移植自Python版)
|
||||||
|
逻辑:
|
||||||
|
1. 使用内存缓存设置过期时间,每次获取链接时取值检查,若没有设置或已过期则尝试刷新Key
|
||||||
|
2. 以计划任务方式运行,每隔一段时间自动执行
|
||||||
|
注:
|
||||||
|
第一次载入时会刷新一次测试可用性&同步过期时间 (默认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 {
|
||||||
|
loger.Debug(`Key未过期,跳过...`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
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`) {
|
||||||
|
body = ztool.Str_FastConcat(
|
||||||
|
`{"req1":{"method":"QQLogin","module":"QQConnectLogin.LoginServer","param":{"expired_in":7776000,"musicid":`,
|
||||||
|
env.Config.Custom.Tx_Uuin,
|
||||||
|
`,"musickey":"`,
|
||||||
|
env.Config.Custom.Tx_Ukey,
|
||||||
|
`"}}}`,
|
||||||
|
)
|
||||||
|
surl = `6`
|
||||||
|
} else {
|
||||||
|
loger.Error(`未知的qqmusic_key格式`)
|
||||||
|
env.Config.Custom.Tx_Refresh_Enable = false // 本次启动阻止继续执行
|
||||||
|
return
|
||||||
|
}
|
||||||
|
loger.Debug(`Body: %v`, body)
|
||||||
|
var resp refreshData
|
||||||
|
signature := sign(bytesconv.StringToBytes(body))
|
||||||
|
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),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
loger.Error(`请求Api失败: %s`, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if resp.Req1.Code != 0 {
|
||||||
|
loger.Warn("刷新登录失败, code: %v\n响应体: %+v", resp.Req1.Code, resp)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
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 // 提前一天
|
||||||
|
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)
|
||||||
|
err = env.Cfg.Save(``)
|
||||||
|
if err != nil {
|
||||||
|
loger.Error(`%s`, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
loger.Info(`数据更新成功`) // 已通过相应数据更新uin和qqmusic_key
|
||||||
|
}
|
@ -46,6 +46,9 @@ var (
|
|||||||
`Q000`: `dolby`,
|
`Q000`: `dolby`,
|
||||||
`AI00`: `master`,
|
`AI00`: `master`,
|
||||||
}
|
}
|
||||||
|
header = map[string]string{
|
||||||
|
`Referer`: `https://y.qq.com/`,
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func signRequest(data []byte, out any) error {
|
func signRequest(data []byte, out any) error {
|
||||||
@ -54,9 +57,7 @@ func signRequest(data []byte, out any) error {
|
|||||||
ztool.Str_FastConcat(`https://u.y.qq.com/cgi-bin/musics.fcg?format=json&sign=`, s),
|
ztool.Str_FastConcat(`https://u.y.qq.com/cgi-bin/musics.fcg?format=json&sign=`, s),
|
||||||
bytes.NewReader(data),
|
bytes.NewReader(data),
|
||||||
[]ztool.Net_ReqHandlerFunc{
|
[]ztool.Net_ReqHandlerFunc{
|
||||||
ztool.Net_ReqAddHeaders(map[string]string{
|
ztool.Net_ReqAddHeaders(header),
|
||||||
`Referer`: `https://y.qq.com/`,
|
|
||||||
}),
|
|
||||||
},
|
},
|
||||||
[]ztool.Net_ResHandlerFunc{
|
[]ztool.Net_ResHandlerFunc{
|
||||||
ztool.Net_ResToStruct(out),
|
ztool.Net_ResToStruct(out),
|
||||||
|
14
update.md
14
update.md
@ -1,5 +1,19 @@
|
|||||||
## Lx-Source/更新日志
|
## Lx-Source/更新日志
|
||||||
|
|
||||||
|
#### \# 2024-01-07 v1.0.2-b0.9 (beta)
|
||||||
|
<!-- + 不再导出 `public` 目录,源脚本统一到 `/lx-custom-source.js` 获取
|
||||||
|
+ 为每个源单独设置直链缓存时间 -->
|
||||||
|
+ 开启tx源账号解析时启用文件缓存
|
||||||
|
+ 移植tx源刷新登录功能
|
||||||
|
+ 注:之前没有登录过手机qq音乐的账号签到可免费领绿钻会员
|
||||||
|
+ **已知问题:生成直链会暴露uin且无法移除,共享时请务必使用缓存**
|
||||||
|
+ 完善速率限制功能:增加容忍限度、封禁时间,详见配置注释
|
||||||
|
+ 准备弃用"MusicId-字符串"传附加参数的方式
|
||||||
|
+ 计划:重构音质对应表部分,每个源使用独立音质表
|
||||||
|
+ 更换wy内置接口为qz源
|
||||||
|
+ 默认监听地址改为127.0.0.1
|
||||||
|
<!-- + *内置接口失效,暂时禁用wy源 (如恢复可修改 [Custom].Wy_Enable=true)* -->
|
||||||
|
|
||||||
#### \# 2024-01-01 v1.0.2-b0.8 (beta)
|
#### \# 2024-01-01 v1.0.2-b0.8 (beta)
|
||||||
+ 注:新年第一次更新,祝大家听歌愉快
|
+ 注:新年第一次更新,祝大家听歌愉快
|
||||||
- 根据源启用状态生成支持音质表
|
- 根据源启用状态生成支持音质表
|
||||||
|
Loading…
x
Reference in New Issue
Block a user