feat: apply phase1 DX cleanup for private registry

This commit is contained in:
hibna
2026-02-12 18:27:50 +03:00
parent 8a32c5c1b3
commit fcd2a14a05
21 changed files with 281 additions and 279 deletions
+17 -15
View File
@@ -5,6 +5,7 @@
import type { AudioTrack, VideoQuality, SubtitleTrack } from '../types'
import { getTranslations, detectBrowserLanguage } from '../i18n'
import { logger } from './logger'
// Re-export control functions for backward compatibility
export { setHlsQualityLevel, setHlsAudioTrack } from './hlsControl'
@@ -47,20 +48,20 @@ const loadHlsFromCDN = (): Promise<any> => {
*/
export const loadHls = async (): Promise<any> => {
try {
console.log('[HLS Loader] Attempting to load from npm package...')
logger.log('[HLS Loader] Attempting to load from npm package...')
// Try loading from npm package first
const hlsModule = await import('hls.js')
console.log('[HLS Loader] Successfully loaded from npm package')
logger.log('[HLS Loader] Successfully loaded from npm package')
return hlsModule.default
} catch (npmError) {
console.warn('[HLS Loader] Failed to load from npm, trying CDN...', npmError)
logger.warn('[HLS Loader] Failed to load from npm, trying CDN...', npmError)
try {
// Fallback to CDN
const Hls = await loadHlsFromCDN()
console.log('[HLS Loader] Successfully loaded from CDN')
logger.log('[HLS Loader] Successfully loaded from CDN')
return Hls
} catch (cdnError) {
console.error('[HLS Loader] Failed to load from CDN:', cdnError)
logger.error('[HLS Loader] Failed to load from CDN:', cdnError)
throw new Error('Unable to load HLS.js library. HLS streaming is not available.')
}
}
@@ -87,17 +88,17 @@ export const hasNativeHlsSupport = (): boolean => {
export const getHlsAudioTracks = (hls: any): AudioTrack[] => {
try {
if (!hls) {
console.warn('[HLS Loader] getHlsAudioTracks: No HLS instance provided')
logger.warn('[HLS Loader] getHlsAudioTracks: No HLS instance provided')
return []
}
// Check if audioTracks property exists
if (!hls.audioTracks || !Array.isArray(hls.audioTracks)) {
console.warn('[HLS Loader] getHlsAudioTracks: No audioTracks array found on HLS instance')
logger.warn('[HLS Loader] getHlsAudioTracks: No audioTracks array found on HLS instance')
return []
}
console.log('[HLS Loader] getHlsAudioTracks: Raw audioTracks from HLS:', hls.audioTracks)
logger.log('[HLS Loader] getHlsAudioTracks: Raw audioTracks from HLS:', hls.audioTracks)
const audioTracks: AudioTrack[] = hls.audioTracks.map((track: any, index: number) => {
const audioTrack = {
@@ -111,10 +112,10 @@ export const getHlsAudioTracks = (hls: any): AudioTrack[] => {
return audioTrack
})
console.log('[HLS Loader] getHlsAudioTracks: Processed tracks:', audioTracks)
logger.log('[HLS Loader] getHlsAudioTracks: Processed tracks:', audioTracks)
return audioTracks
} catch (error) {
console.error('[HLS Loader] getHlsAudioTracks: Error extracting audio tracks:', error)
logger.error('[HLS Loader] getHlsAudioTracks: Error extracting audio tracks:', error)
return []
}
}
@@ -154,16 +155,16 @@ export const getHlsSubtitleTracks = (hls: any): SubtitleTrack[] => {
export const getHlsQualities = (hls: any): VideoQuality[] => {
try {
if (!hls) {
console.warn('[HLS Loader] getHlsQualities: No HLS instance provided')
logger.warn('[HLS Loader] getHlsQualities: No HLS instance provided')
return []
}
if (!Array.isArray(hls.levels)) {
console.warn('[HLS Loader] getHlsQualities: No levels array found on HLS instance')
logger.warn('[HLS Loader] getHlsQualities: No levels array found on HLS instance')
return []
}
console.log('[HLS Loader] getHlsQualities: Raw levels from HLS:', hls.levels)
logger.log('[HLS Loader] getHlsQualities: Raw levels from HLS:', hls.levels)
const qualities: VideoQuality[] = hls.levels.map((level: any, index: number) => {
const resolution = typeof level.attrs?.RESOLUTION === 'string' ? level.attrs.RESOLUTION : undefined
@@ -205,11 +206,12 @@ export const getHlsQualities = (hls: any): VideoQuality[] => {
return (b.bitrate || 0) - (a.bitrate || 0)
})
console.log('[HLS Loader] getHlsQualities: Processed qualities:', sortedQualities)
logger.log('[HLS Loader] getHlsQualities: Processed qualities:', sortedQualities)
return sortedQualities
} catch (error) {
console.error('[HLS Loader] getHlsQualities: Error extracting qualities:', error)
logger.error('[HLS Loader] getHlsQualities: Error extracting qualities:', error)
return []
}
}
+12 -10
View File
@@ -3,6 +3,7 @@
*/
import type { AudioTrack, VideoQuality, SubtitleTrack } from '../types'
import { logger } from './logger'
interface HlsSetupOptions {
video: HTMLVideoElement
@@ -30,7 +31,7 @@ export const setupHlsInstance = async ({
throw new Error('HLS.js is not supported in this browser')
}
console.log('[HLS Setup] Creating HLS instance for:', src)
logger.log('[HLS Setup] Creating HLS instance for:', src)
const hls = new Hls({
enableWorker: true,
@@ -44,10 +45,10 @@ export const setupHlsInstance = async ({
let manifestParsedHandled = false
hls.on(Hls.Events.MANIFEST_PARSED, () => {
console.log('[HLS Setup] MANIFEST_PARSED event fired')
logger.log('[HLS Setup] MANIFEST_PARSED event fired')
if (manifestParsedHandled) {
console.warn('[HLS Setup] MANIFEST_PARSED already handled, skipping')
logger.warn('[HLS Setup] MANIFEST_PARSED already handled, skipping')
return
}
manifestParsedHandled = true
@@ -58,23 +59,23 @@ export const setupHlsInstance = async ({
const qualities = getHlsQualities(hls)
const subtitles = getHlsSubtitleTracks(hls)
console.log('[HLS Setup] Detected tracks:', {
logger.log('[HLS Setup] Detected tracks:', {
audioTracks: tracks.length,
qualities: qualities.length,
subtitles: subtitles.length
})
if (tracks.length > 0) {
console.log('[HLS Setup] Loading audio tracks:', tracks)
logger.log('[HLS Setup] Loading audio tracks:', tracks)
onAudioTracksLoaded?.(tracks)
}
if (subtitles.length > 0) {
console.log('[HLS Setup] Loading subtitle tracks:', subtitles)
logger.log('[HLS Setup] Loading subtitle tracks:', subtitles)
onSubtitleTracksLoaded?.(subtitles)
}
console.log('[HLS Setup] Loading quality levels:', qualities)
logger.log('[HLS Setup] Loading quality levels:', qualities)
onQualityLevelsLoaded?.(qualities)
}
@@ -93,14 +94,14 @@ export const setupHlsInstance = async ({
hls.on(Hls.Events.LEVEL_LOADED, () => {
const qualities = getHlsQualities(hls)
if (qualities.length > 0) {
console.log('[HLS Setup] LEVEL_LOADED - Qualities available:', qualities.length)
logger.log('[HLS Setup] LEVEL_LOADED - Qualities available:', qualities.length)
onQualityLevelsLoaded?.(qualities)
}
})
hls.on(Hls.Events.AUDIO_TRACKS_UPDATED, () => {
const tracks = getHlsAudioTracks(hls)
console.log('[HLS Setup] AUDIO_TRACKS_UPDATED event:', tracks.length, 'tracks')
logger.log('[HLS Setup] AUDIO_TRACKS_UPDATED event:', tracks.length, 'tracks')
if (tracks.length > 0) {
onAudioTracksLoaded?.(tracks)
}
@@ -108,7 +109,7 @@ export const setupHlsInstance = async ({
hls.on(Hls.Events.SUBTITLE_TRACKS_UPDATED, () => {
const subtitles = getHlsSubtitleTracks(hls)
console.log('[HLS Setup] SUBTITLE_TRACKS_UPDATED event:', subtitles.length, 'tracks')
logger.log('[HLS Setup] SUBTITLE_TRACKS_UPDATED event:', subtitles.length, 'tracks')
if (subtitles.length > 0) {
onSubtitleTracksLoaded?.(subtitles)
}
@@ -139,3 +140,4 @@ export const setupHlsInstance = async ({
delete (video as any).__hlsInstance
}
}
+9
View File
@@ -0,0 +1,9 @@
type LoggerMethod = (..._args: unknown[]) => void
const noop: LoggerMethod = () => undefined
export const logger = {
log: noop,
warn: noop,
error: noop,
}
+8 -6
View File
@@ -2,6 +2,7 @@
* MPEG-TS loader utility
* Dynamically loads mpegts.js library
*/
import { logger } from './logger'
export interface MpegtsConfig {
enableWorker?: boolean
@@ -29,26 +30,26 @@ let loadingPromise: Promise<any> | null = null
export const loadMpegts = async (): Promise<any> => {
// Return cached instance if available
if (mpegtsInstance) {
console.log('[MPEG-TS Loader] Using cached mpegts.js instance')
logger.log('[MPEG-TS Loader] Using cached mpegts.js instance')
return mpegtsInstance
}
// Return existing loading promise if already loading
if (loadingPromise) {
console.log('[MPEG-TS Loader] Already loading, waiting for existing promise...')
logger.log('[MPEG-TS Loader] Already loading, waiting for existing promise...')
return loadingPromise
}
// Start loading
loadingPromise = (async () => {
try {
console.log('[MPEG-TS Loader] Attempting to load from npm package...')
logger.log('[MPEG-TS Loader] Attempting to load from npm package...')
const module = await import('mpegts.js')
mpegtsInstance = module.default || module
console.log('[MPEG-TS Loader] Successfully loaded from npm package')
logger.log('[MPEG-TS Loader] Successfully loaded from npm package')
return mpegtsInstance
} catch (error) {
console.error('[MPEG-TS Loader] Failed to load mpegts.js:', error)
logger.error('[MPEG-TS Loader] Failed to load mpegts.js:', error)
throw new Error('Failed to load mpegts.js. Make sure it is installed: npm install mpegts.js')
} finally {
loadingPromise = null
@@ -103,5 +104,6 @@ export const getMpegtsInstance = (): any | null => {
export const clearMpegtsCache = (): void => {
mpegtsInstance = null
loadingPromise = null
console.log('[MPEG-TS Loader] Cache cleared')
logger.log('[MPEG-TS Loader] Cache cleared')
}
+17 -15
View File
@@ -5,6 +5,7 @@
import { loadMpegts, isMpegtsSupported, createDefaultMpegtsConfig } from './mpegtsLoader'
import { isLiveStream } from './videoProtocol'
import { logger } from './logger'
export interface MpegtsSetupOptions {
video: HTMLVideoElement
@@ -41,7 +42,7 @@ export const setupMpegtsInstance = async ({
throw error
}
console.log('[MPEG-TS Setup] Creating player instance for:', src)
logger.log('[MPEG-TS Setup] Creating player instance for:', src)
// Detect if stream is live
const isLive = isLiveStream(src)
@@ -72,40 +73,40 @@ export const setupMpegtsInstance = async ({
// Event handlers
player.on(mpegts.Events.ERROR, (errorType: string, errorDetail: string, errorInfo: any) => {
console.error('mpegts.js error:', { errorType, errorDetail, errorInfo })
logger.error('mpegts.js error:', { errorType, errorDetail, errorInfo })
const error = new Error(`MPEG-TS Player Error: ${errorType} - ${errorDetail}`)
// Handle specific error types
if (errorType === mpegts.ErrorTypes.NETWORK_ERROR) {
console.error('Network error occurred:', errorDetail)
logger.error('Network error occurred:', errorDetail)
// Attempt recovery for recoverable network errors
if (
errorDetail === mpegts.ErrorDetails.NETWORK_EXCEPTION ||
errorDetail === mpegts.ErrorDetails.NETWORK_STATUS_CODE_INVALID
) {
console.log('Attempting to recover from network error...')
logger.log('Attempting to recover from network error...')
try {
player.unload()
player.load()
return
} catch (recoveryError) {
console.error('Failed to recover from network error:', recoveryError)
logger.error('Failed to recover from network error:', recoveryError)
}
}
} else if (errorType === mpegts.ErrorTypes.MEDIA_ERROR) {
console.error('Media error occurred:', errorDetail)
logger.error('Media error occurred:', errorDetail)
// Some media errors are recoverable
if (errorDetail === mpegts.ErrorDetails.MEDIA_MSE_ERROR) {
console.log('Attempting to recover from media error...')
logger.log('Attempting to recover from media error...')
try {
player.unload()
player.load()
return
} catch (recoveryError) {
console.error('Failed to recover from media error:', recoveryError)
logger.error('Failed to recover from media error:', recoveryError)
}
}
}
@@ -117,15 +118,15 @@ export const setupMpegtsInstance = async ({
})
player.on(mpegts.Events.LOADING_COMPLETE, () => {
console.log('mpegts.js: Loading complete')
logger.log('mpegts.js: Loading complete')
})
player.on(mpegts.Events.RECOVERED_EARLY_EOF, () => {
console.log('mpegts.js: Recovered from early EOF')
logger.log('mpegts.js: Recovered from early EOF')
})
player.on(mpegts.Events.METADATA_ARRIVED, (metadata: any) => {
console.log('mpegts.js: Metadata arrived', metadata)
logger.log('mpegts.js: Metadata arrived', metadata)
// Trigger onLoadedMetadata callback
if (onLoadedMetadata) {
@@ -145,7 +146,7 @@ export const setupMpegtsInstance = async ({
try {
await video.play()
} catch (playError) {
console.warn('Autoplay failed:', playError)
logger.warn('Autoplay failed:', playError)
// Autoplay might be blocked by browser, ignore error
}
}
@@ -153,7 +154,7 @@ export const setupMpegtsInstance = async ({
// Return cleanup function
return () => {
try {
console.log('Cleaning up mpegts.js player...')
logger.log('Cleaning up mpegts.js player...')
// Remove event listeners
player.off(mpegts.Events.ERROR)
@@ -176,11 +177,11 @@ export const setupMpegtsInstance = async ({
delete (video as any).__mpegtsInstance
delete (video as any).__mpegtsStats
} catch (cleanupError) {
console.error('Error during mpegts.js cleanup:', cleanupError)
logger.error('Error during mpegts.js cleanup:', cleanupError)
}
}
} catch (error) {
console.error('Failed to setup mpegts.js player:', error)
logger.error('Failed to setup mpegts.js player:', error)
const setupError =
error instanceof Error ? error : new Error('Failed to setup MPEG-TS player')
@@ -225,3 +226,4 @@ export const getMpegtsStats = (video: HTMLVideoElement | null): any | null => {
export const hasMpegtsInstance = (video: HTMLVideoElement | null): boolean => {
return getMpegtsInstance(video) !== null
}
+5 -3
View File
@@ -3,6 +3,7 @@
* Loads flv.js library with NPM fallback to CDN strategy
* Mirrors the HLS loader pattern for consistency
*/
import { logger } from './logger'
const FLVJS_CDN_URL = 'https://cdn.jsdelivr.net/npm/flv.js@1.6.2/dist/flv.min.js'
@@ -48,14 +49,14 @@ export const loadFlvjs = async (): Promise<any> => {
const flvModule = await import('flv.js')
return flvModule.default || flvModule
} catch (npmError) {
console.warn('flv.js NPM package not available, loading from CDN...', npmError)
logger.warn('flv.js NPM package not available, loading from CDN...', npmError)
try {
// Fallback to CDN
const flvjs = await loadFlvjsFromCDN()
return flvjs
} catch (cdnError) {
console.error('Failed to load flv.js from both NPM and CDN', cdnError)
logger.error('Failed to load flv.js from both NPM and CDN', cdnError)
throw new Error(
'Failed to load flv.js library. Please ensure flv.js is available or check your network connection.'
)
@@ -162,7 +163,8 @@ export const extractFlvQualityInfo = (player: any): {
audioBitrate: stats.audioBitrate,
}
} catch (error) {
console.warn('Failed to extract flv.js quality info:', error)
logger.warn('Failed to extract flv.js quality info:', error)
return null
}
}
+17 -15
View File
@@ -6,6 +6,7 @@
import { loadFlvjs, isFlvjsSupported, createDefaultFlvConfig } from './rtmpLoader'
import { isLiveStream } from './videoProtocol'
import { logger } from './logger'
export interface RtmpSetupOptions {
video: HTMLVideoElement
@@ -50,7 +51,7 @@ export const setupRtmpInstance = async ({
if (src.startsWith('rtmp://') || src.startsWith('rtmps://')) {
// For RTMP URLs, flv.js expects HTTP-FLV endpoint
// This is a limitation - direct RTMP playback requires server-side conversion
console.warn(
logger.warn(
'Direct RTMP playback requires an HTTP-FLV proxy. Please ensure your RTMP stream is available via HTTP-FLV.'
)
type = 'flv'
@@ -84,40 +85,40 @@ export const setupRtmpInstance = async ({
// Event handlers
player.on(flvjs.Events.ERROR, (errorType: string, errorDetail: string, errorInfo: any) => {
console.error('flv.js error:', { errorType, errorDetail, errorInfo })
logger.error('flv.js error:', { errorType, errorDetail, errorInfo })
const error = new Error(`FLV Player Error: ${errorType} - ${errorDetail}`)
// Handle specific error types
if (errorType === flvjs.ErrorTypes.NETWORK_ERROR) {
console.error('Network error occurred:', errorDetail)
logger.error('Network error occurred:', errorDetail)
// Attempt recovery for recoverable network errors
if (
errorDetail === flvjs.ErrorDetails.NETWORK_EXCEPTION ||
errorDetail === flvjs.ErrorDetails.NETWORK_STATUS_CODE_INVALID
) {
console.log('Attempting to recover from network error...')
logger.log('Attempting to recover from network error...')
try {
player.unload()
player.load()
return
} catch (recoveryError) {
console.error('Failed to recover from network error:', recoveryError)
logger.error('Failed to recover from network error:', recoveryError)
}
}
} else if (errorType === flvjs.ErrorTypes.MEDIA_ERROR) {
console.error('Media error occurred:', errorDetail)
logger.error('Media error occurred:', errorDetail)
// Some media errors are recoverable
if (errorDetail === flvjs.ErrorDetails.MEDIA_MSE_ERROR) {
console.log('Attempting to recover from media error...')
logger.log('Attempting to recover from media error...')
try {
player.unload()
player.load()
return
} catch (recoveryError) {
console.error('Failed to recover from media error:', recoveryError)
logger.error('Failed to recover from media error:', recoveryError)
}
}
}
@@ -129,15 +130,15 @@ export const setupRtmpInstance = async ({
})
player.on(flvjs.Events.LOADING_COMPLETE, () => {
console.log('flv.js: Loading complete')
logger.log('flv.js: Loading complete')
})
player.on(flvjs.Events.RECOVERED_EARLY_EOF, () => {
console.log('flv.js: Recovered from early EOF')
logger.log('flv.js: Recovered from early EOF')
})
player.on(flvjs.Events.METADATA_ARRIVED, (metadata: any) => {
console.log('flv.js: Metadata arrived', metadata)
logger.log('flv.js: Metadata arrived', metadata)
// Trigger onLoadedMetadata callback
if (onLoadedMetadata) {
@@ -158,7 +159,7 @@ export const setupRtmpInstance = async ({
try {
await video.play()
} catch (playError) {
console.warn('Autoplay failed:', playError)
logger.warn('Autoplay failed:', playError)
// Autoplay might be blocked by browser, ignore error
}
}
@@ -166,7 +167,7 @@ export const setupRtmpInstance = async ({
// Return cleanup function
return () => {
try {
console.log('Cleaning up flv.js player...')
logger.log('Cleaning up flv.js player...')
// Remove event listeners
player.off(flvjs.Events.ERROR)
@@ -189,11 +190,11 @@ export const setupRtmpInstance = async ({
delete (video as any).__rtmpInstance
delete (video as any).__rtmpStats
} catch (cleanupError) {
console.error('Error during flv.js cleanup:', cleanupError)
logger.error('Error during flv.js cleanup:', cleanupError)
}
}
} catch (error) {
console.error('Failed to setup flv.js player:', error)
logger.error('Failed to setup flv.js player:', error)
const setupError =
error instanceof Error ? error : new Error('Failed to setup RTMP/FLV player')
@@ -238,3 +239,4 @@ export const getRtmpStats = (video: HTMLVideoElement | null): any | null => {
export const hasRtmpInstance = (video: HTMLVideoElement | null): boolean => {
return getRtmpInstance(video) !== null
}