Fix SRT subtitle conversion and default selection

This commit is contained in:
Mert Uyanık
2025-10-29 14:22:08 +03:00
parent c5efcb95d5
commit a508919d20
2 changed files with 71 additions and 26 deletions
+22
View File
@@ -265,6 +265,28 @@ export const VideoElement: React.FC<VideoElementProps> = ({
} }
}, [subtitles]) }, [subtitles])
useEffect(() => {
const video = videoRef.current
if (!video) return
if (processedSubtitles.length === 0) return
if (settings.subtitle) return
const defaultSubtitle = processedSubtitles.find((subtitle) => subtitle.default)
if (!defaultSubtitle) return
const tracks = video.textTracks
if (!tracks || tracks.length === 0) return
for (let i = 0; i < tracks.length; i++) {
const track = tracks[i]
if (track.language === defaultSubtitle.lang) {
track.mode = 'showing'
setSubtitle(defaultSubtitle)
break
}
}
}, [processedSubtitles, settings.subtitle, setSubtitle, videoRef])
// Detect HLS source and load hls.js if needed // Detect HLS source and load hls.js if needed
useEffect(() => { useEffect(() => {
const video = videoRef.current const video = videoRef.current
+44 -21
View File
@@ -2,34 +2,57 @@
* Parse SRT subtitle format to WebVTT * Parse SRT subtitle format to WebVTT
*/ */
export const parseSRT = (srtContent: string): string => { export const parseSRT = (srtContent: string): string => {
const lines = srtContent.trim().split('\n') const normalised = srtContent.replace(/\r\n/g, '\n').replace(/\r/g, '\n').trim()
let vttContent = 'WEBVTT\n\n'
let i = 0 if (!normalised) {
while (i < lines.length) { return 'WEBVTT\n\n'
// Skip subtitle number
if (/^\d+$/.test(lines[i].trim())) {
i++
} }
// Parse timestamp line const cues = normalised
if (lines[i] && lines[i].includes('-->')) { .split(/\n{2,}/)
const timeLine = lines[i].replace(/,/g, '.') // SRT uses comma, VTT uses dot .map((cueBlock) => {
vttContent += timeLine + '\n' const lines = cueBlock
i++ .split('\n')
.map((line) => line.replace(/^\ufeff/, '').trimEnd())
.filter((line, index, arr) => !(line === '' && index === arr.length - 1))
// Add subtitle text if (lines.length === 0) {
while (i < lines.length && lines[i].trim() !== '') { return null
vttContent += lines[i] + '\n'
i++
}
vttContent += '\n'
} }
i++ if (/^\d+$/.test(lines[0].trim())) {
lines.shift()
} }
return vttContent if (lines.length === 0) {
return null
}
const timeLine = lines.shift()
if (!timeLine || !timeLine.includes('-->')) {
return null
}
const vttTimeLine = timeLine
.split('-->')
.map((part) => part.trim().replace(/,/g, '.'))
.join(' --> ')
if (!vttTimeLine.includes('-->')) {
return null
}
const text = lines.join('\n')
return `${vttTimeLine}\n${text}`.trim()
})
.filter((cueBlock): cueBlock is string => Boolean(cueBlock))
const header = 'WEBVTT\n\n'
if (cues.length === 0) {
return header
}
return header + cues.join('\n\n') + '\n'
} }
/** /**
@@ -37,7 +60,7 @@ export const parseSRT = (srtContent: string): string => {
*/ */
export const createSubtitleBlobURL = (content: string, format: 'vtt' | 'srt'): string => { export const createSubtitleBlobURL = (content: string, format: 'vtt' | 'srt'): string => {
const vttContent = format === 'srt' ? parseSRT(content) : content const vttContent = format === 'srt' ? parseSRT(content) : content
const blob = new Blob([vttContent], { type: 'text/vtt' }) const blob = new Blob([vttContent], { type: 'text/vtt;charset=utf-8' })
return URL.createObjectURL(blob) return URL.createObjectURL(blob)
} }