[openload] rewrite extractor
[youtube-dl] / youtube_dl / extractor / openload.py
1 # coding: utf-8
2 from __future__ import unicode_literals
3
4 import os
5 import re
6 import subprocess
7 import tempfile
8
9 from .common import InfoExtractor
10 from ..utils import (
11     check_executable,
12     determine_ext,
13     encodeArgument,
14     ExtractorError,
15 )
16
17
18 class OpenloadIE(InfoExtractor):
19     _VALID_URL = r'https?://(?:openload\.(?:co|io)|oload\.tv)/(?:f|embed)/(?P<id>[a-zA-Z0-9-_]+)'
20
21     _TESTS = [{
22         'url': 'https://openload.co/f/kUEfGclsU9o',
23         'md5': 'bf1c059b004ebc7a256f89408e65c36e',
24         'info_dict': {
25             'id': 'kUEfGclsU9o',
26             'ext': 'mp4',
27             'title': 'skyrim_no-audio_1080.mp4',
28             'thumbnail': r're:^https?://.*\.jpg$',
29         },
30     }, {
31         'url': 'https://openload.co/embed/rjC09fkPLYs',
32         'info_dict': {
33             'id': 'rjC09fkPLYs',
34             'ext': 'mp4',
35             'title': 'movie.mp4',
36             'thumbnail': r're:^https?://.*\.jpg$',
37             'subtitles': {
38                 'en': [{
39                     'ext': 'vtt',
40                 }],
41             },
42         },
43         'params': {
44             'skip_download': True,  # test subtitles only
45         },
46     }, {
47         'url': 'https://openload.co/embed/kUEfGclsU9o/skyrim_no-audio_1080.mp4',
48         'only_matching': True,
49     }, {
50         'url': 'https://openload.io/f/ZAn6oz-VZGE/',
51         'only_matching': True,
52     }, {
53         'url': 'https://openload.co/f/_-ztPaZtMhM/',
54         'only_matching': True,
55     }, {
56         # unavailable via https://openload.co/f/Sxz5sADo82g/, different layout
57         # for title and ext
58         'url': 'https://openload.co/embed/Sxz5sADo82g/',
59         'only_matching': True,
60     }, {
61         'url': 'https://oload.tv/embed/KnG-kKZdcfY/',
62         'only_matching': True,
63     }]
64
65     _PHANTOMJS_SCRIPT = r'''
66         phantom.onError = function(msg, trace) {
67           var msgStack = ['PHANTOM ERROR: ' + msg];
68           if(trace && trace.length) {
69             msgStack.push('TRACE:');
70             trace.forEach(function(t) {
71               msgStack.push(' -> ' + (t.file || t.sourceURL) + ': ' + t.line
72                 + (t.function ? ' (in function ' + t.function +')' : ''));
73             });
74           }
75           console.error(msgStack.join('\n'));
76           phantom.exit(1);
77         };
78         var page = require('webpage').create();
79         page.settings.resourceTimeout = 10000;
80         page.onInitialized = function() {
81           page.evaluate(function() {
82             delete window._phantom;
83             delete window.callPhantom;
84           });
85         };
86         page.open('https://openload.co/embed/%s/', function(status) {
87           var info = page.evaluate(function() {
88             return {
89               decoded_id: document.getElementById('streamurl').innerHTML,
90               title: document.querySelector('meta[name="og:title"],'
91                 + 'meta[name=description]').content
92             };
93           });
94           console.log(info.decoded_id + ' ' + info.title);
95           phantom.exit();
96         });'''
97
98     @staticmethod
99     def _extract_urls(webpage):
100         return re.findall(
101             r'<iframe[^>]+src=["\']((?:https?://)?(?:openload\.(?:co|io)|oload\.tv)/embed/[a-zA-Z0-9-_]+)',
102             webpage)
103
104     def _real_extract(self, url):
105         exe = check_executable('phantomjs', ['-v'])
106         if not exe:
107             raise ExtractorError('PhantomJS executable not found in PATH, '
108                                  'download it from http://phantomjs.org',
109                                  expected=True)
110
111         video_id = self._match_id(url)
112         url = 'https://openload.co/embed/%s/' % video_id
113         webpage = self._download_webpage(url, video_id)
114
115         if 'File not found' in webpage or 'deleted by the owner' in webpage:
116             raise ExtractorError('File not found', expected=True, video_id=video_id)
117
118         script_file = tempfile.NamedTemporaryFile(mode='w', delete=False)
119
120         # write JS script to file and close it
121         with script_file:
122             script_file.write(self._PHANTOMJS_SCRIPT % video_id)
123
124         self.to_screen('%s: Decoding video ID with PhantomJS' % video_id)
125
126         p = subprocess.Popen([exe, '--ssl-protocol=any', script_file.name],
127             stdout=subprocess.PIPE, stderr=subprocess.PIPE)
128         output, err = p.communicate()
129         if p.returncode != 0:
130             raise ExtractorError('Decoding failed\n:'
131                                  + encodeArgument(err))
132         else:
133             decoded_id, title = encodeArgument(output).strip().split(' ', 1)
134
135         os.remove(script_file.name)
136
137         video_url = 'https://openload.co/stream/%s?mime=true' % decoded_id
138
139         entries = self._parse_html5_media_entries(url, webpage, video_id)
140         entry = entries[0] if entries else {}
141         subtitles = entry.get('subtitles')
142
143         info_dict = {
144             'id': video_id,
145             'title': title,
146             'thumbnail': entry.get('thumbnail') or self._og_search_thumbnail(webpage, default=None),
147             'url': video_url,
148             # Seems all videos have extensions in their titles
149             'ext': determine_ext(title, 'mp4'),
150             'subtitles': subtitles,
151         }
152         return info_dict