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

Code before:

                return {
                    'id': version_id,
                    'title': title,
                    'formats': formats,
                    'alt_title': init_data.get('shortTitle'),
                    'thumbnail': image_url.replace('$recipe', 'raw') if image_url else None,
                    'description': smp_data.get('summary') or init_data.get('shortSummary'),
                    'upload_date': display_date.replace('-', '') if display_date else None,
                    'subtitles': subtitles,
                    'duration': int_or_none(clip_data.get('duration')),
                    'categories': [topic_title] if topic_title else None,
                }

        # Morph based embed (e.g. http://www.bbc.co.uk/sport/live/olympics/36895975)
        # Several setPayload calls may be present but the video(s)
        # should be in one that mentions leadMedia or videoData
        morph_payload = self._search_json(
            r'\bMorph\s*\.\s*setPayload\s*\([^,]+,', webpage, 'morph payload', playlist_id,
            contains_pattern=r'\{(?:(?!</script>)[\s\S])+?(?:"leadMedia"|\\"videoData\\")\s*:(?:(?!</script>)[\s\S])+\}',
            default={})
        if morph_payload:
            for component in traverse_obj(morph_payload, (
                    'body', 'components', lambda _, v: v['props']['leadMedia']['identifiers'])):
                lead_media = component['props']['leadMedia']
                programme_id = traverse_obj(lead_media['identifiers'], 'vpid', 'playablePid', expected_type=str)
                if not programme_id:
                    continue
                title = lead_media.get('title') or self._og_search_title(webpage)
                formats, subtitles = self._download_media_selector(programme_id)
                return {
                    'id': programme_id,
                    'title': title,
                    **traverse_obj(lead_media, {
                        'description': ('summary', {str}),
                        'duration': ('duration', ('rawDuration', 'formattedDuration', 'spokenDuration'), {parse_duration}),
                        'uploader': ('masterBrand', {str}),
                        'uploader_id': ('mid', {str}),
                    }),
                    'formats': formats,
                    'subtitles': subtitles,
                }
            body = traverse_obj(morph_payload, (
                'body', 'content', 'article', 'body',
                {lambda s: self._parse_json(s, playlist_id, fatal=False)}))
            added = False
            for video_data in traverse_obj(body, (Ellipsis, 'videoData', {lambda v: v.get('pid') and v})):
                if video_data.get('vpid'):
                    video_id = video_data['vpid']
                    formats, subtitles = self._download_media_selector(video_id)
                    entry = {

Review comment (suggestion):
```suggestion
            for lead_media in traverse_obj(morph_payload, (
                    'body', 'components', ..., 'props', 'leadMedia', {dict})):
                programme_id = traverse_obj(lead_media, ('identifiers', ('vpid', 'playablePid'), {str}, any))
```

Code after:

                return {
                    'id': version_id,
                    'title': title,
                    'formats': formats,
                    'alt_title': init_data.get('shortTitle'),
                    'thumbnail': image_url.replace('$recipe', 'raw') if image_url else None,
                    'description': smp_data.get('summary') or init_data.get('shortSummary'),
                    'upload_date': display_date.replace('-', '') if display_date else None,
                    'subtitles': subtitles,
                    'duration': int_or_none(clip_data.get('duration')),
                    'categories': [topic_title] if topic_title else None,
                }

        # Morph based embed (e.g. http://www.bbc.co.uk/sport/live/olympics/36895975)
        # Several setPayload calls may be present but the video(s)
        # should be in one that mentions leadMedia or videoData
        morph_payload = self._search_json(
            r'\bMorph\s*\.\s*setPayload\s*\([^,]+,', webpage, 'morph payload', playlist_id,
            contains_pattern=r'{(?s:(?:(?!</script>).)+(?:"leadMedia"|\\"videoData\\")\s*:.+)}',
            default={})
        if morph_payload:
            for lead_media in traverse_obj(morph_payload, (
                    'body', 'components', ..., 'props', 'leadMedia', {dict})):
                programme_id = traverse_obj(lead_media, ('identifiers', ('vpid', 'playablePid'), {str}, any))
                if not programme_id:
                    continue
                formats, subtitles = self._download_media_selector(programme_id)
                return {
                    'id': programme_id,
                    'title': lead_media.get('title') or self._og_search_title(webpage),
                    **traverse_obj(lead_media, {
                        'description': ('summary', {str}),
                        'duration': ('duration', ('rawDuration', 'formattedDuration', 'spokenDuration'), {parse_duration}),
                        'uploader': ('masterBrand', {str}),
                        'uploader_id': ('mid', {str}),
                    }),
                    'formats': formats,
                    'subtitles': subtitles,
                }
            body = self._parse_json(traverse_obj(morph_payload, (
                'body', 'content', 'article', 'body')), playlist_id, fatal=False)
            for video_data in traverse_obj(body, (lambda _, v: v['videoData']['pid'], 'videoData')):
                if video_data.get('vpid'):
                    video_id = video_data['vpid']
                    formats, subtitles = self._download_media_selector(video_id)
                    entry = {
                        'id': video_id,
                        'formats': formats,
                        'subtitles': subtitles,
                    }