| 
									
										
										
										
											2017-01-28 15:51:52 +01:00
										 |  |  | # coding: utf-8 | 
					
						
							|  |  |  | from __future__ import unicode_literals | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import uuid | 
					
						
							|  |  |  | import xml.etree.ElementTree as etree | 
					
						
							|  |  |  | import json | 
					
						
							| 
									
										
										
										
											2017-11-27 21:59:27 +01:00
										 |  |  | import re | 
					
						
							| 
									
										
										
										
											2017-01-28 15:51:52 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | from .common import InfoExtractor | 
					
						
							| 
									
										
										
										
											2018-05-02 07:21:24 +07:00
										 |  |  | from .brightcove import BrightcoveNewIE | 
					
						
							| 
									
										
										
										
											2017-01-29 16:04:15 +01:00
										 |  |  | from ..compat import ( | 
					
						
							|  |  |  |     compat_str, | 
					
						
							|  |  |  |     compat_etree_register_namespace, | 
					
						
							|  |  |  | ) | 
					
						
							| 
									
										
										
										
											2017-01-28 15:51:52 +01:00
										 |  |  | from ..utils import ( | 
					
						
							| 
									
										
										
										
											2018-06-21 23:12:40 +07:00
										 |  |  |     determine_ext, | 
					
						
							|  |  |  |     ExtractorError, | 
					
						
							| 
									
										
										
										
											2017-01-28 15:51:52 +01:00
										 |  |  |     extract_attributes, | 
					
						
							|  |  |  |     int_or_none, | 
					
						
							| 
									
										
										
										
											2018-06-21 23:06:58 +07:00
										 |  |  |     merge_dicts, | 
					
						
							| 
									
										
										
										
											2017-01-28 15:51:52 +01:00
										 |  |  |     parse_duration, | 
					
						
							| 
									
										
										
										
											2018-05-02 07:21:24 +07:00
										 |  |  |     smuggle_url, | 
					
						
							| 
									
										
										
										
											2018-07-21 19:08:28 +07:00
										 |  |  |     url_or_none, | 
					
						
							| 
									
										
										
										
											2018-06-21 23:12:40 +07:00
										 |  |  |     xpath_with_ns, | 
					
						
							|  |  |  |     xpath_element, | 
					
						
							|  |  |  |     xpath_text, | 
					
						
							| 
									
										
										
										
											2017-01-28 15:51:52 +01:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class ITVIE(InfoExtractor): | 
					
						
							| 
									
										
										
										
											2017-01-28 17:25:15 +01:00
										 |  |  |     _VALID_URL = r'https?://(?:www\.)?itv\.com/hub/[^/]+/(?P<id>[0-9a-zA-Z]+)' | 
					
						
							| 
									
										
										
										
											2017-02-19 03:53:23 +07:00
										 |  |  |     _GEO_COUNTRIES = ['GB'] | 
					
						
							| 
									
										
										
										
											2017-12-14 04:49:07 +07:00
										 |  |  |     _TESTS = [{ | 
					
						
							| 
									
										
										
										
											2017-01-28 15:51:52 +01:00
										 |  |  |         'url': 'http://www.itv.com/hub/mr-bean-animated-series/2a2936a0053', | 
					
						
							|  |  |  |         'info_dict': { | 
					
						
							|  |  |  |             'id': '2a2936a0053', | 
					
						
							|  |  |  |             'ext': 'flv', | 
					
						
							|  |  |  |             'title': 'Home Movie', | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |         'params': { | 
					
						
							|  |  |  |             # rtmp download | 
					
						
							|  |  |  |             'skip_download': True, | 
					
						
							|  |  |  |         }, | 
					
						
							| 
									
										
										
										
											2017-12-14 04:49:07 +07:00
										 |  |  |     }, { | 
					
						
							|  |  |  |         # unavailable via data-playlist-url | 
					
						
							|  |  |  |         'url': 'https://www.itv.com/hub/through-the-keyhole/2a2271a0033', | 
					
						
							|  |  |  |         'only_matching': True, | 
					
						
							| 
									
										
										
										
											2018-05-01 22:46:06 +07:00
										 |  |  |     }, { | 
					
						
							|  |  |  |         # InvalidVodcrid | 
					
						
							|  |  |  |         'url': 'https://www.itv.com/hub/james-martins-saturday-morning/2a5159a0034', | 
					
						
							|  |  |  |         'only_matching': True, | 
					
						
							|  |  |  |     }, { | 
					
						
							|  |  |  |         # ContentUnavailable | 
					
						
							|  |  |  |         'url': 'https://www.itv.com/hub/whos-doing-the-dishes/2a2898a0024', | 
					
						
							|  |  |  |         'only_matching': True, | 
					
						
							| 
									
										
										
										
											2017-12-14 04:49:07 +07:00
										 |  |  |     }] | 
					
						
							| 
									
										
										
										
											2017-01-28 15:51:52 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def _real_extract(self, url): | 
					
						
							|  |  |  |         video_id = self._match_id(url) | 
					
						
							|  |  |  |         webpage = self._download_webpage(url, video_id) | 
					
						
							|  |  |  |         params = extract_attributes(self._search_regex( | 
					
						
							|  |  |  |             r'(?s)(<[^>]+id="video"[^>]*>)', webpage, 'params')) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         ns_map = { | 
					
						
							|  |  |  |             'soapenv': 'http://schemas.xmlsoap.org/soap/envelope/', | 
					
						
							|  |  |  |             'tem': 'http://tempuri.org/', | 
					
						
							|  |  |  |             'itv': 'http://schemas.datacontract.org/2004/07/Itv.BB.Mercury.Common.Types', | 
					
						
							|  |  |  |             'com': 'http://schemas.itv.com/2009/05/Common', | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         for ns, full_ns in ns_map.items(): | 
					
						
							| 
									
										
										
										
											2017-01-29 16:04:15 +01:00
										 |  |  |             compat_etree_register_namespace(ns, full_ns) | 
					
						
							| 
									
										
										
										
											2017-01-28 15:51:52 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         def _add_ns(name): | 
					
						
							|  |  |  |             return xpath_with_ns(name, ns_map) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         def _add_sub_element(element, name): | 
					
						
							|  |  |  |             return etree.SubElement(element, _add_ns(name)) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-07-23 00:59:07 +07:00
										 |  |  |         production_id = ( | 
					
						
							|  |  |  |             params.get('data-video-autoplay-id') or | 
					
						
							|  |  |  |             '%s#001' % ( | 
					
						
							|  |  |  |                 params.get('data-video-episode-id') or | 
					
						
							|  |  |  |                 video_id.replace('a', '/'))) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-28 15:51:52 +01:00
										 |  |  |         req_env = etree.Element(_add_ns('soapenv:Envelope')) | 
					
						
							|  |  |  |         _add_sub_element(req_env, 'soapenv:Header') | 
					
						
							|  |  |  |         body = _add_sub_element(req_env, 'soapenv:Body') | 
					
						
							|  |  |  |         get_playlist = _add_sub_element(body, ('tem:GetPlaylist')) | 
					
						
							|  |  |  |         request = _add_sub_element(get_playlist, 'tem:request') | 
					
						
							| 
									
										
										
										
											2017-07-23 00:59:07 +07:00
										 |  |  |         _add_sub_element(request, 'itv:ProductionId').text = production_id | 
					
						
							| 
									
										
										
										
											2017-01-28 15:51:52 +01:00
										 |  |  |         _add_sub_element(request, 'itv:RequestGuid').text = compat_str(uuid.uuid4()).upper() | 
					
						
							|  |  |  |         vodcrid = _add_sub_element(request, 'itv:Vodcrid') | 
					
						
							|  |  |  |         _add_sub_element(vodcrid, 'com:Id') | 
					
						
							|  |  |  |         _add_sub_element(request, 'itv:Partition') | 
					
						
							|  |  |  |         user_info = _add_sub_element(get_playlist, 'tem:userInfo') | 
					
						
							|  |  |  |         _add_sub_element(user_info, 'itv:Broadcaster').text = 'Itv' | 
					
						
							|  |  |  |         _add_sub_element(user_info, 'itv:DM') | 
					
						
							|  |  |  |         _add_sub_element(user_info, 'itv:RevenueScienceValue') | 
					
						
							|  |  |  |         _add_sub_element(user_info, 'itv:SessionId') | 
					
						
							|  |  |  |         _add_sub_element(user_info, 'itv:SsoToken') | 
					
						
							|  |  |  |         _add_sub_element(user_info, 'itv:UserToken') | 
					
						
							|  |  |  |         site_info = _add_sub_element(get_playlist, 'tem:siteInfo') | 
					
						
							|  |  |  |         _add_sub_element(site_info, 'itv:AdvertisingRestriction').text = 'None' | 
					
						
							|  |  |  |         _add_sub_element(site_info, 'itv:AdvertisingSite').text = 'ITV' | 
					
						
							|  |  |  |         _add_sub_element(site_info, 'itv:AdvertisingType').text = 'Any' | 
					
						
							|  |  |  |         _add_sub_element(site_info, 'itv:Area').text = 'ITVPLAYER.VIDEO' | 
					
						
							|  |  |  |         _add_sub_element(site_info, 'itv:Category') | 
					
						
							|  |  |  |         _add_sub_element(site_info, 'itv:Platform').text = 'DotCom' | 
					
						
							|  |  |  |         _add_sub_element(site_info, 'itv:Site').text = 'ItvCom' | 
					
						
							|  |  |  |         device_info = _add_sub_element(get_playlist, 'tem:deviceInfo') | 
					
						
							|  |  |  |         _add_sub_element(device_info, 'itv:ScreenSize').text = 'Big' | 
					
						
							|  |  |  |         player_info = _add_sub_element(get_playlist, 'tem:playerInfo') | 
					
						
							|  |  |  |         _add_sub_element(player_info, 'itv:Version').text = '2' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         headers = self.geo_verification_headers() | 
					
						
							|  |  |  |         headers.update({ | 
					
						
							|  |  |  |             'Content-Type': 'text/xml; charset=utf-8', | 
					
						
							|  |  |  |             'SOAPAction': 'http://tempuri.org/PlaylistService/GetPlaylist', | 
					
						
							|  |  |  |         }) | 
					
						
							| 
									
										
										
										
											2017-12-14 04:49:07 +07:00
										 |  |  | 
 | 
					
						
							|  |  |  |         info = self._search_json_ld(webpage, video_id, default={}) | 
					
						
							|  |  |  |         formats = [] | 
					
						
							|  |  |  |         subtitles = {} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         def extract_subtitle(sub_url): | 
					
						
							|  |  |  |             ext = determine_ext(sub_url, 'ttml') | 
					
						
							|  |  |  |             subtitles.setdefault('en', []).append({ | 
					
						
							|  |  |  |                 'url': sub_url, | 
					
						
							|  |  |  |                 'ext': 'ttml' if ext == 'xml' else ext, | 
					
						
							|  |  |  |             }) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-28 15:51:52 +01:00
										 |  |  |         resp_env = self._download_xml( | 
					
						
							|  |  |  |             params['data-playlist-url'], video_id, | 
					
						
							| 
									
										
										
										
											2018-06-21 23:06:58 +07:00
										 |  |  |             headers=headers, data=etree.tostring(req_env), fatal=False) | 
					
						
							|  |  |  |         if resp_env: | 
					
						
							|  |  |  |             playlist = xpath_element(resp_env, './/Playlist') | 
					
						
							|  |  |  |             if playlist is None: | 
					
						
							|  |  |  |                 fault_code = xpath_text(resp_env, './/faultcode') | 
					
						
							|  |  |  |                 fault_string = xpath_text(resp_env, './/faultstring') | 
					
						
							|  |  |  |                 if fault_code == 'InvalidGeoRegion': | 
					
						
							|  |  |  |                     self.raise_geo_restricted( | 
					
						
							|  |  |  |                         msg=fault_string, countries=self._GEO_COUNTRIES) | 
					
						
							|  |  |  |                 elif fault_code not in ( | 
					
						
							|  |  |  |                         'InvalidEntity', 'InvalidVodcrid', 'ContentUnavailable'): | 
					
						
							|  |  |  |                     raise ExtractorError( | 
					
						
							|  |  |  |                         '%s said: %s' % (self.IE_NAME, fault_string), expected=True) | 
					
						
							|  |  |  |                 info.update({ | 
					
						
							|  |  |  |                     'title': self._og_search_title(webpage), | 
					
						
							|  |  |  |                     'episode_title': params.get('data-video-episode'), | 
					
						
							|  |  |  |                     'series': params.get('data-video-title'), | 
					
						
							|  |  |  |                 }) | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 title = xpath_text(playlist, 'EpisodeTitle', default=None) | 
					
						
							|  |  |  |                 info.update({ | 
					
						
							|  |  |  |                     'title': title, | 
					
						
							|  |  |  |                     'episode_title': title, | 
					
						
							|  |  |  |                     'episode_number': int_or_none(xpath_text(playlist, 'EpisodeNumber')), | 
					
						
							|  |  |  |                     'series': xpath_text(playlist, 'ProgrammeTitle'), | 
					
						
							|  |  |  |                     'duration': parse_duration(xpath_text(playlist, 'Duration')), | 
					
						
							|  |  |  |                 }) | 
					
						
							|  |  |  |                 video_element = xpath_element(playlist, 'VideoEntries/Video', fatal=True) | 
					
						
							|  |  |  |                 media_files = xpath_element(video_element, 'MediaFiles', fatal=True) | 
					
						
							|  |  |  |                 rtmp_url = media_files.attrib['base'] | 
					
						
							| 
									
										
										
										
											2017-01-28 15:51:52 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-21 23:06:58 +07:00
										 |  |  |                 for media_file in media_files.findall('MediaFile'): | 
					
						
							|  |  |  |                     play_path = xpath_text(media_file, 'URL') | 
					
						
							|  |  |  |                     if not play_path: | 
					
						
							|  |  |  |                         continue | 
					
						
							|  |  |  |                     tbr = int_or_none(media_file.get('bitrate'), 1000) | 
					
						
							|  |  |  |                     f = { | 
					
						
							|  |  |  |                         'format_id': 'rtmp' + ('-%d' % tbr if tbr else ''), | 
					
						
							|  |  |  |                         'play_path': play_path, | 
					
						
							|  |  |  |                         # Providing this swfVfy allows to avoid truncated downloads | 
					
						
							|  |  |  |                         'player_url': 'http://www.itv.com/mercury/Mercury_VideoPlayer.swf', | 
					
						
							|  |  |  |                         'page_url': url, | 
					
						
							|  |  |  |                         'tbr': tbr, | 
					
						
							|  |  |  |                         'ext': 'flv', | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                     app = self._search_regex( | 
					
						
							|  |  |  |                         'rtmpe?://[^/]+/(.+)$', rtmp_url, 'app', default=None) | 
					
						
							|  |  |  |                     if app: | 
					
						
							|  |  |  |                         f.update({ | 
					
						
							|  |  |  |                             'url': rtmp_url.split('?', 1)[0], | 
					
						
							|  |  |  |                             'app': app, | 
					
						
							|  |  |  |                         }) | 
					
						
							|  |  |  |                     else: | 
					
						
							|  |  |  |                         f['url'] = rtmp_url | 
					
						
							|  |  |  |                     formats.append(f) | 
					
						
							| 
									
										
										
										
											2017-12-14 04:49:07 +07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-21 23:06:58 +07:00
										 |  |  |                 for caption_url in video_element.findall('ClosedCaptioningURIs/URL'): | 
					
						
							|  |  |  |                     if caption_url.text: | 
					
						
							|  |  |  |                         extract_subtitle(caption_url.text) | 
					
						
							| 
									
										
										
										
											2017-01-28 15:51:52 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-27 21:59:27 +01:00
										 |  |  |         ios_playlist_url = params.get('data-video-playlist') or params.get('data-video-id') | 
					
						
							| 
									
										
										
										
											2017-01-28 15:51:52 +01:00
										 |  |  |         hmac = params.get('data-video-hmac') | 
					
						
							| 
									
										
										
										
											2017-11-27 21:59:27 +01:00
										 |  |  |         if ios_playlist_url and hmac and re.match(r'https?://', ios_playlist_url): | 
					
						
							| 
									
										
										
										
											2017-01-28 15:51:52 +01:00
										 |  |  |             headers = self.geo_verification_headers() | 
					
						
							|  |  |  |             headers.update({ | 
					
						
							|  |  |  |                 'Accept': 'application/vnd.itv.vod.playlist.v2+json', | 
					
						
							|  |  |  |                 'Content-Type': 'application/json', | 
					
						
							|  |  |  |                 'hmac': hmac.upper(), | 
					
						
							|  |  |  |             }) | 
					
						
							|  |  |  |             ios_playlist = self._download_json( | 
					
						
							|  |  |  |                 ios_playlist_url, video_id, data=json.dumps({ | 
					
						
							|  |  |  |                     'user': { | 
					
						
							|  |  |  |                         'itvUserId': '', | 
					
						
							|  |  |  |                         'entitlements': [], | 
					
						
							|  |  |  |                         'token': '' | 
					
						
							|  |  |  |                     }, | 
					
						
							|  |  |  |                     'device': { | 
					
						
							| 
									
										
										
										
											2017-11-27 21:59:27 +01:00
										 |  |  |                         'manufacturer': 'Safari', | 
					
						
							|  |  |  |                         'model': '5', | 
					
						
							| 
									
										
										
										
											2017-01-28 15:51:52 +01:00
										 |  |  |                         'os': { | 
					
						
							| 
									
										
										
										
											2017-11-27 21:59:27 +01:00
										 |  |  |                             'name': 'Windows NT', | 
					
						
							|  |  |  |                             'version': '6.1', | 
					
						
							|  |  |  |                             'type': 'desktop' | 
					
						
							| 
									
										
										
										
											2017-01-28 15:51:52 +01:00
										 |  |  |                         } | 
					
						
							|  |  |  |                     }, | 
					
						
							|  |  |  |                     'client': { | 
					
						
							|  |  |  |                         'version': '4.1', | 
					
						
							|  |  |  |                         'id': 'browser' | 
					
						
							|  |  |  |                     }, | 
					
						
							|  |  |  |                     'variantAvailability': { | 
					
						
							|  |  |  |                         'featureset': { | 
					
						
							| 
									
										
										
										
											2017-11-27 21:59:27 +01:00
										 |  |  |                             'min': ['hls', 'aes', 'outband-webvtt'], | 
					
						
							|  |  |  |                             'max': ['hls', 'aes', 'outband-webvtt'] | 
					
						
							| 
									
										
										
										
											2017-01-28 15:51:52 +01:00
										 |  |  |                         }, | 
					
						
							| 
									
										
										
										
											2017-11-27 21:59:27 +01:00
										 |  |  |                         'platformTag': 'dotcom' | 
					
						
							| 
									
										
										
										
											2017-01-28 15:51:52 +01:00
										 |  |  |                     } | 
					
						
							|  |  |  |                 }).encode(), headers=headers, fatal=False) | 
					
						
							|  |  |  |             if ios_playlist: | 
					
						
							|  |  |  |                 video_data = ios_playlist.get('Playlist', {}).get('Video', {}) | 
					
						
							|  |  |  |                 ios_base_url = video_data.get('Base') | 
					
						
							|  |  |  |                 for media_file in video_data.get('MediaFiles', []): | 
					
						
							|  |  |  |                     href = media_file.get('Href') | 
					
						
							|  |  |  |                     if not href: | 
					
						
							|  |  |  |                         continue | 
					
						
							|  |  |  |                     if ios_base_url: | 
					
						
							|  |  |  |                         href = ios_base_url + href | 
					
						
							|  |  |  |                     ext = determine_ext(href) | 
					
						
							|  |  |  |                     if ext == 'm3u8': | 
					
						
							| 
									
										
										
										
											2017-04-16 21:52:45 +07:00
										 |  |  |                         formats.extend(self._extract_m3u8_formats( | 
					
						
							|  |  |  |                             href, video_id, 'mp4', entry_protocol='m3u8_native', | 
					
						
							|  |  |  |                             m3u8_id='hls', fatal=False)) | 
					
						
							| 
									
										
										
										
											2017-01-28 15:51:52 +01:00
										 |  |  |                     else: | 
					
						
							|  |  |  |                         formats.append({ | 
					
						
							|  |  |  |                             'url': href, | 
					
						
							|  |  |  |                         }) | 
					
						
							| 
									
										
										
										
											2017-12-14 04:49:07 +07:00
										 |  |  |                 subs = video_data.get('Subtitles') | 
					
						
							|  |  |  |                 if isinstance(subs, list): | 
					
						
							|  |  |  |                     for sub in subs: | 
					
						
							|  |  |  |                         if not isinstance(sub, dict): | 
					
						
							|  |  |  |                             continue | 
					
						
							| 
									
										
										
										
											2018-07-21 19:08:28 +07:00
										 |  |  |                         href = url_or_none(sub.get('Href')) | 
					
						
							|  |  |  |                         if href: | 
					
						
							| 
									
										
										
										
											2017-12-14 04:49:07 +07:00
										 |  |  |                             extract_subtitle(href) | 
					
						
							|  |  |  |                 if not info.get('duration'): | 
					
						
							|  |  |  |                     info['duration'] = parse_duration(video_data.get('Duration')) | 
					
						
							| 
									
										
										
										
											2017-01-28 15:51:52 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-14 04:49:07 +07:00
										 |  |  |         self._sort_formats(formats) | 
					
						
							| 
									
										
										
										
											2017-01-28 17:25:15 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-16 23:19:20 +07:00
										 |  |  |         info.update({ | 
					
						
							| 
									
										
										
										
											2017-01-28 15:51:52 +01:00
										 |  |  |             'id': video_id, | 
					
						
							|  |  |  |             'formats': formats, | 
					
						
							| 
									
										
										
										
											2017-01-28 17:25:15 +01:00
										 |  |  |             'subtitles': subtitles, | 
					
						
							| 
									
										
										
										
											2017-04-16 23:19:20 +07:00
										 |  |  |         }) | 
					
						
							| 
									
										
										
										
											2018-06-21 23:06:58 +07:00
										 |  |  | 
 | 
					
						
							|  |  |  |         webpage_info = self._search_json_ld(webpage, video_id, default={}) | 
					
						
							|  |  |  |         if not webpage_info.get('title'): | 
					
						
							|  |  |  |             webpage_info['title'] = self._html_search_regex( | 
					
						
							|  |  |  |                 r'(?s)<h\d+[^>]+\bclass=["\'][^>]*episode-title["\'][^>]*>([^<]+)<', | 
					
						
							|  |  |  |                 webpage, 'title', default=None) or self._og_search_title( | 
					
						
							|  |  |  |                 webpage, default=None) or self._html_search_meta( | 
					
						
							|  |  |  |                 'twitter:title', webpage, 'title', | 
					
						
							|  |  |  |                 default=None) or webpage_info['episode'] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return merge_dicts(info, webpage_info) | 
					
						
							| 
									
										
										
										
											2018-05-02 07:21:24 +07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class ITVBTCCIE(InfoExtractor): | 
					
						
							|  |  |  |     _VALID_URL = r'https?://(?:www\.)?itv\.com/btcc/(?:[^/]+/)*(?P<id>[^/?#&]+)' | 
					
						
							|  |  |  |     _TEST = { | 
					
						
							|  |  |  |         'url': 'http://www.itv.com/btcc/races/btcc-2018-all-the-action-from-brands-hatch', | 
					
						
							|  |  |  |         'info_dict': { | 
					
						
							|  |  |  |             'id': 'btcc-2018-all-the-action-from-brands-hatch', | 
					
						
							|  |  |  |             'title': 'BTCC 2018: All the action from Brands Hatch', | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |         'playlist_mincount': 9, | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     BRIGHTCOVE_URL_TEMPLATE = 'http://players.brightcove.net/1582188683001/HkiHLnNRx_default/index.html?videoId=%s' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _real_extract(self, url): | 
					
						
							|  |  |  |         playlist_id = self._match_id(url) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         webpage = self._download_webpage(url, playlist_id) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         entries = [ | 
					
						
							|  |  |  |             self.url_result( | 
					
						
							|  |  |  |                 smuggle_url(self.BRIGHTCOVE_URL_TEMPLATE % video_id, { | 
					
						
							|  |  |  |                     # ITV does not like some GB IP ranges, so here are some | 
					
						
							|  |  |  |                     # IP blocks it accepts | 
					
						
							|  |  |  |                     'geo_ip_blocks': [ | 
					
						
							|  |  |  |                         '193.113.0.0/16', '54.36.162.0/23', '159.65.16.0/21' | 
					
						
							|  |  |  |                     ], | 
					
						
							|  |  |  |                     'referrer': url, | 
					
						
							|  |  |  |                 }), | 
					
						
							|  |  |  |                 ie=BrightcoveNewIE.ie_key(), video_id=video_id) | 
					
						
							|  |  |  |             for video_id in re.findall(r'data-video-id=["\'](\d+)', webpage)] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         title = self._og_search_title(webpage, fatal=False) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return self.playlist_result(entries, playlist_id, title) |