You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
206 lines
5.9 KiB
206 lines
5.9 KiB
// Package liveurls
|
|
// @Time:2023/02/05 23:34
|
|
// @File:huya.go
|
|
// @SoftWare:Goland
|
|
// @Author:feiyang
|
|
// @Contact:TG@feiyangdigital
|
|
|
|
package liveurls
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/md5"
|
|
"encoding/base64"
|
|
"encoding/hex"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"math/rand"
|
|
"net/http"
|
|
"net/url"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/tidwall/gjson"
|
|
)
|
|
|
|
type Huya struct {
|
|
Rid string
|
|
Media string
|
|
Type string
|
|
Cdn string
|
|
}
|
|
|
|
type Data struct {
|
|
}
|
|
|
|
type Payload struct {
|
|
AppId int `json:"appId"`
|
|
ByPass int `json:"byPass"`
|
|
Context string `json:"context"`
|
|
Version string `json:"version"`
|
|
Data Data `json:"data"`
|
|
}
|
|
|
|
type ResponseData struct {
|
|
Data struct {
|
|
Uid string `json:"uid"`
|
|
} `json:"data"`
|
|
}
|
|
|
|
func getContent(apiUrl string) ([]byte, error) {
|
|
payload := Payload{
|
|
AppId: 5002,
|
|
ByPass: 3,
|
|
Context: "",
|
|
Version: "2.4",
|
|
Data: Data{},
|
|
}
|
|
jsonPayload, err := json.Marshal(payload)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
req, err := http.NewRequest("POST", apiUrl, bytes.NewBuffer(jsonPayload))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
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 err != nil {
|
|
return nil, err
|
|
}
|
|
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 {
|
|
TimeLocation, err := time.LoadLocation("Asia/Shanghai")
|
|
if err != nil {
|
|
TimeLocation = time.FixedZone("CST", 8*60*60)
|
|
}
|
|
now := time.Now().In(TimeLocation)
|
|
q, _ := url.ParseQuery(antiCode)
|
|
q.Set("t", "102")
|
|
q.Set("ctype", "tars_mp")
|
|
q.Set("wsTime", strconv.FormatInt(time.Now().Unix()+21600, 16))
|
|
q.Set("ver", "1")
|
|
q.Set("sv", now.Format("2006010215"))
|
|
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")
|
|
}
|
|
return q.Encode()
|
|
}
|
|
|
|
func format(jsonStr string, 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"}
|
|
ojsonStr := gjson.Get(jsonStr, "roomInfo.tLiveInfo.tLiveStreamInfo.vStreamInfo").String()
|
|
fmt.Println(gjson.Get(ojsonStr, "value"))
|
|
qreg := regexp.MustCompile(`(?i){"_proto"[\s\S]*?"value":([\s\S]*),"_classname"`)
|
|
qres := qreg.FindStringSubmatch(ojsonStr)
|
|
gjson.Parse(qres[1]).ForEach(func(_, value gjson.Result) bool {
|
|
sFlvUrl := value.Get("sFlvUrl").String()
|
|
sFlvUrlSuffix := value.Get("sFlvUrlSuffix").String()
|
|
sHlsUrl := value.Get("sHlsUrl").String()
|
|
sHlsUrlSuffix := value.Get("sHlsUrlSuffix").String()
|
|
sStreamName := value.Get("sStreamName").String()
|
|
sCdnType := value.Get("sCdnType").String()
|
|
sFlvAntiCode := value.Get("sFlvAntiCode").String()
|
|
sHlsAntiCode := value.Get("sHlsAntiCode").String()
|
|
if sFlvUrl != "" {
|
|
streamInfo["flv"].(map[string]string)[cdnType[sCdnType]] = sFlvUrl + "/" + sStreamName + "." + sFlvUrlSuffix + "?" + processAntiCode(sFlvAntiCode, uid, sStreamName)
|
|
}
|
|
if sHlsUrl != "" {
|
|
streamInfo["hls"].(map[string]string)[cdnType[sCdnType]] = sHlsUrl + "/" + sStreamName + "." + sHlsUrlSuffix + "?" + processAntiCode(sHlsAntiCode, uid, sStreamName)
|
|
}
|
|
return true
|
|
})
|
|
return streamInfo
|
|
}
|
|
|
|
func (h *Huya) GetLiveUrl() 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)<script>[\s\S]window.HNF_GLOBAL_INIT = ([\s\S]*?) </script>`)
|
|
res := freg.FindStringSubmatch(str)
|
|
if len(res) > 1 {
|
|
jsonStr := res[1]
|
|
liveStatus := gjson.Get(jsonStr, "roomInfo.eLiveStatus").Int()
|
|
var mediaurl any
|
|
|
|
if liveStatus == 2 {
|
|
realurl := format(jsonStr, uid)
|
|
if h.Type == "display" {
|
|
return realurl
|
|
}
|
|
for k, v := range realurl {
|
|
if k == h.Media {
|
|
if urlarr, ok := v.(map[string]string); ok {
|
|
for k, v := range urlarr {
|
|
if k == h.Cdn {
|
|
mediaurl = strings.Replace(v, "http://", "https://", 1)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else if liveStatus == 3 {
|
|
liveLineUrl := gjson.Get(jsonStr, "roomProfile.liveLineUrl").String()
|
|
if liveLineUrl != "" {
|
|
decodedLiveLineUrl, _ := base64.StdEncoding.DecodeString(liveLineUrl)
|
|
mediaurl = "https:" + string(decodedLiveLineUrl)
|
|
}
|
|
} else {
|
|
mediaurl = nil
|
|
}
|
|
return mediaurl
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|