From b299e12a400fe4930bf33a584fa9f85c196908c8 Mon Sep 17 00:00:00 2001 From: zijiren233 Date: Sun, 29 Oct 2023 16:25:34 +0800 Subject: [PATCH] Fix: bilibili wbi --- vendors/bilibili/bilibili.go | 13 ++++ vendors/bilibili/client.go | 6 +- vendors/bilibili/movie.go | 3 +- vendors/bilibili/wbi.go | 139 +++++++++++++++++++++++++++++++++++ 4 files changed, 158 insertions(+), 3 deletions(-) create mode 100644 vendors/bilibili/wbi.go diff --git a/vendors/bilibili/bilibili.go b/vendors/bilibili/bilibili.go index 444e9c1..70e546c 100644 --- a/vendors/bilibili/bilibili.go +++ b/vendors/bilibili/bilibili.go @@ -812,3 +812,16 @@ type pgcURLInfo struct { Status int `json:"status"` } `json:"result"` } + +type wbi struct { + Code int `json:"code"` + Message string `json:"message"` + TTL int `json:"ttl"` + Data struct { + IsLogin bool `json:"isLogin"` + WbiImg struct { + ImgURL string `json:"img_url"` + SubURL string `json:"sub_url"` + } `json:"wbi_img"` + } `json:"data"` +} diff --git a/vendors/bilibili/client.go b/vendors/bilibili/client.go index 34838f6..565e4c4 100644 --- a/vendors/bilibili/client.go +++ b/vendors/bilibili/client.go @@ -32,6 +32,10 @@ func NewClient(cookies []*http.Cookie, conf ...ClientConfig) *Client { } func (c *Client) NewRequest(method, url string, body io.Reader) (*http.Request, error) { + url, err := signAndGenerateURL(url) + if err != nil { + return nil, err + } req, err := http.NewRequest(method, url, body) if err != nil { return nil, err @@ -40,6 +44,6 @@ func (c *Client) NewRequest(method, url string, body io.Reader) (*http.Request, req.AddCookie(cookie) } req.Header.Set("User-Agent", utils.UA) - req.Header.Set("Referer", "https://www.bilibili.com/") + req.Header.Set("Referer", "https://www.bilibili.com") return req, nil } diff --git a/vendors/bilibili/movie.go b/vendors/bilibili/movie.go index 42fc083..93f9e9a 100644 --- a/vendors/bilibili/movie.go +++ b/vendors/bilibili/movie.go @@ -227,7 +227,7 @@ func (c *Client) GetPGCURL(ep_id, cid uint, conf ...GetVideoURLConfig) (*VideoUR for _, v := range conf { v(config) } - url := fmt.Sprintf("https://api.bilibili.com/pgc/player/web/playurl?ep_id=%d&cid=%d&qn=%d&fourk=1", ep_id, cid, config.Quality) + url := fmt.Sprintf("https://api.bilibili.com/pgc/player/web/playurl?ep_id=%d&cid=%d&qn=%d&fourk=1&fnval=0", ep_id, cid, config.Quality) req, err := c.NewRequest(http.MethodGet, url, nil) if err != nil { return nil, err @@ -248,5 +248,4 @@ func (c *Client) GetPGCURL(ep_id, cid uint, conf ...GetVideoURLConfig) (*VideoUR CurrentQuality: info.Result.Quality, URL: info.Result.Durl[0].URL, }, nil - } diff --git a/vendors/bilibili/wbi.go b/vendors/bilibili/wbi.go new file mode 100644 index 0000000..628ef31 --- /dev/null +++ b/vendors/bilibili/wbi.go @@ -0,0 +1,139 @@ +package bilibili + +import ( + "crypto/md5" + "encoding/hex" + "net/http" + "net/url" + "sort" + "strconv" + "strings" + "sync" + "time" + + json "github.com/json-iterator/go" + "github.com/synctv-org/synctv/utils" +) + +var ( + mixinKeyEncTab = []int{ + 46, 47, 18, 2, 53, 8, 23, 32, 15, 50, 10, 31, 58, 3, 45, 35, 27, 43, 5, 49, + 33, 9, 42, 19, 29, 28, 14, 39, 12, 38, 41, 13, 37, 48, 7, 16, 24, 55, 40, + 61, 26, 17, 0, 1, 60, 51, 30, 4, 22, 25, 54, 21, 56, 59, 6, 63, 57, 62, 11, + 36, 20, 34, 44, 52, + } + lock sync.RWMutex + imgKey, subKey string + lastUpdateTime time.Time +) + +func signAndGenerateURL(urlStr string) (string, error) { + urlObj, err := url.Parse(urlStr) + if err != nil { + return "", err + } + imgKey, subKey, err := getWbiKeysCached() + if err != nil { + return "", err + } + query := urlObj.Query() + params := map[string]string{} + for k, v := range query { + params[k] = v[0] + } + newParams := encWbi(params, imgKey, subKey) + for k, v := range newParams { + query.Set(k, v) + } + urlObj.RawQuery = query.Encode() + return urlObj.String(), nil +} + +func encWbi(params map[string]string, imgKey, subKey string) map[string]string { + mixinKey := getMixinKey(imgKey + subKey) + currTime := strconv.FormatInt(time.Now().Unix(), 10) + params["wts"] = currTime + + keys := make([]string, 0, len(params)) + for k := range params { + keys = append(keys, k) + } + sort.Strings(keys) + + for k, v := range params { + v = sanitizeString(v) + params[k] = v + } + + query := url.Values{} + for _, k := range keys { + query.Set(k, params[k]) + } + queryStr := query.Encode() + + hash := md5.Sum([]byte(queryStr + mixinKey)) + params["w_rid"] = hex.EncodeToString(hash[:]) + return params +} + +func getMixinKey(orig string) string { + var str strings.Builder + for _, v := range mixinKeyEncTab { + if v < len(orig) { + str.WriteByte(orig[v]) + } + } + return str.String()[:32] +} + +func sanitizeString(s string) string { + unwantedChars := []string{"!", "'", "(", ")", "*"} + for _, char := range unwantedChars { + s = strings.ReplaceAll(s, char, "") + } + return s +} + +func getWbiKeysCached() (string, string, error) { + lock.RLock() + if time.Since(lastUpdateTime).Minutes() < 10 { + defer lock.RUnlock() + return imgKey, subKey, nil + } + lock.RUnlock() + lock.Lock() + defer lock.Unlock() + if time.Since(lastUpdateTime).Minutes() < 10 { + return imgKey, subKey, nil + } + var err error + imgKey, subKey, err = getWbiKeys() + if err != nil { + return "", "", err + } + lastUpdateTime = time.Now() + return imgKey, subKey, nil +} + +func getWbiKeys() (string, string, error) { + req, err := http.NewRequest(http.MethodGet, "https://api.bilibili.com/x/web-interface/nav", nil) + if err != nil { + return "", "", err + } + req.Header.Set("User-Agent", utils.UA) + req.Header.Set("Referer", "https://www.bilibili.com") + resp, err := http.DefaultClient.Do(req) + if err != nil { + return "", "", err + } + defer resp.Body.Close() + info := wbi{} + err = json.NewDecoder(resp.Body).Decode(&info) + if err != nil { + return "", "", err + } + + imgKey := strings.Split(strings.Split(info.Data.WbiImg.ImgURL, "/")[len(strings.Split(info.Data.WbiImg.ImgURL, "/"))-1], ".")[0] + subKey := strings.Split(strings.Split(info.Data.WbiImg.SubURL, "/")[len(strings.Split(info.Data.WbiImg.SubURL, "/"))-1], ".")[0] + return imgKey, subKey, nil +}