This repository provides server-side sample code that can generate license token for DoveRunner multi-DRM service. DRM license tokens are used to authenticate license requests in multi-DRM integration workflows.
Here's how a license token works in the DRM license issuance process.
- When a multi-DRM client tries to play DRM content, the client requests a token to the content service platform to acquire DRM license. The service platform verifies that the user requesting the token has permission to the content, and then generates a token data according to the specification.
- The service platform can set usage rights and various security policies in the token data. The generated token is delivered to the client as response.
- When the client requests a DRM license with the token, DoveRunner cloud server validates the token and issues a license.
This works on PYTHON version :
- 3.13.* and greater
- PyCharm
- conda 24.9.2
Option 1: Using Conda (Recommended for cross-platform compatibility)
For Anaconda users, create a virtual environment using environment.yml file and command.
environment.yml file :
# Cross-Platform Conda Environment for ARM64 & x86_64
# ARM64(Apple Silicon)과 x86_64(Linux/macOS Intel) 양쪽에서 호환되는 환경
name: drm_test or other name you want
channels:
- conda-forge
- defaults
dependencies:
- python=3.13.*
# 도구들(pip, wheel)은 별도 버전 고정 없이 최신 호환 빌드 사용
# Use the latest compatible builds of tools like pip and wheel without fixing versions
- pip
- wheel
# Certificate 패키지는 arm64에 여러 빌드가 있으므로 버전 고정 해제
# Unpin certificate-related packages since multiple builds exist for arm64
- ca-certificates
- certifi
# OpenSSL은 1.1.1k 이상 빌드가 있으므로 최소 요구조건만 명시
# Specify minimum required version for OpenSSL (1.1.1k or higher available)
- openssl>=1.1.1k
# pycrypto는 arm64에서 빌드가 제공되지 않으므로 pycryptodome으로 교체
# Replace pycrypto with pycryptodome as pycrypto is unavailable on arm64
- pycryptodome
- pytz
- setuptools
- sqlite
prefix: {{ conda_prefix }}
command :
conda env create -f environment.ymlOption 2: Using pip
If you prefer using pip instead of conda:
pip install pycryptodome pytz| dir | description | |
|---|---|---|
| doverunner | ||
| /config | configuration module for policy | |
| /exception | exception package | |
| /sample | make token sample @client_test.py |
|
| /token | source directory |
Go to DoveRunner Docs for Policy Json Specification and figure out which specification to use.
-
Configuration Setup (Required)
Before running
make_token.py, create a configuration file with your DoveRunner credentials:# Copy the example configuration file to project root cp config.ini.example config.ini # Edit config.ini and fill in your actual values # Replace placeholders with your DoveRunner credentials
The
config.inifile (in project root) should contain:[drm] site_id = YOUR_SITE_ID site_key = YOUR_SITE_KEY access_key = YOUR_ACCESS_KEY user_id = tester-user content_id = YOUR_CONTENT_ID
Note: The
config.inifile is gitignored to prevent accidental commits of sensitive credentials.After configuration, run:
python -m doverunner.sample.make_token -
Before get token, you need to set up
policy.from doverunner_drm_token_policy import DoveRunnerDrmTokenPolicy as Policy policy = Policy() def set_policy(): set_playback_policy() set_security_policy() set_external_key() policy \ .playback(playback) \ .security(security) \ .external(external)
-
As you can see above, you need to decide whether to set
playback,security, orexternal.If you want to set up all policies,
from doverunner_drm_token_policy import PlaybackPolicy from doverunner_drm_token_policy import SecurityPolicy from doverunner_drm_token_policy import ExternalKey playback = PlaybackPolicy() security = SecurityPolicy() external = ExternalKey() def set_playback_policy(): playback \ .allowed_track_types(allowed_track_types.SD_ONLY) \ .persistent(False) def set_security_policy(): security.track_type(track_type.ALL) \ .widevine(Widevine() .security_level(1) .required_hdcp_version('HDCP_NONE') .required_cgms_flags('CGMS_NONE') .override_device_revocation(False)) \ .playready(Playready() .security_level(150) .digital_video_protection_level(100) .analog_video_protection_level(100) .digital_audio_protection_level(100) ) \ .fairplay(Fairplay() .hdcp_enforcement(-1) .allow_airplay(True) .allow_av_adapter(True)) \ .ncg(Ncg() .allow_mobile_abnormal_device(True) .allow_external_display(True) .control_hdcp(0)) def set_external_key(): """ ### set mpeg_cecn | hls_aes | ncg """ hls_aes_list = [ HlsAes(track_type.SD, '<key_id>', '<key>'), HlsAes(track_type.HD, '<key_id>', '<key>'), HlsAes(track_type.UHD1, '<key_id>', '<key>'), HlsAes(track_type.UHD2, '<key_id>', '<key>') ] external \ .mpeg_cenc(MpegCenc(track_type.HD, '<key_id>', '<key>', '<iv>')) \ .mpeg_cenc(MpegCenc(track_type.UHD1, '<key_id>', '<key>', '<iv>')) \ .mpeg_cenc(MpegCenc(track_type.UHD2, '<key_id>', '<key>', '<iv>')) \ .hls_aes(hls_aes_list) \ .ncg(Ncg(track_type.ALL_VIDEO, '<cek>'))
-
Import
DoveRunnerDrmTokenClientand load configurationfrom doverunner_drm_token_client import DoveRunnerDrmTokenClient as Token from doverunner.config import response_format import configparser import os def load_config(): """Load configuration from config.ini file in project root.""" project_root = os.path.dirname(os.path.dirname(os.path.dirname(__file__))) config_path = os.path.join(project_root, 'config.ini') config = configparser.ConfigParser() config.read(config_path) return { 'site_id': config.get('drm', 'site_id'), 'site_key': config.get('drm', 'site_key'), 'access_key': config.get('drm', 'access_key'), 'user_id': config.get('drm', 'user_id'), 'content_id': config.get('drm', 'content_id') } def set_drm_token(): config = load_config() # Load from config.ini set_policy() token = Token()\ .widevine()\ .site_id(config['site_id'])\ .site_key(config['site_key'])\ .access_key(config['access_key'])\ .user_id(config['user_id'])\ .cid(config['content_id'])\ .policy(policy)\ .response_format(response_format.ORIGINAL) token.execute()
-
Make encrypted token !
try: set_drm_token() except DoveRunnerTokenException as p: print(p)
DoveRunnerTokenExceptionwill arise if there are minor mistakes when created. Theresultwill return JSON with anerror_codeanderror_message. Follow the comment and fix the bugs.For example,
{ "error_code": "1048", "error_message": "Token err : The response_format should be in type of RESPONSE_FORMAT in [doverunner.config.response_format] module" }If you want to see All the error codes and error messages you would get, see the
error_listindoverunner\excepion\doverunner_tokne_exception.pymodule.
We hope this instruction would be helpful to generate DRM License Token to request DoveRunner Multi-DRM Cloud Server and get the License issued from.
| Error Code | Error Messages |
|---|---|
| 1000 | Token err : The userId is Required |
| 1001 | Token err : The cId is Required |
| 1002 | Token err : The siteId is Required |
| 1003 | Token err : The accessKey is Required |
| 1004 | Token err : The siteKey is Required |
| 1005 | Token err : The policy is Required |
| 1006 | Policy Err : The playback_policy should be an instance of PlaybackPolicy |
| 1007 | Policy Err : The security_policy should be an instance of SecurityPolicy or List[SecurityPolicy] |
| 1008 | Policy Err : The external_key should be an instance of ExternalKey |
| 1009 | PlaybackPolicy : The persistent should be Boolean |
| 1010 | PlaybackPolicy : The license_duration should be Integer |
| 1011 | PlaybackPolicy : The expireDate time format should be 'YYYY-MM-DD'T'HH:mm:ss'Z' |
| 1012 | PlaybackPolicy : The allowed_track_types value should be in allowed_track_types module |
| 1013 | SecurityPolicy: The track_type should be in type of TRACK_TYPE in doverunner.config.track_type module |
| 1014 | SecurityPolicy: The widevine should be an instance of SecurityPolicyWidevine |
| 1015 | SecurityPolicy: The playready should be an instance of SecurityPolicyPlayready |
| 1016 | SecurityPolicy: The fairplay should be an instance of SecurityPolicyFairplay |
| 1017 | SecurityPolicy: The ncg should be an instance of SecurityPolicyNcg |
| 1018 | ExternalKey: The ExternalKey Should be filled if called |
| 1019 | ExternalKey: The MpegCenc should be an instance of MpegCenc or List[MpegCenc] |
| 1020 | ExternalKey: The HlsAes should be an instance of HlsAes or List[HlsAes] |
| 1021 | ExternalKey: The Ncg should be an instance of Ncg |
| 1022 | SecurityPolicyWidevine: The security_level should be in type of SECURITY_LEVEL in doverunner.config.widevine.security_level module |
| 1023 | SecurityPolicyWidevine: The required_hdcp_version should be in type of REQUIRED_HDCP_VERSION in doverunner.config.widevine.required_hdcp_version module |
| 1024 | SecurityPolicyWidevine: The required_cgms_flagsshould be in type of REQUIRED_CGMS_FLAGS in doverunner.config.widevine.required_cgms_flags module |
| 1025 | SecurityPolicyWidevine: The disable_analog_output should be Boolean |
| 1026 | SecurityPolicyWidevine: The hdcp_srm_rule should be in type of HDCP_SRM_RULE in doverunner.config.widevine.hdcp_srm_rule module |
| 1027 | SecurityPolicyPlayready: The security_level should be in type of SECURITY_LEVEL in doverunner.config.playready.security_level module |
| 1028 | SecurityPolicyPlayready: The digital_video_protection_level should be in type of DIGITAL_VIDEO_PROTECTION_LEVEL in doverunner.config.playready.digital_video_protection module |
| 1029 | SecurityPolicyPlayready: The analog_video_protection_level should be in type of ANALOG_VIDEO_PROTECTION_LEVEL in doverunner.config.playready.analog_video_protection module |
| 1030 | SecurityPolicyPlayready: The digital_audio_protection_level should be in type of DIGITAL_AUDIO_PROTECTION in doverunner.config.playready.digital_audio_protection module |
| 1032 | SecurityPolicyPlayready: The require_hdcp_type_1 should be Boolean |
| 1033 | SecurityPolicyFairplay: The hdcp_enforcement should be in type of FAIRPLAY_HDCP_ENFORCEMENT in doverunner.config.fairplay_hdcp_enforcement module |
| 1034 | SecurityPolicyFairplay: The allow_airplay should be Boolean |
| 1035 | SecurityPolicyFairplay: The allow_av_adapter should be Boolean |
| 1036 | SecurityPolicyNcg: The allow_mobile_abnormal_device should be Boolean |
| 1037 | SecurityPolicyNcg: The allow_external_display should be Boolean |
| 1038 | SecurityPolicyNcg: The control_hdcp should be in type of CONTROL_HDCP in doverunner.config.ncg_control_hdcp module |
| 1039 | ExternalKeyMpegCenc: The track_type should be in type of TRACK_TYPE in doverunner.config.track_type module |
| 1040 | ExternalKeyMpegCenc : The key_id should be 16byte hex String |
| 1041 | ExternalKeyMpegCenc : The key should be 16byte hex String |
| 1042 | ExternalKeyMpegCenc : The iv should be 16byte hex String |
| 1043 | ExternalKeyHlsAes: The track_type should be in type of TRACK_TYPE in doverunner.config.track_type module |
| 1044 | ExternalKeyHlsAes : The key should be 16byte hex String |
| 1045 | ExternalKeyHlsAes : The iv should be 16byte hex String |
| 1046 | ExternalKeyNcg : The track_type should be in type of TRACK_TYPE in doverunner.config.track_type module |
| 1047 | ExternalKeyNcg : The Cek should be 32byte hex String |
| 1048 | Token err : The response_format should be in type of RESPONSE_FORMAT in doverunner.config.response_format module |
| 1049 | PlaybackPolicy : The rental_duration should be Integer |
| 1050 | PlaybackPolicy : The playback_duration should be Integer |
| 1051 | SecurityPolicyWidevine: The override_device_revocation should be Boolean |
| 1052 | ExternalKeyHlsAes : The key_id should be 16byte hex String |
| 1053 | PlaybackPolicy : The max_stream_per_user should be Integer |
| 1054 | SecurityPolicyWidevine : The enable_license_cipher should be Boolean |
| 1055 | Token err : The key_rotation should be Boolean |
If you have any questions or issues with the token sample, please create a ticket at DoveRunner Helpdesk website.