2024-01-18 v1.0.2-b12-d1

This commit is contained in:
ZxwyWebSite 2024-01-19 00:51:32 +08:00
parent ad64fa1492
commit 6db6e9eb65
14 changed files with 562 additions and 41 deletions

12
src/env/env.go vendored
View File

@ -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"`

View File

@ -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

View File

@ -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
},
},

View File

@ -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`,
}

View File

@ -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)

View File

@ -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)

View File

@ -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/`,
}

View File

@ -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, `?`)
}

View 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
}

View 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
}

View 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`,
},
)
}

View 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`,
// }
)

View 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
}

View File

@ -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验证