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.
215 lines
7.1 KiB
215 lines
7.1 KiB
package liveurls
|
|
|
|
import (
|
|
"encoding/json"
|
|
"github.com/gin-gonic/gin"
|
|
"io"
|
|
"net/http"
|
|
"regexp"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
type Ysptp struct{}
|
|
|
|
var cache sync.Map
|
|
|
|
type CacheItem struct {
|
|
Value string
|
|
Expiration int64
|
|
}
|
|
|
|
var cctvList = map[string]string{
|
|
"cctv1.m3u8": "http://liveali-tpgq.cctv.cn/live/cctv1.m3u8,http://liveali-tpgq.cctv.cn/live/",
|
|
"cctv2.m3u8": "http://liveali-tpgq.cctv.cn/live/cctv2.m3u8,http://liveali-tpgq.cctv.cn/live/",
|
|
"cctv3.m3u8": "http://liveali-tpgq.cctv.cn/live/cctv3.m3u8,http://liveali-tpgq.cctv.cn/live/",
|
|
"cctv4.m3u8": "http://liveali-tpgq.cctv.cn/live/cctv4.m3u8,http://liveali-tpgq.cctv.cn/live/",
|
|
"cctv5.m3u8": "http://liveali-tpgq.cctv.cn/live/cctv5.m3u8,http://liveali-tpgq.cctv.cn/live/",
|
|
"cctv5p.m3u8": "http://liveali-tpgq.cctv.cn/live/cctv5p.m3u8,http://liveali-tpgq.cctv.cn/live/",
|
|
"cctv6.m3u8": "http://liveali-tpgq.cctv.cn/live/cctv6.m3u8,http://liveali-tpgq.cctv.cn/live/",
|
|
"cctv7.m3u8": "http://liveali-tpgq.cctv.cn/live/cctv7.m3u8,http://liveali-tpgq.cctv.cn/live/",
|
|
"cctv8.m3u8": "http://liveali-tpgq.cctv.cn/live/cctv8.m3u8,http://liveali-tpgq.cctv.cn/live/",
|
|
"cctv9.m3u8": "http://liveali-tpgq.cctv.cn/live/cctv9.m3u8,http://liveali-tpgq.cctv.cn/live/",
|
|
"cctv10.m3u8": "http://liveali-tpgq.cctv.cn/live/cctv10.m3u8,http://liveali-tpgq.cctv.cn/live/",
|
|
"cctv11.m3u8": "http://liveali-tpgq.cctv.cn/live/cctv11.m3u8,http://liveali-tpgq.cctv.cn/live/",
|
|
"cctv12.m3u8": "http://liveali-tpgq.cctv.cn/live/cctv12.m3u8,http://liveali-tpgq.cctv.cn/live/",
|
|
"cctv13.m3u8": "http://liveali-tpgq.cctv.cn/live/cctv13.m3u8,http://liveali-tpgq.cctv.cn/live/",
|
|
"cctv14.m3u8": "http://liveali-tpgq.cctv.cn/live/cctv14.m3u8,http://liveali-tpgq.cctv.cn/live/",
|
|
"cctv15.m3u8": "http://liveali-tpgq.cctv.cn/live/cctv15.m3u8,http://liveali-tpgq.cctv.cn/live/",
|
|
"cctv16.m3u8": "http://liveali-tpgq.cctv.cn/live/cctv16.m3u8,http://liveali-tpgq.cctv.cn/live/",
|
|
"cctv17.m3u8": "http://liveali-tpgq.cctv.cn/live/cctv17.m3u8,http://liveali-tpgq.cctv.cn/live/",
|
|
"cgtnar.m3u8": "http://liveali-tpgq.cctv.cn/live/cgtnar.m3u8,http://liveali-tpgq.cctv.cn/live/",
|
|
"cgtndoc.m3u8": "http://liveali-tpgq.cctv.cn/live/cgtndoc.m3u8,http://liveali-tpgq.cctv.cn/live/",
|
|
"cgtnen.m3u8": "http://liveali-tpgq.cctv.cn/live/cgtnen.m3u8,http://liveali-tpgq.cctv.cn/live/",
|
|
"cgtnfr.m3u8": "http://liveali-tpgq.cctv.cn/live/cgtnfr.m3u8,http://liveali-tpgq.cctv.cn/live/",
|
|
"cgtnru.m3u8": "http://liveali-tpgq.cctv.cn/live/cgtnru.m3u8,http://liveali-tpgq.cctv.cn/live/",
|
|
"cgtnsp.m3u8": "http://liveali-tpgq.cctv.cn/live/cgtnsp.m3u8,http://liveali-tpgq.cctv.cn/live/",
|
|
"cctv4k.m3u8": "http://liveali-tpgq.cctv.cn/live/cctv4k.m3u8,http://liveali-tpgq.cctv.cn/live/",
|
|
"cctv4k_10m.m3u8": "http://liveali-tpgq.cctv.cn/live/cctv4k10m.m3u8,http://liveali-tpgq.cctv.cn/live/",
|
|
"cctv4k16.m3u8": "http://liveali-tpgq.cctv.cn/live/cctv4k16.m3u8,http://liveali-tpgq.cctv.cn/live/",
|
|
"cctv4k16_10m.m3u8": "http://liveali-tpgq.cctv.cn/live/cctv4k1610m.m3u8,http://liveali-tpgq.cctv.cn/live/",
|
|
"cctv8k_36m.m3u8": "http://liveali-tp4k.cctv.cn/live/4K36M/playlist.m3u8,http://liveali-tp4k.cctv.cn/live/4K36M/",
|
|
"cctv8k_120m.m3u8": "http://liveali-tp4k.cctv.cn/live/8K120M/playlist.m3u8,http://liveali-tp4k.cctv.cn/live/8K120M/",
|
|
}
|
|
|
|
func (y *Ysptp) HandleMainRequest(c *gin.Context, id string) {
|
|
uid := c.DefaultQuery("uid", "1234123122")
|
|
|
|
if _, ok := cctvList[id]; !ok {
|
|
c.String(http.StatusNotFound, "id not found!")
|
|
return
|
|
}
|
|
|
|
urls := strings.Split(cctvList[id], ",")
|
|
data := getURL(id, urls[0], uid, urls[1])
|
|
golang := "http://" + c.Request.Host + c.Request.URL.Path
|
|
re := regexp.MustCompile(`((?i).*?\.ts)`)
|
|
data = re.ReplaceAllString(data, golang+"?ts="+urls[1]+"$1")
|
|
|
|
c.Header("Content-Disposition", "attachment;filename="+id)
|
|
c.String(http.StatusOK, data)
|
|
}
|
|
|
|
func (y *Ysptp) HandleTsRequest(c *gin.Context, ts, wsTime string) {
|
|
data := ts + "&wsTime=" + wsTime
|
|
c.Header("Content-Type", "video/MP2T")
|
|
c.String(http.StatusOK, getTs(data))
|
|
}
|
|
|
|
func getURL(id, url, uid, path string) string {
|
|
cacheKey := id + uid
|
|
if playURL, found := getCache(cacheKey); found {
|
|
return fetchData(playURL, path, uid)
|
|
}
|
|
|
|
bstrURL := "https://ytpvdn.cctv.cn/cctvmobileinf/rest/cctv/videoliveUrl/getstream"
|
|
postData := `appcommon={"ap":"cctv_app_tv","an":"央视投屏助手","adid":" ` + uid + `","av":"1.1.7"}&url=` + url
|
|
|
|
req, err := http.NewRequest("POST", bstrURL, strings.NewReader(postData))
|
|
if err != nil {
|
|
// 处理请求创建错误
|
|
return ""
|
|
}
|
|
req.Header.Set("User-Agent", "cctv_app_tv")
|
|
req.Header.Set("Referer", "api.cctv.cn")
|
|
req.Header.Set("UID", uid)
|
|
|
|
client := &http.Client{}
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
// 处理请求错误
|
|
return ""
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
var body strings.Builder
|
|
_, err = io.Copy(&body, resp.Body)
|
|
if err != nil {
|
|
// 处理读取响应体错误
|
|
return ""
|
|
}
|
|
|
|
var result map[string]interface{}
|
|
err = json.Unmarshal([]byte(body.String()), &result)
|
|
if err != nil {
|
|
// 处理 JSON 解析错误
|
|
return ""
|
|
}
|
|
|
|
playURL, ok := result["url"].(string)
|
|
if !ok || playURL == "" {
|
|
// 处理类型断言或 URL 为空的情况
|
|
return ""
|
|
}
|
|
|
|
setCache(cacheKey, playURL)
|
|
|
|
return fetchData(playURL, path, uid)
|
|
}
|
|
|
|
func fetchData(playURL, path, uid string) string {
|
|
client := &http.Client{}
|
|
for {
|
|
req, err := http.NewRequest("GET", playURL, nil)
|
|
if err != nil {
|
|
// 处理请求创建错误
|
|
return ""
|
|
}
|
|
req.Header.Set("User-Agent", "cctv_app_tv")
|
|
req.Header.Set("Referer", "api.cctv.cn")
|
|
req.Header.Set("UID", uid)
|
|
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
// 处理请求错误
|
|
return ""
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
var body strings.Builder
|
|
_, err = io.Copy(&body, resp.Body)
|
|
if err != nil {
|
|
// 处理读取响应体错误
|
|
return ""
|
|
}
|
|
|
|
data := body.String()
|
|
re := regexp.MustCompile(`(.*\.m3u8\?.*)`)
|
|
matches := re.FindStringSubmatch(data)
|
|
if len(matches) > 0 {
|
|
playURL = path + matches[0]
|
|
} else {
|
|
return data
|
|
}
|
|
}
|
|
}
|
|
|
|
func getTs(url string) string {
|
|
req, err := http.NewRequest("GET", url, nil)
|
|
if err != nil {
|
|
// 处理请求创建错误
|
|
return ""
|
|
}
|
|
req.Header.Set("User-Agent", "cctv_app_tv")
|
|
req.Header.Set("Referer", "https://api.cctv.cn/")
|
|
req.Header.Set("UID", "1234123122")
|
|
req.Header.Set("accept", "*/*")
|
|
req.Header.Set("accept-encoding", "gzip, deflate")
|
|
req.Header.Set("accept-language", "zh-CN,zh;q=0.9")
|
|
req.Header.Set("Connection", "keep-alive")
|
|
|
|
client := &http.Client{}
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
// 处理请求错误
|
|
return ""
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
var body strings.Builder
|
|
_, err = io.Copy(&body, resp.Body)
|
|
if err != nil {
|
|
// 处理读取响应体错误
|
|
return ""
|
|
}
|
|
|
|
return body.String()
|
|
}
|
|
|
|
func getCache(key string) (string, bool) {
|
|
if item, found := cache.Load(key); found {
|
|
cacheItem, ok := item.(CacheItem)
|
|
if ok && time.Now().Unix() < cacheItem.Expiration {
|
|
return cacheItem.Value, true
|
|
}
|
|
}
|
|
return "", false
|
|
}
|
|
|
|
func setCache(key, value string) {
|
|
cache.Store(key, CacheItem{
|
|
Value: value,
|
|
Expiration: time.Now().Unix() + 3600,
|
|
})
|
|
}
|
|
|