2 from __future__ 
import unicode_literals
 
   6 from .common 
import InfoExtractor
 
  10     compat_urllib_parse_unquote
, 
  11     compat_urllib_parse_urlparse
, 
  25 from .periscope 
import ( 
  31 class TwitterBaseIE(InfoExtractor
): 
  32     _API_BASE 
= 'https://api.twitter.com/1.1/' 
  33     _BASE_REGEX 
= r
'https?://(?:(?:www|m(?:obile)?)\.)?twitter\.com/' 
  36     def _extract_variant_formats(self
, variant
, video_id
): 
  37         variant_url 
= variant
.get('url') 
  40         elif '.m3u8' in variant_url
: 
  41             return self
._extract
_m
3u8_formats
( 
  42                 variant_url
, video_id
, 'mp4', 'm3u8_native', 
  43                 m3u8_id
='hls', fatal
=False) 
  45             tbr 
= int_or_none(dict_get(variant
, ('bitrate', 'bit_rate')), 1000) or None 
  48                 'format_id': 'http' + ('-%d' % tbr 
if tbr 
else ''), 
  51             self
._search
_dimensions
_in
_video
_url
(f
, variant_url
) 
  54     def _extract_formats_from_vmap_url(self
, vmap_url
, video_id
): 
  55         vmap_data 
= self
._download
_xml
(vmap_url
, video_id
) 
  58         for video_variant 
in vmap_data
.findall('.//{http://twitter.com/schema/videoVMapV2.xsd}videoVariant'): 
  59             video_variant
.attrib
['url'] = compat_urllib_parse_unquote( 
  60                 video_variant
.attrib
['url']) 
  61             urls
.append(video_variant
.attrib
['url']) 
  62             formats
.extend(self
._extract
_variant
_formats
( 
  63                 video_variant
.attrib
, video_id
)) 
  64         video_url 
= strip_or_none(xpath_text(vmap_data
, './/MediaFile')) 
  65         if video_url 
not in urls
: 
  66             formats
.extend(self
._extract
_variant
_formats
({'url': video_url
}, video_id
)) 
  70     def _search_dimensions_in_video_url(a_format
, video_url
): 
  71         m 
= re
.search(r
'/(?P<width>\d+)x(?P<height>\d+)/', video_url
) 
  74                 'width': int(m
.group('width')), 
  75                 'height': int(m
.group('height')), 
  78     def _call_api(self
, path
, video_id
, query
={}): 
  80             'Authorization': 'Bearer AAAAAAAAAAAAAAAAAAAAAPYXBAAAAAAACLXUNDekMxqa8h%2F40K4moUkGsoc%3DTYfbDKbT3jJPCEVnMYqilB28NHfOPqkca3qaAxGfsyKCs0wRbw', 
  82         if not self
._GUEST
_TOKEN
: 
  83             self
._GUEST
_TOKEN 
= self
._download
_json
( 
  84                 self
._API
_BASE 
+ 'guest/activate.json', video_id
, 
  85                 'Downloading guest token', data
=b
'', 
  86                 headers
=headers
)['guest_token'] 
  87         headers
['x-guest-token'] = self
._GUEST
_TOKEN
 
  89             return self
._download
_json
( 
  90                 self
._API
_BASE 
+ path
, video_id
, headers
=headers
, query
=query
) 
  91         except ExtractorError 
as e
: 
  92             if isinstance(e
.cause
, compat_HTTPError
) and e
.cause
.code 
== 403: 
  93                 raise ExtractorError(self
._parse
_json
( 
  94                     e
.cause
.read().decode(), 
  95                     video_id
)['errors'][0]['message'], expected
=True) 
  99 class TwitterCardIE(InfoExtractor
): 
 100     IE_NAME 
= 'twitter:card' 
 101     _VALID_URL 
= TwitterBaseIE
._BASE
_REGEX 
+ r
'i/(?:cards/tfw/v1|videos(?:/tweet)?)/(?P<id>\d+)' 
 104             'url': 'https://twitter.com/i/cards/tfw/v1/560070183650213889', 
 105             # MD5 checksums are different in different places 
 107                 'id': '560070183650213889', 
 109                 'title': "Twitter - You can now shoot, edit and share video on Twitter. Capture life's most moving moments from your perspective.", 
 110                 'description': 'md5:18d3e24bb4f6e5007487dd546e53bd96', 
 111                 'uploader': 'Twitter', 
 112                 'uploader_id': 'Twitter', 
 113                 'thumbnail': r
're:^https?://.*\.jpg', 
 115                 'timestamp': 1422366112, 
 116                 'upload_date': '20150127', 
 120             'url': 'https://twitter.com/i/cards/tfw/v1/623160978427936768', 
 121             'md5': '7137eca597f72b9abbe61e5ae0161399', 
 123                 'id': '623160978427936768', 
 125                 'title': "NASA - Fly over Pluto's icy Norgay Mountains and Sputnik Plain in this @NASANewHorizons #PlutoFlyby video.", 
 126                 'description': "Fly over Pluto's icy Norgay Mountains and Sputnik Plain in this @NASANewHorizons #PlutoFlyby video. https://t.co/BJYgOjSeGA", 
 128                 'uploader_id': 'NASA', 
 129                 'timestamp': 1437408129, 
 130                 'upload_date': '20150720', 
 134             'url': 'https://twitter.com/i/cards/tfw/v1/654001591733886977', 
 135             'md5': 'b6d9683dd3f48e340ded81c0e917ad46', 
 139                 'title': 'Ubuntu 11.10 Overview', 
 140                 'description': 'md5:a831e97fa384863d6e26ce48d1c43376', 
 141                 'upload_date': '20111013', 
 142                 'uploader': 'OMG! UBUNTU!', 
 143                 'uploader_id': 'omgubuntu', 
 145             'add_ie': ['Youtube'], 
 148             'url': 'https://twitter.com/i/cards/tfw/v1/665289828897005568', 
 149             'md5': '6dabeaca9e68cbb71c99c322a4b42a11', 
 153                 'upload_date': '20151113', 
 154                 'uploader_id': '1189339351084113920', 
 155                 'uploader': 'ArsenalTerje', 
 156                 'title': 'Vine by ArsenalTerje', 
 157                 'timestamp': 1447451307, 
 161             'url': 'https://twitter.com/i/videos/tweet/705235433198714880', 
 162             'md5': '884812a2adc8aaf6fe52b15ccbfa3b88', 
 164                 'id': '705235433198714880', 
 166                 'title': "Brent Yarina - Khalil Iverson's missed highlight dunk. And made highlight dunk. In one highlight.", 
 167                 'description': "Khalil Iverson's missed highlight dunk. And made highlight dunk. In one highlight. https://t.co/OrxcJ28Bns", 
 168                 'uploader': 'Brent Yarina', 
 169                 'uploader_id': 'BTNBrentYarina', 
 170                 'timestamp': 1456976204, 
 171                 'upload_date': '20160303', 
 173             'skip': 'This content is no longer available.', 
 175             'url': 'https://twitter.com/i/videos/752274308186120192', 
 176             'only_matching': True, 
 180     def _real_extract(self
, url
): 
 181         status_id 
= self
._match
_id
(url
) 
 182         return self
.url_result( 
 183             'https://twitter.com/statuses/' + status_id
, 
 184             TwitterIE
.ie_key(), status_id
) 
 187 class TwitterIE(TwitterBaseIE
): 
 189     _VALID_URL 
= TwitterBaseIE
._BASE
_REGEX 
+ r
'(?:(?:i/web|[^/]+)/status|statuses)/(?P<id>\d+)' 
 192         'url': 'https://twitter.com/freethenipple/status/643211948184596480', 
 194             'id': '643211948184596480', 
 196             'title': 'FREE THE NIPPLE - FTN supporters on Hollywood Blvd today!', 
 197             'thumbnail': r
're:^https?://.*\.jpg', 
 198             'description': 'FTN supporters on Hollywood Blvd today! http://t.co/c7jHH749xJ', 
 199             'uploader': 'FREE THE NIPPLE', 
 200             'uploader_id': 'freethenipple', 
 202             'timestamp': 1442188653, 
 203             'upload_date': '20150913', 
 207         'url': 'https://twitter.com/giphz/status/657991469417025536/photo/1', 
 208         'md5': 'f36dcd5fb92bf7057f155e7d927eeb42', 
 210             'id': '657991469417025536', 
 212             'title': 'Gifs - tu vai cai tu vai cai tu nao eh capaz disso tu vai cai', 
 213             'description': 'Gifs on Twitter: "tu vai cai tu vai cai tu nao eh capaz disso tu vai cai https://t.co/tM46VHFlO5"', 
 214             'thumbnail': r
're:^https?://.*\.png', 
 216             'uploader_id': 'giphz', 
 218         'expected_warnings': ['height', 'width'], 
 219         'skip': 'Account suspended', 
 221         'url': 'https://twitter.com/starwars/status/665052190608723968', 
 223             'id': '665052190608723968', 
 225             'title': 'Star Wars - A new beginning is coming December 18. Watch the official 60 second #TV spot for #StarWars: #TheForceAwakens.', 
 226             'description': 'A new beginning is coming December 18. Watch the official 60 second #TV spot for #StarWars: #TheForceAwakens. https://t.co/OkSqT2fjWJ', 
 227             'uploader_id': 'starwars', 
 228             'uploader': 'Star Wars', 
 229             'timestamp': 1447395772, 
 230             'upload_date': '20151113', 
 233         'url': 'https://twitter.com/BTNBrentYarina/status/705235433198714880', 
 235             'id': '705235433198714880', 
 237             'title': "Brent Yarina - Khalil Iverson's missed highlight dunk. And made highlight dunk. In one highlight.", 
 238             'description': "Khalil Iverson's missed highlight dunk. And made highlight dunk. In one highlight. https://t.co/OrxcJ28Bns", 
 239             'uploader_id': 'BTNBrentYarina', 
 240             'uploader': 'Brent Yarina', 
 241             'timestamp': 1456976204, 
 242             'upload_date': '20160303', 
 245             # The same video as https://twitter.com/i/videos/tweet/705235433198714880 
 246             # Test case of TwitterCardIE 
 247             'skip_download': True, 
 250         'url': 'https://twitter.com/jaydingeer/status/700207533655363584', 
 252             'id': '700207533655363584', 
 254             'title': 'simon vetugo - BEAT PROD: @suhmeduh #Damndaniel', 
 255             'description': 'BEAT PROD: @suhmeduh  https://t.co/HBrQ4AfpvZ #Damndaniel https://t.co/byBooq2ejZ', 
 256             'thumbnail': r
're:^https?://.*\.jpg', 
 257             'uploader': 'simon vetugo', 
 258             'uploader_id': 'simonvertugo', 
 260             'timestamp': 1455777459, 
 261             'upload_date': '20160218', 
 264         'url': 'https://twitter.com/Filmdrunk/status/713801302971588609', 
 265         'md5': '89a15ed345d13b86e9a5a5e051fa308a', 
 269             'title': 'Dr.Pepperć®é£²ćæę¹ #japanese #ćć« #ććÆć #é»åć¬ć³', 
 270             'uploader': 'TAKUMA', 
 271             'uploader_id': '1004126642786242560', 
 272             'timestamp': 1402826626, 
 273             'upload_date': '20140615', 
 277         'url': 'https://twitter.com/captainamerica/status/719944021058060289', 
 279             'id': '719944021058060289', 
 281             'title': 'Captain America - @King0fNerd Are you sure you made the right choice? Find out in theaters.', 
 282             'description': '@King0fNerd Are you sure you made the right choice? Find out in theaters. https://t.co/GpgYi9xMJI', 
 283             'uploader_id': 'CaptainAmerica', 
 284             'uploader': 'Captain America', 
 286             'timestamp': 1460483005, 
 287             'upload_date': '20160412', 
 290         'url': 'https://twitter.com/OPP_HSD/status/779210622571536384', 
 292             'id': '1zqKVVlkqLaKB', 
 294             'title': 'Sgt Kerry Schmidt - Ontario Provincial Police - Road rage, mischief, assault, rollover and fire in one occurrence', 
 295             'upload_date': '20160923', 
 296             'uploader_id': '1PmKqpJdOJQoY', 
 297             'uploader': 'Sgt Kerry Schmidt - Ontario Provincial Police', 
 298             'timestamp': 1474613214, 
 300         'add_ie': ['Periscope'], 
 302         # has mp4 formats via mobile API 
 303         'url': 'https://twitter.com/news_al3alm/status/852138619213144067', 
 305             'id': '852138619213144067', 
 307             'title': 'Ų¹Ų§ŁŁ
 Ų§ŁŲ£Ų®ŲØŲ§Ų± - ŁŁŁ
Ų© ŲŖŲ§Ų±ŁŲ®ŁŲ© ŲØŲ¬ŁŲ³Ų© Ų§ŁŲ¬ŁŲ§Ų³Ł Ų§ŁŲŖŲ§Ų±ŁŲ®ŁŲ©.. Ų§ŁŁŲ§Ų¦ŲØ Ų®Ų§ŁŲÆ Ł
Ų¤ŁŲ³ Ų§ŁŲ¹ŲŖŁŲØŁ ŁŁŁ
Ų¹Ų§Ų±Ų¶ŁŁ : Ų§ŲŖŁŁŲ§ Ų§ŁŁŁ .. Ų§ŁŲøŁŁ
 ŲøŁŁ
Ų§ŲŖ ŁŁŁ
 Ų§ŁŁŁŲ§Ł
Ų©', 
 308             'description': 'ŁŁŁ
Ų© ŲŖŲ§Ų±ŁŲ®ŁŲ© ŲØŲ¬ŁŲ³Ų© Ų§ŁŲ¬ŁŲ§Ų³Ł Ų§ŁŲŖŲ§Ų±ŁŲ®ŁŲ©.. Ų§ŁŁŲ§Ų¦ŲØ Ų®Ų§ŁŲÆ Ł
Ų¤ŁŲ³ Ų§ŁŲ¹ŲŖŁŲØŁ ŁŁŁ
Ų¹Ų§Ų±Ų¶ŁŁ : Ų§ŲŖŁŁŲ§ Ų§ŁŁŁ .. Ų§ŁŲøŁŁ
 ŲøŁŁ
Ų§ŲŖ ŁŁŁ
 Ų§ŁŁŁŲ§Ł
Ų©   https://t.co/xg6OhpyKfN', 
 309             'uploader': 'Ų¹Ų§ŁŁ
 Ų§ŁŲ£Ų®ŲØŲ§Ų±', 
 310             'uploader_id': 'news_al3alm', 
 312             'timestamp': 1492000653, 
 313             'upload_date': '20170412', 
 316         'url': 'https://twitter.com/i/web/status/910031516746514432', 
 318             'id': '910031516746514432', 
 320             'title': 'Préfet de Guadeloupe - [Direct] #Maria Le centre se trouve actuellement au sud de Basse-Terre. Restez confinés. Réfugiez-vous dans la pièce la + sûre.', 
 321             'thumbnail': r
're:^https?://.*\.jpg', 
 322             'description': '[Direct] #Maria Le centre se trouve actuellement au sud de Basse-Terre. Restez confinés. Réfugiez-vous dans la pièce la + sûre. https://t.co/mwx01Rs4lo', 
 323             'uploader': 'PrƩfet de Guadeloupe', 
 324             'uploader_id': 'Prefet971', 
 326             'timestamp': 1505803395, 
 327             'upload_date': '20170919', 
 330             'skip_download': True,  # requires ffmpeg 
 333         # card via api.twitter.com/1.1/videos/tweet/config 
 334         'url': 'https://twitter.com/LisPower1/status/1001551623938805763', 
 336             'id': '1001551623938805763', 
 338             'title': 're:.*?Shep is on a roll today.*?', 
 339             'thumbnail': r
're:^https?://.*\.jpg', 
 340             'description': 'md5:37b9f2ff31720cef23b2bd42ee8a0f09', 
 341             'uploader': 'Lis Power', 
 342             'uploader_id': 'LisPower1', 
 344             'timestamp': 1527623489, 
 345             'upload_date': '20180529', 
 348             'skip_download': True,  # requires ffmpeg 
 351         'url': 'https://twitter.com/foobar/status/1087791357756956680', 
 353             'id': '1087791357756956680', 
 355             'title': 'Twitter - A new is coming.  Some of you got an opt-in to try it now. Check out the emoji button, quick keyboard shortcuts, upgraded trends, advanced search, and more. Let us know your thoughts!', 
 356             'thumbnail': r
're:^https?://.*\.jpg', 
 357             'description': 'md5:6dfd341a3310fb97d80d2bf7145df976', 
 358             'uploader': 'Twitter', 
 359             'uploader_id': 'Twitter', 
 361             'timestamp': 1548184644, 
 362             'upload_date': '20190122', 
 365         # not available in Periscope 
 366         'url': 'https://twitter.com/ViviEducation/status/1136534865145286656', 
 368             'id': '1vOGwqejwoWxB', 
 370             'title': 'Vivi - Vivi founder @lior_rauchy announcing our new student feedback tool live at @EduTECH_AU #EduTECH2019', 
 372             'uploader_id': '1eVjYOLGkGrQL', 
 374         'add_ie': ['TwitterBroadcast'], 
 377         'url': 'https://twitter.com/GunB1g/status/1163218564784017422', 
 378         'only_matching': True, 
 380         # promo_video_website card 
 381         'url': 'https://twitter.com/GunB1g/status/1163218564784017422', 
 382         'only_matching': True, 
 385     def _real_extract(self
, url
): 
 386         twid 
= self
._match
_id
(url
) 
 387         status 
= self
._call
_api
( 
 388             'statuses/show/%s.json' % twid
, twid
, { 
 389                 'cards_platform': 'Web-12', 
 391                 'include_reply_count': 1, 
 392                 'include_user_entities': 0, 
 393                 'tweet_mode': 'extended', 
 396         title 
= description 
= status
['full_text'].replace('\n', ' ') 
 397         # strip  'https -_t.co_BJYgOjSeGA' junk from filenames 
 398         title 
= re
.sub(r
'\s+(https?://[^ ]+)', '', title
) 
 399         user 
= status
.get('user') or {} 
 400         uploader 
= user
.get('name') 
 402             title 
= '%s - %s' % (uploader
, title
) 
 403         uploader_id 
= user
.get('screen_name') 
 406         for hashtag 
in (try_get(status
, lambda x
: x
['entities']['hashtags'], list) or []): 
 407             hashtag_text 
= hashtag
.get('text') 
 410             tags
.append(hashtag_text
) 
 415             'description': description
, 
 416             'uploader': uploader
, 
 417             'timestamp': unified_timestamp(status
.get('created_at')), 
 418             'uploader_id': uploader_id
, 
 419             'uploader_url': 'https://twitter.com/' + uploader_id 
if uploader_id 
else None, 
 420             'like_count': int_or_none(status
.get('favorite_count')), 
 421             'repost_count': int_or_none(status
.get('retweet_count')), 
 422             'comment_count': int_or_none(status
.get('reply_count')), 
 423             'age_limit': 18 if status
.get('possibly_sensitive') else 0, 
 427         media 
= try_get(status
, lambda x
: x
['extended_entities']['media'][0]) 
 428         if media 
and media
.get('type') != 'photo': 
 429             video_info 
= media
.get('video_info') or {} 
 432             for variant 
in video_info
.get('variants', []): 
 433                 formats
.extend(self
._extract
_variant
_formats
(variant
, twid
)) 
 434             self
._sort
_formats
(formats
) 
 437             media_url 
= media
.get('media_url_https') or media
.get('media_url') 
 439                 def add_thumbnail(name
, size
): 
 442                         'url': update_url_query(media_url
, {'name': name
}), 
 443                         'width': int_or_none(size
.get('w') or size
.get('width')), 
 444                         'height': int_or_none(size
.get('h') or size
.get('height')), 
 446                 for name
, size 
in media
.get('sizes', {}).items(): 
 447                     add_thumbnail(name
, size
) 
 448                 add_thumbnail('orig', media
.get('original_info') or {}) 
 452                 'thumbnails': thumbnails
, 
 453                 'duration': float_or_none(video_info
.get('duration_millis'), 1000), 
 456             card 
= status
.get('card') 
 458                 binding_values 
= card
['binding_values'] 
 460                 def get_binding_value(k
): 
 461                     o 
= binding_values
.get(k
) or {} 
 462                     return try_get(o
, lambda x
: x
[x
['type'].lower() + '_value']) 
 464                 card_name 
= card
['name'].split(':')[-1] 
 465                 if card_name 
in ('amplify', 'promo_video_website'): 
 466                     is_amplify 
= card_name 
== 'amplify' 
 467                     vmap_url 
= get_binding_value('amplify_url_vmap') if is_amplify 
else get_binding_value('player_stream_url') 
 468                     content_id 
= get_binding_value('%s_content_id' % (card_name 
if is_amplify 
else 'player')) 
 469                     formats 
= self
._extract
_formats
_from
_vmap
_url
(vmap_url
, content_id 
or twid
) 
 470                     self
._sort
_formats
(formats
) 
 473                     for suffix 
in ('_small', '', '_large', '_x_large', '_original'): 
 474                         image 
= get_binding_value('player_image' + suffix
) or {} 
 475                         image_url 
= image
.get('url') 
 476                         if not image_url 
or '/player-placeholder' in image_url
: 
 479                             'id': suffix
[1:] if suffix 
else 'medium', 
 481                             'width': int_or_none(image
.get('width')), 
 482                             'height': int_or_none(image
.get('height')), 
 487                         'thumbnails': thumbnails
, 
 488                         'duration': int_or_none(get_binding_value( 
 489                             'content_duration_seconds')), 
 491                 elif card_name 
== 'player': 
 494                         'url': get_binding_value('player_url'), 
 496                 elif card_name 
== 'periscope_broadcast': 
 499                         'url': get_binding_value('url') or get_binding_value('player_url'), 
 500                         'ie_key': PeriscopeIE
.ie_key(), 
 502                 elif card_name 
== 'broadcast': 
 505                         'url': get_binding_value('broadcast_url'), 
 506                         'ie_key': TwitterBroadcastIE
.ie_key(), 
 509                     raise ExtractorError('Unsupported Twitter Card.') 
 511                 expanded_url 
= try_get(status
, lambda x
: x
['entities']['urls'][0]['expanded_url']) 
 513                     raise ExtractorError("There's no video in this tweet.") 
 521 class TwitterAmplifyIE(TwitterBaseIE
): 
 522     IE_NAME 
= 'twitter:amplify' 
 523     _VALID_URL 
= r
'https?://amp\.twimg\.com/v/(?P<id>[0-9a-f\-]{36})' 
 526         'url': 'https://amp.twimg.com/v/0ba0c3c7-0af3-4c0a-bed5-7efd1ffa2951', 
 527         'md5': '7df102d0b9fd7066b86f3159f8e81bf6', 
 529             'id': '0ba0c3c7-0af3-4c0a-bed5-7efd1ffa2951', 
 531             'title': 'Twitter Video', 
 532             'thumbnail': 're:^https?://.*', 
 536     def _real_extract(self
, url
): 
 537         video_id 
= self
._match
_id
(url
) 
 538         webpage 
= self
._download
_webpage
(url
, video_id
) 
 540         vmap_url 
= self
._html
_search
_meta
( 
 541             'twitter:amplify:vmap', webpage
, 'vmap url') 
 542         formats 
= self
._extract
_formats
_from
_vmap
_url
(vmap_url
, video_id
) 
 545         thumbnail 
= self
._html
_search
_meta
( 
 546             'twitter:image:src', webpage
, 'thumbnail', fatal
=False) 
 548         def _find_dimension(target
): 
 549             w 
= int_or_none(self
._html
_search
_meta
( 
 550                 'twitter:%s:width' % target
, webpage
, fatal
=False)) 
 551             h 
= int_or_none(self
._html
_search
_meta
( 
 552                 'twitter:%s:height' % target
, webpage
, fatal
=False)) 
 556             thumbnail_w
, thumbnail_h 
= _find_dimension('image') 
 559                 'width': thumbnail_w
, 
 560                 'height': thumbnail_h
, 
 563         video_w
, video_h 
= _find_dimension('player') 
 571             'title': 'Twitter Video', 
 573             'thumbnails': thumbnails
, 
 577 class TwitterBroadcastIE(TwitterBaseIE
, PeriscopeBaseIE
): 
 578     IE_NAME 
= 'twitter:broadcast' 
 579     _VALID_URL 
= TwitterBaseIE
._BASE
_REGEX 
+ r
'i/broadcasts/(?P<id>[0-9a-zA-Z]{13})' 
 581     def _real_extract(self
, url
): 
 582         broadcast_id 
= self
._match
_id
(url
) 
 583         broadcast 
= self
._call
_api
( 
 584             'broadcasts/show.json', broadcast_id
, 
 585             {'ids': broadcast_id
})['broadcasts'][broadcast_id
] 
 586         info 
= self
._parse
_broadcast
_data
(broadcast
, broadcast_id
) 
 587         media_key 
= broadcast
['media_key'] 
 588         source 
= self
._call
_api
( 
 589             'live_video_stream/status/' + media_key
, media_key
)['source'] 
 590         m3u8_url 
= source
.get('noRedirectPlaybackUrl') or source
['location'] 
 591         if '/live_video_stream/geoblocked/' in m3u8_url
: 
 592             self
.raise_geo_restricted() 
 593         m3u8_id 
= compat_parse_qs(compat_urllib_parse_urlparse( 
 594             m3u8_url
).query
).get('type', [None])[0] 
 595         state
, width
, height 
= self
._extract
_common
_format
_info
(broadcast
) 
 596         info
['formats'] = self
._extract
_pscp
_m
3u8_formats
( 
 597             m3u8_url
, broadcast_id
, m3u8_id
, state
, width
, height
)