# @source/player Documentation This document reflects the current codebase in this repository (`version 3.1.0`) and replaces older, drifted documentation. ## Table of contents 1. [Overview](#1-overview) 2. [Architecture](#2-architecture) 3. [Installation and setup](#3-installation-and-setup) 4. [Quick usage](#4-quick-usage) 5. [Streaming and protocol handling](#5-streaming-and-protocol-handling) 6. [Subtitles and subtitle style editor](#6-subtitles-and-subtitle-style-editor) 7. [Customization and modular composition](#7-customization-and-modular-composition) 8. [Keyboard and touch interaction](#8-keyboard-and-touch-interaction) 9. [Internationalization](#9-internationalization) 10. [API reference](#10-api-reference) 11. [Public exports](#11-public-exports) 12. [Error handling and reliability](#12-error-handling-and-reliability) 13. [Browser behavior and known limitations](#13-browser-behavior-and-known-limitations) 14. [Testing and development](#14-testing-and-development) ## 1. Overview `@source/player` is a React video player library with: - Protocol-aware playback (`native`, `hls`, `rtmp/flv`, `mpegts`) - Built-in controls with settings menus (speed, quality, subtitles, subtitle styling, audio tracks) - Modular extension points for custom controls and overlays - Strong TypeScript API (props, handle types, context types, utility exports) - Runtime loading strategy for optional stream engines (`hls.js`, `flv.js`, `mpegts.js`) ## 2. Architecture The player is split into focused modules. ### 2.1 High-level component graph - `VideoPlayer` - `PlayerErrorBoundary` - `PlayerProvider` (context + state + actions + i18n) - `VideoElement` (media element, protocol setup, media events, subtitle rendering) - `ControlsLayer` (controls visibility, settings menu, keyboard/touch integration) - `SettingsMenu` (lazy-loaded, menu subviews) ### 2.2 Core responsibilities - `VideoPlayer` - Normalizes props and default values - Resolves subtitle style editor config (`enabled` + `storageKey`) - Wraps content with `PlayerErrorBoundary` and `PlayerProvider` - Exposes imperative API via ref (`VideoPlayerHandle`) - `PlayerProvider` - Owns central `videoState`, `uiState`, and `settings` - Manages subtitle style draft/commit/persist lifecycle - Merges built-in translations with custom translations - Exposes playback and UI actions through context - `VideoElement` - Validates URL and detects protocol - Initializes and tears down stream engine instances - Subscribes to media events and forwards callbacks - Converts `.srt` subtitles to VTT Blob URLs - Renders subtitles using a custom overlay (native rendering disabled) - `ControlsLayer` - Handles auto-hide controls behavior - Loads `SettingsMenu` lazily - Enables keyboard shortcuts and touch gestures hooks - Renders center play, spinner, progress, volume, settings, PIP, fullscreen ## 3. Installation and setup This package is published to a private registry. 1. Add `.npmrc`: ```ini @source:registry=https://gits.hibna.com.tr/api/packages/hibna/npm/ //gits.hibna.com.tr/api/packages/hibna/npm/:_authToken=${GITS_NPM_TOKEN} ``` 2. Install package: ```bash npm install @source/player # or pnpm add @source/player # or yarn add @source/player ``` 3. Ensure app already includes React and ReactDOM (`>=18`). 4. Import player styles: ```tsx import '@source/player/styles.css' ``` Optional local dependencies (instead of CDN fallback): ```bash npm install hls.js flv.js mpegts.js ``` ## 4. Quick usage ### 4.1 Minimal usage ```tsx import { VideoPlayer } from '@source/player' import '@source/player/styles.css' export function App() { return } ``` ### 4.2 With subtitles + editor ```tsx ``` ### 4.3 With theme and modular controls ```tsx Bookmark} controlsRightExtra={} >
Custom overlay
``` ## 5. Streaming and protocol handling ### 5.1 Auto protocol detection When `protocol="auto"` (default), `detectVideoProtocol(src)` chooses by URL pattern. | Pattern | Detected protocol | Live hint | Needs special engine | | --- | --- | --- | --- | | `rtmp://`, `rtmps://`, `rtmpt://`, `rtmpe://` | `rtmp` | true | true | | contains `.m3u8` | `hls` | true if contains `/live/` or `live.m3u8` | true | | contains `.mpd` | `dash` | true if contains `/live/` or `live.mpd` | true | | contains `.flv` or `flv?` | `rtmp` | true if contains `/live/` or `live.flv` | true | | `.ts` stream URL | `mpegts` | true | true | | other URLs | `native` | false | false | You can override with `protocol="native" | "hls" | "rtmp" | "dash" | "mpegts"`. ### 5.2 HLS flow - For HLS URLs: - Safari/native HLS path is preferred when appropriate. - Other browsers use `hls.js`. Loading strategy: 1. Dynamic import from npm (`hls.js`) 2. If unavailable, CDN fallback: `https://cdn.jsdelivr.net/npm/hls.js@1.6.13/dist/hls.min.js` HLS setup behavior: - Tracks audio tracks, quality levels, subtitle tracks - Handles fatal errors: - `NETWORK_ERROR` -> `startLoad()` - `MEDIA_ERROR` -> `recoverMediaError()` - others -> emit `onError` ### 5.3 RTMP / FLV flow - Uses `flv.js` runtime loader. - npm dynamic import first, then CDN fallback: - `https://cdn.jsdelivr.net/npm/flv.js@1.6.2/dist/flv.min.js` Important note: - Browser playback is effectively HTTP-FLV based. - Direct RTMP endpoints usually require server-side proxy/conversion to HTTP-FLV. ### 5.4 MPEG-TS flow - Uses `mpegts.js` runtime loader. - npm dynamic import first, then CDN fallback: - `https://cdn.jsdelivr.net/npm/mpegts.js@1.7.3/dist/mpegts.js` ### 5.5 DASH status - DASH (`.mpd`) is detected but not implemented. - Player emits `Error('DASH streaming is not yet supported')`. ### 5.6 Live broadcast UI behavior `videoState.isLiveBroadcast` is set from media duration (`Infinity` or `0`): - Progress bar and time display are hidden - Live badge is shown in controls ## 6. Subtitles and subtitle style editor ### 6.1 Subtitle sources `subtitles` prop accepts manual tracks: ```ts type SubtitleTrack = { src: string lang: string label: string default?: boolean } ``` HLS subtitle tracks are merged with manual tracks internally. ### 6.2 SRT support - `.srt` tracks are fetched and converted to VTT Blob URLs. - Blob URLs are revoked on cleanup. ### 6.3 Custom subtitle rendering - Native text track rendering is disabled. - Active cues are read from selected `TextTrack` and rendered inside `.sp-subtitle-overlay`. - Cue markup is stripped before rendering. ### 6.4 Subtitle styling `subtitleStyle` supports: - `fontFamily` - `fontSize` - `fontWeight` - `color` - `backgroundColor` - `backgroundOpacity` (0..1) Applied style is merged as: - base: `subtitleStyle` prop - override: context `settings.subtitleStyle` (including editor changes) ### 6.5 Subtitle style editor Enable via: ```tsx subtitleStyleEditor={true} // or subtitleStyleEditor={{ enabled: true, storageKey: 'my-key' }} ``` Behavior: - Editor appears under `Settings -> Subtitles` - Draft updates preview in real time - `Save` persists to localStorage - `Cancel` reverts to last committed style - `Reset` sets editor defaults (not persisted until save) Default storage key: - `source-player-subtitle-style` ## 7. Customization and modular composition ### 7.1 Theme API `theme` maps to CSS variables: - `primaryColor` -> `--player-primary` - `accentColor` -> `--player-primary-hover` - `backgroundColor` -> `--player-bg` - `textColor` -> `--player-text` - `fontFamily` -> `--player-font-family` - `borderRadius` -> `--player-radius` - `overlayOpacity` -> `--player-overlay-soft` - `controlsBackground` -> `--player-surface` - `textSecondaryColor` -> `--player-text-secondary` - `textMutedColor` -> `--player-text-muted` ### 7.2 Layout and slots - `aspectRatio`: `'16:9' | '4:3' | '21:9' | '1:1' | '9:16' | number` - `children`: overlay content slot - `controlsLeftExtra`: inject extra controls on left side - `controlsRightExtra`: inject extra controls on right side ### 7.3 Controls auto-hide `controlsAutoHideDelay` (default `3000`) affects full-screen auto-hide while: - video is playing - full-screen is active - no settings sub-menu is open ## 8. Keyboard and touch interaction ### 8.1 Keyboard shortcuts Enabled by default (`keyboardShortcuts=true`). The player only reacts when active/focused (clicked/touched/focused instance). Default shortcuts: - `Space` / `K`: play-pause - `ArrowLeft` / `ArrowRight`: seek `5s` - `J` / `L`: seek `10s` - `ArrowUp` / `ArrowDown`: volume +/- `0.1` - `M`: mute toggle - `F`: fullscreen toggle - `P`: picture-in-picture toggle - `0` / `Home`: seek to start - `End`: seek to end - `1..9`: seek to 10%..90% Config: ```ts type KeyboardShortcutConfig = { seekSmall?: number seekLarge?: number volumeStep?: number disabled?: string[] } ``` ### 8.2 Touch gestures Enabled via `useTouchGestures` in control layer. Default gestures: - single tap: play-pause - double tap left/right: seek -/+ `10s` - horizontal swipe: seek proportional to swipe distance - vertical swipe: volume change proportional to swipe distance Config: ```ts type TouchConfig = { maxSeekSeconds?: number maxVolumeChange?: number doubleTapSeekSeconds?: number } ``` Default values: - `maxSeekSeconds = 30` - `maxVolumeChange = 0.5` - `doubleTapSeekSeconds = 10` ## 9. Internationalization Built-in locales: - `en` - `tr` Selection flow: - If `language` prop is provided, it is used. - Else browser language is detected. - Region codes fallback to base language (`en-US` -> `en`). - Unknown language fallback: `en`. You can override any key with `translations?: Partial`. ## 10. API reference ### 10.1 `VideoPlayerProps` #### Source and playback | Prop | Type | Default | Notes | | --- | --- | --- | --- | | `src` | `string` | required | media URL | | `protocol` | `'auto' \| 'native' \| 'hls' \| 'rtmp' \| 'dash' \| 'mpegts'` | `'auto'` | force engine | | `poster` | `string` | - | poster image | | `autoplay` | `boolean` | `false` | autoplay attempt on load | | `loop` | `boolean` | `false` | loop playback | | `muted` | `boolean` | `false` | initial muted state | | `volume` | `number` | - | clamped to `0..1` | | `playbackRate` | `number` | - | initial/current rate | | `currentTime` | `number` | - | seeks when difference is significant | #### Media element attributes | Prop | Type | Default | | --- | --- | --- | | `crossOrigin` | `'' \| 'anonymous' \| 'use-credentials'` | - | | `preload` | `'none' \| 'metadata' \| 'auto'` | `'metadata'` | | `playsInline` | `boolean` | `true` | | `controlsList` | `string` | - | #### UI and feature toggles | Prop | Type | Default | Notes | | --- | --- | --- | --- | | `controls` | `boolean` | `true` | hide entire control layer when false | | `keyboardShortcuts` | `boolean` | `true` | enables keyboard hook | | `pictureInPicture` | `boolean` | `true` | PIP button toggle | | `controlsAutoHideDelay` | `number` | `3000` | ms | | `playbackRates` | `number[]` | `[0.25,0.5,0.75,1,1.25,1.5,1.75,2]` | settings menu speeds | | `aspectRatio` | `'16:9' \| '4:3' \| '21:9' \| '1:1' \| '9:16' \| number` | `'16:9'` equivalent | CSS ratio | #### Subtitles and settings | Prop | Type | Default | | --- | --- | --- | | `subtitles` | `SubtitleTrack[]` | `[]` | | `subtitleStyle` | `SubtitleStyle` | - | | `subtitleStyleEditor` | `boolean \| SubtitleStyleEditorConfig` | `false` | | `subtitlePosition` | `'top' \| 'center' \| 'bottom'` | `'bottom'` | | `subtitleOffset` | `number \| string` | - | #### Styling and composition | Prop | Type | Default | | --- | --- | --- | | `theme` | `PlayerTheme` | - | | `className` | `string` | `''` | | `style` | `CSSProperties` | - | | `children` | `ReactNode` | - | | `controlsLeftExtra` | `ReactNode` | - | | `controlsRightExtra` | `ReactNode` | - | #### Localization and input config | Prop | Type | Default | | --- | --- | --- | | `language` | `string` | browser language | | `translations` | `Partial` | - | | `keyboardShortcutConfig` | `KeyboardShortcutConfig` | - | | `touchConfig` | `TouchConfig` | - | #### Events | Prop | Type | | --- | --- | | `onPlay` | `() => void` | | `onPause` | `() => void` | | `onEnded` | `() => void` | | `onTimeUpdate` | `(currentTime: number) => void` | | `onVolumeChange` | `(volume: number) => void` | | `onError` | `(error: Error) => void` | | `onLoadedMetadata` | `() => void` | | `onSeeking` | `() => void` | | `onSeeked` | `() => void` | | `onProgress` | `(buffered: number) => void` | | `onDurationChange` | `(duration: number) => void` | | `onRateChange` | `(playbackRate: number) => void` | | `onFullscreenChange` | `(isFullscreen: boolean) => void` | | `onPictureInPictureChange` | `(isPictureInPicture: boolean) => void` | | `onWaiting` | `() => void` | | `onCanPlay` | `() => void` | | `onQualityChange` | `(quality: VideoQuality) => void` | | `onBufferStart` | `() => void` | | `onBufferEnd` | `() => void` | | `onFirstPlay` | `() => void` | ### 10.2 `VideoPlayerHandle` ```ts interface VideoPlayerHandle { video: HTMLVideoElement | null container: HTMLDivElement | null play(): void pause(): void seek(time: number): void setVolume(volume: number): void toggleMute(): void toggleFullscreen(): void togglePictureInPicture(): void setPlaybackRate(rate: number): void } ``` ### 10.3 `PlayerErrorBoundaryProps` ```ts interface PlayerErrorBoundaryProps { children: React.ReactNode fallback?: React.ReactNode | ((error: Error, retry: () => void) => React.ReactNode) onError?: (error: Error, errorInfo: React.ErrorInfo) => void onReset?: () => void resetKeys?: ReadonlyArray } ``` ### 10.4 Core types ```ts type SubtitleTrack = { src: string lang: string label: string default?: boolean } type SubtitleStyle = { fontFamily?: string fontSize?: number | string fontWeight?: number | string color?: string backgroundColor?: string backgroundOpacity?: number } type SubtitleStyleEditorConfig = { enabled?: boolean storageKey?: string } type VideoQuality = { height?: number label: string url?: string width?: number bitrate?: number levelIndex?: number } type AudioTrack = { name: string language: string url: string groupId: string default?: boolean autoselect?: boolean } type PlayerTheme = { primaryColor?: string accentColor?: string backgroundColor?: string textColor?: string fontFamily?: string borderRadius?: number | string overlayOpacity?: number controlsBackground?: string textSecondaryColor?: string textMutedColor?: string } ``` ## 11. Public exports From `src/index.ts`: - Components - `VideoPlayer` - `PlayerErrorBoundary` - Context - `PlayerProvider` - `usePlayerContext` - Hooks - `useKeyboardShortcuts` - `useTouchGestures` - i18n - `getTranslations` - `detectBrowserLanguage` - `translations` - Utilities - `formatTime`, `parseTime` - `parseSRT`, `createSubtitleBlobURL`, `fetchSubtitle` - `validateVideoURL`, `getCORSErrorMessage`, `isCORSError`, `checkVideoCORS` - `initializePolyfills`, `features` - Types - `VideoPlayerProps`, `VideoPlayerHandle`, `SubtitleTrack`, `SubtitleStyle`, `SubtitleStyleEditorConfig` - `SubtitlePosition`, `AudioTrack`, `VideoQuality`, `PlayerTheme` - `KeyboardShortcutConfig`, `TouchConfig` - `VideoState`, `UIState`, `PlayerSettings`, `PlayerContextValue` - `Translations` ## 12. Error handling and reliability ### 12.1 URL and CORS helpers Use exported helpers before rendering: ```ts import { validateVideoURL, checkVideoCORS } from '@source/player' const validation = validateVideoURL(src) if (!validation.valid) { throw new Error(validation.error) } const cors = await checkVideoCORS(src) if (!cors.supported) { console.warn(cors.error) } ``` ### 12.2 Error boundary `VideoPlayer` already wraps content in `PlayerErrorBoundary` with `resetKeys={[src]}`. You can also wrap manually for custom fallback UI: ```tsx import { PlayerErrorBoundary, VideoPlayer } from '@source/player' (

