Merge pull request #4 from MertUyanik/codex/refactor-code-to-reduce-bundle-size
Reduce debug logging overhead for leaner bundle
This commit is contained in:
@@ -131,12 +131,12 @@ export const VideoElement: React.FC<VideoElementProps> = ({
|
|||||||
|
|
||||||
// Check if it's a CORS-related error
|
// Check if it's a CORS-related error
|
||||||
const videoError = video.error
|
const videoError = video.error
|
||||||
if (videoError.code === MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED ||
|
if (
|
||||||
videoError.code === MediaError.MEDIA_ERR_NETWORK) {
|
videoError.code === MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED ||
|
||||||
|
videoError.code === MediaError.MEDIA_ERR_NETWORK
|
||||||
|
) {
|
||||||
// Could be a CORS issue
|
// Could be a CORS issue
|
||||||
const corsMessage = getCORSErrorMessage(video.src || src)
|
errorMessage = getCORSErrorMessage(video.src || src)
|
||||||
console.error(corsMessage)
|
|
||||||
errorMessage = `Failed to load video. This might be a CORS issue. Check console for details.`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const error = new Error(errorMessage)
|
const error = new Error(errorMessage)
|
||||||
@@ -153,13 +153,11 @@ export const VideoElement: React.FC<VideoElementProps> = ({
|
|||||||
if (timeSinceLastClick < 300) {
|
if (timeSinceLastClick < 300) {
|
||||||
// Double click - toggle fullscreen
|
// Double click - toggle fullscreen
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
console.log('🖱️ [Video Click] Double-click detected, toggling fullscreen')
|
|
||||||
toggleFullscreen()
|
toggleFullscreen()
|
||||||
lastClickTimeRef.current = 0
|
lastClickTimeRef.current = 0
|
||||||
} else {
|
} else {
|
||||||
// Single click - record time
|
// Single click - record time
|
||||||
lastClickTimeRef.current = now
|
lastClickTimeRef.current = now
|
||||||
console.log('🖱️ [Video Click] Single click detected')
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[toggleFullscreen]
|
[toggleFullscreen]
|
||||||
@@ -208,11 +206,6 @@ export const VideoElement: React.FC<VideoElementProps> = ({
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Log warning if external URL
|
|
||||||
if (validation.warning) {
|
|
||||||
console.warn(`⚠️ [Video] ${validation.warning}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
const isHLS = src.includes('.m3u8')
|
const isHLS = src.includes('.m3u8')
|
||||||
let cleanupFn: (() => void) | null = null
|
let cleanupFn: (() => void) | null = null
|
||||||
|
|
||||||
@@ -237,54 +230,41 @@ export const VideoElement: React.FC<VideoElementProps> = ({
|
|||||||
hls.attachMedia(video)
|
hls.attachMedia(video)
|
||||||
|
|
||||||
hls.on(Hls.Events.MANIFEST_PARSED, () => {
|
hls.on(Hls.Events.MANIFEST_PARSED, () => {
|
||||||
console.log('✅ [HLS] Manifest parsed successfully')
|
|
||||||
|
|
||||||
// Extract audio tracks after manifest is parsed
|
// Extract audio tracks after manifest is parsed
|
||||||
// Sometimes audio tracks are not immediately available, so we try with a small delay
|
// Sometimes audio tracks are not immediately available, so we try with a small delay
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
const tracks = getHlsAudioTracks(hls)
|
const tracks = getHlsAudioTracks(hls)
|
||||||
|
|
||||||
if (tracks.length > 0) {
|
if (tracks.length > 0) {
|
||||||
console.log(`✅ [HLS] Found ${tracks.length} audio tracks:`, tracks)
|
|
||||||
setAvailableAudioTracks(tracks)
|
setAvailableAudioTracks(tracks)
|
||||||
onAudioTracksLoaded?.(tracks)
|
onAudioTracksLoaded?.(tracks)
|
||||||
} else {
|
|
||||||
console.log('ℹ️ [HLS] No audio tracks found in manifest')
|
|
||||||
}
|
}
|
||||||
}, 100)
|
}, 100)
|
||||||
|
|
||||||
if (autoplay) {
|
if (autoplay) {
|
||||||
video.play().catch((err) => {
|
void video.play().catch(() => undefined)
|
||||||
console.warn('⚠️ [HLS] Autoplay prevented:', err)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// Also listen to audio track updates
|
// Also listen to audio track updates
|
||||||
hls.on(Hls.Events.AUDIO_TRACKS_UPDATED, (_event: any, data: any) => {
|
hls.on(Hls.Events.AUDIO_TRACKS_UPDATED, () => {
|
||||||
console.log('🔄 [HLS] Audio tracks updated:', data)
|
|
||||||
const tracks = getHlsAudioTracks(hls)
|
const tracks = getHlsAudioTracks(hls)
|
||||||
if (tracks.length > 0) {
|
if (tracks.length > 0) {
|
||||||
console.log(`✅ [HLS] Found ${tracks.length} audio tracks after update:`, tracks)
|
|
||||||
setAvailableAudioTracks(tracks)
|
setAvailableAudioTracks(tracks)
|
||||||
onAudioTracksLoaded?.(tracks)
|
onAudioTracksLoaded?.(tracks)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
hls.on(Hls.Events.ERROR, (_event: any, data: any) => {
|
hls.on(Hls.Events.ERROR, (_event: any, data: any) => {
|
||||||
console.error('❌ [HLS] Error:', data)
|
|
||||||
if (data.fatal) {
|
if (data.fatal) {
|
||||||
switch (data.type) {
|
switch (data.type) {
|
||||||
case Hls.ErrorTypes.NETWORK_ERROR:
|
case Hls.ErrorTypes.NETWORK_ERROR:
|
||||||
console.error('❌ [HLS] Fatal network error, trying to recover...')
|
|
||||||
hls.startLoad()
|
hls.startLoad()
|
||||||
break
|
break
|
||||||
case Hls.ErrorTypes.MEDIA_ERROR:
|
case Hls.ErrorTypes.MEDIA_ERROR:
|
||||||
console.error('❌ [HLS] Fatal media error, trying to recover...')
|
|
||||||
hls.recoverMediaError()
|
hls.recoverMediaError()
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
console.error('❌ [HLS] Fatal error, cannot recover')
|
|
||||||
handleError()
|
handleError()
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -296,19 +276,16 @@ export const VideoElement: React.FC<VideoElementProps> = ({
|
|||||||
|
|
||||||
// Setup cleanup function
|
// Setup cleanup function
|
||||||
cleanupFn = () => {
|
cleanupFn = () => {
|
||||||
console.log('🧹 [HLS] Cleaning up HLS instance...')
|
|
||||||
if (hls) {
|
if (hls) {
|
||||||
hls.destroy()
|
hls.destroy()
|
||||||
}
|
}
|
||||||
delete (video as any).__hlsInstance
|
delete (video as any).__hlsInstance
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('❌ [HLS] Failed to load or initialize hls.js:', err)
|
|
||||||
let error: Error
|
let error: Error
|
||||||
if (err instanceof Error && isCORSError(err)) {
|
if (err instanceof Error && isCORSError(err)) {
|
||||||
const corsMessage = getCORSErrorMessage(src)
|
const corsMessage = getCORSErrorMessage(src)
|
||||||
console.error(corsMessage)
|
error = new Error(corsMessage)
|
||||||
error = new Error('Failed to load HLS stream. This might be a CORS issue. Check console for details.')
|
|
||||||
} else {
|
} else {
|
||||||
error = err instanceof Error ? err : new Error('Failed to load HLS')
|
error = err instanceof Error ? err : new Error('Failed to load HLS')
|
||||||
}
|
}
|
||||||
@@ -323,9 +300,7 @@ export const VideoElement: React.FC<VideoElementProps> = ({
|
|||||||
// Native support or regular video
|
// Native support or regular video
|
||||||
video.src = src
|
video.src = src
|
||||||
if (autoplay) {
|
if (autoplay) {
|
||||||
video.play().catch((err) => {
|
void video.play().catch(() => undefined)
|
||||||
console.warn('⚠️ [Video] Autoplay prevented:', err)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -363,7 +338,6 @@ export const VideoElement: React.FC<VideoElementProps> = ({
|
|||||||
|
|
||||||
if (trackIndex !== -1) {
|
if (trackIndex !== -1) {
|
||||||
setHlsAudioTrack(hlsInstance, trackIndex)
|
setHlsAudioTrack(hlsInstance, trackIndex)
|
||||||
console.log(`✅ [Video] Audio track changed to: ${settings.audioTrack.name}`)
|
|
||||||
}
|
}
|
||||||
}, [settings.audioTrack, availableAudioTracks, videoRef])
|
}, [settings.audioTrack, availableAudioTracks, videoRef])
|
||||||
|
|
||||||
|
|||||||
@@ -126,7 +126,9 @@ export const PlayerProvider: React.FC<PlayerProviderProps> = ({
|
|||||||
try {
|
try {
|
||||||
await videoRef.current?.requestPictureInPicture()
|
await videoRef.current?.requestPictureInPicture()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('PIP error:', error)
|
if (error instanceof Error) {
|
||||||
|
setVideoState((prev) => ({ ...prev, error }))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
await document.exitPictureInPicture()
|
await document.exitPictureInPicture()
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ export interface CORSCheckResult {
|
|||||||
supported: boolean
|
supported: boolean
|
||||||
error?: string
|
error?: string
|
||||||
needsProxy: boolean
|
needsProxy: boolean
|
||||||
|
supportsRange?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -21,6 +22,7 @@ export const checkVideoCORS = async (url: string): Promise<CORSCheckResult> => {
|
|||||||
|
|
||||||
const corsHeader = response.headers.get('Access-Control-Allow-Origin')
|
const corsHeader = response.headers.get('Access-Control-Allow-Origin')
|
||||||
const rangeHeader = response.headers.get('Accept-Ranges')
|
const rangeHeader = response.headers.get('Accept-Ranges')
|
||||||
|
const supportsRange = !!rangeHeader && rangeHeader !== 'none'
|
||||||
|
|
||||||
if (!corsHeader && !response.ok) {
|
if (!corsHeader && !response.ok) {
|
||||||
return {
|
return {
|
||||||
@@ -30,13 +32,10 @@ export const checkVideoCORS = async (url: string): Promise<CORSCheckResult> => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!rangeHeader || rangeHeader === 'none') {
|
|
||||||
console.warn('⚠️ [CORS] Server does not support Range Requests. Seeking may not work properly.')
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
supported: true,
|
supported: true,
|
||||||
needsProxy: false,
|
needsProxy: false,
|
||||||
|
supportsRange,
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// CORS error or network error
|
// CORS error or network error
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ const loadHlsFromCDN = (): Promise<any> => {
|
|||||||
|
|
||||||
script.onload = () => {
|
script.onload = () => {
|
||||||
if (typeof (window as any).Hls !== 'undefined') {
|
if (typeof (window as any).Hls !== 'undefined') {
|
||||||
console.log('✅ [HLS Loader] Loaded hls.js from CDN')
|
|
||||||
resolve((window as any).Hls)
|
resolve((window as any).Hls)
|
||||||
} else {
|
} else {
|
||||||
reject(new Error('HLS.js CDN loaded but Hls global not found'))
|
reject(new Error('HLS.js CDN loaded but Hls global not found'))
|
||||||
@@ -45,19 +44,14 @@ const loadHlsFromCDN = (): Promise<any> => {
|
|||||||
export const loadHls = async (): Promise<any> => {
|
export const loadHls = async (): Promise<any> => {
|
||||||
try {
|
try {
|
||||||
// Try loading from npm package first
|
// Try loading from npm package first
|
||||||
console.log('🔄 [HLS Loader] Attempting to load hls.js from npm package...')
|
|
||||||
const hlsModule = await import('hls.js')
|
const hlsModule = await import('hls.js')
|
||||||
console.log('✅ [HLS Loader] Loaded hls.js from npm package')
|
|
||||||
return hlsModule.default
|
return hlsModule.default
|
||||||
} catch (npmError) {
|
} catch (npmError) {
|
||||||
console.warn('⚠️ [HLS Loader] Failed to load hls.js from npm, trying CDN fallback...', npmError)
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Fallback to CDN
|
// Fallback to CDN
|
||||||
const Hls = await loadHlsFromCDN()
|
const Hls = await loadHlsFromCDN()
|
||||||
return Hls
|
return Hls
|
||||||
} catch (cdnError) {
|
} catch (cdnError) {
|
||||||
console.error('❌ [HLS Loader] Failed to load hls.js from both npm and CDN', cdnError)
|
|
||||||
throw new Error('Unable to load HLS.js library. HLS streaming is not available.')
|
throw new Error('Unable to load HLS.js library. HLS streaming is not available.')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -84,18 +78,14 @@ export const hasNativeHlsSupport = (): boolean => {
|
|||||||
export const getHlsAudioTracks = (hls: any): AudioTrack[] => {
|
export const getHlsAudioTracks = (hls: any): AudioTrack[] => {
|
||||||
try {
|
try {
|
||||||
if (!hls) {
|
if (!hls) {
|
||||||
console.warn('⚠️ [HLS Loader] HLS instance is null or undefined')
|
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if audioTracks property exists
|
// Check if audioTracks property exists
|
||||||
if (!hls.audioTracks || !Array.isArray(hls.audioTracks)) {
|
if (!hls.audioTracks || !Array.isArray(hls.audioTracks)) {
|
||||||
console.warn('⚠️ [HLS Loader] audioTracks not available or not an array:', hls.audioTracks)
|
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('🔍 [HLS Loader] Raw audio tracks from HLS:', hls.audioTracks)
|
|
||||||
|
|
||||||
const audioTracks: AudioTrack[] = hls.audioTracks.map((track: any, index: number) => {
|
const audioTracks: AudioTrack[] = hls.audioTracks.map((track: any, index: number) => {
|
||||||
const audioTrack = {
|
const audioTrack = {
|
||||||
name: track.name || track.label || `Audio ${index + 1}`,
|
name: track.name || track.label || `Audio ${index + 1}`,
|
||||||
@@ -105,13 +95,11 @@ export const getHlsAudioTracks = (hls: any): AudioTrack[] => {
|
|||||||
default: track.default || false,
|
default: track.default || false,
|
||||||
autoselect: track.autoselect || false,
|
autoselect: track.autoselect || false,
|
||||||
}
|
}
|
||||||
console.log(`🎵 [HLS Loader] Parsed audio track ${index}:`, audioTrack)
|
|
||||||
return audioTrack
|
return audioTrack
|
||||||
})
|
})
|
||||||
|
|
||||||
return audioTracks
|
return audioTracks
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('❌ [HLS Loader] Error extracting audio tracks:', error)
|
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -120,20 +108,13 @@ export const getHlsAudioTracks = (hls: any): AudioTrack[] => {
|
|||||||
* Set active audio track in HLS instance
|
* Set active audio track in HLS instance
|
||||||
*/
|
*/
|
||||||
export const setHlsAudioTrack = (hls: any, audioTrackIndex: number): void => {
|
export const setHlsAudioTrack = (hls: any, audioTrackIndex: number): void => {
|
||||||
try {
|
|
||||||
if (!hls || !hls.audioTracks) {
|
if (!hls || !hls.audioTracks) {
|
||||||
console.warn('⚠️ [HLS Loader] HLS instance or audioTracks not available')
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (audioTrackIndex < 0 || audioTrackIndex >= hls.audioTracks.length) {
|
if (audioTrackIndex < 0 || audioTrackIndex >= hls.audioTracks.length) {
|
||||||
console.warn('⚠️ [HLS Loader] Invalid audio track index:', audioTrackIndex)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
hls.audioTrack = audioTrackIndex
|
hls.audioTrack = audioTrackIndex
|
||||||
console.log(`✅ [HLS Loader] Audio track set to index ${audioTrackIndex}`)
|
|
||||||
} catch (error) {
|
|
||||||
console.error('❌ [HLS Loader] Error setting audio track:', error)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,7 +57,6 @@ const parseAudioMediaTag = (line: string): AudioTrack | null => {
|
|||||||
const autoselect = attributes['AUTOSELECT'] === 'YES'
|
const autoselect = attributes['AUTOSELECT'] === 'YES'
|
||||||
|
|
||||||
if (!name || !uri) {
|
if (!name || !uri) {
|
||||||
console.warn('⚠️ [M3U8 Parser] Audio track missing NAME or URI:', line)
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,8 +68,7 @@ const parseAudioMediaTag = (line: string): AudioTrack | null => {
|
|||||||
default: defaultTrack,
|
default: defaultTrack,
|
||||||
autoselect,
|
autoselect,
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch {
|
||||||
console.error('❌ [M3U8 Parser] Error parsing audio track:', line, error)
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -87,8 +85,7 @@ export const fetchAndParseM3U8 = async (url: string): Promise<AudioTrack[]> => {
|
|||||||
|
|
||||||
const manifestContent = await response.text()
|
const manifestContent = await response.text()
|
||||||
return parseM3U8AudioTracks(manifestContent)
|
return parseM3U8AudioTracks(manifestContent)
|
||||||
} catch (error) {
|
} catch {
|
||||||
console.error('❌ [M3U8 Parser] Error fetching M3U8:', error)
|
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+16
-8
@@ -87,29 +87,37 @@ export const checkFetchSupport = (): boolean => {
|
|||||||
* Initialize all polyfills
|
* Initialize all polyfills
|
||||||
* Call this once when the app loads
|
* Call this once when the app loads
|
||||||
*/
|
*/
|
||||||
export const initializePolyfills = () => {
|
export interface PolyfillDiagnostics {
|
||||||
|
warnings: string[]
|
||||||
|
errors: string[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export const initializePolyfills = (): PolyfillDiagnostics => {
|
||||||
|
const warnings: string[] = []
|
||||||
|
const errors: string[] = []
|
||||||
|
|
||||||
try {
|
try {
|
||||||
setupFullscreenPolyfill()
|
setupFullscreenPolyfill()
|
||||||
setupPIPPolyfill()
|
setupPIPPolyfill()
|
||||||
|
|
||||||
// Check critical API support
|
// Check critical API support
|
||||||
if (!checkPromiseSupport()) {
|
if (!checkPromiseSupport()) {
|
||||||
console.warn('[VideoPlayer] Promise not supported. Please add Promise polyfill.')
|
warnings.push('Promise not supported. Please add a Promise polyfill for full compatibility.')
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!checkFetchSupport()) {
|
if (!checkFetchSupport()) {
|
||||||
console.warn('[VideoPlayer] Fetch API not supported. Subtitle loading may fail.')
|
warnings.push('Fetch API not supported. Subtitle loading may fail without a fetch polyfill.')
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for MediaSource API (required for HLS.js)
|
// Check for MediaSource API (required for HLS.js)
|
||||||
if (typeof MediaSource === 'undefined') {
|
if (typeof MediaSource === 'undefined') {
|
||||||
console.warn('[VideoPlayer] MediaSource API not supported. HLS streaming will not work.')
|
warnings.push('MediaSource API not supported. HLS streaming will not work on this browser.')
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
errors.push(error instanceof Error ? error.message : String(error))
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('✅ [VideoPlayer] Polyfills initialized successfully')
|
return { warnings, errors }
|
||||||
} catch (error) {
|
|
||||||
console.error('[VideoPlayer] Error initializing polyfills:', error)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -45,7 +45,6 @@ export const createSubtitleBlobURL = (content: string, format: 'vtt' | 'srt'): s
|
|||||||
* Fetch and parse subtitle file
|
* Fetch and parse subtitle file
|
||||||
*/
|
*/
|
||||||
export const fetchSubtitle = async (url: string): Promise<string> => {
|
export const fetchSubtitle = async (url: string): Promise<string> => {
|
||||||
try {
|
|
||||||
const response = await fetch(url)
|
const response = await fetch(url)
|
||||||
const content = await response.text()
|
const content = await response.text()
|
||||||
|
|
||||||
@@ -55,8 +54,4 @@ export const fetchSubtitle = async (url: string): Promise<string> => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return content
|
return content
|
||||||
} catch (error) {
|
|
||||||
console.error('Failed to fetch subtitle:', error)
|
|
||||||
throw error
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user