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
): # XXX: Do not subclass from concrete IE
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']
139 'switch': 'HLSServiceSecure',
141 video_id
= video_data
['mpxGuid']
142 tp_path
= 'NnzsPC/media/guid/%s/%s' % (video_data
.get('mpxAccountId') or '2410887629', video_id
)
143 tpm
= self
._download
_theplatform
_metadata
(tp_path
, video_id
)
144 title
= tpm
.get('title') or video_data
.get('secondaryTitle')
145 if video_data
.get('locked'):
146 resource
= self
._get
_mvpd
_resource
(
147 video_data
.get('resourceId') or 'nbcentertainment',
148 title
, video_id
, video_data
.get('rating'))
149 query
['auth'] = self
._extract
_mvpd
_auth
(
150 url
, video_id
, 'nbcentertainment', resource
)
151 theplatform_url
= smuggle_url(update_url_query(
152 'http://link.theplatform.com/s/NnzsPC/media/guid/%s/%s' % (video_data
.get('mpxAccountId') or '2410887629', video_id
),
153 query
), {'force_smil_url': True}
)
155 # Empty string or 0 can be valid values for these. So the check must be `is None`
156 description
= video_data
.get('description')
157 if description
is None:
158 description
= tpm
.get('description')
159 episode_number
= int_or_none(video_data
.get('episodeNumber'))
160 if episode_number
is None:
161 episode_number
= int_or_none(tpm
.get('nbcu$airOrder'))
162 rating
= video_data
.get('rating')
164 try_get(tpm
, lambda x
: x
['ratings'][0]['rating'])
165 season_number
= int_or_none(video_data
.get('seasonNumber'))
166 if season_number
is None:
167 season_number
= int_or_none(tpm
.get('nbcu$seasonNumber'))
168 series
= video_data
.get('seriesShortTitle')
170 series
= tpm
.get('nbcu$seriesShortTitle')
171 tags
= video_data
.get('keywords')
172 if tags
is None or len(tags
) == 0:
173 tags
= tpm
.get('keywords')
176 '_type': 'url_transparent',
177 'age_limit': parse_age_limit(rating
),
178 'description': description
,
180 'episode_number': episode_number
,
182 'ie_key': 'ThePlatform',
183 'season_number': season_number
,
187 'url': theplatform_url
,
191 class NBCSportsVPlayerIE(InfoExtractor
):
192 _VALID_URL_BASE
= r
'https?://(?:vplayer\.nbcsports\.com|(?:www\.)?nbcsports\.com/vplayer)/'
193 _VALID_URL
= _VALID_URL_BASE
+ r
'(?:[^/]+/)+(?P<id>[0-9a-zA-Z_]+)'
194 _EMBED_REGEX
= [r
'(?:iframe[^>]+|var video|div[^>]+data-(?:mpx-)?)[sS]rc\s?=\s?"(?P<url>%s[^\"]+)' % _VALID_URL_BASE
]
197 'url': 'https://vplayer.nbcsports.com/p/BxmELC/nbcsports_embed/select/9CsDKds0kvHI',
199 'id': '9CsDKds0kvHI',
201 'description': 'md5:df390f70a9ba7c95ff1daace988f0d8d',
202 'title': 'Tyler Kalinoski hits buzzer-beater to lift Davidson',
203 'timestamp': 1426270238,
204 'upload_date': '20150313',
205 'uploader': 'NBCU-SPORTS',
208 'thumbnail': r
're:^https?://.*\.jpg$'
211 'url': 'https://vplayer.nbcsports.com/p/BxmELC/nbcsports_embed/select/media/PEgOtlNcC_y2',
212 'only_matching': True,
214 'url': 'https://www.nbcsports.com/vplayer/p/BxmELC/nbcsports/select/PHJSaFWbrTY9?form=html&autoPlay=true',
215 'only_matching': True,
218 def _real_extract(self
, url
):
219 video_id
= self
._match
_id
(url
)
220 webpage
= self
._download
_webpage
(url
, video_id
)
221 theplatform_url
= self
._html
_search
_regex
(r
'tp:releaseUrl="(.+?)"', webpage
, 'url')
222 return self
.url_result(theplatform_url
, 'ThePlatform')
225 class NBCSportsIE(InfoExtractor
):
226 _VALID_URL
= r
'https?://(?:www\.)?nbcsports\.com//?(?!vplayer/)(?:[^/]+/)+(?P<id>[0-9a-z-]+)'
230 'url': 'http://www.nbcsports.com//college-basketball/ncaab/tom-izzo-michigan-st-has-so-much-respect-duke',
232 'id': 'PHJSaFWbrTY9',
234 'title': 'Tom Izzo, Michigan St. has \'so much respect\' for Duke',
235 'description': 'md5:ecb459c9d59e0766ac9c7d5d0eda8113',
236 'uploader': 'NBCU-SPORTS',
237 'upload_date': '20150330',
238 'timestamp': 1427726529,
240 'thumbnail': 'https://hdliveextra-a.akamaihd.net/HD/image_sports/NBCU_Sports_Group_-_nbcsports/253/303/izzodps.jpg',
245 'url': 'https://www.nbcsports.com/philadelphia/philadelphia-phillies/bruce-bochy-hector-neris-hes-idiot',
246 'only_matching': True,
249 'url': 'https://www.nbcsports.com/boston/video/report-card-pats-secondary-no-match-josh-allen',
250 'only_matching': True,
253 def _real_extract(self
, url
):
254 video_id
= self
._match
_id
(url
)
255 webpage
= self
._download
_webpage
(url
, video_id
)
256 return self
.url_result(
257 NBCSportsVPlayerIE
._extract
_url
(webpage
), 'NBCSportsVPlayer')
260 class NBCSportsStreamIE(AdobePassIE
):
261 _VALID_URL
= r
'https?://stream\.nbcsports\.com/.+?\bpid=(?P<id>\d+)'
263 'url': 'http://stream.nbcsports.com/nbcsn/generic?pid=206559',
267 'title': 'Amgen Tour of California Women\'s Recap',
268 'description': 'md5:66520066b3b5281ada7698d0ea2aa894',
272 'skip_download': True,
274 'skip': 'Requires Adobe Pass Authentication',
277 def _real_extract(self
, url
):
278 video_id
= self
._match
_id
(url
)
279 live_source
= self
._download
_json
(
280 'http://stream.nbcsports.com/data/live_sources_%s.json' % video_id
,
282 video_source
= live_source
['videoSources'][0]
283 title
= video_source
['title']
285 for k
in ('source', 'msl4source', 'iossource', 'hlsv4'):
287 source_url
= video_source
.get(sk
) or video_source
.get(sk
+ 'Alt')
291 source_url
= video_source
['ottStreamUrl']
292 is_live
= video_source
.get('type') == 'live' or video_source
.get('status') == 'Live'
293 resource
= self
._get
_mvpd
_resource
('nbcsports', title
, video_id
, '')
294 token
= self
._extract
_mvpd
_auth
(url
, video_id
, 'nbcsports', resource
)
295 tokenized_url
= self
._download
_json
(
296 'https://token.playmakerservices.com/cdn',
297 video_id
, data
=json
.dumps({
298 'requestorId': 'nbcsports',
300 'application': 'NBCSports',
302 'platform': 'desktop',
304 'url': video_source
['sourceUrl'],
305 'token': base64
.b64encode(token
.encode()).decode(),
306 'resourceId': base64
.b64encode(resource
.encode()).decode(),
307 }).encode())['tokenizedUrl']
308 formats
= self
._extract
_m
3u8_formats
(tokenized_url
, video_id
, 'mp4')
312 'description': live_source
.get('description'),
318 class NBCNewsIE(ThePlatformIE
): # XXX: Do not subclass from concrete IE
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')),
442 closed_captioning = video_data.get('closedCaptioning')
443 if closed_captioning:
444 for cc_url in closed_captioning.values():
447 subtitles.setdefault('en', []).append({
454 'description': try_get(video_data, lambda x: x['description']['primary']),
455 'thumbnail': try_get(video_data, lambda x: x['primaryImage']['url']['primary']),
456 'duration': parse_duration(video_data.get('duration')),
457 'timestamp': unified_timestamp(video_data.get('datePublished')),
459 'subtitles': subtitles,
463 class NBCOlympicsIE(InfoExtractor):
464 IE_NAME = 'nbcolympics'
465 _VALID_URL = r'https?://www\.nbcolympics\.com/videos?/(?P<id>[0-9a-z-]+)'
468 # Geo-restricted to US
469 'url': 'http://www.nbcolympics.com/video/justin-roses-son-leo-was-tears-after-his-dad-won-gold',
470 'md5': '54fecf846d05429fbaa18af557ee523a',
472 'id': 'WjTBzDXx5AUq',
473 'display_id': 'justin-roses-son-leo-was-tears-after-his-dad-won-gold',
475 'title': 'Rose\'s son Leo was in tears after his dad won gold',
476 'description': 'Olympic gold medalist Justin Rose gets emotional talking to the impact his win in men\'s golf has already had on his children.',
477 'timestamp': 1471274964,
478 'upload_date': '20160815',
479 'uploader': 'NBCU-SPORTS',
483 def _real_extract(self, url):
484 display_id = self._match_id(url)
486 webpage = self._download_webpage(url, display_id)
489 drupal_settings = self._parse_json(self._search_regex(
490 r'jQuery\.extend\(Drupal\.settings\s*,\s*({.+?})\);',
491 webpage, 'drupal settings'), display_id)
493 iframe_url = drupal_settings['vod']['iframe_url']
494 theplatform_url = iframe_url.replace(
495 'vplayer.nbcolympics.com', 'player.theplatform.com')
496 except RegexNotFoundError:
497 theplatform_url = self._search_regex(
498 r"([\"'])embedUrl\1: *([\"'])(?P
<embedUrl
>.+)\
2",
499 webpage, 'embedding URL', group="embedUrl
")
502 '_type': 'url_transparent',
503 'url': theplatform_url,
504 'ie_key': ThePlatformIE.ie_key(),
505 'display_id': display_id,
509 class NBCOlympicsStreamIE(AdobePassIE):
510 IE_NAME = 'nbcolympics:stream'
511 _VALID_URL = r'https?://stream\.nbcolympics\.com/(?P<id>[0-9a-z-]+)'
514 'note': 'Tokenized m3u8 source URL',
515 'url': 'https://stream.nbcolympics.com/womens-soccer-group-round-11',
519 '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}$",
522 'skip_download
': 'm3u8
',
525 'note
': 'Plain m3u8 source URL
',
526 'url
': 'https
://stream
.nbcolympics
.com
/gymnastics
-event
-finals
-mens
-floor
-pommel
-horse
-womens
-vault
-bars
',
530 '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}$
',
533 'skip_download
': 'm3u8
',
538 def _real_extract(self, url):
539 display_id = self._match_id(url)
540 webpage = self._download_webpage(url, display_id)
541 pid = self._search_regex(r'pid\s
*=\s
*(\d
+);', webpage, 'pid
')
543 event_config = self._download_json(
544 f'http
://stream
.nbcolympics
.com
/data
/event_config_{pid}
.json
',
545 pid, 'Downloading event config
')['eventConfig
']
547 title = event_config['eventTitle
']
548 is_live = {'live': True, 'replay': False}.get(event_config.get('eventStatus
'))
550 source_url = self._download_json(
551 f'https
://api
-leap
.nbcsports
.com
/feeds
/assets
/{pid}?application
=NBCOlympics
&platform
=desktop
&format
=nbc
-player
&env
=staging
',
552 pid, 'Downloading leap config
'
553 )['videoSources
'][0]['cdnSources
']['primary
'][0]['sourceUrl
']
555 if event_config.get('cdnToken
'):
556 ap_resource = self._get_mvpd_resource(
557 event_config.get('resourceId
', 'NBCOlympics
'),
558 re.sub(r'[^\w\d
]+', '', event_config['eventTitle
']), pid,
559 event_config.get('ratingId
', 'NO VALUE
'))
560 media_token = self._extract_mvpd_auth(url, pid, event_config.get('requestorId
', 'NBCOlympics
'), ap_resource)
562 source_url = self._download_json(
563 'https
://tokens
.playmakerservices
.com
/', pid, 'Retrieving tokenized URL
',
565 'application
': 'NBCSports
',
566 'authentication
-type': 'adobe
-pass',
569 'platform
': 'desktop
',
570 'requestorId
': 'NBCOlympics
',
571 'resourceId
': base64.b64encode(ap_resource.encode()).decode(),
572 'token
': base64.b64encode(media_token.encode()).decode(),
576 )['akamai
'][0]['tokenizedUrl
']
578 formats = self._extract_m3u8_formats(source_url, pid, 'mp4
', live=is_live)
580 # -http_seekable requires ffmpeg 4.3+ but it doesnt seem possible to
581 # download with ffmpeg without this option
582 f['downloader_options
'] = {'ffmpeg_args': ['-seekable', '0', '-http_seekable', '0', '-icy', '0']}
586 'display_id
': display_id,
593 class NBCStationsIE(InfoExtractor):
594 _DOMAIN_RE = '|
'.join(map(re.escape, (
595 'nbcbayarea
', 'nbcboston
', 'nbcchicago
', 'nbcconnecticut
', 'nbcdfw
', 'nbclosangeles
',
596 'nbcmiami
', 'nbcnewyork
', 'nbcphiladelphia
', 'nbcsandiego
', 'nbcwashington
',
597 'necn
', 'telemundo52
', 'telemundoarizona
', 'telemundochicago
', 'telemundonuevainglaterra
',
599 _VALID_URL = rf'https?
://(?
:www\
.)?
(?P
<site
>{_DOMAIN_RE}
)\
.com
/(?
:[^
/?
#]+/)*(?P<id>[^/?#]+)/?(?:$|[#?])'
602 'url': 'https://www.nbclosangeles.com/news/local/large-structure-fire-in-downtown-la-prompts-smoke-odor-advisory/2968618/',
603 'md5': '462041d91bd762ef5a38b7d85d6dc18f',
607 'title': 'Large Structure Fire in Downtown LA Prompts Smoke Odor Advisory',
609 'timestamp': 1661135892,
610 'upload_date': '20220821',
612 'uploader_id': 'KNBC',
613 'channel': 'nbclosangeles',
616 'url': 'https://www.telemundoarizona.com/responde/huracan-complica-reembolso-para-televidente-de-tucson/2247002/',
617 'md5': '0917dcf7885be1023a9220630d415f67',
621 'title': 'Huracán complica que televidente de Tucson reciba reembolso',
622 'description': 'md5:af298dc73aab74d4fca6abfb12acb6cf',
623 'timestamp': 1660886507,
624 'upload_date': '20220819',
625 'uploader': 'Telemundo Arizona',
626 'uploader_id': 'KTAZ',
627 'channel': 'telemundoarizona',
639 def _real_extract(self
, url
):
640 channel
, video_id
= self
._match
_valid
_url
(url
).group('site', 'id')
641 webpage
= self
._download
_webpage
(url
, video_id
)
643 nbc_data
= self
._search
_json
(
644 r
'<script>var\s*nbc\s*=', webpage
, 'NBC JSON data', video_id
)
645 pdk_acct
= nbc_data
.get('pdkAcct') or 'Yh1nAC'
646 fw_ssid
= traverse_obj(nbc_data
, ('video', 'fwSSID'))
647 fw_network_id
= traverse_obj(nbc_data
, ('video', 'fwNetworkID'), default
='382114')
649 video_data
= self
._parse
_json
(self
._html
_search
_regex
(
650 r
'data-videos="([^"]*)"', webpage
, 'video data', default
='{}'), video_id
)
651 video_data
= variadic(video_data
)[0]
652 video_data
.update(self
._parse
_json
(self
._html
_search
_regex
(
653 r
'data-meta="([^"]*)"', webpage
, 'metadata', default
='{}'), video_id
))
657 if video_data
.get('mpx_is_livestream') == '1':
659 player_id
= traverse_obj(
660 video_data
, 'mpx_m3upid', ('video', 'meta', 'mpx_m3upid'), 'mpx_pid',
661 ('video', 'meta', 'mpx_pid'), 'pid_streaming_web_medium')
664 'assetTypes': 'LegacyRelease',
665 'fwsitesection': fw_ssid
,
666 'fwNetworkID': fw_network_id
,
667 'pprofile': 'ots_desktop_html',
668 'sensitive': 'false',
675 'formats': 'M3U+none,MPEG-DASH+none,MPEG4,MP3',
681 'title': f
'{channel} livestream',
686 player_id
= traverse_obj(
687 video_data
, ('video', 'meta', 'pid_streaming_web_high'), 'pid_streaming_web_high',
688 ('video', 'meta', 'mpx_pid'), 'mpx_pid')
690 date_string
= traverse_obj(video_data
, 'date_string', 'date_gmt')
692 date_string
= self
._search
_regex
(
693 r
'datetime="([^"]+)"', date_string
, 'date string', fatal
=False)
695 date_string
= traverse_obj(
696 nbc_data
, ('dataLayer', 'adobe', 'prop70'), ('dataLayer', 'adobe', 'eVar70'),
697 ('dataLayer', 'adobe', 'eVar59'))
699 video_url
= traverse_obj(video_data
, ('video', 'meta', 'mp4_url'), 'mp4_url')
701 height
= url_basename(video_url
).split('-')[1].split('p')[0]
705 'width': int_or_none(self
._RESOLUTIONS
.get(height
)),
706 'height': int_or_none(height
),
707 'format_id': f
'http-{height}',
712 'assetTypes': 'LegacyRelease',
713 'fwsitesection': fw_ssid
,
714 'fwNetworkID': fw_network_id
,
715 'format': 'redirect',
722 'title': video_data
.get('title') or traverse_obj(
723 nbc_data
, ('dataLayer', 'contenttitle'), ('dataLayer', 'title'),
724 ('dataLayer', 'adobe', 'prop22'), ('dataLayer', 'id')),
725 'description': traverse_obj(video_data
, 'summary', 'excerpt', 'video_hero_text'),
726 'upload_date': str_or_none(unified_strdate(date_string
)),
727 'timestamp': int_or_none(unified_timestamp(date_string
)),
731 raise ExtractorError(
732 'No video player ID or livestream player ID found in webpage', expected
=True)
734 headers
= {'Origin': f'https://www.{channel}
.com
'}
735 manifest, urlh = self._download_webpage_handle(
736 f'https
://link
.theplatform
.com
/s
/{pdk_acct}
/{player_id}
', video_id,
737 headers=headers, query=query, note='Downloading manifest
')
739 manifest_url = self._search_regex(r'<video src
="([^"]*)', manifest, 'manifest URL
')
741 manifest_url = urlh.geturl()
743 formats.extend(self._extract_m3u8_formats(
744 manifest_url, video_id, 'mp4
', headers=headers, m3u8_id='hls
',
745 fatal=live, live=live, errnote='No HLS formats found
'))
748 'id': str_or_none(video_id),
750 'uploader
': str_or_none(nbc_data.get('on_air_name
')),
751 'uploader_id
': str_or_none(nbc_data.get('callLetters
')),