Merge pull request #8876 from remitamine/html5_media
[youtube-dl] / youtube_dl / extractor / sohu.py
1 # encoding: utf-8
2 from __future__ import unicode_literals
3
4 import re
5
6 from .common import InfoExtractor
7 from ..compat import (
8     compat_str,
9     compat_urllib_parse_urlencode,
10 )
11 from ..utils import ExtractorError
12
13
14 class SohuIE(InfoExtractor):
15     _VALID_URL = r'https?://(?P<mytv>my\.)?tv\.sohu\.com/.+?/(?(mytv)|n)(?P<id>\d+)\.shtml.*?'
16
17     _TESTS = [{
18         'note': 'This video is available only in Mainland China',
19         'url': 'http://tv.sohu.com/20130724/n382479172.shtml#super',
20         'md5': '29175c8cadd8b5cc4055001e85d6b372',
21         'info_dict': {
22             'id': '382479172',
23             'ext': 'mp4',
24             'title': 'MV:Far East Movement《The Illest》',
25         },
26         'skip': 'On available in China',
27     }, {
28         'url': 'http://tv.sohu.com/20150305/n409385080.shtml',
29         'md5': '699060e75cf58858dd47fb9c03c42cfb',
30         'info_dict': {
31             'id': '409385080',
32             'ext': 'mp4',
33             'title': '《2015湖南卫视羊年元宵晚会》唐嫣《花好月圆》',
34         }
35     }, {
36         'url': 'http://my.tv.sohu.com/us/232799889/78693464.shtml',
37         'md5': '9bf34be48f2f4dadcb226c74127e203c',
38         'info_dict': {
39             'id': '78693464',
40             'ext': 'mp4',
41             'title': '【爱范品】第31期:MWC见不到的奇葩手机',
42         }
43     }, {
44         'note': 'Multipart video',
45         'url': 'http://my.tv.sohu.com/pl/8384802/78910339.shtml',
46         'info_dict': {
47             'id': '78910339',
48             'title': '【神探苍实战秘籍】第13期 战争之影 赫卡里姆',
49         },
50         'playlist': [{
51             'md5': 'bdbfb8f39924725e6589c146bc1883ad',
52             'info_dict': {
53                 'id': '78910339_part1',
54                 'ext': 'mp4',
55                 'duration': 294,
56                 'title': '【神探苍实战秘籍】第13期 战争之影 赫卡里姆',
57             }
58         }, {
59             'md5': '3e1f46aaeb95354fd10e7fca9fc1804e',
60             'info_dict': {
61                 'id': '78910339_part2',
62                 'ext': 'mp4',
63                 'duration': 300,
64                 'title': '【神探苍实战秘籍】第13期 战争之影 赫卡里姆',
65             }
66         }, {
67             'md5': '8407e634175fdac706766481b9443450',
68             'info_dict': {
69                 'id': '78910339_part3',
70                 'ext': 'mp4',
71                 'duration': 150,
72                 'title': '【神探苍实战秘籍】第13期 战争之影 赫卡里姆',
73             }
74         }]
75     }, {
76         'note': 'Video with title containing dash',
77         'url': 'http://my.tv.sohu.com/us/249884221/78932792.shtml',
78         'info_dict': {
79             'id': '78932792',
80             'ext': 'mp4',
81             'title': 'youtube-dl testing video',
82         },
83         'params': {
84             'skip_download': True
85         }
86     }]
87
88     def _real_extract(self, url):
89
90         def _fetch_data(vid_id, mytv=False):
91             if mytv:
92                 base_data_url = 'http://my.tv.sohu.com/play/videonew.do?vid='
93             else:
94                 base_data_url = 'http://hot.vrs.sohu.com/vrs_flash.action?vid='
95
96             return self._download_json(
97                 base_data_url + vid_id, video_id,
98                 'Downloading JSON data for %s' % vid_id,
99                 headers=self.geo_verification_headers())
100
101         mobj = re.match(self._VALID_URL, url)
102         video_id = mobj.group('id')
103         mytv = mobj.group('mytv') is not None
104
105         webpage = self._download_webpage(url, video_id)
106
107         title = re.sub(r' - 搜狐视频$', '', self._og_search_title(webpage))
108
109         vid = self._html_search_regex(
110             r'var vid ?= ?["\'](\d+)["\']',
111             webpage, 'video path')
112         vid_data = _fetch_data(vid, mytv)
113         if vid_data['play'] != 1:
114             if vid_data.get('status') == 12:
115                 raise ExtractorError(
116                     'Sohu said: There\'s something wrong in the video.',
117                     expected=True)
118             else:
119                 raise ExtractorError(
120                     'Sohu said: The video is only licensed to users in Mainland China.',
121                     expected=True)
122
123         formats_json = {}
124         for format_id in ('nor', 'high', 'super', 'ori', 'h2644k', 'h2654k'):
125             vid_id = vid_data['data'].get('%sVid' % format_id)
126             if not vid_id:
127                 continue
128             vid_id = compat_str(vid_id)
129             formats_json[format_id] = vid_data if vid == vid_id else _fetch_data(vid_id, mytv)
130
131         part_count = vid_data['data']['totalBlocks']
132
133         playlist = []
134         for i in range(part_count):
135             formats = []
136             for format_id, format_data in formats_json.items():
137                 allot = format_data['allot']
138
139                 data = format_data['data']
140                 clips_url = data['clipsURL']
141                 su = data['su']
142
143                 video_url = 'newflv.sohu.ccgslb.net'
144                 cdnId = None
145                 retries = 0
146
147                 while 'newflv.sohu.ccgslb.net' in video_url:
148                     params = {
149                         'prot': 9,
150                         'file': clips_url[i],
151                         'new': su[i],
152                         'prod': 'flash',
153                         'rb': 1,
154                     }
155
156                     if cdnId is not None:
157                         params['idc'] = cdnId
158
159                     download_note = 'Downloading %s video URL part %d of %d' % (
160                         format_id, i + 1, part_count)
161
162                     if retries > 0:
163                         download_note += ' (retry #%d)' % retries
164                     part_info = self._parse_json(self._download_webpage(
165                         'http://%s/?%s' % (allot, compat_urllib_parse_urlencode(params)),
166                         video_id, download_note), video_id)
167
168                     video_url = part_info['url']
169                     cdnId = part_info.get('nid')
170
171                     retries += 1
172                     if retries > 5:
173                         raise ExtractorError('Failed to get video URL')
174
175                 formats.append({
176                     'url': video_url,
177                     'format_id': format_id,
178                     'filesize': data['clipsBytes'][i],
179                     'width': data['width'],
180                     'height': data['height'],
181                     'fps': data['fps'],
182                 })
183             self._sort_formats(formats)
184
185             playlist.append({
186                 'id': '%s_part%d' % (video_id, i + 1),
187                 'title': title,
188                 'duration': vid_data['data']['clipsDuration'][i],
189                 'formats': formats,
190             })
191
192         if len(playlist) == 1:
193             info = playlist[0]
194             info['id'] = video_id
195         else:
196             info = {
197                 '_type': 'multi_video',
198                 'entries': playlist,
199                 'id': video_id,
200                 'title': title,
201             }
202
203         return info