2023-12-31 v1.0.2-b0.8.2

This commit is contained in:
ZxwyWebSite 2024-01-01 16:01:30 +08:00
parent b26d69a162
commit 0b7e1a9425
14 changed files with 770 additions and 114 deletions

2
.gitignore vendored
View File

@ -1,7 +1,7 @@
bin/
# cache/
data/
outdated/
.outdated/
# conf.ini
test.go
test_test.go

View File

@ -11,9 +11,9 @@
## ZxwyWebSite/LX-Source
### 简介
+ LX-Music 解析源 (洛雪音乐自定义源)
+ 使用Golang编写运行效率较高
+ **由于本项目的特殊性,请低调使用,切勿宣传**
+ 测试阶段,不代表最终品质
+ 验证部分暂未完善,建议仅本地使用,不要公开发布
+ 验证部分暂未完善,建议仅本地部署,不要公开发布
+ 视频教程:[使用教程.mp4](https://r2eu.zxwy.link/gh/lx-source/v1.0.2-b0.1/%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B.mp4)
<!-- + **锟斤拷** -->

14
src/env/env.go vendored
View File

@ -63,6 +63,19 @@ type (
// 平台账号
// ...(待实现)
} // `comment:""`
Conf_Custom struct {
// kg (暂未实现)
// Kg_Enable bool `comment:"是否开启小枸源"`
// tx
Tx_Enable bool `comment:"是否开启小秋源"`
Tx_Ukey string `comment:"Cookie中/客户端的请求体中的comm.authst"`
Tx_Uuin string `comment:"key对应的QQ号"`
// wy (暂未实现)
// Wy_Enable bool `comment:"是否开启小芸源"`
// Wy_Cookie string `comment:"账号cookie数据"`
// mg (暂未实现)
// Mg_Enable bool `comment:"是否开启小蜜源"`
}
Conf_Script struct {
Ver string `comment:"自定义脚本版本" json:"ver"`
Log string `comment:"更新日志" json:"log"`
@ -87,6 +100,7 @@ type (
Apis Conf_Apis `comment:"接口设置"`
Auth Conf_Auth `comment:"访问控制"`
Source Conf_Source `comment:"解析源配置"`
Custom Conf_Custom `comment:"解析账号配置"`
Script Conf_Script `comment:"自定义脚本更新"` // ini:",omitempty"
Cache Conf_Cache `comment:"音乐缓存设置"`
}

View File

@ -46,6 +46,8 @@ func InitHandler(h gin.HandlerFunc) (out []gin.HandlerFunc) {
loger.Debug(`RateLimit Enabled`)
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} }
block_int64 := int64(env.Config.Auth.RateLimit_Block)
block_int := int(env.Config.Auth.RateLimit_Block)
out = append(out, func(c *gin.Context) {
resp.Wrap(c, func() *resp.Resp {
rip := c.RemoteIP()
@ -57,7 +59,7 @@ func InitHandler(h gin.HandlerFunc) (out []gin.HandlerFunc) {
if ok {
if oip, ok := cip.(*RateLimit); ok {
loger.Debug(`GetMemOut: %+v`, oip)
if oip.Tim+int64(env.Config.Auth.RateLimit_Block) > time.Now().Unix() {
if oip.Tim+block_int64 > time.Now().Unix() {
oi := atomic.AddUint32(&oip.Num, 1)
if oi > env.Config.Auth.RateLimit_Single {
return &resp.Resp{Code: 5, Msg: `请求过快,请稍后重试`}
@ -67,7 +69,7 @@ func InitHandler(h gin.HandlerFunc) (out []gin.HandlerFunc) {
}
}
val := newRateLimit()
if err := env.Cache.Set(rip, val, int(env.Config.Auth.RateLimit_Block)); err != nil {
if err := env.Cache.Set(rip, val, block_int); err != nil {
loger.Error(`写入内存: %s`, err)
return &resp.Resp{Code: 4, Msg: `速率限制内部异常,请联系网站管理员`}
}

View File

@ -5,6 +5,7 @@ import (
"lx-source/src/caches"
"lx-source/src/env"
"lx-source/src/sources"
"lx-source/src/sources/custom/tx"
"net/http"
"strconv"
"strings"
@ -28,7 +29,7 @@ var (
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) }}
tx_pool = &sync.Pool{New: func() any { return new(res_tx) }}
// tx_pool = &sync.Pool{New: func() any { return new(res_tx) }}
)
const (
@ -161,37 +162,45 @@ func (s *Source) GetLink(c *caches.Query) (outlink string, msg string) {
}
outlink = data.PlayBackupURL
case s_tx:
resp := tx_pool.Get().(*res_tx)
defer tx_pool.Put(resp)
sep := c.Split()
url := ztool.Str_FastConcat(api_tx,
`{"comm":{"ct":24,"cv":0,"format":"json","uin":"10086"},"req":{"method":"GetCdnDispatch","module":"CDN.SrfCdnDispatchServer","param":{"calltype":0,"guid":"1535153710","userip":""}},"req_0":{"method":"CgiGetVkey","module":"vkey.GetVkeyServer","param":{`,
func(s string) string {
if s == `` {
return ``
}
return ztool.Str_FastConcat(`"filename":["`, rquery, s, `.`, c.Extname, `"],`)
}(sep[1]),
`"guid":"1535153710","loginflag":1,"platform":"20","songmid":["`, sep[0], `"],"songtype":[0],"uin":"10086"}}}`,
)
// jx.Debug(`Tx, Url: %s`, url)
out, err := ztool.Net_HttpReq(http.MethodGet, url, nil, header_tx, &resp)
if err != nil {
jx.Error(`Tx, HttpReq: %s`, err)
msg = errHttpReq
ourl, emsg := tx.Url(sep[0], c.Quality)
if emsg != `` {
msg = emsg
return
}
jx.Debug(`Tx, Resp: %s`, out)
if resp.Code != 0 {
msg = ztool.Str_FastConcat(`Error: `, strconv.Itoa(resp.Code))
return
}
if resp.Req0.Data.Midurlinfo[0].Purl == `` {
msg = errNoLink
return
}
outlink = ztool.Str_FastConcat(`https://dl.stream.qqmusic.qq.com/`, resp.Req0.Data.Midurlinfo[0].Purl)
outlink = ourl
// case `otx`:
// resp := tx_pool.Get().(*res_tx)
// defer tx_pool.Put(resp)
// sep := c.Split()
// url := ztool.Str_FastConcat(api_tx,
// `{"comm":{"ct":24,"cv":0,"format":"json","uin":"10086"},"req":{"method":"GetCdnDispatch","module":"CDN.SrfCdnDispatchServer","param":{"calltype":0,"guid":"1535153710","userip":""}},"req_0":{"method":"CgiGetVkey","module":"vkey.GetVkeyServer","param":{`,
// func(s string) string {
// if s == `` {
// return ``
// }
// return ztool.Str_FastConcat(`"filename":["`, rquery, s, `.`, c.Extname, `"],`)
// }(sep[1]),
// `"guid":"1535153710","loginflag":1,"platform":"20","songmid":["`, sep[0], `"],"songtype":[0],"uin":"10086"}}}`,
// )
// // jx.Debug(`Tx, Url: %s`, url)
// out, err := ztool.Net_HttpReq(http.MethodGet, url, nil, header_tx, &resp)
// if err != nil {
// jx.Error(`Tx, HttpReq: %s`, err)
// msg = errHttpReq
// return
// }
// jx.Debug(`Tx, Resp: %s`, out)
// if resp.Code != 0 {
// msg = ztool.Str_FastConcat(`Error: `, strconv.Itoa(resp.Code))
// return
// }
// if resp.Req0.Data.Midurlinfo[0].Purl == `` {
// msg = errNoLink
// return
// }
// outlink = ztool.Str_FastConcat(`https://dl.stream.qqmusic.qq.com/`, resp.Req0.Data.Midurlinfo[0].Purl)
default:
msg = `不支持的平台`
return

View File

@ -124,84 +124,84 @@ type (
// EVideoID string `json:"e_video_id"`
}
// 腾讯试听接口
res_tx struct {
Code int `json:"code"`
// Ts int64 `json:"ts"`
// StartTs int64 `json:"start_ts"`
// Traceid string `json:"traceid"`
// Req struct {
// Code int `json:"code"`
// Data struct {
// Expiration int `json:"expiration"`
// Freeflowsip []string `json:"freeflowsip"`
// Keepalivefile string `json:"keepalivefile"`
// Msg string `json:"msg"`
// Retcode int `json:"retcode"`
// Servercheck string `json:"servercheck"`
// Sip []string `json:"sip"`
// Testfile2G string `json:"testfile2g"`
// Testfilewifi string `json:"testfilewifi"`
// Uin string `json:"uin"`
// Userip string `json:"userip"`
// Vkey string `json:"vkey"`
// } `json:"data"`
// } `json:"req"`
Req0 struct {
Code int `json:"code"`
Data struct {
// Uin string `json:"uin"`
// Retcode int `json:"retcode"`
// VerifyType int `json:"verify_type"`
// LoginKey string `json:"login_key"`
// Msg string `json:"msg"`
// Sip []string `json:"sip"`
// Thirdip []string `json:"thirdip"`
// Testfile2G string `json:"testfile2g"`
// Testfilewifi string `json:"testfilewifi"`
Midurlinfo []struct {
// Songmid string `json:"songmid"`
// Filename string `json:"filename"`
Purl string `json:"purl"`
// Errtype string `json:"errtype"`
// P2Pfromtag int `json:"p2pfromtag"`
// Qmdlfromtag int `json:"qmdlfromtag"`
// CommonDownfromtag int `json:"common_downfromtag"`
// VipDownfromtag int `json:"vip_downfromtag"`
// Pdl int `json:"pdl"`
// Premain int `json:"premain"`
// Hisdown int `json:"hisdown"`
// Hisbuy int `json:"hisbuy"`
// UIAlert int `json:"uiAlert"`
// Isbuy int `json:"isbuy"`
// Pneedbuy int `json:"pneedbuy"`
// Pneed int `json:"pneed"`
// Isonly int `json:"isonly"`
// Onecan int `json:"onecan"`
// Result int `json:"result"`
// Tips string `json:"tips"`
// Opi48Kurl string `json:"opi48kurl"`
// Opi96Kurl string `json:"opi96kurl"`
// Opi192Kurl string `json:"opi192kurl"`
// Opiflackurl string `json:"opiflackurl"`
// Opi128Kurl string `json:"opi128kurl"`
// Opi192Koggurl string `json:"opi192koggurl"`
// Wififromtag string `json:"wififromtag"`
// Flowfromtag string `json:"flowfromtag"`
// Wifiurl string `json:"wifiurl"`
// Flowurl string `json:"flowurl"`
// Vkey string `json:"vkey"`
// Opi30Surl string `json:"opi30surl"`
// Ekey string `json:"ekey"`
// AuthSwitch int `json:"auth_switch"`
// Subcode int `json:"subcode"`
// Opi96Koggurl string `json:"opi96koggurl"`
// AuthSwitch2 int `json:"auth_switch2"`
} `json:"midurlinfo"`
// Servercheck string `json:"servercheck"`
// Expiration int `json:"expiration"`
} `json:"data"`
} `json:"req_0"`
}
// res_tx struct {
// Code int `json:"code"`
// // Ts int64 `json:"ts"`
// // StartTs int64 `json:"start_ts"`
// // Traceid string `json:"traceid"`
// // Req struct {
// // Code int `json:"code"`
// // Data struct {
// // Expiration int `json:"expiration"`
// // Freeflowsip []string `json:"freeflowsip"`
// // Keepalivefile string `json:"keepalivefile"`
// // Msg string `json:"msg"`
// // Retcode int `json:"retcode"`
// // Servercheck string `json:"servercheck"`
// // Sip []string `json:"sip"`
// // Testfile2G string `json:"testfile2g"`
// // Testfilewifi string `json:"testfilewifi"`
// // Uin string `json:"uin"`
// // Userip string `json:"userip"`
// // Vkey string `json:"vkey"`
// // } `json:"data"`
// // } `json:"req"`
// Req0 struct {
// Code int `json:"code"`
// Data struct {
// // Uin string `json:"uin"`
// // Retcode int `json:"retcode"`
// // VerifyType int `json:"verify_type"`
// // LoginKey string `json:"login_key"`
// // Msg string `json:"msg"`
// // Sip []string `json:"sip"`
// // Thirdip []string `json:"thirdip"`
// // Testfile2G string `json:"testfile2g"`
// // Testfilewifi string `json:"testfilewifi"`
// Midurlinfo []struct {
// // Songmid string `json:"songmid"`
// // Filename string `json:"filename"`
// Purl string `json:"purl"`
// // Errtype string `json:"errtype"`
// // P2Pfromtag int `json:"p2pfromtag"`
// // Qmdlfromtag int `json:"qmdlfromtag"`
// // CommonDownfromtag int `json:"common_downfromtag"`
// // VipDownfromtag int `json:"vip_downfromtag"`
// // Pdl int `json:"pdl"`
// // Premain int `json:"premain"`
// // Hisdown int `json:"hisdown"`
// // Hisbuy int `json:"hisbuy"`
// // UIAlert int `json:"uiAlert"`
// // Isbuy int `json:"isbuy"`
// // Pneedbuy int `json:"pneedbuy"`
// // Pneed int `json:"pneed"`
// // Isonly int `json:"isonly"`
// // Onecan int `json:"onecan"`
// // Result int `json:"result"`
// // Tips string `json:"tips"`
// // Opi48Kurl string `json:"opi48kurl"`
// // Opi96Kurl string `json:"opi96kurl"`
// // Opi192Kurl string `json:"opi192kurl"`
// // Opiflackurl string `json:"opiflackurl"`
// // Opi128Kurl string `json:"opi128kurl"`
// // Opi192Koggurl string `json:"opi192koggurl"`
// // Wififromtag string `json:"wififromtag"`
// // Flowfromtag string `json:"flowfromtag"`
// // Wifiurl string `json:"wifiurl"`
// // Flowurl string `json:"flowurl"`
// // Vkey string `json:"vkey"`
// // Opi30Surl string `json:"opi30surl"`
// // Ekey string `json:"ekey"`
// // AuthSwitch int `json:"auth_switch"`
// // Subcode int `json:"subcode"`
// // Opi96Koggurl string `json:"opi96koggurl"`
// // AuthSwitch2 int `json:"auth_switch2"`
// } `json:"midurlinfo"`
// // Servercheck string `json:"servercheck"`
// // Expiration int `json:"expiration"`
// } `json:"data"`
// } `json:"req_0"`
// }
)
const (
@ -255,12 +255,12 @@ var (
api_mg string
api_kw string
api_kg string = `https://wwwapi.kugou.com/yy/index.php?r=play/getdata&platid=4&mid=1`
api_tx string = `https://u.y.qq.com/cgi-bin/musicu.fcg?data=`
// api_tx string = `https://u.y.qq.com/cgi-bin/musicu.fcg?data=`
// Headers
header_wy map[string]string
header_mg map[string]string
header_kw map[string]string
header_tx = map[string]string{`Referer`: `https://y.qq.com/`}
// header_tx = map[string]string{`Referer`: `https://y.qq.com/`}
)
func init() {

View File

@ -0,0 +1,126 @@
package tx
import (
"crypto/md5"
"encoding/hex"
"regexp"
"strings"
"github.com/ZxwyWebSite/ztool"
"github.com/ZxwyWebSite/ztool/x/bytesconv"
)
func v(b string) string {
res := []byte{}
p := [...]int{21, 4, 9, 26, 16, 20, 27, 30}
for _, x := range p {
res = append(res, b[x])
}
return bytesconv.BytesToString(res) //string(res)
}
func c(b string) string {
res := []byte{}
p := [...]int{18, 11, 3, 2, 1, 7, 6, 25}
for _, x := range p {
res = append(res, b[x])
}
return bytesconv.BytesToString(res) //string(res)
}
func y(a, b, c int) (e []int) {
// e := []int{}
r25 := a >> 2
if b != 0 && c != 0 {
r26 := a & 3
r26_2 := r26 << 4
r26_3 := b >> 4
r26_4 := r26_2 | r26_3
r27 := b & 15
r27_2 := r27 << 2
r27_3 := r27_2 | (c >> 6)
r28 := c & 63
e = append(e, r25)
e = append(e, r26_4)
e = append(e, r27_3)
e = append(e, r28)
} else {
r10 := a >> 2
r11 := a & 3
r11_2 := r11 << 4
e = append(e, r10)
e = append(e, r11_2)
}
return //e
}
func n(ls []int) string {
e := []int{}
for i, r := 0, len(ls); i < r; i += 3 {
if i < r-2 {
e = append(e, y(ls[i], ls[i+1], ls[i+2])...)
} else {
e = append(e, y(ls[i], 0, 0)...)
}
}
res := []byte{}
b64all := `ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=`
for _, i := range e {
res = append(res, b64all[i])
}
return bytesconv.BytesToString(res)
}
func t(b string) (res []int) {
zd := map[string]int{
`0`: 0,
`1`: 1,
`2`: 2,
`3`: 3,
`4`: 4,
`5`: 5,
`6`: 6,
`7`: 7,
`8`: 8,
`9`: 9,
`A`: 10,
`B`: 11,
`C`: 12,
`D`: 13,
`E`: 14,
`F`: 15,
}
ol := [...]int{212, 45, 80, 68, 195, 163, 163, 203, 157, 220, 254, 91, 204, 79, 104, 6}
// res := []int{}
j := 0
for i, r := 0, len(b); i < r; i += 2 {
one := zd[string(b[i])]
two := zd[string(b[i+1])]
r := one*16 ^ two
// if j >= 16 {
// break
// }
res = append(res, r^ol[j])
j++
}
return //res
}
func createMD5(s []byte) string {
hash := md5.New()
hash.Write(s)
return hex.EncodeToString(hash.Sum(nil))
}
func sign(params []byte) string {
md5Str := strings.ToUpper(createMD5(params))
h := v(md5Str)
e := c(md5Str)
ls := t(md5Str)
m := n(ls)
res := ztool.Str_FastConcat(`zzb`, h, m, e) //`zzb` + h + m + e
res = strings.ToLower(res)
r := regexp.MustCompile(`[\/+]`)
res = r.ReplaceAllString(res, ``)
return res
}

View File

@ -0,0 +1,25 @@
package tx
// func Info(songMid string) (info any, msg string) {
// req, emsg := getMusicInfo(songMid)
// if emsg != `` {
// msg = emsg
// return
// }
// var singerList []any
// for _, s := range req.TrackInfo.Singer {
// // item := new(struct{
// // ID int `json:"id"`
// // Str string
// // })
// // item.ID = s.ID
// singerList = append(singerList, s)
// }
// var file_info map[string]struct{
// Size interface{}
// }
// if req.TrackInfo.File.Size128Mp3 != 0 {
// file_info[`128k`].Size =
// }
// return
// }

View File

@ -0,0 +1,247 @@
package tx
import (
"github.com/ZxwyWebSite/ztool"
"github.com/ZxwyWebSite/ztool/x/bytesconv"
)
type musicInfo struct {
// Info struct {
// Company struct {
// Title string `json:"title"`
// Type string `json:"type"`
// Content []struct {
// ID int `json:"id"`
// Value string `json:"value"`
// Mid string `json:"mid"`
// Type int `json:"type"`
// ShowType int `json:"show_type"`
// IsParent int `json:"is_parent"`
// Picurl string `json:"picurl"`
// ReadCnt int `json:"read_cnt"`
// Author string `json:"author"`
// Jumpurl string `json:"jumpurl"`
// OriPicurl string `json:"ori_picurl"`
// } `json:"content"`
// Pos int `json:"pos"`
// More int `json:"more"`
// Selected string `json:"selected"`
// UsePlatform int `json:"use_platform"`
// } `json:"company"`
// Genre struct {
// Title string `json:"title"`
// Type string `json:"type"`
// Content []struct {
// ID int `json:"id"`
// Value string `json:"value"`
// Mid string `json:"mid"`
// Type int `json:"type"`
// ShowType int `json:"show_type"`
// IsParent int `json:"is_parent"`
// Picurl string `json:"picurl"`
// ReadCnt int `json:"read_cnt"`
// Author string `json:"author"`
// Jumpurl string `json:"jumpurl"`
// OriPicurl string `json:"ori_picurl"`
// } `json:"content"`
// Pos int `json:"pos"`
// More int `json:"more"`
// Selected string `json:"selected"`
// UsePlatform int `json:"use_platform"`
// } `json:"genre"`
// Lan struct {
// Title string `json:"title"`
// Type string `json:"type"`
// Content []struct {
// ID int `json:"id"`
// Value string `json:"value"`
// Mid string `json:"mid"`
// Type int `json:"type"`
// ShowType int `json:"show_type"`
// IsParent int `json:"is_parent"`
// Picurl string `json:"picurl"`
// ReadCnt int `json:"read_cnt"`
// Author string `json:"author"`
// Jumpurl string `json:"jumpurl"`
// OriPicurl string `json:"ori_picurl"`
// } `json:"content"`
// Pos int `json:"pos"`
// More int `json:"more"`
// Selected string `json:"selected"`
// UsePlatform int `json:"use_platform"`
// } `json:"lan"`
// } `json:"info"`
// Extras struct {
// Name string `json:"name"`
// Transname string `json:"transname"`
// Subtitle string `json:"subtitle"`
// From string `json:"from"`
// Wikiurl string `json:"wikiurl"`
// } `json:"extras"`
TrackInfo struct {
ID int `json:"id"`
Type int `json:"type"`
Mid string `json:"mid"`
Name string `json:"name"`
Title string `json:"title"`
Subtitle string `json:"subtitle"`
Singer []struct {
ID int `json:"id"`
Mid string `json:"mid"`
Name string `json:"name"`
Title string `json:"title"`
Type int `json:"type"`
Uin int `json:"uin"`
} `json:"singer"`
Album struct {
ID int `json:"id"`
Mid string `json:"mid"`
Name string `json:"name"`
Title string `json:"title"`
Subtitle string `json:"subtitle"`
TimePublic string `json:"time_public"`
Pmid string `json:"pmid"`
} `json:"album"`
Mv struct {
ID int `json:"id"`
Vid string `json:"vid"`
Name string `json:"name"`
Title string `json:"title"`
Vt int `json:"vt"`
} `json:"mv"`
Interval int `json:"interval"`
Isonly int `json:"isonly"`
Language int `json:"language"`
Genre int `json:"genre"`
IndexCd int `json:"index_cd"`
IndexAlbum int `json:"index_album"`
TimePublic string `json:"time_public"`
Status int `json:"status"`
Fnote int `json:"fnote"`
File struct {
MediaMid string `json:"media_mid"`
Size24Aac int `json:"size_24aac"`
Size48Aac int `json:"size_48aac"`
Size96Aac int `json:"size_96aac"`
Size192Ogg int `json:"size_192ogg"`
Size192Aac int `json:"size_192aac"`
Size128Mp3 int `json:"size_128mp3"`
Size320Mp3 int `json:"size_320mp3"`
SizeApe int `json:"size_ape"`
SizeFlac int `json:"size_flac"`
SizeDts int `json:"size_dts"`
SizeTry int `json:"size_try"`
TryBegin int `json:"try_begin"`
TryEnd int `json:"try_end"`
URL string `json:"url"`
SizeHires int `json:"size_hires"`
HiresSample int `json:"hires_sample"`
HiresBitdepth int `json:"hires_bitdepth"`
B30S int `json:"b_30s"`
E30S int `json:"e_30s"`
Size96Ogg int `json:"size_96ogg"`
Size360Ra []interface{} `json:"size_360ra"`
SizeDolby int `json:"size_dolby"`
SizeNew []int `json:"size_new"`
} `json:"file"`
Pay struct {
PayMonth int `json:"pay_month"`
PriceTrack int `json:"price_track"`
PriceAlbum int `json:"price_album"`
PayPlay int `json:"pay_play"`
PayDown int `json:"pay_down"`
PayStatus int `json:"pay_status"`
TimeFree int `json:"time_free"`
} `json:"pay"`
Action struct {
Switch int `json:"switch"`
Msgid int `json:"msgid"`
Alert int `json:"alert"`
Icons int `json:"icons"`
Msgshare int `json:"msgshare"`
Msgfav int `json:"msgfav"`
Msgdown int `json:"msgdown"`
Msgpay int `json:"msgpay"`
Switch2 int `json:"switch2"`
Icon2 int `json:"icon2"`
} `json:"action"`
Ksong struct {
ID int `json:"id"`
Mid string `json:"mid"`
} `json:"ksong"`
Volume struct {
Gain float64 `json:"gain"`
Peak float64 `json:"peak"`
Lra float64 `json:"lra"`
} `json:"volume"`
Label string `json:"label"`
URL string `json:"url"`
Bpm int `json:"bpm"`
Version int `json:"version"`
Trace string `json:"trace"`
DataType int `json:"data_type"`
ModifyStamp int `json:"modify_stamp"`
Pingpong string `json:"pingpong"`
Ppurl string `json:"ppurl"`
Tid int `json:"tid"`
Ov int `json:"ov"`
Sa int `json:"sa"`
Es string `json:"es"`
Vs []string `json:"vs"`
Vi []int `json:"vi"`
Ktag string `json:"ktag"`
Vf []float64 `json:"vf"`
} `json:"track_info"`
}
func getMusicInfo(songMid string) (infoBody musicInfo, emsg string) {
infoReqBody := ztool.Str_FastConcat(`{"comm":{"ct":"19","cv":"1859","uin":"0"},"req":{"method":"get_song_detail_yqq","module":"music.pf_song_detail_svr","param":{"song_mid":"`, songMid, `","song_type":0}}}`)
var infoResp struct {
Code int `json:"code"`
// Ts int64 `json:"ts"`
// StartTs int64 `json:"start_ts"`
// Traceid string `json:"traceid"`
Req struct {
Code int `json:"code"`
Data musicInfo `json:"data"`
} `json:"req"`
}
err := signRequest(bytesconv.StringToBytes(infoReqBody), &infoResp)
if err != nil {
emsg = err.Error()
return //nil, err.Error()
}
if infoResp.Code != 0 || infoResp.Req.Code != 0 {
emsg = `获取音乐信息失败`
return //nil, `获取音乐信息失败`
}
infoBody = infoResp.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
// }

View File

@ -0,0 +1,137 @@
package tx
import (
"lx-source/src/env"
"strings"
"github.com/ZxwyWebSite/ztool"
"github.com/ZxwyWebSite/ztool/x/bytesconv"
)
type playInfo struct {
Code int `json:"code"`
Data struct {
Uin string `json:"uin"`
Retcode int `json:"retcode"`
VerifyType int `json:"verify_type"`
LoginKey string `json:"login_key"`
Msg string `json:"msg"`
Sip []string `json:"sip"`
Thirdip []string `json:"thirdip"`
Testfile2G string `json:"testfile2g"`
Testfilewifi string `json:"testfilewifi"`
Midurlinfo []struct {
Songmid string `json:"songmid"`
Filename string `json:"filename"`
Purl string `json:"purl"`
Errtype string `json:"errtype"`
P2Pfromtag int `json:"p2pfromtag"`
Qmdlfromtag int `json:"qmdlfromtag"`
CommonDownfromtag int `json:"common_downfromtag"`
VipDownfromtag int `json:"vip_downfromtag"`
Pdl int `json:"pdl"`
Premain int `json:"premain"`
Hisdown int `json:"hisdown"`
Hisbuy int `json:"hisbuy"`
UIAlert int `json:"uiAlert"`
Isbuy int `json:"isbuy"`
Pneedbuy int `json:"pneedbuy"`
Pneed int `json:"pneed"`
Isonly int `json:"isonly"`
Onecan int `json:"onecan"`
Result int `json:"result"`
Tips string `json:"tips"`
Opi48Kurl string `json:"opi48kurl"`
Opi96Kurl string `json:"opi96kurl"`
Opi192Kurl string `json:"opi192kurl"`
Opiflackurl string `json:"opiflackurl"`
Opi128Kurl string `json:"opi128kurl"`
Opi192Koggurl string `json:"opi192koggurl"`
Wififromtag string `json:"wififromtag"`
Flowfromtag string `json:"flowfromtag"`
Wifiurl string `json:"wifiurl"`
Flowurl string `json:"flowurl"`
Vkey string `json:"vkey"`
Opi30Surl string `json:"opi30surl"`
Ekey string `json:"ekey"`
AuthSwitch int `json:"auth_switch"`
Subcode int `json:"subcode"`
Opi96Koggurl string `json:"opi96koggurl"`
AuthSwitch2 int `json:"auth_switch2"`
} `json:"midurlinfo"`
Servercheck string `json:"servercheck"`
Expiration int `json:"expiration"`
} `json:"data"`
}
/*
音乐URL获取逻辑
if需要付费播放and无账号信息:
试听获取
el有账号信息or无需付费:
正常获取
if没有链接:
尝试获取试听
if还没有链接:
报错
返回结果
以上逻辑暂时没想好怎么改
当前根据配置文件 [Custom].Tx_Enable 是否开启判断
if需要付费播放and未开启账号解析:
试听获取
el无需付费or有账号信息:
正常获取
if没有链接:
报错
返回结果
*/
func Url(songMid, quality string) (ourl, msg string) {
infoFile, ok := fileInfo[quality]
if !ok {
msg = `不支持的音质`
return
}
infoBody, emsg := getMusicInfo(songMid)
if emsg != `` {
msg = emsg
return
}
var uauthst, uuin string = env.Config.Custom.Tx_Ukey, env.Config.Custom.Tx_Uuin
if uuin == `` {
uuin = `1535153710`
}
var strFileName string
tryLink := infoBody.TrackInfo.Pay.PayPlay == 1 && /*uauthst == ``&&*/ !env.Config.Custom.Tx_Enable
if tryLink {
strFileName = ztool.Str_FastConcat(`RS02`, infoBody.TrackInfo.Vs[0], `.mp3`)
} else {
strFileName = ztool.Str_FastConcat(infoFile.H, infoBody.TrackInfo.File.MediaMid, infoFile.E)
}
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
}
realQuality := strings.Split(infoData.Filename, `.`)[0][:4]
if qualityMapReverse[realQuality] != quality && /*infoBody.TrackInfo.Pay.PayPlay == 0*/ !tryLink {
msg = `实际音质不匹配`
return
}
ourl = ztool.Str_FastConcat(`https://ws.stream.qqmusic.qq.com/`, infoData.Purl)
return
}

View File

@ -0,0 +1,65 @@
package tx
import (
"bytes"
"lx-source/src/sources"
"net/http"
"github.com/ZxwyWebSite/ztool"
)
var (
fileInfo = map[string]struct {
E string
H string
}{
sources.Q_128k: {
E: `.mp3`,
H: `M500`,
},
sources.Q_320k: {
E: `.mp3`,
H: `M800`,
},
sources.Q_flac: {
E: `.flac`,
H: `F000`,
},
sources.Q_fl24: {
E: `.flac`,
H: `RS01`,
},
`dolby`: {
E: `.flac`,
H: `Q000`,
},
`master`: {
E: `.flac`,
H: `AI00`,
},
}
qualityMapReverse = map[string]string{
`M500`: sources.Q_128k,
`M800`: sources.Q_320k,
`F000`: sources.Q_flac,
`RS01`: sources.Q_fl24,
`Q000`: `dolby`,
`AI00`: `master`,
}
)
func signRequest(data []byte, out any) error {
s := sign(data)
return ztool.Net_Request(http.MethodPost,
ztool.Str_FastConcat(`https://u.y.qq.com/cgi-bin/musics.fcg?format=json&sign=`, s),
bytes.NewReader(data),
[]ztool.Net_ReqHandlerFunc{
ztool.Net_ReqAddHeaders(map[string]string{
`Referer`: `https://y.qq.com/`,
}),
},
[]ztool.Net_ResHandlerFunc{
ztool.Net_ResToStruct(out),
},
)
}

View File

@ -0,0 +1,12 @@
package utils
// func SizeFormat(size int) string {
// if size < 1024 {
// return ztool.Str_FastConcat(strconv.Itoa(size), `B`)
// }
// size64 := float64(size)
// if size64 < math.Pow(size64, 2) {
// }
// return ``
// }

View File

@ -5,8 +5,21 @@ import (
)
// var Loger = env.Loger.NewGroup(`Sources`) // JieXiApis
const (
Err_Verify = `Verify Failed`
// 通用音质
Q_128k = `128k`
Q_320k = `320k`
Q_flac = `flac`
Q_fl24 = `flac24bit`
// 通用平台
S_wy = `wy` // 小芸
S_mg = `mg` // 小蜜
S_kw = `kw` // 小蜗
S_kg = `kg` // 小枸
S_tx = `tx` // 小秋
S_lx = `lx` // 小洛 (预留)
)
// 源查询接口

View File

@ -1,5 +1,11 @@
## Lx-Source/更新日志
#### 2023-12-31 v1.0.2-b0.8.2 (dev)
- 注本次还是累积更新不单独发布Release
+ 从Python版移植部分代码
+ (兼容tx源一分钟试听链接获取)
+ 优化速率限制相关逻辑
#### 2023-12-30 v1.0.2-b0.8.1 (dev)
+ 注本次累积更新不单独发布Release
+ 实现单ip速率限制配置方法