2023-12-22 v1.0.2-b0.4

This commit is contained in:
ZxwyWebSite 2023-12-23 03:58:34 +08:00
parent 72cdfd081e
commit 2535319005
11 changed files with 354 additions and 22 deletions

2
.gitignore vendored
View File

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

View File

@ -1,7 +1,7 @@
package caches
import (
"lx-source/src/env"
"sync"
"github.com/ZxwyWebSite/ztool"
)
@ -39,13 +39,16 @@ func (*Nullcache) Stat() bool { return false }
func (*Nullcache) Init() error { return nil }
var (
Loger = env.Loger.NewGroup(`Caches`)
// Loger = env.Loger.NewGroup(`Caches`)
UseCache Cache = &Nullcache{}
// ErrNotInited = errors.New(`缓存策略未初始化`)
query_pool = sync.Pool{New: func() any { return new(Query) }}
)
// 对象池相关 (注:结构体释放时一定要清理未导出字段)
func newQuery() *Query { return query_pool.Get().(*Query) }
func (c *Query) Free() { c.query = ``; query_pool.Put(c) }
// 根据音质判断文件后缀
func rext(q string) string {
if q == `128k` || q == `320k` {
@ -56,12 +59,18 @@ func rext(q string) string {
// 生成查询参数 (必须使用此函数初始化)
func NewQuery(s, id, q string) *Query {
return &Query{
Source: s,
MusicID: id,
Quality: q,
Extname: rext(q),
}
out := newQuery()
out.Source = s
out.MusicID = id
out.Quality = q
out.Extname = rext(q)
return out
// return &Query{
// Source: s,
// MusicID: id,
// Quality: q,
// Extname: rext(q),
// }
}
// 获取旧版查询字符串

View File

@ -3,6 +3,7 @@ package localcache
import (
"errors"
"lx-source/src/caches"
"lx-source/src/env"
"net/url"
"os"
"strings"
@ -16,7 +17,7 @@ type Cache struct {
state bool // 激活状态
}
var loger = caches.Loger.AppGroup(`local`)
var loger = env.Loger.NewGroup(`Caches`) //caches.Loger.AppGroup(`local`)
func (c *Cache) getLink(q string) string {
return ztool.Str_FastConcat(c.Bind, `/file/`, q) // c.Addr + `file/` + q

2
src/env/env.go vendored
View File

@ -9,7 +9,7 @@ import (
)
const (
Version = `1.0.2-β0.3`
Version = `1.0.2-β0.4`
)
var (

View File

@ -8,6 +8,24 @@ import (
"github.com/gin-gonic/gin"
)
func InitHandler(h gin.HandlerFunc) (out []gin.HandlerFunc) {
loger := env.Loger.NewGroup(`AuthHandler`)
// ApiKey
if env.Config.Auth.ApiKey_Enable {
loger.Debug(`ApiKeyAuth Enabled`)
out = append(out, func(c *gin.Context) {
resp.Wrap(c, func() *resp.Resp {
if auth := c.Request.Header.Get(`X-LxM-Auth`); auth != env.Config.Auth.ApiKey_Value {
loger.Debug(`验证失败: %q`, auth)
return &resp.Resp{Code: 3, Msg: `验证Key失败, 请联系网站管理员`}
}
return nil
})
})
}
return append(out, h)
}
// 请求验证
func AuthHandler(c *gin.Context) {
loger := env.Loger.NewGroup(`AuthHandler`)

View File

@ -26,6 +26,32 @@ func LoadHandler(r *gin.Engine) {
// 动态链暂未完成...
}
// Doc 动态链
/*
0. 链接格式
- (Mode: 链接模式 0:本地/1:远程, Link: 真实链接), id(uint32 4294967295)
- yyyymmdd/unixsecond/hex(:s/:id/:q).format(flac24bit->fl24)
1. 传入参数 (得到音乐链接后生成随机链并写入缓存)
+ Data1 查询缓存
- key: "lx/0000000001/320k"
- val: "`{cache.Path}/file/`20231221/1703176257/6c782f303030303030303030312f3332306b.mp3"
+ Data2 直链缓存
- key: "20231221/1703176257/6c782f303030303030303030312f3332306b.mp3"
- val: "&DynLink{Mode: 0, Link: 'cache/lx/0000000001/320k'}"
2. 查询缓存
- key: "20231221/1703176257/6c782f303030303030303030312f3332306b.mp3"
- val: "&DynLink{Mode: 0, Link: 'cache/lx/0000000001/320k'}"
- va2: "&DynLink{Mode: 1, Link: 'http://127.0.0.1/file/lx/0000000001/320k.mp3'}"
3. 实际数据 (访问 /file/:t/:x/:f)
+ if Mode==0 本地数据直接发送
- c.File(Link)
+ if Mode==1 远程数据302跳转
- c.Redirect(Link)
0. 实现思路
*/
// func FileHandler() gin.HandlerFunc {
// loger := env.Loger.NewGroup(`DynLink`)
// // 为了兼容原静态链必须设置3个参数

View File

@ -52,7 +52,7 @@ func InitRouter() *gin.Engine {
// r.StaticFile(`/favicon.ico`, `public/icon.ico`)
// r.StaticFile(`/lx-custom-source.js`, `public/lx-custom-source.js`)
// 解析接口
r.GET(`/link/:s/:id/:q`, auth.AuthHandler, linkHandler)
r.GET(`/link/:s/:id/:q`, auth.InitHandler(linkHandler)...)
dynlink.LoadHandler(r)
// r.GET(`/file/:t/:x/:f`, dynlink.FileHandler())
// if cache, ok := caches.UseCache.(*localcache.Cache); ok {
@ -86,21 +86,18 @@ const (
func linkHandler(c *gin.Context) {
resp.Wrap(c, func() *resp.Resp {
// 获取传入参数 检查合法性
// parmlen := len(c.Params)
// parms := make(map[string]string, parmlen)
// for i := 0; i < parmlen; i++ {
// parms[c.Params[i].Key] = c.Params[i].Value
// }
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
env.Loger.NewGroup(`LinkQuery`).Debug(`s: %v, id: %v, q: %v`, s, id, q)
if ztool.Chk_IsNil(s, q, id) {
if ztool.Chk_IsNilStr(s, q, id) {
return &resp.Resp{Code: 6, Msg: `参数不全`} // http.StatusBadRequest
}
cquery := caches.NewQuery(s, id, q)
// fmt.Printf("%+v\n", cquery)
defer cquery.Free()
// _, ok := sources.UseSource.Verify(cquery) // 获取请求音质 同时检测是否支持(如kw源没有flac24bit) qualitys[q][s]rquery
// if !ok {
// return &resp.Resp{Code: 6, Msg: `不支持的平台或音质`}
@ -110,6 +107,7 @@ func linkHandler(c *gin.Context) {
clink, ok := env.Cache.Get(cquery.Query())
if ok {
if str, ok := clink.(string); ok {
env.Loger.NewGroup(`MemCache`).Debug(`MemHIT [%q]=>[%q]`, cquery.Query(), str)
if str == `` {
return &resp.Resp{Code: 2, Msg: `MemCache Reject`} // 拒绝请求,当前一段时间内解析出错
}
@ -149,6 +147,7 @@ func linkHandler(c *gin.Context) {
}
}
// 无法获取直链 直接返回原链接
env.Cache.Set(cquery.Query(), outlink, 1200)
return &resp.Resp{Msg: CacheMISS, Data: outlink}
})
}

View File

@ -0,0 +1,118 @@
// 内置解析源
package builtin
import (
"lx-source/src/caches"
"lx-source/src/env"
"lx-source/src/sources"
"net/http"
"strings"
"sync"
"time"
"github.com/ZxwyWebSite/ztool"
)
type Source struct{}
// 预检 (兼容旧接口)
func (s *Source) Verify(c *caches.Query) (rquery string, ok bool) {
rquery, ok = qualitys[c.Quality][c.Source]
return
}
var (
// 并发对象池 (用户限制在Router处实现)
wy_pool = &sync.Pool{New: func() any { return new(FyApi_Song) }}
mg_pool = &sync.Pool{New: func() any { return new(MgApi_Song) }}
kw_pool = &sync.Pool{New: func() any { return new(KwApi_Song) }}
)
// 查询
func (s *Source) GetLink(c *caches.Query) (outlink string, msg string) {
rquery, ok := s.Verify(c)
if !ok {
msg = sources.Err_Verify //`Verify Failed`
return
}
// var outlink string
jx := env.Loger.NewGroup(`Sources`) //sources.Loger.AppGroup(`builtin`) //env.Loger.NewGroup(`JieXiApis`)
switch c.Source {
case s_wy:
resp := wy_pool.Get().(*FyApi_Song)
defer wy_pool.Put(resp)
url := ztool.Str_FastConcat(`http://`, api_wy, `?id=`, c.MusicID, `&level=`, rquery, `&noCookie=true`)
// jx.Debug(`Wy, Url: %v`, url)
// wy源增加后端重试 默认3次
for i := 0; true; i++ {
_, err := ztool.Net_HttpReq(http.MethodGet, url, nil, header_wy, &resp)
if err != nil {
jx.Error(`HttpReq, Err: %s, ReTry: %v`, err, i)
if i > 3 {
msg = err.Error()
return
}
time.Sleep(time.Second)
continue
}
break
}
jx.Debug(`Wy, Resp: %+v`, resp)
if len(resp.Data) == 0 {
msg = `No DataApi接口忙请稍后重试`
return
}
var data = resp.Data[0]
if data.FreeTrialInfo != nil {
// jx.Error("发生错误, 返回数据:\n%#v", resp)
msg = `触发风控或专辑单独收费`
return
}
if data.Level != rquery {
msg = `实际音质不匹配`
return
}
// jx.Info(`WyLink, RealQuality: %v`, data.Level)
outlink = data.URL
case s_mg:
resp := mg_pool.Get().(*MgApi_Song)
defer mg_pool.Put(resp)
url := ztool.Str_FastConcat(`https://`, api_mg, `?copyrightId=`, c.MusicID, `&type=`, rquery)
// jx.Debug(`Mg, Url: %v`, url)
_, err := ztool.Net_HttpReq(http.MethodGet, url, nil, header_mg, &resp)
if err != nil {
msg = err.Error()
return
}
jx.Debug(`Mg, Resp: %+v`, resp)
if link := resp.Data.PlayURL; link != `` {
outlink = `https:` + link
} // else {
// jx.Debug(`Mg, Err: %#v`, resp)
// }
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 {
msg = 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]
default:
msg = `不支持的平台`
return
}
return
}

File diff suppressed because one or more lines are too long

View File

@ -2,10 +2,9 @@ package sources
import (
"lx-source/src/caches"
"lx-source/src/env"
)
var Loger = env.Loger.NewGroup(`Sources`) // JieXiApis
// var Loger = env.Loger.NewGroup(`Sources`) // JieXiApis
const (
Err_Verify = `Verify Failed`
)

View File

@ -1,5 +1,20 @@
## Lx-Source/更新日志
#### 2023-12-22 v1.0.2-b0.4 (beta)
<!-- + 没有功能更新,几个未来的想法
- 利用缓存信息制作数据库可通过api搜索音乐
- 修改Lx-Music支持通过脚本新增搜索源 -->
+ update 更新:
- 给CacheQuery加个sync.Pool提高并发分配效率
- 请求验证改为在初始化时载入,降低每次判断性能损耗
<!-- - (现在内存缓存和文件缓存的响应速度差距约为200µs) -->
+ feature 功能:
- 临时链生成仍将推迟
+ bugfix 修复:
- 临时解决预定义(*Loger).NewGroup()无法输出到FileLoger问题
+ 注:
- 如果非调试使用建议关闭控制台日志输出 [Main].Print=false可少量提升io性能 (不影响文件日志记录)
#### 2023-12-19 1.0.2-b0.3 (dev)
+ 增加dev分支日常开发稳定了再合main防止临时补充更新情况
+ 上次补充更新内容将error.mp3换成远程连接