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

@hengkianggia/drm-player

v1.0.3

Published

Lightweight, framework-agnostic Vanilla TypeScript DRM Video Player

Readme

@hengkianggia/drm-player

Package video player DRM modern berbasis Vanilla TypeScript dengan arsitektur Web Component (Shadow DOM) dan Core Headless Engine. Didesain ultra-ringan tanpa dependensi framework (Zero React/Vue runtime overhead), sehingga siap digunakan langsung di Vanilla HTML/JS maupun diintegrasikan ke framework modern (React, Next.js, Vue, Nuxt, Svelte, Angular).

Dilengkapi sistem proteksi layar canggih (Hardware DRM DirectComposition Video Overlay Protection & Blackout Shield pencegah tangkapan layar/screenshot), kontrol UI sinematik transparan, pemilihan resolusi multi-bitrate, serta penanganan pemutaran ulang (replay) yang andal.


🌟 Fitur Utama

  • 🚀 Framework Agnostic & Ultra-Lightweight: Menggunakan TypeScript murni dengan bundling cepat via tsup (esbuild). Ukuran ringkas dan performa decoding native.
  • 🛡️ Multi-DRM & Hardware Blackout Protection:
    • Dukungan Google Widevine Modular (L1/L3, Axinom DRM) & Microsoft PlayReady.
    • Anti-Screenshot & Capture Blackout: Penekanan tombol PrintScreen, Win + Shift + S, Alt + PrtScn, serta shortcut DevTools seketika memicu blackout video dan membersihkan clipboard sistem.
    • Preserved Hardware Video Overlay: Desain CSS controls bebas efek blur (backdrop-filter: blur) untuk mempertahankan DirectComposition Surface GPU overlay agar tangkapan layar OS menghasilkan layar hitam (black screen) secara native.
    • Anti Klik Kanan (Context Menu Guard): Mencegah menu konteks default pada kanvas video.
  • 🎛️ UI Controls Sinematik Transparan:
    • Desain gradien transparan elegan tanpa mengganggu fokus konten video.
    • Baris kontrol lengkap: Play / Pause, Skip Prev 5 Detik, Skip Next 5 Detik, Volume Slider & Mute Toggle, Menu Pemilihan Resolusi / Kualitas, Minimize / Picture-in-Picture (PiP), dan Fullscreen.
    • Scrubber Bar di Paling Bawah: Navigasi waktu presisi dengan visualisasi buffer dan durasi total.
    • Idle Auto-Fade: Kontrol otomatis menghilang halus setelah 2.5 detik mouse tidak aktif saat playback berlangsung.
  • 🔄 Reliable Replay & Playback Recovery: Mekanisme replay khusus untuk Dash.js yang mereset stream controller saat video selesai (ended) tanpa risiko macet di status loading/buffering.
  • 🔗 Dual-Mode Integration:
    1. Plug & Play Web Component (<drm-player>): Langsung taruh tag HTML di mana saja dengan Shadow DOM terisolasi.
    2. Headless Programmatic Core (DrmPlayer): Kendalikan video element HTML5 kustom secara fleksibel melalui kode TypeScript/JavaScript.
  • 📡 Typed Event Emitter: Event lengkap (playbackStateChange, timeUpdate, qualityChanged, screenshotAttempted, error, dll).

📦 Instalasi

Gunakan package manager pilihan Anda:

# Menggunakan pnpm (Direkomendasikan)
pnpm add @hengkianggia/drm-player

# Menggunakan npm
npm install @hengkianggia/drm-player

# Menggunakan yarn
yarn add @hengkianggia/drm-player

🚀 Panduan Penggunaan

1. Menggunakan Web Component <drm-player> (Paling Mudah)

Cukup impor bundle library sekali. Tag kustom <drm-player> otomatis terdaftar di browser (Custom Elements Registry).

Mode A: Integrasi Otomatis dengan Backend API Token (Direkomendasikan)

Player otomatis meminta token dan license URL ke endpoint backend Anda:

<!DOCTYPE html>
<html lang="id">
<head>
  <meta charset="UTF-8">
  <title>VOD Player</title>
