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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@raqemi/video-drm

v1.0.0

Published

A secure DRM solution for Next.js LMS video content protection

Readme

Raqemi Video DRM

A comprehensive Digital Rights Management (DRM) solution for Next.js applications, specifically designed to protect video content in Learning Management Systems and similar platforms.

Features

  • Strong Encryption: AES-256 encryption for video content
  • Secure Streaming: HLS/DASH streaming with encryption
  • Multiple Storage Options: Local storage, Amazon S3, and Cloudinary
  • Anti-Piracy Protection:
    • Prevents screen capture and recording
    • Blocks browser developer tools
    • Applies dynamic watermarks
    • Prevents right-click downloading
  • Session Management: Controls concurrent user sessions
  • Device Fingerprinting: Identifies and restricts suspicious devices
  • License Management: Token-based access control for videos
  • Easy Integration: React components for seamless integration

Installation

npm install @raqemi/video-drm
# or
yarn add @raqemi/video-drm

Quick Start

1. Initialize the DRM System

// _app.tsx or similar entry point
import { initializeRaqemiDRM } from '@raqemi/video-drm';

initializeRaqemiDRM({
  storage: {
    type: 'local',
    path: './videos'
    // For S3:
    // type: 's3',
    // s3: {
    //   region: 'us-east-1',
    //   bucket: 'your-bucket',
    //   accessKeyId: process.env.AWS_ACCESS_KEY_ID,
    //   secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY
    // }
  },
  encryption: {
    algorithm: 'aes-256-gcm',
    keySize: 256,
    secretKey: process.env.ENCRYPTION_SECRET_KEY,
    enableSegmentEncryption: true
  },
  license: {
    tokenExpiryTime: 3600, // 1 hour
    jwtSecret: process.env.JWT_SECRET,
    allowedDomains: ['yourdomain.com'],
    maxConcurrentSessions: 2
  },
  protection: {
    preventScreenCapture: true,
    preventRightClick: true,
    applyWatermark: true,
    watermarkText: 'Confidential Content',
    watermarkOpacity: 0.3,
    detectVirtualMachines: true,
    blockDevTools: true
  }
});

2. Create API Routes for Video Access

// pages/api/videos/[id]/stream.ts
import { NextApiRequest, NextApiResponse } from 'next';
import { validateToken, getVideoMetadata } from '@raqemi/video-drm';
import fs from 'fs';
import path from 'path';

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
  const { id } = req.query;
  const token = req.headers.authorization?.replace('Bearer ', '') || req.query.token as string;
  
  try {
    // Validate token
    const decodedToken = await validateToken(token);
    
    // Check if token is for the requested video
    if (decodedToken.videoId !== id) {
      return res.status(403).json({ error: 'Invalid token for this video' });
    }
    
    // Get video metadata
    const metadata = await getVideoMetadata(id as string);
    if (!metadata) {
      return res.status(404).json({ error: 'Video not found' });
    }
    
    // Set headers for video streaming
    res.setHeader('Content-Type', 'application/x-mpegURL');
    res.setHeader('Access-Control-Allow-Origin', '*');
    
    // Stream the video
    const videoPath = path.join(process.cwd(), 'videos', metadata.fileName);
    const videoStream = fs.createReadStream(videoPath);
    
    videoStream.pipe(res);
  } catch (error) {
    console.error('Error streaming video:', error);
    res.status(500).json({ error: 'Error streaming video' });
  }
}
// pages/api/license/index.ts
import { NextApiRequest, NextApiResponse } from 'next';
import { getLicenseToken } from '@raqemi/video-drm';

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
  if (req.method !== 'POST') {
    return res.status(405).json({ error: 'Method not allowed' });
  }
  
  const { videoId, userId } = req.body;
  
  try {
    // Get JWT token for video access
    const token = await getLicenseToken(videoId, userId);
    
    res.status(200).json({ token });
  } catch (error) {
    console.error('Error generating license token:', error);
    res.status(500).json({ error: 'Error generating license token' });
  }
}

3. Use the Secure Video Player Component

// pages/courses/[id].tsx
import { SecurePlayer } from '@raqemi/video-drm';
import { useEffect, useState } from 'react';

