diff --git a/cms/djangoapps/contentstore/views/tests/test_videos.py b/cms/djangoapps/contentstore/views/tests/test_videos.py index 45a41c3ca2..de95256a6d 100644 --- a/cms/djangoapps/contentstore/views/tests/test_videos.py +++ b/cms/djangoapps/contentstore/views/tests/test_videos.py @@ -15,7 +15,13 @@ import pytz from django.conf import settings from django.core.files.uploadedfile import UploadedFile from django.test.utils import override_settings -from edxval.api import create_profile, create_video, get_video_info, get_course_video_image_url +from edxval.api import ( + create_profile, + create_video, + get_video_info, + get_course_video_image_url, + create_or_update_video_transcript +) from mock import Mock, patch from contentstore.models import VideoUploadConfig @@ -228,6 +234,78 @@ class VideosHandlerTestCase(VideoUploadTestMixin, CourseTestCase): convert_video_status(original_video) ) + @ddt.data( + ( + False, + ['edx_video_id', 'client_video_id', 'created', 'duration', 'status', 'course_video_image_url'], + [], + {} + ), + ( + True, + ['edx_video_id', 'client_video_id', 'created', 'duration', 'status', 'course_video_image_url', + 'transcripts'], + [ + { + 'video_id': 'test1', + 'language_code': 'en', + 'file_name': 'edx101.srt', + 'file_format': 'srt', + 'provider': 'Cielo24' + } + ], + { + 'en': 'English' + } + ), + ( + True, + ['edx_video_id', 'client_video_id', 'created', 'duration', 'status', 'course_video_image_url', + 'transcripts'], + [ + { + 'video_id': 'test1', + 'language_code': 'en', + 'file_name': 'edx101_en.srt', + 'file_format': 'srt', + 'provider': 'Cielo24' + }, + { + 'video_id': 'test1', + 'language_code': 'es', + 'file_name': 'edx101_es.srt', + 'file_format': 'srt', + 'provider': 'Cielo24' + } + ], + { + 'en': 'English', + 'es': 'Spanish' + } + ) + ) + @ddt.unpack + @patch('openedx.core.djangoapps.video_config.models.VideoTranscriptEnabledFlag.feature_enabled') + def test_get_json_transcripts(self, is_video_transcript_enabled, expected_video_keys, uploaded_transcripts, + expected_transcripts, video_transcript_feature): + """ + Test that transcripts are attached based on video transcript feature enablement. + """ + video_transcript_feature.return_value = is_video_transcript_enabled + + for transcript in uploaded_transcripts: + create_or_update_video_transcript(**transcript) + + response = self.client.get_json(self.url) + self.assertEqual(response.status_code, 200) + response_videos = json.loads(response.content)['videos'] + self.assertEqual(len(response_videos), len(self.previous_uploads)) + + for response_video in response_videos: + self.assertEqual(set(response_video.keys()), set(expected_video_keys)) + if response_video['edx_video_id'] == self.previous_uploads[0]['edx_video_id']: + self.assertDictEqual(response_video.get('transcripts', {}), expected_transcripts) + def test_get_html(self): response = self.client.get(self.url) self.assertEqual(response.status_code, 200) diff --git a/cms/djangoapps/contentstore/views/videos.py b/cms/djangoapps/contentstore/views/videos.py index 32070ea09d..62302f9e91 100644 --- a/cms/djangoapps/contentstore/views/videos.py +++ b/cms/djangoapps/contentstore/views/videos.py @@ -30,7 +30,8 @@ from edxval.api import ( remove_transcript_preferences, remove_video_for_course, update_video_image, - update_video_status + update_video_status, + get_available_transcript_languages ) from opaque_keys.edx.keys import CourseKey @@ -72,6 +73,9 @@ VIDEO_UPLOAD_MAX_FILE_SIZE_GB = 5 # maximum time for video to remain in upload state MAX_UPLOAD_HOURS = 24 +# Transcript download format +TRANSCRIPT_DOWNLOAD_FILE_FORMAT = 'srt' + class TranscriptProvider(object): """ @@ -528,12 +532,20 @@ def _get_videos(course): """ Retrieves the list of videos from VAL corresponding to this course. """ + is_video_transcript_enabled = VideoTranscriptEnabledFlag.feature_enabled(course.id) videos = list(get_videos_for_course(unicode(course.id), VideoSortField.created, SortDirection.desc)) # convert VAL's status to studio's Video Upload feature status. for video in videos: video["status"] = convert_video_status(video) + if is_video_transcript_enabled: + all_languages = get_all_transcript_languages() + video['transcripts'] = { + lang_code: all_languages[lang_code] + for lang_code in get_available_transcript_languages([video['edx_video_id']]) + } + return videos @@ -551,6 +563,9 @@ def _get_index_videos(course): course_id = unicode(course.id) attrs = ['edx_video_id', 'client_video_id', 'created', 'duration', 'status', 'courses'] + if VideoTranscriptEnabledFlag.feature_enabled(course.id): + attrs += ['transcripts'] + def _get_values(video): """ Get data for predefined video attributes. @@ -570,6 +585,24 @@ def _get_index_videos(course): ] +def get_all_transcript_languages(): + """ + Returns all possible languages for transcript. + """ + third_party_transcription_languages = {} + transcription_plans = get_3rd_party_transcription_plans() + cielo_fidelity = transcription_plans[TranscriptProvider.CIELO24]['fidelity'] + + # Get third party transcription languages. + third_party_transcription_languages.update(transcription_plans[TranscriptProvider.THREE_PLAY_MEDIA]['languages']) + third_party_transcription_languages.update(cielo_fidelity['MECHANICAL']['languages']) + third_party_transcription_languages.update(cielo_fidelity['PREMIUM']['languages']) + third_party_transcription_languages.update(cielo_fidelity['PROFESSIONAL']['languages']) + + # Return combined system settings and 3rd party transcript languages. + return dict(settings.ALL_LANGUAGES, **third_party_transcription_languages) + + def videos_index_html(course): """ Returns an HTML page to display previous video uploads and allow new ones @@ -596,7 +629,8 @@ def videos_index_html(course): 'is_video_transcript_enabled': is_video_transcript_enabled, 'video_transcript_settings': None, 'active_transcript_preferences': None, - 'transcript_credentials': None + 'transcript_credentials': None, + 'transcript_available_languages': None } if is_video_transcript_enabled: @@ -610,10 +644,12 @@ def videos_index_html(course): unicode(course.id) ), 'transcription_plans': get_3rd_party_transcription_plans(), + 'trancript_download_file_format': TRANSCRIPT_DOWNLOAD_FILE_FORMAT } context['active_transcript_preferences'] = get_transcript_preferences(unicode(course.id)) # Cached state for transcript providers' credentials (org-specific) context['transcript_credentials'] = get_transcript_credentials_state_for_org(course.id.org) + context['transcript_available_languages'] = get_all_transcript_languages() return render_to_response('videos_index.html', context) diff --git a/cms/static/cms/js/spec/main.js b/cms/static/cms/js/spec/main.js index e7b09c1ce0..af109088aa 100644 --- a/cms/static/cms/js/spec/main.js +++ b/cms/static/cms/js/spec/main.js @@ -259,6 +259,7 @@ 'js/spec/views/previous_video_upload_spec', 'js/spec/views/video_thumbnail_spec', 'js/spec/views/course_video_settings_spec', + 'js/spec/views/video_transcripts_spec', 'js/spec/views/previous_video_upload_list_spec', 'js/spec/views/assets_spec', 'js/spec/views/baseview_spec', diff --git a/cms/static/js/factories/videos_index.js b/cms/static/js/factories/videos_index.js index 1ad46cada1..2852b74e89 100644 --- a/cms/static/js/factories/videos_index.js +++ b/cms/static/js/factories/videos_index.js @@ -18,7 +18,8 @@ define([ transcriptOrganizationCredentials, videoTranscriptSettings, isVideoTranscriptEnabled, - videoImageSettings + videoImageSettings, + transcriptAvailableLanguages ) { var activeView = new ActiveVideoUploadListView({ postUrl: videoHandlerUrl, @@ -51,7 +52,11 @@ define([ videoHandlerUrl: videoHandlerUrl, collection: updatedCollection, encodingsDownloadUrl: encodingsDownloadUrl, - videoImageSettings: videoImageSettings + videoImageSettings: videoImageSettings, + videoTranscriptSettings: videoTranscriptSettings, + transcriptAvailableLanguages: transcriptAvailableLanguages, + videoSupportedFileFormats: videoSupportedFileFormats, + isVideoTranscriptEnabled: isVideoTranscriptEnabled }); $contentWrapper.find('.wrapper-assets').replaceWith(updatedView.render().$el); }); @@ -63,7 +68,11 @@ define([ videoHandlerUrl: videoHandlerUrl, collection: new Backbone.Collection(previousUploads), encodingsDownloadUrl: encodingsDownloadUrl, - videoImageSettings: videoImageSettings + videoImageSettings: videoImageSettings, + videoTranscriptSettings: videoTranscriptSettings, + transcriptAvailableLanguages: transcriptAvailableLanguages, + videoSupportedFileFormats: videoSupportedFileFormats, + isVideoTranscriptEnabled: isVideoTranscriptEnabled }); $contentWrapper.append(activeView.render().$el); $contentWrapper.append(previousView.render().$el); diff --git a/cms/static/js/spec/views/video_transcripts_spec.js b/cms/static/js/spec/views/video_transcripts_spec.js new file mode 100644 index 0000000000..ee21b0f2bb --- /dev/null +++ b/cms/static/js/spec/views/video_transcripts_spec.js @@ -0,0 +1,189 @@ +define( + ['jquery', 'underscore', 'backbone', 'js/views/video_transcripts', 'js/views/previous_video_upload_list', + 'common/js/spec_helpers/template_helpers'], + function($, _, Backbone, VideoTranscriptsView, PreviousVideoUploadListView, TemplateHelpers) { + 'use strict'; + describe('VideoTranscriptsView', function() { + var videoTranscriptsView, + renderView, + verifyTranscriptActions, + transcripts = { + en: 'English', + es: 'Spanish', + ur: 'Urdu' + }, + edxVideoID = 'test-edx-video-id', + clientVideoID = 'Video client title name.mp4', + transcriptAvailableLanguages = { + en: 'English', + es: 'Spanish', + cn: 'Chinese', + ar: 'Arabic', + ur: 'Urdu' + }, + TRANSCRIPT_DOWNLOAD_FILE_FORMAT = 'srt', + videoSupportedFileFormats = ['.mov', '.mp4'], + videoTranscriptSettings = { + trancript_download_file_format: TRANSCRIPT_DOWNLOAD_FILE_FORMAT + }, + videoListView; + + verifyTranscriptActions = function($transcriptActionsEl, transcriptLanguage) { + var downloadTranscriptActionEl = $transcriptActionsEl.find('.download-transcript-button'), + uploadTranscriptActionEl = $transcriptActionsEl.find('.upload-transcript-button'); + + expect(downloadTranscriptActionEl.html().trim(), 'Download'); + expect(downloadTranscriptActionEl.attr('href'), '#'); + + expect(uploadTranscriptActionEl.html().trim(), 'Upload'); + expect(uploadTranscriptActionEl.data('edx-video-id'), edxVideoID); + expect(uploadTranscriptActionEl.data('language-code'), transcriptLanguage); + }; + + renderView = function(availableTranscripts, isVideoTranscriptEnabled) { + var videoViewIndex = 0, + isVideoTranscriptEnabled = isVideoTranscriptEnabled || _.isUndefined(isVideoTranscriptEnabled), // eslint-disable-line max-len, no-redeclare + videoData = { + client_video_id: clientVideoID, + edx_video_id: edxVideoID, + created: '2014-11-25T23:13:05', + transcripts: availableTranscripts + }, + videoCollection = new Backbone.Collection([new Backbone.Model(videoData)]); + + videoListView = new PreviousVideoUploadListView({ + collection: videoCollection, + videoImageSettings: {}, + videoTranscriptSettings: videoTranscriptSettings, + transcriptAvailableLanguages: transcriptAvailableLanguages, + videoSupportedFileFormats: videoSupportedFileFormats, + isVideoTranscriptEnabled: isVideoTranscriptEnabled + }); + videoListView.setElement($('.wrapper-assets')); + videoListView.render(); + + videoTranscriptsView = videoListView.itemViews[videoViewIndex].videoTranscriptsView; + }; + + beforeEach(function() { + setFixtures(''); + TemplateHelpers.installTemplate('previous-video-upload-list'); + renderView(transcripts); + }); + + it('renders as expected', function() { + // Verify transcript container is present. + expect(videoListView.$el.find('.show-video-transcripts-container')).toExist(); + // Veirfy transcript column header is present. + expect(videoListView.$el.find('.js-table-head .video-head-col.transcripts-col')).toExist(); + // Verify transcript data column is present. + expect(videoListView.$el.find('.js-table-body .transcripts-col')).toExist(); + // Verify view has initiallized. + expect(_.isUndefined(videoTranscriptsView)).toEqual(false); + }); + + it('does not render transcripts view if feature is disabled', function() { + renderView(transcripts, false); + // Verify transcript container is not present. + expect(videoListView.$el.find('.show-video-transcripts-container')).not.toExist(); + // Veirfy transcript column header is not present. + expect(videoListView.$el.find('.js-table-head .video-head-col.transcripts-col')).not.toExist(); + // Verify transcript data column is not present. + expect(videoListView.$el.find('.js-table-body .transcripts-col')).not.toExist(); + // Verify view has not initiallized. + expect(_.isUndefined(videoTranscriptsView)).toEqual(true); + }); + + it('does not show list of transcripts initially', function() { + expect( + videoTranscriptsView.$el.find('.show-video-transcripts-wrapper').hasClass('hidden') + ).toEqual(true); + expect(videoTranscriptsView.$el.find('.toggle-show-transcripts-button-text').html().trim()).toEqual( + 'Show transcripts (' + _.size(transcripts) + ')' + ); + }); + + it('shows list of transcripts when clicked on show transcript button', function() { + // Verify transcript container is hidden + expect( + videoTranscriptsView.$el.find('.show-video-transcripts-wrapper').hasClass('hidden') + ).toEqual(true); + + // Verify initial button text + expect(videoTranscriptsView.$el.find('.toggle-show-transcripts-button-text').html().trim()).toEqual( + 'Show transcripts (' + _.size(transcripts) + ')' + ); + videoTranscriptsView.$el.find('.toggle-show-transcripts-button').click(); + + // Verify transcript container is not hidden + expect( + videoTranscriptsView.$el.find('.show-video-transcripts-wrapper').hasClass('hidden') + ).toEqual(false); + + // Verify button text is changed. + expect(videoTranscriptsView.$el.find('.toggle-show-transcripts-button-text').html().trim()).toEqual( + 'Hide transcripts (' + _.size(transcripts) + ')' + ); + }); + + it('hides list of transcripts when clicked on hide transcripts button', function() { + // Click to show transcripts first. + videoTranscriptsView.$el.find('.toggle-show-transcripts-button').click(); + + // Verify button text. + expect(videoTranscriptsView.$el.find('.toggle-show-transcripts-button-text').html().trim()).toEqual( + 'Hide transcripts (' + _.size(transcripts) + ')' + ); + + // Verify transcript container is not hidden + expect( + videoTranscriptsView.$el.find('.show-video-transcripts-wrapper').hasClass('hidden') + ).toEqual(false); + + videoTranscriptsView.$el.find('.toggle-show-transcripts-button').click(); + + // Verify button text is changed. + expect(videoTranscriptsView.$el.find('.toggle-show-transcripts-button-text').html().trim()).toEqual( + 'Show transcripts (' + _.size(transcripts) + ')' + ); + + // Verify transcript container is hidden + expect( + videoTranscriptsView.$el.find('.show-video-transcripts-wrapper').hasClass('hidden') + ).toEqual(true); + }); + + it('renders appropriate text when no transcript is available', function() { + // Render view with no transcripts + renderView({}); + + // Verify appropriate text is shown + expect( + videoTranscriptsView.$el.find('.transcripts-empty-text').html() + ).toEqual('No transcript available yet.'); + }); + + it('renders correct transcript attributes', function() { + var $transcriptEl; + // Show transcripts + videoTranscriptsView.$el.find('.toggle-show-transcripts-button').click(); + expect(videoTranscriptsView.$el.find('.show-video-transcript-content').length).toEqual( + _.size(transcripts) + ); + + _.each(transcripts, function(langaugeText, languageCode) { + $transcriptEl = $(videoTranscriptsView.$el.find('#show-video-transcript-content-' + languageCode)); + // Verify correct transcript title is set. + expect($transcriptEl.find('.transcript-title').html()).toEqual( + 'Video client title n_' + languageCode + '.' + TRANSCRIPT_DOWNLOAD_FILE_FORMAT + ); + // Verify transcript language dropdown has correct value set. + expect($transcriptEl.find('.transcript-language-menu').val(), languageCode); + + // Verify transcript actions are rendered correctly. + verifyTranscriptActions($transcriptEl.find('.transcript-actions'), languageCode); + }); + }); + }); + } +); diff --git a/cms/static/js/views/previous_video_upload.js b/cms/static/js/views/previous_video_upload.js index 0e7c288f84..714e4e54ac 100644 --- a/cms/static/js/views/previous_video_upload.js +++ b/cms/static/js/views/previous_video_upload.js @@ -1,10 +1,10 @@ define( ['underscore', 'gettext', 'js/utils/date_utils', 'js/views/baseview', 'common/js/components/views/feedback_prompt', - 'common/js/components/views/feedback_notification', 'js/views/video_thumbnail', + 'common/js/components/views/feedback_notification', 'js/views/video_thumbnail', 'js/views/video_transcripts', 'common/js/components/utils/view_utils', 'edx-ui-toolkit/js/utils/html-utils', 'text!templates/previous-video-upload.underscore'], - function(_, gettext, DateUtils, BaseView, PromptView, NotificationView, VideoThumbnailView, ViewUtils, HtmlUtils, - previousVideoUploadTemplate) { + function(_, gettext, DateUtils, BaseView, PromptView, NotificationView, VideoThumbnailView, VideoTranscriptsView, + ViewUtils, HtmlUtils, previousVideoUploadTemplate) { 'use strict'; var PreviousVideoUploadView = BaseView.extend({ @@ -20,6 +20,7 @@ define( this.template = HtmlUtils.template(previousVideoUploadTemplate); this.videoHandlerUrl = options.videoHandlerUrl; this.videoImageUploadEnabled = options.videoImageSettings.video_image_upload_enabled; + this.isVideoTranscriptEnabled = options.isVideoTranscriptEnabled; if (this.videoImageUploadEnabled) { this.videoThumbnailView = new VideoThumbnailView({ @@ -29,11 +30,22 @@ define( videoImageSettings: options.videoImageSettings }); } + if (this.isVideoTranscriptEnabled) { + this.videoTranscriptsView = new VideoTranscriptsView({ + transcripts: this.model.get('transcripts'), + edxVideoID: this.model.get('edx_video_id'), + clientVideoID: this.model.get('client_video_id'), + transcriptAvailableLanguages: options.transcriptAvailableLanguages, + videoSupportedFileFormats: options.videoSupportedFileFormats, + videoTranscriptSettings: options.videoTranscriptSettings + }); + } }, render: function() { var renderedAttributes = { videoImageUploadEnabled: this.videoImageUploadEnabled, + isVideoTranscriptEnabled: this.isVideoTranscriptEnabled, created: DateUtils.renderDate(this.model.get('created')), status: this.model.get('status') }; @@ -47,6 +59,9 @@ define( if (this.videoImageUploadEnabled) { this.videoThumbnailView.setElement(this.$('.thumbnail-col')).render(); } + if (this.isVideoTranscriptEnabled) { + this.videoTranscriptsView.setElement(this.$('.transcripts-col')).render(); + } return this; }, diff --git a/cms/static/js/views/previous_video_upload_list.js b/cms/static/js/views/previous_video_upload_list.js index c09b3a0dde..a356950315 100644 --- a/cms/static/js/views/previous_video_upload_list.js +++ b/cms/static/js/views/previous_video_upload_list.js @@ -1,22 +1,28 @@ define( - ['jquery', 'underscore', 'backbone', 'js/views/baseview', 'js/views/previous_video_upload'], - function($, _, Backbone, BaseView, PreviousVideoUploadView) { + ['jquery', 'underscore', 'backbone', 'js/views/baseview', 'edx-ui-toolkit/js/utils/html-utils', + 'js/views/previous_video_upload', 'text!templates/previous-video-upload-list.underscore'], + function($, _, Backbone, BaseView, HtmlUtils, PreviousVideoUploadView, previousVideoUploadListTemplate) { 'use strict'; var PreviousVideoUploadListView = BaseView.extend({ tagName: 'section', className: 'wrapper-assets', initialize: function(options) { - this.template = this.loadTemplate('previous-video-upload-list'); + this.template = HtmlUtils.template(previousVideoUploadListTemplate); this.encodingsDownloadUrl = options.encodingsDownloadUrl; this.videoImageUploadEnabled = options.videoImageSettings.video_image_upload_enabled; + this.isVideoTranscriptEnabled = options.isVideoTranscriptEnabled; this.itemViews = this.collection.map(function(model) { return new PreviousVideoUploadView({ videoImageUploadURL: options.videoImageUploadURL, defaultVideoImageURL: options.defaultVideoImageURL, videoHandlerUrl: options.videoHandlerUrl, videoImageSettings: options.videoImageSettings, - model: model + model: model, + transcriptAvailableLanguages: options.transcriptAvailableLanguages, + videoSupportedFileFormats: options.videoSupportedFileFormats, + videoTranscriptSettings: options.videoTranscriptSettings, + isVideoTranscriptEnabled: options.isVideoTranscriptEnabled }); }); }, @@ -24,10 +30,16 @@ define( render: function() { var $el = this.$el, $tabBody; - $el.html(this.template({ - encodingsDownloadUrl: this.encodingsDownloadUrl, - videoImageUploadEnabled: this.videoImageUploadEnabled - })); + + HtmlUtils.setHtml( + this.$el, + this.template({ + encodingsDownloadUrl: this.encodingsDownloadUrl, + videoImageUploadEnabled: this.videoImageUploadEnabled, + isVideoTranscriptEnabled: this.isVideoTranscriptEnabled + }) + ); + $tabBody = $el.find('.js-table-body'); _.each(this.itemViews, function(view) { $tabBody.append(view.render().$el); diff --git a/cms/static/js/views/video_transcripts.js b/cms/static/js/views/video_transcripts.js new file mode 100644 index 0000000000..a1a3c17e72 --- /dev/null +++ b/cms/static/js/views/video_transcripts.js @@ -0,0 +1,97 @@ +define( + ['underscore', 'gettext', 'js/views/baseview', 'edx-ui-toolkit/js/utils/html-utils', + 'edx-ui-toolkit/js/utils/string-utils', 'text!templates/video-transcripts.underscore'], + function(_, gettext, BaseView, HtmlUtils, StringUtils, videoTranscriptsTemplate) { + 'use strict'; + + var VideoTranscriptsView = BaseView.extend({ + tagName: 'div', + + events: { + 'click .toggle-show-transcripts-button': 'toggleShowTranscripts' + }, + + initialize: function(options) { + this.transcripts = options.transcripts; + this.edxVideoID = options.edxVideoID; + this.clientVideoID = options.clientVideoID; + this.transcriptAvailableLanguages = options.transcriptAvailableLanguages; + this.videoSupportedFileFormats = options.videoSupportedFileFormats; + this.videoTranscriptSettings = options.videoTranscriptSettings; + this.template = HtmlUtils.template(videoTranscriptsTemplate); + }, + + /* + Sorts object by value and returns a sorted array. + */ + sortByValue: function(itemObject) { + var sortedArray = []; + _.each(itemObject, function(value, key) { + // Push each JSON Object entry in array by [value, key] + sortedArray.push([value, key]); + }); + return sortedArray.sort(); + }, + + /* + Returns transcript title. + */ + getTranscriptClientTitle: function() { + var clientTitle = this.clientVideoID; + // Remove video file extension for transcript title. + _.each(this.videoSupportedFileFormats, function(videoFormat) { + clientTitle.replace(videoFormat, ''); + }); + return clientTitle.substring(0, 20); + }, + + /* + Toggles Show/Hide transcript button and transcripts container. + */ + toggleShowTranscripts: function() { + var $transcriptsWrapperEl = this.$el.find('.show-video-transcripts-wrapper'); + + // Toggle show transcript wrapper. + $transcriptsWrapperEl.toggleClass('hidden'); + + // Toggle button text. + HtmlUtils.setHtml( + this.$el.find('.toggle-show-transcripts-button-text'), + StringUtils.interpolate( + gettext('{toggleShowTranscriptText} transcripts ({totalTranscripts})'), + { + toggleShowTranscriptText: $transcriptsWrapperEl.hasClass('hidden') ? gettext('Show') : gettext('Hide'), // eslint-disable-line max-len + totalTranscripts: _.size(this.transcripts) + } + ) + ); + + // Toggle icon class. + if ($transcriptsWrapperEl.hasClass('hidden')) { + this.$el.find('.toggle-show-transcripts-icon').removeClass('fa-caret-down').addClass('fa-caret-right'); // eslint-disable-line max-len + } else { + this.$el.find('.toggle-show-transcripts-icon').removeClass('fa-caret-right').addClass('fa-caret-down'); // eslint-disable-line max-len + } + }, + + /* + Renders transcripts view. + */ + render: function() { + HtmlUtils.setHtml( + this.$el, + this.template({ + transcripts: this.transcripts, + transcriptAvailableLanguages: this.sortByValue(this.transcriptAvailableLanguages), + edxVideoID: this.edxVideoID, + transcriptClientTitle: this.getTranscriptClientTitle(), + transcriptDownloadFileFormat: this.videoTranscriptSettings.trancript_download_file_format + }) + ); + return this; + } + }); + + return VideoTranscriptsView; + } +); diff --git a/cms/static/sass/views/_video-upload.scss b/cms/static/sass/views/_video-upload.scss index 3dae1a8d55..20eb128229 100644 --- a/cms/static/sass/views/_video-upload.scss +++ b/cms/static/sass/views/_video-upload.scss @@ -17,6 +17,34 @@ top: 0 !important; } + .button-link { + background:none; + border:none; + padding:0; + color: $ui-link-color; + cursor:pointer + } + + .show-video-transcripts-wrapper { + display: block; + + .button-link { + color: $ui-link-color !important; + } + } + + .show-video-transcripts-wrapper.hidden { + display: none; + } + + .show-video-transcript-content { + margin-top: ($baseline/2); + .transcript-language-menu { + display: block; + width: 200px; + } + } + .course-video-settings-container { position: absolute; overflow: scroll; @@ -30,14 +58,6 @@ -moz-box-shadow: -3px 0px 3px 0px rgba(153,153,153,0.3); box-shadow: -3px 0px 3px 0px rgba(153,153,153,0.3); - .button-link { - background:none; - border:none; - padding:0; - color: $ui-link-color; - cursor:pointer - } - .action-close-wrapper { .action-close-course-video-settings { width: 100%; @@ -416,6 +436,10 @@ width: 25%; } + .transcripts-col { + width: 17%; + } + .thumbnail-col, .video-id-col { width: 15%; } diff --git a/cms/templates/js/previous-video-upload-list.underscore b/cms/templates/js/previous-video-upload-list.underscore index 393dd95301..3248aa78f8 100644 --- a/cms/templates/js/previous-video-upload-list.underscore +++ b/cms/templates/js/previous-video-upload-list.underscore @@ -14,6 +14,9 @@