Merge branch 'master' into subtitles_rework
[youtube-dl] / youtube_dl / extractor / myvideo.py
1 import binascii
2 import base64
3 import hashlib
4 import re
5 import json
6
7 from .common import InfoExtractor
8 from ..utils import (
9     compat_ord,
10     compat_urllib_parse,
11     compat_urllib_request,
12
13     ExtractorError,
14 )
15
16
17
18 class MyVideoIE(InfoExtractor):
19     """Information Extractor for myvideo.de."""
20
21     _VALID_URL = r'(?:http://)?(?:www\.)?myvideo\.de/(?:[^/]+/)?watch/([0-9]+)/([^?/]+).*'
22     IE_NAME = u'myvideo'
23     _TEST = {
24         u'url': u'http://www.myvideo.de/watch/8229274/bowling_fail_or_win',
25         u'file': u'8229274.flv',
26         u'md5': u'2d2753e8130479ba2cb7e0a37002053e',
27         u'info_dict': {
28             u"title": u"bowling-fail-or-win"
29         }
30     }
31
32     # Original Code from: https://github.com/dersphere/plugin.video.myvideo_de.git
33     # Released into the Public Domain by Tristan Fischer on 2013-05-19
34     # https://github.com/rg3/youtube-dl/pull/842
35     def __rc4crypt(self,data, key):
36         x = 0
37         box = list(range(256))
38         for i in list(range(256)):
39             x = (x + box[i] + compat_ord(key[i % len(key)])) % 256
40             box[i], box[x] = box[x], box[i]
41         x = 0
42         y = 0
43         out = ''
44         for char in data:
45             x = (x + 1) % 256
46             y = (y + box[x]) % 256
47             box[x], box[y] = box[y], box[x]
48             out += chr(compat_ord(char) ^ box[(box[x] + box[y]) % 256])
49         return out
50
51     def __md5(self,s):
52         return hashlib.md5(s).hexdigest().encode()
53
54     def _real_extract(self,url):
55         mobj = re.match(self._VALID_URL, url)
56         if mobj is None:
57             raise ExtractorError(u'invalid URL: %s' % url)
58
59         video_id = mobj.group(1)
60
61         GK = (
62           b'WXpnME1EZGhNRGhpTTJNM01XVmhOREU0WldNNVpHTTJOakpt'
63           b'TW1FMU5tVTBNR05pWkRaa05XRXhNVFJoWVRVd1ptSXhaVEV3'
64           b'TnpsbA0KTVRkbU1tSTRNdz09'
65         )
66
67         # Get video webpage
68         webpage_url = 'http://www.myvideo.de/watch/%s' % video_id
69         webpage = self._download_webpage(webpage_url, video_id)
70
71         mobj = re.search('source src=\'(.+?)[.]([^.]+)\'', webpage)
72         if mobj is not None:
73             self.report_extraction(video_id)
74             video_url = mobj.group(1) + '.flv'
75
76             video_title = self._html_search_regex('<title>([^<]+)</title>',
77                 webpage, u'title')
78
79             video_ext = self._search_regex('[.](.+?)$', video_url, u'extension')
80
81             return [{
82                 'id':       video_id,
83                 'url':      video_url,
84                 'uploader': None,
85                 'upload_date':  None,
86                 'title':    video_title,
87                 'ext':      video_ext,
88             }]
89
90         mobj = re.search(r'data-video-service="/service/data/video/%s/config' % video_id, webpage)
91         if mobj is not None:
92             request = compat_urllib_request.Request('http://www.myvideo.de/service/data/video/%s/config' % video_id, '')
93             response = self._download_webpage(request, video_id,
94                                               u'Downloading video info')
95             info = json.loads(base64.b64decode(response).decode('utf-8'))
96             return {'id': video_id,
97                     'title': info['title'],
98                     'url': info['streaming_url'].replace('rtmpe', 'rtmpt'),
99                     'play_path': info['filename'],
100                     'ext': 'flv',
101                     'thumbnail': info['thumbnail'][0]['url'],
102                     }
103
104         # try encxml
105         mobj = re.search('var flashvars={(.+?)}', webpage)
106         if mobj is None:
107             raise ExtractorError(u'Unable to extract video')
108
109         params = {}
110         encxml = ''
111         sec = mobj.group(1)
112         for (a, b) in re.findall('(.+?):\'(.+?)\',?', sec):
113             if not a == '_encxml':
114                 params[a] = b
115             else:
116                 encxml = compat_urllib_parse.unquote(b)
117         if not params.get('domain'):
118             params['domain'] = 'www.myvideo.de'
119         xmldata_url = '%s?%s' % (encxml, compat_urllib_parse.urlencode(params))
120         if 'flash_playertype=MTV' in xmldata_url:
121             self._downloader.report_warning(u'avoiding MTV player')
122             xmldata_url = (
123                 'http://www.myvideo.de/dynamic/get_player_video_xml.php'
124                 '?flash_playertype=D&ID=%s&_countlimit=4&autorun=yes'
125             ) % video_id
126
127         # get enc data
128         enc_data = self._download_webpage(xmldata_url, video_id).split('=')[1]
129         enc_data_b = binascii.unhexlify(enc_data)
130         sk = self.__md5(
131             base64.b64decode(base64.b64decode(GK)) +
132             self.__md5(
133                 str(video_id).encode('utf-8')
134             )
135         )
136         dec_data = self.__rc4crypt(enc_data_b, sk)
137
138         # extracting infos
139         self.report_extraction(video_id)
140
141         video_url = None
142         mobj = re.search('connectionurl=\'(.*?)\'', dec_data)
143         if mobj:
144             video_url = compat_urllib_parse.unquote(mobj.group(1))
145             if 'myvideo2flash' in video_url:
146                 self._downloader.report_warning(u'forcing RTMPT ...')
147                 video_url = video_url.replace('rtmpe://', 'rtmpt://')
148
149         if not video_url:
150             # extract non rtmp videos
151             mobj = re.search('path=\'(http.*?)\' source=\'(.*?)\'', dec_data)
152             if mobj is None:
153                 raise ExtractorError(u'unable to extract url')
154             video_url = compat_urllib_parse.unquote(mobj.group(1)) + compat_urllib_parse.unquote(mobj.group(2))
155
156         video_file = self._search_regex('source=\'(.*?)\'', dec_data, u'video file')
157         video_file = compat_urllib_parse.unquote(video_file)
158
159         if not video_file.endswith('f4m'):
160             ppath, prefix = video_file.split('.')
161             video_playpath = '%s:%s' % (prefix, ppath)
162             video_hls_playlist = ''
163         else:
164             video_playpath = ''
165             video_hls_playlist = (
166                 video_file
167             ).replace('.f4m', '.m3u8')
168
169         video_swfobj = self._search_regex('swfobject.embedSWF\(\'(.+?)\'', webpage, u'swfobj')
170         video_swfobj = compat_urllib_parse.unquote(video_swfobj)
171
172         video_title = self._html_search_regex("<h1(?: class='globalHd')?>(.*?)</h1>",
173             webpage, u'title')
174
175         return [{
176             'id':                 video_id,
177             'url':                video_url,
178             'tc_url':             video_url,
179             'uploader':           None,
180             'upload_date':        None,
181             'title':              video_title,
182             'ext':                u'flv',
183             'play_path':          video_playpath,
184             'video_file':         video_file,
185             'video_hls_playlist': video_hls_playlist,
186             'player_url':         video_swfobj,
187         }]
188