2020-02-08 20:44:46 +08:00

207 lines
4.5 KiB
Go

package mgtv
import (
"encoding/base64"
"encoding/json"
"fmt"
"regexp"
"strconv"
"strings"
"time"
"github.com/iawia002/annie/downloader"
"github.com/iawia002/annie/extractors"
"github.com/iawia002/annie/request"
"github.com/iawia002/annie/utils"
)
type mgtvVideoStream struct {
Name string `json:"name"`
URL string `json:"url"`
Def string `json:"def"`
}
type mgtvVideoInfo struct {
Title string `json:"title"`
Desc string `json:"desc"`
}
type mgtvVideoData struct {
Stream []mgtvVideoStream `json:"stream"`
StreamDomain []string `json:"stream_domain"`
Info mgtvVideoInfo `json:"info"`
}
type mgtv struct {
Data mgtvVideoData `json:"data"`
}
type mgtvVideoAddr struct {
Info string `json:"info"`
}
type mgtvURLInfo struct {
URL string
Size int64
}
type mgtvPm2Data struct {
Data struct {
Atc struct {
Pm2 string `json:"pm2"`
} `json:"atc"`
Info mgtvVideoInfo `json:"info"`
} `json:"data"`
}
func mgtvM3u8(url string) ([]mgtvURLInfo, int64, error) {
var data []mgtvURLInfo
var temp mgtvURLInfo
var size, totalSize int64
urls, err := utils.M3u8URLs(url)
if err != nil {
return nil, 0, err
}
m3u8String, err := request.Get(url, url, nil)
if err != nil {
return nil, 0, err
}
sizes := utils.MatchAll(m3u8String, `#EXT-MGTV-File-SIZE:(\d+)`)
// sizes: [[#EXT-MGTV-File-SIZE:1893724, 1893724]]
for index, u := range urls {
size, err = strconv.ParseInt(sizes[index][1], 10, 64)
if err != nil {
return nil, 0, err
}
totalSize += size
temp = mgtvURLInfo{
URL: u,
Size: size,
}
data = append(data, temp)
}
return data, totalSize, nil
}
func encodeTk2(str string) string {
encodeString := base64.StdEncoding.EncodeToString([]byte(str))
r1 := regexp.MustCompile(`/\+/g`)
r2 := regexp.MustCompile(`/\//g`)
r3 := regexp.MustCompile(`/=/g`)
r1.ReplaceAllString(encodeString, "_")
r2.ReplaceAllString(encodeString, "~")
r3.ReplaceAllString(encodeString, "-")
encodeString = utils.Reverse(encodeString)
return encodeString
}
// Extract is the main function for extracting data
func Extract(url string) ([]downloader.Data, error) {
html, err := request.Get(url, url, nil)
if err != nil {
return nil, err
}
vid := utils.MatchOneOf(
url,
`https?://www.mgtv.com/(?:b|l)/\d+/(\d+).html`,
`https?://www.mgtv.com/hz/bdpz/\d+/(\d+).html`,
)
if vid == nil {
vid = utils.MatchOneOf(html, `vid: (\d+),`)
}
if vid == nil || len(vid) < 2 {
return nil, extractors.ErrURLParseFailed
}
// API extract from https://js.mgtv.com/imgotv-miniv6/global/page/play-tv.js
// getSource and getPlayInfo function
// Chrome Network JS panel
headers := map[string]string{
"Cookie": "PM_CHKID=1",
}
clit := fmt.Sprintf("clit=%d", time.Now().Unix()/1000)
pm2DataString, err := request.Get(
fmt.Sprintf(
"https://pcweb.api.mgtv.com/player/video?video_id=%s&tk2=%s",
vid[1],
encodeTk2(fmt.Sprintf(
"did=f11dee65-4e0d-4d25-bfce-719ad9dc991d|pno=1030|ver=5.5.1|%s", clit,
)),
),
url,
headers,
)
if err != nil {
return nil, err
}
var pm2 mgtvPm2Data
if err = json.Unmarshal([]byte(pm2DataString), &pm2); err != nil {
return nil, err
}
dataString, err := request.Get(
fmt.Sprintf(
"https://pcweb.api.mgtv.com/player/getSource?video_id=%s&tk2=%s&pm2=%s",
vid[1], encodeTk2(clit), pm2.Data.Atc.Pm2,
),
url,
headers,
)
if err != nil {
return nil, err
}
var mgtvData mgtv
if err = json.Unmarshal([]byte(dataString), &mgtvData); err != nil {
return nil, err
}
title := strings.TrimSpace(
pm2.Data.Info.Title + " " + pm2.Data.Info.Desc,
)
mgtvStreams := mgtvData.Data.Stream
var addr mgtvVideoAddr
streams := map[string]downloader.Stream{}
for _, stream := range mgtvStreams {
if stream.URL == "" {
continue
}
// real download address
addr = mgtvVideoAddr{}
addrInfo, err := request.GetByte(mgtvData.Data.StreamDomain[0]+stream.URL, url, headers)
if err != nil {
return nil, err
}
if err = json.Unmarshal(addrInfo, &addr); err != nil {
return nil, err
}
m3u8URLs, totalSize, err := mgtvM3u8(addr.Info)
if err != nil {
return nil, err
}
urls := make([]downloader.URL, len(m3u8URLs))
for index, u := range m3u8URLs {
urls[index] = downloader.URL{
URL: u.URL,
Size: u.Size,
Ext: "ts",
}
}
streams[stream.Def] = downloader.Stream{
URLs: urls,
Size: totalSize,
Quality: stream.Name,
}
}
return []downloader.Data{
{
Site: "芒果TV mgtv.com",
Title: title,
Type: "video",
Streams: streams,
URL: url,
},
}, nil
}