├── package.json
├── .github
└── asserts
│ ├── env.png
│ ├── nodejs.png
│ ├── region.png
│ └── envDemo.png
├── README.md
├── vercel.json
├── go.mod
├── LICENSE
├── list
├── yylunbo.go
├── douyuyqk.go
├── huyayqk.go
└── tvm3u.go
├── utils
├── jsRun.go
└── http.go
├── liveurls
├── douyin.go
├── yy.go
├── youtube.go
├── bilibili.go
├── douyu.go
├── ysptp.go
├── huya.go
└── itv.go
└── api
├── yqk
└── yqk.go
├── live.go
└── index.go
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "engines": {
3 | "node": "18.x"
4 | }
5 | }
--------------------------------------------------------------------------------
/.github/asserts/env.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/papagaye744/iptv-go/HEAD/.github/asserts/env.png
--------------------------------------------------------------------------------
/.github/asserts/nodejs.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/papagaye744/iptv-go/HEAD/.github/asserts/nodejs.png
--------------------------------------------------------------------------------
/.github/asserts/region.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/papagaye744/iptv-go/HEAD/.github/asserts/region.png
--------------------------------------------------------------------------------
/.github/asserts/envDemo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/papagaye744/iptv-go/HEAD/.github/asserts/envDemo.png
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 源仓库已闭源
2 |
3 | 建议自己部署docker的allinone使用,[https://hub.docker.com/r/youshandefeiyang/allinone](https://hub.docker.com/r/youshandefeiyang/allinone)
4 |
--------------------------------------------------------------------------------
/vercel.json:
--------------------------------------------------------------------------------
1 | {
2 | "routes": [
3 | {
4 | "src": "/live/.*",
5 | "dest": "/api/live.go"
6 | },
7 | {
8 | "src": "/yqk/.*",
9 | "dest": "/api/yqk/yqk.go"
10 | },
11 | {
12 | "src": "/favicon.ico",
13 | "dest": "https://assets.vercel.com/image/upload/front/favicon/vercel/favicon.ico"
14 | },
15 | {
16 | "src": "/.*",
17 | "dest": "/api/index.go"
18 | }
19 | ]
20 | }
21 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module Golang
2 |
3 | go 1.19
4 |
5 | require (
6 | github.com/dop251/goja v0.0.0-20230203172422-5460598cfa32
7 | github.com/etherlabsio/go-m3u8 v1.0.0
8 | github.com/forgoer/openssl v1.5.0
9 | github.com/gin-gonic/gin v1.8.2
10 | github.com/hr3lxphr6j/bililive-go v0.7.23
11 | github.com/hr3lxphr6j/requests v0.0.2
12 | github.com/satori/go.uuid v1.2.1-0.20181028125025-b2ce2384e17b
13 | github.com/tidwall/gjson v1.14.4
14 | )
15 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 papagaye744
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/list/yylunbo.go:
--------------------------------------------------------------------------------
1 | // Package list
2 | // @Time:2023/06/03 20:35
3 | // @File:yylunbo.go
4 | // @SoftWare:Goland
5 | // @Author:feiyang
6 | // @Contact:TG@feiyangdigital
7 |
8 | package list
9 |
10 | import (
11 | "io"
12 | "net/http"
13 | )
14 |
15 | type Yylist struct {
16 | }
17 |
18 | type DataElement struct {
19 | Avatar string `json:"avatar"`
20 | Biz string `json:"biz"`
21 | Desc string `json:"desc"`
22 | Sid int `json:"sid"`
23 | }
24 |
25 | type ApiResponse struct {
26 | Data struct {
27 | IsLastPage int `json:"isLastPage"`
28 | Data []DataElement `json:"data"`
29 | } `json:"data"`
30 | }
31 |
32 | func (y *Yylist) Yylb(requesturl string) string {
33 | client := &http.Client{}
34 | req, _ := http.NewRequest("GET", requesturl, nil)
35 | req.Header.Set("Upgrade-Insecure-Requests", "1")
36 | 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")
37 | res, err := client.Do(req)
38 | if err != nil {
39 | panic(err)
40 | }
41 | defer res.Body.Close()
42 | body, _ := io.ReadAll(res.Body)
43 | return string(body)
44 | }
--------------------------------------------------------------------------------
/utils/jsRun.go:
--------------------------------------------------------------------------------
1 | // Package utils
2 | // @Time:2023/08/24 06:36
3 | // @File:jsRun.go
4 | // @SoftWare:Goland
5 | // @Author:feiyang
6 | // @Contact:TG@feiyangdigital
7 |
8 | package utils
9 |
10 | import (
11 | "fmt"
12 | js "github.com/dop251/goja"
13 | "sync"
14 | )
15 |
16 | type JsUtil struct {
17 | pool sync.Pool
18 | }
19 |
20 | func (j *JsUtil) getVm() *js.Runtime {
21 | v := j.pool.Get()
22 | if v != nil {
23 | return v.(*js.Runtime)
24 | }
25 | return js.New()
26 | }
27 |
28 | func (j *JsUtil) putVm(vm *js.Runtime) {
29 | vm.Set("global", nil) // 清除全局对象
30 | j.pool.Put(vm)
31 | }
32 |
33 | func (j *JsUtil) JsRun(funcContent []string, params ...any) any {
34 | vm := j.getVm()
35 | defer j.putVm(vm)
36 | _, err := vm.RunString(funcContent[0])
37 | if err != nil {
38 | return err
39 | }
40 | jsfn, ok := js.AssertFunction(vm.Get(funcContent[1]))
41 | if !ok {
42 | return fmt.Errorf("执行函数失败")
43 | }
44 | jsValues := make([]js.Value, 0, len(params))
45 | for _, v := range params {
46 | jsValues = append(jsValues, vm.ToValue(v))
47 | }
48 | result, err := jsfn(
49 | js.Undefined(),
50 | jsValues...,
51 | )
52 | if err != nil {
53 | return err
54 | }
55 | return result
56 | }
--------------------------------------------------------------------------------
/list/douyuyqk.go:
--------------------------------------------------------------------------------
1 | // Package list
2 | // @Time:2023/06/02 10:00
3 | // @File:mian.go
4 | // @SoftWare:Goland
5 | // @Author:feiyang
6 | // @Contact:TG@feiyangdigital
7 |
8 | package list
9 |
10 | import (
11 | "io"
12 | "net/http"
13 | )
14 |
15 | type DouYuYqk struct {
16 | }
17 |
18 | type DouYuResponse struct {
19 | Data struct {
20 | Pgcnt int `json:"pgcnt"`
21 | Rl []struct {
22 | Av string `json:"av"`
23 | C2name string `json:"c2name"`
24 | Nn string `json:"nn"`
25 | Rid int `json:"rid"`
26 | } `json:"rl"`
27 | } `json:"data"`
28 | }
29 |
30 | func (dy *DouYuYqk) Douyuyqk(requestURL string) ([]byte, error) {
31 | client := &http.Client{}
32 |
33 | req, err := http.NewRequest("GET", requestURL, nil)
34 | if err != nil {
35 | return nil, err
36 | }
37 |
38 | req.Header.Set("upgrade-insecure-requests", "1")
39 | 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")
40 |
41 | resp, err := client.Do(req)
42 | if err != nil {
43 | return nil, err
44 | }
45 | defer resp.Body.Close()
46 |
47 | return io.ReadAll(resp.Body)
48 | }
--------------------------------------------------------------------------------
/list/huyayqk.go:
--------------------------------------------------------------------------------
1 | // Package list
2 | // @Time:2023/06/02 10:00
3 | // @File:mian.go
4 | // @SoftWare:Goland
5 | // @Author:feiyang
6 | // @Contact:TG@feiyangdigital
7 |
8 | package list
9 |
10 | import (
11 | "io"
12 | "net/http"
13 | )
14 |
15 | type HuyaYqk struct {
16 | }
17 |
18 | type YaResponse struct {
19 | ITotalPage int `json:"iTotalPage"`
20 | IPageSize int `json:"iPageSize"`
21 | VList []struct {
22 | SAvatar180 string `json:"sAvatar180"`
23 | SGameFullName string `json:"sGameFullName"`
24 | SNick string `json:"sNick"`
25 | LProfileRoom int `json:"lProfileRoom"`
26 | } `json:"vList"`
27 | }
28 |
29 | func (hy *HuyaYqk) HuYaYqk(requestURL string) ([]byte, error) {
30 | client := &http.Client{}
31 | req, err := http.NewRequest("GET", requestURL, nil)
32 | if err != nil {
33 | return nil, err
34 | }
35 | req.Header.Set("upgrade-insecure-requests", "1")
36 | 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")
37 | resp, err := client.Do(req)
38 | if err != nil {
39 | return nil, err
40 | }
41 | defer resp.Body.Close()
42 | return io.ReadAll(resp.Body)
43 | }
--------------------------------------------------------------------------------
/utils/http.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "os"
5 | "fmt"
6 | "time"
7 | "net/http"
8 | "net/url"
9 | // "log"
10 | )
11 |
12 | func GetTestVideoUrl(w http.ResponseWriter) {
13 | TimeLocation, err := time.LoadLocation("Asia/Shanghai")
14 | if err != nil {
15 | TimeLocation = time.FixedZone("CST", 8*60*60)
16 | }
17 | str_time := time.Now().In(TimeLocation).Format("2006-01-02 15:04:05")
18 | fmt.Fprintln(w, "#EXTM3U")
19 | fmt.Fprintln(w, "#EXTINF:-1 tvg-name=\""+str_time+"\" tvg-logo=\"https://cdn.jsdelivr.net/gh/feiyangdigital/testvideo/tg.jpg\" group-title=\"列表更新时间\","+str_time)
20 | fmt.Fprintln(w, "https://cdn.jsdelivr.net/gh/feiyangdigital/testvideo/time/time.mp4")
21 | fmt.Fprintln(w, "#EXTINF:-1 tvg-name=\"4K60PSDR-H264-AAC测试\" tvg-logo=\"https://cdn.jsdelivr.net/gh/feiyangdigital/testvideo/tg.jpg\" group-title=\"4K频道\",4K60PSDR-H264-AAC测试")
22 | fmt.Fprintln(w, "https://cdn.jsdelivr.net/gh/feiyangdigital/testvideo/sdr4kvideo/index.m3u8")
23 | fmt.Fprintln(w, "#EXTINF:-1 tvg-name=\"4K60PHLG-HEVC-EAC3测试\" tvg-logo=\"https://cdn.jsdelivr.net/gh/feiyangdigital/testvideo/tg.jpg\" group-title=\"4K频道\",4K60PHLG-HEVC-EAC3测试")
24 | fmt.Fprintln(w, "https://cdn.jsdelivr.net/gh/feiyangdigital/testvideo/hlg4kvideo/index.m3u8")
25 | }
26 |
27 | func GetLivePrefix(r *http.Request) string {
28 | // 尝试从环境变量读取url
29 | envUrl := os.Getenv("LIVE_URL")
30 | // log.Println("env url:", envUrl)
31 | if envUrl == "" {
32 | // 默认url
33 | envUrl = "https://www.goodiptv.club"
34 | }
35 | firstUrl := DefaultQuery(r, "url", envUrl)
36 | realUrl, _ := url.QueryUnescape(firstUrl)
37 | return realUrl
38 | }
39 |
40 | func DefaultQuery(r *http.Request, name string, defaultValue string) string {
41 | param := r.URL.Query().Get(name)
42 | if param == "" {
43 | return defaultValue
44 | }
45 | return param
46 | }
47 |
48 | func Duanyan(adurl string, realurl any) string {
49 | var liveurl string
50 | if str, ok := realurl.(string); ok {
51 | liveurl = str
52 | } else {
53 | liveurl = adurl
54 | }
55 | // log.Println("Redirect url:", liveurl)
56 | return liveurl
57 | }
--------------------------------------------------------------------------------
/liveurls/douyin.go:
--------------------------------------------------------------------------------
1 | // Package liveurls
2 | // @Time:2023/02/03 01:59
3 | // @File:douyin.go
4 | // @SoftWare:Goland
5 | // @Author:feiyang
6 | // @Contact:TG@feiyangdigital
7 |
8 | package liveurls
9 |
10 | import (
11 | "fmt"
12 | "github.com/tidwall/gjson"
13 | "io"
14 | "net/http"
15 | "regexp"
16 | "strconv"
17 | )
18 |
19 | type Douyin struct {
20 | Stream string
21 | Rid string
22 | }
23 |
24 | func (d *Douyin) GetDouYinUrl() any {
25 | liveurl := "https://live.douyin.com/" + d.Rid
26 | client := &http.Client{}
27 | re, _ := http.NewRequest("GET", liveurl, nil)
28 | re.Header.Add("user-agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36")
29 | re.Header.Add("upgrade-insecure-requests", "1")
30 | oresp, _ := client.Do(re)
31 | defer oresp.Body.Close()
32 | oreg := regexp.MustCompile(`(?i)__ac_nonce=(.*?);`)
33 | ores := oreg.FindStringSubmatch(oresp.Header["Set-Cookie"][0])
34 | r, _ := http.NewRequest("GET", liveurl, nil)
35 | cookie := &http.Cookie{Name: "__ac_nonce", Value: ores[1]}
36 | r.AddCookie(cookie)
37 | r.Header.Add("user-agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36")
38 | r.Header.Add("upgrade-insecure-requests", "1")
39 | resp, _ := client.Do(r)
40 | defer resp.Body.Close()
41 | reg := regexp.MustCompile(`(?i)ttwid=.*?;`)
42 | res := reg.FindStringSubmatch(fmt.Sprintf("%s", resp.Cookies()))[0]
43 | url := "https://live.douyin.com/webcast/room/web/enter/?aid=6383&app_name=douyin_web&live_id=1&device_platform=web&language=zh-CN&enter_from=web_live&cookie_enabled=true&screen_width=1728&screen_height=1117&browser_language=zh-CN&browser_platform=MacIntel&browser_name=Chrome&browser_version=116.0.0.0&web_rid=" + d.Rid
44 | req, _ := http.NewRequest("GET", url, nil)
45 | req.Header.Add("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36")
46 | req.Header.Add("Cookie", res)
47 | req.Header.Add("Accept", "*/*")
48 | req.Header.Add("Host", "live.douyin.com")
49 | req.Header.Add("Connection", "keep-alive")
50 | ress, _ := client.Do(req)
51 | defer ress.Body.Close()
52 | body, _ := io.ReadAll(ress.Body)
53 | var json = string(body)
54 | status, _ := strconv.Atoi(fmt.Sprintf("%s", gjson.Get(json, "data.data.0.status")))
55 | if status != 2 {
56 | return nil
57 | }
58 | var realurl string
59 | value := gjson.Get(json, "data.data.0.stream_url.live_core_sdk_data.pull_data.stream_data")
60 | value.ForEach(func(key, value gjson.Result) bool {
61 | if gjson.Get(value.String(), "data.origin").Exists() {
62 | switch d.Stream {
63 | case "flv":
64 | realurl = fmt.Sprintf("%s", gjson.Get(value.String(), "data.origin.main.flv"))
65 | case "hls":
66 | realurl = fmt.Sprintf("%s", gjson.Get(value.String(), "data.origin.main.hls"))
67 | }
68 | }
69 | return true
70 | })
71 | return realurl
72 | }
73 |
--------------------------------------------------------------------------------
/liveurls/yy.go:
--------------------------------------------------------------------------------
1 | // Package liveurls
2 | // @Time:2023/06/03 05:40
3 | // @File:yy.go
4 | // @SoftWare:Goland
5 | // @Author:feiyang
6 | // @Contact:TG@feiyangdigital
7 |
8 | package liveurls
9 |
10 | import (
11 | "bytes"
12 | "encoding/json"
13 | "fmt"
14 | "io"
15 | "net/http"
16 | "regexp"
17 | "strconv"
18 | "time"
19 | )
20 |
21 | type Yy struct {
22 | Rid string
23 | Quality string
24 | }
25 |
26 | type StreamLineAddr struct {
27 | CdnInfo struct {
28 | Url string `json:"url"`
29 | } `json:"cdn_info"`
30 | }
31 |
32 | type Result struct {
33 | AvpInfoRes struct {
34 | StreamLineAddr map[string]StreamLineAddr `json:"stream_line_addr"`
35 | } `json:"avp_info_res"`
36 | }
37 |
38 | func (y *Yy) GetLiveUrl() any {
39 | firstrid := y.Rid
40 | quality := y.Quality
41 | var rid string
42 | checkUrl := "https://wap.yy.com/mobileweb/" + firstrid
43 | client := &http.Client{}
44 | req, _ := http.NewRequest("GET", checkUrl, nil)
45 | req.Header.Set("Referer", "https://wap.yy.com")
46 | req.Header.Set("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")
47 | res, _ := client.Do(req)
48 | defer res.Body.Close()
49 | body, _ := io.ReadAll(res.Body)
50 | re := regexp.MustCompile(`md5Hash[\s\S]*?sid.*'(.*)'.*?getQuery`)
51 | realdata := re.FindStringSubmatch(string(body))
52 |
53 | if len(realdata) > 0 {
54 | rid = realdata[1]
55 | } else {
56 | return nil
57 | }
58 | millis_13 := time.Now().UnixNano() / int64(time.Millisecond)
59 | millis_10 := time.Now().Unix()
60 | data := fmt.Sprintf(`{"head":{"seq":%d,"appidstr":"0","bidstr":"0","cidstr":"%s","sidstr":"%s","uid64":0,"client_type":108,"client_ver":"5.14.13","stream_sys_ver":1,"app":"yylive_web","playersdk_ver":"5.14.13","thundersdk_ver":"0","streamsdk_ver":"5.14.13"},"client_attribute":{"client":"web","model":"","cpu":"","graphics_card":"","os":"chrome","osversion":"118.0.0.0","vsdk_version":"","app_identify":"","app_version":"","business":"","width":"1728","height":"1117","scale":"","client_type":8,"h265":0},"avp_parameter":{"version":1,"client_type":8,"service_type":0,"imsi":0,"send_time":%d,"line_seq":-1,"gear":%s,"ssl":1,"stream_format":0}}`, millis_13, rid, rid, millis_10, quality)
61 | url := "https://stream-manager.yy.com/v3/channel/streams?uid=0&cid=" + rid + "&sid=" + rid + "&appid=0&sequence=" + strconv.FormatInt(millis_13, 10) + "&encode=json"
62 | req, _ = http.NewRequest("POST", url, bytes.NewBuffer([]byte(data)))
63 | req.Header.Set("Content-Type", "text/plain;charset=UTF-8")
64 | req.Header.Set("Referer", "https://www.yy.com/")
65 | req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36 Edg/106.0.1370.42")
66 |
67 | res, _ = client.Do(req)
68 | defer res.Body.Close()
69 | body, _ = io.ReadAll(res.Body)
70 | var result Result
71 | json.Unmarshal(body, &result)
72 | if len(result.AvpInfoRes.StreamLineAddr) > 0 {
73 | var arr []string
74 | for k := range result.AvpInfoRes.StreamLineAddr {
75 | arr = append(arr, k)
76 | }
77 | return result.AvpInfoRes.StreamLineAddr[arr[0]].CdnInfo.Url
78 | } else {
79 | return nil
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/api/yqk/yqk.go:
--------------------------------------------------------------------------------
1 | package yqk
2 |
3 | import (
4 | "Golang/list"
5 | "Golang/utils"
6 | "fmt"
7 | "encoding/json"
8 | "net/http"
9 | "strconv"
10 | "log"
11 | )
12 |
13 | // vercel 平台会将请求传递给该函数,这个函数名随意,但函数参数必须按照该规则。
14 | // go语言大写就是公开,所以首字母必须大写
15 | func Handler(w http.ResponseWriter, r *http.Request) {
16 | path := r.URL.Path
17 | switch path {
18 | case "/yqk/huyayqk.m3u":
19 | yaobj := &list.HuyaYqk{}
20 | res, _ := yaobj.HuYaYqk("https://live.cdn.huya.com/liveHttpUI/getLiveList?iGid=2135")
21 | var result list.YaResponse
22 | json.Unmarshal(res, &result)
23 | pageCount := result.ITotalPage
24 | pageSize := result.IPageSize
25 | w.Header().Set("Content-Type", "application/octet-stream")
26 | w.Header().Set("Content-Disposition", "attachment; filename=huyayqk.m3u")
27 | utils.GetTestVideoUrl(w)
28 |
29 | for i := 1; i <= pageCount; i++ {
30 | apiRes, _ := yaobj.HuYaYqk(fmt.Sprintf("https://live.cdn.huya.com/liveHttpUI/getLiveList?iGid=2135&iPageNo=%d&iPageSize=%d", i, pageSize))
31 | var res list.YaResponse
32 | json.Unmarshal(apiRes, &res)
33 | data := res.VList
34 | for _, value := range data {
35 | fmt.Fprintf(w, "#EXTINF:-1 tvg-logo=\"%s\" group-title=\"%s\", %s\n", value.SAvatar180, value.SGameFullName, value.SNick)
36 | fmt.Fprintf(w, "%s/huya/%v\n", utils.GetLivePrefix(r), value.LProfileRoom)
37 | }
38 | }
39 | case "/yqk/douyuyqk.m3u":
40 | yuobj := &list.DouYuYqk{}
41 | resAPI, _ := yuobj.Douyuyqk("https://www.douyu.com/gapi/rkc/directory/mixList/2_208/list")
42 |
43 | var result list.DouYuResponse
44 | json.Unmarshal(resAPI, &result)
45 | pageCount := result.Data.Pgcnt
46 |
47 | w.Header().Set("Content-Type", "application/octet-stream")
48 | w.Header().Set("Content-Disposition", "attachment; filename=douyuyqk.m3u")
49 | utils.GetTestVideoUrl(w)
50 |
51 | for i := 1; i <= pageCount; i++ {
52 | apiRes, _ := yuobj.Douyuyqk("https://www.douyu.com/gapi/rkc/directory/mixList/2_208/" + strconv.Itoa(i))
53 |
54 | var res list.DouYuResponse
55 | json.Unmarshal(apiRes, &res)
56 | data := res.Data.Rl
57 |
58 | for _, value := range data {
59 | fmt.Fprintf(w, "#EXTINF:-1 tvg-logo=\"https://apic.douyucdn.cn/upload/%s_big.jpg\" group-title=\"%s\", %s\n", value.Av, value.C2name, value.Nn)
60 | fmt.Fprintf(w, "%s/douyu/%v\n", utils.GetLivePrefix(r), value.Rid)
61 | }
62 | }
63 | case "/yqk/yylunbo.m3u":
64 | yylistobj := &list.Yylist{}
65 | w.Header().Set("Content-Type", "application/octet-stream")
66 | w.Header().Set("Content-Disposition", "attachment; filename=yylunbo.m3u")
67 | utils.GetTestVideoUrl(w)
68 |
69 | i := 1
70 | for {
71 | apiRes := yylistobj.Yylb(fmt.Sprintf("http://rubiks-ipad.yy.com/nav/other/idx/213?channel=appstore&ispType=0&model=iPad8,6&netType=2&os=iOS&osVersion=17.2&page=%d&uid=0&yyVersion=6.17.0", i))
72 | var res list.ApiResponse
73 | json.Unmarshal([]byte(apiRes), &res)
74 | for _, value := range res.Data.Data {
75 | fmt.Fprintf(w, "#EXTINF:-1 tvg-logo=\"%s\" group-title=\"%s\", %s\n", value.Avatar, value.Biz, value.Desc)
76 | fmt.Fprintf(w, "%s/yy/%v\n", utils.GetLivePrefix(r), value.Sid)
77 | }
78 | if res.Data.IsLastPage == 1 {
79 | break
80 | }
81 | i++
82 | }
83 | default:
84 | log.Println("Invalid path:", path)
85 | fmt.Fprintf(w, "
链接错误!
")
86 | }
87 | }
--------------------------------------------------------------------------------
/liveurls/youtube.go:
--------------------------------------------------------------------------------
1 | // Package liveurls
2 | // @Time:2023/02/17 16:32
3 | // @File:youtube.go
4 | // @SoftWare:Goland
5 | // @Author:Popeye
6 | // @Contact:TG@popeyelau
7 |
8 | package liveurls
9 |
10 | import (
11 | "bytes"
12 | "fmt"
13 | "github.com/tidwall/gjson"
14 | "io"
15 | "net/http"
16 | "strconv"
17 | "sync"
18 | "time"
19 |
20 | "github.com/etherlabsio/go-m3u8/m3u8"
21 | )
22 |
23 | var streamCachedMap sync.Map
24 |
25 | type Youtube struct {
26 | //https://www.youtube.com/watch?v=cK4LemjoFd0
27 | //Rid: cK4LemjoFd0
28 | Rid string
29 | Quality string
30 | }
31 |
32 | func (y *Youtube) GetLiveUrl() any {
33 | if cached, ok := getKey(y.Rid); ok {
34 | return cached
35 | }
36 | //proxyUrl, err := url.Parse("http://127.0.0.1:8888")
37 | client := &http.Client{
38 | Timeout: time.Second * 5,
39 | CheckRedirect: func(req *http.Request, via []*http.Request) error {
40 | return http.ErrUseLastResponse
41 | },
42 | //Transport: &http.Transport{Proxy: http.ProxyURL(proxyUrl)},
43 | }
44 |
45 | json := []byte(fmt.Sprintf(`{"context": {"client": {"hl": "zh","clientVersion": "2.20201021.03.00","clientName": "WEB"}},"videoId": "%s"}`, y.Rid))
46 | reqBody := bytes.NewBuffer(json)
47 | r, _ := http.NewRequest("POST", "https://www.youtube.com/youtubei/v1/player", reqBody)
48 | r.Header.Add("user-agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36")
49 | resp, err := client.Do(r)
50 | if err != nil {
51 | return err
52 | }
53 | defer resp.Body.Close()
54 | body, _ := io.ReadAll(resp.Body)
55 | str := string(body)
56 |
57 | stream := gjson.Get(str, "streamingData.hlsManifestUrl")
58 | if stream.Exists() && len(stream.String()) > 0 {
59 | quality := y.getResolution(stream.String())
60 | if quality != nil {
61 | return *quality
62 | }
63 | return stream
64 | }
65 |
66 | formats := gjson.Get(str, "streamingData.formats")
67 | if formats.Exists() && formats.IsArray() {
68 | arr := formats.Array()
69 | playback := arr[len(arr)-1].Get("url").String()
70 | return playback
71 | }
72 |
73 | return nil
74 | }
75 |
76 | func (y *Youtube) getResolution(liveurl string) *string {
77 | client := &http.Client{Timeout: time.Second * 5}
78 | r, _ := http.NewRequest("GET", liveurl, nil)
79 | r.Header.Add("user-agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36")
80 | resp, err := client.Do(r)
81 | if err != nil {
82 | return nil
83 | }
84 | playlist, err := m3u8.Read(resp.Body)
85 | defer resp.Body.Close()
86 | if err != nil {
87 | return nil
88 | }
89 |
90 | playlists := playlist.Playlists()
91 |
92 | if len(playlists) < 1 {
93 | return nil
94 | }
95 |
96 | mapping := map[string]string{}
97 | for _, item := range playlists {
98 | mapping[strconv.Itoa(item.Resolution.Height)] = item.URI
99 | }
100 |
101 | if stream, ok := mapping[y.Quality]; ok {
102 | setKey(y.Rid, stream, 600)
103 | return &stream
104 | }
105 |
106 | stream := playlists[len(playlists)-1].URI
107 | setKey(y.Rid, stream, 600)
108 | return &stream
109 | }
110 |
111 | func setKey(key string, data interface{}, timeout int) {
112 | streamCachedMap.Store(key, data)
113 | time.AfterFunc(time.Second*time.Duration(timeout), func() {
114 | streamCachedMap.Delete(key)
115 | })
116 | }
117 |
118 | func getKey(key string) (interface{}, bool) {
119 | return streamCachedMap.Load(key)
120 | }
--------------------------------------------------------------------------------
/liveurls/bilibili.go:
--------------------------------------------------------------------------------
1 | // Package liveurls
2 | // @Time:2023/02/10 01:03
3 | // @File:bilibili.go
4 | // @SoftWare:Goland
5 | // @Author:feiyang
6 | // @Contact:TG@feiyangdigital
7 |
8 | package liveurls
9 |
10 | import (
11 | "encoding/json"
12 | "fmt"
13 | "github.com/tidwall/gjson"
14 | "io"
15 | "net/http"
16 | )
17 |
18 | type BiliBili struct {
19 | Rid string
20 | Line string
21 | Quality string
22 | Platform string
23 | }
24 |
25 | func (b *BiliBili) GetRealRoomID() any {
26 | var firstmap = make(map[string]any)
27 | var realroomid string
28 | apiurl := "https://api.live.bilibili.com/room/v1/Room/room_init?id=" + b.Rid
29 | client := &http.Client{}
30 | r, _ := http.NewRequest("GET", apiurl, nil)
31 | 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")
32 | resp, _ := client.Do(r)
33 | defer resp.Body.Close()
34 | body, _ := io.ReadAll(resp.Body)
35 | json.Unmarshal(body, &firstmap)
36 | if firstmap["msg"] == "直播间不存在" {
37 | return nil
38 | }
39 | if newmap, ok := firstmap["data"].(map[string]any); ok {
40 | if newmap["live_status"] != float64(1) {
41 | return nil
42 | } else {
43 | if flt, ok := newmap["room_id"].(float64); ok {
44 | realroomid = fmt.Sprintf("%v", int(flt))
45 | }
46 | }
47 |
48 | }
49 | return realroomid
50 | }
51 |
52 | func (b *BiliBili) GetPlayUrl() any {
53 | var roomid string
54 | var realurl string
55 | if str, ok := b.GetRealRoomID().(string); ok {
56 | roomid = str
57 | } else {
58 | return nil
59 | }
60 | client := &http.Client{}
61 | params := map[string]string{
62 | "room_id": roomid,
63 | "protocol": "0,1",
64 | "format": "0,1,2",
65 | "codec": "0,1",
66 | "qn": b.Quality,
67 | "platform": b.Platform,
68 | "ptype": "8",
69 | }
70 | r, _ := http.NewRequest("GET", "https://api.live.bilibili.com/xlive/web-room/v2/index/getRoomPlayInfo", nil)
71 | q := r.URL.Query()
72 | for k, v := range params {
73 | q.Add(k, v)
74 | }
75 | r.URL.RawQuery = q.Encode()
76 | resp, _ := client.Do(r)
77 | defer resp.Body.Close()
78 | body, _ := io.ReadAll(resp.Body)
79 | var json = string(body)
80 | value := gjson.Get(json, "data.playurl_info.playurl.stream")
81 | value.ForEach(func(key, value gjson.Result) bool {
82 | newvalue := gjson.Get(value.String(), "format.0.format_name")
83 | if newvalue.String() == "ts" {
84 | nnvalue := gjson.Get(value.String(), "format.#")
85 | valuelast := fmt.Sprintf("%v", nnvalue.Int()-1)
86 | codeclen := gjson.Get(value.String(), "format."+valuelast+".codec.#")
87 | codeclast := fmt.Sprintf("%v", codeclen.Int()-1)
88 | base_url := gjson.Get(value.String(), "format."+valuelast+".codec."+codeclast+".base_url")
89 | url_info := gjson.Get(value.String(), "format."+valuelast+".codec."+codeclast+".url_info")
90 | url_info.ForEach(func(key, value gjson.Result) bool {
91 | keyval := fmt.Sprintf("%v", key)
92 | switch b.Line {
93 | case "first":
94 | if keyval == "0" {
95 | host := gjson.Get(value.String(), "host")
96 | extra := gjson.Get(value.String(), "extra")
97 | realurl = fmt.Sprintf("%v%v%v", host, base_url, extra)
98 | }
99 |
100 | case "second":
101 | if keyval == "1" {
102 | host := gjson.Get(value.String(), "host")
103 | extra := gjson.Get(value.String(), "extra")
104 | realurl = fmt.Sprintf("%v%v%v", host, base_url, extra)
105 | }
106 | case "third":
107 | if keyval == "2" {
108 | host := gjson.Get(value.String(), "host")
109 | extra := gjson.Get(value.String(), "extra")
110 | realurl = fmt.Sprintf("%v%v%v", host, base_url, extra)
111 | }
112 | }
113 |
114 | return true
115 | })
116 | }
117 | return true
118 | })
119 | return realurl
120 | }
121 |
--------------------------------------------------------------------------------
/api/live.go:
--------------------------------------------------------------------------------
1 | package handler
2 |
3 | import (
4 | "Golang/liveurls"
5 | "Golang/utils"
6 | "fmt"
7 | "net/http"
8 | "strings"
9 | "log"
10 | "os"
11 | )
12 |
13 | // vercel 平台会将请求传递给该函数,这个函数名随意,但函数参数必须按照该规则。
14 | func Handler(w http.ResponseWriter, r *http.Request) {
15 | adurl := "https://cdn.jsdelivr.net/gh/feiyangdigital/testvideo/sdr1080pvideo/index.m3u8"
16 | path := r.URL.Path
17 | params := strings.Split(path, "/")
18 |
19 | // 是否禁用TV
20 | enableTV := os.Getenv("TV") != "false"
21 |
22 | // fmt.Fprintf(w, "request url: %s", path)
23 |
24 | if len(params) >= 4 {
25 | // 解析成功
26 | // 平台
27 | platform := params[2]
28 | // 房间号
29 | rid := params[3]
30 | ts := utils.DefaultQuery(r, "ts", "")
31 | // fmt.Fprintf(w, "parsed platform=%s, room=%s", platform, rid)
32 | switch platform {
33 | case "itv":
34 | if enableTV {
35 | itvobj := &liveurls.Itv{}
36 | cdn := utils.DefaultQuery(r, "cdn", "")
37 | if ts == "" {
38 | itvobj.HandleMainRequest(w, r, cdn, rid)
39 | } else {
40 | itvobj.HandleTsRequest(w, ts)
41 | }
42 | } else {
43 | http.Error(w, "公共服务不提供TV直播", http.StatusForbidden)
44 | }
45 | case "ysptp":
46 | if enableTV {
47 | ysptpobj := &liveurls.Ysptp{}
48 | if ts == "" {
49 | ysptpobj.HandleMainRequest(w, r, rid)
50 | } else {
51 | ysptpobj.HandleTsRequest(w, ts, utils.DefaultQuery(r, "wsTime", ""))
52 | }
53 | } else {
54 | http.Error(w, "公共服务不提供TV直播", http.StatusForbidden)
55 | }
56 | case "douyin":
57 | // 抖音
58 | douyinobj := &liveurls.Douyin{}
59 | douyinobj.Rid = rid
60 | douyinobj.Stream = utils.DefaultQuery(r, "stream", "flv")
61 | http.Redirect(w, r, utils.Duanyan(adurl, douyinobj.GetDouYinUrl()), http.StatusMovedPermanently)
62 | case "douyu":
63 | // 斗鱼
64 | douyuobj := &liveurls.Douyu{}
65 | douyuobj.Rid = rid
66 | douyuobj.Stream_type = utils.DefaultQuery(r, "stream", "flv")
67 | http.Redirect(w, r, utils.Duanyan(adurl, douyuobj.GetRealUrl()), http.StatusMovedPermanently)
68 | case "huya":
69 | // 虎牙
70 | huyaobj := &liveurls.Huya{}
71 | huyaobj.Rid = rid
72 | huyaobj.Cdn = utils.DefaultQuery(r, "cdn", "hwcdn")
73 | huyaobj.Media = utils.DefaultQuery(r, "media", "flv")
74 | huyaobj.Type = utils.DefaultQuery(r, "cdntype", "nodisplay")
75 | if huyaobj.Type == "display" {
76 | fmt.Fprintf(w, huyaobj.GetLiveUrl().(string))
77 | } else {
78 | http.Redirect(w, r, utils.Duanyan(adurl, huyaobj.GetLiveUrl()), http.StatusMovedPermanently)
79 | }
80 | case "bilibili":
81 | // B站
82 | biliobj := &liveurls.BiliBili{}
83 | biliobj.Rid = rid
84 | biliobj.Platform = utils.DefaultQuery(r, "platform", "web")
85 | biliobj.Quality = utils.DefaultQuery(r, "quality", "10000")
86 | biliobj.Line = utils.DefaultQuery(r, "line", "first")
87 | http.Redirect(w, r, utils.Duanyan(adurl, biliobj.GetPlayUrl()), http.StatusMovedPermanently)
88 | case "youtube":
89 | // 油管
90 | ytbObj := &liveurls.Youtube{}
91 | ytbObj.Rid = rid
92 | ytbObj.Quality = utils.DefaultQuery(r, "quality", "1080")
93 | http.Redirect(w, r, utils.Duanyan(adurl, ytbObj.GetLiveUrl()), http.StatusMovedPermanently)
94 | case "yy":
95 | // YY直播
96 | yyObj := &liveurls.Yy{}
97 | yyObj.Rid = rid
98 | yyObj.Quality = utils.DefaultQuery(r, "quality", "4")
99 | http.Redirect(w, r, utils.Duanyan(adurl, yyObj.GetLiveUrl()), http.StatusMovedPermanently)
100 | default:
101 | fmt.Fprintf(w, "Unknown platform=%s, room=%s", platform, rid)
102 | }
103 | } else {
104 | log.Println("Invalid path:", path)
105 | http.Error(w, "Internal Server Error", http.StatusInternalServerError)
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/liveurls/douyu.go:
--------------------------------------------------------------------------------
1 | // Package liveurls
2 | // @Time:2023/02/05 06:36
3 | // @File:douyu.go
4 | // @SoftWare:Goland
5 | // @Author:feiyang
6 | // @Contact:TG@feiyangdigital
7 |
8 | package liveurls
9 |
10 | import (
11 | "Golang/utils"
12 | "crypto/md5"
13 | "encoding/json"
14 | "fmt"
15 | "io"
16 | "net/http"
17 | "regexp"
18 | "strings"
19 | "time"
20 | )
21 |
22 | type Douyu struct {
23 | Rid string
24 | Stream_type string
25 | }
26 |
27 | func md5V3(str string) string {
28 | w := md5.New()
29 | io.WriteString(w, str)
30 | md5str := fmt.Sprintf("%x", w.Sum(nil))
31 | return md5str
32 | }
33 |
34 | func (d *Douyu) GetRoomId() any {
35 | liveurl := "https://m.douyu.com/" + d.Rid
36 | client := &http.Client{}
37 | r, _ := http.NewRequest("GET", liveurl, nil)
38 | 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")
39 | r.Header.Add("upgrade-insecure-requests", "1")
40 | resp, _ := client.Do(r)
41 | defer resp.Body.Close()
42 | body, _ := io.ReadAll(resp.Body)
43 | roomidreg := regexp.MustCompile(`(?i)rid":(\d{1,8}),"vipId`)
44 | roomidres := roomidreg.FindStringSubmatch(string(body))
45 | if roomidres == nil {
46 | return nil
47 | }
48 | realroomid := roomidres[1]
49 | return realroomid
50 | }
51 |
52 | func (d *Douyu) GetRealUrl() any {
53 | var jsUtil = &utils.JsUtil{}
54 | did := "10000000000000000000000000001501"
55 | var timestamp = time.Now().Unix()
56 | var realroomid string
57 | rid := d.GetRoomId()
58 | if str, ok := rid.(string); ok {
59 | realroomid = str
60 | } else {
61 | return nil
62 | }
63 | liveurl := "https://www.douyu.com/" + realroomid
64 | client := &http.Client{}
65 | r, _ := http.NewRequest("GET", liveurl, nil)
66 | r.Header.Add("user-agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36")
67 | r.Header.Add("upgrade-insecure-requests", "1")
68 | resp, _ := client.Do(r)
69 | defer resp.Body.Close()
70 | body, _ := io.ReadAll(resp.Body)
71 | reg := regexp.MustCompile(`(?i)(vdwdae325w_64we[\s\S]*function ub98484234[\s\S]*?)function`)
72 | res := reg.FindStringSubmatch(string(body))
73 | nreg := regexp.MustCompile(`(?i)eval.*?;}`)
74 | strfn := nreg.ReplaceAllString(res[1], "strc;}")
75 | var funcContent1 []string
76 | funcContent1 = append(append(funcContent1, strfn), "ub98484234")
77 | result := jsUtil.JsRun(funcContent1, "ub98484234")
78 | nres := fmt.Sprintf("%s", result)
79 | nnreg := regexp.MustCompile(`(?i)v=(\d+)`)
80 | nnres := nnreg.FindStringSubmatch(nres)
81 | unrb := fmt.Sprintf("%v%v%v%v", realroomid, did, timestamp, nnres[1])
82 | rb := md5V3(unrb)
83 | nnnreg := regexp.MustCompile(`(?i)return rt;}\);?`)
84 | strfn2 := nnnreg.ReplaceAllString(nres, "return rt;}")
85 | strfn3 := strings.Replace(strfn2, `(function (`, `function sign(`, -1)
86 | strfn4 := strings.Replace(strfn3, `CryptoJS.MD5(cb).toString()`, `"`+rb+`"`, -1)
87 | var funcContent2 []string
88 | funcContent2 = append(append(funcContent2, strfn4), "sign")
89 | result2 := jsUtil.JsRun(funcContent2, realroomid, did, timestamp)
90 | param := fmt.Sprintf("%s", result2)
91 | realparam := param + "&rate=0"
92 | r1, n4err := http.Post("https://www.douyu.com/lapi/live/getH5Play/"+realroomid, "application/x-www-form-urlencoded", strings.NewReader(realparam))
93 | if n4err != nil {
94 | return nil
95 | }
96 | defer r1.Body.Close()
97 | body1, _ := io.ReadAll(r1.Body)
98 | var s1 map[string]any
99 | json.Unmarshal(body1, &s1)
100 | var flv_url string
101 | var rtmp_url string
102 | var rtmp_live string
103 | for k, v := range s1 {
104 | if k == "error" {
105 | if s1[k] != float64(0) {
106 | return nil
107 | }
108 | }
109 | if v, ok := v.(map[string]any); ok {
110 | for k, v := range v {
111 | if k == "rtmp_url" {
112 | if urlstr, ok := v.(string); ok {
113 | rtmp_url = urlstr
114 | }
115 | } else if k == "rtmp_live" {
116 | if urlstr, ok := v.(string); ok {
117 | rtmp_live = urlstr
118 | }
119 | }
120 | }
121 | }
122 | }
123 | flv_url = rtmp_url + "/" + rtmp_live
124 | n4reg := regexp.MustCompile(`(?i)(\d{1,8}[0-9a-zA-Z]+)_?\d{0,4}(.flv|/playlist)`)
125 | houzhui := n4reg.FindStringSubmatch(flv_url)
126 | var real_url string
127 | switch d.Stream_type {
128 | case "hls":
129 | real_url = strings.Replace(flv_url, houzhui[1]+".flv", houzhui[1]+".m3u8", -1)
130 | case "flv":
131 | real_url = flv_url
132 | case "xs":
133 | real_url = strings.Replace(flv_url, houzhui[1]+".flv", houzhui[1]+".xs", -1)
134 | }
135 | return real_url
136 | }
--------------------------------------------------------------------------------
/liveurls/ysptp.go:
--------------------------------------------------------------------------------
1 | package liveurls
2 |
3 | import (
4 | "Golang/utils"
5 | "encoding/json"
6 | "io"
7 | "net/http"
8 | "regexp"
9 | "strings"
10 | "sync"
11 | "time"
12 | )
13 |
14 | type Ysptp struct{}
15 |
16 | var cache sync.Map
17 |
18 | type CacheItem struct {
19 | Value string
20 | Expiration int64
21 | }
22 |
23 | var cctvList = map[string]string{
24 | "cctv1.m3u8": "http://liveali-tpgq.cctv.cn/live/cctv1.m3u8,http://liveali-tpgq.cctv.cn/live/",
25 | "cctv2.m3u8": "http://liveali-tpgq.cctv.cn/live/cctv2.m3u8,http://liveali-tpgq.cctv.cn/live/",
26 | "cctv3.m3u8": "http://liveali-tpgq.cctv.cn/live/cctv3.m3u8,http://liveali-tpgq.cctv.cn/live/",
27 | "cctv4.m3u8": "http://liveali-tpgq.cctv.cn/live/cctv4.m3u8,http://liveali-tpgq.cctv.cn/live/",
28 | "cctv5.m3u8": "http://liveali-tpgq.cctv.cn/live/cctv5.m3u8,http://liveali-tpgq.cctv.cn/live/",
29 | "cctv5p.m3u8": "http://liveali-tpgq.cctv.cn/live/cctv5p.m3u8,http://liveali-tpgq.cctv.cn/live/",
30 | "cctv6.m3u8": "http://liveali-tpgq.cctv.cn/live/cctv6.m3u8,http://liveali-tpgq.cctv.cn/live/",
31 | "cctv7.m3u8": "http://liveali-tpgq.cctv.cn/live/cctv7.m3u8,http://liveali-tpgq.cctv.cn/live/",
32 | "cctv8.m3u8": "http://liveali-tpgq.cctv.cn/live/cctv8.m3u8,http://liveali-tpgq.cctv.cn/live/",
33 | "cctv9.m3u8": "http://liveali-tpgq.cctv.cn/live/cctv9.m3u8,http://liveali-tpgq.cctv.cn/live/",
34 | "cctv10.m3u8": "http://liveali-tpgq.cctv.cn/live/cctv10.m3u8,http://liveali-tpgq.cctv.cn/live/",
35 | "cctv11.m3u8": "http://liveali-tpgq.cctv.cn/live/cctv11.m3u8,http://liveali-tpgq.cctv.cn/live/",
36 | "cctv12.m3u8": "http://liveali-tpgq.cctv.cn/live/cctv12.m3u8,http://liveali-tpgq.cctv.cn/live/",
37 | "cctv13.m3u8": "http://liveali-tpgq.cctv.cn/live/cctv13.m3u8,http://liveali-tpgq.cctv.cn/live/",
38 | "cctv14.m3u8": "http://liveali-tpgq.cctv.cn/live/cctv14.m3u8,http://liveali-tpgq.cctv.cn/live/",
39 | "cctv15.m3u8": "http://liveali-tpgq.cctv.cn/live/cctv15.m3u8,http://liveali-tpgq.cctv.cn/live/",
40 | "cctv16.m3u8": "http://liveali-tpgq.cctv.cn/live/cctv16.m3u8,http://liveali-tpgq.cctv.cn/live/",
41 | "cctv17.m3u8": "http://liveali-tpgq.cctv.cn/live/cctv17.m3u8,http://liveali-tpgq.cctv.cn/live/",
42 | "cgtnar.m3u8": "http://liveali-tpgq.cctv.cn/live/cgtnar.m3u8,http://liveali-tpgq.cctv.cn/live/",
43 | "cgtndoc.m3u8": "http://liveali-tpgq.cctv.cn/live/cgtndoc.m3u8,http://liveali-tpgq.cctv.cn/live/",
44 | "cgtnen.m3u8": "http://liveali-tpgq.cctv.cn/live/cgtnen.m3u8,http://liveali-tpgq.cctv.cn/live/",
45 | "cgtnfr.m3u8": "http://liveali-tpgq.cctv.cn/live/cgtnfr.m3u8,http://liveali-tpgq.cctv.cn/live/",
46 | "cgtnru.m3u8": "http://liveali-tpgq.cctv.cn/live/cgtnru.m3u8,http://liveali-tpgq.cctv.cn/live/",
47 | "cgtnsp.m3u8": "http://liveali-tpgq.cctv.cn/live/cgtnsp.m3u8,http://liveali-tpgq.cctv.cn/live/",
48 | "cctv4k.m3u8": "http://liveali-tpgq.cctv.cn/live/cctv4k.m3u8,http://liveali-tpgq.cctv.cn/live/",
49 | "cctv4k_10m.m3u8": "http://liveali-tpgq.cctv.cn/live/cctv4k10m.m3u8,http://liveali-tpgq.cctv.cn/live/",
50 | "cctv4k16.m3u8": "http://liveali-tpgq.cctv.cn/live/cctv4k16.m3u8,http://liveali-tpgq.cctv.cn/live/",
51 | "cctv4k16_10m.m3u8": "http://liveali-tpgq.cctv.cn/live/cctv4k1610m.m3u8,http://liveali-tpgq.cctv.cn/live/",
52 | "cctv8k_36m.m3u8": "http://liveali-tp4k.cctv.cn/live/4K36M/playlist.m3u8,http://liveali-tp4k.cctv.cn/live/4K36M/",
53 | "cctv8k_120m.m3u8": "http://liveali-tp4k.cctv.cn/live/8K120M/playlist.m3u8,http://liveali-tp4k.cctv.cn/live/8K120M/",
54 | }
55 |
56 | func (y *Ysptp) HandleMainRequest(w http.ResponseWriter, r *http.Request, id string) {
57 | uid := utils.DefaultQuery(r, "uid", "1234123122")
58 |
59 | if _, ok := cctvList[id]; !ok {
60 | http.Error(w, "id not found!", http.StatusNotFound)
61 | return
62 | }
63 |
64 | urls := strings.Split(cctvList[id], ",")
65 | data := getURL(id, urls[0], uid, urls[1])
66 | golang := "http://" + r.Host + r.URL.Path
67 | re := regexp.MustCompile(`((?i).*?\.ts)`)
68 | data = re.ReplaceAllString(data, golang+"?ts="+urls[1]+"$1")
69 |
70 | w.Header().Set("Content-Disposition", "attachment;filename="+id)
71 | w.WriteHeader(http.StatusOK) // Set the status code to 200
72 | w.Write([]byte(data)) // Write the response body
73 | }
74 |
75 | func (y *Ysptp) HandleTsRequest(w http.ResponseWriter, ts, wsTime string) {
76 | data := ts + "&wsTime=" + wsTime
77 | w.Header().Set("Content-Type", "video/MP2T")
78 | w.WriteHeader(http.StatusOK) // Set the status code to 200
79 | w.Write([]byte(getTs(data))) // Write the response body
80 | }
81 |
82 | func getURL(id, url, uid, path string) string {
83 | cacheKey := id + uid
84 | if playURL, found := getCache(cacheKey); found {
85 | return fetchData(playURL, path, uid)
86 | }
87 |
88 | bstrURL := "https://ytpvdn.cctv.cn/cctvmobileinf/rest/cctv/videoliveUrl/getstream"
89 | postData := `appcommon={"ap":"cctv_app_tv","an":"央视投屏助手","adid":" ` + uid + `","av":"1.1.7"}&url=` + url
90 |
91 | req, _ := http.NewRequest("POST", bstrURL, strings.NewReader(postData))
92 | req.Header.Set("User-Agent", "cctv_app_tv")
93 | req.Header.Set("Referer", "api.cctv.cn")
94 | req.Header.Set("UID", uid)
95 |
96 | client := &http.Client{}
97 | resp, _ := client.Do(req)
98 | defer resp.Body.Close()
99 | var body strings.Builder
100 | _, _ = io.Copy(&body, resp.Body)
101 |
102 | var result map[string]interface{}
103 | json.Unmarshal([]byte(body.String()), &result)
104 | playURL := result["url"].(string)
105 |
106 | setCache(cacheKey, playURL)
107 |
108 | return fetchData(playURL, path, uid)
109 | }
110 |
111 | func fetchData(playURL, path, uid string) string {
112 | client := &http.Client{}
113 | for {
114 | req, _ := http.NewRequest("GET", playURL, nil)
115 | req.Header.Set("User-Agent", "cctv_app_tv")
116 | req.Header.Set("Referer", "api.cctv.cn")
117 | req.Header.Set("UID", uid)
118 |
119 | resp, _ := client.Do(req)
120 | defer resp.Body.Close()
121 | var body strings.Builder
122 | _, _ = io.Copy(&body, resp.Body)
123 |
124 | data := body.String()
125 | re := regexp.MustCompile(`(.*\.m3u8\?.*)`)
126 | matches := re.FindStringSubmatch(data)
127 | if len(matches) > 0 {
128 | playURL = path + matches[0]
129 | } else {
130 | return data
131 | }
132 | }
133 | }
134 |
135 | func getTs(url string) string {
136 | req, _ := http.NewRequest("GET", url, nil)
137 | req.Header.Set("User-Agent", "cctv_app_tv")
138 | req.Header.Set("Referer", "https://api.cctv.cn/")
139 | req.Header.Set("UID", "1234123122")
140 | req.Header.Set("accept", "*/*")
141 | req.Header.Set("accept-encoding", "gzip, deflate")
142 | req.Header.Set("accept-language", "zh-CN,zh;q=0.9")
143 | req.Header.Set("Connection", "keep-alive")
144 |
145 | client := &http.Client{}
146 | resp, _ := client.Do(req)
147 | defer resp.Body.Close()
148 | var body strings.Builder
149 | _, _ = io.Copy(&body, resp.Body)
150 |
151 | return body.String()
152 | }
153 |
154 | func getCache(key string) (string, bool) {
155 | if item, found := cache.Load(key); found {
156 | cacheItem := item.(CacheItem)
157 | if time.Now().Unix() < cacheItem.Expiration {
158 | return cacheItem.Value, true
159 | }
160 | }
161 | return "", false
162 | }
163 |
164 | func setCache(key, value string) {
165 | cache.Store(key, CacheItem{
166 | Value: value,
167 | Expiration: time.Now().Unix() + 3600,
168 | })
169 | }
--------------------------------------------------------------------------------
/api/index.go:
--------------------------------------------------------------------------------
1 | package handler
2 |
3 | import (
4 | "Golang/liveurls"
5 | "Golang/list"
6 | "Golang/utils"
7 | "fmt"
8 | "net/http"
9 | "strings"
10 | "encoding/json"
11 | "strconv"
12 | "log"
13 | "os"
14 | )
15 |
16 | // vercel 平台会将请求传递给该函数,这个函数名随意,但函数参数必须按照该规则。
17 | func Handler(w http.ResponseWriter, r *http.Request) {
18 | // 是否禁用TV
19 | enableTV := os.Getenv("TV") != "false"
20 | path := r.URL.Path
21 | ts := utils.DefaultQuery(r, "ts", "")
22 | switch path {
23 | // 电视直播
24 | case "/tv.m3u":
25 | if enableTV {
26 | itvm3uobj := &list.Tvm3u{}
27 | w.Header().Set("Content-Type", "application/octet-stream")
28 | w.Header().Set("Content-Disposition", "attachment; filename=tv.m3u")
29 | itvm3uobj.GetTvM3u(w, r.Host)
30 | } else {
31 | http.Error(w, "公共服务不提供TV直播", http.StatusForbidden)
32 | }
33 | // 虎牙一起看
34 | case "/huyayqk.m3u":
35 | yaobj := &list.HuyaYqk{}
36 | res, _ := yaobj.HuYaYqk("https://live.cdn.huya.com/liveHttpUI/getLiveList?iGid=2135")
37 | var result list.YaResponse
38 | json.Unmarshal(res, &result)
39 | pageCount := result.ITotalPage
40 | pageSize := result.IPageSize
41 | w.Header().Set("Content-Type", "application/octet-stream")
42 | w.Header().Set("Content-Disposition", "attachment; filename=huyayqk.m3u")
43 | utils.GetTestVideoUrl(w)
44 |
45 | for i := 1; i <= pageCount; i++ {
46 | apiRes, _ := yaobj.HuYaYqk(fmt.Sprintf("https://live.cdn.huya.com/liveHttpUI/getLiveList?iGid=2135&iPageNo=%d&iPageSize=%d", i, pageSize))
47 | var res list.YaResponse
48 | json.Unmarshal(apiRes, &res)
49 | data := res.VList
50 | for _, value := range data {
51 | fmt.Fprintf(w, "#EXTINF:-1 tvg-logo=\"%s\" group-title=\"%s\", %s\n", value.SAvatar180, value.SGameFullName, value.SNick)
52 | fmt.Fprintf(w, "%s/huya/%v\n", utils.GetLivePrefix(r), value.LProfileRoom)
53 | }
54 | }
55 | // 斗鱼一起看
56 | case "/douyuyqk.m3u":
57 | yuobj := &list.DouYuYqk{}
58 | resAPI, _ := yuobj.Douyuyqk("https://www.douyu.com/gapi/rkc/directory/mixList/2_208/list")
59 |
60 | var result list.DouYuResponse
61 | json.Unmarshal(resAPI, &result)
62 | pageCount := result.Data.Pgcnt
63 |
64 | w.Header().Set("Content-Type", "application/octet-stream")
65 | w.Header().Set("Content-Disposition", "attachment; filename=douyuyqk.m3u")
66 | utils.GetTestVideoUrl(w)
67 |
68 | for i := 1; i <= pageCount; i++ {
69 | apiRes, _ := yuobj.Douyuyqk("https://www.douyu.com/gapi/rkc/directory/mixList/2_208/" + strconv.Itoa(i))
70 |
71 | var res list.DouYuResponse
72 | json.Unmarshal(apiRes, &res)
73 | data := res.Data.Rl
74 |
75 | for _, value := range data {
76 | fmt.Fprintf(w, "#EXTINF:-1 tvg-logo=\"https://apic.douyucdn.cn/upload/%s_big.jpg\" group-title=\"%s\", %s\n", value.Av, value.C2name, value.Nn)
77 | fmt.Fprintf(w, "%s/douyu/%v\n", utils.GetLivePrefix(r), value.Rid)
78 | }
79 | }
80 | // YY轮播
81 | case "/yylunbo.m3u":
82 | yylistobj := &list.Yylist{}
83 | w.Header().Set("Content-Type", "application/octet-stream")
84 | w.Header().Set("Content-Disposition", "attachment; filename=yylunbo.m3u")
85 | utils.GetTestVideoUrl(w)
86 |
87 | i := 1
88 | for {
89 | apiRes := yylistobj.Yylb(fmt.Sprintf("http://rubiks-ipad.yy.com/nav/other/idx/213?channel=appstore&ispType=0&model=iPad8,6&netType=2&os=iOS&osVersion=17.2&page=%d&uid=0&yyVersion=6.17.0", i))
90 | var res list.ApiResponse
91 | json.Unmarshal([]byte(apiRes), &res)
92 | for _, value := range res.Data.Data {
93 | fmt.Fprintf(w, "#EXTINF:-1 tvg-logo=\"%s\" group-title=\"%s\", %s\n", value.Avatar, value.Biz, value.Desc)
94 | fmt.Fprintf(w, "%s/yy/%v\n", utils.GetLivePrefix(r), value.Sid)
95 | }
96 | if res.Data.IsLastPage == 1 {
97 | break
98 | }
99 | i++
100 | }
101 | // 其他链接
102 | default:
103 | adurl := "https://cdn.jsdelivr.net/gh/feiyangdigital/testvideo/sdr1080pvideo/index.m3u8"
104 | params := strings.Split(path, "/")
105 |
106 | // log.Println("request url: ", path)
107 |
108 | if len(params) >= 3 {
109 | // 解析成功
110 | // 平台
111 | platform := params[1]
112 | // 房间号
113 | rid := params[2]
114 | // fmt.Fprintf(w, "parsed platform=%s, room=%s", platform, rid)
115 | switch platform {
116 | case "itv":
117 | if enableTV {
118 | itvobj := &liveurls.Itv{}
119 | cdn := utils.DefaultQuery(r, "cdn", "")
120 | if ts == "" {
121 | itvobj.HandleMainRequest(w, r, cdn, rid)
122 | } else {
123 | itvobj.HandleTsRequest(w, ts)
124 | }
125 | } else {
126 | http.Error(w, "公共服务不提供TV直播", http.StatusForbidden)
127 | }
128 | case "ysptp":
129 | if enableTV {
130 | ysptpobj := &liveurls.Ysptp{}
131 | if ts == "" {
132 | ysptpobj.HandleMainRequest(w, r, rid)
133 | } else {
134 | ysptpobj.HandleTsRequest(w, ts, utils.DefaultQuery(r, "wsTime", ""))
135 | }
136 | } else {
137 | http.Error(w, "公共服务不提供TV直播", http.StatusForbidden)
138 | }
139 | case "douyin":
140 | // 抖音
141 | douyinobj := &liveurls.Douyin{}
142 | douyinobj.Rid = rid
143 | douyinobj.Stream = utils.DefaultQuery(r, "stream", "flv")
144 | http.Redirect(w, r, utils.Duanyan(adurl, douyinobj.GetDouYinUrl()), http.StatusMovedPermanently)
145 | case "douyu":
146 | // 斗鱼
147 | douyuobj := &liveurls.Douyu{}
148 | douyuobj.Rid = rid
149 | douyuobj.Stream_type = utils.DefaultQuery(r, "stream", "flv")
150 | http.Redirect(w, r, utils.Duanyan(adurl, douyuobj.GetRealUrl()), http.StatusMovedPermanently)
151 | case "huya":
152 | // 虎牙
153 | huyaobj := &liveurls.Huya{}
154 | huyaobj.Rid = rid
155 | huyaobj.Cdn = utils.DefaultQuery(r, "cdn", "hwcdn")
156 | huyaobj.Media = utils.DefaultQuery(r, "media", "flv")
157 | huyaobj.Type = utils.DefaultQuery(r, "cdntype", "nodisplay")
158 | if huyaobj.Type == "display" {
159 | fmt.Fprintf(w, huyaobj.GetLiveUrl().(string))
160 | } else {
161 | http.Redirect(w, r, utils.Duanyan(adurl, huyaobj.GetLiveUrl()), http.StatusMovedPermanently)
162 | }
163 | case "bilibili":
164 | // B站
165 | biliobj := &liveurls.BiliBili{}
166 | biliobj.Rid = rid
167 | biliobj.Platform = utils.DefaultQuery(r, "platform", "web")
168 | biliobj.Quality = utils.DefaultQuery(r, "quality", "10000")
169 | biliobj.Line = utils.DefaultQuery(r, "line", "first")
170 | http.Redirect(w, r, utils.Duanyan(adurl, biliobj.GetPlayUrl()), http.StatusMovedPermanently)
171 | case "youtube":
172 | // 油管
173 | ytbObj := &liveurls.Youtube{}
174 | ytbObj.Rid = rid
175 | ytbObj.Quality = utils.DefaultQuery(r, "quality", "1080")
176 | http.Redirect(w, r, utils.Duanyan(adurl, ytbObj.GetLiveUrl()), http.StatusMovedPermanently)
177 | case "yy":
178 | // YY直播
179 | yyObj := &liveurls.Yy{}
180 | yyObj.Rid = rid
181 | yyObj.Quality = utils.DefaultQuery(r, "quality", "4")
182 | http.Redirect(w, r, utils.Duanyan(adurl, yyObj.GetLiveUrl()), http.StatusMovedPermanently)
183 | default:
184 | fmt.Fprintf(w, "Unknown platform=%s, room=%s", platform, rid)
185 | }
186 | } else {
187 | log.Println("Invalid path:", path)
188 | w.Header().Set("Content-Type", "text/html; charset=utf-8")
189 | // http.Error(w, "链接错误!", http.StatusInternalServerError)
190 | fmt.Fprintf(w, "参数错误!
使用教程
")
191 | }
192 | // log.Println("Invalid path:", path)
193 | // fmt.Fprintf(w, "链接错误!
")
194 | }
195 | return
196 | }
--------------------------------------------------------------------------------
/liveurls/huya.go:
--------------------------------------------------------------------------------
1 | // Package liveurls
2 | // @Time:2023/02/05 23:34
3 | // @File:huya.go
4 | // @SoftWare:Goland
5 | // @Author:feiyang
6 | // @Contact:TG@feiyangdigital
7 |
8 | package liveurls
9 |
10 | import (
11 | "bytes"
12 | "crypto/md5"
13 | "encoding/base64"
14 | "encoding/hex"
15 | "encoding/json"
16 | "fmt"
17 | "io"
18 | "math/rand"
19 | "net/http"
20 | "net/url"
21 | "regexp"
22 | "strconv"
23 | "strings"
24 | "time"
25 |
26 | "github.com/tidwall/gjson"
27 | )
28 |
29 | type Huya struct {
30 | Rid string
31 | Media string
32 | Type string
33 | Cdn string
34 | }
35 |
36 | type Data struct {
37 | }
38 |
39 | type Payload struct {
40 | AppId int `json:"appId"`
41 | ByPass int `json:"byPass"`
42 | Context string `json:"context"`
43 | Version string `json:"version"`
44 | Data Data `json:"data"`
45 | }
46 |
47 | type ResponseData struct {
48 | Data struct {
49 | Uid string `json:"uid"`
50 | } `json:"data"`
51 | }
52 |
53 | func getContent(apiUrl string) ([]byte, error) {
54 | payload := Payload{
55 | AppId: 5002,
56 | ByPass: 3,
57 | Context: "",
58 | Version: "2.4",
59 | Data: Data{},
60 | }
61 | jsonPayload, err := json.Marshal(payload)
62 | if err != nil {
63 | return nil, err
64 | }
65 |
66 | req, err := http.NewRequest("POST", apiUrl, bytes.NewBuffer(jsonPayload))
67 | if err != nil {
68 | return nil, err
69 | }
70 |
71 | req.Header.Set("Content-Type", "application/json")
72 | req.Header.Set("Content-Length", fmt.Sprintf("%d", len(jsonPayload)))
73 | req.Header.Set("upgrade-insecure-requests", "1")
74 | 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")
75 |
76 | client := &http.Client{}
77 | resp, err := client.Do(req)
78 | if err != nil {
79 | return nil, err
80 | }
81 | defer resp.Body.Close()
82 |
83 | return io.ReadAll(resp.Body)
84 | }
85 |
86 | var streamInfo = map[string]any{"flv": make(map[string]string), "hls": make(map[string]string)}
87 |
88 | func getUid() string {
89 | content, _ := getContent("https://udblgn.huya.com/web/anonymousLogin")
90 | var responseData ResponseData
91 | json.Unmarshal(content, &responseData)
92 | uid := responseData.Data.Uid
93 | return uid
94 | }
95 |
96 | var uid, _ = strconv.Atoi(getUid())
97 |
98 | func getUUID() int64 {
99 | now := time.Now().UnixNano() / int64(time.Millisecond)
100 | randNum := rand.Intn(1000)
101 | return ((now % 10000000000 * 1000) + int64(randNum)) % 4294967295
102 | }
103 |
104 | func processAntiCode(antiCode string, uid int, streamName string) string {
105 | TimeLocation, err := time.LoadLocation("Asia/Shanghai")
106 | if err != nil {
107 | TimeLocation = time.FixedZone("CST", 8*60*60)
108 | }
109 | now := time.Now().In(TimeLocation)
110 | q, _ := url.ParseQuery(antiCode)
111 | q.Set("t", "102")
112 | q.Set("ctype", "tars_mp")
113 | q.Set("wsTime", strconv.FormatInt(time.Now().Unix()+21600, 16))
114 | q.Set("ver", "1")
115 | q.Set("sv", now.Format("2006010215"))
116 | seqId := strconv.Itoa(uid + int(time.Now().UnixNano()/int64(time.Millisecond)))
117 | q.Set("seqid", seqId)
118 | q.Set("uid", strconv.Itoa(uid))
119 | q.Set("uuid", strconv.FormatInt(getUUID(), 10))
120 | h := md5.New()
121 | h.Write([]byte(seqId + "|" + q.Get("ctype") + "|" + q.Get("t")))
122 | ss := hex.EncodeToString(h.Sum(nil))
123 | fm, _ := base64.StdEncoding.DecodeString(q.Get("fm"))
124 | 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))
125 | h.Reset()
126 | h.Write([]byte(q.Get("fm")))
127 | q.Set("wsSecret", hex.EncodeToString(h.Sum(nil)))
128 | q.Del("fm")
129 | if _, ok := q["txyp"]; ok {
130 | q.Del("txyp")
131 | }
132 | return q.Encode()
133 | }
134 |
135 | func format(jsonStr string, uid int) map[string]any {
136 | cdnType := map[string]string{"HY": "hycdn", "AL": "alicdn", "TX": "txcdn", "HW": "hwcdn", "HS": "hscdn", "WS": "wscdn"}
137 | ojsonStr := gjson.Get(jsonStr, "roomInfo.tLiveInfo.tLiveStreamInfo.vStreamInfo").String()
138 | fmt.Println(gjson.Get(ojsonStr, "value"))
139 | qreg := regexp.MustCompile(`(?i){"_proto"[\s\S]*?"value":([\s\S]*),"_classname"`)
140 | qres := qreg.FindStringSubmatch(ojsonStr)
141 | gjson.Parse(qres[1]).ForEach(func(_, value gjson.Result) bool {
142 | sFlvUrl := value.Get("sFlvUrl").String()
143 | sFlvUrlSuffix := value.Get("sFlvUrlSuffix").String()
144 | sHlsUrl := value.Get("sHlsUrl").String()
145 | sHlsUrlSuffix := value.Get("sHlsUrlSuffix").String()
146 | sStreamName := value.Get("sStreamName").String()
147 | sCdnType := value.Get("sCdnType").String()
148 | sFlvAntiCode := value.Get("sFlvAntiCode").String()
149 | sHlsAntiCode := value.Get("sHlsAntiCode").String()
150 | if sFlvUrl != "" {
151 | streamInfo["flv"].(map[string]string)[cdnType[sCdnType]] = sFlvUrl + "/" + sStreamName + "." + sFlvUrlSuffix + "?" + processAntiCode(sFlvAntiCode, uid, sStreamName)
152 | }
153 | if sHlsUrl != "" {
154 | streamInfo["hls"].(map[string]string)[cdnType[sCdnType]] = sHlsUrl + "/" + sStreamName + "." + sHlsUrlSuffix + "?" + processAntiCode(sHlsAntiCode, uid, sStreamName)
155 | }
156 | return true
157 | })
158 | return streamInfo
159 | }
160 |
161 | func (h *Huya) GetLiveUrl() any {
162 | liveurl := "https://m.huya.com/" + h.Rid
163 | client := &http.Client{}
164 | r, _ := http.NewRequest("GET", liveurl, nil)
165 | 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")
166 | r.Header.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8")
167 | resp, _ := client.Do(r)
168 | defer resp.Body.Close()
169 | body, _ := io.ReadAll(resp.Body)
170 | str := string(body)
171 | freg := regexp.MustCompile(`(?i)`)
172 | res := freg.FindStringSubmatch(str)
173 | if len(res) > 1 && !strings.Contains(res[1], "\"exceptionType\":0") {
174 | jsonStr := res[1]
175 | liveStatus := gjson.Get(jsonStr, "roomInfo.eLiveStatus").Int()
176 | var mediaurl any
177 |
178 | if liveStatus == 2 {
179 | realurl := format(jsonStr, uid)
180 | if h.Type == "display" {
181 | return realurl
182 | }
183 | for k, v := range realurl {
184 | if k == h.Media {
185 | if urlarr, ok := v.(map[string]string); ok {
186 | for k, v := range urlarr {
187 | if k == h.Cdn {
188 | mediaurl = strings.Replace(v, "http://", "https://", 1)
189 | }
190 | }
191 | }
192 | }
193 | }
194 | } else if liveStatus == 3 {
195 | liveLineUrl := gjson.Get(jsonStr, "roomProfile.liveLineUrl").String()
196 | if liveLineUrl != "" {
197 | decodedLiveLineUrl, _ := base64.StdEncoding.DecodeString(liveLineUrl)
198 | mediaurl = "https:" + string(decodedLiveLineUrl)
199 | }
200 | } else {
201 | mediaurl = nil
202 | }
203 | return mediaurl
204 | } else if strings.Contains(res[1], "\"exceptionType\":0") {
205 | var h5info any
206 | ostr, _ := getContent("https://www.huya.com/" + h.Rid)
207 | nstr := string(ostr)
208 | lreg := regexp.MustCompile(`(?i)