UNPKG

unified-video-framework

Version:

Cross-platform video player framework supporting iOS, Android, Web, Smart TVs (Samsung/LG), Roku, and more

434 lines (386 loc) 14.8 kB
/** * Core video player interface that all platform implementations must follow */ export interface FallbackSource { url: string; type?: 'mp4' | 'hls' | 'dash' | 'webm' | 'auto'; priority?: number; // Lower number = higher priority (default: index order) } export interface VideoSource { url: string; type?: 'mp4' | 'hls' | 'dash' | 'webm' | 'auto'; drm?: DRMConfig; subtitles?: SubtitleTrack[]; metadata?: VideoMetadata; // Fallback configuration fallbackSources?: FallbackSource[]; // Alternative video URLs to try on failure fallbackPoster?: string; // Static image to show when all video sources fail fallbackShowErrorMessage?: boolean; // Show error message overlay on fallback poster (default: true) fallbackRetryDelay?: number; // Delay in ms before trying next fallback (default: 1000) fallbackRetryAttempts?: number; // Number of retry attempts per source (default: 1) onAllSourcesFailed?: (errors: Array<{ url: string; error: any }>) => void; // Callback when all sources fail } export interface DRMConfig { licenseUrl: string; certificateUrl?: string; headers?: Record<string, string>; type: 'widevine' | 'playready' | 'fairplay' | 'clearkey'; } export interface SubtitleTrack { url: string; language: string; label: string; kind: 'subtitles' | 'captions' | 'descriptions'; default?: boolean; } export interface VideoMetadata { id?: string; videoId?: string; title?: string; description?: string; duration?: number; thumbnailUrl?: string; posterUrl?: string; } export interface Quality { height: number; width: number; bitrate: number; label: string; index: number; } export interface PlayerState { isPlaying: boolean; isPaused: boolean; isBuffering: boolean; isEnded: boolean; isError: boolean; currentTime: number; duration: number; bufferedPercentage: number; volume: number; isMuted: boolean; playbackRate: number; currentQuality?: Quality; availableQualities: Quality[]; } export interface PlayerEvents { onReady?: () => void; onPlay?: () => void; onPause?: () => void; onEnded?: () => void; onTimeUpdate?: (time: number) => void; onBuffering?: (isBuffering: boolean) => void; onError?: (error: PlayerError) => void; onQualityChanged?: (quality: Quality) => void; onVolumeChanged?: (volume: number) => void; onFullscreenChanged?: (isFullscreen: boolean) => void; onProgress?: (buffered: number) => void; onSeeking?: () => void; onSeeked?: () => void; onLoadedMetadata?: (metadata: VideoMetadata) => void; // Fired exactly once when free preview duration is reached and playback is blocked onFreePreviewEnded?: () => void; // EPG (Electronic Program Guide) events epgToggle?: (data?: any) => void; epgDataSet?: (data?: any) => void; // Framework branding events frameworkBrandingClick?: (data: { timestamp: number; url: string; userAgent: string }) => void; // Chapter events chapterchange?: (chapter: any) => void; segmententered?: (segment: any) => void; segmentexited?: (segment: any) => void; segmentskipped?: (segment: any) => void; chapterSegmentEntered?: (data: any) => void; chapterSegmentSkipped?: (data: any) => void; chapterSkipButtonShown?: (data: any) => void; chapterSkipButtonHidden?: (data: any) => void; chaptersLoaded?: (data: any) => void; chaptersLoadError?: (data: any) => void; // Navigation events navigationBackClicked?: () => void; navigationCloseClicked?: () => void; // Live stream waiting events onLiveStreamWaiting?: () => void; onLiveStreamReady?: () => void; // Bandwidth detection events onBandwidthDetected?: (data: { bandwidth: number | null; tier: string; method: string }) => void; onBandwidthTierChanged?: (data: { tier: string; bandwidth: number | null }) => void; } export interface PlayerError { code: string; message: string; type: 'network' | 'media' | 'drm' | 'unknown'; fatal: boolean; details?: any; } export interface PaywallConfig { enabled: boolean; apiBase: string; // e.g., http://localhost:3100 userId: string; videoId: string; gateways: Array<'stripe' | 'cashfree'>; branding?: { title?: string; description?: string; logoUrl?: string; theme?: any }; popup?: { width?: number; height?: number }; // Email OTP Authentication (optional - if not provided, assumes user is already authenticated) emailAuth?: { enabled: boolean; // Enable email authentication flow skipIfAuthenticated?: boolean; // Skip email auth if user already has valid session (default: true) sessionStorage?: { tokenKey?: string; // Key for storing session token (default: 'uvf_session_token') refreshTokenKey?: string; // Key for storing refresh token (default: 'uvf_refresh_token') userIdKey?: string; // Key for storing user ID (default: 'uvf_user_id') }; api?: { requestOtp: string; // POST /auth/request-otp endpoint verifyOtp: string; // POST /auth/verify-otp endpoint refreshToken?: string; // POST /auth/refresh-token endpoint logout?: string; // POST /auth/logout endpoint }; ui?: { title?: string; // Modal title (default: "Sign in to continue") description?: string; // Modal description emailPlaceholder?: string; // Email input placeholder otpPlaceholder?: string; // OTP input placeholder submitButtonText?: string; // Submit button text resendButtonText?: string; // Resend OTP button text resendCooldown?: number; // Resend cooldown in seconds (default: 30) }; validation?: { otpLength?: number; // Expected OTP length (default: 6) otpTimeout?: number; // OTP validity timeout in seconds (default: 300) rateLimiting?: { maxAttempts?: number; // Max OTP requests per hour (default: 5) windowMinutes?: number; // Rate limiting window (default: 60) }; }; }; } export interface ShareConfig { enabled?: boolean; url?: string; title?: string; text?: string; generateUrl?: (videoData: { videoId?: string; metadata?: any }) => string; } /** * Configuration for individual player control visibility * All controls default to true (visible) unless explicitly set to false */ export interface ControlsVisibilityConfig { /** Playback control buttons */ playback?: { /** Large center play button overlay (default: true) */ centerPlayButton?: boolean; /** Bottom bar play/pause button (default: true) */ playPauseButton?: boolean; /** Skip forward/backward 10s buttons (default: true) */ skipButtons?: boolean; /** Previous track button (default: auto based on playlist config) */ previousButton?: boolean; /** Next track button (default: auto based on playlist config) */ nextButton?: boolean; }; /** Audio control buttons */ audio?: { /** Volume mute/unmute button (default: true) */ volumeButton?: boolean; /** Volume slider panel (default: true) */ volumeSlider?: boolean; }; /** Progress and time controls */ progress?: { /** Video seekbar/progress bar (default: true) */ progressBar?: boolean; /** Current time / duration display (default: true) */ timeDisplay?: boolean; }; /** Quality and settings controls */ quality?: { /** Quality badge indicator (HD, 4K, etc.) (default: true) */ badge?: boolean; /** Settings menu button (default: true) */ settingsButton?: boolean; }; /** Display mode controls */ display?: { /** Fullscreen toggle button (default: true) */ fullscreenButton?: boolean; /** Picture-in-picture button (default: true on supported browsers) */ pipButton?: boolean; }; /** Advanced feature controls */ features?: { /** Electronic Program Guide button (default: true when EPG data available) */ epgButton?: boolean; /** Playlist panel toggle button (default: true when callback provided) */ playlistButton?: boolean; /** Cast to TV button (default: true) */ castButton?: boolean; /** Share video button (default: true) */ shareButton?: boolean; // Note: Stop casting button is automatically shown/hidden based on active casting state }; /** Navigation and branding chrome */ chrome?: { /** Back/close navigation buttons (default: auto based on navigation config) */ navigationButtons?: boolean; /** Framework watermark/branding (default: true) */ frameworkBranding?: boolean; }; } export interface PlayerConfig { autoPlay?: boolean; muted?: boolean; volume?: number; controls?: boolean; loop?: boolean; preload?: 'none' | 'metadata' | 'auto'; crossOrigin?: 'anonymous' | 'use-credentials'; playsInline?: boolean; defaultQuality?: number; enableAdaptiveBitrate?: boolean; debug?: boolean; // Playback start time startTime?: number; // Start playback from this time in seconds (e.g., 125 for 2:05) // Free preview freeDuration?: number; // seconds of free playback before paywall // Live stream indicator isLive?: boolean; // Force LIVE indicator display (for live streams) // Live stream waiting configuration liveWaitingMessages?: { waitingForStream?: string; // Default: "Waiting for Stream" loading?: string; // Default: "Loading" comingBack?: string; // Default: "Coming back" }; liveMessageRotationInterval?: number; // Default: 2500ms (2.5s per message) liveRetryInterval?: number; // Default: 5000ms (stream retry interval) liveMaxRetryAttempts?: number; // Default: undefined (infinite) // Live stream countdown configuration - shows timer when program hasn't started liveCountdown?: { /** UTC timestamp (milliseconds) when next program starts - will be converted to local time */ nextProgramStartTime?: number; /** Message shown above countdown (default: "There is no active program in this channel.") */ noProgramMessage?: string; /** Countdown prefix message (default: "Next program will start in:") */ countdownMessage?: string; /** Color for the countdown timer text (default: uses theme accent color or #00d4ff) */ timerColor?: string; /** Callback when countdown reaches zero */ onCountdownComplete?: () => void; /** Auto-reload stream when timer ends (default: true) */ autoReloadOnComplete?: boolean; /** Update interval in milliseconds (default: 1000) */ updateInterval?: number; }; // Optional paywall for dynamic rental flow paywall?: PaywallConfig; // Share configuration share?: ShareConfig; // Adaptive bitrate starting configuration adaptiveBitrate?: { /** Starting bitrate in bps (e.g. 2_000_000 for 2 Mbps) */ startBitrate?: number; }; // Playlist navigation controls playlist?: { showPrevNext?: boolean; // show ⏮/⏭ standalone buttons (deprecated — use replaceSkipWithPrevNext) onPreviousTrack?: () => void; // fired when previous track is triggered onNextTrack?: () => void; // fired when next track is triggered replaceSkipWithPrevNext?: boolean; // repurpose skip-back/forward buttons as prev/next track onPlaylistToggle?: () => void; // fired when playlist icon button in controls is clicked }; // Fine-grained control over individual player control visibility /** * Fine-grained control over individual player control visibility * All controls default to true unless explicitly set to false */ controlsVisibility?: ControlsVisibilityConfig; // Settings menu configuration /** * Settings menu configuration * @deprecated Use controlsVisibility.quality.settingsButton to hide/show the settings button */ settings?: { enabled?: boolean; // Show/hide settings button (deprecated - use controlsVisibility.quality.settingsButton) speed?: boolean; // Enable playback speed control quality?: boolean; // Enable quality selection subtitles?: boolean; // Enable subtitle selection }; // Navigation configuration (back/close buttons) /** * Navigation button configuration (back/close buttons in top bar) */ navigation?: { onBack?: () => void; // Callback when back button is clicked onClose?: () => void; // Callback when close button is clicked }; // Framework branding display /** * Show/hide framework branding watermark * @deprecated Use controlsVisibility.chrome.frameworkBranding instead */ showFrameworkBranding?: boolean; // Custom element to use for requestFullscreen() — defaults to the internal player wrapper fullscreenElement?: HTMLElement; } /** * Main video player interface */ export interface IVideoPlayer { // Lifecycle methods initialize(container: HTMLElement | string, config?: PlayerConfig): Promise<void>; destroy(): Promise<void>; // Media control load(source: VideoSource): Promise<void>; play(): Promise<void>; pause(): void; stop(): void; seek(time: number): void; // Volume control setVolume(level: number): void; mute(): void; unmute(): void; toggleMute(): void; // Quality control getQualities(): Quality[]; getCurrentQuality(): Quality | null; setQuality(index: number): void; setAutoQuality(enabled: boolean): void; // Playback control setPlaybackRate(rate: number): void; getPlaybackRate(): number; // State queries getCurrentTime(): number; getDuration(): number; getBufferedPercentage(): number; getState(): PlayerState; isPlaying(): boolean; isPaused(): boolean; isEnded(): boolean; // Display control enterFullscreen(): Promise<void>; exitFullscreen(): Promise<void>; toggleFullscreen(): Promise<void>; enterPictureInPicture(): Promise<void>; exitPictureInPicture(): Promise<void>; // Event handling on(event: string, handler: Function): void; off(event: string, handler?: Function): void; once(event: string, handler: Function): void; // Subtitle control getSubtitles(): SubtitleTrack[]; setSubtitleTrack(index: number): void; disableSubtitles(): void; // Advanced features setAudioTrack?(index: number): void; getAudioTracks?(): any[]; getThumbnail?(time: number): string; getStats?(): any; // Free preview runtime controls (optional) setFreeDuration?(seconds: number): void; resetFreePreviewGate?(): void; // Bandwidth detection controls (optional) getBandwidthInfo?(): { estimated: number | null; tier: string | null; method: string | null }; redetectBandwidth?(): Promise<void>; }