]> Raphaël G. Git Repositories - youtubedl/blob - youtube_dl/extractor/openload.py
Update changelog
[youtubedl] / youtube_dl / extractor / openload.py
1 # coding: utf-8
2 from __future__ import unicode_literals, division
3
4 import re
5
6 from .common import InfoExtractor
7 from ..compat import (
8 compat_chr,
9 compat_ord,
10 )
11 from ..utils import (
12 determine_ext,
13 ExtractorError,
14 )
15 from ..jsinterp import (
16 JSInterpreter,
17 _NAME_RE
18 )
19
20
21 class OpenloadIE(InfoExtractor):
22 _VALID_URL = r'https?://openload\.(?:co|io)/(?:f|embed)/(?P<id>[a-zA-Z0-9-_]+)'
23
24 _TESTS = [{
25 'url': 'https://openload.co/f/kUEfGclsU9o',
26 'md5': 'bf1c059b004ebc7a256f89408e65c36e',
27 'info_dict': {
28 'id': 'kUEfGclsU9o',
29 'ext': 'mp4',
30 'title': 'skyrim_no-audio_1080.mp4',
31 'thumbnail': 're:^https?://.*\.jpg$',
32 },
33 }, {
34 'url': 'https://openload.co/embed/rjC09fkPLYs',
35 'info_dict': {
36 'id': 'rjC09fkPLYs',
37 'ext': 'mp4',
38 'title': 'movie.mp4',
39 'thumbnail': 're:^https?://.*\.jpg$',
40 'subtitles': {
41 'en': [{
42 'ext': 'vtt',
43 }],
44 },
45 },
46 'params': {
47 'skip_download': True, # test subtitles only
48 },
49 }, {
50 'url': 'https://openload.co/embed/kUEfGclsU9o/skyrim_no-audio_1080.mp4',
51 'only_matching': True,
52 }, {
53 'url': 'https://openload.io/f/ZAn6oz-VZGE/',
54 'only_matching': True,
55 }, {
56 'url': 'https://openload.co/f/_-ztPaZtMhM/',
57 'only_matching': True,
58 }, {
59 # unavailable via https://openload.co/f/Sxz5sADo82g/, different layout
60 # for title and ext
61 'url': 'https://openload.co/embed/Sxz5sADo82g/',
62 'only_matching': True,
63 }]
64
65 def openload_decode(self, txt):
66 symbol_dict = {
67 '(゚Д゚) [゚Θ゚]': '_',
68 '(゚Д゚) [゚ω゚ノ]': 'a',
69 '(゚Д゚) [゚Θ゚ノ]': 'b',
70 '(゚Д゚) [\'c\']': 'c',
71 '(゚Д゚) [゚ー゚ノ]': 'd',
72 '(゚Д゚) [゚Д゚ノ]': 'e',
73 '(゚Д゚) [1]': 'f',
74 '(゚Д゚) [\'o\']': 'o',
75 '(o゚ー゚o)': 'u',
76 '(゚Д゚) [\'c\']': 'c',
77 '((゚ー゚) + (o^_^o))': '7',
78 '((o^_^o) +(o^_^o) +(c^_^o))': '6',
79 '((゚ー゚) + (゚Θ゚))': '5',
80 '(-~3)': '4',
81 '(-~-~1)': '3',
82 '(-~1)': '2',
83 '(-~0)': '1',
84 '((c^_^o)-(c^_^o))': '0',
85 }
86 delim = '(゚Д゚)[゚ε゚]+'
87 end_token = '(゚Д゚)[゚o゚]'
88 symbols = '|'.join(map(re.escape, symbol_dict.keys()))
89 txt = re.sub('(%s)\+\s?' % symbols, lambda m: symbol_dict[m.group(1)], txt)
90 ret = ''
91 for aacode in re.findall(r'{0}\+\s?{1}(.*?){0}'.format(re.escape(end_token), re.escape(delim)), txt):
92 for aachar in aacode.split(delim):
93 if aachar.isdigit():
94 ret += compat_chr(int(aachar, 8))
95 else:
96 m = re.match(r'^u([\da-f]{4})$', aachar)
97 if m:
98 ret += compat_chr(int(m.group(1), 16))
99 else:
100 self.report_warning("Cannot decode: %s" % aachar)
101 return ret
102
103 def _real_extract(self, url):
104 video_id = self._match_id(url)
105 webpage = self._download_webpage('https://openload.co/embed/%s/' % video_id, video_id)
106
107 if 'File not found' in webpage or 'deleted by the owner' in webpage:
108 raise ExtractorError('File not found', expected=True)
109
110 # The following decryption algorithm is written by @yokrysty and
111 # declared to be freely used in youtube-dl
112 # See https://github.com/rg3/youtube-dl/issues/10408
113 enc_data = self._html_search_regex(
114 r'<span[^>]*>([^<]+)</span>\s*<span[^>]*>[^<]+</span>\s*<span[^>]+id="streamurl"',
115 webpage, 'encrypted data')
116
117 enc_code = self._html_search_regex(r'<script[^>]+>(゚ω゚[^<]+)</script>',
118 webpage, 'encrypted code')
119
120 js_code = self.openload_decode(enc_code)
121 jsi = JSInterpreter(js_code)
122
123 m_offset_fun = self._search_regex(r'slice\(0\s*-\s*(%s)\(\)' % _NAME_RE, js_code, 'javascript offset function')
124 m_diff_fun = self._search_regex(r'charCodeAt\(0\)\s*\+\s*(%s)\(\)' % _NAME_RE, js_code, 'javascript diff function')
125
126 offset = jsi.call_function(m_offset_fun)
127 diff = jsi.call_function(m_diff_fun)
128
129 video_url_chars = []
130
131 for idx, c in enumerate(enc_data):
132 j = compat_ord(c)
133 if j >= 33 and j <= 126:
134 j = ((j + 14) % 94) + 33
135 if idx == len(enc_data) - offset:
136 j += diff
137 video_url_chars += compat_chr(j)
138
139 video_url = 'https://openload.co/stream/%s?mime=true' % ''.join(video_url_chars)
140
141 title = self._og_search_title(webpage, default=None) or self._search_regex(
142 r'<span[^>]+class=["\']title["\'][^>]*>([^<]+)', webpage,
143 'title', default=None) or self._html_search_meta(
144 'description', webpage, 'title', fatal=True)
145
146 entries = self._parse_html5_media_entries(url, webpage, video_id)
147 subtitles = entries[0]['subtitles'] if entries else None
148
149 info_dict = {
150 'id': video_id,
151 'title': title,
152 'thumbnail': self._og_search_thumbnail(webpage, default=None),
153 'url': video_url,
154 # Seems all videos have extensions in their titles
155 'ext': determine_ext(title),
156 'subtitles': subtitles,
157 }
158
159 return info_dict