@source/player (2.0.0)
Published 2026-02-12 17:00:37 +00:00 by hibna
Installation
@source:registry=npm install @source/player@2.0.0"@source/player": "2.0.0"About this package
🎬 Modern Video Player
A compact, feature-rich React video player with zero runtime dependencies.
A feature-rich, modern video player library built with React, TypeScript, and Vite. Designed for reusability across multiple projects.
🏆 Why Choose This Player?
| Feature | @source/player | video.js | react-player | plyr |
|---|---|---|---|---|
| Bundle Size (gzipped) | ~18KB JS + ~3.5KB CSS ✅ | ~500KB ❌ | ~50KB ⚠️ | ~30KB ⚠️ |
| Runtime Dependencies | 0 ✅ | Many ❌ | Few ⚠️ | Few ⚠️ |
| React (Web) | Yes ✅ | Wrapper ⚠️ | Yes ✅ | Wrapper ⚠️ |
| TypeScript Native | Yes ✅ | Types ⚠️ | Partial ⚠️ | Types ⚠️ |
| HLS Support | Yes ✅ | Yes ✅ | Yes ✅ | No ❌ |
| Quality Switching | Yes ✅ | Yes ✅ | Limited ⚠️ | No ❌ |
| Touch Gestures | 15+ ✅ | Limited ⚠️ | No ❌ | Limited ⚠️ |
| Keyboard Shortcuts | 15+ ✅ | ~8 ⚠️ | Basic ⚠️ | ~10 ⚠️ |
| i18n Support | Yes ✅ | Yes ✅ | No ❌ | Yes ✅ |
Key Advantages
- 📦 Compact bundle - Core player ships around ~18KB gzipped JS (+ ~3.5KB CSS)
- ⚡ Blazing fast - Zero runtime dependencies means faster load times
- 🎯 React-first - Built specifically for React, not a wrapper
- 🔧 Full TypeScript - Complete type safety out of the box
- 🎨 Easy customization - CSS variables for theming
- 📱 Mobile-ready - Comprehensive touch gesture support
- 🌍 Internationalized - Built-in i18n with English and Turkish
- ♿ Accessible - ARIA labels and keyboard navigation
✨ Features
🎮 Core Playback
- ▶️ Play/Pause controls
- ⏭️ Seek/scrub with progress bar
- 🔊 Volume control with slider
- 🎚️ Playback speed control
- 🔄 Loop support
- 🖼️ Custom poster/thumbnail
🎨 Modern UI
- Clean, minimalist design with red theme
- Smooth animations and transitions
- Auto-hiding controls
- Responsive layout (desktop & mobile)
- Custom SVG icons
- Loading spinner
- Center play button
⌨️ Keyboard Shortcuts
SpaceorK- Play/Pause←/→- Seek 5 secondsJ/L- Seek 10 seconds↑/↓- Volume up/downM- Mute/UnmuteF- Fullscreen toggleP- Picture-in-Picture0-9- Jump to percentage (10%-90%)Home/End- Jump to start/end- Shortcuts only work for the currently active/focused player instance
📱 Touch Gestures
- Tap - Play/Pause
- Double tap left - Rewind 10 seconds
- Double tap right - Forward 10 seconds
- Swipe left/right - Seek
- Swipe up/down - Volume control
🚀 Advanced Features
- HLS Streaming - Automatic HLS.js integration for .m3u8 files
- IPTV Support - MPEG-TS (.ts) streams for IPTV services
- HTTP Range Request - Progressive download for large MP4 files
- Subtitles - WebVTT and SRT support
- Multiple Audio Tracks - Switch between different audio streams
- Picture-in-Picture - Native browser PIP support
- Fullscreen - Native fullscreen API
- Buffer Indicator - Visual buffering progress
- Error Handling - Graceful error states
📦 Installation
This package is distributed through a private registry.
1. Configure .npmrc
Create .npmrc in your app root:
@source:registry=https://gits.hibna.com.tr/api/packages/hibna/npm/
//gits.hibna.com.tr/api/packages/hibna/npm/:_authToken=${GITS_NPM_TOKEN}
2. Set token
Set your token in environment variables (GITS_NPM_TOKEN) and do not commit .npmrc with a hardcoded token.
3. Install package
npm install @source/player
# or
pnpm add @source/player
# or
yarn add @source/player
4. Ensure peer dependencies
npm install react react-dom
Local development (optional)
# In this repository
npm run build:lib
npm link
# In your consuming app
npm link @source/player
🚀 Usage
Basic Example
import { VideoPlayer } from '@source/player'
import '@source/player/styles.css'
function App() {
return (
<VideoPlayer
src="https://example.com/video.mp4"
poster="https://example.com/poster.jpg"
/>
)
}
With Subtitles
<VideoPlayer
src="https://example.com/video.mp4"
subtitles={[
{ src: '/subtitles/en.vtt', lang: 'en', label: 'English', default: true },
{ src: '/subtitles/tr.srt', lang: 'tr', label: 'Türkçe' },
]}
/>
Subtitle Styling (Custom Renderer)
<VideoPlayer
src="https://example.com/video.mp4"
subtitles={[{ src: '/subtitles/en.vtt', lang: 'en', label: 'English', default: true }]}
subtitlePosition="bottom"
subtitleOffset={72}
subtitleStyle={{
fontSize: 24,
fontFamily: 'Arial, sans-serif',
color: '#f8fafc',
backgroundColor: '#111827',
backgroundOpacity: 0.72,
}}
/>
HLS Streaming
<VideoPlayer
src="https://example.com/stream/playlist.m3u8"
autoplay={false}
/>
Force Protocol (Override Auto Detection)
<VideoPlayer
src="https://cdn.example.com/video"
protocol="hls"
/>
IPTV Streaming
// Note: Browser support for direct .ts streams is limited
// For best compatibility, request .m3u8 (HLS) links from your IPTV provider
<VideoPlayer
src="http://server.com:8080/live/username/password/12345.ts"
poster="http://example.com/channel-logo.png"
onError={(error) => {
console.error('Stream error - try requesting .m3u8 format:', error)
}}
/>
Custom Theme
<VideoPlayer
src="https://example.com/video.mp4"
theme={{
primaryColor: '#ef4444',
accentColor: '#dc2626',
backgroundColor: '#000000',
textColor: '#ffffff',
}}
/>
With Volume and Playback Control
<VideoPlayer
src="https://example.com/video.mp4"
volume={0.5} // 50% volume
playbackRate={1.5} // 1.5x speed
currentTime={30} // Start at 30 seconds
/>
With Event Handlers
<VideoPlayer
src="https://example.com/video.mp4"
onPlay={() => console.log('Video started playing')}
onPause={() => console.log('Video paused')}
onEnded={() => console.log('Video ended')}
onTimeUpdate={(time) => console.log('Current time:', time)}
onVolumeChange={(volume) => console.log('Volume:', volume)}
onRateChange={(rate) => console.log('Playback rate:', rate)}
onFullscreenChange={(isFs) => console.log('Fullscreen:', isFs)}
onError={(error) => console.error('Video error:', error)}
/>
Feature Detection & Polyfills
import { features, initializePolyfills } from '@source/player'
// Initialize polyfills manually (optional - auto-initialized on VideoPlayer mount)
initializePolyfills()
// Check browser capabilities
console.log('Has PIP support:', features.hasPIP())
console.log('Has native HLS:', features.hasNativeHLS())
console.log('Has MSE for HLS.js:', features.hasMSE())
console.log('Is iOS Safari:', features.isIOSSafari())
console.log('Has volume control:', features.hasVolumeControl())
// Hide PIP button on unsupported devices
<VideoPlayer
src="video.mp4"
pictureInPicture={features.hasPIP()}
/>
CORS Error Handling
import { validateVideoURL, checkVideoCORS } from '@source/player'
// Validate URL before loading
const validation = validateVideoURL(videoUrl)
if (!validation.valid) {
console.error(validation.error)
}
// Check CORS support (async)
const corsCheck = await checkVideoCORS(videoUrl)
if (!corsCheck.supported) {
console.error('CORS issue:', corsCheck.error)
console.log('Needs proxy:', corsCheck.needsProxy)
}
🛠️ Development
Setup
# Install dependencies
npm install
# Start development server
npm run dev
# Build library
npm run build:lib
# Type check
npx tsc --noEmit
Project Structure
video-player/
├── src/
│ ├── components/ # React components
│ │ ├── controls/ # Control UI components
│ │ ├── overlays/ # Overlay components (loading, etc.)
│ │ └── menus/ # Settings menus
│ ├── contexts/ # React Context (PlayerContext)
│ ├── hooks/ # Custom React hooks
│ ├── utils/ # Utility functions
│ ├── types/ # TypeScript type definitions
│ ├── icons/ # Custom SVG icons
│ ├── styles/ # Global styles and CSS variables
│ └── index.ts # Main export file
├── examples/ # Demo application
├── public/ # Static assets
└── dist/ # Built library (generated)
📖 API Reference
VideoPlayer Props
Basic Props
| Prop | Type | Default | Description |
|---|---|---|---|
src |
string |
required | Video source URL (MP4, WebM, HLS, IPTV .ts) |
poster |
string |
- | Poster image URL |
protocol |
'auto' | 'native' | 'hls' | 'rtmp' | 'dash' | 'mpegts' |
'auto' |
Force playback engine instead of URL auto-detection |
autoplay |
boolean |
false |
Auto-play video on load |
loop |
boolean |
false |
Loop video playback |
muted |
boolean |
false |
Start muted |
volume |
number |
- | Initial volume (0-1) |
playbackRate |
number |
- | Playback speed (0.25, 0.5, 1, 1.5, 2, etc.) |
currentTime |
number |
- | Initial playback position in seconds |
crossOrigin |
'' | 'anonymous' | 'use-credentials' |
- | Sets the video crossOrigin attribute |
preload |
'none' | 'metadata' | 'auto' |
'metadata' |
Sets the video preload strategy |
playsInline |
boolean |
true |
Enables inline playback on mobile browsers |
controlsList |
string |
- | Passes controlsList attribute to the video element |
controls |
boolean |
true |
Show player controls |
subtitles |
SubtitleTrack[] |
[] |
Subtitle tracks |
subtitleStyle |
SubtitleStyle |
- | Custom subtitle text/background style |
subtitlePosition |
'top' | 'center' | 'bottom' |
'bottom' |
Subtitle vertical placement |
subtitleOffset |
number | string |
- | Subtitle offset (px if number) |
theme |
PlayerTheme |
- | Custom theme colors |
language |
string |
'en' |
UI language ('en' or 'tr') |
keyboardShortcuts |
boolean |
true |
Enable keyboard shortcuts |
pictureInPicture |
boolean |
true |
Enable PIP button |
className |
string |
- | Custom CSS class |
style |
CSSProperties |
- | Inline styles |
Event Handlers
| Prop | Type | Description |
|---|---|---|
onPlay |
() => void |
Fired when playback starts |
onPause |
() => void |
Fired when playback pauses |
onEnded |
() => void |
Fired when playback ends |
onTimeUpdate |
(currentTime: number) => void |
Fired during playback with current time |
onVolumeChange |
(volume: number) => void |
Fired when volume changes |
onError |
(error: Error) => void |
Fired on playback error |
onLoadedMetadata |
() => void |
Fired when video metadata is loaded |
onSeeking |
() => void |
Fired when seeking starts |
onSeeked |
() => void |
Fired when seeking completes |
onProgress |
(buffered: number) => void |
Fired during download progress |
onDurationChange |
(duration: number) => void |
Fired when duration changes |
onRateChange |
(playbackRate: number) => void |
Fired when playback rate changes |
onFullscreenChange |
(isFullscreen: boolean) => void |
Fired when fullscreen state changes |
onPictureInPictureChange |
(isPip: boolean) => void |
Fired when PIP state changes |
onWaiting |
() => void |
Fired when buffering starts |
onCanPlay |
() => void |
Fired when enough data is available to play |
SubtitleTrack
interface SubtitleTrack {
src: string // Subtitle file URL (.vtt or .srt)
lang: string // Language code (e.g., 'en', 'tr')
label: string // Display label
default?: boolean // Set as default subtitle
}
PlayerTheme
interface PlayerTheme {
primaryColor?: string // Primary color (default: #ef4444)
accentColor?: string // Accent/hover color (default: #dc2626)
backgroundColor?: string // Background color (default: #000000)
textColor?: string // Text color (default: #ffffff)
}
🎯 Browser Support
- Chrome/Edge 90+
- Firefox 88+
- Safari 14+
- Mobile Safari 14+
- Chrome Mobile 90+
📊 Bundle Size
- Core player JS bundle: ~18KB (gzipped)
- Core player CSS bundle: ~3.5KB (gzipped)
- HLS.js (optional, lazy-loaded): ~200KB (only when using HLS streams)
- MPEGTS.js (optional, lazy-loaded): ~72KB (gzipped, only for
.tsstreams) - Zero runtime dependencies (React is peer dependency)
🔧 Technical Details
Native Browser APIs Used
- HTML5 Video API
- Fullscreen API
- Picture-in-Picture API
- Fetch API (Range Requests)
- TextTrack API (subtitles)
- Touch Events API
- Keyboard Events API
Streaming Support
MP4/WebM (Progressive Download)
- Uses HTTP Range Requests
- Browser automatically chunks the download
- No additional library needed
- Works with any standard web server that supports Range headers
HLS (.m3u8)
- Automatically detects HLS sources
- Lazy loads hls.js library when needed
- Safari has native HLS support (no library needed)
- Adaptive bitrate streaming
Performance Optimizations
- Lazy loading of HLS.js with CDN fallback
- CSS-only animations
- Debounced control hiding
- Optimized re-renders with React.memo
- Tree-shakeable exports
- Memory leak prevention with proper cleanup
- Polyfills for older browser support
Error Handling & Reliability
- CORS Detection: Automatically detects and reports CORS issues with helpful error messages
- HLS.js Fallback: If npm package fails to load, automatically falls back to CDN
- Memory Management: Proper cleanup of HLS instances to prevent memory leaks
- Browser Polyfills: Vendor prefix support for Fullscreen and PIP APIs
- URL Validation: Validates video URLs before attempting to load
- Feature Detection: Checks browser capabilities before using advanced features
🚧 TODO / Future Enhancements
- Multiple audio track UI and switching
- Quality selector for HLS streams
- Playback speed menu
- Settings panel
- Chapters/markers support
- Thumbnail preview on hover
- Playlist support
- Chromecast support
- AirPlay support
- DASH streaming support
- Accessibility improvements (ARIA labels)
- Unit tests
- E2E tests
- Storybook documentation
📝 License
MIT
👤 Author
Alper
Built with ❤️ using React, TypeScript, and Vite
Dependencies
Development Dependencies
| ID | Version |
|---|---|
| @eslint/js | ^9.38.0 |
| @testing-library/jest-dom | ^6.9.1 |
| @testing-library/react | ^16.3.0 |
| @testing-library/user-event | ^14.6.1 |
| @types/react | ^19.2.2 |
| @types/react-dom | ^19.2.2 |
| @typescript-eslint/eslint-plugin | ^8.46.2 |
| @typescript-eslint/parser | ^8.46.2 |
| @vitejs/plugin-react | ^5.1.0 |
| @vitest/ui | ^4.0.4 |
| eslint | ^9.38.0 |
| eslint-plugin-react-hooks | ^7.0.1 |
| eslint-plugin-react-refresh | ^0.4.24 |
| globals | ^16.4.0 |
| jsdom | ^27.0.1 |
| react | ^19.2.0 |
| react-dom | ^19.2.0 |
| terser | ^5.44.0 |
| typescript | ^5.9.3 |
| vite | ^7.1.12 |
| vite-plugin-dts | ^4.5.4 |
| vitest | ^4.0.4 |
Peer Dependencies
| ID | Version |
|---|---|
| react | ^18.0.0 |
| react-dom | ^18.0.0 |
Optional Dependencies
| ID | Version |
|---|---|
| flv.js | ^1.6.2 |
| hls.js | ^1.6.13 |
| mpegts.js | ^1.7.3 |
Keywords
react
video
player
video-player
hls
rtmp
flv
mpegts
iptv
streaming
media