@guardvideo/player-sdk
v3.7.0
Published
GuardVideo Player SDK - Secure HLS video player with embed token authentication for React, Next.js, and vanilla JavaScript
Downloads
1,467
Maintainers
Readme
GuardVideo Player SDK
🎬 Secure HLS video player with embed token authentication — React, Next.js, and vanilla JavaScript
Features
✅ Custom Professional Player UI — No native browser controls; full custom design
✅ Adaptive Bitrate Streaming — Automatic quality switching based on network conditions
✅ Secure Token Authentication — API key never exposed to the browser
✅ Multi-Quality Support — 1080p, 720p, 480p, 360p
✅ AES-128 Encryption — Secure video segment delivery
✅ Forensic Watermark — Viewer-identifying overlay baked into playback (default: on)
✅ React & Next.js Support — First-class TypeScript support
✅ Vanilla JS / CDN Support — Full custom UI without any framework
✅ TypeScript Types — Full type safety included
✅ Browser Support — All modern browsers + Safari/iOS native HLS
Installation
NPM / Yarn (React / Next.js)
npm install @guardvideo/player-sdk
# or
yarn add @guardvideo/player-sdkCDN (Vanilla JavaScript)
<!-- full bundle (recommended for development) -->
<script src="https://cdn.jsdelivr.net/npm/@guardvideo/player-sdk@latest/dist/vanilla/guardvideo-player.js"></script>
<!-- minified bundle (recommended for production) -->
<script src="https://cdn.jsdelivr.net/npm/@guardvideo/player-sdk@latest/dist/vanilla/guardvideo-player.min.js"></script>Quick Start
React / Next.js Component
import { GuardVideoPlayer } from '@guardvideo/player-sdk';
function VideoPage() {
return (
<GuardVideoPlayer
videoId="your-video-id"
embedTokenEndpoint="/api/embed-token"
width="100%"
height="500px"
viewerEmail="[email protected]"
viewerName="Jane Doe"
forensicWatermark // default: true
onReady={() => console.log('Video ready!')}
onError={(error) => console.error(error)}
onQualityChange={(quality) => console.log('Quality:', quality)}
onStateChange={(state) => console.log('State:', state)}
/>
);
}Note: The component renders a fully custom player UI with play/pause, seek bar, volume control, quality selector, playback speed menu, and fullscreen. You do not need to add the native
controlsattribute or wrap it in any overlay.
React Hook (build your own controls)
import { useGuardVideoPlayer } from '@guardvideo/player-sdk';
function CustomPlayer({ videoId }: { videoId: string }) {
const {
videoRef,
isReady,
isPlaying,
currentTime,
duration,
qualityLevels,
play,
pause,
seek,
setVolume,
setQuality,
} = useGuardVideoPlayer({
videoId,
embedTokenEndpoint: '/api/embed-token',
viewerEmail: '[email protected]',
});
const formatTime = (s: number) =>
`${Math.floor(s / 60)}:${String(Math.floor(s % 60)).padStart(2, '0')}`;
return (
<div>
<video ref={videoRef} style={{ width: '100%' }} />
{isReady && (
<div>
<button onClick={isPlaying ? pause : play}>
{isPlaying ? 'Pause' : 'Play'}
</button>
<input
type="range" min={0} max={duration} value={currentTime}
onChange={(e) => seek(Number(e.target.value))}
/>
<span>{formatTime(currentTime)} / {formatTime(duration)}</span>
<select onChange={(e) => setQuality(Number(e.target.value))}>
<option value="-1">Auto</option>
{qualityLevels.map((l) => (
<option key={l.index} value={l.index}>{l.name}</option>
))}
</select>
</div>
)}
</div>
);
}Next.js App Router
// app/watch/[videoId]/page.tsx
'use client';
import { GuardVideoPlayer } from '@guardvideo/player-sdk';
export default function WatchPage({ params }: { params: { videoId: string } }) {
return (
<GuardVideoPlayer
videoId={params.videoId}
embedTokenEndpoint="/api/embed-token"
width="100%"
height="600px"
viewerEmail="[email protected]"
onReady={() => console.log('Ready!')}
onError={(e) => console.error(e)}
/>
);
}Vanilla JavaScript (CDN)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>GuardVideo Player</title>
<style>
#player-container {
width: 100%;
max-width: 900px;
aspect-ratio: 16/9;
margin: 40px auto;
}
</style>
</head>
<body>
<div id="player-container"></div>
<script src="https://cdn.jsdelivr.net/npm/@guardvideo/player-sdk@latest/dist/vanilla/guardvideo-player.min.js"></script>
<script>
const player = GuardVideoPlayer.create('player-container', 'your-video-id', {
embedTokenEndpoint: '/api/embed-token',
viewerEmail: '[email protected]',
viewerName: 'Jane Doe',
onReady: function () {
console.log('Ready! Qualities:', player.getQualityLevels());
},
onError: function (err) {
console.error('Player error:', err);
},
onQualityChange: function (quality) {
console.log('Quality changed to:', quality);
},
onStateChange: function (state) {
console.log('State:', state);
},
});
</script>
</body>
</html>The vanilla build bundles everything (hls.js included). No npm install, no bundler needed. The player renders the same full custom UI as the React component.
Backend Setup
Your backend needs to create embed tokens using your API key:
// Next.js API Route: app/api/embed-token/[videoId]/route.ts
export async function POST(
request: Request,
{ params }: { params: { videoId: string } }
) {
const VIDEO_API_KEY = process.env.VIDEO_API_KEY!;
const VIDEO_API_BASE = process.env.NEXT_PUBLIC_VIDEO_API_BASE!;
const response = await fetch(
`${VIDEO_API_BASE}/videos/${params.videoId}/embed-token`,
{
method: 'POST',
headers: {
'X-API-Key': VIDEO_API_KEY, // API key stays on the server!
'Content-Type': 'application/json',
},
body: JSON.stringify({
allowedDomain: request.headers.get('origin'),
expiresInMinutes: 120,
maxViews: null,
viewerEmail: /* optionally forward from session */ undefined,
forensicWatermark: true,
}),
}
);
return Response.json(await response.json());
}Environment Variables
VIDEO_API_KEY=your-secret-api-key
NEXT_PUBLIC_VIDEO_API_BASE=http://localhost:3001API Reference
React Component Props (GuardVideoPlayerProps)
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| videoId | string | — | Required. Video ID to load |
| embedTokenEndpoint | string | — | Required. Your backend endpoint that mints embed tokens |
| width | string \| number | "100%" | Player container width |
| height | string \| number | "auto" | Player container height |
| apiBaseUrl | string | — | Override default API base URL |
| autoplay | boolean | false | Auto-play on load |
| debug | boolean | false | Enable verbose debug logging |
| viewerName | string | — | Viewer name (used for forensic watermark) |
| viewerEmail | string | — | Viewer email (used for forensic watermark) |
| forensicWatermark | boolean | true | Show viewer-identifying watermark overlay |
| branding | BrandingConfig | — | Custom accent colour and brand name |
| security | SecurityConfig | — | Security hardening options |
| contextMenuItems | ContextMenuItem[] | — | Extra context menu items |
| hlsConfig | HlsConfig | — | HLS.js configuration overrides |
| onReady | () => void | — | Called when video is ready to play |
| onError | (err: PlayerError) => void | — | Called on error |
| onQualityChange | (quality: string) => void | — | Called when quality changes |
| onStateChange | (state: PlayerState) => void | — | Called on state transitions |
| onTimeUpdate | (time: number) => void | — | Called on time updates |
| onEnded | () => void | — | Called when video ends |
Vanilla JS Config (PlayerConfig)
All React props above except JSX-specific ones (className, style) are available.
Extra vanilla-only props:
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| width | string | "100%" | Player container width CSS value |
| height | string | "auto" | Player container height CSS value |
Player Methods
player.play(): Promise<void> // Start playback
player.pause(): void // Pause
player.seek(time: number): void // Seek to time in seconds
player.getCurrentTime(): number // Current playback position (seconds)
player.getDuration(): number // Total duration (seconds)
player.getVolume(): number // Current volume (0–1)
player.setVolume(v: number): void // Set volume (0–1)
player.getQualityLevels(): QualityLevel[] // Available quality levels
player.getCurrentQuality(): QualityLevel | null // Active quality
player.setQuality(index: number): void // Set quality (-1 = auto)
player.getState(): PlayerState // Current player state
player.getVideoElement(): HTMLVideoElement | null // Underlying <video> element
player.destroy(): void // Clean up and remove playerPlayer States
enum PlayerState {
IDLE = 'idle', // Not yet initialized
LOADING = 'loading', // Loading video
READY = 'ready', // Ready to play
PLAYING = 'playing', // Currently playing
PAUSED = 'paused', // Paused
BUFFERING = 'buffering', // Re-buffering mid-play
ERROR = 'error', // Unrecoverable error
}BrandingConfig
interface BrandingConfig {
accentColor?: string; // Hex color, default: '#44c09b'
name?: string; // Brand name shown in secure badge, default: 'GuardVideo'
logoUrl?: string; // Logo URL (used in context menu)
websiteUrl?: string; // Website URL (used in context menu)
}SecurityConfig
interface SecurityConfig {
disableContextMenu?: boolean; // Block right-click (default: true)
disableDevTools?: boolean; // Detect/block DevTools (default: false)
disablePiP?: boolean; // Block Picture-in-Picture (default: false)
disableDragDrop?: boolean; // Block drag-and-drop (default: true)
disableScreenshot?: boolean; // Attempt screenshot blocking (default: false)
}Keyboard Shortcuts
When the player has focus:
| Key | Action |
|-----|--------|
| Space / K | Play / Pause |
| ← | Seek back 5 s |
| → | Seek forward 5 s |
| ↑ | Volume +10% |
| ↓ | Volume −10% |
| M | Toggle mute |
| F | Toggle fullscreen |
Using Ref for Imperative Control (React)
import { useRef } from 'react';
import { GuardVideoPlayer, GuardVideoPlayerRef } from '@guardvideo/player-sdk';
function VideoWithRef() {
const playerRef = useRef<GuardVideoPlayerRef>(null);
return (
<div>
<GuardVideoPlayer
ref={playerRef}
videoId="your-video-id"
embedTokenEndpoint="/api/embed-token"
/>
<button onClick={() => playerRef.current?.seek(playerRef.current.getCurrentTime() + 10)}>
+10 s
</button>
</div>
);
}TypeScript Support
import type {
PlayerConfig,
PlayerInstance,
PlayerState,
PlayerError,
QualityLevel,
BrandingConfig,
SecurityConfig,
} from '@guardvideo/player-sdk';Browser Support
| Browser | Minimum version | |---------|----------------| | Chrome | 90+ | | Firefox | 88+ | | Safari | 14+ (native HLS) | | Edge | 90+ | | iOS Safari | 14+ | | Android Chrome | 90+ |
License
MIT © GuardVideo
Support
- GitHub Issues: github.com/guardvideo/player-sdk
- Documentation: docs.guardvideo.com
|
Space/K| Play / Pause | |←| Seek back 5 s | |→| Seek forward 5 s | |↑| Volume +10% | |↓| Volume −10% | |M| Toggle mute | |F| Toggle fullscreen |
Using Ref for Imperative Control (React)
import { useRef } from 'react';
import { GuardVideoPlayer, GuardVideoPlayerRef } from '@guardvideo/player-sdk';
function VideoWithRef() {
const playerRef = useRef<GuardVideoPlayerRef>(null);
return (
<div>
<GuardVideoPlayer
ref={playerRef}
videoId="your-video-id"
embedTokenEndpoint="/api/embed-token"
/>
<button onClick={() => playerRef.current?.seek(playerRef.current.getCurrentTime() + 10)}>
+10 s
</button>
</div>
);
}TypeScript Support
import type {
PlayerConfig,
PlayerInstance,
PlayerState,
PlayerError,
QualityLevel,
BrandingConfig,
SecurityConfig,
} from '@guardvideo/player-sdk';Browser Support
| Browser | Minimum version | |---------|----------------| | Chrome | 90+ | | Firefox | 88+ | | Safari | 14+ (native HLS) | | Edge | 90+ | | iOS Safari | 14+ | | Android Chrome | 90+ |
License
MIT © GuardVideo
Support
- GitHub Issues: github.com/guardvideo/player-sdk
- Documentation: docs.guardvideo.com
| Safari | 14+ (native HLS) | | Edge | 90+ | | iOS Safari | 14+ | | Android Chrome | 90+ |
License
MIT (C) GuardVideo
Support
- GitHub Issues: https://github.com/guardvideo/player-sdk
- Documentation: https://docs.guardvideo.com
