Initial commit: modern React video player library
Add all source files for a feature-rich, reusable video player built with React, TypeScript, and Vite. Includes core components, context, hooks, utilities, styles, demo app, and configuration files.
This commit is contained in:
@@ -0,0 +1,139 @@
|
||||
/**
|
||||
* HLS.js dynamic loader with CDN fallback
|
||||
* Handles loading hls.js from npm or CDN
|
||||
*/
|
||||
|
||||
import type { AudioTrack } from '../types'
|
||||
|
||||
const HLS_CDN_URL = 'https://cdn.jsdelivr.net/npm/hls.js@1.5.13/dist/hls.min.js'
|
||||
|
||||
/**
|
||||
* Load hls.js from CDN as fallback
|
||||
*/
|
||||
const loadHlsFromCDN = (): Promise<any> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
// Check if already loaded globally
|
||||
if (typeof (window as any).Hls !== 'undefined') {
|
||||
resolve((window as any).Hls)
|
||||
return
|
||||
}
|
||||
|
||||
const script = document.createElement('script')
|
||||
script.src = HLS_CDN_URL
|
||||
script.async = true
|
||||
|
||||
script.onload = () => {
|
||||
if (typeof (window as any).Hls !== 'undefined') {
|
||||
console.log('✅ [HLS Loader] Loaded hls.js from CDN')
|
||||
resolve((window as any).Hls)
|
||||
} else {
|
||||
reject(new Error('HLS.js CDN loaded but Hls global not found'))
|
||||
}
|
||||
}
|
||||
|
||||
script.onerror = () => {
|
||||
reject(new Error('Failed to load hls.js from CDN'))
|
||||
}
|
||||
|
||||
document.head.appendChild(script)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Load hls.js with npm fallback to CDN
|
||||
*/
|
||||
export const loadHls = async (): Promise<any> => {
|
||||
try {
|
||||
// Try loading from npm package first
|
||||
console.log('🔄 [HLS Loader] Attempting to load hls.js from npm package...')
|
||||
const hlsModule = await import('hls.js')
|
||||
console.log('✅ [HLS Loader] Loaded hls.js from npm package')
|
||||
return hlsModule.default
|
||||
} catch (npmError) {
|
||||
console.warn('⚠️ [HLS Loader] Failed to load hls.js from npm, trying CDN fallback...', npmError)
|
||||
|
||||
try {
|
||||
// Fallback to CDN
|
||||
const Hls = await loadHlsFromCDN()
|
||||
return Hls
|
||||
} catch (cdnError) {
|
||||
console.error('❌ [HLS Loader] Failed to load hls.js from both npm and CDN')
|
||||
throw new Error('Unable to load HLS.js library. HLS streaming is not available.')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if HLS.js is supported in current browser
|
||||
*/
|
||||
export const isHlsSupported = (Hls: any): boolean => {
|
||||
return Hls && typeof Hls.isSupported === 'function' && Hls.isSupported()
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if browser has native HLS support (Safari)
|
||||
*/
|
||||
export const hasNativeHlsSupport = (): boolean => {
|
||||
const video = document.createElement('video')
|
||||
return video.canPlayType('application/vnd.apple.mpegurl') !== ''
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract audio tracks from HLS instance
|
||||
*/
|
||||
export const getHlsAudioTracks = (hls: any): AudioTrack[] => {
|
||||
try {
|
||||
if (!hls) {
|
||||
console.warn('⚠️ [HLS Loader] HLS instance is null or undefined')
|
||||
return []
|
||||
}
|
||||
|
||||
// Check if audioTracks property exists
|
||||
if (!hls.audioTracks || !Array.isArray(hls.audioTracks)) {
|
||||
console.warn('⚠️ [HLS Loader] audioTracks not available or not an array:', hls.audioTracks)
|
||||
return []
|
||||
}
|
||||
|
||||
console.log('🔍 [HLS Loader] Raw audio tracks from HLS:', hls.audioTracks)
|
||||
|
||||
const audioTracks: AudioTrack[] = hls.audioTracks.map((track: any, index: number) => {
|
||||
const audioTrack = {
|
||||
name: track.name || track.label || `Audio ${index + 1}`,
|
||||
language: track.lang || track.language || 'unknown',
|
||||
url: track.url || '',
|
||||
groupId: track.groupId || 'audio',
|
||||
default: track.default || false,
|
||||
autoselect: track.autoselect || false,
|
||||
}
|
||||
console.log(`🎵 [HLS Loader] Parsed audio track ${index}:`, audioTrack)
|
||||
return audioTrack
|
||||
})
|
||||
|
||||
return audioTracks
|
||||
} catch (error) {
|
||||
console.error('❌ [HLS Loader] Error extracting audio tracks:', error)
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set active audio track in HLS instance
|
||||
*/
|
||||
export const setHlsAudioTrack = (hls: any, audioTrackIndex: number): void => {
|
||||
try {
|
||||
if (!hls || !hls.audioTracks) {
|
||||
console.warn('⚠️ [HLS Loader] HLS instance or audioTracks not available')
|
||||
return
|
||||
}
|
||||
|
||||
if (audioTrackIndex < 0 || audioTrackIndex >= hls.audioTracks.length) {
|
||||
console.warn('⚠️ [HLS Loader] Invalid audio track index:', audioTrackIndex)
|
||||
return
|
||||
}
|
||||
|
||||
hls.audioTrack = audioTrackIndex
|
||||
console.log(`✅ [HLS Loader] Audio track set to index ${audioTrackIndex}`)
|
||||
} catch (error) {
|
||||
console.error('❌ [HLS Loader] Error setting audio track:', error)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user