{"record":{"id":"ca588e7b6c97c6ac","repo":"yt-dlp/yt-dlp","slug":"unable-to-download-js-dependency-crypto-js-md5","errorCode":null,"errorMessage":"Unable to download JS dependency (crypto-js/md5)","messagePattern":"Unable to download JS dependency \\(crypto-js/md5\\)","errorType":"exception","errorClass":"ExtractorError","httpStatus":null,"severity":"error","filePath":"yt_dlp/extractor/douyutv.py","lineNumber":36,"sourceCode":"    url_or_none,\n    urlencode_postdata,\n    urljoin,\n)\n\n\nclass DouyuBaseIE(InfoExtractor):\n    def _download_cryptojs_md5(self, video_id):\n        for url in [\n            # XXX: Do NOT use cdn.bootcdn.net; ref: https://sansec.io/research/polyfill-supply-chain-attack\n            'https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/rollups/md5.js',\n            'https://unpkg.com/cryptojslib@3.1.2/rollups/md5.js',\n        ]:\n            js_code = self._download_webpage(\n                url, video_id, note='Downloading signing dependency', fatal=False)\n            if js_code:\n                self.cache.store('douyu', 'crypto-js-md5', js_code)\n                return js_code\n        raise ExtractorError('Unable to download JS dependency (crypto-js/md5)')\n\n    def _get_cryptojs_md5(self, video_id):\n        return self.cache.load(\n            'douyu', 'crypto-js-md5', min_ver='2024.07.04') or self._download_cryptojs_md5(video_id)\n\n    def _calc_sign(self, sign_func, video_id, a):\n        b = uuid.uuid4().hex\n        c = round(time.time())\n        js_script = f'{self._get_cryptojs_md5(video_id)};{sign_func};console.log(ub98484234(\"{a}\",\"{b}\",\"{c}\"))'\n        phantom = PhantomJSwrapper(self)\n        result = phantom.execute(js_script, video_id,\n                                 note='Executing JS signing script').strip()\n        return {i: v[0] for i, v in urllib.parse.parse_qs(result).items()}\n\n    def _search_js_sign_func(self, webpage, fatal=True):\n        # The greedy look-behind ensures last possible script tag is matched\n        return self._search_regex(\n            r'(?:<script.*)?<script[^>]*>(.*?ub98484234.*?)</script>', webpage, 'JS sign func', fatal=fatal)","sourceCodeStart":18,"sourceCodeEnd":54,"githubUrl":"https://github.com/yt-dlp/yt-dlp/blob/81ecd58b1394793e6da9998cc19fdb45657f1685/yt_dlp/extractor/douyutv.py#L18-L54","documentation":"Douyu signing dependency failure. Douyu stream URLs require an MD5-based signature computed by executing crypto-js 3.1.2 inside PhantomJS; DouyuBaseIE first downloads the md5 rollup from cdnjs.cloudflare.com or unpkg.com (both fatal=False) and caches it. If both downloads fail and no cache entry with min_ver 2024.07.04 exists, this unexpected error is raised. The bootcdn mirror was deliberately removed after the polyfill supply-chain attack.","triggerScenarios":"_download_webpage of both CDN URLs returns empty: cdnjs.cloudflare.com/unpkg.com unreachable (GFW, corporate proxy, DNS filtering), offline machine, or TLS interception; first run has no cached copy to fall back on.","commonSituations":"Running yt-dlp in mainland China where those CDNs are blocked; locked-down CI containers with no internet beyond douyu.com; air-gapped machines with a pre-min_ver cache.","solutions":["Check reachability of cdnjs.cloudflare.com and unpkg.com (curl) and fix proxy/DNS; pass --proxy if yt-dlp must route through one.","Update yt-dlp - the CDN list has changed before and may change again.","Run once from an unrestricted network to warm the 'douyu/crypto-js-md5' cache; later runs reuse it.","Note PhantomJS is required for the next step anyway - run yt-dlp --update-to nightly / check requirements so JS execution works."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"import urllib.request\n\n\ndef cdn_reachable(url: str = 'https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/rollups/md5.js') -> bool:\n    try:\n        urllib.request.urlopen(url, timeout=10)\n        return True\n    except OSError:\n        return False","typeGuard":null,"tryCatchPattern":"from yt_dlp.utils import ExtractorError\n\nfor attempt in range(3):\n    try:\n        info = ydl.extract_info(url, download=False)\n        break\n    except ExtractorError as e:\n        if 'JS dependency' not in str(e) or attempt == 2:\n            raise\n        time.sleep(2 ** attempt)","preventionTips":["Warm the douyu cache once from an unrestricted network so later runs need no CDN.","Configure --proxy in environments where cdnjs/unpkg are blocked."],"tags":["douyu","cdn","crypto-js","phantomjs","dependency-download"],"backgroundTag":"dependency-download-failed","analyzedSha":"81ecd58b1394793e6da9998cc19fdb45657f1685","analyzedAt":"2026-08-22T12:21:25.439Z","schemaVersion":2},"datasetVersion":"2026-08-22T14:17:55.899Z"}