### Install Pre-commit Tooling Source: https://github.com/rlaphoenix/pywidevine/blob/master/CONTRIBUTING.md Installs the pre-commit framework, which helps ensure code quality and consistency by running checks before each commit. ```shell pre-commit install ``` -------------------------------- ### Install Project Dependencies with Poetry Source: https://github.com/rlaphoenix/pywidevine/blob/master/CONTRIBUTING.md This command installs all project dependencies and executables into a virtual environment managed by Poetry, ensuring the system's Python environment remains unaffected. ```shell poetry install ``` -------------------------------- ### CLI Operations for Device and License Management Source: https://context7.com/rlaphoenix/pywidevine/llms.txt Lists common CLI commands for testing CDMs, requesting licenses, managing device files, and starting the API server. ```bash pywidevine test /path/to/device.wvd pywidevine license /path/to/device.wvd "PSSH_DATA" "LICENSE_URL" --type STREAMING pywidevine create-device --type ANDROID --level 3 --key key.pem --client_id client.bin --output device.wvd pywidevine migrate /path/to/devices/ ``` -------------------------------- ### Configure and Start Remote CDM API Source: https://context7.com/rlaphoenix/pywidevine/llms.txt Provides the YAML configuration structure for the serve module and the command to launch the API server. ```yaml devices: - '/path/to/device1.wvd' - '/path/to/device2.wvd' users: fvYBh0C3fRAxlvyJcynD1see3GmNbIiC: username: jane devices: - device1 - device2 anotherSecretKey1234567890123456: username: john devices: - device1 force_privacy_mode: true ``` ```bash pywidevine serve /path/to/serve.yml -h 0.0.0.0 -p 8786 ``` -------------------------------- ### Install pywidevine via Package Managers Source: https://github.com/rlaphoenix/pywidevine/blob/master/README.md Instructions for installing the pywidevine library using standard Python package management tools like pip or the uv project manager. ```bash pip install pywidevine ``` ```bash uv run pywidevine --version ``` -------------------------------- ### GET /:device_name/open Source: https://context7.com/rlaphoenix/pywidevine/llms.txt Opens a new CDM session for the specified device. ```APIDOC ## GET /{device_name}/open ### Description Opens a new session on the specified device to begin the Widevine license acquisition process. ### Method GET ### Endpoint /{device_name}/open ### Parameters #### Path Parameters - **device_name** (string) - Required - The name of the device configured in the serve.yml file. ### Request Example GET /device1/open ### Response #### Success Response (200) - **session_id** (string) - The unique identifier for the opened session. - **device** (object) - Device metadata including system_id and security_level. #### Response Example { "data": { "session_id": "session_123", "device": { "system_id": 12345, "security_level": 3 } } } ``` -------------------------------- ### Configure Poetry to Install Virtual Environments in Project Source: https://github.com/rlaphoenix/pywidevine/blob/master/CONTRIBUTING.md This command configures Poetry to create virtual environments within the project directory, which helps IDEs detect them and avoids potential issues with duplicate environments. ```shell poetry config virtualenvs.in-project true ``` -------------------------------- ### Initialize CDM and Acquire License Keys Source: https://context7.com/rlaphoenix/pywidevine/llms.txt Demonstrates the standard workflow for initializing a CDM session, generating a license challenge from a PSSH, and parsing the license response to extract decryption keys. ```python from pywidevine.cdm import Cdm from pywidevine.device import Device from pywidevine.pssh import PSSH import requests device = Device.load("/path/to/device.wvd") cdm = Cdm.from_device(device) session_id = cdm.open() pssh = PSSH("AAAAW3Bzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAADsIARIQ62dqu8s0Xpa7z2FmMPGj2hoNd2lkZXZpbmVfdGVzdCIQZmtqM2xqYVNkZmFsa3IzaioCSEQyAA==") challenge = cdm.get_license_challenge(session_id=session_id, pssh=pssh, license_type="STREAMING", privacy_mode=True) response = requests.post(url="https://license-server.example.com/license", data=challenge, headers={"Content-Type": "application/octet-stream"}) response.raise_for_status() cdm.parse_license(session_id, response.content) for key in cdm.get_keys(session_id, type_="CONTENT"): print(f"Key ID: {key.kid.hex}") print(f"Key: {key.key.hex()}") cdm.close(session_id) ``` -------------------------------- ### Initializing CDM and Managing Sessions Source: https://github.com/rlaphoenix/pywidevine/blob/master/CHANGELOG.md Shows the updated workflow for using the Cdm class, which now requires initializing with device info, opening a session, and passing init_data to the license challenge method. ```python cdm = Cdm.from_device(device) session_id = cdm.open() challenge = cdm.get_license_challenge(session_id, init_data) # ... process license ... cdm.close(session_id) ``` -------------------------------- ### Perform License Acquisition and Decryption Source: https://context7.com/rlaphoenix/pywidevine/llms.txt A complete workflow demonstrating how to load a device, open a session, fetch a license, and parse it for decryption. ```python from pywidevine.cdm import Cdm from pywidevine.device import Device from pywidevine.pssh import PSSH import requests device = Device.load("/path/to/device.wvd") cdm = Cdm.from_device(device) session_id = cdm.open() pssh = PSSH("AAAAW3Bzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7Q...") challenge = cdm.get_license_challenge(session_id, pssh) response = requests.post("https://license-server.example.com", data=challenge) cdm.parse_license(session_id, response.content) ``` -------------------------------- ### Manage CDM Sessions and Keys Source: https://context7.com/rlaphoenix/pywidevine/llms.txt Demonstrates how to open a session, retrieve keys filtered by type, and close the session using the pywidevine library. ```python all_keys = remote_cdm.get_keys(session_id) content_keys = remote_cdm.get_keys(session_id, type_="CONTENT") for key in content_keys: print(f"[{key.type}] {key.kid.hex}:{key.key.hex()}") remote_cdm.close(session_id) ``` -------------------------------- ### Acquire License with pywidevine Source: https://github.com/rlaphoenix/pywidevine/blob/master/README.md A minimal script demonstrating how to load a device, create a CDM session, generate a license challenge, and parse the resulting license response to extract decryption keys. ```python from pywidevine.cdm import Cdm from pywidevine.device import Device from pywidevine.pssh import PSSH import requests pssh = PSSH("AAAAW3Bzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAADsIARIQ62dqu8s0Xpa7z2FmMPGj2hoNd2lkZXZpbmVfdGVzdCIQZmtqM2xqYVNkZmFsa3IzaioCSEQyAA==") device = Device.load("C:/Path/To/A/Provision.wvd") cdm = Cdm.from_device(device) session_id = cdm.open() challenge = cdm.get_license_challenge(session_id, pssh) licence = requests.post("https://cwip-shaka-proxy.appspot.com/no_auth", data=challenge) licence.raise_for_status() cdm.parse_license(session_id, licence.content) for key in cdm.get_keys(session_id): print(f"[{key.type}] {key.kid.hex}:{key.key.hex()}") cdm.close(session_id) ``` -------------------------------- ### Build Source and Wheel Distributions Source: https://github.com/rlaphoenix/pywidevine/blob/master/CONTRIBUTING.md This command builds both the source distribution (sdist) and the wheel distribution for the project. The built files are placed in the '/dist' directory. The `-f` flag can be used to build only sdist or wheel. ```shell poetry build ``` -------------------------------- ### Parse and Create PSSH Data Source: https://context7.com/rlaphoenix/pywidevine/llms.txt Demonstrates parsing PSSH from bytes, creating new PSSH objects with key IDs or custom init data, exporting PSSH to different formats, and modifying existing PSSH objects. It also covers converting between PlayReady and Widevine PSSH formats. ```python from pywidevine.pssh import PSSH from uuid import UUID from pywidevine.device import WidevinePsshData # Parse PSSH from raw bytes pssh_bytes = b'\x00\x00\x00[pssh...' pssh = PSSH(pssh_bytes) # Create a new Widevine PSSH from Key IDs key_ids = [ UUID("eb676abbcb345e96bbcf616630f1a3da"), "eb676abbcb345e96bbcf616630f1a3db", # String UUID bytes.fromhex("eb676abbcb345e96bbcf616630f1a3dc") # Raw bytes ] pssh = PSSH.new( system_id=PSSH.SystemId.Widevine, key_ids=key_ids, version=0 # Version 0 or 1 ) # Create PSSH with custom init_data (WidevinePsshData) widevine_data = WidevinePsshData() widevine_data.algorithm = "AESCTR" widevine_data.key_ids.append(UUID("eb676abbcb345e96bbcf616630f1a3da").bytes) widevine_data.provider = "widevine_test" widevine_data.content_id = b"content123" pssh = PSSH.new( system_id=PSSH.SystemId.Widevine, init_data=widevine_data, version=0 ) # Export PSSH to base64 or bytes pssh_base64 = pssh.dumps() # Base64 string pssh_bytes = pssh.dump() # Raw bytes print(f"PSSH Base64: {pssh_base64}") # Modify Key IDs in existing PSSH new_key_ids = [UUID("11111111-1111-1111-1111-111111111111")] pssh.set_key_ids(new_key_ids) # Convert PlayReady PSSH to Widevine playready_pssh = PSSH("AAACwnBzc2gAAAAAmgTweZhAQoarkuZb4IhflQAAAqI...") playready_pssh.to_widevine() print(f"Converted System ID: {playready_pssh.system_id}") # Convert Widevine PSSH to PlayReady widevine_pssh = PSSH("AAAAW3Bzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7Q...") widevine_pssh.to_playready( la_url="https://license.example.com/pr", # License acquisition URL lui_url=None, # License UI URL ds_id=None, # Domain service ID decryptor_setup="ONDEMAND", # Optional custom_data="data" # Optional XML ) ``` -------------------------------- ### Manage Widevine Device Files (.wvd) Source: https://context7.com/rlaphoenix/pywidevine/llms.txt This snippet shows how to load Widevine Device (.wvd) files from disk or base64 strings, create new devices from raw components, save devices to files, export them as bytes, and migrate older .wvd formats. It also demonstrates accessing client information within the device object. ```python from pywidevine.device import Device, DeviceTypes from pathlib import Path # Load device from .wvd file device = Device.load("/path/to/device.wvd") print(f"Device Type: {device.type.name}") # CHROME or ANDROID print(f"System ID: {device.system_id}") print(f"Security Level: {device.security_level}") # 1 (highest) to 3 (lowest) print(f"Flags: {device.flags}") print(f"Private Key Size: {device.private_key.size_in_bits()} bits") print(f"Has VMP Data: {bool(device.client_id.vmp_data)}") # Load device from base64-encoded string device = Device.loads("V1ZEAgEDAAABAAEA...") # Create a new device from raw components device = Device( type_=DeviceTypes.ANDROID, # or DeviceTypes.CHROME security_level=3, flags=None, private_key=Path("/path/to/private_key.pem").read_bytes(), client_id=Path("/path/to/client_id.bin").read_bytes() ) # Save device to .wvd file device.dump("/path/to/output.wvd") # Export device to bytes device_bytes = device.dumps() # Migrate older .wvd format (v1) to current format (v2) old_device_bytes = Path("/path/to/old_device.wvd").read_bytes() migrated_device = Device.migrate(old_device_bytes) migrated_device.dump("/path/to/migrated_device.wvd") # Access client info from device for entry in device.client_id.client_info: print(f"{entry.name}: {entry.value}") # Typical output: company_name, model_name, architecture_name, # device_name, build_info, widevine_cdm_version, etc. ``` -------------------------------- ### Configure Privacy Mode with Service Certificates Source: https://context7.com/rlaphoenix/pywidevine/llms.txt Shows how to enable Privacy Mode by setting a service certificate, which encrypts the Client ID during the license request process to protect device identity. ```python from pywidevine.cdm import Cdm from pywidevine.device import Device from pywidevine.pssh import PSSH import requests device = Device.load("/path/to/device.wvd") cdm = Cdm.from_device(device) session_id = cdm.open() cert_response = requests.post(url="https://license-server.example.com/license", data=cdm.service_certificate_challenge) cert_response.raise_for_status() provider_id = cdm.set_service_certificate(session_id, cert_response.content) pssh = PSSH("AAAAW3Bzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7Q...") challenge = cdm.get_license_challenge(session_id=session_id, pssh=pssh, license_type="STREAMING", privacy_mode=True) cdm.close(session_id) ``` -------------------------------- ### Accessing License Keys from CDM Sessions Source: https://github.com/rlaphoenix/pywidevine/blob/master/CHANGELOG.md Demonstrates how to retrieve decrypted license keys from the internal session storage after parsing a license. This replaces the previous direct return behavior of the parse_license method. ```python for key in cdm._sessions[session_id].keys: print(f"[{key.type}] {key.kid.hex}:{key.key.hex()}") ``` -------------------------------- ### POST /:device_name/get_license_challenge/:type Source: https://context7.com/rlaphoenix/pywidevine/llms.txt Generates a license challenge for a specific streaming type. ```APIDOC ## POST /{device_name}/get_license_challenge/{type} ### Description Generates a license challenge for the provided PSSH data to be sent to a Widevine license server. ### Method POST ### Endpoint /{device_name}/get_license_challenge/{type} ### Parameters #### Path Parameters - **device_name** (string) - Required - The name of the device. - **type** (string) - Required - The license type (e.g., STREAMING). #### Request Body - **session_id** (string) - Required - The active session ID. - **init_data** (string) - Required - Base64 encoded PSSH data. - **privacy_mode** (boolean) - Optional - Whether to enable privacy mode. ### Request Example { "session_id": "session_123", "init_data": "AAAAW3Bzc2g...", "privacy_mode": true } ### Response #### Success Response (200) - **challenge_b64** (string) - The base64 encoded license challenge. #### Response Example { "data": { "challenge_b64": "CAES..." } } ``` -------------------------------- ### Parse Protection System Specific Header (PSSH) Source: https://context7.com/rlaphoenix/pywidevine/llms.txt Illustrates how to parse a base64-encoded PSSH box to extract metadata such as version, system ID, and key IDs required for DRM license requests. ```python from pywidevine.pssh import PSSH from pywidevine.license_protocol_pb2 import WidevinePsshData from uuid import UUID pssh = PSSH("AAAAW3Bzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAADsIARIQ...") print(f"Version: {pssh.version}") print(f"System ID: {pssh.system_id}") print(f"Key IDs: {[str(kid) for kid in pssh.key_ids]}") print(f"Init Data (hex): {pssh.init_data.hex()}") ``` -------------------------------- ### Clone Pywidevine Repository and Navigate Source: https://github.com/rlaphoenix/pywidevine/blob/master/CONTRIBUTING.md These commands clone the Pywidevine repository from GitHub and change the current directory into the cloned repository. ```shell git clone https://github.com/devine-dl/pywidevine cd pywidevine ``` -------------------------------- ### Activate Poetry Virtual Environment Source: https://github.com/rlaphoenix/pywidevine/blob/master/CONTRIBUTING.md This command activates the Poetry-managed virtual environment, allowing you to run project-specific commands. Alternatively, commands can be prefixed with 'poetry run'. ```shell poetry shell ``` -------------------------------- ### Decrypt Content using Shaka Packager Source: https://context7.com/rlaphoenix/pywidevine/llms.txt This snippet demonstrates how to decrypt content using the `cdm.decrypt` method, which leverages the Shaka Packager tool. It requires the shaka-packager executable to be in the system's PATH. The function takes input and output file paths, an optional temporary directory, and a flag to control overwriting existing files. It returns a status code indicating success or failure. ```python for key in cdm.get_keys(session_id, type_="CONTENT"): print(f"Key: {key.kid.hex}:{key.key.hex()}") return_code = cdm.decrypt( session_id=session_id, input_file="/path/to/encrypted_video.mp4", output_file="/path/to/decrypted_video.mp4", temp_dir="/path/to/temp", # Optional exists_ok=False # Set True to overwrite existing output ) if return_code == 0: print("Decryption successful!") else: print(f"Decryption failed with code: {return_code}") cdm.close(session_id) ``` -------------------------------- ### Decrypt Content with Shaka Packager Source: https://context7.com/rlaphoenix/pywidevine/llms.txt This snippet demonstrates how to decrypt encrypted content using the CDM and Shaka Packager. It includes printing keys for reference and handling the decryption process. ```APIDOC ## Decrypt Content with Shaka Packager ### Description This example shows how to retrieve decryption keys and then use them to decrypt a media file using Shaka Packager. ### Method N/A (Script Execution) ### Endpoint N/A (Local Execution) ### Parameters N/A ### Request Example ```python # Assuming session_id is already established and keys are available # Print keys for reference for key in cdm.get_keys(session_id, type_="CONTENT"): print(f"Key: {key.kid.hex}:{key.key.hex()}") # Decrypt content using Shaka Packager # Requires shaka-packager executable in PATH or system return_code = cdm.decrypt( session_id=session_id, input_file="/path/to/encrypted_video.mp4", output_file="/path/to/decrypted_video.mp4", temp_dir="/path/to/temp", # Optional exists_ok=False # Set True to overwrite existing output ) if return_code == 0: print("Decryption successful!") else: print(f"Decryption failed with code: {return_code}") cdm.close(session_id) ``` ### Response #### Success Response (0) - **Decryption successful!** (string) - Indicates that the decryption process completed without errors. #### Response Example ``` Decryption successful! ``` #### Error Response - **Decryption failed with code: [return_code]** (string) - Indicates decryption failure with the specific return code from the packager. ``` -------------------------------- ### Key Class - Working with Decryption Keys Source: https://context7.com/rlaphoenix/pywidevine/llms.txt This section details the `Key` class, which represents content decryption keys. It explains how to access key properties like type, ID, and permissions, and how to format them for common decryption tools. ```APIDOC ## Key Class - Working with Decryption Keys ### Description The `Key` class encapsulates decryption keys obtained from license responses. It provides access to key details such as type, Key ID (kid), the key material itself, and specific permissions for operator session keys. ### Method N/A (Class Usage) ### Endpoint N/A (Local Usage) ### Parameters N/A ### Request Example ```python from pywidevine.cdm import Cdm from pywidevine.device import Device from pywidevine.pssh import PSSH from pywidevine.key import Key from uuid import UUID device = Device.load("/path/to/device.wvd") cdm = Cdm.from_device(device) session_id = cdm.open() # ... license acquisition code ... # Get all keys from session all_keys = cdm.get_keys(session_id) for key in all_keys: # Key properties print(f"Type: {key.type}") # CONTENT, SIGNING, OPERATOR_SESSION, etc. print(f"Key ID (UUID): {key.kid}") # UUID object print(f"Key ID (hex): {key.kid.hex}") # Hex string without dashes print(f"Key (bytes): {key.key}") # Raw key bytes print(f"Key (hex): {key.key.hex()}") # Hex string print(f"Permissions: {key.permissions}") # For OPERATOR_SESSION keys print("---") # Filter by key type content_keys = cdm.get_keys(session_id, type_="CONTENT") signing_keys = cdm.get_keys(session_id, type_="SIGNING") # Format for common decryption tools (kid:key format) for key in content_keys: # Format used by mp4decrypt, shaka-packager, etc. print(f"{key.kid.hex}:{key.key.hex()}") # Convert Key ID formats kid_bytes = bytes.fromhex("eb676abbcb345e96bbcf616630f1a3da") kid_uuid = Key.kid_to_uuid(kid_bytes) print(f"UUID: {kid_uuid}") # Handle decimal Key IDs (some services use these) kid_decimal = b"12345678901234567890" kid_uuid = Key.kid_to_uuid(kid_decimal) cdm.close(session_id) ``` ### Response #### Success Response - **Type** (string) - The type of the key (e.g., CONTENT, SIGNING, OPERATOR_SESSION). - **Key ID (UUID)** (UUID) - The Key ID as a UUID object. - **Key ID (hex)** (string) - The Key ID in hexadecimal format. - **Key (bytes)** (bytes) - The raw key material in bytes. - **Key (hex)** (string) - The key material in hexadecimal format. - **Permissions** (any) - Permissions associated with the key (relevant for OPERATOR_SESSION keys). #### Response Example ``` Type: CONTENT Key ID (UUID): e610736c-6572-656c-6561-737365727365 Key ID (hex): e610736c6572656c6561737365727365 Key (bytes): b'\xe6\x10s l\x00\x96\xbb\xcf\x61\x66\x30\xf1\xa3\xda' Key (hex): e610736c6572656c6561737365727365 --- Type: CONTENT Key ID (UUID): aabbccdd-eeff-0011-2233-445566778899 Key ID (hex): aabbccddeeff00112233445566778899 Key (bytes): b'\xaa\xbb\xcc\xdd\xee\xff\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99' Key (hex): aabbccddeeff00112233445566778899 --- UUID: e610736c-6572-656c-6561-737365727365 ``` ``` -------------------------------- ### Working with Decryption Keys using PyWidevine Source: https://context7.com/rlaphoenix/pywidevine/llms.txt This snippet illustrates how to extract and inspect decryption keys using the `Key` class and `cdm.get_keys` method. It shows how to retrieve all keys, filter them by type (e.g., 'CONTENT', 'SIGNING'), and format them for use with common decryption tools like mp4decrypt and shaka-packager. It also covers converting Key IDs between byte, hex, and UUID formats. ```python from pywidevine.cdm import Cdm from pywidevine.device import Device from pywidevine.pssh import PSSH from pywidevine.key import Key from uuid import UUID device = Device.load("/path/to/device.wvd") cdm = Cdm.from_device(device) session_id = cdm.open() # ... license acquisition code ... # Get all keys from session all_keys = cdm.get_keys(session_id) for key in all_keys: # Key properties print(f"Type: {key.type}") # CONTENT, SIGNING, OPERATOR_SESSION, etc. print(f"Key ID (UUID): {key.kid}") # UUID object print(f"Key ID (hex): {key.kid.hex}") # Hex string without dashes print(f"Key (bytes): {key.key}") # Raw key bytes print(f"Key (hex): {key.key.hex()}") # Hex string print(f"Permissions: {key.permissions}") # For OPERATOR_SESSION keys print("---") # Filter by key type content_keys = cdm.get_keys(session_id, type_="CONTENT") signing_keys = cdm.get_keys(session_id, type_="SIGNING") # Format for common decryption tools (kid:key format) for key in content_keys: # Format used by mp4decrypt, shaka-packager, etc. print(f"{key.kid.hex}:{key.key.hex()}") # Convert Key ID formats kid_bytes = bytes.fromhex("eb676abbcb345e96bbcf616630f1a3da") kid_uuid = Key.kid_to_uuid(kid_bytes) print(f"UUID: {kid_uuid}") # Handle decimal Key IDs (some services use these) kid_decimal = b"12345678901234567890" kid_uuid = Key.kid_to_uuid(kid_decimal) cdm.close(session_id) ``` -------------------------------- ### Use RemoteCdm API for CDM Operations Source: https://context7.com/rlaphoenix/pywidevine/llms.txt This section explains how to use the `RemoteCdm` class to perform CDM operations by connecting to a remote pywidevine serve API. It covers initialization with server details, opening a session, setting service certificates, generating license challenges, and parsing licenses without local device files. ```python from pywidevine.remotecdm import RemoteCdm from pywidevine.device import DeviceTypes from pywidevine.pssh import PSSH import requests # Initialize RemoteCdm with server details # The API must be running pywidevine serve v1.4.3 or newer remote_cdm = RemoteCdm( device_type=DeviceTypes.ANDROID, # Must match server's device system_id=1234567890, # Must match server's device security_level=3, # Must match server's device host="https://api.example.com:8786", secret="fvYBh0C3fRAxlvyJcynD1see3GmNbIiC", # API secret key device_name="my_device" # Device name configured on server ) # Usage is identical to local Cdm session_id = remote_cdm.open() # Set service certificate (optional, for privacy mode) provider_id = remote_cdm.set_service_certificate( session_id, RemoteCdm.common_privacy_cert ) # Generate license challenge pssh = PSSH("AAAAW3Bzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7Q...") challenge = remote_cdm.get_license_challenge( session_id=session_id, pssh=pssh, license_type="STREAMING", privacy_mode=True ) # Send to license server (this part is done locally) response = requests.post("https://license-server.example.com", data=challenge) response.raise_for_status() # Parse license and get keys via remote API remote_cdm.parse_license(session_id, response.content) ``` -------------------------------- ### POST /:device_name/get_keys/:type Source: https://context7.com/rlaphoenix/pywidevine/llms.txt Retrieves decrypted keys from an active session. ```APIDOC ## POST /{device_name}/get_keys/{type} ### Description Extracts keys from an active session after a license has been parsed. ### Method POST ### Endpoint /{device_name}/get_keys/{type} ### Parameters #### Path Parameters - **device_name** (string) - Required - The name of the device. - **type** (string) - Required - The key type (e.g., CONTENT, SIGNING, ALL). #### Request Body - **session_id** (string) - Required - The active session ID. ### Request Example { "session_id": "session_123" } ### Response #### Success Response (200) - **keys** (array) - List of key objects containing key_id and key. #### Response Example { "data": { "keys": [ { "type": "CONTENT", "key_id": "...", "key": "..." } ] } } ``` -------------------------------- ### Interact with Remote CDM API Source: https://context7.com/rlaphoenix/pywidevine/llms.txt Shows how to perform common operations like health checks, session management, and license parsing against the pywidevine HTTP API. ```python import requests import base64 API_HOST = "https://api.example.com:8786" SECRET_KEY = "fvYBh0C3fRAxlvyJcynD1see3GmNbIiC" DEVICE_NAME = "device1" headers = {"X-Secret-Key": SECRET_KEY} response = requests.get(f"{API_HOST}/{DEVICE_NAME}/open", headers=headers) # ... subsequent POST requests for set_service_certificate, get_license_challenge, parse_license, and get_keys ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.