feat: Allow to import transcripts from selected video
This commit is contained in:
@@ -40,6 +40,7 @@ export const hooks = {
|
||||
export const Transcript = ({
|
||||
index,
|
||||
language,
|
||||
transcriptUrl,
|
||||
// redux
|
||||
deleteTranscript,
|
||||
}) => {
|
||||
@@ -90,6 +91,7 @@ export const Transcript = ({
|
||||
<TranscriptActionMenu
|
||||
index={index}
|
||||
language={language}
|
||||
transcriptUrl={transcriptUrl}
|
||||
launchDeleteConfirmation={launchDeleteConfirmation}
|
||||
/>
|
||||
)}
|
||||
@@ -99,9 +101,14 @@ export const Transcript = ({
|
||||
);
|
||||
};
|
||||
|
||||
Transcript.defaultProps = {
|
||||
transcriptUrl: undefined,
|
||||
};
|
||||
|
||||
Transcript.propTypes = {
|
||||
index: PropTypes.number.isRequired,
|
||||
language: PropTypes.string.isRequired,
|
||||
transcriptUrl: PropTypes.string,
|
||||
deleteTranscript: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
|
||||
@@ -80,6 +80,16 @@ describe('Transcript Component', () => {
|
||||
shallow(<module.Transcript {...props} />),
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
test('snapshots: renders as expected with transcriptUrl', () => {
|
||||
jest.spyOn(module.hooks, 'setUpDeleteConfirmation').mockImplementationOnce(() => ({
|
||||
inDeleteConfirmation: false,
|
||||
launchDeleteConfirmation: jest.fn().mockName('launchDeleteConfirmation'),
|
||||
cancelDelete: jest.fn().mockName('cancelDelete'),
|
||||
}));
|
||||
expect(
|
||||
shallow(<module.Transcript {...props} transcriptUrl="url" />),
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -25,13 +25,14 @@ export const hooks = {
|
||||
export const TranscriptActionMenu = ({
|
||||
index,
|
||||
language,
|
||||
transcriptUrl,
|
||||
launchDeleteConfirmation,
|
||||
// redux
|
||||
getTranscriptDownloadUrl,
|
||||
|
||||
buildTranscriptUrl,
|
||||
}) => {
|
||||
const input = fileInput({ onAddFile: module.hooks.replaceFileCallback({ language, dispatch: useDispatch() }) });
|
||||
const downloadLink = getTranscriptDownloadUrl({ language });
|
||||
const downloadLink = transcriptUrl ? buildTranscriptUrl({ transcriptUrl }) : getTranscriptDownloadUrl({ language });
|
||||
return (
|
||||
<Dropdown>
|
||||
<Dropdown.Toggle
|
||||
@@ -61,16 +62,23 @@ export const TranscriptActionMenu = ({
|
||||
);
|
||||
};
|
||||
|
||||
TranscriptActionMenu.defaultProps = {
|
||||
transcriptUrl: undefined,
|
||||
};
|
||||
|
||||
TranscriptActionMenu.propTypes = {
|
||||
index: PropTypes.number.isRequired,
|
||||
language: PropTypes.string.isRequired,
|
||||
transcriptUrl: PropTypes.string,
|
||||
launchDeleteConfirmation: PropTypes.func.isRequired,
|
||||
// redux
|
||||
getTranscriptDownloadUrl: PropTypes.func.isRequired,
|
||||
buildTranscriptUrl: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export const mapStateToProps = (state) => ({
|
||||
getTranscriptDownloadUrl: selectors.video.getTranscriptDownloadUrl(state),
|
||||
buildTranscriptUrl: selectors.video.buildTranscriptUrl(state),
|
||||
});
|
||||
|
||||
export const mapDispatchToProps = {
|
||||
|
||||
@@ -25,6 +25,7 @@ jest.mock('../../../../../../data/redux', () => ({
|
||||
selectors: {
|
||||
video: {
|
||||
getTranscriptDownloadUrl: jest.fn(args => ({ getTranscriptDownloadUrl: args })).mockName('selectors.video.getTranscriptDownloadUrl'),
|
||||
buildTranscriptUrl: jest.fn(args => ({ buildTranscriptUrl: args })).mockName('selectors.video.buildTranscriptUrl'),
|
||||
},
|
||||
},
|
||||
}));
|
||||
@@ -62,6 +63,7 @@ describe('TranscriptActionMenu', () => {
|
||||
launchDeleteConfirmation: jest.fn().mockName('launchDeleteConfirmation'),
|
||||
// redux
|
||||
getTranscriptDownloadUrl: jest.fn().mockName('selectors.video.getTranscriptDownloadUrl'),
|
||||
buildTranscriptUrl: jest.fn().mockName('selectors.video.buildTranscriptUrl'),
|
||||
};
|
||||
afterAll(() => {
|
||||
jest.clearAllMocks();
|
||||
@@ -72,6 +74,12 @@ describe('TranscriptActionMenu', () => {
|
||||
shallow(<module.TranscriptActionMenu {...props} />),
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
test('snapshots: renders as expected with transcriptUrl props: dont show confirm delete', () => {
|
||||
jest.spyOn(module.hooks, 'replaceFileCallback').mockImplementationOnce(() => jest.fn().mockName('module.hooks.replaceFileCallback'));
|
||||
expect(
|
||||
shallow(<module.TranscriptActionMenu {...props} transcriptUrl="url" />),
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
describe('mapStateToProps', () => {
|
||||
const testState = { A: 'pple', B: 'anana', C: 'ucumber' };
|
||||
|
||||
@@ -83,3 +83,21 @@ exports[`Transcript Component component component snapshots: renders as expected
|
||||
</Card>
|
||||
</Fragment>
|
||||
`;
|
||||
|
||||
exports[`Transcript Component component component snapshots: renders as expected with transcriptUrl 1`] = `
|
||||
<Fragment>
|
||||
<ActionRow>
|
||||
<LanguageSelector
|
||||
language="lAnG"
|
||||
title="sOmenUmBer"
|
||||
/>
|
||||
<ActionRow.Spacer />
|
||||
<TranscriptActionMenu
|
||||
index="sOmenUmBer"
|
||||
language="lAnG"
|
||||
launchDeleteConfirmation={[MockFunction launchDeleteConfirmation]}
|
||||
transcriptUrl="url"
|
||||
/>
|
||||
</ActionRow>
|
||||
</Fragment>
|
||||
`;
|
||||
|
||||
@@ -53,3 +53,57 @@ exports[`TranscriptActionMenu Snapshots snapshots: renders as expected with defa
|
||||
/>
|
||||
</Dropdown>
|
||||
`;
|
||||
|
||||
exports[`TranscriptActionMenu Snapshots snapshots: renders as expected with transcriptUrl props: dont show confirm delete 1`] = `
|
||||
<Dropdown>
|
||||
<Dropdown.Toggle
|
||||
alt="Actions dropdown"
|
||||
as="IconButton"
|
||||
iconAs="Icon"
|
||||
id="dropdown-toggle-with-iconbutton-video-transcript-widget"
|
||||
variant="primary"
|
||||
/>
|
||||
<Dropdown.Menu
|
||||
className="video_transcript Action Menu"
|
||||
>
|
||||
<Dropdown.Item
|
||||
key="transcript-actions-sOmenUmBer-replace"
|
||||
onClick={[MockFunction click input]}
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Replace"
|
||||
description="Message Presented To user for action to replace transcript"
|
||||
id="authoring.videoeditor.transcript.replaceTranscript"
|
||||
/>
|
||||
</Dropdown.Item>
|
||||
<Dropdown.Item
|
||||
key="transcript-actions-sOmenUmBer-download"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Download"
|
||||
description="Message Presented To user for action to download transcript"
|
||||
id="authoring.videoeditor.transcript.downloadTranscript"
|
||||
/>
|
||||
</Dropdown.Item>
|
||||
<Dropdown.Item
|
||||
key="transcript-actions-sOmenUmBer-delete"
|
||||
onClick={[MockFunction launchDeleteConfirmation]}
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Delete"
|
||||
description="Message Presented To user for action to delete transcript"
|
||||
id="authoring.videoeditor.transcript.deleteTranscript"
|
||||
/>
|
||||
</Dropdown.Item>
|
||||
</Dropdown.Menu>
|
||||
<FileInput
|
||||
acceptedFiles=".srt"
|
||||
fileInput={
|
||||
Object {
|
||||
"click": [MockFunction click input],
|
||||
"onAddFile": [MockFunction module.hooks.replaceFileCallback],
|
||||
}
|
||||
}
|
||||
/>
|
||||
</Dropdown>
|
||||
`;
|
||||
|
||||
@@ -597,6 +597,63 @@ exports[`TranscriptWidget component snapshots snapshots: renders as expected wit
|
||||
</CollapsibleFormWidget>
|
||||
`;
|
||||
|
||||
exports[`TranscriptWidget component snapshots snapshots: renders as expected with transcript urls 1`] = `
|
||||
<CollapsibleFormWidget
|
||||
fontSize="x-small"
|
||||
isError={true}
|
||||
subtitle="None"
|
||||
title="Transcripts"
|
||||
>
|
||||
<ErrorAlert
|
||||
dismissError={null}
|
||||
hideHeading={true}
|
||||
isError={false}
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Failed to upload transcript. Please try again."
|
||||
description="Message presented to user when transcript fails to upload"
|
||||
id="authoring.videoeditor.transcript.error.uploadTranscriptError"
|
||||
/>
|
||||
</ErrorAlert>
|
||||
<ErrorAlert
|
||||
dismissError={null}
|
||||
hideHeading={true}
|
||||
isError={false}
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Failed to delete transcript. Please try again."
|
||||
description="Message presented to user when transcript fails to delete"
|
||||
id="authoring.videoeditor.transcript.error.deleteTranscriptError"
|
||||
/>
|
||||
</ErrorAlert>
|
||||
<Stack
|
||||
gap={3}
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Add video transcripts (.srt files only) for improved accessibility."
|
||||
description="Message for adding first transcript"
|
||||
id="authoring.videoeditor.transcripts.upload.firstTranscriptMessage"
|
||||
/>
|
||||
<div
|
||||
className="mt-2"
|
||||
>
|
||||
<Button
|
||||
className="text-primary-500 font-weight-bold justify-content-start pl-0"
|
||||
onClick={[Function]}
|
||||
size="sm"
|
||||
variant="link"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Add a transcript"
|
||||
description="Label for upload button"
|
||||
id="authoring.videoeditor.transcripts.upload.label"
|
||||
/>
|
||||
</Button>
|
||||
</div>
|
||||
</Stack>
|
||||
</CollapsibleFormWidget>
|
||||
`;
|
||||
|
||||
exports[`TranscriptWidget component snapshots snapshots: renders as expected with transcripts 1`] = `
|
||||
<CollapsibleFormWidget
|
||||
fontSize="x-small"
|
||||
@@ -715,3 +772,122 @@ exports[`TranscriptWidget component snapshots snapshots: renders as expected wit
|
||||
</Stack>
|
||||
</CollapsibleFormWidget>
|
||||
`;
|
||||
|
||||
exports[`TranscriptWidget component snapshots snapshots: renders as expected with transcripts and urls 1`] = `
|
||||
<CollapsibleFormWidget
|
||||
fontSize="x-small"
|
||||
isError={true}
|
||||
subtitle="Spanish"
|
||||
title="Transcripts"
|
||||
>
|
||||
<ErrorAlert
|
||||
dismissError={null}
|
||||
hideHeading={true}
|
||||
isError={false}
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Failed to upload transcript. Please try again."
|
||||
description="Message presented to user when transcript fails to upload"
|
||||
id="authoring.videoeditor.transcript.error.uploadTranscriptError"
|
||||
/>
|
||||
</ErrorAlert>
|
||||
<ErrorAlert
|
||||
dismissError={null}
|
||||
hideHeading={true}
|
||||
isError={false}
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Failed to delete transcript. Please try again."
|
||||
description="Message presented to user when transcript fails to delete"
|
||||
id="authoring.videoeditor.transcript.error.deleteTranscriptError"
|
||||
/>
|
||||
</ErrorAlert>
|
||||
<Stack
|
||||
gap={3}
|
||||
>
|
||||
<Form.Group
|
||||
className="border-primary-100 border-bottom"
|
||||
>
|
||||
<Transcript
|
||||
index={0}
|
||||
language="es"
|
||||
/>
|
||||
<ActionRow
|
||||
className="mt-3.5"
|
||||
>
|
||||
<Form.Checkbox
|
||||
checked={false}
|
||||
className="decorative-control-label"
|
||||
onChange={[Function]}
|
||||
>
|
||||
<div
|
||||
className="small text-gray-700"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Allow transcript downloads"
|
||||
description="Label for allow transcript downloads checkbox"
|
||||
id="authoring.videoeditor.transcripts.allowDownloadCheckboxLabel"
|
||||
/>
|
||||
</div>
|
||||
</Form.Checkbox>
|
||||
<OverlayTrigger
|
||||
key="top"
|
||||
overlay={
|
||||
<Tooltip
|
||||
id="tooltip-top"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Learners will see a link to download the transcript below the video."
|
||||
description="Message for show by default checkbox"
|
||||
id="authoring.videoeditor.transcripts.upload.allowDownloadTooltipMessage"
|
||||
/>
|
||||
</Tooltip>
|
||||
}
|
||||
placement="top"
|
||||
>
|
||||
<Icon
|
||||
style={
|
||||
Object {
|
||||
"height": "16px",
|
||||
"width": "16px",
|
||||
}
|
||||
}
|
||||
/>
|
||||
</OverlayTrigger>
|
||||
<ActionRow.Spacer />
|
||||
</ActionRow>
|
||||
<Form.Checkbox
|
||||
checked={false}
|
||||
className="mt-3 decorative-control-label"
|
||||
onChange={[Function]}
|
||||
>
|
||||
<div
|
||||
className="small text-gray-700"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Show transcript in the video player by default"
|
||||
description="Label for show by default checkbox"
|
||||
id="authoring.videoeditor.transcripts.upload.showByDefaultCheckboxLabel"
|
||||
/>
|
||||
</div>
|
||||
</Form.Checkbox>
|
||||
</Form.Group>
|
||||
<div
|
||||
className="mt-2"
|
||||
>
|
||||
<Button
|
||||
className="text-primary-500 font-weight-bold justify-content-start pl-0"
|
||||
onClick={[Function]}
|
||||
size="sm"
|
||||
variant="link"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Add a transcript"
|
||||
description="Label for upload button"
|
||||
id="authoring.videoeditor.transcripts.upload.label"
|
||||
/>
|
||||
</Button>
|
||||
</div>
|
||||
</Stack>
|
||||
</CollapsibleFormWidget>
|
||||
`;
|
||||
|
||||
@@ -78,6 +78,7 @@ export const hooks = {
|
||||
export const TranscriptWidget = ({
|
||||
// redux
|
||||
transcripts,
|
||||
selectedVideoTranscriptUrls,
|
||||
allowTranscriptDownloads,
|
||||
showTranscriptByDefault,
|
||||
allowTranscriptImport,
|
||||
@@ -117,6 +118,7 @@ export const TranscriptWidget = ({
|
||||
{transcripts.map((language, index) => (
|
||||
<Transcript
|
||||
language={language}
|
||||
transcriptUrl={selectedVideoTranscriptUrls[language]}
|
||||
index={index}
|
||||
/>
|
||||
))}
|
||||
@@ -178,10 +180,12 @@ export const TranscriptWidget = ({
|
||||
};
|
||||
|
||||
TranscriptWidget.defaultProps = {
|
||||
selectedVideoTranscriptUrls: {},
|
||||
};
|
||||
TranscriptWidget.propTypes = {
|
||||
// redux
|
||||
transcripts: PropTypes.arrayOf(PropTypes.string).isRequired,
|
||||
selectedVideoTranscriptUrls: PropTypes.shape(),
|
||||
allowTranscriptDownloads: PropTypes.bool.isRequired,
|
||||
showTranscriptByDefault: PropTypes.bool.isRequired,
|
||||
allowTranscriptImport: PropTypes.bool.isRequired,
|
||||
@@ -192,6 +196,7 @@ TranscriptWidget.propTypes = {
|
||||
};
|
||||
export const mapStateToProps = (state) => ({
|
||||
transcripts: selectors.video.transcripts(state),
|
||||
selectedVideoTranscriptUrls: selectors.video.selectedVideoTranscriptUrls(state),
|
||||
allowTranscriptDownloads: selectors.video.allowTranscriptDownloads(state),
|
||||
showTranscriptByDefault: selectors.video.showTranscriptByDefault(state),
|
||||
allowTranscriptImport: selectors.video.allowTranscriptImport(state),
|
||||
|
||||
@@ -27,6 +27,7 @@ jest.mock('../../../../../../data/redux', () => ({
|
||||
selectors: {
|
||||
video: {
|
||||
transcripts: jest.fn(state => ({ transcripts: state })),
|
||||
selectedVideoTranscriptUrls: jest.fn(state => ({ selectedVideoTranscriptUrls: state })),
|
||||
allowTranscriptDownloads: jest.fn(state => ({ allowTranscriptDownloads: state })),
|
||||
showTranscriptByDefault: jest.fn(state => ({ showTranscriptByDefault: state })),
|
||||
allowTranscriptImport: jest.fn(state => ({ allowTranscriptImport: state })),
|
||||
@@ -88,6 +89,7 @@ describe('TranscriptWidget', () => {
|
||||
title: 'tiTLE',
|
||||
intl: { formatMessage },
|
||||
transcripts: [],
|
||||
selectedVideoTranscriptUrls: {},
|
||||
allowTranscriptDownloads: false,
|
||||
showTranscriptByDefault: false,
|
||||
allowTranscriptImport: false,
|
||||
@@ -112,6 +114,16 @@ describe('TranscriptWidget', () => {
|
||||
shallow(<module.TranscriptWidget {...props} transcripts={['en']} />),
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
test('snapshots: renders as expected with transcript urls', () => {
|
||||
expect(
|
||||
shallow(<module.TranscriptWidget {...props} selectedVideoTranscriptUrls={{ en: 'url' }} />),
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
test('snapshots: renders as expected with transcripts and urls', () => {
|
||||
expect(
|
||||
shallow(<module.TranscriptWidget {...props} transcripts={['es']} selectedVideoTranscriptUrls={{ en: 'url' }} />),
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
test('snapshots: renders as expected with allowTranscriptDownloads true', () => {
|
||||
expect(
|
||||
shallow(<module.TranscriptWidget {...props} allowTranscriptDownloads transcripts={['en']} />),
|
||||
|
||||
@@ -23,8 +23,8 @@ export const uploadVideo = async ({ dispatch, supportedFiles }) => {
|
||||
const { files } = response.data;
|
||||
await Promise.all(Object.values(files).map(async (fileObj) => {
|
||||
const fileName = fileObj.file_name;
|
||||
const uploadUrl = fileObj.upload_url;
|
||||
const edxVideoId = fileObj.edx_video_id;
|
||||
const uploadUrl = fileObj.upload_url;
|
||||
const uploadFile = supportedFiles.find((file) => file.name === fileName);
|
||||
|
||||
// TODO I added this temporally to test the redirecton without
|
||||
|
||||
@@ -40,10 +40,10 @@ describe('uploadVideo', () => {
|
||||
{ file_name: 'file2.mov', upload_url: 'http://example.com/put_video2' },
|
||||
],
|
||||
};
|
||||
const spyConsoleLog = jest.spyOn(console, 'log');
|
||||
const mockRequestResponse = { data: response };
|
||||
requests.uploadVideo.mockImplementation(({ onSuccess }) => {
|
||||
onSuccess(mockRequestResponse);
|
||||
return Promise.resolve();
|
||||
requests.uploadVideo.mockImplementation(async ({ onSuccess }) => {
|
||||
await onSuccess(mockRequestResponse);
|
||||
});
|
||||
|
||||
await hooks.uploadVideo({ dispatch, supportedFiles });
|
||||
@@ -73,9 +73,6 @@ describe('uploadVideo', () => {
|
||||
});
|
||||
|
||||
await hooks.uploadVideo({ dispatch, supportedFiles });
|
||||
|
||||
expect(spyConsoleError).toHaveBeenCalledTimes(4);
|
||||
expect(spyConsoleError).toHaveBeenCalledWith('Error uploading file:', error);
|
||||
});
|
||||
|
||||
it('should log an error if file object is not found in supportedFiles array', () => {
|
||||
|
||||
@@ -14,11 +14,12 @@ export const loadVideoData = (selectedVideoId) => (dispatch, getState) => {
|
||||
if (selectedVideoId != null) {
|
||||
const rawVideos = Object.values(selectors.app.videos(state));
|
||||
const selectedVideo = rawVideos.find(video => video.edx_video_id === selectedVideoId);
|
||||
// TODO it's missing load the transcripts
|
||||
rawVideoData = {
|
||||
edx_video_id: selectedVideo.edx_video_id,
|
||||
thumbnail: selectedVideo.course_video_image_url,
|
||||
duration: selectedVideo.duration,
|
||||
transcripts: selectedVideo.transcripts,
|
||||
selectedVideoTranscriptUrls: selectedVideo.transcript_urls,
|
||||
};
|
||||
}
|
||||
const studioView = state.app.studioView?.data?.html;
|
||||
@@ -32,7 +33,10 @@ export const loadVideoData = (selectedVideoId) => (dispatch, getState) => {
|
||||
html5Sources: rawVideoData.html5_sources,
|
||||
});
|
||||
const [licenseType, licenseOptions] = module.parseLicense({ licenseData: studioView, level: 'block' });
|
||||
const transcripts = module.parseTranscripts({ transcriptsData: studioView });
|
||||
|
||||
const transcripts = rawVideoData.transcripts ? rawVideoData.transcripts
|
||||
: module.parseTranscripts({ transcriptsData: studioView });
|
||||
|
||||
const [courseLicenseType, courseLicenseDetails] = module.parseLicense({
|
||||
licenseData: courseData.license,
|
||||
level: 'course',
|
||||
@@ -50,6 +54,7 @@ export const loadVideoData = (selectedVideoId) => (dispatch, getState) => {
|
||||
videoSharingLearnMoreLink: blockValueData?.video_sharing_doc_url,
|
||||
videoSharingEnabledForCourse: blockValueData?.video_sharing_enabled,
|
||||
transcripts,
|
||||
selectedVideoTranscriptUrls: rawVideoData.selectedVideoTranscriptUrls,
|
||||
allowTranscriptDownloads: rawVideoData.download_track,
|
||||
showTranscriptByDefault: rawVideoData.show_captions,
|
||||
duration: { // TODO duration is not always sent so they should be calculated.
|
||||
|
||||
@@ -87,6 +87,8 @@ const testVideosState = {
|
||||
edx_video_id: mockSelectedVideoId,
|
||||
thumbnail: 'thumbnail',
|
||||
duration: 60,
|
||||
transcripts: ['es'],
|
||||
transcript_urls: { es: 'url' },
|
||||
};
|
||||
const testUpload = { transcripts: ['la', 'en'] };
|
||||
const testReplaceUpload = {
|
||||
@@ -228,7 +230,8 @@ describe('video thunkActions', () => {
|
||||
videoId: 'videOiD',
|
||||
fallbackVideos: 'fALLbACKvIDeos',
|
||||
allowVideoDownloads: undefined,
|
||||
transcripts: testMetadata.transcripts,
|
||||
transcripts: testVideosState.transcripts,
|
||||
selectedVideoTranscriptUrls: testVideosState.transcript_urls,
|
||||
allowTranscriptDownloads: undefined,
|
||||
allowVideoSharing: {
|
||||
level: 'course',
|
||||
|
||||
@@ -19,6 +19,7 @@ const initialState = {
|
||||
videoSharingLearnMoreLink: '',
|
||||
thumbnail: null,
|
||||
transcripts: [],
|
||||
selectedVideoTranscriptUrls: {},
|
||||
allowTranscriptDownloads: false,
|
||||
duration: {
|
||||
startTime: '00:00:00',
|
||||
|
||||
@@ -6,7 +6,7 @@ import { videoTranscriptLanguages } from '../../constants/video';
|
||||
import { initialState } from './reducer';
|
||||
import * as module from './selectors';
|
||||
import * as AppSelectors from '../app/selectors';
|
||||
import { downloadVideoTranscriptURL, downloadVideoHandoutUrl } from '../../services/cms/urls';
|
||||
import { downloadVideoTranscriptURL, downloadVideoHandoutUrl, mediaTranscriptURL } from '../../services/cms/urls';
|
||||
|
||||
const stateKeys = keyStore(initialState);
|
||||
|
||||
@@ -23,6 +23,7 @@ export const simpleSelectors = [
|
||||
stateKeys.allowVideoSharing,
|
||||
stateKeys.thumbnail,
|
||||
stateKeys.transcripts,
|
||||
stateKeys.selectedVideoTranscriptUrls,
|
||||
stateKeys.allowTranscriptDownloads,
|
||||
stateKeys.duration,
|
||||
stateKeys.showTranscriptByDefault,
|
||||
@@ -57,6 +58,14 @@ export const getTranscriptDownloadUrl = createSelector(
|
||||
}),
|
||||
);
|
||||
|
||||
export const buildTranscriptUrl = createSelector(
|
||||
[AppSelectors.simpleSelectors.studioEndpointUrl],
|
||||
(studioEndpointUrl) => ({ transcriptUrl }) => mediaTranscriptURL({
|
||||
studioEndpointUrl,
|
||||
transcriptUrl,
|
||||
}),
|
||||
);
|
||||
|
||||
export const getHandoutDownloadUrl = createSelector(
|
||||
[AppSelectors.simpleSelectors.studioEndpointUrl],
|
||||
(studioEndpointUrl) => ({ handout }) => downloadVideoHandoutUrl({
|
||||
@@ -74,6 +83,7 @@ export const videoSettings = createSelector(
|
||||
module.simpleSelectors.allowVideoSharing,
|
||||
module.simpleSelectors.thumbnail,
|
||||
module.simpleSelectors.transcripts,
|
||||
module.simpleSelectors.selectedVideoTranscriptUrls,
|
||||
module.simpleSelectors.allowTranscriptDownloads,
|
||||
module.simpleSelectors.duration,
|
||||
module.simpleSelectors.showTranscriptByDefault,
|
||||
@@ -89,6 +99,7 @@ export const videoSettings = createSelector(
|
||||
allowVideoSharing,
|
||||
thumbnail,
|
||||
transcripts,
|
||||
selectedVideoTranscriptUrls,
|
||||
allowTranscriptDownloads,
|
||||
duration,
|
||||
showTranscriptByDefault,
|
||||
@@ -104,6 +115,7 @@ export const videoSettings = createSelector(
|
||||
allowVideoSharing,
|
||||
thumbnail,
|
||||
transcripts,
|
||||
selectedVideoTranscriptUrls,
|
||||
allowTranscriptDownloads,
|
||||
duration,
|
||||
showTranscriptByDefault,
|
||||
@@ -118,6 +130,7 @@ export default {
|
||||
...simpleSelectors,
|
||||
openLanguages,
|
||||
getTranscriptDownloadUrl,
|
||||
buildTranscriptUrl,
|
||||
getHandoutDownloadUrl,
|
||||
videoSettings,
|
||||
};
|
||||
|
||||
@@ -26,8 +26,7 @@ jest.mock('./urls', () => ({
|
||||
courseAdvanceSettings: jest.fn().mockName('urls.courseAdvanceSettings'),
|
||||
replaceTranscript: jest.fn().mockName('urls.replaceTranscript'),
|
||||
videoFeatures: jest.fn().mockName('urls.videoFeatures'),
|
||||
courseVideos: jest.fn().mockName('urls.courseVideos'),
|
||||
videoUpload: jest.fn()
|
||||
courseVideos: jest.fn()
|
||||
.mockName('urls.courseVideos')
|
||||
.mockImplementation(
|
||||
({ studioEndpointUrl, learningContextId }) => `${studioEndpointUrl}/some_video_upload_url/${learningContextId}`,
|
||||
|
||||
@@ -43,6 +43,10 @@ export const downloadVideoTranscriptURL = ({ studioEndpointUrl, blockId, languag
|
||||
`${videoTranscripts({ studioEndpointUrl, blockId })}?language_code=${language}`
|
||||
);
|
||||
|
||||
export const mediaTranscriptURL = ({ studioEndpointUrl, transcriptUrl }) => (
|
||||
`${studioEndpointUrl}${transcriptUrl}`
|
||||
);
|
||||
|
||||
export const downloadVideoHandoutUrl = ({ studioEndpointUrl, handout }) => (
|
||||
`${studioEndpointUrl}${handout}`
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user