Add i18n, tests, and update documentation

Introduces internationalization (i18n) support with English and Turkish, adds unit tests and test setup with Vitest and React Testing Library, and updates documentation including README and changelog. Removes legacy publishing and usage guides, refactors components to use translation system, and updates build and test scripts in package.json. Also adds new utility modules for HLS and CORS, and improves PlayerContext and SettingsMenu for language support.
This commit is contained in:
hibna
2025-10-29 13:10:07 +03:00
parent e75d241421
commit bad1cc6ca0
26 changed files with 1843 additions and 1341 deletions
+13 -72
View File
@@ -2,7 +2,8 @@ import React, { useEffect, useCallback, useState } from 'react'
import { usePlayerContext } from '../contexts/PlayerContext'
import type { SubtitleTrack, AudioTrack, VideoQuality } from '../types'
import { validateVideoURL, getCORSErrorMessage, isCORSError } from '../utils/corsHelper'
import { getHlsAudioTracks, getHlsQualities, setHlsAudioTrack, setHlsQualityLevel } from '../utils/hlsLoader'
import { setHlsAudioTrack, setHlsQualityLevel } from '../utils/hlsControl'
import { setupHlsInstance } from '../utils/hlsSetup'
import './VideoElement.css'
interface VideoElementProps {
@@ -219,80 +220,21 @@ export const VideoElement: React.FC<VideoElementProps> = ({
const setupHls = async () => {
if (isHLS && video.canPlayType('application/vnd.apple.mpegurl') === '') {
// Browser doesn't support HLS natively, load hls.js
try {
// Dynamic import with CDN fallback
const { loadHls, isHlsSupported } = await import('../utils/hlsLoader')
const Hls = await loadHls()
if (!isHlsSupported(Hls)) {
throw new Error('HLS.js is not supported in this browser')
}
const hls = new Hls({
enableWorker: true,
lowLatencyMode: false,
})
hls.loadSource(src)
hls.attachMedia(video)
hls.on(Hls.Events.MANIFEST_PARSED, () => {
// Extract audio tracks after manifest is parsed
// Sometimes audio tracks are not immediately available, so we try with a small delay
setTimeout(() => {
const tracks = getHlsAudioTracks(hls)
const qualities = getHlsQualities(hls)
if (tracks.length > 0) {
setAvailableAudioTracks(tracks)
onAudioTracksLoaded?.(tracks)
}
setAvailableQualities(qualities)
onQualityLevelsLoaded?.(qualities)
}, 100)
if (autoplay) {
void video.play().catch(() => undefined)
}
})
// Also listen to audio track updates
hls.on(Hls.Events.AUDIO_TRACKS_UPDATED, () => {
const tracks = getHlsAudioTracks(hls)
if (tracks.length > 0) {
cleanupFn = await setupHlsInstance({
video,
src,
autoplay,
onAudioTracksLoaded: (tracks) => {
setAvailableAudioTracks(tracks)
onAudioTracksLoaded?.(tracks)
}
},
onQualityLevelsLoaded: (qualities) => {
setAvailableQualities(qualities)
onQualityLevelsLoaded?.(qualities)
},
onError: handleError,
})
hls.on(Hls.Events.ERROR, (_event: any, data: any) => {
if (data.fatal) {
switch (data.type) {
case Hls.ErrorTypes.NETWORK_ERROR:
hls.startLoad()
break
case Hls.ErrorTypes.MEDIA_ERROR:
hls.recoverMediaError()
break
default:
handleError()
break
}
}
})
// Store hls instance for cleanup
;(video as any).__hlsInstance = hls
// Setup cleanup function
cleanupFn = () => {
if (hls) {
hls.destroy()
}
delete (video as any).__hlsInstance
}
} catch (err) {
let error: Error
if (err instanceof Error && isCORSError(err)) {
@@ -309,7 +251,6 @@ export const VideoElement: React.FC<VideoElementProps> = ({
onError?.(error)
}
} else {
// Native support or regular video
video.src = src
if (autoplay) {
void video.play().catch(() => undefined)