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

Code before:
                'uploader_id': ('upData', 'mid', {str_or_none}),
                'like_count': ('videoData', 'stat', 'like', {int_or_none}),
                'tags': ('tags', ..., 'tag_name'),
                'thumbnail': ('videoData', 'pic', {url_or_none}),
            }),
            **festival_info,
            **traverse_obj(video_data, {
                'description': 'desc',
                'timestamp': ('pubdate', {int_or_none}),
                'view_count': (('viewCount', ('stat', 'view')), {int_or_none}),
                'comment_count': ('stat', 'reply', {int_or_none}),
            }, get_all=False),
            'id': f'{video_id}{format_field(part_id, None, "_p%d")}',
            'formats': self.extract_formats(play_info),
            '_old_archive_ids': [make_archive_id(self, old_video_id)] if old_video_id else None,
            'title': title,
            'duration': float_or_none(play_info.get('timelength'), scale=1000),
            'chapters': self._get_chapters(aid, cid),
            'subtitles': self.extract_subtitles(video_id, aid, cid),
            '__post_extractor': self.extract_comments(aid),
            'http_headers': {'Referer': url},
        }


class BiliBiliBangumiEpisodeIE(BilibiliBaseIE):
    _VALID_URL = r'(?x)https?://www\.bilibili\.com/bangumi/play/ep(?P<id>\d+)$'

    _TESTS = [{
        'url': 'https://www.bilibili.com/bangumi/play/ep267851',
        'info_dict': {
            'id': '267851',
            'ext': 'mp4',
            'series': '鬼灭之刃',
            'series_id': '4358',
            'season': '鬼灭之刃',
            'season_id': '26801',
            'season_number': 1,
            'episode': '残酷',
            'episode_id': '267851',
            'episode_number': 1,
            'title': '1 残酷',
            'duration': 1425.256,
            'timestamp': 1554566400,
            'upload_date': '20190406',
            'thumbnail': r're:^https?://.*\.(jpg|jpeg|png)$'
        },
        'skip': 'According to the copyright owner\'s request, you may only watch the video after you are premium member.'
    }]

    def _real_extract(self, url):
        video_id = self._match_id(url)

Review comment (refactor):
Don't change the extractor name; it would break users' download archives. Revert the extractor name change in `_extractors.py` too.

For the same reason, we don't want to change the `video_id` that is returned in the info_dict, but I understand that we need to omit the 'ep' prefix for the API queries. So let's match the old prefixed ID from the URL, and then trim the prefix in `_real_extract`.

also let's clean up some of the old code: `(?x)` flag is unnecessary, `www` subdomain should be optional

```suggestion
class BiliBiliBangumiIE(BilibiliBaseIE):
    _VALID_URL = r'https?://(?:www\.)?bilibili\.com/bangumi/play/(?P<id>ep\d+)/?(?:$|[#?])'
```

and then below in `_real_extract`:
```diff
     def _real_extract(self, url):
         video_id = self._match_id(url)
+        episode_id = video_id[2:]
         webpage = self._download_webpage(url, video_id)
```


Code after:
                note='Extracting festival video formats')['data']

            festival_info = traverse_obj(initial_state, {
                'uploader': ('videoInfo', 'upName'),
                'uploader_id': ('videoInfo', 'upMid', {str_or_none}),
                'like_count': ('videoStatus', 'like', {int_or_none}),
                'thumbnail': ('sectionEpisodes', lambda _, v: v['bvid'] == video_id, 'cover'),
            }, get_all=False)

        return {
            **traverse_obj(initial_state, {
                'uploader': ('upData', 'name'),
                'uploader_id': ('upData', 'mid', {str_or_none}),
                'like_count': ('videoData', 'stat', 'like', {int_or_none}),
                'tags': ('tags', ..., 'tag_name'),
                'thumbnail': ('videoData', 'pic', {url_or_none}),
            }),
            **festival_info,
            **traverse_obj(video_data, {
                'description': 'desc',
                'timestamp': ('pubdate', {int_or_none}),
                'view_count': (('viewCount', ('stat', 'view')), {int_or_none}),
                'comment_count': ('stat', 'reply', {int_or_none}),
            }, get_all=False),
            'id': f'{video_id}{format_field(part_id, None, "_p%d")}',
            'formats': self.extract_formats(play_info),
            '_old_archive_ids': [make_archive_id(self, old_video_id)] if old_video_id else None,
            'title': title,
            'duration': float_or_none(play_info.get('timelength'), scale=1000),
            'chapters': self._get_chapters(aid, cid),
            'subtitles': self.extract_subtitles(video_id, aid, cid),
            '__post_extractor': self.extract_comments(aid),
            'http_headers': {'Referer': url},
        }


class BiliBiliBangumiIE(BilibiliBaseIE):
    _VALID_URL = r'https?://(?:www\.)?bilibili\.com/bangumi/play/(?P<id>ep\d+)'

    _TESTS = [{
        'url': 'https://www.bilibili.com/bangumi/play/ep267851',
        'info_dict': {
            'id': '267851',
            'ext': 'mp4',
            'series': '鬼灭之刃',
            'series_id': '4358',
            'season': '鬼灭之刃',
            'season_id': '26801',
            'season_number': 1,
            'episode': '残酷',
            'episode_id': '267851',