From ad7614c56490b811e74ad3281213f06cf196512d Mon Sep 17 00:00:00 2001 From: ZxwyWebSite Date: Sun, 21 Jan 2024 23:23:47 +0800 Subject: [PATCH] 2024-01-21 v1.0.2-b12-d2 --- src/env/env.go | 11 +- src/sources/custom/tx/info.go | 2 +- src/sources/custom/wy/encrypt.go | 96 +++++++++---- src/sources/custom/wy/init.go | 61 +++++++++ src/sources/custom/wy/login_refresh.go | 27 ++++ src/sources/custom/wy/player.go | 178 ++++++++++++++++--------- src/sources/custom/wy/song_url.go | 47 +++++-- src/sources/custom/wy/song_url_v1.go | 37 +++++ src/sources/custom/wy/types.go | 12 +- src/sources/custom/wy/utils.go | 79 ++++++----- update.md | 27 ++++ 11 files changed, 433 insertions(+), 144 deletions(-) create mode 100644 src/sources/custom/wy/init.go create mode 100644 src/sources/custom/wy/login_refresh.go create mode 100644 src/sources/custom/wy/song_url_v1.go diff --git a/src/env/env.go b/src/env/env.go index b158026..45974b8 100644 --- a/src/env/env.go +++ b/src/env/env.go @@ -69,9 +69,11 @@ type ( ForceFallback bool `comment:"忽略音质限制,强制获取试听音频"` } // `comment:""` Conf_Custom struct { - // wy (暂未实现) - Wy_Enable bool `comment:"是否开启小芸源"` - Wy_Cookie string `comment:"账号cookie数据"` + // wy + Wy_Enable bool `comment:"是否开启小芸源"` + Wy_Cookie string `comment:"账号cookie数据"` + Wy_Refresh_Enable bool `comment:"是否启用刷新登录"` + Wy_Refresh_Interval int64 `comment:"下次刷新时间 (由程序维护)"` // mg (暂未实现) // Mg_Enable bool `comment:"是否开启小蜜源"` @@ -161,7 +163,8 @@ var ( Proxy_Address: `{protocol}://({user}:{password})@{address}:{port}`, }, Custom: Conf_Custom{ - Wy_Enable: true, + Wy_Enable: true, + Wy_Refresh_Interval: 1633622400, Kw_Enable: true, Kw_Mode: `kwdes`, diff --git a/src/sources/custom/tx/info.go b/src/sources/custom/tx/info.go index 3d4902c..d09f36e 100644 --- a/src/sources/custom/tx/info.go +++ b/src/sources/custom/tx/info.go @@ -33,7 +33,7 @@ import ( func init() { env.Inits.Add(func() { 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) return nil }, 86000, true) diff --git a/src/sources/custom/wy/encrypt.go b/src/sources/custom/wy/encrypt.go index fe50a00..3076bf1 100644 --- a/src/sources/custom/wy/encrypt.go +++ b/src/sources/custom/wy/encrypt.go @@ -4,7 +4,12 @@ import ( "bytes" "crypto/aes" "crypto/cipher" + "crypto/rsa" + "crypto/x509" "encoding/base64" + "encoding/pem" + "math/rand" + _ "unsafe" "github.com/ZxwyWebSite/ztool" "github.com/ZxwyWebSite/ztool/x/bytesconv" @@ -13,33 +18,28 @@ import ( ) 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`) + 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`) - 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))}, - } -} +// 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 @@ -64,6 +64,54 @@ func aesEncrypt(text, key []byte, iv bool) []byte { 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 { text, err := json.Marshal(object) if err != nil { diff --git a/src/sources/custom/wy/init.go b/src/sources/custom/wy/init.go new file mode 100644 index 0000000..940d233 --- /dev/null +++ b/src/sources/custom/wy/init.go @@ -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) + } + }) +} diff --git a/src/sources/custom/wy/login_refresh.go b/src/sources/custom/wy/login_refresh.go new file mode 100644 index 0000000..f3fc209 --- /dev/null +++ b/src/sources/custom/wy/login_refresh.go @@ -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 +} diff --git a/src/sources/custom/wy/player.go b/src/sources/custom/wy/player.go index 656a9b0..588061e 100644 --- a/src/sources/custom/wy/player.go +++ b/src/sources/custom/wy/player.go @@ -1,34 +1,76 @@ 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" ) +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) { loger := env.Loger.NewGroup(`Wy`) - rquality, ok := brMap[quality] + rquality, ok := qualityMap[quality] if !ok { msg = sources.E_QNotSupport return } cookies := cookie.Parse(env.Config.Custom.Wy_Cookie) - answer, err := SongUrl(song_url_query{ + answer, err := SongUrlV1(ReqQuery{ Cookie: cookie.ToMap(cookies), Ids: songMid, - Br: rquality, + // Br: rquality, + Level: rquality, }) - var body builtin.WyApi_Song + var body playInfo if err == nil { err = ztool.Val_MapToStruct(answer.Body, &body) } @@ -43,65 +85,69 @@ func Url(songMid, quality string) (ourl, msg string) { 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 + if data.Level != rquality { + msg = ztool.Str_FastConcat(`实际音质不匹配: `, rquality, ` <= `, data.Level) 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) 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 -} +// 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 +// } diff --git a/src/sources/custom/wy/song_url.go b/src/sources/custom/wy/song_url.go index 8acbe75..b43407f 100644 --- a/src/sources/custom/wy/song_url.go +++ b/src/sources/custom/wy/song_url.go @@ -2,19 +2,25 @@ package wy import ( "net/http" + "sort" + "strconv" "strings" "github.com/ZxwyWebSite/ztool" ) -type song_url_query struct { - Cookie map[string]string - Ids string - Br string - RealIP string -} +// type Query_song_url struct { +// Cookie map[string]string +// Ids string +// Br 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` if query.Br == `` { 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"]`, `br`: query.Br, //ztool.Str_Select(query.Br, `999000`), } - return createRequest( + res, err := createRequest( http.MethodPost, `https://interface3.music.163.com/eapi/song/enhance/player/url`, data, @@ -39,4 +45,29 @@ func SongUrl(query song_url_query) (*reqAnswer, error) { 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 } diff --git a/src/sources/custom/wy/song_url_v1.go b/src/sources/custom/wy/song_url_v1.go new file mode 100644 index 0000000..780863b --- /dev/null +++ b/src/sources/custom/wy/song_url_v1.go @@ -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`, + }, + ) +} diff --git a/src/sources/custom/wy/types.go b/src/sources/custom/wy/types.go index 02c5f26..120a912 100644 --- a/src/sources/custom/wy/types.go +++ b/src/sources/custom/wy/types.go @@ -3,12 +3,12 @@ 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` - } + // 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`, diff --git a/src/sources/custom/wy/utils.go b/src/sources/custom/wy/utils.go index 26d0b9e..ca6ca85 100644 --- a/src/sources/custom/wy/utils.go +++ b/src/sources/custom/wy/utils.go @@ -28,24 +28,30 @@ var userAgentMap = map[string]string{ } type ( - // reqCookie struct{} + ReqQuery struct { + Cookie map[string]string + RealIP string + Ids string + Br string + Level string + } reqOptions struct { - UA string Headers map[string]string + UA string RealIP string IP string - Cookie interface{} Crypto string Url string + Cookie interface{} } - reqAnswer struct { + ReqAnswer struct { Status int Body map[string]any 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 { 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 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`) + 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`] = `` + } + form = weapi(data) + // fmt.Println(form.Encode()) + 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` + form = linuxapi(map[string]any{ + `method`: method, + `url`: wapiReg.ReplaceAllString(url, `weapi`), + `params`: 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 { @@ -168,7 +173,7 @@ func createRequest(method, url string, data map[string]any, options reqOptions) // for k, v := range data { // 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, strings.NewReader(form.Encode()), []ztool.Net_ReqHandlerFunc{ @@ -178,17 +183,21 @@ func createRequest(method, url string, data map[string]any, options reqOptions) 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) + // 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=[^(;|$)]+;*`) + 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 code, ok := answer.Body[`code`].(string); ok { answer.Body[`code`], err = strconv.Atoi(code) } else { @@ -212,7 +221,7 @@ func createRequest(method, url string, data map[string]any, options reqOptions) } if err != nil { 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 nil diff --git a/update.md b/update.md index 71e54b8..b68daac 100644 --- a/update.md +++ b/update.md @@ -1,5 +1,32 @@ ## 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)、... + + +#### \# 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) + 对部分功能实现方式进行优化,去除qualityMapReverse依赖 + 由于wy修改api验证方式,python版逻辑已不可用,现参考NeteaseCloudMusicApi项目进行修改