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

Code before:
                'id': '196',
                'ext': 'flv',
            },
            'params': {
                'skip_download': True,
            },
            'skip': 'extractor won\'t work while stream is not live'
        }, {
            'url': 'https://live.bilibili.com/196?broadcast_type=0&is_room_feed=1?spm_id_from=333.999.space_home.strengthen_live_card.click',
            'only_matching': True
        }
    ]

    def _call_api_base(self, path, video_id):
        json = self._download_json('https://api.live.bilibili.com' + path, video_id)
        if json.get('code') != 0:
            if json.get('message'):
                raise ExtractorError(json.get('message'))
            else:
                raise ExtractorError('Unable to download JSON metadata')
        return json.get('data')

    def _call_api_info(self, room_id):
        json = self._call_api_base(f'/room/v1/Room/get_info?id={room_id}', room_id)
        if json.get('live_status') == 0:
            raise ExtractorError('Streamer is not live')
        return json

    def _call_api_stream(self, room_id, qn):
        json = self._call_api_base(f'/xlive/web-room/v2/index/getRoomPlayInfo?room_id={room_id}&no_playurl=0&mask=0&qn={qn}&platform=web&protocol=0,1&format=0,2&codec=0,1',
                                   room_id)
        return json

    def _real_extract(self, url):
        room_id = self._match_valid_url(url).group('room_id')
        room_data = self._call_api_info(room_id)
        formats = []
        qns = [
            {
                'qn': 80,
                'desc': '流畅',
                'quality': -5,
            }, {
                'qn': 150,
                'desc': '高清',
                'quality': -4,
            }, {
                'qn': 250,
                'desc': '超清',
                'quality': -3,
            }, {

Review comment (refactor):
```suggestion
            raise ExtractorError('Streamer is not live', expected=True)
```

Code after:
            'id': '33989',
            'description': "周六杂谈回，其他时候随机游戏。 | \n录播：@下播型泛式录播组。 | \n直播通知群（全员禁言）：666906670，902092584，59971⑧481 （功能一样，别多加）",
            'ext': 'flv',
            'title': "太空狼人杀联动，不被爆杀就算赢",
            'thumbnail': "https://i0.hdslb.com/bfs/live/new_room_cover/e607bc1529057ef4b332e1026e62cf46984c314d.jpg",
            'timestamp': 1650802769,
        },
        'skip': 'not live'
    }, {
        'url': 'https://live.bilibili.com/196?broadcast_type=0&is_room_feed=1?spm_id_from=333.999.space_home.strengthen_live_card.click',
        'only_matching': True
    }]

    _FORMATS = {
        80: {'format_id': 'low', 'format_note': '流畅'},
        150: {'format_id': 'high_res', 'format_note': '高清'},
        250: {'format_id': 'ultra_high_res', 'format_note': '超清'},
        400: {'format_id': 'blue_ray', 'format_note': '蓝光'},
        10000: {'format_id': 'source', 'format_note': '原画'},
        20000: {'format_id': '4K', 'format_note': '4K'},
        30000: {'format_id': 'dolby', 'format_note': '杜比'},
    }

    _quality = staticmethod(qualities(list(_FORMATS)))

    def _call_api(self, path, room_id, query):
        api_result = self._download_json(f'https://api.live.bilibili.com/{path}', room_id, query=query)
        if api_result.get('code') != 0:
            raise ExtractorError(api_result.get('message') or 'Unable to download JSON metadata')
        return api_result.get('data') or {}

    def _parse_formats(self, qn, fmt):
        for codec in fmt.get('codec') or []:
            if codec.get('current_qn') != qn:
                continue
            for url_info in codec['url_info']:
                yield {
                    'url': f'{url_info["host"]}{codec["base_url"]}{url_info["extra"]}',
                    'ext': fmt.get('format_name'),
                    'vcodec': codec.get('codec_name'),
                    'quality': self._quality(qn),
                    **self._FORMATS[qn],
                }

    def _real_extract(self, url):
        room_id = self._match_id(url)
        room_data = self._call_api('room/v1/Room/get_info', room_id, {'id': room_id})
        if room_data.get('live_status') == 0:
            raise ExtractorError('Streamer is not live', expected=True)

        formats = []