diff --git a/main.go b/main.go index c786d02..73ba5a3 100644 --- a/main.go +++ b/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) diff --git a/src/env/env.go b/src/env/env.go index 3e05a0a..8918a3f 100644 --- a/src/env/env.go +++ b/src/env/env.go @@ -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`, diff --git a/src/middleware/dynlink/dynlink.go b/src/middleware/dynlink/dynlink.go index 2ba6b14..efb4169 100644 --- a/src/middleware/dynlink/dynlink.go +++ b/src/middleware/dynlink/dynlink.go @@ -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个参数 diff --git a/src/middleware/loadpublic/public/lx-custom-source.js b/src/middleware/loadpublic/public/lx-custom-source.js index c24b14d..eaf1826 100644 --- a/src/middleware/loadpublic/public/lx-custom-source.js +++ b/src/middleware/loadpublic/public/lx-custom-source.js @@ -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 diff --git a/src/middleware/resp/resp.go b/src/middleware/resp/resp.go index 65cc30f..a4efc3c 100644 --- a/src/middleware/resp/resp.go +++ b/src/middleware/resp/resp.go @@ -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() } diff --git a/src/router/router.go b/src/router/router.go index 89b1161..3e22be2 100644 --- a/src/router/router.go +++ b/src/router/router.go @@ -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} }) } diff --git a/src/sources/builtin/driver.go b/src/sources/builtin/driver.go index 1783ffe..4bf8d7b 100644 --- a/src/sources/builtin/driver.go +++ b/src/sources/builtin/driver.go @@ -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 diff --git a/src/sources/builtin/types.go b/src/sources/builtin/types.go index 9120313..f6ca457 100644 --- a/src/sources/builtin/types.go +++ b/src/sources/builtin/types.go @@ -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 diff --git a/update.md b/update.md index 45bcc08..ac848e8 100644 --- a/update.md +++ b/update.md @@ -1,5 +1,20 @@ ## Lx-Source/更新日志 +#### **2023-12-23 v1.0.2-b0.5 (beta)** + +### update: +- 请求解析接口时遇到的错误输出到log,不返回msg + +### feature: +- 添加kg试听源(不计入缓存) **\*(需更新脚本)** +- 注:部分歌曲只能试听前1分钟内容,无法试听的无法播放 +- (请手动删除 `data/public` 目录后启动程序重新释放静态资源) +- 脚本还是 `data/public/lx-coustom-source.js`,开了Key验证的记得将原apipass复制过去 +### bugfix: +- 修复wy源返回错误判断逻辑 + + #### 2023-12-22 v1.0.2-b0.4 (beta)