mirror of
https://github.com/ZxwyWebSite/lx-source.git
synced 2025-05-23 21:37:42 +08:00
2024-01-18 v1.0.2-b12-d1
This commit is contained in:
parent
ad64fa1492
commit
6db6e9eb65
12
src/env/env.go
vendored
12
src/env/env.go
vendored
@ -12,7 +12,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
Version = `1.0.2-b11`
|
||||
Version = `1.0.2-b12`
|
||||
)
|
||||
|
||||
var (
|
||||
@ -70,8 +70,8 @@ type (
|
||||
} // `comment:""`
|
||||
Conf_Custom struct {
|
||||
// wy (暂未实现)
|
||||
Wy_Enable bool `comment:"是否开启小芸源"`
|
||||
// Wy_Cookie string `comment:"账号cookie数据"`
|
||||
Wy_Enable bool `comment:"是否开启小芸源"`
|
||||
Wy_Cookie string `comment:"账号cookie数据"`
|
||||
|
||||
// mg (暂未实现)
|
||||
// Mg_Enable bool `comment:"是否开启小蜜源"`
|
||||
@ -80,9 +80,9 @@ type (
|
||||
Kw_Enable bool `comment:"是否开启小蜗源"`
|
||||
Kw_Mode string `comment:"接口模式 0: bdapi(需验证), 1: kwdes"`
|
||||
// kw bdapi
|
||||
Kw_Bd_Uid string `comment:"user.uid"`
|
||||
Kw_Bd_Token string `comment:"user.token"`
|
||||
Kw_Bd_DevId string `comment:"user.device_id"`
|
||||
Kw_Bd_Uid string `comment:"field user.uid"`
|
||||
Kw_Bd_Token string `comment:"field user.token"`
|
||||
Kw_Bd_DevId string `comment:"field user.device_id"`
|
||||
// kw kwdes
|
||||
Kw_Des_Type string `comment:"返回格式 0: text, 1: json"`
|
||||
Kw_Des_Header string `comment:"请求头 User-Agent"`
|
||||
|
@ -31,6 +31,8 @@ func loadQMap() [][]string {
|
||||
// 0.wy
|
||||
if env.Config.Custom.Wy_Enable {
|
||||
m[0] = defQuality
|
||||
} else {
|
||||
m[0] = tstQuality
|
||||
}
|
||||
// 1.mg
|
||||
m[1] = defQuality
|
||||
|
@ -4,9 +4,9 @@ import (
|
||||
"io"
|
||||
"lx-source/src/env"
|
||||
"lx-source/src/sources"
|
||||
"lx-source/src/sources/custom/utils"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/ZxwyWebSite/ztool"
|
||||
@ -94,7 +94,7 @@ func bdapi(songMid, quality string) (ourl, msg string) {
|
||||
msg = ztool.Str_FastConcat(`failed: `, resp.Msg)
|
||||
return
|
||||
}
|
||||
ourl = strings.Split(resp.Data.URL, `?`)[0]
|
||||
ourl = utils.DelQuery(resp.Data.URL) //strings.Split(resp.Data.URL, `?`)[0]
|
||||
return
|
||||
}
|
||||
|
||||
@ -136,11 +136,11 @@ func kwdes(songMid, quality string) (ourl, msg string) {
|
||||
return
|
||||
}
|
||||
realQuality := strconv.Itoa(resp.Data.Bitrate)
|
||||
if qualityMapReverse[realQuality] != quality {
|
||||
if realQuality != infoFile.H[:len(infoFile.H)-1] {
|
||||
msg = sources.E_QNotMatch
|
||||
return
|
||||
}
|
||||
ourl = resp.Data.URL[:strings.Index(resp.Data.URL, `?`)]
|
||||
ourl = utils.DelQuery(resp.Data.URL) //resp.Data.URL[:strings.Index(resp.Data.URL, `?`)]
|
||||
return
|
||||
}
|
||||
ztool.Net_Request(http.MethodGet, target_url, nil,
|
||||
@ -161,12 +161,12 @@ func kwdes(songMid, quality string) (ourl, msg string) {
|
||||
}
|
||||
infoData := mkMap(data)
|
||||
loger.Debug(`infoData: %+v`, infoData)
|
||||
realQuality := qualityMapReverse[infoData[`bitrate`]]
|
||||
if realQuality != quality {
|
||||
realQuality := infoData[`bitrate`]
|
||||
if realQuality != infoFile.H[:len(infoFile.H)-1] {
|
||||
msg = sources.E_QNotMatch
|
||||
return
|
||||
}
|
||||
ourl = infoData[`url`][:strings.Index(infoData[`url`], `?`)]
|
||||
ourl = utils.DelQuery(infoData[`url`]) //infoData[`url`][:strings.Index(infoData[`url`], `?`)]
|
||||
return
|
||||
},
|
||||
},
|
||||
|
@ -29,12 +29,13 @@ var (
|
||||
H: `4000k`,
|
||||
},
|
||||
}
|
||||
qualityMapReverse = map[string]string{
|
||||
`128`: sources.Q_128k,
|
||||
`320`: sources.Q_320k,
|
||||
`2000`: sources.Q_flac,
|
||||
`4000`: sources.Q_fl24,
|
||||
}
|
||||
// 注:这个还是有规律的,加上或去掉k即可直接比较
|
||||
// qualityMapReverse = map[string]string{
|
||||
// `128`: sources.Q_128k,
|
||||
// `320`: sources.Q_320k,
|
||||
// `2000`: sources.Q_flac,
|
||||
// `4000`: sources.Q_fl24,
|
||||
// }
|
||||
desheader = map[string]string{
|
||||
// `User-Agent`: `okhttp/3.10.0`,
|
||||
}
|
||||
|
@ -1,13 +1,12 @@
|
||||
package tx
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/ZxwyWebSite/ztool"
|
||||
"github.com/ZxwyWebSite/ztool/x/bytesconv"
|
||||
"github.com/ZxwyWebSite/ztool/zcypt"
|
||||
)
|
||||
|
||||
func v(b string) string {
|
||||
@ -106,14 +105,14 @@ func t(b string) (res []int) {
|
||||
return //res
|
||||
}
|
||||
|
||||
func createMD5(s []byte) string {
|
||||
hash := md5.New()
|
||||
hash.Write(s)
|
||||
return hex.EncodeToString(hash.Sum(nil))
|
||||
}
|
||||
// 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))
|
||||
md5Str := strings.ToUpper(zcypt.CreateMD5(params))
|
||||
h := v(md5Str)
|
||||
e := c(md5Str)
|
||||
ls := t(md5Str)
|
@ -3,7 +3,6 @@ package tx
|
||||
import (
|
||||
"lx-source/src/env"
|
||||
"lx-source/src/sources"
|
||||
"strings"
|
||||
|
||||
"github.com/ZxwyWebSite/ztool"
|
||||
"github.com/ZxwyWebSite/ztool/x/bytesconv"
|
||||
@ -133,9 +132,13 @@ func Url(songMid, quality string) (ourl, msg string) {
|
||||
msg = sources.E_NoLink //`无法获取音乐链接`
|
||||
return
|
||||
}
|
||||
realQuality := strings.Split(infoData.Filename, `.`)[0][:4]
|
||||
if qualityMapReverse[realQuality] != quality && /*infoBody.TrackInfo.Pay.PayPlay == 0*/ !tryLink {
|
||||
msg = sources.E_QNotMatch //`实际音质不匹配`
|
||||
realQuality := ztool.Str_Before(infoData.Filename, `.`)[:4] //strings.Split(infoData.Filename, `.`)[0][:4]
|
||||
// if qualityMapReverse[realQuality] != quality && /*infoBody.TrackInfo.Pay.PayPlay == 0*/ !tryLink {
|
||||
// msg = sources.E_QNotMatch //`实际音质不匹配`
|
||||
// return
|
||||
// }
|
||||
if realQuality != infoFile.H && !tryLink {
|
||||
msg = sources.E_QNotMatch
|
||||
return
|
||||
}
|
||||
ourl = ztool.Str_FastConcat(`https://ws.stream.qqmusic.qq.com/`, infoData.Purl)
|
||||
|
@ -10,8 +10,8 @@ import (
|
||||
|
||||
var (
|
||||
fileInfo = map[string]struct {
|
||||
E string
|
||||
H string
|
||||
E string // 扩展名
|
||||
H string // 专用音质
|
||||
}{
|
||||
sources.Q_128k: {
|
||||
E: `.mp3`,
|
||||
@ -38,14 +38,14 @@ var (
|
||||
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`,
|
||||
}
|
||||
// qualityMapReverse = map[string]string{
|
||||
// `M500`: sources.Q_128k,
|
||||
// `M800`: sources.Q_320k,
|
||||
// `F000`: sources.Q_flac,
|
||||
// `RS01`: sources.Q_fl24,
|
||||
// `Q000`: `dolby`,
|
||||
// `AI00`: `master`,
|
||||
// }
|
||||
header = map[string]string{
|
||||
`Referer`: `https://y.qq.com/`,
|
||||
}
|
||||
|
@ -1,5 +1,9 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"github.com/ZxwyWebSite/ztool"
|
||||
)
|
||||
|
||||
// func SizeFormat(size int) string {
|
||||
// if size < 1024 {
|
||||
// return ztool.Str_FastConcat(strconv.Itoa(size), `B`)
|
||||
@ -10,3 +14,8 @@ package utils
|
||||
// }
|
||||
// return ``
|
||||
// }
|
||||
|
||||
// 删除?号后尾随内容
|
||||
func DelQuery(str string) string {
|
||||
return ztool.Str_Before(str, `?`)
|
||||
}
|
||||
|
98
src/sources/custom/wy/encrypt.go
Normal file
98
src/sources/custom/wy/encrypt.go
Normal file
@ -0,0 +1,98 @@
|
||||
package wy
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"encoding/base64"
|
||||
|
||||
"github.com/ZxwyWebSite/ztool"
|
||||
"github.com/ZxwyWebSite/ztool/x/bytesconv"
|
||||
"github.com/ZxwyWebSite/ztool/x/json"
|
||||
"github.com/ZxwyWebSite/ztool/zcypt"
|
||||
)
|
||||
|
||||
var (
|
||||
// __all__ = []string{`weEncrypt`, `linuxEncrypt`, `eEncrypt`}
|
||||
// MODULUS = ztool.Str_FastConcat(
|
||||
// `00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7`,
|
||||
// `b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280`,
|
||||
// `104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932`,
|
||||
// `575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b`,
|
||||
// `3ece0462db0a22b8e7`,
|
||||
// )
|
||||
// PUBKEY = `010001`
|
||||
// NONCE = bytesconv.StringToBytes(`0CoJUm6Qyw8W8jud`)
|
||||
// LINUXKEY = bytesconv.StringToBytes(`rFgB&h#%2?^eDg:Q`)
|
||||
eapiKey = bytesconv.StringToBytes(`e82ckenh8dichen8`)
|
||||
ivKey = bytesconv.StringToBytes(`0102030405060708`)
|
||||
)
|
||||
|
||||
func eapiEncrypt(url, text string) map[string][]string {
|
||||
digest := zcypt.CreateMD5(bytesconv.StringToBytes(ztool.Str_FastConcat(
|
||||
`nobody`, url, `use`, text, `md5forencrypt`,
|
||||
)))
|
||||
data := ztool.Str_FastConcat(
|
||||
url, `-36cd479b6b5-`, text, `-36cd479b6b5-`, digest,
|
||||
)
|
||||
// 注:JSON编码时会自动将[]byte转为string,这里省去一步转换
|
||||
return map[string][]string{
|
||||
`params`: {bytesconv.BytesToString(aesEncrypt(bytesconv.StringToBytes(data), eapiKey, false))},
|
||||
}
|
||||
}
|
||||
|
||||
// crypto.js
|
||||
|
||||
func aesEncrypt(text, key []byte, iv bool) []byte {
|
||||
pad := 16 - len(text)%16
|
||||
text = append(text, bytes.Repeat([]byte{byte(pad)}, pad)...)
|
||||
block, _ := aes.NewCipher(key)
|
||||
// if err != nil {
|
||||
// panic(err)
|
||||
// }
|
||||
var encryptor cipher.BlockMode
|
||||
if iv {
|
||||
encryptor = cipher.NewCBCEncrypter(block, ivKey)
|
||||
} else {
|
||||
encryptor = zcypt.NewECBEncrypter(block)
|
||||
}
|
||||
ciphertext := make([]byte, len(text))
|
||||
encryptor.CryptBlocks(ciphertext, text)
|
||||
if iv {
|
||||
return zcypt.Base64Encode(base64.StdEncoding, ciphertext)
|
||||
}
|
||||
return bytes.ToUpper(zcypt.HexEncode(ciphertext))
|
||||
}
|
||||
|
||||
func eapi(url string, object map[string]any) map[string][]string {
|
||||
text, err := json.Marshal(object)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
message := ztool.Str_FastConcat(
|
||||
`nobody`, url, `use`, bytesconv.BytesToString(text), `md5forencrypt`,
|
||||
)
|
||||
digest := zcypt.CreateMD5(bytesconv.StringToBytes(message))
|
||||
data := bytes.Join(
|
||||
[][]byte{
|
||||
bytesconv.StringToBytes(url),
|
||||
text,
|
||||
bytesconv.StringToBytes(digest),
|
||||
},
|
||||
[]byte{45, 51, 54, 99, 100, 52, 55, 57, 98, 54, 98, 53, 45},
|
||||
)
|
||||
return map[string][]string{
|
||||
`params`: {bytesconv.BytesToString(aesEncrypt(data, eapiKey, false))},
|
||||
}
|
||||
}
|
||||
|
||||
func decrypt(data []byte) (out []byte) {
|
||||
dec, err := zcypt.HexDecode(data)
|
||||
if err == nil {
|
||||
out, err = zcypt.AesDecrypt(dec, eapiKey)
|
||||
}
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return out
|
||||
}
|
107
src/sources/custom/wy/player.go
Normal file
107
src/sources/custom/wy/player.go
Normal file
@ -0,0 +1,107 @@
|
||||
package wy
|
||||
|
||||
import (
|
||||
"io"
|
||||
"lx-source/src/env"
|
||||
"lx-source/src/sources"
|
||||
"lx-source/src/sources/builtin"
|
||||
"lx-source/src/sources/custom/utils"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/ZxwyWebSite/ztool"
|
||||
"github.com/ZxwyWebSite/ztool/x/cookie"
|
||||
)
|
||||
|
||||
func Url(songMid, quality string) (ourl, msg string) {
|
||||
loger := env.Loger.NewGroup(`Wy`)
|
||||
rquality, ok := brMap[quality]
|
||||
if !ok {
|
||||
msg = sources.E_QNotSupport
|
||||
return
|
||||
}
|
||||
cookies := cookie.Parse(env.Config.Custom.Wy_Cookie)
|
||||
answer, err := SongUrl(song_url_query{
|
||||
Cookie: cookie.ToMap(cookies),
|
||||
Ids: songMid,
|
||||
Br: rquality,
|
||||
})
|
||||
var body builtin.WyApi_Song
|
||||
if err == nil {
|
||||
err = ztool.Val_MapToStruct(answer.Body, &body)
|
||||
}
|
||||
if err != nil {
|
||||
loger.Error(`SongUrl: %s`, err)
|
||||
msg = sources.ErrHttpReq
|
||||
return
|
||||
}
|
||||
loger.Debug(`Resp: %+v`, body)
|
||||
if len(body.Data) == 0 {
|
||||
msg = `No Data:无返回数据`
|
||||
return
|
||||
}
|
||||
data := body.Data[0]
|
||||
br := strconv.Itoa(data.Br) // 注:由于flac返回br值不固定,暂无法进行比较
|
||||
if br != rquality && !ztool.Chk_IsMatch(br, sources.Q_flac, sources.Q_fl24) {
|
||||
msg = sources.E_QNotMatch
|
||||
return
|
||||
}
|
||||
ourl = utils.DelQuery(data.URL)
|
||||
return
|
||||
}
|
||||
|
||||
func PyUrl(songMid, quality string) (ourl, msg string) {
|
||||
loger := env.Loger.NewGroup(`Wy`)
|
||||
rquality, ok := qualityMap[quality]
|
||||
if !ok {
|
||||
msg = sources.E_QNotSupport
|
||||
return
|
||||
}
|
||||
path := `/api/song/enhance/player/url/v1`
|
||||
requestUrl := `https://interface.music.163.com/eapi/song/enhance/player/url/v1`
|
||||
var body builtin.WyApi_Song
|
||||
text := ztool.Str_FastConcat(
|
||||
`{"encodeType":"flac","ids":["`, songMid, `"],"level":"`, rquality, `"}`,
|
||||
)
|
||||
var form url.Values = eapiEncrypt(path, text)
|
||||
// form, err := json.Marshal(eapiEncrypt(path, text))
|
||||
// if err == nil {
|
||||
err := ztool.Net_Request(
|
||||
http.MethodPost, requestUrl,
|
||||
strings.NewReader(form.Encode()), //bytes.NewReader(form),
|
||||
[]ztool.Net_ReqHandlerFunc{ztool.Net_ReqAddHeader(map[string]string{
|
||||
`Cookie`: env.Config.Custom.Wy_Cookie,
|
||||
})},
|
||||
[]ztool.Net_ResHandlerFunc{
|
||||
func(res *http.Response) error {
|
||||
body, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
loger.Info(`%s`, body)
|
||||
return ztool.Err_EsContinue
|
||||
},
|
||||
ztool.Net_ResToStruct(&body),
|
||||
},
|
||||
)
|
||||
// }
|
||||
if err != nil {
|
||||
loger.Error(`Request: %s`, err)
|
||||
msg = sources.ErrHttpReq
|
||||
return
|
||||
}
|
||||
loger.Debug(`Resp: %+v`, body)
|
||||
if len(body.Data) == 0 {
|
||||
msg = `No Data:无返回数据`
|
||||
return
|
||||
}
|
||||
data := body.Data[0]
|
||||
if data.Level != rquality {
|
||||
msg = sources.E_QNotMatch
|
||||
return
|
||||
}
|
||||
ourl = utils.DelQuery(data.URL)
|
||||
return
|
||||
}
|
42
src/sources/custom/wy/song_url.go
Normal file
42
src/sources/custom/wy/song_url.go
Normal file
@ -0,0 +1,42 @@
|
||||
package wy
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/ZxwyWebSite/ztool"
|
||||
)
|
||||
|
||||
type song_url_query struct {
|
||||
Cookie map[string]string
|
||||
Ids string
|
||||
Br string
|
||||
RealIP string
|
||||
}
|
||||
|
||||
func SongUrl(query song_url_query) (*reqAnswer, error) {
|
||||
query.Cookie[`os`] = `pc`
|
||||
if query.Br == `` {
|
||||
query.Br = `999000`
|
||||
}
|
||||
ids := strings.Split(query.Ids, `,`)
|
||||
// idj, err := json.Marshal(ids)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
data := map[string]any{
|
||||
`ids`: ztool.Str_FastConcat(`["`, strings.Join(ids, `","`), `"]`), //bytesconv.BytesToString(idj), //`["1998644237"]`,
|
||||
`br`: query.Br, //ztool.Str_Select(query.Br, `999000`),
|
||||
}
|
||||
return createRequest(
|
||||
http.MethodPost,
|
||||
`https://interface3.music.163.com/eapi/song/enhance/player/url`,
|
||||
data,
|
||||
reqOptions{
|
||||
Crypto: `eapi`,
|
||||
Cookie: query.Cookie,
|
||||
RealIP: query.RealIP,
|
||||
Url: `/api/song/enhance/player/url`,
|
||||
},
|
||||
)
|
||||
}
|
31
src/sources/custom/wy/types.go
Normal file
31
src/sources/custom/wy/types.go
Normal file
@ -0,0 +1,31 @@
|
||||
package wy
|
||||
|
||||
import "lx-source/src/sources"
|
||||
|
||||
var (
|
||||
brMap = map[string]string{
|
||||
sources.Q_128k: `128000`,
|
||||
sources.Q_320k: `320000`,
|
||||
sources.Q_flac: `1000000`, //`743625`,`915752`
|
||||
sources.Q_fl24: `2000000`, //`1453955`,`1683323`
|
||||
}
|
||||
qualityMap = map[string]string{
|
||||
sources.Q_128k: `standard`,
|
||||
sources.Q_320k: `exhigh`,
|
||||
sources.Q_flac: `lossless`,
|
||||
sources.Q_fl24: `hires`,
|
||||
`dolby`: `jyeffect`,
|
||||
`sky`: `jysky`,
|
||||
`master`: `jymaster`,
|
||||
}
|
||||
// 优化:返回音质与查询音质相同,完全可以直接比较,不用多一步Reverse
|
||||
// qualityMapReverse = map[string]string{
|
||||
// `standard`: sources.Q_128k,
|
||||
// `exhigh`: sources.Q_320k,
|
||||
// `lossless`: sources.Q_flac,
|
||||
// `hires`: sources.Q_fl24,
|
||||
// `jyeffect`: `dolby`,
|
||||
// `jysky`: `sky`,
|
||||
// `jymaster`: `master`,
|
||||
// }
|
||||
)
|
223
src/sources/custom/wy/utils.go
Normal file
223
src/sources/custom/wy/utils.go
Normal file
@ -0,0 +1,223 @@
|
||||
package wy
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
stdurl "net/url"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/ZxwyWebSite/ztool"
|
||||
"github.com/ZxwyWebSite/ztool/x/json"
|
||||
"github.com/ZxwyWebSite/ztool/zcypt"
|
||||
)
|
||||
|
||||
// request.js
|
||||
|
||||
const anonymous_token = `1f5fa7b6a6a9f81a11886e5186fde7fb98e25cf0036d7afd055b980b2261f5464b7f5273fc3921d1262bfec66a19a30c41d8da00c3685f5ace96f0d5a48b6db334d974731083682e3324751bcc9aaf44c3061cd1`
|
||||
|
||||
var wapiReg = regexp.MustCompile(`\w*api`)
|
||||
|
||||
var userAgentMap = map[string]string{
|
||||
`mobile`: `Mozilla/5.0 (iPhone; CPU iPhone OS 17_2_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.2 Mobile/15E148 Safari/604.1`,
|
||||
`pc`: `Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0`,
|
||||
}
|
||||
|
||||
type (
|
||||
// reqCookie struct{}
|
||||
reqOptions struct {
|
||||
UA string
|
||||
Headers map[string]string
|
||||
RealIP string
|
||||
IP string
|
||||
Cookie interface{}
|
||||
Crypto string
|
||||
Url string
|
||||
}
|
||||
reqAnswer struct {
|
||||
Status int
|
||||
Body map[string]any
|
||||
Cookie []string
|
||||
}
|
||||
)
|
||||
|
||||
func createRequest(method, url string, data map[string]any, options reqOptions) (*reqAnswer, error) {
|
||||
if options.Headers == nil {
|
||||
options.Headers = make(map[string]string)
|
||||
}
|
||||
options.Headers[`User-Agent`] = userAgentMap[options.UA]
|
||||
if method == http.MethodPost {
|
||||
options.Headers[`Content-Type`] = `application/x-www-form-urlencoded`
|
||||
}
|
||||
if strings.Contains(url, `music.163.com`) {
|
||||
options.Headers[`Referer`] = `https://music.163.com`
|
||||
}
|
||||
ip := ztool.Str_Select(options.RealIP, options.IP, ``)
|
||||
if ip != `` {
|
||||
options.Headers[`X-Real-IP`] = ip
|
||||
options.Headers[`X-Forwarded-For`] = ip
|
||||
}
|
||||
if obj, ok := options.Cookie.(map[string]string); ok {
|
||||
obj[`__remember_me`] = `true`
|
||||
obj[`_ntes_nuid`] = zcypt.HexToString(zcypt.RandomBytes(16))
|
||||
if !strings.Contains(url, `login`) {
|
||||
obj[`NMTID`] = zcypt.HexToString(zcypt.RandomBytes(16))
|
||||
}
|
||||
if _, ok := obj[`MUSIC_U`]; !ok {
|
||||
// 游客
|
||||
if _, ok := obj[`MUSIC_A`]; !ok {
|
||||
obj[`MUSIC_A`] = anonymous_token
|
||||
if obj[`os`] == `` {
|
||||
obj[`os`] = `ios`
|
||||
}
|
||||
if obj[`appver`] == `` {
|
||||
obj[`appver`] = `8.20.21`
|
||||
}
|
||||
}
|
||||
}
|
||||
keys, i := make([]string, len(obj)), 0
|
||||
for k, v := range obj {
|
||||
keys[i] = ztool.Str_FastConcat(
|
||||
stdurl.QueryEscape(k), `=`, stdurl.QueryEscape(v),
|
||||
)
|
||||
i++
|
||||
}
|
||||
options.Headers[`Cookie`] = strings.Join(keys, `; `)
|
||||
options.Cookie = obj
|
||||
} else if str, ok := options.Cookie.(string); ok && str != `` {
|
||||
options.Headers[`Cookie`] = str
|
||||
} else {
|
||||
options.Headers[`Cookie`] = `__remember_me=true; NMTID=xxx`
|
||||
}
|
||||
var form stdurl.Values
|
||||
switch options.Crypto {
|
||||
case `weapi`:
|
||||
panic(`not support`)
|
||||
// options.Headers[`User-Agent`] = `Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36 Edg/116.0.1938.69`
|
||||
// reg := regexp.MustCompile(`_csrf=([^(;|$)]+)`)
|
||||
// csrfToken := reg.FindStringSubmatch(options.Headers[`Cookie`])
|
||||
// if len(csrfToken) > 1 {
|
||||
// data[`csrf_token`] = csrfToken[1]
|
||||
// } else {
|
||||
// data[`csrf_token`] = ``
|
||||
// }
|
||||
// data = weapi(data)
|
||||
// url = wapiReg.ReplaceAllString(url, `weapi`)
|
||||
case `linuxapi`:
|
||||
panic(`not support`)
|
||||
// data = linuxapi(
|
||||
// method,
|
||||
// wapiReg.ReplaceAllString(url, `weapi`),
|
||||
// data,
|
||||
// )
|
||||
// options.Headers[`User-Agent`] = `Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36`
|
||||
// url = `https://music.163.com/api/linux/forward`
|
||||
case `eapi`:
|
||||
cookie, ok := options.Cookie.(map[string]string)
|
||||
if !ok {
|
||||
cookie = make(map[string]string)
|
||||
}
|
||||
// csrfToken := cookie[`__csrf`]
|
||||
now := time.Now()
|
||||
reqid := strconv.Itoa(rand.Intn(1000))
|
||||
header := map[string]string{
|
||||
`osver`: ztool.Str_Select(cookie[`osver`], `17,1,2`), //系统版本
|
||||
`deviceId`: cookie[`deviceId`], //zcypt.Base64ToString(base64.StdEncoding, bytesconv.StringToBytes(imei+"'\t02:00:00:00:00:00\t5106025eb79a5247\t70ffbaac7'"))
|
||||
`appver`: ztool.Str_Select(cookie[`appver`], `8.9.70`), // app版本
|
||||
`versioncode`: ztool.Str_Select(cookie[`versioncode`], `140`), //版本号
|
||||
`mobilename`: cookie[`mobilename`], //设备model
|
||||
`buildver`: ztool.Str_Select(cookie[`buildver`], strconv.FormatInt(now.Unix(), 10)),
|
||||
`resolution`: ztool.Str_Select(cookie[`resolution`], `1920x1080`), //设备分辨率
|
||||
`__csrf`: cookie[`__csrf`], //csrfToken,
|
||||
`os`: ztool.Str_Select(cookie[`os`], `ios`),
|
||||
`channel`: cookie[`channel`],
|
||||
`requestId`: ztool.Str_FastConcat(
|
||||
strconv.FormatInt(now.UnixMilli(), 10), `_`,
|
||||
strings.Repeat(`0`, 4-len(reqid)), reqid,
|
||||
),
|
||||
}
|
||||
if cookie[`MUSIC_U`] != `` {
|
||||
header[`MUSIC_U`] = cookie[`MUSIC_U`]
|
||||
}
|
||||
if cookie[`MUSIC_A`] != `` {
|
||||
header[`MUSIC_A`] = cookie[`MUSIC_A`]
|
||||
}
|
||||
keys, i := make([]string, len(header)), 0
|
||||
for k, v := range header {
|
||||
keys[i] = ztool.Str_FastConcat(
|
||||
stdurl.QueryEscape(k), `=`, stdurl.QueryEscape(v),
|
||||
)
|
||||
i++
|
||||
}
|
||||
options.Headers[`Cookie`] = strings.Join(keys, `; `)
|
||||
out, err := json.Marshal(header)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
data[`header`] = out //bytesconv.BytesToString(out)
|
||||
form = eapi(options.Url, data)
|
||||
url = wapiReg.ReplaceAllString(url, `eapi`)
|
||||
default:
|
||||
return nil, errors.New(`not support`)
|
||||
}
|
||||
// values := stdurl.Values{}
|
||||
// for k, v := range data {
|
||||
// values.Add(k, v)
|
||||
// }
|
||||
answer := reqAnswer{Status: 500, Body: map[string]any{} /*, Cookie: []string{}*/}
|
||||
err := ztool.Net_Request(method, url,
|
||||
strings.NewReader(form.Encode()),
|
||||
[]ztool.Net_ReqHandlerFunc{
|
||||
ztool.Net_ReqAddHeader(options.Headers),
|
||||
},
|
||||
[]ztool.Net_ResHandlerFunc{
|
||||
func(res *http.Response) error {
|
||||
body, err := io.ReadAll(res.Body)
|
||||
if err == nil {
|
||||
answer.Cookie = res.Header[`Set-Cookie`]
|
||||
reg := regexp.MustCompile(`\s*Domain=[^(;|$)]+;*`)
|
||||
for i, v := range answer.Cookie {
|
||||
answer.Cookie[i] = reg.ReplaceAllString(v, ``)
|
||||
}
|
||||
if options.Crypto == `eapi` && body[0] != '{' {
|
||||
err = json.Unmarshal(decrypt(body), &answer.Body)
|
||||
} else {
|
||||
err = json.Unmarshal(body, &answer.Body)
|
||||
}
|
||||
if err == nil {
|
||||
if code, ok := answer.Body[`code`].(string); ok {
|
||||
answer.Body[`code`], err = strconv.Atoi(code)
|
||||
} else {
|
||||
answer.Body[`code`] = res.StatusCode
|
||||
}
|
||||
if err == nil {
|
||||
if code, ok := answer.Body[`code`].(int); ok {
|
||||
if !ztool.Chk_IsMatchInt(code, 201, 302, 400, 502, 800, 801, 802, 803) {
|
||||
// 特殊状态码
|
||||
answer.Status = 200
|
||||
}
|
||||
}
|
||||
if answer.Status < 100 || answer.Status >= 600 {
|
||||
answer.Status = 400
|
||||
}
|
||||
if answer.Status != 200 {
|
||||
err = errors.New(strconv.Itoa(answer.Status))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
answer.Status = 502
|
||||
answer.Body = map[string]any{`code`: 502, `msg`: err}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
)
|
||||
return &answer, err
|
||||
}
|
@ -1,5 +1,11 @@
|
||||
## Lx-Source/更新日志
|
||||
|
||||
#### \# 2024-01-18 v1.0.2-b12-d1 (dev)
|
||||
+ 对部分功能实现方式进行优化,去除qualityMapReverse依赖
|
||||
+ 由于wy修改api验证方式,python版逻辑已不可用,现参考NeteaseCloudMusicApi项目进行修改
|
||||
+ 实现wy外链获取逻辑(暂未实装)
|
||||
<!-- + 添加wy电台(wd)支持 -->
|
||||
|
||||
#### \# 2024-01-15 v1.0.2-b11 (beta)
|
||||
<!-- + 支持ForceFallback(忽略音质限制,获取试听音频) -->
|
||||
+ 测试版MusicId验证
|
||||
|
Loading…
x
Reference in New Issue
Block a user