moe-video.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  1. /** moe-video enabler component **/
  2. (function() {
  3. // state
  4. let moeOTSession = false;
  5. let moePublisher = false;
  6. let moeVideoElement = false;
  7. let moeArchiveID = false;
  8. // initialize elements, events
  9. function init(_container = false) {
  10. $('[moe-video-progress]').remove();
  11. $('<div/>')
  12. .attr('moe-video-progress', 1)
  13. .append('<img src="/img/loading.svg">')
  14. .appendTo('body');
  15. _container = $(_container || 'body');
  16. _container
  17. .find('[moe-video]:not([moe-initialized])')
  18. .each(function() {
  19. // assign a unique id
  20. let moeID = Math.ceil(Math.random() * 1000000);
  21. $(this).attr('moe-id', moeID);
  22. // if there's already a video, show it
  23. let videoUrl = $(this).attr('moe-video-url');
  24. if(!!videoUrl) {
  25. $('<video/>')
  26. .addClass('d-block mt-2 mb-3')
  27. .attr({
  28. 'moe-answer-video': 1,
  29. 'controls': 1,
  30. 'src': videoUrl
  31. })
  32. .appendTo(this);
  33. }
  34. var buttonsDiv = $('<div class="d-flex flex-wrap" />').appendTo(this);
  35. // record button
  36. $('<button type="button" />')
  37. .addClass('btn btn-sm btn-outline-info px-4 py-2 text-nowrap me-3 mb-2')
  38. .attr('moe-show-popup', 1)
  39. .html('<i class="fa fa-video me-2"></i>Record Answer')
  40. .appendTo(buttonsDiv);
  41. // upload button
  42. $('<form enctype="multipart/form-data" ' +
  43. 'method="post" ' +
  44. 'class="d-inline-block mb-2" ' +
  45. 'action="/upload-video"/>')
  46. .append('<input type="hidden" name="_token" value="' + $(this).attr('moe-csrf') + '">')
  47. .append('<input type="hidden" name="question_key" value="' + $(this).attr('moe-question-key') + '" />')
  48. .append(
  49. $('<button type="button" />')
  50. .addClass('btn btn-sm btn-outline-default border border-dark px-4 py-2 text-nowrap file')
  51. .html('<i class="fa fa-upload me-2"></i>' +
  52. 'Upload Video' +
  53. '<input type="file" class="input-file" name="hcp_intro" accept="video/*;capture=camcorder">')
  54. )
  55. .appendTo(buttonsDiv);
  56. // mark as initialized
  57. $(this).attr('moe-initialized', 1);
  58. });
  59. // bind events
  60. $(document).off('click.moe-show-popup', '[moe-video] [moe-show-popup]')
  61. $(document).on('click.moe-show-popup', '[moe-video] [moe-show-popup]', function() {
  62. initVideoPopup();
  63. moeVideoElement = $(this).closest('[moe-video]');
  64. $('[moe-video-backdrop]')
  65. .css('display', 'flex')
  66. .find('.card-title').first().text(moeVideoElement.find('p').first().text());
  67. initCamera(moeVideoElement);
  68. });
  69. $(document).off('click.moe-start-recording', '[moe-video-backdrop] [moe-start-recording]')
  70. $(document).on('click.moe-start-recording', '[moe-video-backdrop] [moe-start-recording]', function() {
  71. startWait();
  72. $('[moe-video-player]')
  73. .attr('src', '')
  74. .removeClass('d-block')
  75. .addClass('d-none');
  76. $('[moe-video-canvas]')
  77. .removeClass('d-none')
  78. .addClass('d-block');
  79. $.post('/start/' + moeVideoElement.attr('moe-ot-session-id'));
  80. });
  81. $(document).off('click.moe-stop-recording', '[moe-video-backdrop] [moe-stop-recording]')
  82. $(document).on('click.moe-stop-recording', '[moe-video-backdrop] [moe-stop-recording]', function() {
  83. startWait();
  84. $.post('stop/' + moeArchiveID).complete(function () {
  85. $.ajax('/archive/'+ moeArchiveID, {
  86. success: function (data) { // success callback function
  87. stopWait();
  88. $('[moe-video-player]')
  89. .attr('src', data.video)
  90. .removeClass('d-none')
  91. .addClass('d-block');
  92. $('[moe-video-canvas]')
  93. .removeClass('d-block')
  94. .addClass('d-none');
  95. $('[moe-start-recording]').show();
  96. $('[moe-stop-recording]').hide();
  97. $('[moe-play-recording]').show();
  98. $('[moe-restart-recording]').hide();
  99. $('[moe-submit-recording]').show();
  100. $('[moe-close-popup]').show();
  101. }
  102. });
  103. });
  104. });
  105. $(document).off('click.moe-play-recording', '[moe-video-backdrop] [moe-play-recording]')
  106. $(document).on('click.moe-play-recording', '[moe-video-backdrop] [moe-play-recording]', function() {
  107. $('[moe-video-player]')[0].play();
  108. });
  109. $(document).off('click.moe-submit-recording', '[moe-video-backdrop] [moe-submit-recording]')
  110. $(document).on('click.moe-submit-recording', '[moe-video-backdrop] [moe-submit-recording]', function() {
  111. startWait();
  112. $.post('/save-archive', {
  113. archiveID: moeArchiveID,
  114. question_key: moeVideoElement.attr('moe-question-key')
  115. }, function(_data) {
  116. if(!!moeOTSession) {
  117. moeOTSession.disconnect();
  118. moeOTSession = false;
  119. moePublisher = false;
  120. }
  121. // $('[moe-video-backdrop]').css('display', 'none');
  122. window.location.reload();
  123. }, 'json');
  124. });
  125. $(document).off('click.moe-close-popup', '[moe-video-backdrop] [moe-close-popup]')
  126. $(document).on('click.moe-close-popup', '[moe-video-backdrop] [moe-close-popup]', function() {
  127. if(!!moeOTSession) {
  128. moeOTSession.disconnect();
  129. moeOTSession = false;
  130. moePublisher = false;
  131. }
  132. $('[moe-video-backdrop]').css('display', 'none');
  133. });
  134. $(document).off('change.input-file', '[moe-video] .input-file')
  135. $(document).on('change.input-file', '[moe-video] .input-file', function() {
  136. $(this).closest('form').submit();
  137. });
  138. }
  139. function initVideoPopup() {
  140. $('[moe-video-backdrop]').remove();
  141. $('<div moe-video-backdrop>' +
  142. '<div class="card">' +
  143. '<div class="card-header">' +
  144. '<div class="card-title text-secondary"></div>' +
  145. '</div>' +
  146. '<div class="card-body">' +
  147. '<div class="access-information">' +
  148. '<p>Setting up camera and microphone access...</p>' +
  149. '<p class="click-allow d-none">Click "Allow" to allow hardware access and begin the recording</p>' +
  150. '<div class="denied d-none">' +
  151. '<p class="text-danger"><b>It appears we do not have access to your input hardware.</b></p>' +
  152. '<p class="font-weight-normal">Please allow access to your hardware by clicking the ' +
  153. '<img class="border border-dark" src="/img/denied.png" alt=""> icon ' +
  154. 'towards the far right of the browser address bar. <b>Once done, please click <a href="">here</a> to refresh the page and retry.</b></p>' +
  155. '</div>' +
  156. '</div>' +
  157. '<div moe-video-canvas class="d-none"></div>' +
  158. '<video moe-video-player class="d-none" controls><source src="" type="video/mp4"></video>' +
  159. '</div>' +
  160. '<div class="card-footer">' +
  161. '<div class="d-flex align-items-center justify-content-center">' +
  162. '<button type="button" class="btn btn-danger btn-sm me-3" moe-start-recording>' +
  163. '<i class="fa fa-video me-2"></i>Record' +
  164. '</button>' +
  165. '<button type="button" class="btn btn-danger btn-sm me-3" moe-stop-recording>' +
  166. '<i class="fa fa-stop me-2"></i>Stop' +
  167. '</button>' +
  168. '<button type="button" class="btn btn-info btn-sm me-3" moe-play-recording>' +
  169. '<i class="fa fa-play me-2"></i>Play' +
  170. '</button>' +
  171. '<button type="button" class="btn btn-info btn-sm me-3" moe-restart-recording>' +
  172. '<i class="fa fa-play me-2"></i>Play' +
  173. '</button>' +
  174. '<button type="button" class="btn btn-success btn-sm me-3" moe-submit-recording>' +
  175. '<i class="fa fa-check me-2"></i>Submit' +
  176. '</button>' +
  177. '<button type="button" class="btn btn-default bg-light border border-default btn-sm me-3" moe-close-popup>' +
  178. '<i class="fa fa-times me-2"></i>Close' +
  179. '</button>' +
  180. '<span moe-wait>Please wait...</span>' +
  181. '</div>' +
  182. '</div>' +
  183. '</div>' +
  184. '</div>').appendTo('body');
  185. }
  186. function startWait() {
  187. $('[moe-start-recording]').hide();
  188. $('[moe-stop-recording]').hide();
  189. $('[moe-play-recording]').hide();
  190. $('[moe-restart-recording]').hide();
  191. $('[moe-submit-recording]').hide();
  192. $('[moe-close-popup]').hide();
  193. $('[moe-wait]').show();
  194. $('[moe-video-progress]').removeClass('d-none').addClass('d-flex');
  195. }
  196. function stopWait() {
  197. $('[moe-wait]').hide();
  198. $('[moe-video-progress]').removeClass('d-flex').addClass('d-none');
  199. }
  200. // init ot, publisher and show cam feed
  201. function initCamera(_moeVideoElement) {
  202. startWait();
  203. // cleanup
  204. if(!!moeOTSession) {
  205. moeOTSession.disconnect();
  206. moeOTSession = false;
  207. moePublisher = false;
  208. }
  209. // create session (using sessionID given by the server)
  210. moeOTSession = OT.initSession(
  211. _moeVideoElement.attr('moe-ot-api-key'),
  212. _moeVideoElement.attr('moe-ot-session-id')
  213. );
  214. // init publisher
  215. moePublisher = OT.initPublisher(
  216. $('[moe-video-canvas]').first()[0],
  217. {
  218. //videoSource: 'screen', // comment out in prod
  219. width: 540
  220. }
  221. );
  222. // publisher events
  223. moePublisher.on({
  224. accessAllowed: function (event) {
  225. // The user has granted access to the camera and mic.
  226. console.log('ALIX: accessAllowed');
  227. $('.access-information').removeClass('d-block').addClass('d-none');
  228. $('[moe-video-canvas]').removeClass('d-none').addClass('d-block');
  229. stopWait()
  230. },
  231. accessDenied: function accessDeniedHandler(event) {
  232. // The user has denied access to the camera and mic.
  233. console.log('ALIX: accessDenied');
  234. $('.click-allow').addClass('d-none');
  235. $('.denied').removeClass('d-none');
  236. stopWait()
  237. },
  238. accessDialogOpened: function (event) {
  239. // The Allow/Deny dialog box is opened.
  240. console.log('ALIX: accessDialogOpened');
  241. $('.click-allow').removeClass('d-none');
  242. stopWait()
  243. },
  244. accessDialogClosed: function (event) {
  245. // The Allow/Deny dialog box is closed.
  246. console.log('ALIX: accessDialogClosed');
  247. stopWait()
  248. }
  249. });
  250. // OT events
  251. moeOTSession.connect(_moeVideoElement.attr('moe-ot-client-token'), function(error) {
  252. if (error) {
  253. console.error('Failed to connect', error);
  254. } else {
  255. moeOTSession.publish(moePublisher, function(error) {
  256. if (error) {
  257. console.error('Failed to publish', error);
  258. }
  259. else {
  260. stopWait();
  261. $('[moe-start-recording]').show();
  262. $('[moe-stop-recording]').hide();
  263. $('[moe-play-recording]').hide();
  264. $('[moe-restart-recording]').hide();
  265. $('[moe-submit-recording]').hide();
  266. $('[moe-close-popup]').show();
  267. }
  268. });
  269. }
  270. });
  271. moeOTSession.on('archiveStarted', function(event) {
  272. stopWait();
  273. moeArchiveID = event.id;
  274. $('[moe-start-recording]').hide();
  275. $('[moe-stop-recording]').show();
  276. $('[moe-play-recording]').hide();
  277. $('[moe-restart-recording]').hide();
  278. $('[moe-submit-recording]').hide();
  279. $('[moe-close-popup]').hide();
  280. });
  281. moeOTSession.on('archiveStopped', function() {
  282. // moeArchiveID = false;
  283. // stopWait();
  284. // $('[moe-start-recording]').show();
  285. // $('[moe-stop-recording]').hide();
  286. // $('[moe-play-recording]').hide();
  287. // $('[moe-restart-recording]').hide();
  288. // $('[moe-submit-recording]').hide();
  289. // $('[moe-close-popup]').show();
  290. });
  291. }
  292. $(document).ready(function() {
  293. init();
  294. });
  295. })();