| 
									
										
										
										
											2016-04-08 13:50:09 -07:00
										 |  |  | # coding: utf-8 | 
					
						
							|  |  |  | from __future__ import unicode_literals | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-11 17:44:35 +08:00
										 |  |  | import re | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-08 13:50:09 -07:00
										 |  |  | from .common import InfoExtractor | 
					
						
							|  |  |  | from ..utils import ( | 
					
						
							|  |  |  |     ExtractorError, | 
					
						
							| 
									
										
										
										
											2016-07-17 02:01:00 +07:00
										 |  |  |     float_or_none, | 
					
						
							|  |  |  |     int_or_none, | 
					
						
							| 
									
										
										
										
											2016-04-08 13:50:09 -07:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class StreamableIE(InfoExtractor): | 
					
						
							| 
									
										
										
										
											2017-04-26 12:39:53 -04:00
										 |  |  |     _VALID_URL = r'https?://streamable\.com/(?:[es]/)?(?P<id>\w+)' | 
					
						
							| 
									
										
										
										
											2016-04-08 13:50:09 -07:00
										 |  |  |     _TESTS = [ | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             'url': 'https://streamable.com/dnd1', | 
					
						
							|  |  |  |             'md5': '3e3bc5ca088b48c2d436529b64397fef', | 
					
						
							|  |  |  |             'info_dict': { | 
					
						
							|  |  |  |                 'id': 'dnd1', | 
					
						
							|  |  |  |                 'ext': 'mp4', | 
					
						
							|  |  |  |                 'title': 'Mikel Oiarzabal scores to make it 0-3 for La Real against Espanyol', | 
					
						
							| 
									
										
										
										
											2017-01-02 20:08:07 +08:00
										 |  |  |                 'thumbnail': r're:https?://.*\.jpg$', | 
					
						
							| 
									
										
										
										
											2016-07-17 02:01:00 +07:00
										 |  |  |                 'uploader': 'teabaker', | 
					
						
							|  |  |  |                 'timestamp': 1454964157.35115, | 
					
						
							|  |  |  |                 'upload_date': '20160208', | 
					
						
							|  |  |  |                 'duration': 61.516, | 
					
						
							|  |  |  |                 'view_count': int, | 
					
						
							| 
									
										
										
										
											2016-04-08 13:50:09 -07:00
										 |  |  |             } | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |         # older video without bitrate, width/height, etc. info | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             'url': 'https://streamable.com/moo', | 
					
						
							|  |  |  |             'md5': '2cf6923639b87fba3279ad0df3a64e73', | 
					
						
							|  |  |  |             'info_dict': { | 
					
						
							|  |  |  |                 'id': 'moo', | 
					
						
							|  |  |  |                 'ext': 'mp4', | 
					
						
							|  |  |  |                 'title': '"Please don\'t eat me!"', | 
					
						
							| 
									
										
										
										
											2017-01-02 20:08:07 +08:00
										 |  |  |                 'thumbnail': r're:https?://.*\.jpg$', | 
					
						
							| 
									
										
										
										
											2016-07-17 02:01:00 +07:00
										 |  |  |                 'timestamp': 1426115495, | 
					
						
							|  |  |  |                 'upload_date': '20150311', | 
					
						
							|  |  |  |                 'duration': 12, | 
					
						
							|  |  |  |                 'view_count': int, | 
					
						
							| 
									
										
										
										
											2016-04-08 13:50:09 -07:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2016-07-17 02:01:00 +07:00
										 |  |  |         }, | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             'url': 'https://streamable.com/e/dnd1', | 
					
						
							|  |  |  |             'only_matching': True, | 
					
						
							| 
									
										
										
										
											2017-04-26 12:39:53 -04:00
										 |  |  |         }, | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             'url': 'https://streamable.com/s/okkqk/drxjds', | 
					
						
							|  |  |  |             'only_matching': True, | 
					
						
							| 
									
										
										
										
											2016-04-08 13:50:09 -07:00
										 |  |  |         } | 
					
						
							|  |  |  |     ] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-11 17:44:35 +08:00
										 |  |  |     @staticmethod | 
					
						
							|  |  |  |     def _extract_url(webpage): | 
					
						
							|  |  |  |         mobj = re.search( | 
					
						
							|  |  |  |             r'<iframe[^>]+src=(?P<q1>[\'"])(?P<src>(?:https?:)?//streamable\.com/(?:(?!\1).+))(?P=q1)', | 
					
						
							|  |  |  |             webpage) | 
					
						
							|  |  |  |         if mobj: | 
					
						
							|  |  |  |             return mobj.group('src') | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-08 13:50:09 -07:00
										 |  |  |     def _real_extract(self, url): | 
					
						
							|  |  |  |         video_id = self._match_id(url) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Note: Using the ajax API, as the public Streamable API doesn't seem | 
					
						
							|  |  |  |         # to return video info like the title properly sometimes, and doesn't | 
					
						
							|  |  |  |         # include info like the video duration | 
					
						
							|  |  |  |         video = self._download_json( | 
					
						
							| 
									
										
										
										
											2017-03-12 11:51:59 -07:00
										 |  |  |             'https://ajax.streamable.com/videos/%s' % video_id, video_id) | 
					
						
							| 
									
										
										
										
											2016-04-08 13:50:09 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |         # Format IDs: | 
					
						
							|  |  |  |         # 0 The video is being uploaded | 
					
						
							|  |  |  |         # 1 The video is being processed | 
					
						
							|  |  |  |         # 2 The video has at least one file ready | 
					
						
							|  |  |  |         # 3 The video is unavailable due to an error | 
					
						
							|  |  |  |         status = video.get('status') | 
					
						
							|  |  |  |         if status != 2: | 
					
						
							|  |  |  |             raise ExtractorError( | 
					
						
							|  |  |  |                 'This video is currently unavailable. It may still be uploading or processing.', | 
					
						
							|  |  |  |                 expected=True) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-17 02:01:00 +07:00
										 |  |  |         title = video.get('reddit_title') or video['title'] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-08 13:50:09 -07:00
										 |  |  |         formats = [] | 
					
						
							| 
									
										
										
										
											2016-07-17 02:01:00 +07:00
										 |  |  |         for key, info in video['files'].items(): | 
					
						
							|  |  |  |             if not info.get('url'): | 
					
						
							|  |  |  |                 continue | 
					
						
							| 
									
										
										
										
											2016-04-08 13:50:09 -07:00
										 |  |  |             formats.append({ | 
					
						
							|  |  |  |                 'format_id': key, | 
					
						
							| 
									
										
										
										
											2016-07-17 02:01:00 +07:00
										 |  |  |                 'url': self._proto_relative_url(info['url']), | 
					
						
							|  |  |  |                 'width': int_or_none(info.get('width')), | 
					
						
							|  |  |  |                 'height': int_or_none(info.get('height')), | 
					
						
							|  |  |  |                 'filesize': int_or_none(info.get('size')), | 
					
						
							|  |  |  |                 'fps': int_or_none(info.get('framerate')), | 
					
						
							| 
									
										
										
										
											2016-04-08 13:50:09 -07:00
										 |  |  |                 'vbr': float_or_none(info.get('bitrate'), 1000) | 
					
						
							|  |  |  |             }) | 
					
						
							|  |  |  |         self._sort_formats(formats) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return { | 
					
						
							|  |  |  |             'id': video_id, | 
					
						
							| 
									
										
										
										
											2016-07-17 02:01:00 +07:00
										 |  |  |             'title': title, | 
					
						
							|  |  |  |             'description': video.get('description'), | 
					
						
							|  |  |  |             'thumbnail': self._proto_relative_url(video.get('thumbnail_url')), | 
					
						
							|  |  |  |             'uploader': video.get('owner', {}).get('user_name'), | 
					
						
							|  |  |  |             'timestamp': float_or_none(video.get('date_added')), | 
					
						
							|  |  |  |             'duration': float_or_none(video.get('duration')), | 
					
						
							|  |  |  |             'view_count': int_or_none(video.get('plays')), | 
					
						
							| 
									
										
										
										
											2016-04-08 13:50:09 -07:00
										 |  |  |             'formats': formats | 
					
						
							|  |  |  |         } |