Skip to content

Commit d244049

Browse files
committed
Fix subtitles
1 parent 5eeaec1 commit d244049

File tree

3 files changed

+52
-5
lines changed

3 files changed

+52
-5
lines changed

example/ActiveLayerLayoutDemo.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,7 @@ export const ActiveLayerLayoutDemo: React.FC<ActiveLayerLayoutDemoProps> = ({ on
305305
<View style={styles.videoContainer}>
306306
<View style={styles.videoWrapper}>
307307
<CLDVideoLayer
308+
key={currentExample} // Force remount when switching examples to reset state
308309
cldVideo={createMyVideoObject()}
309310
videoUrl={(currentExample === 'subtitles' || currentExample === 'quality') ? 'https://res.cloudinary.com/demo/video/upload/sp_sd:subtitles_((code_en-US;file_outdoors.vtt);(code_es;file_outdoors-es.vtt))/sea_turtle.m3u8' : undefined}
310311
autoPlay={false}

src/widgets/video/layer/utils.ts

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ export const isHLSVideo = (videoUrl: string | undefined): boolean => {
8787
export const parseHLSManifest = async (manifestUrl: string): Promise<SubtitleOption[]> => {
8888
try {
8989
const response = await fetch(manifestUrl);
90+
9091
if (!response.ok) {
9192
console.warn('Failed to fetch HLS manifest:', response.status);
9293
return [];
@@ -109,13 +110,21 @@ export const parseHLSManifest = async (manifestUrl: string): Promise<SubtitleOpt
109110
const attributes = parseM3U8Attributes(line);
110111

111112
if (attributes.LANGUAGE && attributes.NAME) {
112-
const languageCode = attributes.LANGUAGE;
113-
const displayName = attributes.NAME.replace(/"/g, ''); // Remove quotes
113+
const languageCode = attributes.LANGUAGE.replace(/"/g, ''); // Remove quotes from language code
114+
const displayName = attributes.NAME.replace(/"/g, ''); // Remove quotes from display name
114115
let subtitleUrl = attributes.URI ? attributes.URI.replace(/"/g, '') : undefined;
115116

116-
// Resolve relative URLs
117+
// Resolve relative URLs properly
117118
if (subtitleUrl && !subtitleUrl.startsWith('http')) {
118-
subtitleUrl = baseUrl + subtitleUrl;
119+
// Handle URLs that start with /
120+
if (subtitleUrl.startsWith('/')) {
121+
// Extract the base domain from the manifest URL
122+
const urlObj = new URL(manifestUrl);
123+
subtitleUrl = `${urlObj.protocol}//${urlObj.host}${subtitleUrl}`;
124+
} else {
125+
// For other relative URLs, use baseUrl
126+
subtitleUrl = baseUrl + subtitleUrl;
127+
}
119128
}
120129

121130
// Only add if not already present

src/widgets/video/layer/utils/subtitleUtils.ts

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,12 +95,49 @@ export const findActiveSubtitle = (cues: SubtitleCue[], currentTime: number): Su
9595
export const fetchSubtitleFile = async (url: string): Promise<SubtitleCue[]> => {
9696
try {
9797
const response = await fetch(url);
98+
9899
if (!response.ok) {
99100
throw new Error(`Failed to fetch subtitle file: ${response.status}`);
100101
}
101102

102103
const content = await response.text();
103-
return parseWebVTT(content);
104+
105+
// Check if this is an M3U8 playlist instead of a VTT file
106+
if (content.trim().startsWith('#EXTM3U')) {
107+
// Parse the M3U8 to find the actual VTT file URL
108+
const lines = content.split('\n');
109+
let vttUrl = null;
110+
111+
for (const line of lines) {
112+
const trimmedLine = line.trim();
113+
// Look for lines that end with .vtt and don't start with #
114+
if (!trimmedLine.startsWith('#') && trimmedLine.includes('.vtt')) {
115+
vttUrl = trimmedLine;
116+
break;
117+
}
118+
}
119+
120+
if (vttUrl) {
121+
// Resolve relative URL if needed
122+
if (vttUrl.startsWith('/')) {
123+
const urlObj = new URL(url);
124+
vttUrl = `${urlObj.protocol}//${urlObj.host}${vttUrl}`;
125+
} else if (!vttUrl.startsWith('http')) {
126+
const baseUrl = url.substring(0, url.lastIndexOf('/') + 1);
127+
vttUrl = baseUrl + vttUrl;
128+
}
129+
130+
// Recursively fetch the actual VTT file
131+
return await fetchSubtitleFile(vttUrl);
132+
} else {
133+
console.warn('No VTT URL found in M3U8 playlist');
134+
return [];
135+
}
136+
}
137+
138+
// Content is already VTT, parse it directly
139+
const cues = parseWebVTT(content);
140+
return cues;
104141
} catch (error) {
105142
console.warn('Failed to fetch subtitle file:', error);
106143
return [];

0 commit comments

Comments
 (0)