iqiyi: support more stream quality

algorism form @ERioK
thank you @ERioK

v2. remove iqiyi_sc.py, and remove Unicode literal syntax
v3. indent mistake
v4. support vip streams
v5. combine stream profile by size
v6. support 4k profiles

Signed-off-by: Zhang Ning <zhangn1985@gmail.com>
This commit is contained in:
Zhang Ning 2016-06-29 20:18:18 +08:00
parent 9c8d8b0023
commit 78ee9fffe3
2 changed files with 60 additions and 113 deletions

View File

@ -8,11 +8,10 @@ import json
from math import floor from math import floor
from zlib import decompress from zlib import decompress
import hashlib import hashlib
from ..util import log
import time import time
from .iqiyi_sc import gen_sc
''' '''
Changelog: Changelog:
-> http://www.iqiyi.com/common/flashplayer/20150916/MainPlayer_5_2_28_c3_3_7_4.swf -> http://www.iqiyi.com/common/flashplayer/20150916/MainPlayer_5_2_28_c3_3_7_4.swf
@ -81,31 +80,36 @@ def getDispathKey(rid):
t=str(int(floor(int(time)/(10*60.0)))) t=str(int(floor(int(time)/(10*60.0))))
return hashlib.new("md5",bytes(t+tp+rid,"utf-8")).hexdigest() return hashlib.new("md5",bytes(t+tp+rid,"utf-8")).hexdigest()
''' '''
def getVMS(tvid, vid):
t = int(time.time() * 1000)
src = '76f90cbd92f94a2e925d83e8ccd22cb7'
key = 'd5fb4bd9d50c4be6948c97edd7254b0e'
sc = hashlib.new('md5', bytes(str(t) + key + vid, 'utf-8')).hexdigest()
vmsreq= url = 'http://cache.m.iqiyi.com/tmts/{0}/{1}/?t={2}&sc={3}&src={4}'.format(tvid,vid,t,sc,src)
return json.loads(get_content(vmsreq))
class Iqiyi(VideoExtractor): class Iqiyi(VideoExtractor):
name = "爱奇艺 (Iqiyi)" name = "爱奇艺 (Iqiyi)"
stream_types = [ stream_types = [
{'id': 'high', 'container': 'mp4', 'video_profile': '高清'}, {'id': '4k', 'container': 'm3u8', 'video_profile': '4k'},
{'id': 'standard', 'container': 'mp4', 'video_profile': '标清'}, {'id': 'BD', 'container': 'm3u8', 'video_profile': '1080p'},
{'id': 'TD', 'container': 'm3u8', 'video_profile': '720p'},
{'id': 'HD', 'container': 'm3u8', 'video_profile': '540p'},
{'id': 'SD', 'container': 'm3u8', 'video_profile': '360p'},
{'id': 'LD', 'container': 'm3u8', 'video_profile': '210p'},
] ]
'''
supported_stream_types = [ 'high', 'standard'] supported_stream_types = [ 'high', 'standard']
stream_to_bid = { '4k': 10, 'fullhd' : 5, 'suprt-high' : 4, 'super' : 3, 'high' : 2, 'standard' :1, 'topspeed' :96} stream_to_bid = { '4k': 10, 'fullhd' : 5, 'suprt-high' : 4, 'super' : 3, 'high' : 2, 'standard' :1, 'topspeed' :96}
'''
ids = ['4k','BD', 'TD', 'HD', 'SD', 'LD']
vd_2_id = {10: '4k', 19: '4k', 5:'BD', 18: 'BD', 21: 'HD', 2: 'HD', 4: 'TD', 17: 'TD', 96: 'LD', 1: 'SD'}
id_2_profile = {'4k':'4k', 'BD': '1080p','TD': '720p', 'HD': '540p', 'SD': '360p', 'LD': '210p'}
def getVMS(self,rate):
#tm ->the flash run time for md5 usage
#um -> vip 1 normal 0
#authkey -> for password protected video ,replace '' with your password
#puid user.passportid may empty?
#TODO: support password protected video
tvid, vid = self.vid
t = int(time.time() * 1000)
sc = gen_sc(tvid, t).decode('utf-8')
vmsreq= 'http://cache.m.iqiyi.com/jp/tmts/{}/{}/?platForm=h5&rate={}&tvid={}&vid={}&cupid=qc_100001_100186&type=mp4&olimit=0&agenttype=13&src=d846d0c32d664d32b6b54ea48997a589&sc={}&t={}&__jsT=null'.format(tvid, vid, rate, tvid, vid, sc, t - 7)
return json.loads(get_content(vmsreq)[13:])
def download_playlist_by_url(self, url, **kwargs): def download_playlist_by_url(self, url, **kwargs):
self.url = url self.url = url
@ -128,12 +132,47 @@ class Iqiyi(VideoExtractor):
r1(r'vid=([^&]+)', self.url) or \ r1(r'vid=([^&]+)', self.url) or \
r1(r'data-player-videoid="([^"]+)"', html) r1(r'data-player-videoid="([^"]+)"', html)
self.vid = (tvid, videoid) self.vid = (tvid, videoid)
self.title = match1(html, '<title>([^<]+)').split('-')[0]
tvid, videoid = self.vid
info = getVMS(tvid, videoid)
assert info['code'] == 'A00000', 'can\'t play this video'
for stream in self.supported_stream_types: for stream in info['data']['vidl']:
info = self.getVMS(self.stream_to_bid[stream]) try:
if info["code"] == "A00000": stream_id = self.vd_2_id[stream['vd']]
self.title = info['data']['playInfo']['vn'] if stream_id in self.stream_types:
self.streams[stream] = {'container': 'mp4', 'video_profile': stream, 'src' : [info['data']['m3u']], 'size' : url_size(info['data']['m3u'])} continue
stream_profile = self.id_2_profile[stream_id]
self.streams[stream_id] = {'video_profile': stream_profile, 'container': 'm3u8', 'src': [stream['m3u']], 'size' : 0}
except:
log.i("vd: {} is not handled".format(stream['vd']))
log.i("info is {}".format(stream))
# why I need do below???
if not 'BD' in self.stream_types:
p1080_vids = []
if 18 in info['data']['ctl']['vip']['bids']:
p1080_vids.append(info['data']['ctl']['configs']['18']['vid'])
if 5 in info['data']['ctl']['vip']['bids']:
p1080_vids.append(info['data']['ctl']['configs']['5']['vid'])
for v in p1080_vids:
p1080_info = getVMS(tvid, v)
if info['code'] == 'A00000':
p1080_url = p1080_info['data']['m3u']
self.streams['BD'] = {'video_profile': '1080p', 'container': 'm3u8', 'src': [p1080_url], 'size' : 0}
break
if not '4k' in self.stream_types:
k4_vids = []
if 19 in info['data']['ctl']['vip']['bids']:
k4_vids.append(info['data']['ctl']['configs']['19']['vid'])
if 10 in info['data']['ctl']['vip']['bids']:
k4_vids.append(info['data']['ctl']['configs']['10']['vid'])
for v in k4_vids:
k4_info = getVMS(tvid, v)
if info['code'] == 'A00000':
k4_url = k4_info['data']['m3u']
self.streams['4k'] = {'video_profile': '4k', 'container': 'm3u8', 'src': [k4_url], 'size' : 0}
break
''' '''
if info["code"] != "A000000": if info["code"] != "A000000":
log.e("[error] outdated iQIYI key") log.e("[error] outdated iQIYI key")

