198 lines
4.2 KiB
Go
Raw Normal View History

2020-02-08 20:44:46 +08:00
package iqiyi
import (
"encoding/json"
"fmt"
"math/rand"
"strconv"
"strings"
"time"
"github.com/iawia002/annie/downloader"
"github.com/iawia002/annie/extractors"
"github.com/iawia002/annie/parser"
"github.com/iawia002/annie/request"
"github.com/iawia002/annie/utils"
)
type iqiyi struct {
Code string `json:"code"`
Data struct {
VP struct {
Du string `json:"du"`
Tkl []struct {
Vs []struct {
Bid int `json:"bid"`
Scrsz string `json:"scrsz"`
Vsize int64 `json:"vsize"`
Fs []struct {
L string `json:"l"`
B int64 `json:"b"`
} `json:"fs"`
} `json:"vs"`
} `json:"tkl"`
} `json:"vp"`
} `json:"data"`
Msg string `json:"msg"`
}
type iqiyiURL struct {
L string `json:"l"`
}
const iqiyiReferer = "https://www.iqiyi.com"
func getMacID() string {
var macID string
chars := []string{
"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "n", "m", "o", "p", "q", "r", "s", "t", "u", "v",
"w", "x", "y", "z", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
}
size := len(chars)
for i := 0; i < 32; i++ {
macID += chars[rand.Intn(size)]
}
return macID
}
func getVF(params string) string {
var suffix string
for j := 0; j < 8; j++ {
for k := 0; k < 4; k++ {
var v8 int
v4 := 13 * (66*k + 27*j) % 35
if v4 >= 10 {
v8 = v4 + 88
} else {
v8 = v4 + 49
}
suffix += string(v8) // string(97) -> "a"
}
}
params += suffix
return utils.Md5(params)
}
func getVPS(tvid, vid string) (iqiyi, error) {
t := time.Now().Unix() * 1000
host := "http://cache.video.qiyi.com"
params := fmt.Sprintf(
"/vps?tvid=%s&vid=%s&v=0&qypid=%s_12&src=01012001010000000000&t=%d&k_tag=1&k_uid=%s&rs=1",
tvid, vid, tvid, t, getMacID(),
)
vf := getVF(params)
apiURL := fmt.Sprintf("%s%s&vf=%s", host, params, vf)
info, err := request.Get(apiURL, iqiyiReferer, nil)
if err != nil {
return iqiyi{}, err
}
var data iqiyi
json.Unmarshal([]byte(info), &data)
return data, nil
}
// Extract is the main function for extracting data
func Extract(url string) ([]downloader.Data, error) {
html, err := request.Get(url, iqiyiReferer, nil)
if err != nil {
return nil, err
}
tvid := utils.MatchOneOf(
url,
`#curid=(.+)_`,
`tvid=([^&]+)`,
)
if tvid == nil {
tvid = utils.MatchOneOf(
html,
`data-player-tvid="([^"]+)"`,
`param\['tvid'\]\s*=\s*"(.+?)"`,
`"tvid":"(\d+)"`,
)
}
if tvid == nil || len(tvid) < 2 {
return nil, extractors.ErrURLParseFailed
}
vid := utils.MatchOneOf(
url,
`#curid=.+_(.*)$`,
`vid=([^&]+)`,
)
if vid == nil {
vid = utils.MatchOneOf(
html,
`data-player-videoid="([^"]+)"`,
`param\['vid'\]\s*=\s*"(.+?)"`,
`"vid":"(\w+)"`,
)
}
if vid == nil || len(vid) < 2 {
return nil, extractors.ErrURLParseFailed
}
doc, err := parser.GetDoc(html)
if err != nil {
return nil, err
}
title := strings.TrimSpace(doc.Find("h1>a").First().Text())
var sub string
for _, k := range []string{"span", "em"} {
if sub != "" {
break
}
sub = strings.TrimSpace(doc.Find("h1>" + k).First().Text())
}
title += sub
if title == "" {
title = doc.Find("title").Text()
}
videoDatas, err := getVPS(tvid[1], vid[1])
if err != nil {
return nil, err
}
if videoDatas.Code != "A00000" {
return nil, fmt.Errorf("can't play this video: %s", videoDatas.Msg)
}
streams := map[string]downloader.Stream{}
urlPrefix := videoDatas.Data.VP.Du
for _, video := range videoDatas.Data.VP.Tkl[0].Vs {
urls := make([]downloader.URL, len(video.Fs))
for index, v := range video.Fs {
realURLData, err := request.Get(urlPrefix+v.L, iqiyiReferer, nil)
if err != nil {
return nil, err
}
var realURL iqiyiURL
if err = json.Unmarshal([]byte(realURLData), &realURL); err != nil {
return nil, err
}
_, ext, err := utils.GetNameAndExt(realURL.L)
if err != nil {
return nil, err
}
urls[index] = downloader.URL{
URL: realURL.L,
Size: v.B,
Ext: ext,
}
}
streams[strconv.Itoa(video.Bid)] = downloader.Stream{
URLs: urls,
Size: video.Vsize,
Quality: video.Scrsz,
}
}
return []downloader.Data{
{
Site: "爱奇艺 iqiyi.com",
Title: title,
Type: "video",
Streams: streams,
URL: url,
},
}, nil
}