Skip to content

Commit 229ce1e

Browse files
authored
Merge pull request #499 from performant-software/feature/archnet1476_video_captions
Archnet #1476 - Video captions
2 parents 5dd736d + ce4796f commit 229ce1e

File tree

11 files changed

+170
-7
lines changed

11 files changed

+170
-7
lines changed

packages/semantic-ui/src/components/LazyMedia.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import i18n from '../i18n/i18n';
1616
import LazyAudio from './LazyAudio';
1717
import LazyDocument from './LazyDocument';
1818
import LazyImage from './LazyImage';
19-
import LazyVideo from './LazyVideo';
19+
import LazyVideo, { type Transcription } from './LazyVideo';
2020

2121
type Props = {
2222
children?: Node,
@@ -27,7 +27,8 @@ type Props = {
2727
onUpload: (file: File) => void,
2828
preview?: string,
2929
size?: string,
30-
src?: string
30+
src?: string,
31+
transcriptions?: Array<Transcription>
3132
};
3233

3334
const ContentTypes = {
@@ -146,6 +147,7 @@ const LazyMedia: ComponentType<any> = (props: Props) => {
146147
preview={preview}
147148
src={source}
148149
size={props.size}
150+
transcriptions={props.transcriptions}
149151
>
150152
{ renderChildren() }
151153
</LazyVideo>

packages/semantic-ui/src/components/LazyVideo.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import {
1919
import i18n from '../i18n/i18n';
2020
import DownloadButton from './DownloadButton';
2121
import LazyLoader from './LazyLoader';
22-
import VideoPlayer from './VideoPlayer';
22+
import VideoPlayer, { type Transcription } from './VideoPlayer';
2323
import './LazyVideo.css';
2424

2525
type Props = {
@@ -35,7 +35,8 @@ type Props = {
3535
playButtonLabel?: string,
3636
preview?: ?string,
3737
size?: string,
38-
src?: string
38+
src?: string,
39+
transcriptions?: Array<Transcription>
3940
};
4041

4142
const LazyVideo = (props: Props) => {
@@ -184,6 +185,7 @@ const LazyVideo = (props: Props) => {
184185
open={modal}
185186
placeholder={props.preview}
186187
size='large'
188+
transcriptions={props.transcriptions}
187189
video={props.src}
188190
/>
189191
)}
@@ -201,3 +203,7 @@ LazyVideo.defaultProps = {
201203
};
202204

203205
export default LazyVideo;
206+
207+
export type {
208+
Transcription
209+
};

packages/semantic-ui/src/components/MediaGallery.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {
88
Transition
99
} from 'semantic-ui-react';
1010
import PlayButton from './PlayButton';
11-
import VideoPlayer from './VideoPlayer';
11+
import VideoPlayer, { type Transcription } from './VideoPlayer';
1212
import VideoPlayerButton from './VideoPlayerButton';
1313
import './MediaGallery.css';
1414

@@ -17,6 +17,7 @@ type Item = {
1717
embedded?: boolean,
1818
preview?: string,
1919
src?: string,
20+
transcriptions?: Array<Transcription>,
2021
type: 'image' | 'video'
2122
};
2223

@@ -136,6 +137,7 @@ const MediaGallery = (props: Props) => {
136137
open={video}
137138
placeholder={props.item.preview}
138139
size='huge'
140+
transcriptions={props.item.transcriptions}
139141
video={props.item.src}
140142
/>
141143
)}

packages/semantic-ui/src/components/VideoPlayer.js

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// @flow
22

3-
import { ModalContext } from '@performant-software/shared-components';
3+
import { ModalContext, VideoTrack } from '@performant-software/shared-components';
44
import React, {
55
useEffect,
66
useRef,
@@ -13,9 +13,17 @@ import {
1313
Modal,
1414
Ref
1515
} from 'semantic-ui-react';
16+
import _ from 'underscore';
1617
import i18n from '../i18n/i18n';
1718
import './VideoPlayer.css';
1819

20+
export type Transcription = {
21+
default?: boolean,
22+
label: string,
23+
language: string,
24+
src: string
25+
};
26+
1927
type Props = {
2028
autoPlay?: boolean,
2129
embedded?: boolean,
@@ -25,6 +33,7 @@ type Props = {
2533
placeholder?: ?string,
2634
placeholderAlt?: string,
2735
size?: string,
36+
transcriptions?: Array<Transcription>,
2837
video: string
2938
};
3039

@@ -85,7 +94,18 @@ const VideoPlayer = (props: Props) => {
8594
controls
8695
onError={() => setError(true)}
8796
src={props.video}
88-
/>
97+
>
98+
{ _.map(props.transcriptions, (transcription) => (
99+
<VideoTrack
100+
default={transcription.default}
101+
key={transcription.language}
102+
kind='captions'
103+
label={transcription.label}
104+
srcLang={transcription.language}
105+
src={transcription.src}
106+
/>
107+
))}
108+
</video>
89109
)}
90110
</Modal.Content>
91111
</Modal>
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// @flow
2+
3+
import React, { useEffect, useMemo, useState } from 'react';
4+
5+
type Props = {
6+
src: string
7+
};
8+
9+
const VideoTrack = (props: Props) => {
10+
const [content, setContent] = useState();
11+
12+
/**
13+
* Memo-izes the local file URL.
14+
*/
15+
const src = useMemo(() => {
16+
const blob = new Blob([content], { type: 'text/plain' });
17+
return URL.createObjectURL(blob);
18+
}, [content]);
19+
20+
/**
21+
* Fetches the `src` attribute to store on the state. This will avoid any cross-domain issue with loading VTTs.
22+
*/
23+
useEffect(() => {
24+
fetch(props.src)
25+
.then((response) => response.text())
26+
.then(setContent);
27+
}, [props.src]);
28+
29+
if (!src) {
30+
return null;
31+
}
32+
33+
return (
34+
<track
35+
{...props}
36+
src={src}
37+
/>
38+
);
39+
};
40+
41+
export default VideoTrack;

packages/shared/src/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export { default as GoogleScript } from './components/GoogleScript';
88
export { default as InfiniteScroll } from './components/InfiniteScroll';
99
export { default as Keyboard } from './components/Keyboard';
1010
export { default as RichTextArea } from './components/RichTextArea';
11+
export { default as VideoTrack } from './components/VideoTrack';
1112

1213
// Context
1314
export { default as ModalContext } from './context/ModalContext';
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
WEBVTT
2+
3+
00:00:00.500 --> 00:00:02.000
4+
The Web is always changing
5+
6+
00:00:02.500 --> 00:00:04.300
7+
and the way we access it is changing

packages/storybook/src/semantic-ui/LazyMedia.stories.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import LazyMedia from '../../../semantic-ui/src/components/LazyMedia';
66
import audio from '../assets/SampleAudio.mp3';
77
import image from '../assets/test-image.jpg';
88
import document from '../assets/sample2.pdf';
9+
import video from '../assets/SampleVideo.mp4';
910

1011
export default {
1112
title: 'Components/Semantic UI/LazyMedia',
@@ -37,6 +38,25 @@ export const Image = () => (
3738
/>
3839
);
3940

41+
export const Video = () => (
42+
<LazyMedia
43+
contentType='video/mp4'
44+
src={video}
45+
/>
46+
);
47+
48+
export const VideoWithCaptions = () => (
49+
<LazyMedia
50+
contentType='video/mp4'
51+
src={video}
52+
transcriptions={[{
53+
label: 'English',
54+
language: 'en',
55+
src: '/src/assets/test.vtt'
56+
}]}
57+
/>
58+
);
59+
4060
export const Uploadable = () => {
4161
const [file, setFile] = useState();
4262

packages/storybook/src/semantic-ui/LazyVideo.stories.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,3 +269,18 @@ export const Downloadable = () => (
269269
src={video}
270270
/>
271271
);
272+
273+
export const Transcriptions = () => (
274+
<LazyVideo
275+
image={{
276+
alt: 'Test Image'
277+
}}
278+
preview={image}
279+
src={video}
280+
transcriptions={[{
281+
label: 'English',
282+
language: 'en',
283+
src: '/src/assets/test.vtt'
284+
}]}
285+
/>
286+
);

packages/storybook/src/semantic-ui/MediaGallery.stories.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,3 +79,19 @@ export const Video = () => (
7979
visible
8080
/>
8181
);
82+
83+
export const VideoWithCaptions = () => (
84+
<MediaGallery
85+
item={{
86+
src: video,
87+
transcriptions: [{
88+
label: 'English',
89+
language: 'en',
90+
src: '/src/assets/test.vtt'
91+
}],
92+
type: 'video'
93+
}}
94+
onClose={action('close')}
95+
visible
96+
/>
97+
);

0 commit comments

Comments
 (0)