Skip to content
This repository has been archived by the owner on Oct 19, 2024. It is now read-only.

Commit

Permalink
2024-06-14 v1.0.3.0614
Browse files Browse the repository at this point in the history
  • Loading branch information
ZxwyWebSite committed Jun 14, 2024
1 parent 76e7f5a commit 55e7c62
Show file tree
Hide file tree
Showing 9 changed files with 429 additions and 87 deletions.
265 changes: 201 additions & 64 deletions action.go

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion src/env/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (
)

const (
Version = `1.0.3.0518`
Version = `1.0.3.0614`
)

var (
Expand Down Expand Up @@ -124,6 +124,7 @@ type (
Kw_Bd_DevId string `comment:"field user.device_id"`
// kw kwdes
Kw_Des_Type string `comment:"返回格式 0: text, 1: json"`
Kw_Des_Source string `comment:"query source"`
Kw_Des_Header string `comment:"请求头 User-Agent"`

// kg
Expand Down
156 changes: 151 additions & 5 deletions src/sources/custom/kw/player.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
package kw

import (
"errors"
"io"
"lx-source/src/env"
"lx-source/src/sources"
"lx-source/src/sources/custom/utils"
"net/http"
"strconv"
"strings"
"sync"

"github.com/ZxwyWebSite/ztool"
"github.com/ZxwyWebSite/ztool/x/bytesconv"
"github.com/ZxwyWebSite/ztool/zcypt"
)

var (
Expand All @@ -19,6 +23,7 @@ var (

parsemod bool
convtype string
desource string
// desParse func([]byte, *playInfo) error
// desParse func(any) ztool.Net_ResHandlerFunc
)
Expand All @@ -36,11 +41,13 @@ func init() {
) {
loger.Fatal(`使用bdapi且验证参数为空`)
}
// bdheader[`token`] = env.Config.Custom.Kw_Bd_Token
bdheader[`uid`] = env.Config.Custom.Kw_Bd_Uid
bdheader[`devId`] = env.Config.Custom.Kw_Bd_DevId
kw_pool = &sync.Pool{New: func() any { return new(kwApi_Song) }}
Url = bdapi
case `1`, `kwdes`:
Url = kwdes
switch env.Config.Custom.Kw_Des_Type {
case `0`, `text`:
loger.Debug(`use kwdes text`)
Expand All @@ -51,12 +58,20 @@ func init() {
convtype = `convert_url_with_sign`
// desParse = ztool.Net_ResToStruct
parsemod = true
case `2`, `anti`:
loger.Debug(`use kwdes anti`)
Url = manti
default:
loger.Fatal(`未定义的返回格式,请检查配置 [Custom].Kw_Des_Type`)
}
desheader[`User-Agent`] = env.Config.Custom.Kw_Des_Header
kw_pool = &sync.Pool{New: func() any { return new(playInfo) }}
Url = kwdes
if env.Config.Custom.Kw_Des_Source != `` {
desource = env.Config.Custom.Kw_Des_Source
} else {
dec, _ := zcypt.HexDecode([]byte{0x36, 0x62, 0x37, 0x37, 0x37, 0x30, 0x36, 0x63, 0x36, 0x31, 0x37, 0x39, 0x36, 0x35, 0x37, 0x32, 0x36, 0x38, 0x36, 0x34, 0x35, 0x66, 0x36, 0x31, 0x37, 0x32, 0x35, 0x66, 0x33, 0x35, 0x32, 0x65, 0x33, 0x31, 0x32, 0x65, 0x33, 0x30, 0x32, 0x65, 0x33, 0x30, 0x35, 0x66, 0x34, 0x32, 0x35, 0x66, 0x36, 0x61, 0x36, 0x39, 0x36, 0x31, 0x36, 0x62, 0x36, 0x66, 0x36, 0x65, 0x36, 0x37, 0x35, 0x66, 0x37, 0x36, 0x36, 0x38, 0x32, 0x65, 0x36, 0x31, 0x37, 0x30, 0x36, 0x62})
desource = bytesconv.BytesToString(dec)
}
default:
loger.Fatal(`未定义的接口模式,请检查配置 [Custom].Kw_Mode`)
}
Expand Down Expand Up @@ -92,7 +107,7 @@ func bdapi(songMid, quality string) (ourl, msg string) {
loger.Debug(`Resp: %+v`, resp)
if resp.Code != 200 || resp.Data.AudioInfo.Bitrate == `1` {
// jx.Debug(`Kw, Err: %#v`, resp)
msg = ztool.Str_FastConcat(`failed: `, resp.Msg)
msg = ztool.Str_FastConcat(strconv.Itoa(resp.Code), `: `, resp.Msg)
return
}
ourl = utils.DelQuery(resp.Data.URL) //strings.Split(resp.Data.URL, `?`)[0]
Expand All @@ -110,12 +125,12 @@ func kwdes(songMid, quality string) (ourl, msg string) {
target_url := ztool.Str_FastConcat(
`https://mobi.kuwo.cn/mobi.s?f=kuwo&q=`,
base64_encrypt(ztool.Str_FastConcat(
`corp=kuwo&source=kwplayer_ar_1.1.9_oppo_118980_320.apk&p2p=1&sig=0`,
`corp=kuwo&p2p=1&sig=0&notrace=0&priority=bitrate&network=WIFI&mode=down`,
`&source=`, desource,
`&type=`, convtype,
`&br=`, infoFile.H, infoFile.E,
`&format=`, infoFile.E,
`&rid=`, songMid,
`&notrace=0&priority=bitrate&network=WIFI&mode=down`,
)),
)
if parsemod {
Expand All @@ -131,6 +146,7 @@ func kwdes(songMid, quality string) (ourl, msg string) {
msg = sources.ErrHttpReq
return
}
resp.Data.URL = utils.DelQuery(resp.Data.URL)
loger.Debug(`Resp: %+v`, resp)
if resp.Code != http.StatusOK {
msg = ztool.Str_FastConcat(`failed: `, resp.Msg)
Expand All @@ -144,7 +160,7 @@ func kwdes(songMid, quality string) (ourl, msg string) {
return
}
}
ourl = utils.DelQuery(resp.Data.URL) //resp.Data.URL[:strings.Index(resp.Data.URL, `?`)]
ourl = resp.Data.URL //resp.Data.URL[:strings.Index(resp.Data.URL, `?`)]
return
}
ztool.Net_Request(http.MethodGet, target_url, nil,
Expand Down Expand Up @@ -179,3 +195,133 @@ func kwdes(songMid, quality string) (ourl, msg string) {
)
return
}

// 一种替代方案,仅在试听可用时获取cdn前缀,缺点是无法获取不能试听的歌曲
func manti(songMid, quality string) (ourl, msg string) {
loger := env.Loger.NewGroup(`Kw`)
defer loger.Free()
infoFile, ok := fileInfo[quality]
if !ok {
msg = sources.E_QNotSupport
return
}
// 获取CDN地址
// var out_a string
/*var out_c struct {
// Timestamp int `json:"timestamp"`
Songs []struct {
ID int `json:"id"`
Duration int `json:"duration"`
URL string `json:"url"`
HTTPS string `json:"https"`
CarURL string `json:"car_url"`
CarURLHTTPS string `json:"car_url_https"`
Format string `json:"format"`
Br int `json:"br"`
OverseasCopyright string `json:"overseas_copyright"`
Start int `json:"start"`
End int `json:"end"`
Group string `json:"group"`
} `json:"songs"`
IP string `json:"ip"`
Country string `json:"country"`
Region string `json:"region"`
Locationid int `json:"locationid"`
Code int `json:"code"`
Result string `json:"result"`
}
err := ztool.Net_Request(
http.MethodGet,
`https://musicpay30.kuwo.cn/audi.tion?op=query&ids=`+songMid,
nil,
[]ztool.Net_ReqHandlerFunc{ztool.Net_ReqAddHeaders()},
[]ztool.Net_ResHandlerFunc{ztool.Net_ResToStruct(&out_c)},
)
if err != nil {
loger.Error(`Request: %s`, err)
msg = sources.ErrHttpReq
return
}*/
// 获取音频路径
if quality != sources.Q_128k {
ourl, msg = manti(songMid, sources.Q_128k)
if msg != `` {
return
}
if i := strings.LastIndexByte(ourl, '/'); i != -1 {
if ourl[i+1:] == `2272659253.mp3` {
msg = sources.ErrNoLink
return
}
}
target_url := ztool.Str_FastConcat(
`https://mobi.kuwo.cn/mobi.s?f=web&type=convert_url`,
`&br=`, infoFile.H, infoFile.E,
`&format=`, infoFile.E,
`&rid=`, songMid,
)
var out_u, realQuality string
err := ztool.Net_Request(
http.MethodGet, target_url, nil,
[]ztool.Net_ReqHandlerFunc{ztool.Net_ReqAddHeaders()},
[]ztool.Net_ResHandlerFunc{func(res *http.Response) (err error) {
var data []byte
data, err = io.ReadAll(res.Body)
if err != nil {
return
}
if res.StatusCode != http.StatusOK {
return errors.New(`failed: ` + res.Status)
}
infoData := mkMap(data)
loger.Debug(`uData: %+v`, infoData)
realQuality = infoData[`bitrate`]
out_u = utils.DelQuery(infoData[`url`])
return
}},
)
if err != nil {
loger.Error(`Request: %s`, err)
msg = sources.ErrHttpReq
return
}
if realQuality != infoFile.H[:len(infoFile.H)-1] {
msg = sources.E_QNotMatch
if !env.Config.Source.ForceFallback {
return
}
}
if ourl != `` && out_u != `` {
ourl = ourl[:24] + out_u
}
return
}
resp := kw_pool.Get().(*playInfo)
defer kw_pool.Put(resp)

err := ztool.Net_Request(
http.MethodGet, `https://mobi.kuwo.cn/mobi.s?f=web&type=convert_url_with_sign&br=128kmp3&format=mp3&rid=`+songMid, nil,
[]ztool.Net_ReqHandlerFunc{ztool.Net_ReqAddHeader(desheader)},
[]ztool.Net_ResHandlerFunc{ztool.Net_ResToStruct(&resp)},
)
if err != nil {
loger.Error(`Request: %s`, err)
msg = sources.ErrHttpReq
return
}
loger.Debug(`tData: %+v`, resp)
if resp.Code != http.StatusOK {
msg = ztool.Str_FastConcat(`failed: `, resp.Msg)
loger.Debug(msg)
return
}
realQuality := strconv.Itoa(resp.Data.Bitrate)
if realQuality != infoFile.H[:len(infoFile.H)-1] && resp.Data.Bitrate != 1 {
msg = sources.E_QNotMatch
if !env.Config.Source.ForceFallback {
return
}
}
ourl = utils.DelQuery(resp.Data.URL)
return
}
4 changes: 2 additions & 2 deletions src/sources/custom/kw/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ type (
Format string `json:"format"`
P2PAudiosourceid string `json:"p2p_audiosourceid"`
Rid int `json:"rid"`
Source string `json:"source"`
URL string `json:"url"`
// Source string `json:"source"`
URL string `json:"url"`
} `json:"data"`
Msg string `json:"msg"`
}
Expand Down
53 changes: 46 additions & 7 deletions src/sources/custom/kw/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,25 +40,64 @@ var (
// `User-Agent`: `okhttp/3.10.0`,
}
bdheader = map[string]string{
`channel`: `qq`,
`channel`: `guanfang`,
`plat`: `ar`,
`net`: `wifi`,
`ver`: `3.1.2`,
// `uid`: ``,
// `devId`: `0`,
`ver`: `3.1.4`,
`api-ver`: `application/json`,

`user-agent`: `Dart/2.18 (dart:io)`, //`Dalvik/2.1.0 (Linux; U; Android 7.1.1; OPPO R9sk Build/NMF26F)`,
}
// bdsreg = regexp.MustCompile(`[^a-zA-Z0-9]`)
)

func mkMap(data []byte) map[string]string {
out := make(map[string]string)
sep := bytes.Split(data, []byte{13, 10})
for i, r := 0, len(sep); i < r; i++ {
pat := bytes.Split(sep[i], []byte{61})
if len(pat) == 2 {
var s = sep[i]
if p := bytes.IndexByte(s, '='); p != -1 {
out[bytesconv.BytesToString(s[:p])] = bytesconv.BytesToString(s[p+1:])
continue
} else {
out[`_`] += bytesconv.BytesToString(s) + `;`
}
/*pat := bytes.Split(sep[i], []byte{61})
if len(pat) >= 2 {
out[bytesconv.BytesToString(pat[0])] = bytesconv.BytesToString(pat[1])
continue
}
out[`_`] += bytesconv.BytesToString(pat[0]) + `;`
out[`_`] += bytesconv.BytesToString(pat[0]) + `;`*/
}
return out
}

// 波点签名算法
/*func Bdsign(str string, m, m2 map[string]string) *strings.Builder {
var b strings.Builder
b.WriteString(`uid=`)
b.WriteString(env.Config.Custom.Kw_Bd_Uid)
b.WriteByte('&')
b.WriteString(`token=`)
b.WriteString(env.Config.Custom.Kw_Bd_Token)
b.WriteByte('&')
b.WriteString(`timestamp=`)
b.WriteString(strconv.FormatInt(time.Now().UnixMilli(), 10))
for k, v := range m2 {
b.WriteByte('&')
b.WriteString(k)
b.WriteByte('=')
b.WriteString(url.QueryEscape(v))
}
// 取 strings.Builder.buf []byte 地址
pb := (*[]byte)(unsafe.Pointer(uintptr(unsafe.Pointer(&b)) + unsafe.Sizeof((*strings.Builder)(nil))))
charArray := bdsreg.ReplaceAll(*pb, []byte{})
slices.Sort(charArray)
str3 := string(charArray)
fmt.Println(str3)
lowerCase := zcypt.MD5EncStr(`kuwotest` + str3 + `/api/play/music/v2/audioUrl`)
b.WriteByte('&')
b.WriteString(`sign=`)
b.WriteString(lowerCase)
return &b
}*/
4 changes: 2 additions & 2 deletions src/sources/custom/tx/login.go
Original file line number Diff line number Diff line change
Expand Up @@ -343,8 +343,8 @@ func Qlogin_graph(l *logs.Logger) error {
env.Config.Custom.Tx_Enable = true
env.Config.Custom.Tx_Uuin = out6.Req.Data.StrMusicid
env.Config.Custom.Tx_Ukey = out6.Req.Data.Musickey
env.Config.Custom.Tx_Refresh_Enable = true
env.Config.Custom.Tx_Refresh_Interval = time.Date(now.Year(), now.Month(), now.Day()+5, 0, 0, 0, 0, now.Location()).Unix()
env.Config.Custom.Tx_Refresh_Enable = false
// env.Config.Custom.Tx_Refresh_Interval = time.Date(now.Year(), now.Month(), now.Day()+5, 0, 0, 0, 0, now.Location()).Unix()
// env.Config.Custom.Tx_RefreshToken = out6.Req.Data.RefreshToken
// env.Config.Custom.Tx_AccessToken = out6.Req.Data.AccessToken

Expand Down
11 changes: 10 additions & 1 deletion src/sources/custom/tx/refresh.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,9 +123,18 @@ func refresh(loger *logs.Logger, now int64) error {
// loger.Error(`请求Api失败: %s`, err)
return errors.New(`请求Api失败: ` + err.Error())
}
loger.Debug(`Resp: %+v`, resp)
if resp.Req1.Code != 0 {
switch resp.Req1.Code {
case 1000:
return fmt.Errorf(`%v: Token无效或已过期`, resp.Req1.Code)
case 2000:
return fmt.Errorf(`%v: 该Token不支持刷新`, resp.Req1.Code)
default:
return fmt.Errorf(`%v: 刷新登录失败`, resp.Req1.Code)
}
// loger.Warn("刷新登录失败, code: %v\n响应体: %+v", resp.Req1.Code, resp)
return fmt.Errorf("刷新登录失败, code: %v\n响应体: %+v", resp.Req1.Code, resp)
// return fmt.Errorf("刷新登录失败, code: %v\n响应体: %+v", resp.Req1.Code, resp)
}
loger.Info(`刷新登录成功`)
env.Config.Custom.Tx_Uuin = resp.Req1.Data.StrMusicId
Expand Down
Loading

0 comments on commit 55e7c62

Please sign in to comment.