PyWidevine
raw JSON → 1.9.0 verified Sat May 09 auth: no python
A Python implementation of the Widevine Content Decryption Module (CDM) for decrypting DRM-protected content. Version 1.9.0 supports Python 3.9–3.14. Regular releases, roughly quarterly.
pip install pywidevine Common errors
error ModuleNotFoundError: No module named 'yaml' ↓
cause yaml dependency was not installed unless the 'serve' extras were used. Fixed in v1.8.0.
fix
Upgrade to v1.8.0+ or install yaml separately: pip install pyyaml
error AttributeError: module 'pywidevine' has no attribute 'Cdm' ↓
cause Importing incorrectly; Cdm is only available in top-level since v1.5.1. Older versions or wrong import path.
fix
Use correct import: from pywidevine import Cdm. Or upgrade to v1.5.1+. Alternatively use: from pywidevine.cdm import Cdm
error pywidevine.exceptions.InvalidInitData: Failed to parse PSSH data ↓
cause PSSH data is malformed or not base64 encoded correctly.
fix
Ensure PSSH string is a valid base64-encoded Widevine PSSH box. Use PSSH() with raw bytes or a valid base64 string.
Warnings
breaking Dropped Python 3.8 support in v1.9.0. Python 3.7 dropped in v1.8.0. ↓
fix Upgrade to Python 3.9 or newer. If you must use older Python, pin to pywidevine<1.9.0.
gotcha The 'serve' extras group installs flask and yaml. Without it, yaml dependency was missing before v1.8.0, causing import errors if YAML config is used. ↓
fix Upgrade to v1.8.0+ or install with extras: pip install pywidevine[serve]
gotcha License responses must be the raw bytes returned from the license server, not base64-encoded strings. ↓
fix Ensure the response is passed as bytes, e.g., response.content from requests.
Install
pip install pywidevine[serve] Imports
- Cdm wrong
from pywidevine.cdm import Cdmcorrectfrom pywidevine import Cdm - PSSH wrong
from pywidevine.pssh import PSSHcorrectfrom pywidevine import PSSH - Device wrong
from pywidevine.device import Devicecorrectfrom pywidevine import Device
Quickstart
from pywidevine import Cdm, Device, PSSH
# Load a Widevine device (WVD file)
device = Device.load('path/to/device.wvd')
# Create a CDM session
cdm = Cdm()
session = cdm.open(device)
# Set a service certificate (optional, for privacy mode)
cert_data = b'...' # your service certificate bytes
cdm.set_service_certificate(session, cert_data)
# Obtain a license challenge from a PSSH box
pssh = PSSH(pssh='AAAAWnBzc2gAAAAA...')
challenge = cdm.get_license_challenge(session, pssh)
# Send the challenge to your license server, get license response
license_response = b'...' # bytes from server
cdm.parse_license(session, license_response)
# Extract content keys
for key in cdm.get_keys(session):
print(f'{key.type}: {key.kid.hex} -> {key.key.hex()}')
cdm.close(session)