5 from .common
import InfoExtractor
6 from .theplatform
import ThePlatformIE
7 from .adobepass
import AdobePassIE
8 from ..compat
import compat_urllib_parse_unquote
27 class NBCIE(ThePlatformIE
):
28 _VALID_URL
= r
'https?(?P<permalink>://(?:www\.)?nbc\.com/(?:classic-tv/)?[^/]+/video/[^/]+/(?P<id>n?\d+))'
32 'url': 'http://www.nbc.com/the-tonight-show/video/jimmy-fallon-surprises-fans-at-ben-jerrys/2848237',
36 'title': 'Jimmy Fallon Surprises Fans at Ben & Jerry\'s',
37 'description': 'Jimmy gives out free scoops of his new "Tonight Dough" ice cream flavor by surprising customers at the Ben & Jerry\'s scoop shop.',
38 'timestamp': 1424246400,
39 'upload_date': '20150218',
40 'uploader': 'NBCU-COM',
44 'skip_download': True,
48 'url': 'http://www.nbc.com/saturday-night-live/video/star-wars-teaser/2832821',
52 'title': 'Star Wars Teaser',
53 'description': 'md5:0b40f9cbde5b671a7ff62fceccc4f442',
54 'timestamp': 1417852800,
55 'upload_date': '20141206',
56 'uploader': 'NBCU-COM',
60 'skip_download': True,
62 'skip': 'Only works from US',
65 # HLS streams requires the 'hdnea3' cookie
66 'url': 'http://www.nbc.com/Kings/video/goliath/n1806',
68 'id': '101528f5a9e8127b107e98c5e6ce4638',
71 'description': 'When an unknown soldier saves the life of the King\'s son in battle, he\'s thrust into the limelight and politics of the kingdom.',
72 'timestamp': 1237100400,
73 'upload_date': '20090315',
74 'uploader': 'NBCU-COM',
77 'skip_download': True,
79 'skip': 'Only works from US',
82 'url': 'https://www.nbc.com/classic-tv/charles-in-charge/video/charles-in-charge-pilot/n3310',
83 'only_matching': True,
87 'url': 'https://www.nbc.com/up-all-night/video/day-after-valentine%27s-day/n2189',
88 'only_matching': True,
92 def _real_extract(self
, url
):
93 permalink
, video_id
= self
._match
_valid
_url
(url
).groups()
94 permalink
= 'http' + compat_urllib_parse_unquote(permalink
)
95 video_data
= self
._download
_json
(
96 'https://friendship.nbc.co/v2/graphql', video_id
, query
={
97 'query': '''query bonanzaPage(
98 $app: NBCUBrands! = nbc
101 $platform: SupportedPlatforms! = web
102 $type: EntityPageType! = VIDEO
114 ... on VideoPageData {
130 'variables': json
.dumps({
135 })['data']['bonanzaPage']['metadata']
140 video_id
= video_data
['mpxGuid']
141 tp_path
= 'NnzsPC/media/guid/%s/%s' % (video_data
.get('mpxAccountId') or '2410887629', video_id
)
142 tpm
= self
._download
_theplatform
_metadata
(tp_path
, video_id
)
143 title
= tpm
.get('title') or video_data
.get('secondaryTitle')
144 if video_data
.get('locked'):
145 resource
= self
._get
_mvpd
_resource
(
146 video_data
.get('resourceId') or 'nbcentertainment',
147 title
, video_id
, video_data
.get('rating'))
148 query
['auth'] = self
._extract
_mvpd
_auth
(
149 url
, video_id
, 'nbcentertainment', resource
)
150 theplatform_url
= smuggle_url(update_url_query(
151 'http://link.theplatform.com/s/NnzsPC/media/guid/%s/%s' % (video_data
.get('mpxAccountId') or '2410887629', video_id
),
152 query
), {'force_smil_url': True}
)
154 # Empty string or 0 can be valid values for these. So the check must be `is None`
155 description
= video_data
.get('description')
156 if description
is None:
157 description
= tpm
.get('description')
158 episode_number
= int_or_none(video_data
.get('episodeNumber'))
159 if episode_number
is None:
160 episode_number
= int_or_none(tpm
.get('nbcu$airOrder'))
161 rating
= video_data
.get('rating')
163 try_get(tpm
, lambda x
: x
['ratings'][0]['rating'])
164 season_number
= int_or_none(video_data
.get('seasonNumber'))
165 if season_number
is None:
166 season_number
= int_or_none(tpm
.get('nbcu$seasonNumber'))
167 series
= video_data
.get('seriesShortTitle')
169 series
= tpm
.get('nbcu$seriesShortTitle')
170 tags
= video_data
.get('keywords')
171 if tags
is None or len(tags
) == 0:
172 tags
= tpm
.get('keywords')
175 '_type': 'url_transparent',
176 'age_limit': parse_age_limit(rating
),
177 'description': description
,
179 'episode_number': episode_number
,
181 'ie_key': 'ThePlatform',
182 'season_number': season_number
,
186 'url': theplatform_url
,
190 class NBCSportsVPlayerIE(InfoExtractor
):
191 _VALID_URL_BASE
= r
'https?://(?:vplayer\.nbcsports\.com|(?:www\.)?nbcsports\.com/vplayer)/'
192 _VALID_URL
= _VALID_URL_BASE
+ r
'(?:[^/]+/)+(?P<id>[0-9a-zA-Z_]+)'
193 _EMBED_REGEX
= [r
'(?:iframe[^>]+|var video|div[^>]+data-(?:mpx-)?)[sS]rc\s?=\s?"(?P<url>%s[^\"]+)' % _VALID_URL_BASE
]
196 'url': 'https://vplayer.nbcsports.com/p/BxmELC/nbcsports_embed/select/9CsDKds0kvHI',
198 'id': '9CsDKds0kvHI',
200 'description': 'md5:df390f70a9ba7c95ff1daace988f0d8d',
201 'title': 'Tyler Kalinoski hits buzzer-beater to lift Davidson',
202 'timestamp': 1426270238,
203 'upload_date': '20150313',
204 'uploader': 'NBCU-SPORTS',
207 'thumbnail': r
're:^https?://.*\.jpg$'
210 'url': 'https://vplayer.nbcsports.com/p/BxmELC/nbcsports_embed/select/media/PEgOtlNcC_y2',
211 'only_matching': True,
213 'url': 'https://www.nbcsports.com/vplayer/p/BxmELC/nbcsports/select/PHJSaFWbrTY9?form=html&autoPlay=true',
214 'only_matching': True,
217 def _real_extract(self
, url
):
218 video_id
= self
._match
_id
(url
)
219 webpage
= self
._download
_webpage
(url
, video_id
)
220 theplatform_url
= self
._html
_search
_regex
(r
'tp:releaseUrl="(.+?)"', webpage
, 'url')
221 return self
.url_result(theplatform_url
, 'ThePlatform')
224 class NBCSportsIE(InfoExtractor
):
225 _VALID_URL
= r
'https?://(?:www\.)?nbcsports\.com//?(?!vplayer/)(?:[^/]+/)+(?P<id>[0-9a-z-]+)'
229 'url': 'http://www.nbcsports.com//college-basketball/ncaab/tom-izzo-michigan-st-has-so-much-respect-duke',
231 'id': 'PHJSaFWbrTY9',
233 'title': 'Tom Izzo, Michigan St. has \'so much respect\' for Duke',
234 'description': 'md5:ecb459c9d59e0766ac9c7d5d0eda8113',
235 'uploader': 'NBCU-SPORTS',
236 'upload_date': '20150330',
237 'timestamp': 1427726529,
239 'thumbnail': 'https://hdliveextra-a.akamaihd.net/HD/image_sports/NBCU_Sports_Group_-_nbcsports/253/303/izzodps.jpg',
244 'url': 'https://www.nbcsports.com/philadelphia/philadelphia-phillies/bruce-bochy-hector-neris-hes-idiot',
245 'only_matching': True,
248 'url': 'https://www.nbcsports.com/boston/video/report-card-pats-secondary-no-match-josh-allen',
249 'only_matching': True,
252 def _real_extract(self
, url
):
253 video_id
= self
._match
_id
(url
)
254 webpage
= self
._download
_webpage
(url
, video_id
)
255 return self
.url_result(
256 NBCSportsVPlayerIE
._extract
_url
(webpage
), 'NBCSportsVPlayer')
259 class NBCSportsStreamIE(AdobePassIE
):
260 _VALID_URL
= r
'https?://stream\.nbcsports\.com/.+?\bpid=(?P<id>\d+)'
262 'url': 'http://stream.nbcsports.com/nbcsn/generic?pid=206559',
266 'title': 'Amgen Tour of California Women\'s Recap',
267 'description': 'md5:66520066b3b5281ada7698d0ea2aa894',
271 'skip_download': True,
273 'skip': 'Requires Adobe Pass Authentication',
276 def _real_extract(self
, url
):
277 video_id
= self
._match
_id
(url
)
278 live_source
= self
._download
_json
(
279 'http://stream.nbcsports.com/data/live_sources_%s.json' % video_id
,
281 video_source
= live_source
['videoSources'][0]
282 title
= video_source
['title']
284 for k
in ('source', 'msl4source', 'iossource', 'hlsv4'):
286 source_url
= video_source
.get(sk
) or video_source
.get(sk
+ 'Alt')
290 source_url
= video_source
['ottStreamUrl']
291 is_live
= video_source
.get('type') == 'live' or video_source
.get('status') == 'Live'
292 resource
= self
._get
_mvpd
_resource
('nbcsports', title
, video_id
, '')
293 token
= self
._extract
_mvpd
_auth
(url
, video_id
, 'nbcsports', resource
)
294 tokenized_url
= self
._download
_json
(
295 'https://token.playmakerservices.com/cdn',
296 video_id
, data
=json
.dumps({
297 'requestorId': 'nbcsports',
299 'application': 'NBCSports',
301 'platform': 'desktop',
303 'url': video_source
['sourceUrl'],
304 'token': base64
.b64encode(token
.encode()).decode(),
305 'resourceId': base64
.b64encode(resource
.encode()).decode(),
306 }).encode())['tokenizedUrl']
307 formats
= self
._extract
_m
3u8_formats
(tokenized_url
, video_id
, 'mp4')
308 self
._sort
_formats
(formats
)
312 'description': live_source
.get('description'),
318 class NBCNewsIE(ThePlatformIE
):
319 _VALID_URL
= r
'(?x)https?://(?:www\.)?(?:nbcnews|today|msnbc)\.com/([^/]+/)*(?:.*-)?(?P<id>[^/?]+)'
320 _EMBED_REGEX
= [r
'<iframe[^>]+src=(["\'])(?P
<url
>(?
:https?
:)?
//www\
.nbcnews\
.com
/widget
/video
-embed
/[^
"\']+)\1']
324 'url': 'http://www.nbcnews.com/watch/nbcnews-com/how-twitter-reacted-to-the-snowden-interview-269389891880',
325 'md5': 'cf4bc9e6ce0130f00f545d80ecedd4bf',
327 'id': '269389891880',
329 'title': 'How Twitter Reacted To The Snowden Interview',
330 'description': 'md5:65a0bd5d76fe114f3c2727aa3a81fe64',
331 'timestamp': 1401363060,
332 'upload_date': '20140529',
336 'url': 'http://www.nbcnews.com/feature/dateline-full-episodes/full-episode-family-business-n285156',
337 'md5': 'fdbf39ab73a72df5896b6234ff98518a',
339 'id': '529953347624',
341 'title': 'FULL EPISODE: Family Business',
342 'description': 'md5:757988edbaae9d7be1d585eb5d55cc04',
344 'skip': 'This page is unavailable.',
347 'url': 'http://www.nbcnews.com/nightly-news/video/nightly-news-with-brian-williams-full-broadcast-february-4-394064451844',
348 'md5': '8eb831eca25bfa7d25ddd83e85946548',
350 'id': '394064451844',
352 'title': 'Nightly News with Brian Williams Full Broadcast (February 4)',
353 'description': 'md5:1c10c1eccbe84a26e5debb4381e2d3c5',
354 'timestamp': 1423104900,
355 'upload_date': '20150205',
359 'url': 'http://www.nbcnews.com/business/autos/volkswagen-11-million-vehicles-could-have-suspect-software-emissions-scandal-n431456',
360 'md5': '4a8c4cec9e1ded51060bdda36ff0a5c0',
364 'title': "Volkswagen U
.S
. Chief
: We
'Totally Screwed Up'",
365 'description': 'md5:d22d1281a24f22ea0880741bb4dd6301',
366 'upload_date': '20150922',
367 'timestamp': 1442917800,
371 'url': 'http://www.today.com/video/see-the-aurora-borealis-from-space-in-stunning-new-nasa-video-669831235788',
372 'md5': '118d7ca3f0bea6534f119c68ef539f71',
374 'id': '669831235788',
376 'title': 'See the aurora borealis from space in stunning new NASA video',
377 'description': 'md5:74752b7358afb99939c5f8bb2d1d04b1',
378 'upload_date': '20160420',
379 'timestamp': 1461152093,
383 'url': 'http://www.msnbc.com/all-in-with-chris-hayes/watch/the-chaotic-gop-immigration-vote-314487875924',
384 'md5': '6d236bf4f3dddc226633ce6e2c3f814d',
386 'id': '314487875924',
388 'title': 'The chaotic GOP immigration vote',
389 'description': 'The Republican House votes on a border bill that has no chance of getting through the Senate or signed by the President and is drawing criticism from all sides.',
390 'thumbnail': r're:^https?://.*\.jpg$',
391 'timestamp': 1406937606,
392 'upload_date': '20140802',
396 'url': 'http://www.nbcnews.com/watch/dateline/full-episode--deadly-betrayal-386250819952',
397 'only_matching': True,
400 # From http://www.vulture.com/2016/06/letterman-couldnt-care-less-about-late-night.html
401 'url': 'http://www.nbcnews.com/widget/video-embed/701714499682',
402 'only_matching': True,
406 def _real_extract(self, url):
407 video_id = self._match_id(url)
408 webpage = self._download_webpage(url, video_id)
410 data = self._search_nextjs_data(webpage, video_id)['props']['initialState']
411 video_data = try_get(data, lambda x: x['video']['current'], dict)
413 video_data = data['article']['content'][0]['primaryMedia']['video']
414 title = video_data['headline']['primary']
417 for va in video_data.get('videoAssets', []):
418 public_url = va.get('publicUrl')
421 if '://link.theplatform.com/' in public_url:
422 public_url = update_url_query(public_url, {'format': 'redirect'})
423 format_id = va.get('format')
424 if format_id == 'M3U':
425 formats.extend(self._extract_m3u8_formats(
426 public_url, video_id, 'mp4', 'm3u8_native',
427 m3u8_id=format_id, fatal=False))
429 tbr = int_or_none(va.get('bitrate'), 1000)
431 format_id += '-%d' % tbr
433 'format_id': format_id,
435 'width': int_or_none(va.get('width')),
436 'height': int_or_none(va.get('height')),
440 self._sort_formats(formats)
443 closed_captioning = video_data.get('closedCaptioning')
444 if closed_captioning:
445 for cc_url in closed_captioning.values():
448 subtitles.setdefault('en', []).append({
455 'description': try_get(video_data, lambda x: x['description']['primary']),
456 'thumbnail': try_get(video_data, lambda x: x['primaryImage']['url']['primary']),
457 'duration': parse_duration(video_data.get('duration')),
458 'timestamp': unified_timestamp(video_data.get('datePublished')),
460 'subtitles': subtitles,
464 class NBCOlympicsIE(InfoExtractor):
465 IE_NAME = 'nbcolympics'
466 _VALID_URL = r'https?://www\.nbcolympics\.com/videos?/(?P<id>[0-9a-z-]+)'
469 # Geo-restricted to US
470 'url': 'http://www.nbcolympics.com/video/justin-roses-son-leo-was-tears-after-his-dad-won-gold',
471 'md5': '54fecf846d05429fbaa18af557ee523a',
473 'id': 'WjTBzDXx5AUq',
474 'display_id': 'justin-roses-son-leo-was-tears-after-his-dad-won-gold',
476 'title': 'Rose\'s son Leo was in tears after his dad won gold',
477 'description': 'Olympic gold medalist Justin Rose gets emotional talking to the impact his win in men\'s golf has already had on his children.',
478 'timestamp': 1471274964,
479 'upload_date': '20160815',
480 'uploader': 'NBCU-SPORTS',
484 def _real_extract(self, url):
485 display_id = self._match_id(url)
487 webpage = self._download_webpage(url, display_id)
490 drupal_settings = self._parse_json(self._search_regex(
491 r'jQuery\.extend\(Drupal\.settings\s*,\s*({.+?})\);',
492 webpage, 'drupal settings'), display_id)
494 iframe_url = drupal_settings['vod']['iframe_url']
495 theplatform_url = iframe_url.replace(
496 'vplayer.nbcolympics.com', 'player.theplatform.com')
497 except RegexNotFoundError:
498 theplatform_url = self._search_regex(
499 r"([\"'])embedUrl\1: *([\"'])(?P
<embedUrl
>.+)\
2",
500 webpage, 'embedding URL', group="embedUrl
")
503 '_type': 'url_transparent',
504 'url': theplatform_url,
505 'ie_key': ThePlatformIE.ie_key(),
506 'display_id': display_id,
510 class NBCOlympicsStreamIE(AdobePassIE):
511 IE_NAME = 'nbcolympics:stream'
512 _VALID_URL = r'https?://stream\.nbcolympics\.com/(?P<id>[0-9a-z-]+)'
515 'note': 'Tokenized m3u8 source URL',
516 'url': 'https://stream.nbcolympics.com/womens-soccer-group-round-11',
520 'title': r"re
:Women
's Group Stage - Netherlands vs\. Brazil [0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}$",
523 'skip_download
': 'm3u8
',
526 'note
': 'Plain m3u8 source URL
',
527 'url
': 'https
://stream
.nbcolympics
.com
/gymnastics
-event
-finals
-mens
-floor
-pommel
-horse
-womens
-vault
-bars
',
531 'title
': r're
:Event Finals
: M Floor
, W Vault
, M Pommel
, W Uneven Bars
[0-9]{4}
-[0-9]{2}
-[0-9]{2}
[0-9]{2}
:[0-9]{2}$
',
534 'skip_download
': 'm3u8
',
539 def _real_extract(self, url):
540 display_id = self._match_id(url)
541 webpage = self._download_webpage(url, display_id)
542 pid = self._search_regex(r'pid\s
*=\s
*(\d
+);', webpage, 'pid
')
544 event_config = self._download_json(
545 f'http
://stream
.nbcolympics
.com
/data
/event_config_{pid}
.json
',
546 pid, 'Downloading event config
')['eventConfig
']
548 title = event_config['eventTitle
']
549 is_live = {'live': True, 'replay': False}.get(event_config.get('eventStatus
'))
551 source_url = self._download_json(
552 f'https
://api
-leap
.nbcsports
.com
/feeds
/assets
/{pid}?application
=NBCOlympics
&platform
=desktop
&format
=nbc
-player
&env
=staging
',
553 pid, 'Downloading leap config
'
554 )['videoSources
'][0]['cdnSources
']['primary
'][0]['sourceUrl
']
556 if event_config.get('cdnToken
'):
557 ap_resource = self._get_mvpd_resource(
558 event_config.get('resourceId
', 'NBCOlympics
'),
559 re.sub(r'[^\w\d
]+', '', event_config['eventTitle
']), pid,
560 event_config.get('ratingId
', 'NO VALUE
'))
561 media_token = self._extract_mvpd_auth(url, pid, event_config.get('requestorId
', 'NBCOlympics
'), ap_resource)
563 source_url = self._download_json(
564 'https
://tokens
.playmakerservices
.com
/', pid, 'Retrieving tokenized URL
',
566 'application
': 'NBCSports
',
567 'authentication
-type': 'adobe
-pass',
570 'platform
': 'desktop
',
571 'requestorId
': 'NBCOlympics
',
572 'resourceId
': base64.b64encode(ap_resource.encode()).decode(),
573 'token
': base64.b64encode(media_token.encode()).decode(),
577 )['akamai
'][0]['tokenizedUrl
']
579 formats = self._extract_m3u8_formats(source_url, pid, 'mp4
', live=is_live)
581 # -http_seekable requires ffmpeg 4.3+ but it doesnt seem possible to
582 # download with ffmpeg without this option
583 f['downloader_options
'] = {'ffmpeg_args': ['-seekable', '0', '-http_seekable', '0', '-icy', '0']}
584 self._sort_formats(formats)
588 'display_id
': display_id,
595 class NBCStationsIE(InfoExtractor):
596 _DOMAIN_RE = '|
'.join(map(re.escape, (
597 'nbcbayarea
', 'nbcboston
', 'nbcchicago
', 'nbcconnecticut
', 'nbcdfw
', 'nbclosangeles
',
598 'nbcmiami
', 'nbcnewyork
', 'nbcphiladelphia
', 'nbcsandiego
', 'nbcwashington
',
599 'necn
', 'telemundo52
', 'telemundoarizona
', 'telemundochicago
', 'telemundonuevainglaterra
',
601 _VALID_URL = rf'https?
://(?
:www\
.)?
(?P
<site
>{_DOMAIN_RE}
)\
.com
/(?
:[^
/?
#]+/)*(?P<id>[^/?#]+)/?(?:$|[#?])'
604 'url': 'https://www.nbclosangeles.com/news/local/large-structure-fire-in-downtown-la-prompts-smoke-odor-advisory/2968618/',
605 'md5': '462041d91bd762ef5a38b7d85d6dc18f',
609 'title': 'Large Structure Fire in Downtown LA Prompts Smoke Odor Advisory',
611 'timestamp': 1661135892,
612 'upload_date': '20220821',
614 'uploader_id': 'KNBC',
615 'channel': 'nbclosangeles',
618 'url': 'https://www.telemundoarizona.com/responde/huracan-complica-reembolso-para-televidente-de-tucson/2247002/',
619 'md5': '0917dcf7885be1023a9220630d415f67',
623 'title': 'Huracán complica que televidente de Tucson reciba reembolso',
624 'description': 'md5:af298dc73aab74d4fca6abfb12acb6cf',
625 'timestamp': 1660886507,
626 'upload_date': '20220819',
627 'uploader': 'Telemundo Arizona',
628 'uploader_id': 'KTAZ',
629 'channel': 'telemundoarizona',
641 def _real_extract(self
, url
):
642 channel
, video_id
= self
._match
_valid
_url
(url
).group('site', 'id')
643 webpage
= self
._download
_webpage
(url
, video_id
)
645 nbc_data
= self
._search
_json
(
646 r
'<script>var\s*nbc\s*=\s*', webpage
, 'NBC JSON data', video_id
)
647 pdk_acct
= nbc_data
.get('pdkAcct') or 'Yh1nAC'
648 fw_ssid
= traverse_obj(nbc_data
, ('video', 'fwSSID'))
649 fw_network_id
= traverse_obj(nbc_data
, ('video', 'fwNetworkID'), default
='382114')
651 video_data
= self
._parse
_json
(self
._html
_search
_regex
(
652 r
'data-videos="([^"]*)"', webpage
, 'video data', default
='{}'), video_id
)
653 video_data
= variadic(video_data
)[0]
654 video_data
.update(self
._parse
_json
(self
._html
_search
_regex
(
655 r
'data-meta="([^"]*)"', webpage
, 'metadata', default
='{}'), video_id
))
659 if video_data
.get('mpx_is_livestream') == '1':
661 player_id
= traverse_obj(
662 video_data
, 'mpx_m3upid', ('video', 'meta', 'mpx_m3upid'), 'mpx_pid',
663 ('video', 'meta', 'mpx_pid'), 'pid_streaming_web_medium')
666 'assetTypes': 'LegacyRelease',
667 'fwsitesection': fw_ssid
,
668 'fwNetworkID': fw_network_id
,
669 'pprofile': 'ots_desktop_html',
670 'sensitive': 'false',
677 'formats': 'M3U+none,MPEG-DASH+none,MPEG4,MP3',
683 'title': f
'{channel} livestream',
688 player_id
= traverse_obj(
689 video_data
, ('video', 'meta', 'pid_streaming_web_high'), 'pid_streaming_web_high',
690 ('video', 'meta', 'mpx_pid'), 'mpx_pid')
692 date_string
= traverse_obj(video_data
, 'date_string', 'date_gmt')
694 date_string
= self
._search
_regex
(
695 r
'datetime="([^"]+)"', date_string
, 'date string', fatal
=False)
697 date_string
= traverse_obj(
698 nbc_data
, ('dataLayer', 'adobe', 'prop70'), ('dataLayer', 'adobe', 'eVar70'),
699 ('dataLayer', 'adobe', 'eVar59'))
701 video_url
= traverse_obj(video_data
, ('video', 'meta', 'mp4_url'), 'mp4_url')
703 height
= url_basename(video_url
).split('-')[1].split('p')[0]
707 'width': int_or_none(self
._RESOLUTIONS
.get(height
)),
708 'height': int_or_none(height
),
709 'format_id': f
'http-{height}',
714 'assetTypes': 'LegacyRelease',
715 'fwsitesection': fw_ssid
,
716 'fwNetworkID': fw_network_id
,
717 'format': 'redirect',
724 'title': video_data
.get('title') or traverse_obj(
725 nbc_data
, ('dataLayer', 'contenttitle'), ('dataLayer', 'title'),
726 ('dataLayer', 'adobe', 'prop22'), ('dataLayer', 'id')),
727 'description': traverse_obj(video_data
, 'summary', 'excerpt', 'video_hero_text'),
728 'upload_date': str_or_none(unified_strdate(date_string
)),
729 'timestamp': int_or_none(unified_timestamp(date_string
)),
733 raise ExtractorError(
734 'No video player ID or livestream player ID found in webpage', expected
=True)
736 headers
= {'Origin': f'https://www.{channel}
.com
'}
737 manifest, urlh = self._download_webpage_handle(
738 f'https
://link
.theplatform
.com
/s
/{pdk_acct}
/{player_id}
', video_id,
739 headers=headers, query=query, note='Downloading manifest
')
741 manifest_url = self._search_regex(r'<video src
="([^"]*)', manifest, 'manifest URL
')
743 manifest_url = urlh.geturl()
745 formats.extend(self._extract_m3u8_formats(
746 manifest_url, video_id, 'mp4
', headers=headers, m3u8_id='hls
',
747 fatal=live, live=live, errnote='No HLS formats found
'))
748 self._sort_formats(formats)
751 'id': str_or_none(video_id),
753 'uploader
': str_or_none(nbc_data.get('on_air_name
')),
754 'uploader_id
': str_or_none(nbc_data.get('callLetters
')),