</head>
<body>
  <!-- Web Component Player -->
  <drm-player
    api-url="https://be-vod-staging-181929713274.asia-southeast2.run.app/api"
    video-id="e2ff3d35-e99f-44a7-a777-c01f9526d05d"
    app-credential="cdbaac23-3064-4616-ba8b-8b57ca2bc237"
    credential-header-key="x-app-credential"
  ></drm-player>

  <!-- Import bundle script -->
  <script type="module" src="./node_modules/@hengkianggia/drm-player/dist/index.js"></script>
</body>
</html>

Alur Kerja Mode A: Komponen akan melakukan GET {{apiUrl}}/drm/token/:videoId dengan header x-app-credential. Response berisi { videoUrl, licenseUrl, token } akan langsung dipakai untuk memutar video terenkripsi secara otomatis.


Mode B: Menggunakan Parameter Manual (Direct MPD & License URL)

Jika Anda sudah memiliki URL manifest dan token lisensi sendiri:

<drm-player
  src="https://storage.googleapis.com/your-bucket/stream/index.mpd"
  widevine-url="https://601af1f0.drm-widevine-licensing.axprod.net/AcquireLicense"
  drm-token="eyJhbGciOi..."
></drm-player>

<script type="module" src="./node_modules/@hengkianggia/drm-player/dist/index.js"></script>

2. Kustomisasi Tema & Tampilan Kontrol

Tampilan kontrol <drm-player> dapat diatur warnanya menggunakan CSS Custom Properties (CSS Variables):

/* Mengubah warna aksen tombol scrubber dan icon */
drm-player {
  --drm-accent: #38bdf8; /* Warna aksen cyan / biru */
  --drm-radius: 12px;    /* Kelengkungan sudut player */
  width: 100%;
  max-width: 900px;
  display: block;
}

Menghidupkan Mode Native Controls (controls attribute)

Jika ingin menggunakan kontrol bawaan browser standar (tanpa overlay kustom):

<drm-player
  api-url="https://.../api"
  video-id="..."
  app-credential="..."
  controls
></drm-player>

3. Penggunaan di Framework Modern

React / Next.js (App Router / Pages Router)

Gunakan ref dan komponen DrmPlayer programmatic:

'use client';

import React, { useEffect, useRef } from 'react';
import { DrmPlayer } from '@hengkianggia/drm-player';

export const MyDrmVideo = () => {
  const videoRef = useRef<HTMLVideoElement | null>(null);
  const playerRef = useRef<DrmPlayer | null>(null);

  useEffect(() => {
    if (!videoRef.current) return;

    // Inisialisasi engine DrmPlayer
    const player = new DrmPlayer(videoRef.current, {
      autoplay: false,
      muted: false,
      screenProtection: true,
      disableContextMenu: true,
    });
    playerRef.current = player;

    // Memuat video dari API Backend
    player.loadFromApi({
      apiUrl: 'https://be-vod-staging-181929713274.asia-southeast2.run.app/api',
      videoId: 'e2ff3d35-e99f-44a7-a777-c01f9526d05d',
      appCredential: 'cdbaac23-3064-4616-ba8b-8b57ca2bc237',
    }).catch((err) => console.error('Gagal memuat video:', err));

    // Cleanup memori saat komponen unmount
    return () => {
      player.destroy();
      playerRef.current = null;
    };
  }, []);

  return (
    <div style={{ position: 'relative', maxWidth: '800px', margin: '0 auto' }}>
      <video
        ref={videoRef}
        controls
        style={{ width: '100%', borderRadius: '8px', background: '#000' }}
      />
    </div>
  );
};

Vue 3 / Nuxt 3

Cukup daftarkan custom element atau gunakan script module:

<template>
  <div class="player-container">
    <drm-player
      api-url="https://be-vod-staging-181929713274.asia-southeast2.run.app/api"
      video-id="e2ff3d35-e99f-44a7-a777-c01f9526d05d"
      app-credential="cdbaac23-3064-4616-ba8b-8b57ca2bc237"
    ></drm-player>
  </div>
</template>

<script setup>
import { onMounted } from 'vue';

onMounted(async () => {
  // Import Web Component di sisi client
  await import('@hengkianggia/drm-player');
});
</script>

<style scoped>
.player-container {
  max-width: 860px;
  margin: 0 auto;
}
</style>

