Repository: yt-dlp/yt-dlp
File: yt_dlp/extractor/nytimes.py (Python)

Code before:

    def _build_playlist(self, media_items):
        for media_id in media_items:
            json_obj = traverse_obj(self._json_from_graphql(media_id) or {}, ('data', 'video'))

            title = json_obj.get('promotionalHeadline') or media_id
            description = json_obj.get('summary')
            creators = ', '.join(
                [remove_start(creator, 'By ') for creator in traverse_obj(
                    json_obj, ('bylines', ..., 'renderedRepresentation'))])
            duration = int_or_none(json_obj.get('duration'))
            formats, subtitles = self._extract_media_from_json(media_id, json_obj)

            yield {
                'id': media_id,
                'title': title,
                'description': description,
                'duration': duration,
                'creator': creators,  # TODO: change to 'creators'
                'formats': formats,
                'subtitles': subtitles,
                'thumbnails': self._extract_thumbnails(
                    traverse_obj(json_obj, ('promotionalMedia', 'crops', ..., 'renditions', ...))),
            }

    def _json_from_graphql(self, media_id):
        # reference: `id-to-uri.js`
        video_uuid = uuid.uuid5(self._DNS_NAMESPACE, 'video')
        media_uuid = uuid.uuid5(video_uuid, media_id)

        return self._download_json(
            self._GRAPHQL_API, media_id, note='Downloading json from GRAPHQL API', data=json.dumps({
                'query': self._GRAPHQL_QUERY,
                'variables': {'id': f'nyt://video/{media_uuid}'},
            }, separators=(',', ':')).encode(), headers={
                'Content-Type': 'application/json',
                'Nyt-App-Type': 'vhs',
                'Nyt-App-Version': 'v3.52.21',
                'Nyt-Token': self._TOKEN,
                'Origin': 'https://cooking.nytimes.com',
            }, fatal=False) or {}

    def _real_extract(self, url):
        page_id = self._match_id(url)
        webpage = self._download_webpage(url, page_id)

        lead_video_id = self._search_regex(
            r'data-video-player-id="(\d+)"></div>', webpage, 'lead video', default=None)
        media_items = traverse_obj(
            get_elements_html_by_class('video-item', webpage), (..., {extract_attributes}, 'data-video-id'))
        title = self._html_search_meta(['og:title', 'twitter:title'], webpage)

Review comment (suggestion):
`_call_api` is the convention
```suggestion
    def _call_api(self, media_id):
```

Code after:
            'timestamp': 1447804800,
            'upload_date': '20151118',
            'creator': 'David Tanis',
            'thumbnail': r're:https?://\w+\.nyt.com/images/.*\.jpg',
        },
    }, {
        'url': 'https://cooking.nytimes.com/recipes/1024781-neapolitan-checkerboard-cookies',
        'md5': '58df35998241dcf0620e99e646331b42',
        'info_dict': {
            'id': '1024781',
            'ext': 'mp4',
            'title': 'Neapolitan Checkerboard Cookies',
            'description': 'md5:ba12394c585ababea951cb6d2fcc6631',
            'timestamp': 1701302400,
            'upload_date': '20231130',
            'creator': 'Sue Li',
            'thumbnail': r're:https?://\w+\.nyt.com/images/.*\.jpg',
        },
    }, {
        'url': 'https://cooking.nytimes.com/recipes/1019516-overnight-oats',
        'md5': '2fe7965a3adc899913b8e25ada360823',
        'info_dict': {
            'id': '1019516',
            'ext': 'mp4',
            'timestamp': 1546387200,
            'description': 'md5:8856ce10239161bd2596ac335b9f9bfb',
            'upload_date': '20190102',
            'title': 'Overnight Oats',
            'creator': 'Genevieve Ko',
            'thumbnail': r're:https?://\w+\.nyt.com/images/.*\.jpg',
        },
    }]

    def _real_extract(self, url):
        page_id = self._match_id(url)
        webpage = self._download_webpage(url, page_id)
        recipe_data = self._search_nextjs_data(webpage, page_id)['props']['pageProps']['recipe']

        formats, subtitles = self._extract_m3u8_formats_and_subtitles(
            recipe_data['videoSrc'], page_id, 'mp4', m3u8_id='hls')

        return {
            **traverse_obj(recipe_data, {
                'id': ('id', {str_or_none}),
                'title': ('title', {str}),
                'description': ('topnote', {clean_html}),
                'timestamp': ('publishedAt', {int_or_none}),
                'creator': ('contentAttribution', 'cardByline', {str}),
            }),
            'formats': formats,
            'subtitles': subtitles,