{error.message}

)}>
``` ### 12.3 Stream instance lifecycle For HLS/FLV/MPEG-TS setups: - instance is attached to video element for runtime control - cleanup runs on source/protocol change and unmount - orphan instances are explicitly destroyed if still present ## 13. Browser behavior and known limitations - PIP button renders only when browser supports PIP APIs. - iOS Safari lacks full programmatic volume control. - Direct RTMP URLs usually need HTTP-FLV proxying. - DASH detection exists but playback is not implemented yet. - MPEG-TS behavior depends on MSE support and stream/server conditions. Polyfill and feature utilities: ```ts import { initializePolyfills, features } from '@source/player' initializePolyfills() features.hasNativeHLS() features.hasMSE() features.hasPIP() features.hasFullscreen() features.hasTouch() features.isIOSSafari() features.hasVolumeControl() ``` ## 14. Testing and development ### 14.1 Useful commands ```bash npm install npm run dev npm run lint npm run typecheck npm run test:run npm run build:lib npm run validate:publish ``` ### 14.2 Test coverage areas in repository - Core rendering and props (`VideoPlayer.test.tsx`) - Settings interactions (`SettingsMenu.test.tsx`) - Error boundary reset and fallback behavior - Keyboard and touch hook behavior - Protocol detection and streaming setup utilities - CORS helper validation --- If you maintain this document, update it together with changes to `src/types/index.ts`, `src/index.ts`, and `src/components/VideoPlayer.tsx` to avoid API drift.