4. Penggunaan Programmatic Headless Core (DrmPlayer)

Jika ingin membangun antarmuka kontrol video Anda sendiri secara leluasa:

import { DrmPlayer } from '@hengkianggia/drm-player';

const videoElement = document.getElementById('my-video') as HTMLVideoElement;

const player = new DrmPlayer(videoElement, {
  autoplay: true,
  muted: false,
  preventShortcuts: true,
  disableContextMenu: true,
  screenProtection: true,
});

// Event Listeners
player.on('playbackStateChange', (state) => {
  console.log('Status pemutaran:', state); // 'loading' | 'playing' | 'paused' | 'buffering' | 'ended'
});

player.on('timeUpdate', (current, duration) => {
  console.log(`Waktu: ${current.toFixed(1)} / ${duration.toFixed(1)} detik`);
});

player.on('screenshotAttempted', (info) => {
  console.warn(`Upaya screenshot dicegat: tombol ${info.key}`);
});

player.on('error', (err) => {
  console.error('Player error:', err);
});

// Memuat stream DASH langsung
await player.load('https://example.com/stream/manifest.mpd', {
  widevine: {
    serverURL: 'https://license.server.com/widevine',
    httpRequestHeaders: {
      'X-AxDRM-Message': 'YOUR_JWT_TOKEN',
    },
  },
});

// Kontrol Playback
player.play();
player.pause();
player.replay(); // Memutar ulang dari detik ke-0 saat video telah usai
player.seek(30); // Lompat ke detik 30
player.setVolume(0.8);

📋 Daftar Atribut Web Component (<drm-player>)

| Atribut | Tipe | Default | Keterangan | | :--- | :--- | :--- | :--- | | api-url | string | undefined | URL endpoint base backend DRM VOD. | | video-id | string | undefined | ID unik video yang akan di-fetch dari backend API. | | app-credential | string | undefined | Token credential otentikasi aplikasi ke backend API. | | credential-header-key | string | 'x-app-credential' | Nama header HTTP pengiriman credential ke API. | | src | string | undefined | URL manifest file .mpd langsung (jika tanpa API backend). | | widevine-url | string | undefined | URL endpoint license server Widevine. | | playready-url | string | undefined | URL endpoint license server PlayReady (Edge/Windows). | | drm-token | string | undefined | Token lisensi JWT (disematkan pada header X-AxDRM-Message). | | controls | boolean | false | Jika dicantumkan, menampilkan kontrol bawaan native HTML5 video alih-alih UI kustom. | | autoplay | boolean | false | Memulai playback secara otomatis saat video siap. | | muted | boolean | false | Mengatur audio ke posisi bisu (mute). | | screen-protection | boolean | true | Mengaktifkan blackout shield saat penekanan tombol screenshot terdeteksi. | | disable-context-menu | boolean | true | Memblokir klik kanan pada kanvas pemutar video. |


🛠️ API Reference (DrmPlayer Class)

Method

Playback

  • play(): void: Memulai pemutaran video. Jika video sudah selesai (ended), otomatis memanggil replay().
  • pause(): void: Menjeda pemutaran video.
  • replay(): void: Memutar ulang video dari detik ke-0 secara mulus dengan reset pipeline buffer Dash.js dan safety fallback.
  • seek(seconds: number): void: Melompat ke posisi waktu tertentu (dalam detik).
  • setVolume(volume: number): void: Mengatur level volume (0.0 sampai 1.0).
  • setMute(muted: boolean): void: Mengatur status mute audio (true / false).

Status & Info

  • getCurrentTime(): number: Mengambil posisi waktu pemutaran saat ini (detik).
  • getDuration(): number: Mengambil durasi total video (detik).
  • getState(): PlaybackState: Mengambil status pemutaran saat ini ('idle' | 'loading' | 'playing' | 'paused' | 'buffering' | 'ended' | 'error').
  • getVideoElement(): HTMLVideoElement: Mengambil referensi elemen DOM <video> yang dikontrol.
  • getDashPlayer(): dashjs.MediaPlayerClass | null: Mengambil instance controller internal Dash.js.

