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

Code before:
    int_or_none,
    str_or_none,
    url_or_none,
    traverse_obj,
    determine_ext,
    parse_resolution,
    urlencode_postdata,
    unescapeHTML,
    js_to_json,
    urljoin,
)


class DouyuBaseIE(InfoExtractor):
    _cryptojs_md5 = None

    def _get_cryptojs_md5(self, video_id):
        for url in [
            'https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/rollups/md5.js',
            'https://cdn.bootcdn.net/ajax/libs/crypto-js/3.1.2/rollups/md5.js',
        ]:
            if DouyuBaseIE._cryptojs_md5:
                break
            DouyuBaseIE._cryptojs_md5 = self._download_webpage(
                url, video_id, note='Downloading signing dependency', fatal=False)
        return DouyuBaseIE._cryptojs_md5

    def _calc_sign(self, sign_func, a, b, c, video_id):
        js_script = f'{self._get_cryptojs_md5(video_id)};{sign_func};console.log(ub98484234("{a}","{b}","{c}"))'
        phantom = PhantomJSwrapper(self)
        result = phantom.execute(js_script, video_id,
                                 note='Executing JS signing script').strip()
        return {i: v[0] for i, v in urllib.parse.parse_qs(result).items()}


class DouyuTVIE(DouyuBaseIE):
    IE_DESC = '斗鱼直播'
    _VALID_URL = r'https?://(?:www\.)?douyu(?:tv)?\.com/(topic/\w+\?rid=|(?:[^/]+/))*(?P<id>[A-Za-z0-9]+)'
    _TESTS = [{
        'url': 'https://www.douyu.com/pigff',
        'info_dict': {
            'id': '24422',
            'display_id': 'pigff',
            'ext': 'ts',
            'title': 're:^【PIGFF】.* [0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}$',
            'description': r'≥15级牌子看鱼吧置顶帖进粉丝vx群',
            'thumbnail': str,
            'uploader': 'pigff',
            'is_live': True,
            'live_status': 'is_live',
        },

Review comment (performance):
Better to cache on disk. See https://github.com/yt-dlp/yt-dlp/blob/a250b247334ce9f641e709cbb64974da6034a2b3/yt_dlp/extractor/youtube.py#L3124-L3127

or

https://github.com/yt-dlp/yt-dlp/blob/a250b247334ce9f641e709cbb64974da6034a2b3/yt_dlp/extractor/iqiyi.py#L524-L544

Code after:
    UserNotLive,
    determine_ext,
    int_or_none,
    js_to_json,
    parse_resolution,
    str_or_none,
    traverse_obj,
    unescapeHTML,
    url_or_none,
    urlencode_postdata,
    urljoin,
)


class DouyuBaseIE(InfoExtractor):
    def _download_cryptojs_md5(self, video_id):
        for url in [
            'https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/rollups/md5.js',
            'https://cdn.bootcdn.net/ajax/libs/crypto-js/3.1.2/rollups/md5.js',
        ]:
            js_code = self._download_webpage(
                url, video_id, note='Downloading signing dependency', fatal=False)
            if js_code:
                self.cache.store('douyu', 'crypto-js-md5', js_code)
                return js_code
        raise ExtractorError('Unable to download JS dependency (crypto-js/md5)')

    def _get_cryptojs_md5(self, video_id):
        return self.cache.load('douyu', 'crypto-js-md5') or self._download_cryptojs_md5(video_id)

    def _calc_sign(self, sign_func, video_id, a):
        b = uuid.uuid4().hex
        c = round(time.time())
        js_script = f'{self._get_cryptojs_md5(video_id)};{sign_func};console.log(ub98484234("{a}","{b}","{c}"))'
        phantom = PhantomJSwrapper(self)
        result = phantom.execute(js_script, video_id,
                                 note='Executing JS signing script').strip()
        return {i: v[0] for i, v in urllib.parse.parse_qs(result).items()}

    def _search_js_sign_func(self, webpage, fatal=True):
        # The greedy look-behind ensures last possible script tag is matched
        return self._search_regex(
            r'(?:<script.*)?<script[^>]*>(.*?ub98484234.*?)</script>', webpage, 'JS sign func', fatal=fatal)


class DouyuTVIE(DouyuBaseIE):
    IE_DESC = '斗鱼直播'
    _VALID_URL = r'https?://(?:www\.)?douyu(?:tv)?\.com/(topic/\w+\?rid=|(?:[^/]+/))*(?P<id>[A-Za-z0-9]+)'
    _TESTS = [{
        'url': 'https://www.douyu.com/pigff',
        'info_dict': {