diff --git a/yt_dlp/extractor/_extractors.py b/yt_dlp/extractor/_extractors.py index 2646ed9ac9..8c247908de 100644 --- a/yt_dlp/extractor/_extractors.py +++ b/yt_dlp/extractor/_extractors.py @@ -1218,6 +1218,7 @@ from .n1 import ( N1InfoAssetIE, N1InfoIIE, ) +from .nascar import NascarClassicsIE from .nate import ( NateIE, NateProgramIE, diff --git a/yt_dlp/extractor/nascar.py b/yt_dlp/extractor/nascar.py new file mode 100644 index 0000000000..b14a3b0aa1 --- /dev/null +++ b/yt_dlp/extractor/nascar.py @@ -0,0 +1,60 @@ +from .common import InfoExtractor +from ..utils import ( + float_or_none, + parse_iso8601, + url_or_none, +) +from ..utils.traversal import traverse_obj + + +class NascarClassicsIE(InfoExtractor): + _VALID_URL = r'https?://(?:www\.)?classics\.nascar\.com/video/(?P[\w~-]+)' + _TESTS = [{ + 'url': 'https://classics.nascar.com/video/Ka5qGuxzZ~SIvJii7uAC~wszPshklHN', + 'md5': '81d712eccffa7169c328281b8cc28f77', + 'info_dict': { + 'id': 'Ka5qGuxzZ~SIvJii7uAC~wszPshklHN', + 'ext': 'mp4', + 'title': 'Cook Out 400 2023', + 'thumbnail': 'https://va.aws.nascar.com/IMAGES/CUP_2023_22_RICHMOND_THUMB_NCD.jpg', + 'timestamp': 1690732800, + 'upload_date': '20230730', + 'tags': ['2023', 'race #22', 'richmond', 'chris buescher', 'cup'], + 'chapters': 'count:18', + }, + }, { + 'url': 'https://classics.nascar.com/video/UASvPDOwEha~SIvJii7uAC~wszPshklHN', + 'md5': 'a5e8d6ec6005da3857d25ba2df5e7133', + 'info_dict': { + 'id': 'UASvPDOwEha~SIvJii7uAC~wszPshklHN', + 'ext': 'mp4', + 'title': 'I Love New York 355 at the Glen 2017', + 'thumbnail': 'https://va.aws.nascar.com/IMAGES/CUP_2017_22_WATKINSGLEN_THUMB_NCD.jpg', + 'timestamp': 1501995600, + 'upload_date': '20170806', + 'tags': ['watkins glen', 'race #22', '2017', 'martin truex jr.', 'cup'], + 'chapters': 'count:13', + }, + }] + + def _real_extract(self, url): + video_id = self._match_id(url) + webpage = self._download_webpage(url, video_id) + content_data = self._search_nextjs_data( + webpage, video_id)['props']['pageProps']['contentData'] + + return { + 'id': video_id, + 'formats': self._extract_m3u8_formats(content_data['input']['src'], video_id, 'mp4'), + **traverse_obj(content_data, { + 'title': ('input', 'name', {str}), + 'description': ('input', 'description', {str}, filter), + 'thumbnail': ('input', 'thumbnail', {url_or_none}), + 'tags': ('input', 'settings', 'tags', ..., {str}), + 'timestamp': ('input', 'start_time', {parse_iso8601}), + 'chapters': ('overlay', 'data', 'timelines', 0, 'events', lambda _, v: float(v['timestamp']) is not None, { + 'start_time': ('timestamp', {float_or_none}), + 'title': ('name', {str}), + }), + }), + }