ytdl-org/youtube-dl · error · ExtractorError

This video is DRM protected.

Error message

This video is DRM protected.

What it means

Raised by the 9c9media (CBC etc.) extractor when the content package's Constraints.Security.Type is truthy — the platform flags the package as DRM-protected. HLS/HDS/DASH manifests may still be fetched, but streams are encrypted, so the extractor refuses up front. Marked expected=True.

Source

Thrown at youtube_dl/extractor/ninecninemedia.py:38

    _API_BASE_TEMPLATE = 'http://capi.9c9media.com/destinations/%s/platforms/desktop/contents/%s/'

    def _real_extract(self, url):
        destination_code, content_id = re.match(self._VALID_URL, url).groups()
        api_base_url = self._API_BASE_TEMPLATE % (destination_code, content_id)
        content = self._download_json(api_base_url, content_id, query={
            '$include': '[Media.Name,Season,ContentPackages.Duration,ContentPackages.Id]',
        })
        title = content['Name']
        content_package = content['ContentPackages'][0]
        package_id = content_package['Id']
        content_package_url = api_base_url + 'contentpackages/%s/' % package_id
        content_package = self._download_json(
            content_package_url, content_id, query={
                '$include': '[HasClosedCaptions]',
            })

        if try_get(content_package, lambda x: x['Constraints']['Security']['Type']):
            raise ExtractorError('This video is DRM protected.', expected=True)

        manifest_base_url = content_package_url + 'manifest.'
        formats = []
        formats.extend(self._extract_m3u8_formats(
            manifest_base_url + 'm3u8', content_id, 'mp4',
            'm3u8_native', m3u8_id='hls', fatal=False))
        formats.extend(self._extract_f4m_formats(
            manifest_base_url + 'f4m', content_id,
            f4m_id='hds', fatal=False))
        formats.extend(self._extract_mpd_formats(
            manifest_base_url + 'mpd', content_id,
            mpd_id='dash', fatal=False))
        self._sort_formats(formats)

        thumbnails = []
        for image in (content.get('Images') or []):
            image_url = image.get('Url')
            if not image_url:

View on GitHub (pinned to 956b8c5855)

Solutions

  1. Accept it: DRM content cannot be downloaded with youtube-dl/yt-dlp; use the platform's official offline/app option if available.
  2. Look for a non-DRM on-demand replay of the same program (news clips are often unprotected while live streams are not).
  3. Do not retry with proxies/cookies — DRM is independent of region and session.
Defensive patterns

Strategy: try-catch

Validate before calling

import requests
pkg = requests.get(f'{api_base}contentpackages/{package_id}/', params={'$include': '[HasClosedCaptions]'}).json()
if (pkg.get('Constraints') or {}).get('Security', {}).get('Type'):
    print('DRM protected — not downloadable')

Try / catch

except ExtractorError as e:
    if e.expected and 'DRM' in str(e):
        give_up_permanently(url)  # no proxy/cookie/retry will help
    else:
        raise

Prevention

When it happens

Trigger: Downloading any 9c9media-backed content whose contentpackages API response has Constraints.Security.Type set (e.g. Widevine/PlayReady-protected broadcasts).

Common situations: Premium or live CBC/GEM content; syndicated shows with DRM requirements; attempts to archive streaming platforms' protected catalogs — by design not downloadable by youtube-dl.

Related errors


AI-assisted analysis of ytdl-org/youtube-dl@956b8c5855 (2026-08-14). Data as JSON: /api/errors/c86585a1a9492d8d. Report an issue: GitHub.