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

Code before:
    def json2srt(self, json):
        data = '\n\n'.join(
            f'{i + 1}\n{srt_subtitles_timecode(line["from"])} --> {srt_subtitles_timecode(line["to"])}\n{line["content"]}'
            for i, line in enumerate(traverse_obj(json, (
                'body', lambda _, l: l['content'] and l['from'] and l['to']))))
        return data

    def _get_subtitles(self, *, ep_id=None, aid=None):
        subtitles = {}
        sub_json = self._call_api(
            '/web/v2/subtitle', ep_id or aid, fatal=False,
            note='Downloading subtitles list', errnote='Unable to download subtitles list',
            query=filter_dict({
                'platform': 'web',
                's_locale': 'en_US',
                'episode_id': ep_id,
                'aid': aid,
            })) or {}

        for sub in traverse_obj(sub_json, ('subtitles', lambda _, v: v['url'])):
            sub_ext = determine_ext(sub['url'])
            sub_lang = sub.get('lang_key') or 'en'

            sub_data = self._download_webpage(
                sub['url'], ep_id or aid, fatal=False,
                encoding='utf-8-sig' if sub_ext == 'ass' else None,
                note=f'Downloading subtitles{format_field(sub, "lang", "for %s")} ({sub_lang})',
                errnote='Unable to download subtitles')
            if sub_ext != 'ass':
                sub_ext, sub_data = 'srt', self.json2srt(sub_data)

            if sub_data:
                subtitles.setdefault(sub_lang, []).append({
                    'ext': sub_ext,
                    'data': sub_data
                })

        for sub in traverse_obj(sub_json, ('video_subtitle', ..., {dict})):
            sub_lang = sub.get('lang_key') or 'en'
            if sub.get('ass'):
                subtitles.setdefault(sub_lang, []).append({
                    'ext': 'ass',
                    'url': traverse_obj(sub, ('ass', 'url'))
                })

            if sub.get('srt'):
                sub_data = self._download_json(
                    traverse_obj(sub, ('srt', 'url')), ep_id or aid, fatal=False,
                    note=f'Downloading subtitles{format_field(sub, "lang", "for %s")} ({sub_lang})',
                    errnote='Unable to download subtitles')
                if sub_data:

Review comment (suggestion):
Specifying the encoding like this is not necessary for `_download_webpage`.
```suggestion
```
If it's not specified/specifying `utf-8`, the BOM will be part of the returned string. While with `utg-8-sig` the BOM will be removed. Having the BOM removed from the string would cause the file to be written without a BOM.
This is not what we want, since ASS files should generally be written with CRLF and a BOM.

Code after:
                'id': '229832',
                'ext': 'mp4',
                'title': '1 - 课程先导片',
                'alt_title': '视频课 · 3分41秒',
                'uploader': '马督工',
                'uploader_id': '316568752',
                'episode': '课程先导片',
                'episode_id': '229832',
                'episode_number': 1,
                'duration': 221,
                'timestamp': 1695549606,
                'upload_date': '20230924',
                'thumbnail': r're:^https?://.*\.(jpg|jpeg|png)$',
                'view_count': int,
            }
        }],
        'params': {'playlist_items': '1'},
    }, {
        'url': 'https://www.bilibili.com/cheese/play/ss5918',
        'info_dict': {
            'id': '5918',
            'title': '【限时五折】新闻系学不到：马督工教你做自媒体',
            'description': '帮普通人建立世界模型，降低人与人的沟通门槛',
        },
        'playlist_mincount': 5,
        'skip': 'paid video in list',
    }]

    def _get_cheese_entries(self, season_info):
        for ep_id in traverse_obj(season_info, ('episodes', lambda _, v: v['episode_can_view'], 'id')):
            yield self._extract_episode(season_info, ep_id)

    def _real_extract(self, url):
        season_id = self._match_id(url)
        season_info = self._download_season_info('season_id', season_id)

        return self.playlist_result(
            self._get_cheese_entries(season_info), season_id,
            **traverse_obj(season_info, {
                'title': ('title', {str}),
                'description': ('subtitle', {str}),
            }))


class BilibiliSpaceBaseIE(InfoExtractor):
    def _extract_playlist(self, fetch_page, get_metadata, get_entries):
        first_page = fetch_page(0)
        metadata = get_metadata(first_page)

        paged_list = InAdvancePagedList(
            lambda idx: get_entries(fetch_page(idx) if idx else first_page),