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

Code before:
                            item_desc = '\n\n'.join(summary)
                    item_time = None
                    for meta in try_get(media, lambda x: x['metadata']['items'], list) or []:
                        if try_get(meta, lambda x: x['label']) == 'Published':
                            item_time = unified_timestamp(meta.get('timestamp'))
                            break
                    entries.append({
                        'id': item_id,
                        'title': item_title,
                        'thumbnail': item.get('holdingImageUrl'),
                        'formats': formats,
                        'subtitles': subtitles,
                        'timestamp': item_time,
                        'description': strip_or_none(item_desc),
                    })

            for resp in (initial_data.get('data') or {}).values():
                name = resp.get('name')
                if name == 'media-experience':
                    parse_media(try_get(resp, lambda x: x['data']['initialItem']['mediaItem'], dict))
                elif name == 'article':
                    for block in traverse_obj(resp, (
                            'data', (None, ('content', 'model')), 'blocks',
                            lambda _, v: v.get('type') in {'media', 'video'},
                            'model', {dict})):
                        parse_media(block)
            return self.playlist_result(
                entries, playlist_id, playlist_title, playlist_description)

        def extract_all(pattern):
            return list(filter(None, map(
                lambda s: self._parse_json(s, playlist_id, fatal=False),
                re.findall(pattern, webpage))))

        # US accessed article with single embedded video (e.g.
        # https://www.bbc.com/news/uk-68546268)
        next_data = traverse_obj(self._search_nextjs_data(webpage, playlist_id, default={}),
                                 ('props', 'pageProps', 'page'))
        model = traverse_obj(next_data, (
            ..., 'contents', is_type('video'),
            'model', 'blocks', is_type('media'),
            'model', 'blocks', is_type('mediaMetadata'),
            'model', {dict}, any))
        if model:
            if entry := parse_model(model):
                if not entry.get('timestamp'):
                    entry['timestamp'] = traverse_obj(next_data, (
                        ..., 'contents', is_type('timestamp'),
                        'model', 'timestamp', {k_int_or_none}, any))
                entries.append(entry)
                return self.playlist_result(

Review comment (refactor):
Support SIMORGH_DATA:
```suggestion

        # extract from SIMORGH_DATA hydration JSON 
        simorgh_data = self._search_json(
            r'window\s*\.\s*SIMORGH_DATA\s*=', webpage,
            'simorgh data', playlist_id, default={})
        if simorgh_data:
            added, done = False, False
            for video_data in traverse_obj(simorgh_data, (
                    'pageData', 'content', 'model', 'blocks', is_type('video', 'legacyMedia'))):
                model = traverse_obj(video_data, (
                    'model', 'blocks', is_type('aresMedia'),
                    'model', 'blocks', is_type('aresMediaMetadata'),
                    'model', {dict}, any))
                if video_data['type'] == 'video':
                    entry = parse_model(model)
                else:  # legacyMedia: no duration, subtitles
                    block_id, entry = traverse_obj(model, ('blockId', {str})), None
                    media_data = traverse_obj(simorgh_data, (
                        'pageData', 'promo', 'media',
                        {lambda x: x if x['id'] == block_id else None}))
                    formats = traverse_obj(media_data, ('playlist', lambda _, v: v['url'], {
                        'url': ('url', {url_or_none}),
                        'ext': ('format', {str}),
                        'tbr': ('bitrate', {k_int_or_none}),
                    }, {lambda u: u.get('url') and u}))
                    if formats:
                        entry = merge_dicts({
                            'id': block_id,
                            'display_id': playlist_id,
                            'formats': formats,
                        }, traverse_obj(simorgh_data, ('pageData', 'promo', {
                            'description': ('summary', {str}),
                        })), traverse_obj(model, {
                                'title': ('title', {str}),
                                'thumbnail': ('imageUrl', {lambda u: urljoin(url, u.replace('$recipe', 'raw'))}),
                                'description': (
                                    'synopses', ('long', 'medium', 'short'), {str}, any),
                                'timestamp': ('firstPublished', {k_int_or_none}),
                            }),
                        )
                        done = True

                if entry:
                    entries.append(entry)
                    added = True
                if done:
                    break
            if added:
                return self.playlist_result(
                    entries, playlist_id, playlist_title, playlist_description)

```

Code after:
                        if try_get(meta, lambda x: x['label']) == 'Published':
                            item_time = unified_timestamp(meta.get('timestamp'))
                            break
                    entries.append({
                        'id': item_id,
                        'title': item_title,
                        'thumbnail': item.get('holdingImageUrl'),
                        'formats': formats,
                        'subtitles': subtitles,
                        'timestamp': item_time,
                        'description': strip_or_none(item_desc),
                        'duration': int_or_none(item.get('duration')),
                    })

            for resp in traverse_obj(initial_data, ('data', lambda _, v: v['name'])):
                name = resp['name']
                if name == 'media-experience':
                    parse_media(try_get(resp, lambda x: x['data']['initialItem']['mediaItem'], dict))
                elif name == 'article':
                    for block in traverse_obj(resp, (
                            'data', (None, ('content', 'model')), 'blocks',
                            is_type('media', 'video'), 'model', {dict})):
                        parse_media(block)
            return self.playlist_result(
                entries, playlist_id, playlist_title, playlist_description)

        # extract from SIMORGH_DATA hydration JSON
        simorgh_data = self._search_json(
            r'window\s*\.\s*SIMORGH_DATA\s*=', webpage,
            'simorgh data', playlist_id, default={})
        if simorgh_data:
            done = False
            for video_data in traverse_obj(simorgh_data, (
                    'pageData', 'content', 'model', 'blocks', is_type('video', 'legacyMedia'))):
                model = traverse_obj(video_data, (
                    'model', 'blocks', is_type('aresMedia'),
                    'model', 'blocks', is_type('aresMediaMetadata'),
                    'model', {dict}, any))
                if video_data['type'] == 'video':
                    entry = parse_model(model)
                else:  # legacyMedia: no duration, subtitles
                    block_id, entry = traverse_obj(model, ('blockId', {str})), None
                    media_data = traverse_obj(simorgh_data, (
                        'pageData', 'promo', 'media',
                        {lambda x: x if x['id'] == block_id else None}))
                    formats = traverse_obj(media_data, ('playlist', lambda _, v: url_or_none(v['url']), {
                        'url': ('url', {url_or_none}),
                        'ext': ('format', {str}),
                        'tbr': ('bitrate', {functools.partial(int_or_none, scale=1000)}),
                    }))
                    if formats: