npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@doverunner/secureplay-web

v1.0.1

Published

SecurePlay Web SDK - Developer-focused media player with multi-DRM support

Readme

SecurePlay Web SDK

Developer-focused media player SDK with multi-DRM support, built on Clean Architecture principles.

Features

  • Multi-DRM Support: Widevine, FairPlay, PlayReady, Wiseplay
  • Clean Architecture: Maintainable and testable codebase
  • License Cipher: Optional DRM challenge encryption
  • Simple API: Easy integration in under a day

Browser Requirements

SecurePlay requires modern browsers with EME (Encrypted Media Extensions) support:

| Browser | Support | DRM | |---------|---------|-----| | Chrome | ✅ | Widevine, PlayReady (Windows 11) | | Firefox | ✅ | Widevine | | Safari | 14.1+ | FairPlay only | | Edge | ✅ | Widevine, PlayReady |

Not Supported:

  • Legacy browsers without EME support

Notes:

  • Safari only supports FairPlay DRM with HLS manifests
  • Safari 14.1+ required for stable FairPlay Streaming API and HLS interoperability
  • Chrome supports PlayReady SL3000 on Windows 11 (Chrome 142+)

Quick Start

For working examples, check out the Sample Project.

Installation

npm install @doverunner/secureplay-web

Basic Usage

import { SecurePlay } from '@doverunner/secureplay-web';

// Create player instance
const player = new SecurePlay({
  videoElement: document.getElementById('video'),
});

// Initialize
await player.initialize();

// Load DRM content
await player.load({
  manifestUri: 'https://example.com/manifest.mpd',
  drm: {
    licenseUri: 'https://license.example.com/license',
    headers: {
      'pallycon-customdata-v2': 'YOUR_LICENSE_TOKEN'
    },
    drmType: 'widevine'
  }
});

// Play
await player.play();

API Reference

Constructor

const player = new SecurePlay({
  videoElement: HTMLVideoElement, // Required: Video element
  logger?: {
    level?: 'DEBUG' | 'INFO' | 'WARN' | 'ERROR'  // Optional: Log level
  }
});

Methods

initialize()

Initialize the player.

await player.initialize();

load(options)

Load content for playback.

await player.load({
  manifestUri: string,      // Required: DASH or HLS manifest URI
  drm?: {
    licenseUri: string,     // Required: License server URI
    certificateUri?: string, // Optional: Certificate URI (Widevine/FairPlay)
    headers?: object,       // Optional: Custom headers (e.g., authentication tokens)
    drmType?: 'widevine' | 'fairplay' | 'playready', // Optional: Specific DRM type

    // Hardware DRM options
    videoRobustness?: string | string[], // Optional: Video robustness level (e.g., ['HW_SECURE_ALL'])
    audioRobustness?: string | string[], // Optional: Audio robustness level (e.g., ['HW_SECURE_CRYPTO'])
    securityLevel?: number,  // Optional: PlayReady security level (currently only 3000 is supported)

    // License Cipher options
    useLicenseCipher?: boolean, // Optional: Enable License Cipher (default: false)
    siteId?: string,        // Optional: DoveRunner site ID (required when useLicenseCipher is true)
    cipherSdkUrl?: string,  // Optional: URL to License Cipher SDK file (for dynamic loading)

    // CSL (Concurrent Stream Limitation) options
    manualRenewalIntervalSec?: number // Optional: Renewal interval in seconds (0 to disable, default: 0)
  }
});

play()

Start playback.

await player.play();

pause()

Pause playback.

player.pause();

seek(time)

Seek to specific time.

player.seek(30); // Seek to 30 seconds

getCurrentTime()

Get current playback time.

const time = player.getCurrentTime();

getDuration()

Get video duration.

const duration = player.getDuration();

isPaused() / isEnded()

Check playback state.

const paused = player.isPaused();
const ended = player.isEnded();

setVolume() / getVolume()

Control volume (0.0 to 1.0).

player.setVolume(0.5);
const volume = player.getVolume();

setMuted() / isMuted()

Control mute state.

player.setMuted(true);
const muted = player.isMuted();

setPlaybackRate() / getPlaybackRate()

Control playback speed.

player.setPlaybackRate(1.5);  // 1.5x speed
const rate = player.getPlaybackRate();

getBuffered()

Get buffered time ranges.

const buffered = player.getBuffered();

getPlayerInstance()

Get the underlying player instance (for advanced usage).

const shakaPlayer = player.getPlayerInstance();
// Now you can use Shaka Player API directly

addEventListener(eventType, callback)

Add event listener.

player.addEventListener('error', (event) => {
  console.error('Player error:', event);
});

removeEventListener(eventType, callback)

