Files
edx-platform/xmodule/js/src/video/10_main.js
Rodrigo Martin e51c01bf4e feat: add support for user feedback on autogenerated transcripts (#33518)
* feat: WIP transcript feedback

* feat: Add UI mock for Transcript Feedbacks (#33416)

* feat: Add UI mock for Transcript Feedbacks

* fix: Fix mongo tests

* feat: Get video_uuid, user_uuid and language for request (#33445)

* feat: make call to ai-translations to obtain feedback

* feat: Show widget if transcript was AI generated

* feat: bind all class methods

* fix: async calls

* feat: send request when choosing feedback

* feat: update showing condition (#33474)

* fix: ajax success lint

* fix: video caption specs errors fixed

* feat: add coverage to feedback widget

* chore: connect XT to LMS and CMS

* feat: use url

* chore: add vars to devstack

* chore: fix url name

* feat: update unit tests regarding env vars

* fix: fix test_video_mongo

* feat: add more tests

* feat: remove console log

Co-authored-by: Jesper Hodge <19345795+jesperhodge@users.noreply.github.com>

* fix: rename shouldShowWidget to loadAndSetVisibility

---------

Co-authored-by: María Guillermina Véscovo <mvescovo@2u.com>
Co-authored-by: Jesper Hodge <19345795+jesperhodge@users.noreply.github.com>
2023-11-06 16:33:53 +00:00

199 lines
8.5 KiB
JavaScript

/* globals _ */
/* RequireJS */
(function(require, $) {
'use strict';
// In the case when the Video constructor will be called before RequireJS finishes loading all of the Video
// dependencies, we will have a mock function that will collect all the elements that must be initialized as
// Video elements.
//
// Once RequireJS will load all of the necessary dependencies, main code will invoke the mock function with
// the second parameter set to truthy value. This will trigger the actual Video constructor on all elements
// that are stored in a temporary list.
window.Video = (function() {
// Temporary storage place for elements that must be initialized as Video elements.
var tempCallStack = [];
return function(element, processTempCallStack) {
// If mock function was called with second parameter set to truthy value, we invoke the real `window.Video`
// on all the stored elements so far.
if (processTempCallStack) {
$.each(tempCallStack, function(index, el) {
// By now, `window.Video` is the real constructor.
window.Video(el);
});
return null;
}
// If normal call to `window.Video` constructor, store the element for later initializing.
tempCallStack.push(element);
// Real Video constructor returns the `state` object. The mock function will return an empty object.
return {};
};
}());
// Main module.
require(
/* End RequireJS */
/* Webpack
define(
/* End Webpack */
[
'video/00_video_storage.js',
'video/01_initialize.js',
'video/025_focus_grabber.js',
'video/035_video_accessible_menu.js',
'video/04_video_control.js',
'video/04_video_full_screen.js',
'video/05_video_quality_control.js',
'video/06_video_progress_slider.js',
'video/07_video_volume_control.js',
'video/08_video_speed_control.js',
'video/08_video_auto_advance_control.js',
'video/09_video_caption.js',
'video/09_play_placeholder.js',
'video/09_play_pause_control.js',
'video/09_play_skip_control.js',
'video/09_skip_control.js',
'video/09_bumper.js',
'video/09_save_state_plugin.js',
'video/09_events_plugin.js',
'video/09_events_bumper_plugin.js',
'video/09_poster.js',
'video/09_completion.js',
'video/10_commands.js',
'video/095_video_context_menu.js',
'video/036_video_social_sharing.js',
'video/037_video_transcript_feedback.js'
],
function(
VideoStorage, initialize, FocusGrabber, VideoAccessibleMenu, VideoControl, VideoFullScreen,
VideoQualityControl, VideoProgressSlider, VideoVolumeControl, VideoSpeedControl, VideoAutoAdvanceControl,
VideoCaption, VideoPlayPlaceholder, VideoPlayPauseControl, VideoPlaySkipControl, VideoSkipControl,
VideoBumper, VideoSaveStatePlugin, VideoEventsPlugin, VideoEventsBumperPlugin, VideoPoster,
VideoCompletionHandler, VideoCommands, VideoContextMenu, VideoSocialSharing, VideoTranscriptFeedback
) {
/* RequireJS */
var youtubeXhr = null,
oldVideo = window.Video;
/* End RequireJS */
/* Webpack
var youtubeXhr = null;
/* End Webpack */
window.Video = function(element) {
var el = $(element).find('.video'),
id = el.attr('id').replace(/video_/, ''),
storage = VideoStorage('VideoState', id),
bumperMetadata = el.data('bumper-metadata'),
autoAdvanceEnabled = el.data('autoadvance-enabled') === 'True',
mainVideoModules = [
FocusGrabber, VideoControl, VideoPlayPlaceholder,
VideoPlayPauseControl, VideoProgressSlider, VideoSpeedControl,
VideoVolumeControl, VideoQualityControl, VideoFullScreen, VideoCaption, VideoCommands,
VideoContextMenu, VideoSaveStatePlugin, VideoEventsPlugin, VideoCompletionHandler, VideoTranscriptFeedback
].concat(autoAdvanceEnabled ? [VideoAutoAdvanceControl] : []),
bumperVideoModules = [VideoControl, VideoPlaySkipControl, VideoSkipControl,
VideoVolumeControl, VideoCaption, VideoCommands, VideoSaveStatePlugin, VideoTranscriptFeedback,
VideoEventsBumperPlugin, VideoCompletionHandler],
state = {
el: el,
id: id,
metadata: el.data('metadata'),
storage: storage,
options: {},
youtubeXhr: youtubeXhr,
modules: mainVideoModules
};
var getBumperState = function(metadata) {
var bumperState = $.extend(true, {
el: el,
id: id,
storage: storage,
options: {},
youtubeXhr: youtubeXhr
}, {metadata: metadata});
bumperState.modules = bumperVideoModules;
bumperState.options = {
SaveStatePlugin: {events: ['language_menu:change']}
};
return bumperState;
};
var player = function(innerState) {
return function() {
_.extend(innerState.metadata, {autoplay: true, focusFirstControl: true});
initialize(innerState, element);
};
};
var onSequenceChange;
VideoAccessibleMenu(el, {
storage: storage,
saveStateUrl: state.metadata.saveStateUrl
});
VideoSocialSharing(el);
if (bumperMetadata) {
VideoPoster(el, {
poster: el.data('poster'),
onClick: _.once(function() {
var mainVideoPlayer = player(state);
var bumper, bumperState;
if (storage.getItem('isBumperShown')) {
mainVideoPlayer();
} else {
bumperState = getBumperState(bumperMetadata);
bumper = new VideoBumper(player(bumperState), bumperState);
state.bumperState = bumperState;
bumper.getPromise().done(function() {
delete state.bumperState;
mainVideoPlayer();
});
}
})
});
} else {
initialize(state, element);
}
if (!youtubeXhr) {
youtubeXhr = state.youtubeXhr;
}
el.data('video-player-state', state);
onSequenceChange = function() {
if (state && state.videoPlayer) {
state.videoPlayer.destroy();
}
$('.sequence').off('sequence:change', onSequenceChange);
};
$('.sequence').on('sequence:change', onSequenceChange);
// Because the 'state' object is only available inside this closure, we will also make it available to
// the caller by returning it. This is necessary so that we can test Video with Jasmine.
return state;
};
window.Video.clearYoutubeXhr = function() {
youtubeXhr = null;
};
window.Video.loadYouTubeIFrameAPI = initialize.prototype.loadYouTubeIFrameAPI;
/* RequireJS */
// Invoke the mock Video constructor so that the elements stored within it can be processed by the real
// `window.Video` constructor.
oldVideo(null, true);
/* End RequireJS */
}
);
/* RequireJS */
}(window.RequireJS.require, window.jQuery));
/* End RequireJS */