diff --git a/Golang/liveurls/huya.go b/Golang/liveurls/huya.go index fb196a6..95345d3 100644 --- a/Golang/liveurls/huya.go +++ b/Golang/liveurls/huya.go @@ -8,180 +8,99 @@ package liveurls import ( - "bytes" "crypto/md5" "encoding/base64" "encoding/hex" - "encoding/json" "fmt" - "io" + "github.com/hr3lxphr6j/requests" + uuid "github.com/satori/go.uuid" + "github.com/tidwall/gjson" "math/rand" "net/http" "net/url" - "regexp" "strconv" "strings" "time" ) -type Huya struct { - Rid string - Media string - Type string - Cdn string -} - -type Data struct { -} +const userAgent = "Mozilla/5.0 (iPhone; CPU iPhone OS 16_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.6 Mobile/15E148 Safari/604.1" -type Payload struct { - AppId int `json:"appId"` - ByPass int `json:"byPass"` - Context string `json:"context"` - Version string `json:"version"` - Data Data `json:"data"` +type Huya struct { + Rid string + Cdn string } -type ResponseData struct { - Data struct { - Uid string `json:"uid"` - } `json:"data"` +func GetMD5Hash(text string) string { + hash := md5.Sum([]byte(text)) + return hex.EncodeToString(hash[:]) } -func getContent(apiUrl string) ([]byte, error) { - payload := Payload{ - AppId: 5002, - ByPass: 3, - Context: "", - Version: "2.4", - Data: Data{}, - } - jsonPayload, err := json.Marshal(payload) +func parseAntiCode(anticode string, uid int64, streamName string) (string, error) { + qr, err := url.ParseQuery(anticode) if err != nil { - return nil, err + return "", err } + qr.Set("ver", "1") + qr.Set("sv", "2110211124") + qr.Set("seqid", strconv.FormatInt(time.Now().Unix()*1000+uid, 10)) + qr.Set("uid", strconv.FormatInt(uid, 10)) + qr.Set("uuid", uuid.NewV4().String()) + ss := GetMD5Hash(fmt.Sprintf("%s|%s|%s", qr.Get("seqid"), qr.Get("ctype"), qr.Get("t"))) + wsTime := strconv.FormatInt(time.Now().Add(6*time.Hour).Unix(), 16) + + decodeString, _ := base64.StdEncoding.DecodeString(qr.Get("fm")) + fm := string(decodeString) + fm = strings.ReplaceAll(fm, "$0", qr.Get("uid")) + fm = strings.ReplaceAll(fm, "$1", streamName) + fm = strings.ReplaceAll(fm, "$2", ss) + fm = strings.ReplaceAll(fm, "$3", wsTime) + + qr.Set("wsSecret", GetMD5Hash(fm)) + qr.Set("ratio", "0") + qr.Set("wsTime", wsTime) + return qr.Encode(), nil +} - req, err := http.NewRequest("POST", apiUrl, bytes.NewBuffer(jsonPayload)) +func (h *Huya) GetLiveUrl() any { + mobileUrl := "https://m.huya.com/" + h.Rid + resp, err := requests.Get(mobileUrl, requests.UserAgent(userAgent)) if err != nil { - return nil, err + return nil } - - req.Header.Set("Content-Type", "application/json") - req.Header.Set("Content-Length", fmt.Sprintf("%d", len(jsonPayload))) - req.Header.Set("upgrade-insecure-requests", "1") - req.Header.Set("user-agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36") - - client := &http.Client{} - resp, err := client.Do(req) + if resp.StatusCode != http.StatusOK { + return nil + } + body, err := resp.Text() if err != nil { - return nil, err + return nil } - defer resp.Body.Close() - - return io.ReadAll(resp.Body) -} - -func getUid() string { - content, _ := getContent("https://udblgn.huya.com/web/anonymousLogin") - var responseData ResponseData - json.Unmarshal(content, &responseData) - uid := responseData.Data.Uid - return uid -} - -var uid, _ = strconv.Atoi(getUid()) - -func getUUID() int64 { - now := time.Now().UnixNano() / int64(time.Millisecond) - randNum := rand.Intn(1000) - return ((now % 10000000000 * 1000) + int64(randNum)) % 4294967295 -} - -func processAntiCode(antiCode string, uid int, streamName string) string { - q, _ := url.ParseQuery(antiCode) - q.Set("ver", "1") - q.Set("sv", "2110211124") - seqId := strconv.Itoa(uid + int(time.Now().UnixNano()/int64(time.Millisecond))) - q.Set("seqid", seqId) - q.Set("uid", strconv.Itoa(uid)) - q.Set("uuid", strconv.FormatInt(getUUID(), 10)) - h := md5.New() - h.Write([]byte(seqId + "|" + q.Get("ctype") + "|" + q.Get("t"))) - ss := hex.EncodeToString(h.Sum(nil)) - fm, _ := base64.StdEncoding.DecodeString(q.Get("fm")) - q.Set("fm", strings.Replace(strings.Replace(strings.Replace(strings.Replace(string(fm), "$0", q.Get("uid"), -1), "$1", streamName, -1), "$2", ss, -1), "$3", q.Get("wsTime"), -1)) - h.Reset() - h.Write([]byte(q.Get("fm"))) - q.Set("wsSecret", hex.EncodeToString(h.Sum(nil))) - q.Del("fm") - if _, ok := q["txyp"]; ok { - q.Del("txyp") + tmpStrings := strings.Split(body, `"tLiveInfo":`) + if len(tmpStrings) < 2 { + return nil } - return q.Encode() -} - -func format(liveArr map[string]any, uid int) map[string]any { - streamInfo := map[string]any{"flv": make(map[string]string), "hls": make(map[string]string)} - cdnType := map[string]string{"HY": "hycdn", "AL": "alicdn", "TX": "txcdn", "HW": "hwcdn", "HS": "hscdn", "WS": "wscdn"} - for _, s := range liveArr["roomInfo"].(map[string]any)["tLiveInfo"].(map[string]any)["tLiveStreamInfo"].(map[string]any)["vStreamInfo"].(map[string]any)["value"].([]any) { - sMap := s.(map[string]any) - if sMap["sFlvUrl"] != nil { - streamInfo["flv"].(map[string]string)[cdnType[sMap["sCdnType"].(string)]] = sMap["sFlvUrl"].(string) + "/" + sMap["sStreamName"].(string) + "." + sMap["sFlvUrlSuffix"].(string) + "?" + processAntiCode(sMap["sFlvAntiCode"].(string), uid, sMap["sStreamName"].(string)) - } - if sMap["sHlsUrl"] != nil { - streamInfo["hls"].(map[string]string)[cdnType[sMap["sCdnType"].(string)]] = sMap["sHlsUrl"].(string) + "/" + sMap["sStreamName"].(string) + "." + sMap["sHlsUrlSuffix"].(string) + "?" + processAntiCode(sMap["sHlsAntiCode"].(string), uid, sMap["sStreamName"].(string)) - } + liveInfoJsonRawString := strings.Split(tmpStrings[1], `,"_classname":"LiveRoom.LiveInfo"}`)[0] + "}" + if !gjson.Valid(liveInfoJsonRawString) { + return nil } - return streamInfo -} + liveInfoJson := gjson.Parse(liveInfoJsonRawString) -func (h *Huya) GetLiveUrl() any { - var liveArr map[string]any - liveurl := "https://m.huya.com/" + h.Rid - client := &http.Client{} - r, _ := http.NewRequest("GET", liveurl, nil) - r.Header.Add("user-agent", "Mozilla/5.0 (iPhone; CPU iPhone OS 16_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.3 Mobile/15E148 Safari/604.1") - r.Header.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8") - resp, _ := client.Do(r) - defer resp.Body.Close() - body, _ := io.ReadAll(resp.Body) - str := string(body) - freg := regexp.MustCompile(`(?i)`) - res := freg.FindStringSubmatch(str) + streamInfoJsons := liveInfoJson.Get("tLiveStreamInfo.vStreamInfo.value") - json.Unmarshal([]byte(res[1]), &liveArr) - var mediaurl any - if roomInfo, ok := liveArr["roomInfo"].(map[string]any); ok { - if liveStatus, ok := roomInfo["eLiveStatus"].(float64); ok && liveStatus == 2 { - realurl := format(liveArr, uid) - if h.Type == "display" { - return realurl - } - for k, v := range realurl { - switch k { - case h.Media: - if urlarr, ok := v.(map[string]string); ok { - for k, v := range urlarr { - switch k { - case h.Cdn: - mediaurl = v - } - } - } - } - } + var finalurl string + streamInfoJsons.ForEach(func(key, value gjson.Result) bool { + if gjson.Get(value.String(), "sCdnType").String() == h.Cdn { + + sStreamName := gjson.Get(value.String(), "sStreamName").String() + sFlvAntiCode := gjson.Get(value.String(), "sFlvAntiCode").String() + sFlvUrl := gjson.Get(value.String(), "sFlvUrl").String() + uid := rand.Int63n(99999999999) + 1400000000000 + + query, _ := parseAntiCode(sFlvAntiCode, uid, sStreamName) + finalurl = fmt.Sprintf("%s/%s.flv?%s", sFlvUrl, sStreamName, query) - } else if liveStatus, ok := roomInfo["eLiveStatus"].(float64); ok && liveStatus == 3 { - if roomProfile, ok := liveArr["roomProfile"].(map[string]any); ok { - if liveLineUrl, ok := roomProfile["liveLineUrl"].(string); ok { - decodedLiveLineUrl, _ := base64.StdEncoding.DecodeString(liveLineUrl) - mediaurl = "http:" + string(decodedLiveLineUrl) - } - } - } else { - mediaurl = nil } - } - return mediaurl + return true + }) + return finalurl } diff --git a/Golang/main.go b/Golang/main.go index 87bcc30..66321a0 100644 --- a/Golang/main.go +++ b/Golang/main.go @@ -152,14 +152,8 @@ func setupRouter(adurl string) *gin.Engine { case "huya": huyaobj := &liveurls.Huya{} huyaobj.Rid = rid - huyaobj.Cdn = c.DefaultQuery("cdn", "hwcdn") - huyaobj.Media = c.DefaultQuery("media", "flv") - huyaobj.Type = c.DefaultQuery("type", "nodisplay") - if huyaobj.Type == "display" { - c.JSON(200, huyaobj.GetLiveUrl()) - } else { - c.Redirect(http.StatusMovedPermanently, duanyan(adurl, huyaobj.GetLiveUrl())) - } + huyaobj.Cdn = c.DefaultQuery("cdn", "HW") + c.Redirect(http.StatusMovedPermanently, duanyan(adurl, huyaobj.GetLiveUrl())) case "bilibili": biliobj := &liveurls.BiliBili{} biliobj.Rid = rid