Remove event listener.

player.removeEventListener('error', callback);

destroy()

Destroy the player and clean up resources.

await player.destroy();

DRM Support

Widevine (Chrome, Edge, Firefox)

await player.load({
  manifestUri: 'https://example.com/stream.mpd',
  drm: {
    licenseUri: 'https://license.example.com/widevine',
    certificateUri: 'https://license.example.com/cert',
    headers: {
      'pallycon-customdata-v2': 'YOUR_WIDEVINE_TOKEN'  // or other custom headers
    },
    drmType: 'widevine'
  }
});

FairPlay (Safari)

await player.load({
  manifestUri: 'https://example.com/master.m3u8',
  drm: {
    licenseUri: 'https://license.example.com/fairplay',
    certificateUri: 'https://license.example.com/fps-cert',
    headers: {
      'pallycon-customdata-v2': 'YOUR_FAIRPLAY_TOKEN'
    },
    drmType: 'fairplay'
  }
});

PlayReady (IE, Edge Legacy)

await player.load({
  manifestUri: 'https://example.com/stream.mpd',
  drm: {
    licenseUri: 'https://license.example.com/playready',
    headers: {
      'pallycon-customdata-v2': 'YOUR_PLAYREADY_TOKEN'
    },
    drmType: 'playready'
  }
});

Note: The headers field allows flexible authentication. Different DRM services may use different header names (e.g., Authorization, X-Custom-Data, etc.).

Hardware DRM (Widevine L1, PlayReady SL3000)

For enhanced security, you can use hardware-backed DRM which provides the highest level of content protection.

Widevine L1

await player.load({
  manifestUri: 'https://example.com/stream_hardware.mpd',
  drm: {
    licenseUri: 'https://license.example.com/widevine',
    certificateUri: 'https://license.example.com/cert',
    headers: {
      'pallycon-customdata-v2': 'YOUR_WIDEVINE_L1_TOKEN'
    },
    drmType: 'widevine',
    // Hardware DRM configuration
    videoRobustness: ['HW_SECURE_ALL'],
    audioRobustness: ['HW_SECURE_CRYPTO']
  }
});

PlayReady SL3000

await player.load({
  manifestUri: 'https://example.com/stream_hardware.mpd',
  drm: {
    licenseUri: 'https://license.example.com/playready',
    headers: {
      'pallycon-customdata-v2': 'YOUR_PLAYREADY_SL3000_TOKEN'
    },
    drmType: 'playready',
    // Use hardware level key system (com.microsoft.playready.recommendation.3000)
    securityLevel: '3000' // Currently only 3000 is supported (SL3000)
  }
});

Notes:

  • Hardware DRM requires device hardware support (TEE, Secure Video Path)
  • Widevine L1: Available on Android devices with hardware DRM support
  • PlayReady SL3000: Available on Windows devices with hardware DRM support

Concurrent Stream Limitation (CSL)

For multi-device control, you can enable automatic license renewal which is required for CSL scenarios.

await player.load({
  manifestUri: 'https://example.com/manifest.mpd',
  drm: {
    licenseUri: 'https://license.example.com/widevine',
    certificateUri: 'https://license.example.com/cert',
    headers: {
      'pallycon-customdata-v2': 'YOUR_TOKEN_WITH_CSL'
    },
    drmType: 'fairplay',
    // Enable automatic license renewal for CSL (0 to disable)
    manualRenewalIntervalSec: 600  // Renew every 10 minutes
  }
});

How CSL Works:

  1. License server tracks active playback sessions
  2. SDK automatically renews licenses at specified intervals
  3. Server can revoke licenses when concurrent stream limit is exceeded
  4. Playback stops if license renewal fails

Notes:

  • CSL options are typically used for FairPlay and PlayReady
  • Widevine handles license renewal automatically via CDM
  • Set manualRenewalIntervalSec to 0 to disable automatic renewal
  • License renewal interval can be configured or returned from the license server response

License Cipher (DRM Challenge Encryption)

DoveRunner License Cipher provides an additional security layer by encrypting DRM license challenges at the network level before they are sent to the license server. This protects against content key extraction attacks on web browsers.

Prerequisites:

  1. Obtain License Cipher SDK files from DoveRunner
  2. Host all SDK files in the same directory on your server
  3. Each DoveRunner site ID requires its own unique SDK files
  4. License token must have enable_license_cipher: true for the license server to decrypt challenges

Option 1: Pre-load SDK via Script Tag

<!-- Load SDK before SecurePlay -->
<script src="/path/to/your/cipher/module.js"></script>
<script src="secureplay.umd.js"></script>

