mirror of
https://github.com/ZxwyWebSite/lx-source.git
synced 2025-05-23 21:37:42 +08:00
2023-12-23 v1.0.2-b0.5
This commit is contained in:
parent
2535319005
commit
83cf0eee8f
1
main.go
1
main.go
@ -87,6 +87,7 @@ func init() {
|
||||
// logs.DefLogger(`LX-SOURCE`, logs.LevelDebu)
|
||||
// logs.Main = `LX-SOURCE`
|
||||
env.Cfg.MustInit(confPath) //conf.InitConfig(confPath)
|
||||
// fmt.Printf("%+v\n", env.Config)
|
||||
env.Loger.NewGroup(`ServHello`).Info(`欢迎使用 LX-SOURCE 洛雪音乐自定义源`)
|
||||
if !env.Config.Main.Debug {
|
||||
gin.SetMode(gin.ReleaseMode)
|
||||
|
26
src/env/env.go
vendored
26
src/env/env.go
vendored
@ -9,7 +9,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
Version = `1.0.2-β0.4`
|
||||
Version = `1.0.2-b0.5`
|
||||
)
|
||||
|
||||
var (
|
||||
@ -79,19 +79,19 @@ type (
|
||||
Cloud_Path string `comment:"Cloudreve存储路径"`
|
||||
}
|
||||
Conf struct {
|
||||
Main *Conf_Main `comment:"程序主配置"`
|
||||
Apis *Conf_Apis `comment:"接口设置"`
|
||||
Auth *Conf_Auth `comment:"访问控制"`
|
||||
Source *Conf_Source `comment:"解析源配置"`
|
||||
Script *Conf_Script `comment:"自定义脚本更新"` // ini:",omitempty"
|
||||
Cache *Conf_Cache `comment:"音乐缓存设置"`
|
||||
Main Conf_Main `comment:"程序主配置"`
|
||||
Apis Conf_Apis `comment:"接口设置"`
|
||||
Auth Conf_Auth `comment:"访问控制"`
|
||||
Source Conf_Source `comment:"解析源配置"`
|
||||
Script Conf_Script `comment:"自定义脚本更新"` // ini:",omitempty"
|
||||
Cache Conf_Cache `comment:"音乐缓存设置"`
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
// 默认配置
|
||||
defCfg = Conf{
|
||||
Main: &Conf_Main{
|
||||
Main: Conf_Main{
|
||||
Debug: false,
|
||||
Listen: `0.0.0.0:1011`,
|
||||
Gzip: false,
|
||||
@ -99,10 +99,10 @@ var (
|
||||
Print: true,
|
||||
SysLev: false,
|
||||
},
|
||||
Apis: &Conf_Apis{
|
||||
Apis: Conf_Apis{
|
||||
// BindAddr: `http://192.168.10.22:1011/`,
|
||||
},
|
||||
Auth: &Conf_Auth{
|
||||
Auth: Conf_Auth{
|
||||
ApiKey_Enable: true,
|
||||
RateLimit_Enable: false,
|
||||
RateLimit_Global: 1,
|
||||
@ -110,21 +110,21 @@ var (
|
||||
BanList_Mode: `off`,
|
||||
BanList_White: []string{`127.0.0.1`},
|
||||
},
|
||||
Source: &Conf_Source{
|
||||
Source: Conf_Source{
|
||||
Mode: `builtin`,
|
||||
FakeIP_Mode: `0`,
|
||||
FakeIP_Value: `192.168.10.2`,
|
||||
Proxy_Enable: false,
|
||||
Proxy_Address: `{protocol}://({user}:{password})@{address}:{port}`,
|
||||
},
|
||||
Script: &Conf_Script{
|
||||
Script: Conf_Script{
|
||||
Log: `发布更新 (请删除旧源后重新导入):进行了部分优化,修复了部分Bug`, // 更新日志
|
||||
|
||||
Ver: `1.0.1`, // 自定义脚本版本
|
||||
Url: `lx-custom-source.js`, // 脚本下载地址
|
||||
Force: false, // 强制推送更新
|
||||
},
|
||||
Cache: &Conf_Cache{
|
||||
Cache: Conf_Cache{
|
||||
Mode: `local`, // 缓存模式
|
||||
LinkMode: `1`,
|
||||
Local_Path: `data/cache`,
|
||||
|
@ -4,11 +4,10 @@ import (
|
||||
"lx-source/src/caches"
|
||||
"lx-source/src/caches/localcache"
|
||||
"lx-source/src/env"
|
||||
"lx-source/src/middleware/util"
|
||||
"net/http"
|
||||
|
||||
// "lx-source/src/middleware/util"
|
||||
// "net/http"
|
||||
|
||||
// "github.com/ZxwyWebSite/ztool"
|
||||
"github.com/ZxwyWebSite/ztool"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
@ -17,14 +16,13 @@ type DynLink struct {
|
||||
Link string
|
||||
}
|
||||
|
||||
func LoadHandler(r *gin.Engine) {
|
||||
loger := env.Loger.NewGroup(`DynLink`)
|
||||
if cache, ok := caches.UseCache.(*localcache.Cache); ok {
|
||||
loger.Debug(`UseStatic`)
|
||||
r.Static(`/file`, cache.Path)
|
||||
}
|
||||
// 动态链暂未完成...
|
||||
}
|
||||
// var ExLink func(string) string = func(s string) string { return s }
|
||||
|
||||
// func localInit(c string) func(string) string {
|
||||
// return func(l string) string {
|
||||
// return ztool.Str_FastConcat(c, `/`, l)
|
||||
// }
|
||||
// }
|
||||
|
||||
// Doc 动态链
|
||||
/*
|
||||
@ -38,6 +36,7 @@ func LoadHandler(r *gin.Engine) {
|
||||
+ Data2 直链缓存
|
||||
- key: "20231221/1703176257/6c782f303030303030303030312f3332306b.mp3"
|
||||
- val: "&DynLink{Mode: 0, Link: 'cache/lx/0000000001/320k'}"
|
||||
+ 返回链接: "http://127.0.0.1/file/lx/0000000001/320k.mp3"
|
||||
2. 查询缓存
|
||||
- key: "20231221/1703176257/6c782f303030303030303030312f3332306b.mp3"
|
||||
- val: "&DynLink{Mode: 0, Link: 'cache/lx/0000000001/320k'}"
|
||||
@ -52,6 +51,36 @@ func LoadHandler(r *gin.Engine) {
|
||||
|
||||
*/
|
||||
|
||||
func LoadHandler(r *gin.Engine) {
|
||||
loger := env.Loger.NewGroup(`DynLink`)
|
||||
cache, cok := caches.UseCache.(*localcache.Cache)
|
||||
// env.Cache.Set(`date/second/fname.mp3`, DynLink{Mode: 0, Link: `wy/3203127/320k.mp3`}, 0)
|
||||
// env.Cache.Set(`date/second/lname.mp3`, DynLink{Mode: 1, Link: `https://r2eu.zxwy.link/gh/lx-source/static/error.mp3`}, 0)
|
||||
// 动态链已完成(beta)...
|
||||
if env.Config.Cache.LinkMode == `dynamic` || env.Config.Cache.LinkMode == `2` /*|| true*/ {
|
||||
loger.Debug(`UseDynamic`)
|
||||
r.GET(`/file/:t/:x/:f`, func(c *gin.Context) {
|
||||
parms := util.ParaMap(c)
|
||||
t, x, f := parms[`t`], parms[`x`], parms[`f`]
|
||||
if clink, ok := env.Cache.Get(ztool.Str_FastConcat(t, `/`, x, `/`, f)); ok {
|
||||
if dyn, ok := clink.(DynLink); ok {
|
||||
if dyn.Mode == 0 && cok {
|
||||
c.File(ztool.Str_FastConcat(cache.Path, `/`, dyn.Link))
|
||||
return
|
||||
}
|
||||
c.Redirect(http.StatusFound, dyn.Link)
|
||||
return
|
||||
}
|
||||
}
|
||||
c.AbortWithStatus(http.StatusNotFound)
|
||||
})
|
||||
} else if cok {
|
||||
loger.Debug(`UseStatic`)
|
||||
// ExLink = localInit(cache.Path)
|
||||
r.Static(`/file`, cache.Path)
|
||||
}
|
||||
}
|
||||
|
||||
// func FileHandler() gin.HandlerFunc {
|
||||
// loger := env.Loger.NewGroup(`DynLink`)
|
||||
// // 为了兼容原静态链,必须设置3个参数
|
||||
|
@ -47,7 +47,9 @@ 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 query = `${source}/${id}/${quality}`; console.log('创建任务: %s, 音乐信息: %O', query, info)
|
||||
const album = source == 'kg' && info.albumId != void 0 ? '-' + info.albumId : ''
|
||||
const query = `${source}/${id}${album}/${quality}`
|
||||
console.log('创建任务: %s, 音乐信息: %O', query, info)
|
||||
const body = await httpRequest(`${apiaddr}link/${query}`, { method: 'get' });
|
||||
console.log('返回数据: %O', body, `, 耗时 ${new Date().getTime() - start} ms`)
|
||||
return body.data != '' ? body.data : Promise.reject(body.msg) // 没有获取到链接则将msg作为错误抛出
|
||||
@ -106,6 +108,8 @@ const init = () => {
|
||||
// ...defs, qualitys: source[v].qualitys, // 支持返回音质时启用 使用后端音质表
|
||||
}
|
||||
}
|
||||
sourcess['kg'] = { name: '酷狗试听', ...defaults }
|
||||
sourcess['kg'].qualitys = ['128k']
|
||||
})
|
||||
// 完成初始化
|
||||
stat = true
|
||||
|
@ -36,6 +36,8 @@ var statusMap = map[int]int{
|
||||
6: http.StatusBadRequest, // 参数错误
|
||||
}
|
||||
|
||||
var ErrMp3 = `https://r2eu.zw-cdn.tk/gh/lx-source/static/error.mp3`
|
||||
|
||||
// 返回请求
|
||||
/*
|
||||
注:Code不为0时调用c.Abort()终止Handler
|
||||
@ -47,7 +49,7 @@ func (o *Resp) Execute(c *gin.Context) {
|
||||
}
|
||||
if o.Code != 0 {
|
||||
if o.Code == 2 /*&& o.Data == ``*/ {
|
||||
o.Data = `https://r2eu.zw-cdn.tk/gh/lx-source/static/error.mp3`
|
||||
o.Data = ErrMp3
|
||||
}
|
||||
c.Abort()
|
||||
}
|
||||
|
@ -77,9 +77,12 @@ func InitRouter() *gin.Engine {
|
||||
|
||||
// 数据返回格式
|
||||
const (
|
||||
CacheHIT = `Cache HIT` // 缓存已命中
|
||||
CacheMISS = `Cache MISS` // 缓存未命中
|
||||
CacheSet = `Cache Seted` // 缓存已设置
|
||||
cacheHIT = `Cache HIT` // 缓存已命中
|
||||
cacheMISS = `Cache MISS` // 缓存未命中
|
||||
cacheSet = `Cache Seted` // 缓存已设置
|
||||
|
||||
memHIT = `Memory HIT` // 内存已命中
|
||||
memRej = `Memory Reject` // 内存已拒绝
|
||||
)
|
||||
|
||||
// 外链解析
|
||||
@ -109,9 +112,9 @@ func linkHandler(c *gin.Context) {
|
||||
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`} // 拒绝请求,当前一段时间内解析出错
|
||||
return &resp.Resp{Code: 2, Msg: memRej} // 拒绝请求,当前一段时间内解析出错 `MemCache Reject`
|
||||
}
|
||||
return &resp.Resp{Msg: `MemCache HIT`, Data: str}
|
||||
return &resp.Resp{Msg: memHIT, Data: str} // `MemCache HIT`
|
||||
}
|
||||
}
|
||||
// 查询缓存
|
||||
@ -124,7 +127,7 @@ func linkHandler(c *gin.Context) {
|
||||
sc.Debug(`Method: Get, Query: %v`, cquery.Query())
|
||||
if link := caches.UseCache.Get(cquery); link != `` {
|
||||
env.Cache.Set(cquery.Query(), link, 3600)
|
||||
return &resp.Resp{Msg: CacheHIT, Data: link}
|
||||
return &resp.Resp{Msg: cacheHIT, Data: link}
|
||||
}
|
||||
} else {
|
||||
sc.Debug(`Disabled`)
|
||||
@ -139,15 +142,15 @@ func linkHandler(c *gin.Context) {
|
||||
return &resp.Resp{Code: 2, Msg: emsg}
|
||||
}
|
||||
// 缓存并获取直链
|
||||
if outlink != `` && cstat {
|
||||
if outlink != `` && cstat && cquery.Source != `kg` {
|
||||
sc.Debug(`Method: Set, Link: %v`, outlink)
|
||||
if link := caches.UseCache.Set(cquery, outlink); link != `` {
|
||||
env.Cache.Set(cquery.Query(), link, 3600)
|
||||
return &resp.Resp{Msg: CacheSet, Data: link}
|
||||
return &resp.Resp{Msg: cacheSet, Data: link}
|
||||
}
|
||||
}
|
||||
// 无法获取直链 直接返回原链接
|
||||
env.Cache.Set(cquery.Query(), outlink, 1200)
|
||||
return &resp.Resp{Msg: CacheMISS, Data: outlink}
|
||||
return &resp.Resp{Msg: cacheMISS, Data: outlink}
|
||||
})
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ import (
|
||||
"lx-source/src/env"
|
||||
"lx-source/src/sources"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
@ -26,8 +27,11 @@ var (
|
||||
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) }}
|
||||
kg_pool = &sync.Pool{New: func() any { return new(KgApi_Song) }}
|
||||
)
|
||||
|
||||
const errHttpReq = `无法连接解析接口`
|
||||
|
||||
// 查询
|
||||
func (s *Source) GetLink(c *caches.Query) (outlink string, msg string) {
|
||||
rquery, ok := s.Verify(c)
|
||||
@ -50,7 +54,8 @@ func (s *Source) GetLink(c *caches.Query) (outlink string, msg string) {
|
||||
if err != nil {
|
||||
jx.Error(`HttpReq, Err: %s, ReTry: %v`, err, i)
|
||||
if i > 3 {
|
||||
msg = err.Error()
|
||||
jx.Error(`Wy, HttpReq: %s`, err)
|
||||
msg = errHttpReq //err.Error()
|
||||
return
|
||||
}
|
||||
time.Sleep(time.Second)
|
||||
@ -64,13 +69,13 @@ func (s *Source) GetLink(c *caches.Query) (outlink string, msg string) {
|
||||
return
|
||||
}
|
||||
var data = resp.Data[0]
|
||||
if data.FreeTrialInfo != nil {
|
||||
if data.Code != 200 || data.FreeTrialInfo != nil {
|
||||
// jx.Error("发生错误, 返回数据:\n%#v", resp)
|
||||
msg = `触发风控或专辑单独收费`
|
||||
return
|
||||
}
|
||||
if data.Level != rquery {
|
||||
msg = `实际音质不匹配`
|
||||
msg = ztool.Str_FastConcat(`实际音质不匹配: `, rquery, ` <= `, data.Level) // 实际音质不匹配: exhigh <= standard
|
||||
return
|
||||
}
|
||||
// jx.Info(`WyLink, RealQuality: %v`, data.Level)
|
||||
@ -83,7 +88,8 @@ func (s *Source) GetLink(c *caches.Query) (outlink string, msg string) {
|
||||
// jx.Debug(`Mg, Url: %v`, url)
|
||||
_, err := ztool.Net_HttpReq(http.MethodGet, url, nil, header_mg, &resp)
|
||||
if err != nil {
|
||||
msg = err.Error()
|
||||
jx.Error(`Mg, HttpReq: %s`, err)
|
||||
msg = errHttpReq //err.Error()
|
||||
return
|
||||
}
|
||||
jx.Debug(`Mg, Resp: %+v`, resp)
|
||||
@ -100,7 +106,8 @@ func (s *Source) GetLink(c *caches.Query) (outlink string, msg string) {
|
||||
// jx.Debug(`Kw, Url: %s`, url)
|
||||
_, err := ztool.Net_HttpReq(http.MethodGet, url, nil, header_kw, &resp)
|
||||
if err != nil {
|
||||
msg = err.Error()
|
||||
jx.Error(`Kw, HttpReq: %s`, err)
|
||||
msg = errHttpReq //err.Error()
|
||||
return
|
||||
}
|
||||
jx.Debug(`Kw, Resp: %+v`, resp)
|
||||
@ -110,6 +117,44 @@ func (s *Source) GetLink(c *caches.Query) (outlink string, msg string) {
|
||||
return
|
||||
}
|
||||
outlink = strings.Split(resp.Data.URL, `?`)[0]
|
||||
case s_kg:
|
||||
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 ``
|
||||
}()
|
||||
url := ztool.Str_FastConcat(api_kg, `&hash=`, sep[0], `&album_id=`, alb, `&_=`, 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 = errHttpReq //err.Error()
|
||||
return
|
||||
}
|
||||
jx.Debug(`Kw, 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.PlayURL == `` {
|
||||
if data.PlayBackupURL == `` {
|
||||
msg = `无法获取试听链接`
|
||||
return
|
||||
}
|
||||
outlink = data.PlayBackupURL
|
||||
}
|
||||
outlink = data.PlayURL
|
||||
default:
|
||||
msg = `不支持的平台`
|
||||
return
|
||||
|
@ -76,6 +76,53 @@ type (
|
||||
ProfileID string `json:"profileId"`
|
||||
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"`
|
||||
}
|
||||
)
|
||||
|
||||
const (
|
||||
@ -83,7 +130,7 @@ const (
|
||||
s_wy = `wy`
|
||||
s_mg = `mg`
|
||||
s_kw = `kw`
|
||||
// s_kg = `kg`
|
||||
s_kg = `kg`
|
||||
// s_tx = `tx`
|
||||
// s_lx = `lx`
|
||||
)
|
||||
@ -95,6 +142,7 @@ var (
|
||||
s_wy: `standard`,
|
||||
s_mg: `1`,
|
||||
s_kw: `128k`,
|
||||
s_kg: `128k`,
|
||||
},
|
||||
`320k`: {
|
||||
s_wy: `exhigh`,
|
||||
@ -119,6 +167,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`
|
||||
// Headers
|
||||
header_wy map[string]string
|
||||
header_mg map[string]string
|
||||
|
15
update.md
15
update.md
@ -1,5 +1,20 @@
|
||||
## Lx-Source/更新日志
|
||||
|
||||
#### **2023-12-23 v1.0.2-b0.5 (beta)**
|
||||
<!-- + zTool:
|
||||
- 修复直接传入make([]byte, n)时潜在内存泄露问题 -->
|
||||
### update:
|
||||
- 请求解析接口时遇到的错误输出到log,不返回msg
|
||||
<!-- - 服务端返回音质列表(需更新脚本) -->
|
||||
### feature:
|
||||
- 添加kg试听源(不计入缓存) **\*(需更新脚本)**
|
||||
- 注:部分歌曲只能试听前1分钟内容,无法试听的无法播放
|
||||
- (请手动删除 `data/public` 目录后启动程序重新释放静态资源)
|
||||
- 脚本还是 `data/public/lx-coustom-source.js`,开了Key验证的记得将原apipass复制过去
|
||||
### bugfix:
|
||||
- 修复wy源返回错误判断逻辑
|
||||
<!-- + [msg] 当前api结构与动态链实现方式不兼容,需要大改,故推迟更新 -->
|
||||
|
||||
#### 2023-12-22 v1.0.2-b0.4 (beta)
|
||||
<!-- + 没有功能更新,几个未来的想法
|
||||
- 利用缓存信息制作数据库,可通过api搜索音乐
|
||||
|
Loading…
x
Reference in New Issue
Block a user