| 
									
										
										
										
											2016-10-02 13:39:18 +02:00
										 |  |  | # coding: utf-8 | 
					
						
							| 
									
										
										
										
											2014-01-17 03:15:09 +01:00
										 |  |  | from __future__ import unicode_literals | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-05-15 02:17:22 +08:00
										 |  |  | import json | 
					
						
							| 
									
										
										
										
											2013-06-23 22:31:50 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-30 13:21:07 +01:00
										 |  |  | from .turner import TurnerBaseIE | 
					
						
							| 
									
										
										
										
											2015-04-09 23:54:53 +03:00
										 |  |  | from ..utils import ( | 
					
						
							| 
									
										
										
										
											2018-05-10 08:19:32 +01:00
										 |  |  |     determine_ext, | 
					
						
							| 
									
										
										
										
											2015-04-09 23:54:53 +03:00
										 |  |  |     ExtractorError, | 
					
						
							| 
									
										
										
										
											2018-05-10 08:19:32 +01:00
										 |  |  |     int_or_none, | 
					
						
							|  |  |  |     mimetype2ext, | 
					
						
							|  |  |  |     parse_duration, | 
					
						
							|  |  |  |     parse_iso8601, | 
					
						
							| 
									
										
										
										
											2015-04-09 23:54:53 +03:00
										 |  |  |     qualities, | 
					
						
							|  |  |  | ) | 
					
						
							| 
									
										
										
										
											2013-06-23 22:31:50 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-30 13:21:07 +01:00
										 |  |  | class TeamcocoIE(TurnerBaseIE): | 
					
						
							| 
									
										
										
										
											2019-04-05 08:26:04 +01:00
										 |  |  |     _VALID_URL = r'https?://(?:\w+\.)?teamcoco\.com/(?P<id>([^/]+/)*[^/?#]+)' | 
					
						
							| 
									
										
										
										
											2014-04-04 13:52:35 -04:00
										 |  |  |     _TESTS = [ | 
					
						
							| 
									
										
										
										
											2014-11-23 21:39:15 +01:00
										 |  |  |         { | 
					
						
							| 
									
										
										
										
											2018-05-10 08:19:32 +01:00
										 |  |  |             'url': 'http://teamcoco.com/video/mary-kay-remote', | 
					
						
							|  |  |  |             'md5': '55d532f81992f5c92046ad02fec34d7d', | 
					
						
							| 
									
										
										
										
											2014-11-23 21:39:15 +01:00
										 |  |  |             'info_dict': { | 
					
						
							| 
									
										
										
										
											2015-02-01 15:00:54 +01:00
										 |  |  |                 'id': '80187', | 
					
						
							|  |  |  |                 'ext': 'mp4', | 
					
						
							| 
									
										
										
										
											2014-11-23 21:39:15 +01:00
										 |  |  |                 'title': 'Conan Becomes A Mary Kay Beauty Consultant', | 
					
						
							| 
									
										
										
										
											2015-02-08 17:45:38 +02:00
										 |  |  |                 'description': 'Mary Kay is perhaps the most trusted name in female beauty, so of course Conan is a natural choice to sell their products.', | 
					
						
							| 
									
										
										
										
											2018-05-10 08:19:32 +01:00
										 |  |  |                 'duration': 495.0, | 
					
						
							|  |  |  |                 'upload_date': '20140402', | 
					
						
							|  |  |  |                 'timestamp': 1396407600, | 
					
						
							| 
									
										
										
										
											2014-11-23 21:39:15 +01:00
										 |  |  |             } | 
					
						
							|  |  |  |         }, { | 
					
						
							|  |  |  |             'url': 'http://teamcoco.com/video/louis-ck-interview-george-w-bush', | 
					
						
							|  |  |  |             'md5': 'cde9ba0fa3506f5f017ce11ead928f9a', | 
					
						
							|  |  |  |             'info_dict': { | 
					
						
							| 
									
										
										
										
											2015-02-01 15:00:54 +01:00
										 |  |  |                 'id': '19705', | 
					
						
							|  |  |  |                 'ext': 'mp4', | 
					
						
							| 
									
										
										
										
											2015-02-21 22:19:39 +02:00
										 |  |  |                 'description': 'Louis C.K. got starstruck by George W. Bush, so what? Part one.', | 
					
						
							|  |  |  |                 'title': 'Louis C.K. Interview Pt. 1 11/3/11', | 
					
						
							| 
									
										
										
										
											2015-04-10 02:03:38 +03:00
										 |  |  |                 'duration': 288, | 
					
						
							| 
									
										
										
										
											2018-05-10 08:19:32 +01:00
										 |  |  |                 'upload_date': '20111104', | 
					
						
							|  |  |  |                 'timestamp': 1320405840, | 
					
						
							| 
									
										
										
										
											2014-11-23 21:39:15 +01:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2015-04-15 19:56:21 +08:00
										 |  |  |         }, { | 
					
						
							|  |  |  |             'url': 'http://teamcoco.com/video/timothy-olyphant-drinking-whiskey', | 
					
						
							|  |  |  |             'info_dict': { | 
					
						
							|  |  |  |                 'id': '88748', | 
					
						
							|  |  |  |                 'ext': 'mp4', | 
					
						
							|  |  |  |                 'title': 'Timothy Olyphant Raises A Toast To “Justified”', | 
					
						
							|  |  |  |                 'description': 'md5:15501f23f020e793aeca761205e42c24', | 
					
						
							| 
									
										
										
										
											2018-05-10 08:19:32 +01:00
										 |  |  |                 'upload_date': '20150415', | 
					
						
							|  |  |  |                 'timestamp': 1429088400, | 
					
						
							| 
									
										
										
										
											2015-04-15 19:56:21 +08:00
										 |  |  |             }, | 
					
						
							|  |  |  |             'params': { | 
					
						
							|  |  |  |                 'skip_download': True,  # m3u8 downloads | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2015-06-05 22:55:29 +08:00
										 |  |  |         }, { | 
					
						
							|  |  |  |             'url': 'http://teamcoco.com/video/full-episode-mon-6-1-joel-mchale-jake-tapper-and-musical-guest-courtney-barnett?playlist=x;eyJ0eXBlIjoidGFnIiwiaWQiOjl9', | 
					
						
							|  |  |  |             'info_dict': { | 
					
						
							|  |  |  |                 'id': '89341', | 
					
						
							|  |  |  |                 'ext': 'mp4', | 
					
						
							|  |  |  |                 'title': 'Full Episode - Mon. 6/1 - Joel McHale, Jake Tapper, And Musical Guest Courtney Barnett', | 
					
						
							|  |  |  |                 'description': 'Guests: Joel McHale, Jake Tapper, And Musical Guest Courtney Barnett', | 
					
						
							|  |  |  |             }, | 
					
						
							|  |  |  |             'params': { | 
					
						
							|  |  |  |                 'skip_download': True,  # m3u8 downloads | 
					
						
							| 
									
										
										
										
											2018-05-10 08:19:32 +01:00
										 |  |  |             }, | 
					
						
							|  |  |  |             'skip': 'This video is no longer available.', | 
					
						
							| 
									
										
										
										
											2018-05-19 12:19:05 +01:00
										 |  |  |         }, { | 
					
						
							|  |  |  |             'url': 'http://teamcoco.com/video/the-conan-audiencey-awards-for-04/25/18', | 
					
						
							|  |  |  |             'only_matching': True, | 
					
						
							| 
									
										
										
										
											2018-05-19 13:05:51 +01:00
										 |  |  |         }, { | 
					
						
							|  |  |  |             'url': 'http://teamcoco.com/italy/conan-jordan-schlansky-hit-the-streets-of-florence', | 
					
						
							|  |  |  |             'only_matching': True, | 
					
						
							|  |  |  |         }, { | 
					
						
							|  |  |  |             'url': 'http://teamcoco.com/haiti/conan-s-haitian-history-lesson', | 
					
						
							|  |  |  |             'only_matching': True, | 
					
						
							|  |  |  |         }, { | 
					
						
							|  |  |  |             'url': 'http://teamcoco.com/israel/conan-hits-the-streets-beaches-of-tel-aviv', | 
					
						
							|  |  |  |             'only_matching': True, | 
					
						
							| 
									
										
										
										
											2019-04-05 08:26:04 +01:00
										 |  |  |         }, { | 
					
						
							|  |  |  |             'url': 'https://conan25.teamcoco.com/video/ice-cube-kevin-hart-conan-share-lyft', | 
					
						
							|  |  |  |             'only_matching': True, | 
					
						
							| 
									
										
										
										
											2014-04-04 13:52:35 -04:00
										 |  |  |         } | 
					
						
							|  |  |  |     ] | 
					
						
							| 
									
										
										
										
											2013-06-23 22:31:50 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-10 08:19:32 +01:00
										 |  |  |     def _graphql_call(self, query_template, object_type, object_id): | 
					
						
							|  |  |  |         find_object = 'find' + object_type | 
					
						
							|  |  |  |         return self._download_json( | 
					
						
							| 
									
										
										
										
											2019-04-05 08:26:04 +01:00
										 |  |  |             'https://teamcoco.com/graphql', object_id, data=json.dumps({ | 
					
						
							| 
									
										
										
										
											2018-05-10 08:19:32 +01:00
										 |  |  |                 'query': query_template % (find_object, object_id) | 
					
						
							| 
									
										
										
										
											2019-04-05 08:26:04 +01:00
										 |  |  |             }).encode(), headers={ | 
					
						
							|  |  |  |                 'Content-Type': 'application/json', | 
					
						
							|  |  |  |             })['data'][find_object] | 
					
						
							| 
									
										
										
										
											2014-04-07 15:24:12 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-10 08:19:32 +01:00
										 |  |  |     def _real_extract(self, url): | 
					
						
							|  |  |  |         display_id = self._match_id(url) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         response = self._graphql_call('''{
 | 
					
						
							| 
									
										
										
										
											2018-05-19 13:05:51 +01:00
										 |  |  |   %s(slug: "%s") { | 
					
						
							| 
									
										
										
										
											2018-05-10 08:19:32 +01:00
										 |  |  |     ... on RecordSlug { | 
					
						
							|  |  |  |       record { | 
					
						
							|  |  |  |         id | 
					
						
							|  |  |  |         title | 
					
						
							|  |  |  |         teaser | 
					
						
							|  |  |  |         publishOn | 
					
						
							|  |  |  |         thumb { | 
					
						
							|  |  |  |           preview | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2018-05-19 13:05:51 +01:00
										 |  |  |         file { | 
					
						
							|  |  |  |           url | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2018-05-10 08:19:32 +01:00
										 |  |  |         tags { | 
					
						
							|  |  |  |           name | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         duration | 
					
						
							| 
									
										
										
										
											2018-05-30 13:21:07 +01:00
										 |  |  |         turnerMediaId | 
					
						
							|  |  |  |         turnerMediaAuthToken | 
					
						
							| 
									
										
										
										
											2018-05-10 08:19:32 +01:00
										 |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     ... on NotFoundSlug { | 
					
						
							|  |  |  |       status | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | }''', 'Slug', display_id)
 | 
					
						
							|  |  |  |         if response.get('status'): | 
					
						
							|  |  |  |             raise ExtractorError('This video is no longer available.', expected=True) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         record = response['record'] | 
					
						
							|  |  |  |         video_id = record['id'] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-30 13:21:07 +01:00
										 |  |  |         info = { | 
					
						
							| 
									
										
										
										
											2014-01-17 03:15:09 +01:00
										 |  |  |             'id': video_id, | 
					
						
							| 
									
										
										
										
											2014-04-07 15:24:12 +02:00
										 |  |  |             'display_id': display_id, | 
					
						
							| 
									
										
										
										
											2018-05-10 08:19:32 +01:00
										 |  |  |             'title': record['title'], | 
					
						
							|  |  |  |             'thumbnail': record.get('thumb', {}).get('preview'), | 
					
						
							|  |  |  |             'description': record.get('teaser'), | 
					
						
							|  |  |  |             'duration': parse_duration(record.get('duration')), | 
					
						
							|  |  |  |             'timestamp': parse_iso8601(record.get('publishOn')), | 
					
						
							| 
									
										
										
										
											2013-11-03 17:48:12 +01:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2018-05-30 13:21:07 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         media_id = record.get('turnerMediaId') | 
					
						
							|  |  |  |         if media_id: | 
					
						
							|  |  |  |             self._initialize_geo_bypass({ | 
					
						
							|  |  |  |                 'countries': ['US'], | 
					
						
							|  |  |  |             }) | 
					
						
							|  |  |  |             info.update(self._extract_ngtv_info(media_id, { | 
					
						
							|  |  |  |                 'accessToken': record['turnerMediaAuthToken'], | 
					
						
							|  |  |  |                 'accessTokenType': 'jws', | 
					
						
							|  |  |  |             })) | 
					
						
							|  |  |  |         else: | 
					
						
							| 
									
										
										
										
											2019-04-05 08:26:04 +01:00
										 |  |  |             d = self._download_json( | 
					
						
							|  |  |  |                 'https://teamcoco.com/_truman/d/' + video_id, | 
					
						
							|  |  |  |                 video_id, fatal=False) or {} | 
					
						
							|  |  |  |             video_sources = d.get('meta') or {} | 
					
						
							|  |  |  |             if not video_sources: | 
					
						
							|  |  |  |                 video_sources = self._graphql_call('''{
 | 
					
						
							| 
									
										
										
										
											2018-05-30 13:21:07 +01:00
										 |  |  |   %s(id: "%s") { | 
					
						
							|  |  |  |     src | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | }''', 'RecordVideoSource', video_id) or {}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             formats = [] | 
					
						
							|  |  |  |             get_quality = qualities(['low', 'sd', 'hd', 'uhd']) | 
					
						
							|  |  |  |             for format_id, src in video_sources.get('src', {}).items(): | 
					
						
							|  |  |  |                 if not isinstance(src, dict): | 
					
						
							|  |  |  |                     continue | 
					
						
							|  |  |  |                 src_url = src.get('src') | 
					
						
							|  |  |  |                 if not src_url: | 
					
						
							|  |  |  |                     continue | 
					
						
							|  |  |  |                 ext = determine_ext(src_url, mimetype2ext(src.get('type'))) | 
					
						
							|  |  |  |                 if format_id == 'hls' or ext == 'm3u8': | 
					
						
							|  |  |  |                     # compat_urllib_parse.urljoin does not work here | 
					
						
							|  |  |  |                     if src_url.startswith('/'): | 
					
						
							|  |  |  |                         src_url = 'http://ht.cdn.turner.com/tbs/big/teamcoco' + src_url | 
					
						
							|  |  |  |                     formats.extend(self._extract_m3u8_formats( | 
					
						
							|  |  |  |                         src_url, video_id, 'mp4', m3u8_id=format_id, fatal=False)) | 
					
						
							|  |  |  |                 else: | 
					
						
							|  |  |  |                     if src_url.startswith('/mp4:protected/'): | 
					
						
							|  |  |  |                         # TODO Correct extraction for these files | 
					
						
							|  |  |  |                         continue | 
					
						
							|  |  |  |                     tbr = int_or_none(self._search_regex( | 
					
						
							|  |  |  |                         r'(\d+)k\.mp4', src_url, 'tbr', default=None)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     formats.append({ | 
					
						
							|  |  |  |                         'url': src_url, | 
					
						
							|  |  |  |                         'ext': ext, | 
					
						
							|  |  |  |                         'tbr': tbr, | 
					
						
							|  |  |  |                         'format_id': format_id, | 
					
						
							|  |  |  |                         'quality': get_quality(format_id), | 
					
						
							|  |  |  |                     }) | 
					
						
							|  |  |  |             if not formats: | 
					
						
							|  |  |  |                 formats = self._extract_m3u8_formats( | 
					
						
							|  |  |  |                     record['file']['url'], video_id, 'mp4', fatal=False) | 
					
						
							|  |  |  |             self._sort_formats(formats) | 
					
						
							|  |  |  |             info['formats'] = formats | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return info |