<script>
  const player = new SecurePlay.SecurePlay({
    videoElement: document.getElementById('video')
  });

  await player.load({
    manifestUri: 'https://example.com/manifest.mpd',
    drm: {
      licenseUri: 'https://license.example.com/widevine',
      certificateUri: 'https://license.example.com/cert',
      headers: {
        'pallycon-customdata-v2': 'YOUR_TOKEN'
      },
      drmType: 'widevine',
      // Enable License Cipher
      useLicenseCipher: true,
      siteId: 'YOUR_SITE_ID'  // Required when useLicenseCipher is enabled
    }
  });
</script>

Option 2: Dynamic SDK Loading

await player.load({
  manifestUri: 'https://example.com/manifest.mpd',
  drm: {
    licenseUri: 'https://license.example.com/widevine',
    certificateUri: 'https://license.example.com/cert',
    headers: {
      'pallycon-customdata-v2': 'YOUR_TOKEN'
    },
    drmType: 'widevine',
    // Enable License Cipher with dynamic SDK loading
    useLicenseCipher: true,
    siteId: 'YOUR_SITE_ID',
    cipherSdkUrl: '/path/to/your/cipher/module.js'  // SDK will be loaded dynamically
  }
});

Supported DRM Types:

  • Widevine: License challenges are automatically encrypted
  • PlayReady: License challenges are automatically encrypted
  • FairPlay: Not supported

For License Cipher SDK files and detailed integration guide, please contact DoveRunner Support.

Error Handling

SecurePlay uses a structured error code system:

| Category | Code Range | Example | |----------|-----------|---------| | Player | SP-1xxx | SP-1001: PLAYER_INIT_FAILED, SP-1006: PLAYER_RENDERER_ERROR | | DRM | SP-2xxx | SP-2001: DRM_LICENSE_REQUEST_FAILED, SP-2008: DRM_PROVISIONING_FAILED | | Network | SP-3xxx | SP-3001: NETWORK_REQUEST_FAILED, SP-3005: NETWORK_NO_CONNECTION | | Config | SP-4xxx | SP-4001: CONFIG_INVALID, SP-4005: CONFIG_INVALID_MEDIA_URL | | Download | SP-5xxx | SP-5001: DOWNLOAD_FAILED, SP-5002: DOWNLOAD_INSUFFICIENT_STORAGE | | General | SP-9xxx | SP-9999: UNKNOWN_ERROR |

Example

try {
  await player.load({ manifestUri, drm });
} catch (error) {
  console.error('Error Code:', error.code);    // SP-2001
  console.error('Message:', error.message);    // DRM license request failed
  console.error('Recovery Hint:', error.recoveryHint);
  console.error('Original:', error.originalError);
}

Async Error Events

Errors that occur during playback (after initialization) are dispatched as events on the video element:

player.addEventListener('error', (event) => {
  const error = event.detail;
  console.error('Playback Error:', error.code, error.message);

  // Handle specific error types
  switch (error.code) {
    case 'SP-2010':  // DRM_LICENSE_EXPIRED
      // Prompt user to refresh or re-authenticate
      break;
    case 'SP-3001':  // NETWORK_REQUEST_FAILED
      // Show network error message
      break;
    default:
      // Generic error handling
  }
});

Logging Best Practices

SecurePlay includes a built-in structured logger that helps with debugging and monitoring. The SDK automatically sanitizes sensitive information (tokens, URLs with query parameters, etc.) from log output.

Security Recommendation

⚠️ Important: In production environments, set the log level to WARN or ERROR to minimize information disclosure and improve performance.

import { SecurePlay, LogLevel } from 'secureplay-web';

// Recommended: Use environment-based log level
const logLevel = process.env.NODE_ENV === 'production'
  ? LogLevel.WARN   // Production: Only warnings and errors
  : LogLevel.DEBUG; // Development: Full debug logging

const player = new SecurePlay({
  videoElement: document.getElementById('video'),
  logger: {
    level: logLevel
  }
});

Available Log Levels

  • DEBUG: Detailed information for debugging (includes all logs)
  • INFO: General informational messages (default in development)
  • WARN: Warning messages (recommended for production)
  • ERROR: Error messages only (most restrictive)

Automatic Data Sanitization

The logger automatically masks sensitive information:

// Input
logger.info('Loading content', {
  url: 'https://cdn.example.com/video.mpd?token=secret123',
  authorization: 'Bearer xyz-token-abc'
});

// Actual log output (sanitized)
{
  url: 'https://cdn.example.com/video.mpd?***PARAMS_MASKED***',
  authorization: 'Be...bc'  // Only first 2 and last 2 characters shown
}

Automatically masked keys: token, authorization, apikey, secret, password, session, cookie, deviceid