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 (
|
const (
|
||||||
Version = `1.0.2-b11`
|
Version = `1.0.2-b12`
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -70,8 +70,8 @@ type (
|
|||||||
} // `comment:""`
|
} // `comment:""`
|
||||||
Conf_Custom struct {
|
Conf_Custom struct {
|
||||||
// wy (暂未实现)
|
// wy (暂未实现)
|
||||||
Wy_Enable bool `comment:"是否开启小芸源"`
|
Wy_Enable bool `comment:"是否开启小芸源"`
|
||||||
// Wy_Cookie string `comment:"账号cookie数据"`
|
Wy_Cookie string `comment:"账号cookie数据"`
|
||||||
|
|
||||||
// mg (暂未实现)
|
// mg (暂未实现)
|
||||||
// Mg_Enable bool `comment:"是否开启小蜜源"`
|
// Mg_Enable bool `comment:"是否开启小蜜源"`
|
||||||
@ -80,9 +80,9 @@ type (
|
|||||||
Kw_Enable bool `comment:"是否开启小蜗源"`
|
Kw_Enable bool `comment:"是否开启小蜗源"`
|
||||||
Kw_Mode string `comment:"接口模式 0: bdapi(需验证), 1: kwdes"`
|
Kw_Mode string `comment:"接口模式 0: bdapi(需验证), 1: kwdes"`
|
||||||
// kw bdapi
|
// kw bdapi
|
||||||
Kw_Bd_Uid string `comment:"user.uid"`
|
Kw_Bd_Uid string `comment:"field user.uid"`
|
||||||
Kw_Bd_Token string `comment:"user.token"`
|
Kw_Bd_Token string `comment:"field user.token"`
|
||||||
Kw_Bd_DevId string `comment:"user.device_id"`
|
Kw_Bd_DevId string `comment:"field user.device_id"`
|
||||||
// kw kwdes
|
// kw kwdes
|
||||||
Kw_Des_Type string `comment:"返回格式 0: text, 1: json"`
|
Kw_Des_Type string `comment:"返回格式 0: text, 1: json"`
|
||||||
Kw_Des_Header string `comment:"请求头 User-Agent"`
|
Kw_Des_Header string `comment:"请求头 User-Agent"`
|
||||||
|
@ -31,6 +31,8 @@ func loadQMap() [][]string {
|
|||||||
// 0.wy
|
// 0.wy
|
||||||
if env.Config.Custom.Wy_Enable {
|
if env.Config.Custom.Wy_Enable {
|
||||||
m[0] = defQuality
|
m[0] = defQuality
|
||||||
|
} else {
|
||||||
|
m[0] = tstQuality
|
||||||
}
|
}
|
||||||
// 1.mg
|
// 1.mg
|
||||||
m[1] = defQuality
|
m[1] = defQuality
|
||||||
|
@ -4,9 +4,9 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"lx-source/src/env"
|
"lx-source/src/env"
|
||||||
"lx-source/src/sources"
|
"lx-source/src/sources"
|
||||||
|
"lx-source/src/sources/custom/utils"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/ZxwyWebSite/ztool"
|
"github.com/ZxwyWebSite/ztool"
|
||||||
@ -94,7 +94,7 @@ func bdapi(songMid, quality string) (ourl, msg string) {
|
|||||||
msg = ztool.Str_FastConcat(`failed: `, resp.Msg)
|
msg = ztool.Str_FastConcat(`failed: `, resp.Msg)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ourl = strings.Split(resp.Data.URL, `?`)[0]
|
ourl = utils.DelQuery(resp.Data.URL) //strings.Split(resp.Data.URL, `?`)[0]
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -136,11 +136,11 @@ func kwdes(songMid, quality string) (ourl, msg string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
realQuality := strconv.Itoa(resp.Data.Bitrate)
|
realQuality := strconv.Itoa(resp.Data.Bitrate)
|
||||||
if qualityMapReverse[realQuality] != quality {
|
if realQuality != infoFile.H[:len(infoFile.H)-1] {
|
||||||
msg = sources.E_QNotMatch
|
msg = sources.E_QNotMatch
|
||||||
return
|
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
|
return
|
||||||
}
|
}
|
||||||
ztool.Net_Request(http.MethodGet, target_url, nil,
|
ztool.Net_Request(http.MethodGet, target_url, nil,
|
||||||
@ -161,12 +161,12 @@ func kwdes(songMid, quality string) (ourl, msg string) {
|
|||||||
}
|
}
|
||||||
infoData := mkMap(data)
|
infoData := mkMap(data)
|
||||||
loger.Debug(`infoData: %+v`, infoData)
|
loger.Debug(`infoData: %+v`, infoData)
|
||||||
realQuality := qualityMapReverse[infoData[`bitrate`]]
|
realQuality := infoData[`bitrate`]
|
||||||
if realQuality != quality {
|
if realQuality != infoFile.H[:len(infoFile.H)-1] {
|
||||||
msg = sources.E_QNotMatch
|
msg = sources.E_QNotMatch
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ourl = infoData[`url`][:strings.Index(infoData[`url`], `?`)]
|
ourl = utils.DelQuery(infoData[`url`]) //infoData[`url`][:strings.Index(infoData[`url`], `?`)]
|
||||||
return
|
return
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -29,12 +29,13 @@ var (
|
|||||||
H: `4000k`,
|
H: `4000k`,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
qualityMapReverse = map[string]string{
|
// 注:这个还是有规律的,加上或去掉k即可直接比较
|
||||||
`128`: sources.Q_128k,
|
// qualityMapReverse = map[string]string{
|
||||||
`320`: sources.Q_320k,
|
// `128`: sources.Q_128k,
|
||||||
`2000`: sources.Q_flac,
|
// `320`: sources.Q_320k,
|
||||||
`4000`: sources.Q_fl24,
|
// `2000`: sources.Q_flac,
|
||||||
}
|
// `4000`: sources.Q_fl24,
|
||||||
|
// }
|
||||||
desheader = map[string]string{
|
desheader = map[string]string{
|
||||||
// `User-Agent`: `okhttp/3.10.0`,
|
// `User-Agent`: `okhttp/3.10.0`,
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,12 @@
|
|||||||
package tx
|
package tx
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/md5"
|
|
||||||
"encoding/hex"
|
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/ZxwyWebSite/ztool"
|
"github.com/ZxwyWebSite/ztool"
|
||||||
"github.com/ZxwyWebSite/ztool/x/bytesconv"
|
"github.com/ZxwyWebSite/ztool/x/bytesconv"
|
||||||
|
"github.com/ZxwyWebSite/ztool/zcypt"
|
||||||
)
|
)
|
||||||
|
|
||||||
func v(b string) string {
|
func v(b string) string {
|
||||||
@ -106,14 +105,14 @@ func t(b string) (res []int) {
|
|||||||
return //res
|
return //res
|
||||||
}
|
}
|
||||||
|
|
||||||
func createMD5(s []byte) string {
|
// func createMD5(s []byte) string {
|
||||||
hash := md5.New()
|
// hash := md5.New()
|
||||||
hash.Write(s)
|
// hash.Write(s)
|
||||||
return hex.EncodeToString(hash.Sum(nil))
|
// return hex.EncodeToString(hash.Sum(nil))
|
||||||
}
|
// }
|
||||||
|
|
||||||
func sign(params []byte) string {
|
func sign(params []byte) string {
|
||||||
md5Str := strings.ToUpper(createMD5(params))
|
md5Str := strings.ToUpper(zcypt.CreateMD5(params))
|
||||||
h := v(md5Str)
|
h := v(md5Str)
|
||||||
e := c(md5Str)
|
e := c(md5Str)
|
||||||
ls := t(md5Str)
|
ls := t(md5Str)
|
@ -3,7 +3,6 @@ package tx
|
|||||||
import (
|
import (
|
||||||
"lx-source/src/env"
|
"lx-source/src/env"
|
||||||
"lx-source/src/sources"
|
"lx-source/src/sources"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/ZxwyWebSite/ztool"
|
"github.com/ZxwyWebSite/ztool"
|
||||||
"github.com/ZxwyWebSite/ztool/x/bytesconv"
|
"github.com/ZxwyWebSite/ztool/x/bytesconv"
|
||||||
@ -133,9 +132,13 @@ func Url(songMid, quality string) (ourl, msg string) {
|
|||||||
msg = sources.E_NoLink //`无法获取音乐链接`
|
msg = sources.E_NoLink //`无法获取音乐链接`
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
realQuality := strings.Split(infoData.Filename, `.`)[0][:4]
|
realQuality := ztool.Str_Before(infoData.Filename, `.`)[:4] //strings.Split(infoData.Filename, `.`)[0][:4]
|
||||||
if qualityMapReverse[realQuality] != quality && /*infoBody.TrackInfo.Pay.PayPlay == 0*/ !tryLink {
|
// if qualityMapReverse[realQuality] != quality && /*infoBody.TrackInfo.Pay.PayPlay == 0*/ !tryLink {
|
||||||
msg = sources.E_QNotMatch //`实际音质不匹配`
|
// msg = sources.E_QNotMatch //`实际音质不匹配`
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
if realQuality != infoFile.H && !tryLink {
|
||||||
|
msg = sources.E_QNotMatch
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ourl = ztool.Str_FastConcat(`https://ws.stream.qqmusic.qq.com/`, infoData.Purl)
|
ourl = ztool.Str_FastConcat(`https://ws.stream.qqmusic.qq.com/`, infoData.Purl)
|
||||||
|
@ -10,8 +10,8 @@ import (
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
fileInfo = map[string]struct {
|
fileInfo = map[string]struct {
|
||||||
E string
|
E string // 扩展名
|
||||||
H string
|
H string // 专用音质
|
||||||
}{
|
}{
|
||||||
sources.Q_128k: {
|
sources.Q_128k: {
|
||||||
E: `.mp3`,
|
E: `.mp3`,
|
||||||
@ -38,14 +38,14 @@ var (
|
|||||||
H: `AI00`,
|
H: `AI00`,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
qualityMapReverse = map[string]string{
|
// qualityMapReverse = map[string]string{
|
||||||
`M500`: sources.Q_128k,
|
// `M500`: sources.Q_128k,
|
||||||
`M800`: sources.Q_320k,
|
// `M800`: sources.Q_320k,
|
||||||
`F000`: sources.Q_flac,
|
// `F000`: sources.Q_flac,
|
||||||
`RS01`: sources.Q_fl24,
|
// `RS01`: sources.Q_fl24,
|
||||||
`Q000`: `dolby`,
|
// `Q000`: `dolby`,
|
||||||
`AI00`: `master`,
|
// `AI00`: `master`,
|
||||||
}
|
// }
|
||||||
header = map[string]string{
|
header = map[string]string{
|
||||||
`Referer`: `https://y.qq.com/`,
|
`Referer`: `https://y.qq.com/`,
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
package utils
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/ZxwyWebSite/ztool"
|
||||||
|
)
|
||||||
|
|
||||||
// func SizeFormat(size int) string {
|
// func SizeFormat(size int) string {
|
||||||
// if size < 1024 {
|
// if size < 1024 {
|
||||||
// return ztool.Str_FastConcat(strconv.Itoa(size), `B`)
|
// return ztool.Str_FastConcat(strconv.Itoa(size), `B`)
|
||||||
@ -10,3 +14,8 @@ package utils
|
|||||||
// }
|
// }
|
||||||
// return ``
|
// 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/更新日志
|
## 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)
|
#### \# 2024-01-15 v1.0.2-b11 (beta)
|
||||||
<!-- + 支持ForceFallback(忽略音质限制,获取试听音频) -->
|
<!-- + 支持ForceFallback(忽略音质限制,获取试听音频) -->
|
||||||
+ 测试版MusicId验证
|
+ 测试版MusicId验证
|
||||||
|
Loading…
x
Reference in New Issue
Block a user