yt-dlp/yt-dlp · error · ExtractorError

Unable to extract video URL

Error message

Unable to extract video URL

What it means

Thrown by MuseAIIE._real_extract (yt_dlp/extractor/museai.py:78). The embed page was downloaded and its player data (the JSON inside player.setData(...)) was parsed, but data['url'] - the media source URL - is not a valid URL (url_or_none fails, typically empty string). With no source URL there is no format to build, so extraction aborts.

Source

Thrown at yt_dlp/extractor/museai.py:78

        'params': {'allowed_extractors': ['all', '-html5']},
    }]
    _EMBED_REGEX = [r'<iframe[^>]*\bsrc=["\'](?P<url>https://muse\.ai/embed/\w+)']

    @classmethod
    def _extract_embed_urls(cls, url, webpage):
        yield from super()._extract_embed_urls(url, webpage)
        for embed_id in re.findall(r'<script>[^<]*\bMusePlayer\(\{[^}<]*\bvideo:\s*["\'](\w+)["\']', webpage):
            yield f'https://muse.ai/embed/{embed_id}'

    def _real_extract(self, url):
        video_id = self._match_id(url)
        webpage = self._download_webpage(f'https://muse.ai/embed/{video_id}', video_id)
        data = self._search_json(
            r'player\.setData\(', webpage, 'player data', video_id, transform_source=js_to_json)

        source_url = data['url']
        if not url_or_none(source_url):
            raise ExtractorError('Unable to extract video URL')

        formats = [{
            'url': source_url,
            'format_id': 'source',
            'quality': 1,
            **traverse_obj(data, {
                'ext': ('filename', {determine_ext}),
                'width': ('width', {int_or_none}),
                'height': ('height', {int_or_none}),
                'filesize': ('size', {int_or_none}),
            }),
        }]
        if source_url.endswith('/data'):
            base_url = f'{source_url[:-5]}/videos'
            formats.extend(self._extract_m3u8_formats(
                f'{base_url}/hls.m3u8', video_id, m3u8_id='hls', fatal=False))
            formats.extend(self._extract_mpd_formats(
                f'{base_url}/dash.mpd', video_id, mpd_id='dash', fatal=False))

View on GitHub (pinned to 81ecd58b13)

Solutions

  1. Open https://muse.ai/embed/<id> in a logged-out browser and confirm the video still plays
  2. Update yt-dlp to latest nightly in case the player-data JSON shape changed
  3. If the video needs an account, pass cookies: --cookies-from-browser BROWSER or --cookies FILE
  4. Check for DRM (Widevine) - yt-dlp cannot download DRM streams and this error is the usual symptom
  5. If it plays in a browser but still fails, report with --verbose to yt-dlp
Defensive patterns

Strategy: try-catch

Try / catch

from yt_dlp.utils import ExtractorError
try:
    info = ydl.extract_info(url, download=False)
except ExtractorError as e:
    if 'Unable to extract video URL' in str(e):
        # player data parsed but data['url'] not a URL: deleted/private/DRM video
        mark_unavailable(video_id)

Prevention

When it happens

Trigger: GET https://muse.ai/embed/<video_id> where the captured player JSON has an empty/absent 'url': deleted or private video, expired share link, DRM/protected stream that exposes no plain source, or a change in the embed page's JS data shape.

Common situations: Video deleted or made private after the link was saved; unauthenticated access to paid/protected muse.ai content; muse.ai changing the embed player payload so 'url' is no longer populated.

Related errors


AI-assisted analysis of yt-dlp/yt-dlp@81ecd58b13 (2026-08-22). Data as JSON: /api/errors/7e68e439fa32d45f. Report an issue: GitHub.