Plugin: DRM
DrmPlugin (id 'drm') coordinates Encrypted Media Extensions (EME) license acquisition for Widevine, FairPlay, and PlayReady, and carries HDCP / output-protection requirements. Registration and license-fetch routing are real; the actual key-system handshake is gated behind requestMediaKeySystemAccess, so environments without EME (headless test runners, older browsers) stay quiet instead of throwing.
Options
| Option | Type | Purpose |
|---|---|---|
keySystem | string | EME key system identifier, 'com.widevine.alpha', 'com.apple.fps', 'com.microsoft.playready', etc. |
licenseUrl | string | License server URL. Required before fetchLicense() can be called. |
certificate | ArrayBuffer | string | FairPlay service certificate. Optional for Widevine/PlayReady. |
customSignRequest | (request: Request) => Request | Promise<Request> | Sign the license request (HMAC, proprietary auth) before it goes out. |
transformLicenseRequest | (challenge: ArrayBuffer) => ArrayBuffer | Promise<ArrayBuffer> | Transform the challenge body before sending. |
transformLicenseResponse | (response: ArrayBuffer) => ArrayBuffer | Promise<ArrayBuffer> | Transform the license response body after receiving. |
hdcpRequired | 'type-0' | 'type-1' | 'none' | Output protection requirement. |
Events
| Event | Payload | Fires when |
|---|---|---|
key:error | { sessionId, error } | The key-system handshake fails. |
unsupported | { reason } | EME isn’t available in this environment, either at plugin registration or at handshake time. |
How activation works
The plugin listens for the player’s 'item' event, when the incoming item carries a drm.keySystem hint (a consumer-supplied field on your own item shape, not part of the canonical VideoPlaylistItem), it attempts the EME handshake for that key system, falling back to the plugin’s own keySystem option when the item doesn’t specify one:
import nmplayer from '@nomercy-entertainment/nomercy-video-player';
import { DrmPlugin } from '@nomercy-entertainment/nomercy-video-player/plugins/drm';
const player = nmplayer('player');
player.addPlugin(DrmPlugin, {
keySystem: 'com.widevine.alpha',
licenseUrl: 'https://license.example.com/widevine',
});
await player.ready();
fetchLicense(challenge) posts the challenge to licenseUrl, wrapped through customSignRequest when provided, and through transformLicenseRequest / transformLicenseResponse for payload shaping either direction:
const drm = player.getPlugin(DrmPlugin);
const licenseBlob = await drm?.fetchLicense(challengeBuffer);
Rules & restrictions
requestMediaKeySystemAccessmissing at registration time emitsunsupportedimmediately and the plugin does nothing further, it does not throw and does not retry.fetchLicense()throwscore:drm/license-url-missingiflicenseUrlwas never configured, this is a real, addressable configuration error, not a soft failure.- All license network I/O goes through the kit’s
Plugin.fetch(), the same auth pipeline every other plugin uses, so theauthconfig fromsetup()(bearer token, custom headers, request signing) already applies without extra config here. mediaKeys()returnsnullwhenever EME isn’t supported or the handshake hasn’t completed, always guard before use.
How to extend it
Subclass to add key-system-specific certificate handling or a different license transport (a WebSocket-based DRM proxy, for instance):
import { DrmPlugin } from '@nomercy-entertainment/nomercy-video-player/plugins/drm';
class MyDrmPlugin extends DrmPlugin {
override async fetchLicense(challenge: ArrayBuffer): Promise<ArrayBuffer> {
// custom transport, then fall back to the base implementation if needed
return super.fetchLicense(challenge);
}
}
player.addPlugin(MyDrmPlugin, { keySystem: 'com.widevine.alpha', licenseUrl });
Next steps
- Adapter: Video Backend: the backend that mounts the media source EME and DRM attach to.