Kualitas Video & Multi-Bitrate

  • getQualities(): VideoQuality[]: Mengembalikan daftar resolusi dan bitrate yang tersedia pada manifest ([{ qualityIndex, label, width, height, bitrate }]).
  • setQuality(qualityIndex: number): void: Memilih resolusi tertentu berdasarkan indeks, atau -1 untuk mengaktifkan Auto ABR (Adaptive Bitrate).

Ingestion & DRM

  • load(manifestUrl: string, drmConfig?: DrmProtectionConfig): Promise<void>: Memuat manifest MPD dengan opsi proteksi DRM kustom.
  • loadFromApi(config: DrmTokenFetchConfig): Promise<void>: Mengambil token dari API backend lalu langsung memutar video.
  • fetchTokenFromApi(config: DrmTokenFetchConfig): Promise<DrmTokenResponse>: Helper request token ke endpoint backend.
  • setDrmConfig(config: DrmProtectionConfig): void: Memperbarui konfigurasi DRM saat runtime.
  • updateLicenseToken(headerName: string, token: string): void: Memperbarui nilai header token lisensi (misal: refresh token expired).
  • destroy(): void: Melepaskan seluruh listener DOM, menghentikan buffer Dash.js, dan membersihkan alokasi memori.

Event List

Dengarkan event menggunakan player.on(eventName, handler):

| Nama Event | Parameter | Penjelasan | | :--- | :--- | :--- | | playbackStateChange | (state: PlaybackState) | Terpanggil saat status berubah ('loading', 'playing', 'paused', 'buffering', 'ended', 'error'). | | timeUpdate | (currentTime: number, duration: number) | Terpanggil berkala saat posisi detik pemutaran bergulir. | | manifestLoaded | () | Manifest MPD berhasil diunduh dan diparsing oleh engine. | | qualityChanged | (qualityIndex: number) | Pengguna atau Auto ABR berganti tingkat resolusi / bitrate. | | tokenFetched | (data: DrmTokenResponse) | Berhasil mendapatkan token lisensi dari API backend. | | screenshotAttempted| (info: { key: string }) | Pengguna menekan kombinasi tombol screenshot (PrtScn, Win+Shift+S, dll). | | insecureContextWarning| (message: string) | Halaman dibuka pada protokol non-HTTPS selain localhost. | | error | (err: { message: string, code?: number, raw?: any }) | Terjadi kesalahan pada DRM, manifest, atau decoding video. | | destroyed | () | Instance player telah dibersihkan secara sempurna dari memori. |


🛡️ Mekanisme Layar Hitam (Anti-Screenshot)

Player menerapkan mekanisme proteksi berlapis untuk memastikan konten DRM tidak mudah dibajak:

  1. DirectComposition Hardware Video Overlay: Ketika video diputar dengan enkripsi DRM (Widevine / PlayReady), GPU Windows dan browser Chromium/Edge merender frame langsung ke lapisan perangkat keras (Hardware DirectComposition Surface).

    Catatan Teknis: Kontrol UI pada package ini sengaja dirancang tanpa filter blur (backdrop-filter: blur()). Efek blur pada browser berbasis Chromium memaksa compositor menyatukan (flatten) surface video ke software canvas, yang dapat merusak proteksi layar hitam native OS. Dengan CSS transparan sinematik murni, proteksi hardware overlay tetap aktif 100%.

  2. Intercept Shortcut Screenshot & Snipping Tool: Player mencegat event tombol seperti:

    • PrintScreen
    • Windows + Shift + S (Snipping Tool)
    • Alt + PrintScreen
    • Shortcut DevTools: F12, Ctrl + Shift + I, Ctrl + Shift + J

    Saat shortcut terdeteksi, player seketika menampilkan layar blackout shield dan memicu navigator.clipboard.writeText('') untuk mengosongkan clipboard sistem.


🧪 Menguji Demo Lokal (Playground)

Untuk menjalankan playground pengujian interaktif lokal:

# 1. Install dependensi
pnpm install

# 2. Build library
pnpm run build

# 3. Jalankan server preview
pnpm run preview

Buka peramban di http://localhost:3000/playground/web-component.html untuk melihat demo player yang terhubung ke backend staging VOD.


📄 Lisensi

MIT License © 2026