| 
									
										
										
										
											2013-07-05 21:31:50 +02:00
										 |  |  | import re | 
					
						
							|  |  |  | import json | 
					
						
							| 
									
										
										
										
											2013-07-10 17:49:11 +02:00
										 |  |  | import xml.etree.ElementTree | 
					
						
							| 
									
										
										
										
											2013-07-05 21:31:50 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | from .common import InfoExtractor | 
					
						
							| 
									
										
										
										
											2013-07-10 17:49:11 +02:00
										 |  |  | from ..utils import ( | 
					
						
							|  |  |  |     compat_urllib_parse, | 
					
						
							| 
									
										
										
										
											2013-07-11 16:31:29 +02:00
										 |  |  |     find_xpath_attr, | 
					
						
							| 
									
										
										
										
											2013-07-12 14:53:28 +02:00
										 |  |  |     compat_urlparse, | 
					
						
							| 
									
										
										
										
											2013-07-10 17:49:11 +02:00
										 |  |  | ) | 
					
						
							| 
									
										
										
										
											2013-07-05 21:31:50 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | class BrightcoveIE(InfoExtractor): | 
					
						
							| 
									
										
										
										
											2013-07-11 00:04:33 +02:00
										 |  |  |     _VALID_URL = r'https?://.*brightcove\.com/(services|viewer).*\?(?P<query>.*)' | 
					
						
							| 
									
										
										
										
											2013-07-10 17:49:11 +02:00
										 |  |  |     _FEDERATED_URL_TEMPLATE = 'http://c.brightcove.com/services/viewer/htmlFederated?%s' | 
					
						
							| 
									
										
										
										
											2013-07-11 00:04:33 +02:00
										 |  |  |     _PLAYLIST_URL_TEMPLATE = 'http://c.brightcove.com/services/json/experience/runtime/?command=get_programming_for_experience&playerKey=%s' | 
					
						
							| 
									
										
										
										
											2013-07-10 17:49:11 +02:00
										 |  |  |      | 
					
						
							|  |  |  |     # There is a test for Brigtcove in GenericIE, that way we test both the download | 
					
						
							|  |  |  |     # and the detection of videos, and we don't have to find an URL that is always valid | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @classmethod | 
					
						
							|  |  |  |     def _build_brighcove_url(cls, object_str): | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         Build a Brightcove url from a xml string containing | 
					
						
							|  |  |  |         <object class="BrightcoveExperience">{params}</object> | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         object_doc = xml.etree.ElementTree.fromstring(object_str) | 
					
						
							| 
									
										
										
										
											2013-07-11 00:25:38 +02:00
										 |  |  |         assert u'BrightcoveExperience' in object_doc.attrib['class'] | 
					
						
							| 
									
										
										
										
											2013-07-10 17:49:11 +02:00
										 |  |  |         params = {'flashID': object_doc.attrib['id'], | 
					
						
							| 
									
										
										
										
											2013-07-11 16:16:02 +02:00
										 |  |  |                   'playerID': find_xpath_attr(object_doc, './param', 'name', 'playerID').attrib['value'], | 
					
						
							| 
									
										
										
										
											2013-07-10 17:49:11 +02:00
										 |  |  |                   } | 
					
						
							| 
									
										
										
										
											2013-07-11 16:16:02 +02:00
										 |  |  |         playerKey = find_xpath_attr(object_doc, './param', 'name', 'playerKey') | 
					
						
							| 
									
										
										
										
											2013-07-10 17:49:11 +02:00
										 |  |  |         # Not all pages define this value | 
					
						
							|  |  |  |         if playerKey is not None: | 
					
						
							|  |  |  |             params['playerKey'] = playerKey.attrib['value'] | 
					
						
							| 
									
										
										
										
											2013-07-11 16:16:02 +02:00
										 |  |  |         videoPlayer = find_xpath_attr(object_doc, './param', 'name', '@videoPlayer') | 
					
						
							| 
									
										
										
										
											2013-07-11 00:04:33 +02:00
										 |  |  |         if videoPlayer is not None: | 
					
						
							|  |  |  |             params['@videoPlayer'] = videoPlayer.attrib['value'] | 
					
						
							| 
									
										
										
										
											2013-07-10 17:49:11 +02:00
										 |  |  |         data = compat_urllib_parse.urlencode(params) | 
					
						
							|  |  |  |         return cls._FEDERATED_URL_TEMPLATE % data | 
					
						
							| 
									
										
										
										
											2013-07-05 21:31:50 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def _real_extract(self, url): | 
					
						
							|  |  |  |         mobj = re.match(self._VALID_URL, url) | 
					
						
							| 
									
										
										
										
											2013-07-12 14:53:28 +02:00
										 |  |  |         query_str = mobj.group('query') | 
					
						
							|  |  |  |         query = compat_urlparse.parse_qs(query_str) | 
					
						
							| 
									
										
										
										
											2013-07-05 21:31:50 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-07-12 14:53:28 +02:00
										 |  |  |         videoPlayer = query.get('@videoPlayer') | 
					
						
							|  |  |  |         if videoPlayer: | 
					
						
							|  |  |  |             return self._get_video_info(videoPlayer[0], query_str) | 
					
						
							| 
									
										
										
										
											2013-07-11 00:04:33 +02:00
										 |  |  |         else: | 
					
						
							| 
									
										
										
										
											2013-07-12 14:53:28 +02:00
										 |  |  |             player_key = query['playerKey'] | 
					
						
							|  |  |  |             return self._get_playlist_info(player_key[0]) | 
					
						
							| 
									
										
										
										
											2013-07-11 00:04:33 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def _get_video_info(self, video_id, query): | 
					
						
							| 
									
										
										
										
											2013-07-10 17:49:11 +02:00
										 |  |  |         request_url = self._FEDERATED_URL_TEMPLATE % query | 
					
						
							| 
									
										
										
										
											2013-07-05 21:31:50 +02:00
										 |  |  |         webpage = self._download_webpage(request_url, video_id) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.report_extraction(video_id) | 
					
						
							|  |  |  |         info = self._search_regex(r'var experienceJSON = ({.*?});', webpage, 'json') | 
					
						
							|  |  |  |         info = json.loads(info)['data'] | 
					
						
							|  |  |  |         video_info = info['programmedContent']['videoPlayer']['mediaDTO'] | 
					
						
							| 
									
										
										
										
											2013-07-11 00:04:33 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         return self._extract_video_info(video_info) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _get_playlist_info(self, player_key): | 
					
						
							|  |  |  |         playlist_info = self._download_webpage(self._PLAYLIST_URL_TEMPLATE % player_key, | 
					
						
							|  |  |  |                                                player_key, u'Downloading playlist information') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         playlist_info = json.loads(playlist_info)['videoList'] | 
					
						
							|  |  |  |         videos = [self._extract_video_info(video_info) for video_info in playlist_info['mediaCollectionDTO']['videoDTOs']] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return self.playlist_result(videos, playlist_id=playlist_info['id'], | 
					
						
							|  |  |  |                                     playlist_title=playlist_info['mediaCollectionDTO']['displayName']) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _extract_video_info(self, video_info): | 
					
						
							| 
									
										
										
										
											2013-07-05 21:31:50 +02:00
										 |  |  |         renditions = video_info['renditions'] | 
					
						
							|  |  |  |         renditions = sorted(renditions, key=lambda r: r['size']) | 
					
						
							|  |  |  |         best_format = renditions[-1] | 
					
						
							| 
									
										
										
										
											2013-07-11 00:04:33 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         return {'id': video_info['id'], | 
					
						
							| 
									
										
										
										
											2013-07-05 21:31:50 +02:00
										 |  |  |                 'title': video_info['displayName'], | 
					
						
							|  |  |  |                 'url': best_format['defaultURL'],  | 
					
						
							|  |  |  |                 'ext': 'mp4', | 
					
						
							|  |  |  |                 'description': video_info.get('shortDescription'), | 
					
						
							|  |  |  |                 'thumbnail': video_info.get('videoStillURL') or video_info.get('thumbnailURL'), | 
					
						
							|  |  |  |                 'uploader': video_info.get('publisherName'), | 
					
						
							|  |  |  |                 } |