export default function CoursePage({ courseId, videoId }) {
  const [token, setToken] = useState('');
  
  useEffect(() => {
    // Get license token
    const fetchToken = async () => {
      const response = await fetch('/api/license', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          videoId,
          userId: 'user-123',
        }),
      });
      
      const data = await response.json();
      setToken(data.token);
    };
    
    if (videoId) {
      fetchToken();
    }
  }, [videoId]);
  
  if (!token) {
    return <div>Loading...</div>;
  }
  
  return (
    <div>
      <h1>Course Video</h1>
      <SecurePlayer
        videoId={videoId}
        width="100%"
        height="auto"
        autoPlay={false}
        controls={true}
        watermarkText={`User: user-123 - Course: ${courseId}`}
      />
    </div>
  );
}

4. Add the Upload Component

// pages/admin/upload.tsx
import { UploadComponent } from '@raqemi/video-drm';
import { useState } from 'react';

export default function UploadPage() {
  const [uploadedVideo, setUploadedVideo] = useState(null);
  
  const handleUploadComplete = (metadata) => {
    setUploadedVideo(metadata);
    // Notify server or update database
  };
  
  return (
    <div>
      <h1>Upload Course Video</h1>
      
      <UploadComponent
        userId="admin-user"
        onUploadComplete={handleUploadComplete}
        onUploadError={(error) => console.error('Upload error:', error)}
        onUploadProgress={(progress) => console.log('Upload progress:', progress)}
        acceptedFileTypes="video/mp4,video/webm,video/ogg"
        maxFileSize={1024 * 1024 * 500} // 500MB
        metadata={{
          title: 'Introduction to Course',
          allowedRoles: ['subscriber', 'premium']
        }}
      />
      
      {uploadedVideo && (
        <div>
          <h2>Upload Successful!</h2>
          <p>Video ID: {uploadedVideo.id}</p>
          <p>Title: {uploadedVideo.title}</p>
        </div>
      )}
    </div>
  );
}

Configuration Options

Storage Configuration

| Option | Type | Description | |--------|------|-------------| | type | 'local' \| 's3' \| 'cloudinary' | Storage provider type | | path | string | Local storage path (for type 'local') | | s3 | object | S3 configuration object (for type 's3') | | cloudinary | object | Cloudinary configuration (for type 'cloudinary') |

Encryption Configuration

| Option | Type | Description | |--------|------|-------------| | algorithm | 'aes-256-cbc' \| 'aes-256-ctr' \| 'aes-256-gcm' | Encryption algorithm | | keySize | 256 \| 128 | Key size in bits | | enableSegmentEncryption | boolean | Enable per-segment encryption for HLS | | secretKey | string | Master encryption key | | ivSize | number | Initialization vector size |

License Management

| Option | Type | Description | |--------|------|-------------| | tokenExpiryTime | number | Token expiry time in seconds | | jwtSecret | string | Secret for JWT signing | | allowedDomains | string[] | List of domains allowed to play videos | | maxConcurrentSessions | number | Maximum number of concurrent user sessions |

Protection Features

| Option | Type | Description | |--------|------|-------------| | preventScreenCapture | boolean | Enable screen capture prevention | | preventRightClick | boolean | Disable right-click menu | | applyWatermark | boolean | Apply watermark to videos | | watermarkText | string | Custom watermark text | | watermarkOpacity | number | Watermark opacity (0-1) | | detectVirtualMachines | boolean | Detect and block virtual machines | | blockDevTools | boolean | Block browser developer tools |

API Reference

Components

  • SecurePlayer - Protected video player component
  • UploadComponent - Video upload component with encryption

Services

  • CryptoService - Handles encryption and decryption
  • getLicenseToken - Generates a license token for video access
  • validateToken - Validates a license token
  • uploadVideo - Uploads and encrypts a video file
  • prepareVideoForStreaming - Prepares a video for HLS streaming

Utilities

  • getFingerprint - Generates a unique device fingerprint
  • applyClientSideProtection - Applies protection measures to a video player

Security Considerations

  • Secret Keys: Store encryption keys and JWT secrets securely and never expose them to the client
  • Server-Side Validation: Always validate license tokens on the server-side
  • HTTPS: Use HTTPS to prevent man-in-the-middle attacks
  • Environment Variables: Store sensitive configuration in environment variables
  • Regular Key Rotation: Rotate encryption keys and JWT secrets regularly

Limitations

  • No DRM solution is 100% effective against determined attackers
  • Some protection features may be circumvented by sophisticated tools
  • Client-side protection works best when combined with server-side measures
  • Screen recording protection has limitations due to browser security models

License

MIT