mirror of
https://github.com/ZxwyWebSite/lx-source.git
synced 2025-05-23 21:37:42 +08:00
2023-12-31 v1.0.2-b0.8.2
This commit is contained in:
parent
b26d69a162
commit
0b7e1a9425
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,7 +1,7 @@
|
||||
bin/
|
||||
# cache/
|
||||
data/
|
||||
outdated/
|
||||
.outdated/
|
||||
# conf.ini
|
||||
test.go
|
||||
test_test.go
|
||||
|
@ -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
14
src/env/env.go
vendored
@ -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:"音乐缓存设置"`
|
||||
}
|
||||
|
@ -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: `速率限制内部异常,请联系网站管理员`}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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() {
|
||||
|
126
src/sources/custom/tx/QMWSign.go
Normal file
126
src/sources/custom/tx/QMWSign.go
Normal 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
|
||||
}
|
25
src/sources/custom/tx/info.go
Normal file
25
src/sources/custom/tx/info.go
Normal 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
|
||||
// }
|
247
src/sources/custom/tx/musicinfo.go
Normal file
247
src/sources/custom/tx/musicinfo.go
Normal 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
|
||||
// }
|
137
src/sources/custom/tx/player.go
Normal file
137
src/sources/custom/tx/player.go
Normal 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
|
||||
}
|
65
src/sources/custom/tx/utils.go
Normal file
65
src/sources/custom/tx/utils.go
Normal 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),
|
||||
},
|
||||
)
|
||||
}
|
12
src/sources/custom/utils/utils.go
Normal file
12
src/sources/custom/utils/utils.go
Normal 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 ``
|
||||
// }
|
@ -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` // 小洛 (预留)
|
||||
)
|
||||
|
||||
// 源查询接口
|
||||
|
Loading…
x
Reference in New Issue
Block a user