81 lines
1.9 KiB
TypeScript
81 lines
1.9 KiB
TypeScript
/**
|
|
* Parse SRT subtitle format to WebVTT
|
|
*/
|
|
export const parseSRT = (srtContent: string): string => {
|
|
const normalised = srtContent.replace(/\r\n/g, '\n').replace(/\r/g, '\n').trim()
|
|
|
|
if (!normalised) {
|
|
return 'WEBVTT\n\n'
|
|
}
|
|
|
|
const cues = normalised
|
|
.split(/\n{2,}/)
|
|
.map((cueBlock) => {
|
|
const lines = cueBlock
|
|
.split('\n')
|
|
.map((line) => line.replace(/^\ufeff/, '').trimEnd())
|
|
.filter((line, index, arr) => !(line === '' && index === arr.length - 1))
|
|
|
|
if (lines.length === 0) {
|
|
return null
|
|
}
|
|
|
|
if (/^\d+$/.test(lines[0].trim())) {
|
|
lines.shift()
|
|
}
|
|
|
|
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'
|
|
}
|
|
|
|
/**
|
|
* Create a blob URL from subtitle content
|
|
*/
|
|
export const createSubtitleBlobURL = (content: string, format: 'vtt' | 'srt'): string => {
|
|
const vttContent = format === 'srt' ? parseSRT(content) : content
|
|
const blob = new Blob([vttContent], { type: 'text/vtt;charset=utf-8' })
|
|
return URL.createObjectURL(blob)
|
|
}
|
|
|
|
/**
|
|
* Fetch and parse subtitle file
|
|
*/
|
|
export const fetchSubtitle = async (url: string): Promise<string> => {
|
|
const response = await fetch(url)
|
|
const content = await response.text()
|
|
|
|
// Detect format
|
|
if (url.endsWith('.srt')) {
|
|
return parseSRT(content)
|
|
}
|
|
|
|
return content
|
|
}
|