2024-01-21 v1.0.2-b12-d2

This commit is contained in:
ZxwyWebSite 2024-01-21 23:23:47 +08:00
parent 6db6e9eb65
commit ad7614c564
11 changed files with 433 additions and 144 deletions

5
src/env/env.go vendored
View File

@ -69,9 +69,11 @@ type (
ForceFallback bool `comment:"忽略音质限制,强制获取试听音频"` ForceFallback bool `comment:"忽略音质限制,强制获取试听音频"`
} // `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数据"`
Wy_Refresh_Enable bool `comment:"是否启用刷新登录"`
Wy_Refresh_Interval int64 `comment:"下次刷新时间 (由程序维护)"`
// mg (暂未实现) // mg (暂未实现)
// Mg_Enable bool `comment:"是否开启小蜜源"` // Mg_Enable bool `comment:"是否开启小蜜源"`
@ -162,6 +164,7 @@ var (
}, },
Custom: Conf_Custom{ Custom: Conf_Custom{
Wy_Enable: true, Wy_Enable: true,
Wy_Refresh_Interval: 1633622400,
Kw_Enable: true, Kw_Enable: true,
Kw_Mode: `kwdes`, Kw_Mode: `kwdes`,

View File

@ -33,7 +33,7 @@ import (
func init() { func init() {
env.Inits.Add(func() { env.Inits.Add(func() {
if env.Config.Custom.Tx_Refresh_Enable { if env.Config.Custom.Tx_Refresh_Enable {
env.Tasker.Add(`refresh_login`, func(l *logs.Logger) error { env.Tasker.Add(`tx_refresh`, func(l *logs.Logger) error {
refresh(l) refresh(l)
return nil return nil
}, 86000, true) }, 86000, true)

View File

@ -4,7 +4,12 @@ import (
"bytes" "bytes"
"crypto/aes" "crypto/aes"
"crypto/cipher" "crypto/cipher"
"crypto/rsa"
"crypto/x509"
"encoding/base64" "encoding/base64"
"encoding/pem"
"math/rand"
_ "unsafe"
"github.com/ZxwyWebSite/ztool" "github.com/ZxwyWebSite/ztool"
"github.com/ZxwyWebSite/ztool/x/bytesconv" "github.com/ZxwyWebSite/ztool/x/bytesconv"
@ -13,33 +18,28 @@ import (
) )
var ( 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`) ivKey = bytesconv.StringToBytes(`0102030405060708`)
presetKey = bytesconv.StringToBytes(`0CoJUm6Qyw8W8jud`)
linuxapiKey = bytesconv.StringToBytes(`rFgB&h#%2?^eDg:Q`)
base62 = bytesconv.StringToBytes(`abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789`)
publicKey = bytesconv.StringToBytes(`-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDgtQn2JZ34ZC28NWYpAUd98iZ37BUrX/aKzmFbt7clFSs6sXqHauqKWqdtLkF2KexO40H1YTX8z2lSgBBOAxLsvaklV8k4cBFK9snQXE9/DDaFt6Rr7iVZMldczhC0JNgTz+SHXT6CBHuX3e9SdB1Ua44oncaTWz7OBGLbCiK45wIDAQAB
-----END PUBLIC KEY-----`)
eapiKey = bytesconv.StringToBytes(`e82ckenh8dichen8`)
) )
func eapiEncrypt(url, text string) map[string][]string { // func eapiEncrypt(url, text string) map[string][]string {
digest := zcypt.CreateMD5(bytesconv.StringToBytes(ztool.Str_FastConcat( // digest := zcypt.CreateMD5(bytesconv.StringToBytes(ztool.Str_FastConcat(
`nobody`, url, `use`, text, `md5forencrypt`, // `nobody`, url, `use`, text, `md5forencrypt`,
))) // )))
data := ztool.Str_FastConcat( // data := ztool.Str_FastConcat(
url, `-36cd479b6b5-`, text, `-36cd479b6b5-`, digest, // url, `-36cd479b6b5-`, text, `-36cd479b6b5-`, digest,
) // )
// 注JSON编码时会自动将[]byte转为string这里省去一步转换 // // 注JSON编码时会自动将[]byte转为string这里省去一步转换
return map[string][]string{ // return map[string][]string{
`params`: {bytesconv.BytesToString(aesEncrypt(bytesconv.StringToBytes(data), eapiKey, false))}, // `params`: {bytesconv.BytesToString(aesEncrypt(bytesconv.StringToBytes(data), eapiKey, false))},
} // }
} // }
// crypto.js // crypto.js
@ -64,6 +64,54 @@ func aesEncrypt(text, key []byte, iv bool) []byte {
return bytes.ToUpper(zcypt.HexEncode(ciphertext)) return bytes.ToUpper(zcypt.HexEncode(ciphertext))
} }
//go:linkname rsaEncryptNone crypto/rsa.encrypt
func rsaEncryptNone(*rsa.PublicKey, []byte) ([]byte, error)
func rsaEncrypt(data []byte) string {
pblock, _ := pem.Decode(publicKey)
pubKey, _ := x509.ParsePKIXPublicKey(pblock.Bytes)
// 注为实现NONE加密手动导出了标准库里的encrypt方法若编译不过添加以下代码
// /usr/local/go/src/crypto/rsa/rsa.go:478
// ```
// var Encrypt = encrypt // export
// ```
// 第二种方式linkname调用不用改库 https://www.jianshu.com/p/7b3638b47845
encData, err := rsaEncryptNone(pubKey.(*rsa.PublicKey), data)
if err != nil {
panic(err)
}
return zcypt.HexToString(encData)
}
func weapi(object map[string]any) map[string][]string {
text, err := json.Marshal(object)
if err != nil {
panic(err)
}
secretKey := make([]byte, 16)
for i := 0; i < 16; i++ {
secretKey[i] = base62[rand.Intn(62)]
}
return map[string][]string{
`params`: {bytesconv.BytesToString(aesEncrypt(
aesEncrypt(text, presetKey, true),
secretKey,
true,
))},
`encSecKey`: {rsaEncrypt(ztool.Sort_ReverseNew(secretKey))},
}
}
func linuxapi(object map[string]any) map[string][]string {
text, err := json.Marshal(object)
if err != nil {
panic(err)
}
return map[string][]string{
`eparams`: {bytesconv.BytesToString(aesEncrypt(text, linuxapiKey, false))},
}
}
func eapi(url string, object map[string]any) map[string][]string { func eapi(url string, object map[string]any) map[string][]string {
text, err := json.Marshal(object) text, err := json.Marshal(object)
if err != nil { if err != nil {

View File

@ -0,0 +1,61 @@
package wy
import (
"lx-source/src/env"
"maps"
"time"
"github.com/ZxwyWebSite/ztool/logs"
"github.com/ZxwyWebSite/ztool/x/cookie"
)
/*
刷新登录模块 (来自 NeteaseCloudMusicApi)
逻辑
检测返回结果中是否含有"MUSIC_U":
如果有则为正常刷新延时30天
否则延时1天
原代码未提供详细描述无法确定有效结果判断条件暂时先这么写
*/
func init() {
env.Inits.Add(func() {
if env.Config.Custom.Wy_Refresh_Enable && env.Config.Custom.Wy_Cookie != `` {
env.Tasker.Add(`wy_refresh`, func(loger *logs.Logger) error {
// 前置检测
now := time.Now().Unix()
if now < env.Config.Custom.Wy_Refresh_Interval {
loger.Debug(`Key未过期跳过...`)
return nil
}
// 刷新逻辑
cookies := cookie.ToMap(cookie.Parse(env.Config.Custom.Wy_Cookie))
res, err := LoginRefresh(ReqQuery{
Cookie: cookies,
})
loger.Debug(`Resp: %+v`, res)
if err == nil {
if out, ok := res.Body[`cookie`].(string); ok {
loger.Info(`获取数据成功`)
cmap := cookie.ToMap(cookie.Parse(out))
maps.Copy(cookies, cmap)
env.Config.Custom.Wy_Cookie = cookie.Marshal(cookies)
loger.Debug(`Cookie: %#v`, cookies)
if cmap[`MUSIC_U`] != `` {
env.Config.Custom.Wy_Refresh_Interval = now + 2147483647 - 86000
} else {
env.Config.Custom.Wy_Refresh_Interval = now + 86000
loger.Warn(`未发现有效结果,将在下次检测时再次尝试`)
}
err = env.Cfg.Save(``)
if err == nil {
loger.Info(`配置更新成功`)
}
}
}
return err
}, 86000, true)
}
})
}

View File

@ -0,0 +1,27 @@
package wy
import (
"net/http"
"strings"
)
// 登录刷新
func LoginRefresh(query ReqQuery) (*ReqAnswer, error) {
res, err := createRequest(
http.MethodPost,
`https://music.163.com/weapi/login/token/refresh`,
map[string]any{},
reqOptions{
Crypto: `weapi`,
UA: `pc`,
Cookie: query.Cookie,
RealIP: query.RealIP,
},
)
if code, ok := res.Body[`code`].(int); ok && err == nil {
if code == 200 {
res.Body[`cookie`] = strings.Join(res.Cookie, `;`)
}
}
return res, err
}

View File

@ -1,34 +1,76 @@
package wy package wy
import ( import (
"io"
"lx-source/src/env" "lx-source/src/env"
"lx-source/src/sources" "lx-source/src/sources"
"lx-source/src/sources/builtin"
"lx-source/src/sources/custom/utils" "lx-source/src/sources/custom/utils"
"net/http"
"net/url"
"strconv"
"strings"
"github.com/ZxwyWebSite/ztool" "github.com/ZxwyWebSite/ztool"
"github.com/ZxwyWebSite/ztool/x/cookie" "github.com/ZxwyWebSite/ztool/x/cookie"
) )
type playInfo struct {
Data []struct {
ID int `json:"id"`
URL string `json:"url"`
Br int `json:"br"`
Size int `json:"size"`
Md5 string `json:"md5"`
Code int `json:"code"`
Expi int `json:"expi"`
Type string `json:"type"`
Gain float64 `json:"gain"`
Peak float64 `json:"peak"`
Fee int `json:"fee"`
Uf interface{} `json:"uf"`
Payed int `json:"payed"`
Flag int `json:"flag"`
CanExtend bool `json:"canExtend"`
FreeTrialInfo struct {
AlgData interface{} `json:"algData"`
End int `json:"end"`
FragmentType int `json:"fragmentType"`
Start int `json:"start"`
} `json:"freeTrialInfo"`
Level string `json:"level"`
EncodeType string `json:"encodeType"`
FreeTrialPrivilege struct {
ResConsumable bool `json:"resConsumable"`
UserConsumable bool `json:"userConsumable"`
ListenType int `json:"listenType"`
CannotListenReason int `json:"cannotListenReason"`
PlayReason interface{} `json:"playReason"`
} `json:"freeTrialPrivilege"`
FreeTimeTrialPrivilege struct {
ResConsumable bool `json:"resConsumable"`
UserConsumable bool `json:"userConsumable"`
Type int `json:"type"`
RemainTime int `json:"remainTime"`
} `json:"freeTimeTrialPrivilege"`
URLSource int `json:"urlSource"`
RightSource int `json:"rightSource"`
PodcastCtrp interface{} `json:"podcastCtrp"`
EffectTypes interface{} `json:"effectTypes"`
Time int `json:"time"`
} `json:"data"`
Code int `json:"code"`
}
func Url(songMid, quality string) (ourl, msg string) { func Url(songMid, quality string) (ourl, msg string) {
loger := env.Loger.NewGroup(`Wy`) loger := env.Loger.NewGroup(`Wy`)
rquality, ok := brMap[quality] rquality, ok := qualityMap[quality]
if !ok { if !ok {
msg = sources.E_QNotSupport msg = sources.E_QNotSupport
return return
} }
cookies := cookie.Parse(env.Config.Custom.Wy_Cookie) cookies := cookie.Parse(env.Config.Custom.Wy_Cookie)
answer, err := SongUrl(song_url_query{ answer, err := SongUrlV1(ReqQuery{
Cookie: cookie.ToMap(cookies), Cookie: cookie.ToMap(cookies),
Ids: songMid, Ids: songMid,
Br: rquality, // Br: rquality,
Level: rquality,
}) })
var body builtin.WyApi_Song var body playInfo
if err == nil { if err == nil {
err = ztool.Val_MapToStruct(answer.Body, &body) err = ztool.Val_MapToStruct(answer.Body, &body)
} }
@ -43,65 +85,69 @@ func Url(songMid, quality string) (ourl, msg string) {
return return
} }
data := body.Data[0] data := body.Data[0]
br := strconv.Itoa(data.Br) // 注由于flac返回br值不固定暂无法进行比较 if data.Level != rquality {
if br != rquality && !ztool.Chk_IsMatch(br, sources.Q_flac, sources.Q_fl24) { msg = ztool.Str_FastConcat(`实际音质不匹配: `, rquality, ` <= `, data.Level)
msg = sources.E_QNotMatch
return return
} }
// 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) ourl = utils.DelQuery(data.URL)
return return
} }
func PyUrl(songMid, quality string) (ourl, msg string) { // func PyUrl(songMid, quality string) (ourl, msg string) {
loger := env.Loger.NewGroup(`Wy`) // loger := env.Loger.NewGroup(`Wy`)
rquality, ok := qualityMap[quality] // rquality, ok := qualityMap[quality]
if !ok { // if !ok {
msg = sources.E_QNotSupport // msg = sources.E_QNotSupport
return // return
} // }
path := `/api/song/enhance/player/url/v1` // path := `/api/song/enhance/player/url/v1`
requestUrl := `https://interface.music.163.com/eapi/song/enhance/player/url/v1` // requestUrl := `https://interface.music.163.com/eapi/song/enhance/player/url/v1`
var body builtin.WyApi_Song // var body builtin.WyApi_Song
text := ztool.Str_FastConcat( // text := ztool.Str_FastConcat(
`{"encodeType":"flac","ids":["`, songMid, `"],"level":"`, rquality, `"}`, // `{"encodeType":"flac","ids":["`, songMid, `"],"level":"`, rquality, `"}`,
) // )
var form url.Values = eapiEncrypt(path, text) // var form url.Values = eapiEncrypt(path, text)
// form, err := json.Marshal(eapiEncrypt(path, text)) // // form, err := json.Marshal(eapiEncrypt(path, text))
// if err == nil { // // if err == nil {
err := ztool.Net_Request( // err := ztool.Net_Request(
http.MethodPost, requestUrl, // http.MethodPost, requestUrl,
strings.NewReader(form.Encode()), //bytes.NewReader(form), // strings.NewReader(form.Encode()), //bytes.NewReader(form),
[]ztool.Net_ReqHandlerFunc{ztool.Net_ReqAddHeader(map[string]string{ // []ztool.Net_ReqHandlerFunc{ztool.Net_ReqAddHeader(map[string]string{
`Cookie`: env.Config.Custom.Wy_Cookie, // `Cookie`: env.Config.Custom.Wy_Cookie,
})}, // })},
[]ztool.Net_ResHandlerFunc{ // []ztool.Net_ResHandlerFunc{
func(res *http.Response) error { // func(res *http.Response) error {
body, err := io.ReadAll(res.Body) // body, err := io.ReadAll(res.Body)
if err != nil { // if err != nil {
return err // return err
} // }
loger.Info(`%s`, body) // loger.Info(`%s`, body)
return ztool.Err_EsContinue // return ztool.Err_EsContinue
}, // },
ztool.Net_ResToStruct(&body), // 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
// } // }
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

@ -2,19 +2,25 @@ package wy
import ( import (
"net/http" "net/http"
"sort"
"strconv"
"strings" "strings"
"github.com/ZxwyWebSite/ztool" "github.com/ZxwyWebSite/ztool"
) )
type song_url_query struct { // type Query_song_url struct {
Cookie map[string]string // Cookie map[string]string
Ids string // Ids string
Br string // Br string
RealIP string // RealIP string
} // }
func SongUrl(query song_url_query) (*reqAnswer, error) { // 歌曲链接
func SongUrl(query ReqQuery) (*ReqAnswer, error) {
if query.Cookie == nil {
query.Cookie = make(map[string]string)
}
query.Cookie[`os`] = `pc` query.Cookie[`os`] = `pc`
if query.Br == `` { if query.Br == `` {
query.Br = `999000` query.Br = `999000`
@ -28,7 +34,7 @@ func SongUrl(query song_url_query) (*reqAnswer, error) {
`ids`: ztool.Str_FastConcat(`["`, strings.Join(ids, `","`), `"]`), //bytesconv.BytesToString(idj), //`["1998644237"]`, `ids`: ztool.Str_FastConcat(`["`, strings.Join(ids, `","`), `"]`), //bytesconv.BytesToString(idj), //`["1998644237"]`,
`br`: query.Br, //ztool.Str_Select(query.Br, `999000`), `br`: query.Br, //ztool.Str_Select(query.Br, `999000`),
} }
return createRequest( res, err := createRequest(
http.MethodPost, http.MethodPost,
`https://interface3.music.163.com/eapi/song/enhance/player/url`, `https://interface3.music.163.com/eapi/song/enhance/player/url`,
data, data,
@ -39,4 +45,29 @@ func SongUrl(query song_url_query) (*reqAnswer, error) {
Url: `/api/song/enhance/player/url`, Url: `/api/song/enhance/player/url`,
}, },
) )
// 根据id排序
if length := len(ids); length > 1 && err == nil {
indexOf := make(map[string]int, length)
for i := 0; i < length; i++ {
indexOf[ids[i]] = i
}
if data, ok := res.Body[`data`].([]interface{}); ok {
sort.SliceStable(data, func(a, b int) bool {
da, oa := data[a].(map[string]interface{})
db, ob := data[b].(map[string]interface{})
if oa && ob {
ia, ka := da[`id`].(float64)
ib, kb := db[`id`].(float64)
if ka && kb {
ta := strconv.FormatInt(int64(ia), 10)
tb := strconv.FormatInt(int64(ib), 10)
return indexOf[ta] < indexOf[tb]
}
}
return false
})
res.Body[`data`] = data
}
}
return res, err
} }

View File

@ -0,0 +1,37 @@
package wy
import (
"net/http"
"github.com/ZxwyWebSite/ztool"
)
// 歌曲链接 - v1
// 此版本不再采用 br 作为音质区分的标准
// 而是采用 standard, exhigh, lossless, hires, jyeffect(高清环绕声), sky(沉浸环绕声), jymaster(超清母带) 进行音质判断
func SongUrlV1(query ReqQuery) (*ReqAnswer, error) {
if query.Cookie == nil {
query.Cookie = make(map[string]string)
}
query.Cookie[`os`] = `android`
query.Cookie[`appver`] = `8.10.05`
data := map[string]any{
`ids`: ztool.Str_FastConcat(`[`, query.Ids, `]`),
`level`: query.Level,
`encodeType`: `flac`,
}
if query.Level == `sky` /*|| query.Level == `jysky`*/ {
data[`immerseType`] = `c51`
}
return createRequest(
http.MethodPost,
`https://interface.music.163.com/eapi/song/enhance/player/url/v1`,
data,
reqOptions{
Crypto: `eapi`,
Cookie: query.Cookie,
RealIP: query.RealIP,
Url: `/api/song/enhance/player/url/v1`,
},
)
}

View File

@ -3,12 +3,12 @@ package wy
import "lx-source/src/sources" import "lx-source/src/sources"
var ( var (
brMap = map[string]string{ // brMap = map[string]string{
sources.Q_128k: `128000`, // sources.Q_128k: `128000`,
sources.Q_320k: `320000`, // sources.Q_320k: `320000`,
sources.Q_flac: `1000000`, //`743625`,`915752` // sources.Q_flac: `1000000`, //`743625`,`915752`
sources.Q_fl24: `2000000`, //`1453955`,`1683323` // sources.Q_fl24: `2000000`, //`1453955`,`1683323`
} // }
qualityMap = map[string]string{ qualityMap = map[string]string{
sources.Q_128k: `standard`, sources.Q_128k: `standard`,
sources.Q_320k: `exhigh`, sources.Q_320k: `exhigh`,

View File

@ -28,24 +28,30 @@ var userAgentMap = map[string]string{
} }
type ( type (
// reqCookie struct{} ReqQuery struct {
Cookie map[string]string
RealIP string
Ids string
Br string
Level string
}
reqOptions struct { reqOptions struct {
UA string
Headers map[string]string Headers map[string]string
UA string
RealIP string RealIP string
IP string IP string
Cookie interface{}
Crypto string Crypto string
Url string Url string
Cookie interface{}
} }
reqAnswer struct { ReqAnswer struct {
Status int Status int
Body map[string]any Body map[string]any
Cookie []string Cookie []string
} }
) )
func createRequest(method, url string, data map[string]any, options reqOptions) (*reqAnswer, error) { func createRequest(method, url string, data map[string]any, options reqOptions) (*ReqAnswer, error) {
if options.Headers == nil { if options.Headers == nil {
options.Headers = make(map[string]string) options.Headers = make(map[string]string)
} }
@ -96,26 +102,25 @@ func createRequest(method, url string, data map[string]any, options reqOptions)
var form stdurl.Values var form stdurl.Values
switch options.Crypto { switch options.Crypto {
case `weapi`: 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`
// 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=([^(;|$)]+)`)
// reg := regexp.MustCompile(`_csrf=([^(;|$)]+)`) csrfToken := reg.FindStringSubmatch(options.Headers[`Cookie`])
// csrfToken := reg.FindStringSubmatch(options.Headers[`Cookie`]) if len(csrfToken) > 1 {
// if len(csrfToken) > 1 { data[`csrf_token`] = csrfToken[1]
// data[`csrf_token`] = csrfToken[1] } else {
// } else { data[`csrf_token`] = ``
// data[`csrf_token`] = `` }
// } form = weapi(data)
// data = weapi(data) // fmt.Println(form.Encode())
// url = wapiReg.ReplaceAllString(url, `weapi`) url = wapiReg.ReplaceAllString(url, `weapi`)
case `linuxapi`: case `linuxapi`:
panic(`not support`) form = linuxapi(map[string]any{
// data = linuxapi( `method`: method,
// method, `url`: wapiReg.ReplaceAllString(url, `weapi`),
// wapiReg.ReplaceAllString(url, `weapi`), `params`: data,
// 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`
// 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`
// url = `https://music.163.com/api/linux/forward`
case `eapi`: case `eapi`:
cookie, ok := options.Cookie.(map[string]string) cookie, ok := options.Cookie.(map[string]string)
if !ok { if !ok {
@ -168,7 +173,7 @@ func createRequest(method, url string, data map[string]any, options reqOptions)
// for k, v := range data { // for k, v := range data {
// values.Add(k, v) // values.Add(k, v)
// } // }
answer := reqAnswer{Status: 500, Body: map[string]any{} /*, Cookie: []string{}*/} answer := ReqAnswer{Status: 500, Body: map[string]any{} /*, Cookie: []string{}*/}
err := ztool.Net_Request(method, url, err := ztool.Net_Request(method, url,
strings.NewReader(form.Encode()), strings.NewReader(form.Encode()),
[]ztool.Net_ReqHandlerFunc{ []ztool.Net_ReqHandlerFunc{
@ -178,7 +183,12 @@ func createRequest(method, url string, data map[string]any, options reqOptions)
func(res *http.Response) error { func(res *http.Response) error {
body, err := io.ReadAll(res.Body) body, err := io.ReadAll(res.Body)
if err == nil { if err == nil {
answer.Cookie = res.Header[`Set-Cookie`] // fmt.Println(`body:`, string(body), "\nstr:", body)
if len(body) == 0 {
err = errors.New(`nil Body`)
}
if err == nil {
answer.Cookie = res.Header[`Set-Cookie`] //res.Header.Values(`set-cookie`)
reg := regexp.MustCompile(`\s*Domain=[^(;|$)]+;*`) reg := regexp.MustCompile(`\s*Domain=[^(;|$)]+;*`)
for i, v := range answer.Cookie { for i, v := range answer.Cookie {
answer.Cookie[i] = reg.ReplaceAllString(v, ``) answer.Cookie[i] = reg.ReplaceAllString(v, ``)
@ -188,7 +198,6 @@ func createRequest(method, url string, data map[string]any, options reqOptions)
} else { } else {
err = json.Unmarshal(body, &answer.Body) err = json.Unmarshal(body, &answer.Body)
} }
if err == nil {
if code, ok := answer.Body[`code`].(string); ok { if code, ok := answer.Body[`code`].(string); ok {
answer.Body[`code`], err = strconv.Atoi(code) answer.Body[`code`], err = strconv.Atoi(code)
} else { } else {
@ -212,7 +221,7 @@ func createRequest(method, url string, data map[string]any, options reqOptions)
} }
if err != nil { if err != nil {
answer.Status = 502 answer.Status = 502
answer.Body = map[string]any{`code`: 502, `msg`: err} answer.Body = map[string]any{`code`: 502, `msg`: err.Error()}
return err return err
} }
return nil return nil

View File

@ -1,5 +1,32 @@
## Lx-Source/更新日志 ## Lx-Source/更新日志
#### \# 2024-01-21 v1.0.2-b12-d2 (dev)
+ 新版api结构设计(暂定)
```
# 基础接口
/
/link/{source}/{musicid}/{quality}
# 功能接口
/api/{source}/{method}/{query}
| 源 | 功能 | 参数 |
/api/wy/link/?id=xxx&quality=320k&key=xxx
# 软件接口
/app/{name}/{method}/{?query}
| 名称 | 功能 |
/app/lxmusic/link (参数通过post传入)
/app/musicfree/xxx (计划支持MusicFree)
```
+ 添加wy外链获取v1支持并改用此版本
+ 计划:统一错误输出,进行以下分类
+ 验证失败(Verify Failed)、实际音质不匹配、无返回数据(No Data)、...
<!-- + 其它这一段时间主要完善wy源接口kg和mg账号源推迟更新 -->
#### \# 2024-01-19 v1.0.2-b12-d2 (dev)
+ 添加wy批量SongUrl获取排序功能
+ 完善wy请求加密支持
+ 添加wy刷新登录模块(beta)
+ 待优化cookie需要频繁在map和string之间转换
#### \# 2024-01-18 v1.0.2-b12-d1 (dev) #### \# 2024-01-18 v1.0.2-b12-d1 (dev)
+ 对部分功能实现方式进行优化去除qualityMapReverse依赖 + 对部分功能实现方式进行优化去除qualityMapReverse依赖
+ 由于wy修改api验证方式python版逻辑已不可用现参考NeteaseCloudMusicApi项目进行修改 + 由于wy修改api验证方式python版逻辑已不可用现参考NeteaseCloudMusicApi项目进行修改