198 lines
4.2 KiB
Go
198 lines
4.2 KiB
Go
|
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
|
||
|
}
|