[streamable] Add extractor
[youtube-dl] / youtube_dl / extractor / streamable.py
1 # coding: utf-8
2 from __future__ import unicode_literals
3
4 from .common import InfoExtractor
5 from ..utils import (
6     ExtractorError,
7     float_or_none
8 )
9
10
11 class StreamableIE(InfoExtractor):
12     _VALID_URL = r'https?://streamable\.com/(?P<id>[\w]+)'
13     _TESTS = [
14         {
15             'url': 'https://streamable.com/dnd1',
16             'md5': '3e3bc5ca088b48c2d436529b64397fef',
17             'info_dict': {
18                 'id': 'dnd1',
19                 'ext': 'mp4',
20                 'title': 'Mikel Oiarzabal scores to make it 0-3 for La Real against Espanyol',
21                 'thumbnail': 'http://cdn.streamable.com/image/dnd1.jpg',
22             }
23         },
24         # older video without bitrate, width/height, etc. info
25         {
26             'url': 'https://streamable.com/moo',
27             'md5': '2cf6923639b87fba3279ad0df3a64e73',
28             'info_dict': {
29                 'id': 'moo',
30                 'ext': 'mp4',
31                 'title': '"Please don\'t eat me!"',
32                 'thumbnail': 'http://cdn.streamable.com/image/f6441ae0c84311e4af010bc47400a0a4.jpg',
33             }
34         }
35     ]
36
37     def _real_extract(self, url):
38         video_id = self._match_id(url)
39
40         # Note: Using the ajax API, as the public Streamable API doesn't seem
41         # to return video info like the title properly sometimes, and doesn't
42         # include info like the video duration
43         video = self._download_json(
44             'https://streamable.com/ajax/videos/%s' % video_id, video_id)
45
46         # Format IDs:
47         # 0 The video is being uploaded
48         # 1 The video is being processed
49         # 2 The video has at least one file ready
50         # 3 The video is unavailable due to an error
51         status = video.get('status')
52         if status != 2:
53             raise ExtractorError(
54                 'This video is currently unavailable. It may still be uploading or processing.',
55                 expected=True)
56
57         formats = []
58         for key, info in video.get('files').items():
59             formats.append({
60                 'format_id': key,
61                 'url': info['url'],
62                 'width': info.get('width'),
63                 'height': info.get('height'),
64                 'filesize': info.get('size'),
65                 'fps': info.get('framerate'),
66                 'vbr': float_or_none(info.get('bitrate'), 1000)
67             })
68         self._sort_formats(formats)
69
70         return {
71             'id': video_id,
72             'title': video.get('result_title'),
73             'thumbnail': video.get('thumbnail_url'),
74             'duration': video.get('duration'),
75             'formats': formats
76         }