@hengkianggia/drm-player
v1.0.3
Published
Lightweight, framework-agnostic Vanilla TypeScript DRM Video Player
Maintainers
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:
- Plug & Play Web Component (
<drm-player>): Langsung taruh tag HTML di mana saja dengan Shadow DOM terisolasi. - Headless Programmatic Core (
DrmPlayer): Kendalikan video element HTML5 kustom secara fleksibel melalui kode TypeScript/JavaScript.
- Plug & Play Web Component (
- 📡 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/:videoIddengan headerx-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 memanggilreplay().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.0sampai1.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-1untuk 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:
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%.Intercept Shortcut Screenshot & Snipping Tool: Player mencegat event tombol seperti:
PrintScreenWindows + 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 previewBuka peramban di http://localhost:3000/playground/web-component.html untuk melihat demo player yang terhubung ke backend staging VOD.
📄 Lisensi
MIT License © 2026