View File

@ -1,92 +0,0 @@
#!/usr/bin/env python
import binascii
import math
import time
M = [1732584193, -271733879]
M.extend([~M[0], ~M[1]])
I_table = [7, 12, 17, 22, 5, 9, 14, 20, 4, 11, 16, 23, 6, 10, 15, 21]
C_base = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8388608, 432]
def L(n, t):
if t is None:
t = 0
return trunc(((n >> 1) + (t >> 1) << 1) + (n & 1) + (t & 1))
def rshift(val, n):
return val >> n if val >= 0 else (val+0x100000000) >> n
def trunc(n):
n = n % 0x100000000
if n > 0x7fffffff:
n -= 0x100000000
return n
def gen_sc(tvid, Z):
def transform(string, mod):
num = int(string, 16)
return (num >> 8 * (i % 4) & 255 ^ i % mod) << ((a & 3) << 3)
C = list(C_base)
o = list(M)
k = str(Z - 7)
for i in range(13):
a = i
C[a >> 2] |= ord(k[a]) << 8 * (a % 4)
for i in range(16):
a = i + 13
start = (i >> 2) * 8
r = '03967743b643f66763d623d637e30733'
C[a >> 2] |= transform(''.join(reversed(r[start:start + 8])), 7)
for i in range(16):
a = i + 29
start = (i >> 2) * 8
r = '7038766939776a32776a32706b337139'
C[a >> 2] |= transform(r[start:start + 8], 1)
for i in range(9):
a = i + 45
if i < len(tvid):
C[a >> 2] |= ord(tvid[i]) << 8 * (a % 4)
for a in range(64):
i = a
I = i >> 4
C_index = [i, 5 * i + 1, 3 * i + 5, 7 * i][I] % 16 + rshift(a, 6)
m = L(
L(
o[0],
[
trunc(o[1] & o[2]) | trunc(~o[1] & o[3]),
trunc(o[3] & o[1]) | trunc(~o[3] & o[2]),
o[1] ^ o[2] ^ o[3],
o[2] ^ trunc(o[1] | ~o[3])
][I]
),
L(
trunc(int(abs(math.sin(i + 1)) * 4294967296)),
C[C_index] if C_index < len(C) else None
)
)
I = I_table[4 * I + i % 4]
o = [
o[3],
L(o[1], trunc(trunc(m << I) | rshift(m, 32 - I))),
o[1],
o[2],
]
new_M = [L(o[0], M[0]), L(o[1], M[1]), L(o[2], M[2]), L(o[3], M[3])]
s = [new_M[a >> 3] >> (1 ^ a & 7) * 4 & 15 for a in range(32)]
return binascii.hexlify(bytes(s))[1::2]
if __name__ == '__main__':
print(gen_sc("494496100", 1466495259194))
print(gen_sc("397768800", 1466795077775))
print(gen_sc("397768800", 1466796325746))