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
- Open https://muse.ai/embed/<id> in a logged-out browser and confirm the video still plays
- Update yt-dlp to latest nightly in case the player-data JSON shape changed
- If the video needs an account, pass cookies: --cookies-from-browser BROWSER or --cookies FILE
- Check for DRM (Widevine) - yt-dlp cannot download DRM streams and this error is the usual symptom
- 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
- Validate muse.ai share links by loading the embed page (HTTP 200 + plays) before queueing downloads
- Pass cookies for account-gated videos rather than relying on anonymous access
- Handle 'no source URL' as a permanent condition: do not blind-retry it
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
- canonical URL not found
- Unable to find embedded YouTube video.
- Unsafe placeholder for exec command: {na!r} The --output-na-
- Unsafe default(s) in exec command: {outtmpl!r} Conversions a
- Invalid syntax in Cookie Header
AI-assisted analysis of yt-dlp/yt-dlp@81ecd58b13 (2026-08-22).
Data as JSON: /api/errors/7e68e439fa32d45f.
Report an issue: GitHub.