MDL-75259 tiny_recordrtc: Implement Record RTC plugin for TinyMCE

Part of MDL-75966

Co-authored by Andrew Lyons <andrew@nicols.co.uk>

AMOS BEGIN
  CPY [audioandvideo,atto_recordrtc],[audioandvideo,tiny_recordrtc]
  CPY [audiobitrate,atto_recordrtc],[audiobitrate,tiny_recordrtc]
  CPY [attachrecording,atto_recordrtc],[attachrecording,tiny_recordrtc]
  CPY [allowedtypes,atto_recordrtc],[allowedtypes,tiny_recordrtc]
  CPY [allowedtypes_desc,atto_recordrtc],[allowedtypes_desc,tiny_recordrtc]
  CPY [audiobitrate,atto_recordrtc],[audiobitrate,tiny_recordrtc]
  CPY [audiobitrate_desc,atto_recordrtc],[audiobitrate_desc,tiny_recordrtc]
  CPY [audiotimelimit,atto_recordrtc],[audiotimelimit,tiny_recordrtc]
  CPY [audiotimelimit_desc,atto_recordrtc],[audiotimelimit_desc,tiny_recordrtc]
  CPY [confirm_yes,atto_recordrtc],[yes,core]
  CPY [gumabort,atto_recordrtc],[gumabort,tiny_recordrtc]
  CPY [gumabort_title,atto_recordrtc],[gumabort_title,tiny_recordrtc]
  CPY [gumnotallowed,atto_recordrtc],[gumnotallowed,tiny_recordrtc]
  CPY [gumnotallowed_title,atto_recordrtc],[gumnotallowed_title,tiny_recordrtc]
  CPY [gumnotfound,atto_recordrtc],[gumnotfound,tiny_recordrtc]
  CPY [gumnotfound_title,atto_recordrtc],[gumnotfound_title,tiny_recordrtc]
  CPY [gumnotreadable,atto_recordrtc],[gumnotreadable,tiny_recordrtc]
  CPY [gumnotreadable_title,atto_recordrtc],[gumnotreadable_title,tiny_recordrtc]
  CPY [gumnotsupported,atto_recordrtc],[gumnotsupported,tiny_recordrtc]
  CPY [gumnotsupported_title,atto_recordrtc],[gumnotsupported_title,tiny_recordrtc]
  CPY [gumoverconstrained,atto_recordrtc],[gumoverconstrained,tiny_recordrtc]
  CPY [gumoverconstrained_title,atto_recordrtc],[gumoverconstrained_title,tiny_recordrtc]
  CPY [gumsecurity,atto_recordrtc],[gumsecurity,tiny_recordrtc]
  CPY [gumsecurity_title,atto_recordrtc],[gumsecurity_title,tiny_recordrtc]
  CPY [gumtype,atto_recordrtc],[gumtype,tiny_recordrtc]
  CPY [gumtype_title,atto_recordrtc],[gumtype_title,tiny_recordrtc]
  CPY [insecurealert,atto_recordrtc],[insecurealert,tiny_recordrtc]
  CPY [insecurealert_title,atto_recordrtc],[insecurealert_title,tiny_recordrtc]
  CPY [insecurealerttitle,atto_recordrtc],[insecurealerttitle,tiny_recordrtc]
  CPY [insecurealert,atto_recordrtc],[insecurealert,tiny_recordrtc]
  CPY [nearingmaxsize,atto_recordrtc],[maxfilesizehit,tiny_recordrtc]
  CPY [nearingmaxsize_title,atto_recordrtc],[maxfilesizehit_title,tiny_recordrtc]
  CPY [norecordingfound,atto_recordrtc],[norecordingfound,tiny_recordrtc]
  CPY [norecordingfound_title,atto_recordrtc],[norecordingfound_title,tiny_recordrtc]
  CPY [nowebrtc,atto_recordrtc],[nowebrtc,tiny_recordrtc]
  CPY [nowebrtc_title,atto_recordrtc],[nowebrtc_title,tiny_recordrtc]
  CPY [onlyaudio,atto_recordrtc],[onlyaudio,tiny_recordrtc]
  CPY [onlyvideo,atto_recordrtc],[onlyvideo,tiny_recordrtc]
  CPY [recordagain,atto_recordrtc],[recordagain,tiny_recordrtc]
  CPY [recordinguploaded,atto_recordrtc],[recordinguploaded,tiny_recordrtc]
  CPY [recordingfailed,atto_recordrtc],[recordingfailed,tiny_recordrtc]
  CPY [recordrtc:recordaudio,atto_recordrtc],[recordrtc:recordaudio,tiny_recordrtc]
  CPY [recordrtc:recordvideo,atto_recordrtc],[recordrtc:recordvideo,tiny_recordrtc]
  CPY [startrecording,atto_recordrtc],[startrecording,tiny_recordrtc]
  CPY [stoprecording,atto_recordrtc],[stoprecording,tiny_recordrtc]
  CPY [timelimitwarning,atto_recordrtc],[timelimitwarning,tiny_recordrtc]
  CPY [uploadaborted,atto_recordrtc],[uploadaborted,tiny_recordrtc]
  CPY [uploadprogress,atto_recordrtc],[uploadprogress,tiny_recordrtc]
  CPY [videobitrate,atto_recordrtc],[videobitrate,tiny_recordrtc]
  CPY [videobitrate_desc,atto_recordrtc],[videobitrate_desc,tiny_recordrtc]
  CPY [videobuttontitle,atto_recordrtc],[videobuttontitle,tiny_recordrtc]
  CPY [videotimelimit,atto_recordrtc],[videotimelimit,tiny_recordrtc]
  CPY [videotimelimit_desc,atto_recordrtc],[videotimelimit_desc,tiny_recordrtc]
AMOS END
This commit is contained in:
Stevani Andolo 2022-08-31 17:37:52 +08:00 committed by Andrew Nicols
parent 71046558b1
commit 0cd3b75164
47 changed files with 2308 additions and 0 deletions

View file

@ -2032,6 +2032,7 @@ class core_plugin_manager {
'autosave', 'autosave',
'h5p', 'h5p',
'media', 'media',
'recordrtc',
], ],
'tinymce' => array( 'tinymce' => array(

View file

@ -0,0 +1,3 @@
define("tiny_recordrtc/audio_recorder",["exports","./base_recorder","./modal","core/modal_registry","tiny_recordrtc/common"],(function(_exports,_base_recorder,_modal,_modal_registry,_common){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _defineProperty(obj,key,value){return key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,obj}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_base_recorder=_interopRequireDefault(_base_recorder),_modal=_interopRequireDefault(_modal),_modal_registry=_interopRequireDefault(_modal_registry);class Audio extends _base_recorder.default{configurePlayer(){return this.modalRoot.querySelector("audio")}getSupportedTypes(){return["audio/webm;codecs=opus","audio/ogg;codecs=opus"]}getParsedRecordingOptions(){return{audioBitsPerSecond:parseInt(this.config.audiobitrate)}}getMediaConstraints(){return{audio:!0}}getRecordingType(){return"audio"}getTimeLimit(){return this.config.audiotimelimit}getEmbedTemplateName(){return"tiny_recordrtc/embed_audio"}getFileName(prefix){return"".concat(prefix,"-audio.ogg")}static getModalClass(){var _class;const modalType="".concat(_common.component,"/audio_recorder"),registration=_modal_registry.default.get(modalType);if(registration)return registration.module;const AudioModal=(_defineProperty(_class=class extends _modal.default{},"TYPE",modalType),_defineProperty(_class,"TEMPLATE","".concat(_common.component,"/audio_recorder")),_class);return _modal_registry.default.register(AudioModal.TYPE,AudioModal,AudioModal.TEMPLATE),AudioModal}}return _exports.default=Audio,_exports.default}));
//# sourceMappingURL=audio_recorder.min.js.map

View file

@ -0,0 +1 @@
{"version":3,"file":"audio_recorder.min.js","sources":["../src/audio_recorder.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Tiny Record RTC - audio recorder configuration.\n *\n * @module tiny_recordrtc/audio\n * @copyright 2022 Stevani Andolo <stevani@hotmail.com.au>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport BaseClass from './base_recorder';\nimport Modal from './modal';\nimport ModalRegistry from 'core/modal_registry';\nimport {component} from 'tiny_recordrtc/common';\n\nexport default class Audio extends BaseClass {\n configurePlayer() {\n return this.modalRoot.querySelector('audio');\n }\n\n getSupportedTypes() {\n return [\n 'audio/webm;codecs=opus',\n 'audio/ogg;codecs=opus',\n ];\n }\n\n getParsedRecordingOptions() {\n return {\n audioBitsPerSecond: parseInt(this.config.audiobitrate),\n };\n }\n\n getMediaConstraints() {\n return {\n audio: true,\n };\n }\n\n getRecordingType() {\n return 'audio';\n }\n\n getTimeLimit() {\n return this.config.audiotimelimit;\n }\n\n getEmbedTemplateName() {\n return 'tiny_recordrtc/embed_audio';\n }\n\n getFileName(prefix) {\n return `${prefix}-audio.ogg`;\n }\n\n static getModalClass() {\n const modalType = `${component}/audio_recorder`;\n const registration = ModalRegistry.get(modalType);\n if (registration) {\n return registration.module;\n }\n\n const AudioModal = class extends Modal {\n static TYPE = modalType;\n static TEMPLATE = `${component}/audio_recorder`;\n };\n\n ModalRegistry.register(AudioModal.TYPE, AudioModal, AudioModal.TEMPLATE);\n return AudioModal;\n }\n}\n"],"names":["Audio","BaseClass","configurePlayer","this","modalRoot","querySelector","getSupportedTypes","getParsedRecordingOptions","audioBitsPerSecond","parseInt","config","audiobitrate","getMediaConstraints","audio","getRecordingType","getTimeLimit","audiotimelimit","getEmbedTemplateName","getFileName","prefix","modalType","component","registration","ModalRegistry","get","module","AudioModal","Modal","register","TYPE","TEMPLATE"],"mappings":"+pBA4BqBA,cAAcC,uBAC/BC,yBACWC,KAAKC,UAAUC,cAAc,SAGxCC,0BACW,CACH,yBACA,yBAIRC,kCACW,CACHC,mBAAoBC,SAASN,KAAKO,OAAOC,eAIjDC,4BACW,CACHC,OAAO,GAIfC,yBACW,QAGXC,sBACWZ,KAAKO,OAAOM,eAGvBC,6BACW,6BAGXC,YAAYC,wBACEA,6DAIJC,oBAAeC,qCACfC,aAAeC,wBAAcC,IAAIJ,cACnCE,oBACOA,aAAaG,aAGlBC,mCAAa,cAAcC,wBACfP,uDACOC,6EAGXO,SAASF,WAAWG,KAAMH,WAAYA,WAAWI,UACxDJ"}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,10 @@
define("tiny_recordrtc/commands_audio",["exports","core/str","editor_tiny/utils","./common","./audio_recorder"],(function(_exports,_str,_utils,_common,_audio_recorder){var obj;
/**
* Tiny Record RTC - record audio command.
*
* @module tiny_recordrtc/recordAudioCommands
* @copyright 2022, Stevani Andolo <stevani@hotmail.com.au>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_audio_recorder=(obj=_audio_recorder)&&obj.__esModule?obj:{default:obj};return _exports.default=async()=>{if(!_audio_recorder.default.isBrowserCompatible())return()=>!1;const[audioButtonTitle,audio]=await Promise.all([(0,_str.get_string)("audiobuttontitle",_common.component),(0,_utils.getButtonImage)("audio",_common.component)]);return editor=>{editor.ui.registry.addIcon("audio",audio.html),editor.ui.registry.addButton(_common.audioButtonName,{icon:"audio",tooltip:audioButtonTitle,onAction:()=>_audio_recorder.default.display(editor)}),editor.ui.registry.addMenuItem(_common.audioButtonName,{icon:"audio",text:audioButtonTitle,onAction:()=>_audio_recorder.default.display(editor)})}},_exports.default}));
//# sourceMappingURL=commands_audio.min.js.map

View file

@ -0,0 +1 @@
{"version":3,"file":"commands_audio.min.js","sources":["../src/commands_audio.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Tiny Record RTC - record audio command.\n *\n * @module tiny_recordrtc/recordAudioCommands\n * @copyright 2022, Stevani Andolo <stevani@hotmail.com.au>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {get_string as getString} from 'core/str';\nimport {getButtonImage} from 'editor_tiny/utils';\nimport {\n audioButtonName,\n component\n} from './common';\nimport Recorder from './audio_recorder';\n\nexport default async() => {\n if (!Recorder.isBrowserCompatible()) {\n // The browser doesn't support the plugin, so just don't show it.\n return () => false;\n }\n\n const [\n audioButtonTitle,\n audio,\n ] = await Promise.all([\n getString('audiobuttontitle', component),\n getButtonImage('audio', component),\n ]);\n\n return (editor) => {\n const icon = 'audio';\n editor.ui.registry.addIcon(icon, audio.html);\n\n editor.ui.registry.addButton(audioButtonName, {\n icon,\n tooltip: audioButtonTitle,\n onAction: () => Recorder.display(editor),\n });\n\n editor.ui.registry.addMenuItem(audioButtonName, {\n icon,\n text: audioButtonTitle,\n onAction: () => Recorder.display(editor),\n });\n };\n};\n"],"names":["async","Recorder","isBrowserCompatible","audioButtonTitle","audio","Promise","all","component","editor","ui","registry","addIcon","html","addButton","audioButtonName","icon","tooltip","onAction","display","addMenuItem","text"],"mappings":";;;;;;;qLA+BeA,cACNC,wBAASC,4BAEH,KAAM,QAIbC,iBACAC,aACMC,QAAQC,IAAI,EAClB,mBAAU,mBAAoBC,oBAC9B,yBAAe,QAASA,4BAGpBC,SAEJA,OAAOC,GAAGC,SAASC,QADN,QACoBP,MAAMQ,MAEvCJ,OAAOC,GAAGC,SAASG,UAAUC,wBAAiB,CAC1CC,KAJS,QAKTC,QAASb,iBACTc,SAAU,IAAMhB,wBAASiB,QAAQV,UAGrCA,OAAOC,GAAGC,SAASS,YAAYL,wBAAiB,CAC5CC,KAVS,QAWTK,KAAMjB,iBACNc,SAAU,IAAMhB,wBAASiB,QAAQV"}

View file

@ -0,0 +1,10 @@
define("tiny_recordrtc/commands_video",["exports","core/str","editor_tiny/utils","./common","./video_recorder"],(function(_exports,_str,_utils,_common,_video_recorder){var obj;
/**
* Tiny Record RTC - record video command.
*
* @module tiny_recordrtc/recordVideoCommands
* @copyright 2022, Stevani Andolo <stevani@hotmail.com.au>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_video_recorder=(obj=_video_recorder)&&obj.__esModule?obj:{default:obj};return _exports.default=async()=>{if(!_video_recorder.default.isBrowserCompatible())return()=>!1;const[videoButtonTitle,buttonImage]=await Promise.all([(0,_str.get_string)("videobuttontitle",_common.component),(0,_utils.getButtonImage)("video",_common.component)]);return editor=>{editor.ui.registry.addIcon("video",buttonImage.html),editor.ui.registry.addButton(_common.videoButtonName,{icon:"video",tooltip:videoButtonTitle,onAction:()=>_video_recorder.default.display(editor)}),editor.ui.registry.addMenuItem(_common.videoButtonName,{icon:"video",text:videoButtonTitle,onAction:()=>_video_recorder.default.display(editor)})}},_exports.default}));
//# sourceMappingURL=commands_video.min.js.map

View file

@ -0,0 +1 @@
{"version":3,"file":"commands_video.min.js","sources":["../src/commands_video.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Tiny Record RTC - record video command.\n *\n * @module tiny_recordrtc/recordVideoCommands\n * @copyright 2022, Stevani Andolo <stevani@hotmail.com.au>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {get_string as getString} from 'core/str';\nimport {getButtonImage as getVideoIcon} from 'editor_tiny/utils';\nimport {\n videoButtonName,\n component\n} from './common';\nimport Recorder from './video_recorder';\n\nexport default async() => {\n if (!Recorder.isBrowserCompatible()) {\n // The browser doesn't support the plugin, so just don't show it.\n return () => false;\n }\n\n const [\n videoButtonTitle,\n buttonImage,\n ] = await Promise.all([\n getString('videobuttontitle', component),\n getVideoIcon('video', component),\n ]);\n\n return (editor) => {\n let icon = 'video';\n editor.ui.registry.addIcon(icon, buttonImage.html);\n\n editor.ui.registry.addButton(videoButtonName, {\n icon,\n tooltip: videoButtonTitle,\n onAction: () => Recorder.display(editor),\n });\n\n editor.ui.registry.addMenuItem(videoButtonName, {\n icon,\n text: videoButtonTitle,\n onAction: () => Recorder.display(editor),\n });\n };\n};\n"],"names":["async","Recorder","isBrowserCompatible","videoButtonTitle","buttonImage","Promise","all","component","editor","ui","registry","addIcon","html","addButton","videoButtonName","icon","tooltip","onAction","display","addMenuItem","text"],"mappings":";;;;;;;qLA+BeA,cACNC,wBAASC,4BAEH,KAAM,QAIbC,iBACAC,mBACMC,QAAQC,IAAI,EAClB,mBAAU,mBAAoBC,oBAC9B,yBAAa,QAASA,4BAGlBC,SAEJA,OAAOC,GAAGC,SAASC,QADR,QACsBP,YAAYQ,MAE7CJ,OAAOC,GAAGC,SAASG,UAAUC,wBAAiB,CAC1CC,KAJO,QAKPC,QAASb,iBACTc,SAAU,IAAMhB,wBAASiB,QAAQV,UAGrCA,OAAOC,GAAGC,SAASS,YAAYL,wBAAiB,CAC5CC,KAVO,QAWPK,KAAMjB,iBACNc,SAAU,IAAMhB,wBAASiB,QAAQV"}

View file

@ -0,0 +1,3 @@
define("tiny_recordrtc/common",["exports"],(function(_exports){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0;return _exports.default={pluginName:"tiny_recordrtc/plugin",component:"tiny_recordrtc",audioButtonName:"tiny_recordrtc_audio",videoButtonName:"tiny_recordrtc_video"},_exports.default}));
//# sourceMappingURL=common.min.js.map

View file

@ -0,0 +1 @@
{"version":3,"file":"common.min.js","sources":["../src/common.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Tiny Record RTC common values.\n *\n * @module tiny_recordrtc/common\n * @copyright 2022, Stevani Andolo <stevani@hotmail.com.au>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nexport default {\n pluginName: 'tiny_recordrtc/plugin',\n component: 'tiny_recordrtc',\n audioButtonName: 'tiny_recordrtc_audio',\n videoButtonName: 'tiny_recordrtc_video'\n};\n"],"names":["pluginName","component","audioButtonName","videoButtonName"],"mappings":"uKAuBe,CACXA,WAAY,wBACZC,UAAW,iBACXC,gBAAiB,uBACjBC,gBAAiB"}

View file

@ -0,0 +1,11 @@
define("tiny_recordrtc/configuration",["exports","./common","editor_tiny/utils"],(function(_exports,_common,_utils){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.configure=void 0;
/**
* Tiny Record RTC configuration.
*
* @module tiny_recordrtc/configuration
* @copyright 2022, Stevani Andolo <stevani@hotmail.com.au>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
const configureMenu=menu=>{const items=menu.insert.items.split(" ");return items.some(((item,index)=>!!item.match(/(media|video)\b/)&&(items.splice(index+1,0,_common.audioButtonName,_common.videoButtonName),!0)))?menu.insert.items=items.join(" "):(0,_utils.addMenubarItem)(menu,"insert","".concat(_common.audioButtonName," ").concat(_common.videoButtonName)),menu};_exports.configure=instanceConfig=>{return{toolbar:(toolbar=instanceConfig.toolbar,toolbar.map((section=>("content"===section.name&&(section.items.some(((item,index)=>!!item.match(/(media|video)\b/)&&(section.items.splice(index+1,0,_common.audioButtonName,_common.videoButtonName),!0)))||section.items.unshift(_common.audioButtonName,_common.videoButtonName)),section)))),menu:configureMenu(instanceConfig.menu)};var toolbar}}));
//# sourceMappingURL=configuration.min.js.map

View file

@ -0,0 +1 @@
{"version":3,"file":"configuration.min.js","sources":["../src/configuration.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Tiny Record RTC configuration.\n *\n * @module tiny_recordrtc/configuration\n * @copyright 2022, Stevani Andolo <stevani@hotmail.com.au>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {\n audioButtonName,\n videoButtonName\n} from './common';\nimport {\n addMenubarItem,\n} from 'editor_tiny/utils';\n\nconst configureMenu = (menu) => {\n const items = menu.insert.items.split(' ');\n const inserted = items.some((item, index) => {\n // Append after the media or video button.\n if (item.match(/(media|video)\\b/)) {\n items.splice(index + 1, 0, audioButtonName, videoButtonName);\n return true;\n }\n\n return false;\n });\n\n if (inserted) {\n menu.insert.items = items.join(' ');\n } else {\n addMenubarItem(menu, 'insert', `${audioButtonName} ${videoButtonName}`);\n }\n\n return menu;\n};\n\nconst configureToolbar = (toolbar) => {\n // The toolbar contains an array of named sections.\n // The Moodle integration ensures that there is a section called 'content'.\n\n\n return toolbar.map((section) => {\n if (section.name === 'content') {\n const inserted = section.items.some((item, index) => {\n // Append after the media or video button.\n if (item.match(/(media|video)\\b/)) {\n section.items.splice(index + 1, 0, audioButtonName, videoButtonName);\n return true;\n }\n return false;\n });\n\n if (!inserted) {\n section.items.unshift(audioButtonName, videoButtonName);\n }\n }\n\n return section;\n });\n};\n\nexport const configure = (instanceConfig) => {\n // Update the instance configuration to add the Media menu option to the menus and toolbars and upload_handler.\n return {\n toolbar: configureToolbar(instanceConfig.toolbar),\n menu: configureMenu(instanceConfig.menu),\n };\n};\n"],"names":["configureMenu","menu","items","insert","split","some","item","index","match","splice","audioButtonName","videoButtonName","join","instanceConfig","toolbar","map","section","name","unshift"],"mappings":";;;;;;;;MA+BMA,cAAiBC,aACbC,MAAQD,KAAKE,OAAOD,MAAME,MAAM,YACrBF,MAAMG,MAAK,CAACC,KAAMC,UAE3BD,KAAKE,MAAM,qBACXN,MAAMO,OAAOF,MAAQ,EAAG,EAAGG,wBAAiBC,0BACrC,KAOXV,KAAKE,OAAOD,MAAQA,MAAMU,KAAK,+BAEhBX,KAAM,mBAAaS,oCAAmBC,0BAGlDV,yBA4BeY,uBAEf,CACHC,SA5BkBA,QA4BQD,eAAeC,QAvBtCA,QAAQC,KAAKC,UACK,YAAjBA,QAAQC,OACSD,QAAQd,MAAMG,MAAK,CAACC,KAAMC,UAEnCD,KAAKE,MAAM,qBACXQ,QAAQd,MAAMO,OAAOF,MAAQ,EAAG,EAAGG,wBAAiBC,0BAC7C,MAMXK,QAAQd,MAAMgB,QAAQR,wBAAiBC,0BAIxCK,YAQPf,KAAMD,cAAca,eAAeZ,OA7BjBa,IAAAA"}

View file

@ -0,0 +1,10 @@
define("tiny_recordrtc/modal",["exports","core/modal"],(function(_exports,_modal){var obj;
/**
* Abstract Recording Modal for TinyMCE's RecordRTC plugin.
*
* @module tiny_recordrtc/modal
* @copyright 2022 Andrew Lyons <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_modal=(obj=_modal)&&obj.__esModule?obj:{default:obj};class _default extends _modal.default{registerEventListeners(){this.setRemoveOnClose(!0),super.registerEventListeners(),this.registerCloseOnSave(),this.registerCloseOnCancel()}}return _exports.default=_default,_exports.default}));
//# sourceMappingURL=modal.min.js.map

View file

@ -0,0 +1 @@
{"version":3,"file":"modal.min.js","sources":["../src/modal.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Abstract Recording Modal for TinyMCE's RecordRTC plugin.\n *\n * @module tiny_recordrtc/modal\n * @copyright 2022 Andrew Lyons <andrew@nicols.co.uk>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Modal from 'core/modal';\n\nexport default class extends Modal {\n registerEventListeners() {\n // Remove this Modal when it is closed.\n // This must be called before registering any other event listeners.\n this.setRemoveOnClose(true);\n\n // Call the parent registration.\n super.registerEventListeners();\n\n // Register to close on save/cancel.\n this.registerCloseOnSave();\n this.registerCloseOnCancel();\n }\n}\n"],"names":["Modal","registerEventListeners","setRemoveOnClose","registerCloseOnSave","registerCloseOnCancel"],"mappings":";;;;;;;kKAyB6BA,eACzBC,8BAGSC,kBAAiB,SAGhBD,8BAGDE,2BACAC"}

View file

@ -0,0 +1,11 @@
define("tiny_recordrtc/options",["exports","./common"],(function(_exports,_common){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.register=_exports.getData=void 0;
/**
* Options helper for Tiny Record RTC plugin.
*
* @module tiny_recordrtc/options
* @copyright 2022, Stevani Andolo <stevani@hotmail.com.au>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
const dataName="".concat(_common.pluginName,":data");_exports.register=editor=>{(0,editor.options.register)(dataName,{processor:"object"})};_exports.getData=editor=>editor.options.get(dataName)}));
//# sourceMappingURL=options.min.js.map

View file

@ -0,0 +1 @@
{"version":3,"file":"options.min.js","sources":["../src/options.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Options helper for Tiny Record RTC plugin.\n *\n * @module tiny_recordrtc/options\n * @copyright 2022, Stevani Andolo <stevani@hotmail.com.au>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {pluginName} from './common';\n\nconst dataName = `${pluginName}:data`;\n\nexport const register = (editor) => {\n const registerOption = editor.options.register;\n\n registerOption(dataName, {\n processor: 'object',\n });\n};\n\nexport const getData = (editor) => editor.options.get(dataName);\n"],"names":["dataName","pluginName","editor","registerOption","options","register","processor","get"],"mappings":";;;;;;;;MAyBMA,mBAAcC,8CAEKC,UAGrBC,EAFuBD,OAAOE,QAAQC,UAEvBL,SAAU,CACrBM,UAAW,6BAIKJ,QAAWA,OAAOE,QAAQG,IAAIP"}

View file

@ -0,0 +1,10 @@
define("tiny_recordrtc/plugin",["exports","editor_tiny/loader","editor_tiny/utils","./commands_audio","./commands_video","./configuration","./options","./common"],(function(_exports,_loader,_utils,_commands_audio,_commands_video,Configuration,Options,_common){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireWildcard(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}
/**
* Tiny Record RTC plugin for Moodle.
*
* @module tiny_recordrtc/plugin
* @copyright 2022, Stevani Andolo <stevani@hotmail.com.au>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_commands_audio=_interopRequireDefault(_commands_audio),_commands_video=_interopRequireDefault(_commands_video),Configuration=_interopRequireWildcard(Configuration),Options=_interopRequireWildcard(Options);var _default=new Promise((async resolve=>{const[tinyMCE,setupAudioCommands,setupVideoCommands,pluginMetadata]=await Promise.all([(0,_loader.getTinyMCE)(),(0,_commands_audio.default)(),(0,_commands_video.default)(),(0,_utils.getPluginMetadata)(_common.component,_common.pluginName)]);tinyMCE.PluginManager.add("".concat(_common.component,"/plugin"),(editor=>(Options.register(editor),setupVideoCommands(editor),setupAudioCommands(editor),pluginMetadata))),resolve(["".concat(_common.component,"/plugin"),Configuration])}));return _exports.default=_default,_exports.default}));
//# sourceMappingURL=plugin.min.js.map

View file

@ -0,0 +1 @@
{"version":3,"file":"plugin.min.js","sources":["../src/plugin.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Tiny Record RTC plugin for Moodle.\n *\n * @module tiny_recordrtc/plugin\n * @copyright 2022, Stevani Andolo <stevani@hotmail.com.au>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport {getTinyMCE} from 'editor_tiny/loader';\nimport {getPluginMetadata} from 'editor_tiny/utils';\nimport getSetupAudioCommands from './commands_audio';\nimport getSetupVideoCommands from './commands_video';\nimport * as Configuration from './configuration';\nimport * as Options from './options';\nimport {\n component,\n pluginName\n} from './common';\n\nexport default new Promise(async(resolve) => {\n const [\n tinyMCE,\n setupAudioCommands,\n setupVideoCommands,\n pluginMetadata,\n ] = await Promise.all([\n getTinyMCE(),\n getSetupAudioCommands(),\n getSetupVideoCommands(),\n getPluginMetadata(component, pluginName),\n ]);\n\n tinyMCE.PluginManager.add(`${component}/plugin`, (editor) => {\n // Register options.\n Options.register(editor);\n\n // Setup the Commands (buttons, menu items, and so on) for video.\n setupVideoCommands(editor);\n\n // Setup the Commands (buttons, menu items, and so on) for audio.\n setupAudioCommands(editor);\n\n return pluginMetadata;\n });\n\n // Resolve the Media Plugin and include configuration.\n resolve([`${component}/plugin`, Configuration]);\n});\n"],"names":["Promise","async","tinyMCE","setupAudioCommands","setupVideoCommands","pluginMetadata","all","component","pluginName","PluginManager","add","editor","Options","register","resolve","Configuration"],"mappings":";;;;;;;gTAiCe,IAAIA,SAAQC,MAAAA,gBAEnBC,QACAC,mBACAC,mBACAC,sBACML,QAAQM,IAAI,EAClB,yBACA,8BACA,8BACA,4BAAkBC,kBAAWC,sBAGjCN,QAAQO,cAAcC,cAAOH,8BAAqBI,SAE9CC,QAAQC,SAASF,QAGjBP,mBAAmBO,QAGnBR,mBAAmBQ,QAEZN,kBAIXS,QAAQ,WAAIP,6BAAoBQ"}

View file

@ -0,0 +1,3 @@
define("tiny_recordrtc/video_recorder",["exports","./base_recorder","tiny_recordrtc/modal","core/modal_registry","tiny_recordrtc/common"],(function(_exports,_base_recorder,_modal,_modal_registry,_common){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _defineProperty(obj,key,value){return key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,obj}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_base_recorder=_interopRequireDefault(_base_recorder),_modal=_interopRequireDefault(_modal),_modal_registry=_interopRequireDefault(_modal_registry);class Video extends _base_recorder.default{configurePlayer(){return this.modalRoot.querySelector("video")}getSupportedTypes(){return["video/webm;codecs=vp9,opus","video/webm;codecs=h264,opus","video/webm;codecs=vp8,opus"]}getParsedRecordingOptions(){return{audioBitsPerSecond:parseInt(this.config.audiobitrate),videoBitsPerSecond:parseInt(this.config.videobitrate)}}getMediaConstraints(){return{audio:!0,video:{width:{ideal:640},height:{ideal:480}}}}playOnCapture(){return!0}getRecordingType(){return"video"}getTimeLimit(){return this.config.videotimelimit}getEmbedTemplateName(){return"tiny_recordrtc/embed_video"}getFileName(prefix){return"".concat(prefix,"-video.webm")}static getModalClass(){var _class;const modalType="".concat(_common.component,"/video_recorder"),registration=_modal_registry.default.get(modalType);if(registration)return registration.module;const VideoModal=(_defineProperty(_class=class extends _modal.default{},"TYPE",modalType),_defineProperty(_class,"TEMPLATE","".concat(_common.component,"/video_recorder")),_class);return _modal_registry.default.register(VideoModal.TYPE,VideoModal,VideoModal.TEMPLATE),VideoModal}}return _exports.default=Video,_exports.default}));
//# sourceMappingURL=video_recorder.min.js.map

View file

@ -0,0 +1 @@
{"version":3,"file":"video_recorder.min.js","sources":["../src/video_recorder.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Tiny Record RTC - Video recorder configuration.\n *\n * @module tiny_recordrtc/video\n * @copyright 2022 Stevani Andolo <stevani@hotmail.com.au>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport BaseClass from './base_recorder';\nimport Modal from 'tiny_recordrtc/modal';\nimport ModalRegistry from 'core/modal_registry';\nimport {component} from 'tiny_recordrtc/common';\n\nexport default class Video extends BaseClass {\n configurePlayer() {\n return this.modalRoot.querySelector('video');\n }\n\n getSupportedTypes() {\n return [\n 'video/webm;codecs=vp9,opus',\n 'video/webm;codecs=h264,opus',\n 'video/webm;codecs=vp8,opus',\n ];\n\n }\n\n getParsedRecordingOptions() {\n return {\n audioBitsPerSecond: parseInt(this.config.audiobitrate),\n videoBitsPerSecond: parseInt(this.config.videobitrate)\n };\n }\n\n getMediaConstraints() {\n return {\n audio: true,\n video: {\n width: {\n ideal: 640,\n },\n height: {\n ideal: 480,\n },\n },\n };\n }\n\n playOnCapture() {\n // Play the recording back on capture.\n return true;\n }\n\n getRecordingType() {\n return 'video';\n }\n\n getTimeLimit() {\n return this.config.videotimelimit;\n }\n\n getEmbedTemplateName() {\n return 'tiny_recordrtc/embed_video';\n }\n\n getFileName(prefix) {\n return `${prefix}-video.webm`;\n }\n\n static getModalClass() {\n const modalType = `${component}/video_recorder`;\n const registration = ModalRegistry.get(modalType);\n if (registration) {\n return registration.module;\n }\n\n const VideoModal = class extends Modal {\n static TYPE = modalType;\n static TEMPLATE = `${component}/video_recorder`;\n };\n\n ModalRegistry.register(VideoModal.TYPE, VideoModal, VideoModal.TEMPLATE);\n return VideoModal;\n }\n}\n"],"names":["Video","BaseClass","configurePlayer","this","modalRoot","querySelector","getSupportedTypes","getParsedRecordingOptions","audioBitsPerSecond","parseInt","config","audiobitrate","videoBitsPerSecond","videobitrate","getMediaConstraints","audio","video","width","ideal","height","playOnCapture","getRecordingType","getTimeLimit","videotimelimit","getEmbedTemplateName","getFileName","prefix","modalType","component","registration","ModalRegistry","get","module","VideoModal","Modal","register","TYPE","TEMPLATE"],"mappings":"4qBA4BqBA,cAAcC,uBAC/BC,yBACWC,KAAKC,UAAUC,cAAc,SAGxCC,0BACW,CACH,6BACA,8BACA,8BAKRC,kCACW,CACHC,mBAAoBC,SAASN,KAAKO,OAAOC,cACzCC,mBAAoBH,SAASN,KAAKO,OAAOG,eAIjDC,4BACW,CACHC,OAAO,EACPC,MAAO,CACHC,MAAO,CACHC,MAAO,KAEXC,OAAQ,CACJD,MAAO,OAMvBE,uBAEW,EAGXC,yBACW,QAGXC,sBACWnB,KAAKO,OAAOa,eAGvBC,6BACW,6BAGXC,YAAYC,wBACEA,8DAIJC,oBAAeC,qCACfC,aAAeC,wBAAcC,IAAIJ,cACnCE,oBACOA,aAAaG,aAGlBC,mCAAa,cAAcC,wBACfP,uDACOC,6EAGXO,SAASF,WAAWG,KAAMH,WAAYA,WAAWI,UACxDJ"}

View file

@ -0,0 +1,84 @@
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Tiny Record RTC - audio recorder configuration.
*
* @module tiny_recordrtc/audio
* @copyright 2022 Stevani Andolo <stevani@hotmail.com.au>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
import BaseClass from './base_recorder';
import Modal from './modal';
import ModalRegistry from 'core/modal_registry';
import {component} from 'tiny_recordrtc/common';
export default class Audio extends BaseClass {
configurePlayer() {
return this.modalRoot.querySelector('audio');
}
getSupportedTypes() {
return [
'audio/webm;codecs=opus',
'audio/ogg;codecs=opus',
];
}
getParsedRecordingOptions() {
return {
audioBitsPerSecond: parseInt(this.config.audiobitrate),
};
}
getMediaConstraints() {
return {
audio: true,
};
}
getRecordingType() {
return 'audio';
}
getTimeLimit() {
return this.config.audiotimelimit;
}
getEmbedTemplateName() {
return 'tiny_recordrtc/embed_audio';
}
getFileName(prefix) {
return `${prefix}-audio.ogg`;
}
static getModalClass() {
const modalType = `${component}/audio_recorder`;
const registration = ModalRegistry.get(modalType);
if (registration) {
return registration.module;
}
const AudioModal = class extends Modal {
static TYPE = modalType;
static TEMPLATE = `${component}/audio_recorder`;
};
ModalRegistry.register(AudioModal.TYPE, AudioModal, AudioModal.TEMPLATE);
return AudioModal;
}
}

View file

@ -0,0 +1,840 @@
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
//
/**
* Tiny Record RTC type.
*
* @module tiny_recordrtc/recording/base
* @copyright 2022 Stevani Andolo <stevani@hotmail.com.au>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
import {get_string as getString, get_strings as getStrings} from 'core/str';
import {component} from './common';
import Pending from 'core/pending';
import {getData} from './options';
import uploadFile from 'editor_tiny/uploader';
import {add as addToast} from 'core/toast';
import * as ModalEvents from 'core/modal_events';
import * as ModalFactory from 'core/modal_factory';
import * as Templates from 'core/templates';
import {saveCancelPromise} from 'core/notification';
import {prefetchStrings, prefetchTemplates} from 'core/prefetch';
/**
* The RecordRTC base class for audio, video, and any other future types
*/
export default class {
stopRequested = false;
/**
* Constructor for the RecordRTC class
*
* @param {TinyMCE} editor The Editor to which the content will be inserted
* @param {Modal} modal The Moodle Modal that contains the interface used for recording
*/
constructor(editor, modal) {
this.ready = false;
if (!this.checkAndWarnAboutBrowserCompatibility()) {
return;
}
this.editor = editor;
this.config = getData(editor).params;
this.modal = modal;
this.modalRoot = modal.getRoot()[0];
this.startStopButton = this.modalRoot.querySelector('button[data-action="startstop"]');
this.uploadButton = this.modalRoot.querySelector('button[data-action="upload"]');
// Disable the record button untilt he stream is acquired.
this.setRecordButtonState(false);
this.player = this.configurePlayer();
this.registerEventListeners();
this.ready = true;
this.captureUserMedia();
this.prefetchContent();
}
/**
* Check whether the browser is compatible.
*
* @returns {boolean}
*/
isReady() {
return this.ready;
}
// Disable eslint's valid-jsdoc rule as the following methods are abstract and mnust be overridden by the child class.
/* eslint-disable valid-jsdoc, no-unused-vars */
/**
* Get the Player element for this type.
*
* @returns {HTMLElement} The player element, typically an audio or video tag.
*/
configurePlayer() {
throw new Error(`configurePlayer() must be implemented in ${this.constructor.name}`);
}
/**
* Get the list of supported mimetypes for this recorder.
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/MediaRecorder/isTypeSupported}
*
* @returns {string[]} The list of supported mimetypes.
*/
getSupportedTypes() {
throw new Error(`getSupportedTypes() must be implemented in ${this.constructor.name}`);
}
/**
* Get any recording options passed into the MediaRecorder.
* Please note that the mimeType will be fetched from {@link getSupportedTypes()}.
*
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/MediaRecorder/MediaRecorder#options}
* @returns {Object}
*/
getRecordingOptions() {
throw new Error(`getRecordingOptions() must be implemented in ${this.constructor.name}`);
}
/**
* Get a filename for the generated file.
*
* Typically this function will take a prefix and add a type-specific suffix such as the extension to it.
*
* @param {string} prefix The prefix for the filename generated by the recorder.
* @returns {string}
*/
getFileName(prefix) {
throw new Error(`getFileName() must be implemented in ${this.constructor.name}`);
}
/**
* Get a list of constraints as required by the getUserMedia() function.
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia#constraints}
*
* @returns {Object}
*/
getMediaConstraints() {
throw new Error(`getMediaConstraints() must be implemented in ${this.constructor.name}`);
}
/**
* Whether to start playing the recording as it is captured.
* @returns {boolean} Whether to start playing the recording as it is captured.
*/
playOnCapture() {
return false;
}
/**
* Get the time limit for this recording type.
*
* @returns {number} The time limit in seconds.
*/
getTimeLimit() {
throw new Error(`getTimeLimit() must be implemented in ${this.constructor.name}`);
}
/**
* Get the name of the template used when embedding the URL in the editor content.
*
* @returns {string}
*/
getEmbedTemplateName() {
throw new Error(`getEmbedTemplateName() must be implemented in ${this.constructor.name}`);
}
/**
* Fetch the Class of the Modal to be displayed.
*
* @returns {Modal}
*/
static getModalClass() {
throw new Error(`getModalClass() must be implemented in ${this.constructor.name}`);
}
/* eslint-enable valid-jsdoc, no-unused-vars */
/**
* Get the options for the MediaRecorder.
*
* @returns {object} The options for the MediaRecorder instance.
*/
getParsedRecordingOptions() {
const types = this.getSupportedTypes();
const options = this.getParsedRecordingOptions();
const compatTypes = types.filter((type) => window.MediaRecorder.isTypeSupported(type));
if (compatTypes.length !== 0) {
options.mimeType = compatTypes[0];
}
return options;
}
/**
* Start capturing the User Media and handle success or failure of the capture.
*/
async captureUserMedia() {
try {
const stream = await navigator.mediaDevices.getUserMedia(this.getMediaConstraints());
this.handleCaptureSuccess(stream);
} catch (error) {
this.handleCaptureFailure(error);
}
}
/**
* Prefetch some of the content that will be used in the UI.
*
* Note: not all of the strings used are pre-fetched.
* Some of the strings will be fetched because their template is used.
*/
prefetchContent() {
prefetchStrings(component, [
'uploading',
'recordagain_title',
'recordagain_desc',
'discard_title',
'discard_desc',
'confirm_yes',
'recordinguploaded',
'maxfilesizehit',
'maxfilesizehit_title',
'uploadfailed',
]);
prefetchTemplates([
this.getEmbedTemplateName(),
'tiny_recordrtc/timeremaining',
]);
}
/**
* Display an error message to the user.
*
* @param {Promise<string>} title The error title
* @param {Promise<string>} content The error message
* @returns {Promise<Modal>}
*/
async displayAlert(title, content) {
const pendingPromise = new Pending('core/confirm:alert');
const ModalFactory = await import('core/modal_factory');
const modal = await ModalFactory.create({
type: ModalFactory.types.ALERT,
title: title,
body: content,
removeOnClose: true,
});
modal.show();
pendingPromise.resolve();
return modal;
}
/**
* Handle successful capture of the User Media.
*
* @param {MediaStream} stream The stream as captured by the User Media.
*/
handleCaptureSuccess(stream) {
// Set audio player source to microphone stream.
this.player.srcObject = stream;
if (this.playOnCapture()) {
// Mute audio, distracting while recording.
this.player.muted = true;
this.player.play();
}
this.stream = stream;
this.setupPlayerSource();
this.setRecordButtonState(true);
}
/**
* Setup the player to use the stream as a source.
*/
setupPlayerSource() {
if (!this.player.srcObject) {
this.player.srcObject = this.stream;
// Mute audio, distracting while recording.
this.player.muted = true;
this.player.play();
}
}
/**
* Enable the record button.
*
* @param {boolean|null} enabled Set the button state
*/
setRecordButtonState(enabled) {
this.startStopButton.disabled = !enabled;
}
/**
* Configure button visibility for the record button.
*
* @param {boolean} visible Set the visibility of the button.
*/
setRecordButtonVisibility(visible) {
const container = this.getButtonContainer('start-stop');
container.classList.toggle('hide', !visible);
}
/**
* Enable the upload button.
*
* @param {boolean|null} enabled Set the button state
*/
setUploadButtonState(enabled) {
this.uploadButton.disabled = !enabled;
}
/**
* Configure button visibility for the upload button.
*
* @param {boolean} visible Set the visibility of the button.
*/
setUploadButtonVisibility(visible) {
const container = this.getButtonContainer('upload');
container.classList.toggle('hide', !visible);
}
/**
* Handle failure to capture the User Media.
*
* @param {Error} error
*/
handleCaptureFailure(error) {
// Changes 'CertainError' -> 'gumcertain' to match language string names.
var subject = `gum${error.name.replace('Error', '').toLowerCase()}`;
this.displayAlert(
getString(`${subject}_title`, component),
getString(subject, component)
);
}
/**
* Close the modal and stop recording.
*/
close() {
// Closing the modal will destroy it and remove it from the DOM.
// It will also stop the recording via the hidden Modal Event.
this.modal.hide();
}
/**
* Register event listeners for the modal.
*/
registerEventListeners() {
this.modalRoot.addEventListener('click', this.handleModalClick.bind(this));
this.modal.getRoot().on(ModalEvents.outsideClick, this.outsideClickHandler.bind(this));
this.modal.getRoot().on(ModalEvents.hidden, () => {
this.cleanupStream();
this.requestRecordingStop();
});
}
/**
* Prevent the Modal from closing when recording is on process.
*
* @param {MouseEvent} event The click event
*/
async outsideClickHandler(event) {
if (this.isRecording()) {
// The user is recording.
// Do not distract with a confirmation, just prevent closing.
event.preventDefault();
} else if (this.hasData()) {
// If there is a blobsize then there is data that may be lost.
// Ask the user to confirm they want to close the modal.
// We prevent default here, and then close the modal if they confirm.
event.preventDefault();
try {
await saveCancelPromise(
await getString("discard_title", component),
await getString("discard_desc", component),
await getString("confirm_yes", component),
);
this.modal.hide();
} catch (error) {
// Do nothing, the modal will not close.
}
}
}
/**
* Handle a click within the Modal.
*
* @param {MouseEvent} event The click event
*/
handleModalClick(event) {
const button = event.target.closest('button');
if (button && button.dataset.action) {
const action = button.dataset.action;
if (action === 'startstop') {
this.handleRecordingStartStopRequested();
}
if (action === 'upload') {
this.uploadRecording();
}
}
}
/**
* Handle the click event for the recording start/stop button.
*/
handleRecordingStartStopRequested() {
if (this.mediaRecorder?.state === 'recording') {
this.requestRecordingStop();
} else {
this.startRecording();
}
}
/**
* Handle the media stream after it has finished.
*/
async onMediaStopped() {
// Set source of audio player.
this.blob = new Blob(this.data.chunks, {
type: this.mediaRecorder.mimeType
});
this.player.srcObject = null;
this.player.src = URL.createObjectURL(this.blob);
// Change the label to "Record again".
this.setRecordButtonTextFromString('recordagain');
// Show audio player with controls enabled, and unmute.
this.player.muted = false;
this.player.controls = true;
this.getButtonContainer('player')?.classList.toggle('hide', false);
// Show upload button.
this.setUploadButtonVisibility(true);
this.setUploadButtonState(true);
}
/**
* Upload the recording and insert it into the editor content.
*/
async uploadRecording() {
// Trigger error if no recording has been made.
if (this.data.chunks.length === 0) {
this.displayAlert('norecordingfound');
return;
}
const fileName = this.getFileName((Math.random() * 1000).toString().replace('.', ''));
// Upload recording to server.
try {
// Once uploading starts, do not allow any further changes to the recording.
this.setRecordButtonVisibility(false);
// Disable the upload button.
this.setUploadButtonState(false);
// Upload the recording.
const fileURL = await uploadFile(this.editor, 'media', this.blob, fileName, (progress) => {
this.setUploadButtonTextProgress(progress);
});
this.insertMedia(fileURL);
this.close();
addToast(await getString('recordinguploaded', component));
} catch (error) {
// Show a toast and unhide the button.
this.setUploadButtonState(true);
addToast(await getString('uploadfailed', component, {error}), {
type: 'error',
});
}
}
/**
* Helper to get the container that a button is in.
*
* @param {string} purpose The button purpose
* @returns {HTMLElement}
*/
getButtonContainer(purpose) {
return this.modalRoot.querySelector(`[data-purpose="${purpose}-container"]`);
}
/**
* Check whether the browser is compatible with capturing media.
*
* @returns {boolean}
*/
static isBrowserCompatible() {
return this.checkSecure() && this.hasUserMedia();
}
static async display(editor) {
const ModalClass = this.getModalClass();
const modal = await ModalFactory.create({
type: ModalClass.TYPE,
templateContext: {},
large: true,
});
// Set up the VideoRecorder.
const recorder = new this(editor, modal);
if (recorder.isReady()) {
modal.show();
}
return modal;
}
/**
* Check whether the browser is compatible with capturing media, and display a warning if not.
*
* @returns {boolean}
*/
checkAndWarnAboutBrowserCompatibility() {
if (!this.constructor.checkSecure()) {
getStrings(['insecurealert_title', 'insecurealert'].map((key) => ({key, component})))
.then(([title, message]) => addToast(message, {title, type: 'error'}))
.catch();
return false;
}
if (!this.constructor.hasUserMedia) {
getStrings(['nowebrtc_title', 'nowebrtc'].map((key) => ({key, component})))
.then(([title, message]) => addToast(message, {title, type: 'error'}))
.catch();
return false;
}
return true;
}
/**
* Check whether the browser supports WebRTC.
*
* @returns {boolean}
*/
static hasUserMedia() {
return (navigator.mediaDevices && window.MediaRecorder);
}
/**
* Check whether the hostname is either hosted over SSL, or from a valid localhost hostname.
*
* The UserMedia API can only be used in secure contexts as noted.
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia#privacy_and_security}
*
* @returns {boolean} Whether the plugin can be loaded.
*/
static checkSecure() {
// Note: We can now use window.isSecureContext.
// https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts#feature_detection
// https://developer.mozilla.org/en-US/docs/Web/API/isSecureContext
return window.isSecureContext;
}
/**
* Update the content of the stop recording button timer.
*/
async setStopRecordingButton() {
const {html, js} = await Templates.renderForPromise('tiny_recordrtc/timeremaining', this.getTimeRemaining());
Templates.replaceNodeContents(this.startStopButton, html, js);
this.buttonTimer = setInterval(this.updateRecordButtonTime.bind(this), 500);
}
/**
* Update the time on the stop recording button.
*/
updateRecordButtonTime() {
const {remaining, minutes, seconds} = this.getTimeRemaining();
if (remaining < 0) {
this.requestRecordingStop();
} else {
this.startStopButton.querySelector('[data-type="minutes"]').textContent = minutes;
this.startStopButton.querySelector('[data-type="seconds"]').textContent = seconds;
}
}
/**
* Set the text of the record button using a language string.
*
* @param {string} string The string identifier
*/
async setRecordButtonTextFromString(string) {
this.startStopButton.textContent = await getString(string, component);
}
/**
* Set the upload button text progress.
*
* @param {number} progress The progress
*/
async setUploadButtonTextProgress(progress) {
this.uploadButton.textContent = await getString('uploading', component, {
progress: Math.round(progress * 100) / 100,
});
}
async resetUploadButtonText() {
this.uploadButton.textContent = await getString('upload', component);
}
/**
* Clear the timer for the stop recording button.
*/
clearButtonTimer() {
if (this.buttonTimer) {
clearInterval(this.buttonTimer);
}
this.buttonTimer = null;
}
/**
* Get the time remaining for the recording.
*
* @returns {Object} The minutes and seconds remaining.
*/
getTimeRemaining() {
// All times are in milliseconds
const now = new Date().getTime();
const remaining = Math.floor(this.getTimeLimit() - ((now - this.startTime) / 1000));
const formatter = new Intl.NumberFormat(navigator.language, {minimumIntegerDigits: 2});
const seconds = formatter.format(remaining % 60);
const minutes = formatter.format(Math.floor((remaining - seconds) / 60));
return {
remaining,
minutes,
seconds,
};
}
/**
* Get the maximum file size that can be uploaded.
*
* @returns {number} The max byte size
*/
getMaxUploadSize() {
return this.config.maxrecsize;
}
/**
* Stop the recording.
* Please note that this should only stop the recording.
* Anything related to processing the recording should be handled by the
* mediaRecorder's stopped event handler which is processed after it has stopped.
*/
requestRecordingStop() {
if (this.mediaRecorder && this.mediaRecorder.state !== 'inactive') {
this.stopRequested = true;
} else {
// There is no recording to stop, but the stream must still be cleaned up.
this.cleanupStream();
}
}
stopRecorder() {
this.mediaRecorder.stop();
// Unmute the player so that the audio is heard during playback.
this.player.muted = false;
}
/**
* Clean up the stream.
*
* This involves stopping any track which is still active.
*/
cleanupStream() {
if (this.stream) {
this.stream.getTracks()
.filter((track) => track.readyState !== 'ended')
.forEach((track) => track.stop());
}
}
/**
* Handle the mediaRecorder `stop` event.
*/
handleStopped() {
// Handle the stream data.
this.onMediaStopped();
// Clear the button timer.
this.clearButtonTimer();
}
/**
* Handle the mediaRecorder `start` event.
*
* This event is called when the recording starts.
*/
handleStarted() {
this.startTime = new Date().getTime();
this.setStopRecordingButton();
}
/**
* Handle the mediaRecorder `dataavailable` event.
*
* @param {Event} event
*/
handleDataAvailable(event) {
if (this.isRecording()) {
const newSize = this.data.blobSize + event.data.size;
// Recording stops when either the maximum upload size is reached, or the time limit expires.
// The time limit is checked in the `updateButtonTime` function.
if (newSize >= this.getMaxUploadSize()) {
this.stopRecorder();
this.displayFileLimitHitMessage();
} else {
// Push recording slice to array.
this.data.chunks.push(event.data);
// Size of all recorded data so far.
this.data.blobSize = newSize;
if (this.stopRequested) {
this.stopRecorder();
}
}
}
}
async displayFileLimitHitMessage() {
addToast(await getString('maxfilesizehit', component), {
title: await getString('maxfilesizehit_title', component),
type: 'error',
});
}
/**
* Check whether the recording is in progress.
*
* @returns {boolean}
*/
isRecording() {
return this.mediaRecorder?.state === 'recording';
}
/**
* Whether any data has been recorded.
*
* @returns {boolean}
*/
hasData() {
return !!this.data?.blobSize;
}
/**
* Start the recording
*/
async startRecording() {
if (this.mediaRecorder) {
// Stop the existing recorder if it exists.
if (this.isRecording()) {
this.mediaRecorder.stop();
}
if (this.hasData()) {
const resetRecording = await this.recordAgainConfirmation();
if (!resetRecording) {
// User cancelled at the confirmation to reset the data, so exit early.
return;
}
this.setUploadButtonVisibility(false);
}
this.mediaRecorder = null;
}
// The options for the recording codecs and bitrates.
this.mediaRecorder = new MediaRecorder(this.stream, this.getParsedRecordingOptions());
this.mediaRecorder.addEventListener('dataavailable', this.handleDataAvailable.bind(this));
this.mediaRecorder.addEventListener('stop', this.handleStopped.bind(this));
this.mediaRecorder.addEventListener('start', this.handleStarted.bind(this));
this.data = {
chunks: [],
blobSize: 0
};
this.setupPlayerSource();
this.stopRequested = false;
// Capture in 50ms chunks.
this.mediaRecorder.start(50);
}
/**
* Confirm whether the user wants to reset the existing recoring.
*
* @returns {Promise<boolean>} Whether the user confirmed the reset.
*/
async recordAgainConfirmation() {
try {
await saveCancelPromise(
await getString("recordagain_title", component),
await getString("recordagain_desc", component),
await getString("confirm_yes", component)
);
return true;
} catch {
return false;
}
}
/**
* Insert the HTML to embed the recording into the editor content.
*
* @param {string} source The URL to view the media.
*/
async insertMedia(source) {
const {html} = await Templates.renderForPromise(
this.getEmbedTemplateName(),
this.getEmbedTemplateContext({
source,
})
);
this.editor.insertContent(html);
}
/**
* Add or modify the template parameters for the specified type.
*
* @param {Object} templateContext The Tempalte context to use
* @returns {Object} The finalised template context
*/
getEmbedTemplateContext(templateContext) {
return templateContext;
}
}

View file

@ -0,0 +1,62 @@
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Tiny Record RTC - record audio command.
*
* @module tiny_recordrtc/recordAudioCommands
* @copyright 2022, Stevani Andolo <stevani@hotmail.com.au>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
import {get_string as getString} from 'core/str';
import {getButtonImage} from 'editor_tiny/utils';
import {
audioButtonName,
component
} from './common';
import Recorder from './audio_recorder';
export default async() => {
if (!Recorder.isBrowserCompatible()) {
// The browser doesn't support the plugin, so just don't show it.
return () => false;
}
const [
audioButtonTitle,
audio,
] = await Promise.all([
getString('audiobuttontitle', component),
getButtonImage('audio', component),
]);
return (editor) => {
const icon = 'audio';
editor.ui.registry.addIcon(icon, audio.html);
editor.ui.registry.addButton(audioButtonName, {
icon,
tooltip: audioButtonTitle,
onAction: () => Recorder.display(editor),
});
editor.ui.registry.addMenuItem(audioButtonName, {
icon,
text: audioButtonTitle,
onAction: () => Recorder.display(editor),
});
};
};

View file

@ -0,0 +1,62 @@
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Tiny Record RTC - record video command.
*
* @module tiny_recordrtc/recordVideoCommands
* @copyright 2022, Stevani Andolo <stevani@hotmail.com.au>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
import {get_string as getString} from 'core/str';
import {getButtonImage as getVideoIcon} from 'editor_tiny/utils';
import {
videoButtonName,
component
} from './common';
import Recorder from './video_recorder';
export default async() => {
if (!Recorder.isBrowserCompatible()) {
// The browser doesn't support the plugin, so just don't show it.
return () => false;
}
const [
videoButtonTitle,
buttonImage,
] = await Promise.all([
getString('videobuttontitle', component),
getVideoIcon('video', component),
]);
return (editor) => {
let icon = 'video';
editor.ui.registry.addIcon(icon, buttonImage.html);
editor.ui.registry.addButton(videoButtonName, {
icon,
tooltip: videoButtonTitle,
onAction: () => Recorder.display(editor),
});
editor.ui.registry.addMenuItem(videoButtonName, {
icon,
text: videoButtonTitle,
onAction: () => Recorder.display(editor),
});
};
};

View file

@ -0,0 +1,29 @@
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Tiny Record RTC common values.
*
* @module tiny_recordrtc/common
* @copyright 2022, Stevani Andolo <stevani@hotmail.com.au>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
export default {
pluginName: 'tiny_recordrtc/plugin',
component: 'tiny_recordrtc',
audioButtonName: 'tiny_recordrtc_audio',
videoButtonName: 'tiny_recordrtc_video'
};

View file

@ -0,0 +1,84 @@
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Tiny Record RTC configuration.
*
* @module tiny_recordrtc/configuration
* @copyright 2022, Stevani Andolo <stevani@hotmail.com.au>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
import {
audioButtonName,
videoButtonName
} from './common';
import {
addMenubarItem,
} from 'editor_tiny/utils';
const configureMenu = (menu) => {
const items = menu.insert.items.split(' ');
const inserted = items.some((item, index) => {
// Append after the media or video button.
if (item.match(/(media|video)\b/)) {
items.splice(index + 1, 0, audioButtonName, videoButtonName);
return true;
}
return false;
});
if (inserted) {
menu.insert.items = items.join(' ');
} else {
addMenubarItem(menu, 'insert', `${audioButtonName} ${videoButtonName}`);
}
return menu;
};
const configureToolbar = (toolbar) => {
// The toolbar contains an array of named sections.
// The Moodle integration ensures that there is a section called 'content'.
return toolbar.map((section) => {
if (section.name === 'content') {
const inserted = section.items.some((item, index) => {
// Append after the media or video button.
if (item.match(/(media|video)\b/)) {
section.items.splice(index + 1, 0, audioButtonName, videoButtonName);
return true;
}
return false;
});
if (!inserted) {
section.items.unshift(audioButtonName, videoButtonName);
}
}
return section;
});
};
export const configure = (instanceConfig) => {
// Update the instance configuration to add the Media menu option to the menus and toolbars and upload_handler.
return {
toolbar: configureToolbar(instanceConfig.toolbar),
menu: configureMenu(instanceConfig.menu),
};
};

View file

@ -0,0 +1,39 @@
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Abstract Recording Modal for TinyMCE's RecordRTC plugin.
*
* @module tiny_recordrtc/modal
* @copyright 2022 Andrew Lyons <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
import Modal from 'core/modal';
export default class extends Modal {
registerEventListeners() {
// Remove this Modal when it is closed.
// This must be called before registering any other event listeners.
this.setRemoveOnClose(true);
// Call the parent registration.
super.registerEventListeners();
// Register to close on save/cancel.
this.registerCloseOnSave();
this.registerCloseOnCancel();
}
}

View file

@ -0,0 +1,36 @@
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Options helper for Tiny Record RTC plugin.
*
* @module tiny_recordrtc/options
* @copyright 2022, Stevani Andolo <stevani@hotmail.com.au>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
import {pluginName} from './common';
const dataName = `${pluginName}:data`;
export const register = (editor) => {
const registerOption = editor.options.register;
registerOption(dataName, {
processor: 'object',
});
};
export const getData = (editor) => editor.options.get(dataName);

View file

@ -0,0 +1,62 @@
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Tiny Record RTC plugin for Moodle.
*
* @module tiny_recordrtc/plugin
* @copyright 2022, Stevani Andolo <stevani@hotmail.com.au>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
import {getTinyMCE} from 'editor_tiny/loader';
import {getPluginMetadata} from 'editor_tiny/utils';
import getSetupAudioCommands from './commands_audio';
import getSetupVideoCommands from './commands_video';
import * as Configuration from './configuration';
import * as Options from './options';
import {
component,
pluginName
} from './common';
export default new Promise(async(resolve) => {
const [
tinyMCE,
setupAudioCommands,
setupVideoCommands,
pluginMetadata,
] = await Promise.all([
getTinyMCE(),
getSetupAudioCommands(),
getSetupVideoCommands(),
getPluginMetadata(component, pluginName),
]);
tinyMCE.PluginManager.add(`${component}/plugin`, (editor) => {
// Register options.
Options.register(editor);
// Setup the Commands (buttons, menu items, and so on) for video.
setupVideoCommands(editor);
// Setup the Commands (buttons, menu items, and so on) for audio.
setupAudioCommands(editor);
return pluginMetadata;
});
// Resolve the Media Plugin and include configuration.
resolve([`${component}/plugin`, Configuration]);
});

View file

@ -0,0 +1,100 @@
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Tiny Record RTC - Video recorder configuration.
*
* @module tiny_recordrtc/video
* @copyright 2022 Stevani Andolo <stevani@hotmail.com.au>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
import BaseClass from './base_recorder';
import Modal from 'tiny_recordrtc/modal';
import ModalRegistry from 'core/modal_registry';
import {component} from 'tiny_recordrtc/common';
export default class Video extends BaseClass {
configurePlayer() {
return this.modalRoot.querySelector('video');
}
getSupportedTypes() {
return [
'video/webm;codecs=vp9,opus',
'video/webm;codecs=h264,opus',
'video/webm;codecs=vp8,opus',
];
}
getParsedRecordingOptions() {
return {
audioBitsPerSecond: parseInt(this.config.audiobitrate),
videoBitsPerSecond: parseInt(this.config.videobitrate)
};
}
getMediaConstraints() {
return {
audio: true,
video: {
width: {
ideal: 640,
},
height: {
ideal: 480,
},
},
};
}
playOnCapture() {
// Play the recording back on capture.
return true;
}
getRecordingType() {
return 'video';
}
getTimeLimit() {
return this.config.videotimelimit;
}
getEmbedTemplateName() {
return 'tiny_recordrtc/embed_video';
}
getFileName(prefix) {
return `${prefix}-video.webm`;
}
static getModalClass() {
const modalType = `${component}/video_recorder`;
const registration = ModalRegistry.get(modalType);
if (registration) {
return registration.module;
}
const VideoModal = class extends Modal {
static TYPE = modalType;
static TEMPLATE = `${component}/video_recorder`;
};
ModalRegistry.register(VideoModal.TYPE, VideoModal, VideoModal.TEMPLATE);
return VideoModal;
}
}

View file

@ -0,0 +1,122 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace tiny_recordrtc;
use context;
use editor_tiny\editor;
use editor_tiny\plugin;
use editor_tiny\plugin_with_buttons;
use editor_tiny\plugin_with_configuration;
use editor_tiny\plugin_with_menuitems;
/**
* Tiny RecordRTC plugin.
*
* @package tiny_recordrtc
* @copyright 2022 Stevani Andolo <stevani@hotmail.com.au>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class plugininfo extends plugin implements plugin_with_buttons, plugin_with_menuitems, plugin_with_configuration {
/**
* Whether the plugin is enabled
*
* @param context $context The context that the editor is used within
* @param array $options The options passed in when requesting the editor
* @param array $fpoptions The filepicker options passed in when requesting the editor
* @param editor $editor The editor instance in which the plugin is initialised
* @return boolean
*/
public static function is_enabled(
context $context,
array $options,
array $fpoptions,
?editor $editor = null
): bool {
// Disabled if:
// - Not logged in or guest.
// - Files are not allowed.
// - Only URL are supported.
$canhavefiles = !empty($options['maxfiles']);
$canhaveexternalfiles = !empty($options['return_types']) && ($options['return_types'] & FILE_EXTERNAL);
return isloggedin() && !isguestuser() && $canhavefiles && $canhaveexternalfiles;
}
public static function get_available_buttons(): array {
return [
'tiny_recordrtc/tiny_recordrtc_image',
];
}
public static function get_available_menuitems(): array {
return [
'tiny_recordrtc/tiny_recordrtc_image',
];
}
public static function get_plugin_configuration_for_context(
context $context,
array $options,
array $fpoptions,
?editor $editor = null
): array {
$sesskey = sesskey();
$allowedtypes = get_config('tiny_recordrtc', 'allowedtypes');
$audiobitrate = get_config('tiny_recordrtc', 'audiobitrate');
$videobitrate = get_config('tiny_recordrtc', 'videobitrate');
$audiotimelimit = get_config('tiny_recordrtc', 'audiotimelimit');
$videotimelimit = get_config('tiny_recordrtc', 'videotimelimit');
// Update $allowedtypes to account for capabilities.
$audioallowed = $allowedtypes === 'audio' || $allowedtypes === 'both';
$videoallowed = $allowedtypes === 'video' || $allowedtypes === 'both';
$audioallowed = $audioallowed && has_capability('tiny/recordrtc:recordaudio', $context);
$videoallowed = $videoallowed && has_capability('tiny/recordrtc:recordvideo', $context);
if ($audioallowed && $videoallowed) {
$allowedtypes = 'both';
} else if ($audioallowed) {
$allowedtypes = 'audio';
} else if ($videoallowed) {
$allowedtypes = 'video';
} else {
$allowedtypes = '';
}
$maxrecsize = get_max_upload_file_size();
if (!empty($options['maxbytes'])) {
$maxrecsize = min($maxrecsize, $options['maxbytes']);
}
$params = [
'contextid' => $context->id,
'sesskey' => $sesskey,
'allowedtypes' => $allowedtypes,
'audiobitrate' => $audiobitrate,
'videobitrate' => $videobitrate,
'audiotimelimit' => $audiotimelimit,
'videotimelimit' => $videotimelimit,
'maxrecsize' => $maxrecsize
];
$data = [
'params' => $params,
'fpoptions' => $fpoptions
];
return [
'data' => $data
];
}
}

View file

@ -0,0 +1,30 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace tiny_recordrtc\privacy;
/**
* Privacy Subsystem implementation for the recordrtc plugin for TinyMCE.
*
* @package tiny_recordrtc
* @copyright 2022 Andrew Lyons <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider implements \core_privacy\local\metadata\null_provider {
public static function get_reason(): string {
return 'privacy:metadata';
}
}

View file

@ -0,0 +1,44 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Tiny text editor recordrtc capabilities.
*
* @package tiny_recordrtc
* @copyright 2022 Stevani Andolo <stevani@hotmail.com.au>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$capabilities = [
// Capability to record audio using this plugin.
'tiny/recordrtc:recordaudio' => [
'captype' => 'write',
'contextlevel' => CONTEXT_MODULE,
'archetypes' => [
'user' => CAP_ALLOW,
],
],
// Capability to record video using this plugin.
'tiny/recordrtc:recordvideo' => [
'captype' => 'write',
'contextlevel' => CONTEXT_MODULE,
'archetypes' => [
'user' => CAP_ALLOW,
],
],
];

View file

@ -0,0 +1,87 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Strings for component 'tiny_recordrtc', language 'en'.
*
* @package tiny_recordrtc
* @copyright 2022 Stevani Andolo <stevani@hotmail.com.au>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
$string['allowedtypes'] = 'Allowed types';
$string['allowedtypes_desc'] = 'Which recording buttons should appear in Atto. In addition to this setting, there are also capabilities which can control access to the buttons to particular users in particular contexts.';
$string['attachrecording'] = 'Attach recording';
$string['audioandvideo'] = 'Audio and video';
$string['audiobitrate'] = 'Audio bitrate';
$string['audiobitrate_desc'] = 'Quality of audio recording (larger number means higher quality)';
$string['audiobuttontitle'] = 'Record audio';
$string['audiotimelimit'] = 'Audio time limit in seconds';
$string['audiotimelimit_desc'] = 'Maximum recording length allowed for the audio clips';
$string['audiotitle'] = 'Record Audio';
$string['confirm_yes'] = 'Yes';
$string['discard_desc'] = 'There is a reacording data or the recording is in process.<br>Do you want to close the recording?';
$string['discard_title'] = 'Close confirmation';
$string['gumabort'] = 'Something strange happened which prevented the webcam/microphone from being used';
$string['gumabort_title'] = 'Something happened';
$string['gumnotallowed'] = 'The user must allow the browser access to the webcam/microphone';
$string['gumnotallowed_title'] = 'Wrong permissions';
$string['gumnotfound'] = 'There is no input device connected or enabled';
$string['gumnotfound_title'] = 'Device missing';
$string['gumnotreadable'] = 'Something is preventing the browser from accessing the webcam/microphone';
$string['gumnotreadable_title'] = 'Hardware error';
$string['gumnotsupported'] = 'Your browser does not support recording over an insecure connection and must close the plugin';
$string['gumnotsupported_title'] = 'No support for insecure connection';
$string['gumoverconstrained'] = 'The current webcam/microphone can not produce a stream with the required constraints';
$string['gumoverconstrained_title'] = 'Problem with constraints';
$string['gumsecurity'] = 'Your browser does not support recording over an insecure connection and must close the plugin';
$string['gumsecurity_title'] = 'No support for insecure connection';
$string['gumtype'] = 'Tried to get stream from the webcam/microphone, but no constraints were specified';
$string['gumtype_title'] = 'No constraints specified';
$string['insecurealert'] = 'Your browser might not allow this plugin to work unless it is used either over HTTPS or from localhost';
$string['insecurealert_title'] = 'Insecure connection!';
$string['maxfilesizehit'] = 'You have attained the maximum size limit for file uploads';
$string['maxfilesizehit_title'] = 'Recording stopped';
$string['norecordingfound'] = 'Something appears to have gone wrong, it seems nothing has been recorded';
$string['norecordingfound_title'] = 'No recording found';
$string['nowebrtc'] = 'Your browser offers limited or no support for WebRTC technologies yet, and cannot be used with this plugin. Please switch or upgrade your browser';
$string['nowebrtc_title'] = 'WebRTC not supported';
$string['onlyaudio'] = 'Audio only';
$string['onlyvideo'] = 'Video only';
$string['pluginname'] = 'Tiny Record RTC plugin for Moodle';
$string['privacy:metadata'] = 'The RecordRTC plugin does not store any personal data.';
$string['recordagain'] = 'Record again';
$string['recordagain_desc'] = 'You have already recorded some content. Recording again will remove this content.<br>Are you sure you want to reset the recording?';
$string['recordagain_title'] = 'Record again confirmation';
$string['recordingfailed'] = 'Recording failed, try again';
$string['recordinguploaded'] = 'Recording uploaded';
$string['recordrtc:recordaudio'] = 'Record audio directly into the text editor';
$string['recordrtc:recordvideo'] = 'Record video directly into the text editor';
$string['startrecording'] = 'Start recording';
$string['stoprecording'] = 'Stop recording';
$string['timelimitwarning'] = 'You must enter a number that is greater than 0.';
$string['uploadaborted'] = 'Upload aborted:';
$string['uploadfailed'] = 'Upload failed with error: {$a->error}';
$string['uploadfailed404'] = 'Upload failed: file too large';
$string['uploading'] = 'Uploading - {$a->progress}%';
$string['uploadprogress'] = 'completed';
$string['videobitrate'] = 'Video bitrate';
$string['videobitrate_desc'] = 'Quality of video recording (larger number means higher quality)';
$string['videobuttontitle'] = 'Record video';
$string['videotimelimit'] = 'Video time limit in seconds';
$string['videotimelimit_desc'] = 'Maximum recording length allowed for the video clips';
$string['videotitle'] = 'Record Video';
$string['helplinktext'] = 'Moodle RecordRTC';

View file

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="-50 0 608 500" style="enable-background:new 100 0 608 500;" xml:space="preserve">
<g>
<path d="M394,196.1c0-11.9-9.7-21.6-21.6-21.6s-21.6,9.7-21.6,21.6c0,61.7-50.2,111.8-111.8,111.8
s-111.8-50.2-111.8-111.8c0-11.9-9.7-21.6-21.6-21.6S84,184.2,84,196.1c0,78.2,58.1,143,133.4,153.5v85.2h-55.6
c-11.9,0-21.6,9.7-21.6,21.6s9.7,21.6,21.6,21.6h154.5c11.9,0,21.6-9.7,21.6-21.6s-9.7-21.6-21.6-21.6h-55.6v-85.2
C335.9,339.1,394,274.2,394,196.1z"/>
<path d="M239,0L239,0c-46.4,0-84,37.6-84,84v110.7c0,46.4,37.6,84,84,84l0,0c46.4,0,84-37.6,84-84V84
C323,37.6,285.4,0,239,0z M239.1,55.9c-15.1,0-27.4,12.3-27.4,27.4c0,6.9-5.6,12.5-12.5,12.5s-12.5-5.6-12.5-12.5
c0-28.9,23.5-52.4,52.4-52.4c6.9,0,12.5,5.6,12.5,12.5S246,55.9,239.1,55.9z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

View file

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 458.317 458.317" style="enable-background:new 0 0 458.317 458.317;" xml:space="preserve">
<g>
<g>
<path d="M450.237,109.509c-4.964-2.582-10.953-2.187-15.534,1.026l-68.72,48.19V98.933c0-18.121-14.743-32.864-32.865-32.864
H32.865C14.743,66.068,0,80.811,0,98.933v260.452c0,18.121,14.743,32.864,32.865,32.864h300.254
c18.122,0,32.865-14.743,32.865-32.864V299.59l68.72,48.191c4.573,3.206,10.561,3.613,15.534,1.026
c4.964-2.582,8.079-7.712,8.079-13.308V122.816C458.315,117.222,455.201,112.092,450.237,109.509z M95.621,125.816
c20.745,0,37.622,16.877,37.622,37.622s-16.877,37.623-37.622,37.623s-37.623-16.878-37.623-37.623S74.875,125.816,95.621,125.816
z M297.095,326.817H68.889c-8.284,0-15-6.716-15-15s6.716-15,15-15h228.206c8.284,0,15,6.716,15,15
S305.379,326.817,297.095,326.817z"/>
</g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View file

@ -0,0 +1,97 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Settings that allow turning on and off recordrtc features
*
* @package tiny_recordrtc
* @copyright 2022, Stevani Andolo <stevani@hotmail.com.au>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
// Needed for constants.
require_once($CFG->dirroot . '/lib/editor/tiny/plugins/recordrtc/classes/plugininfo.php');
$ADMIN->add('editortiny', new admin_category('tiny_recordrtc', new lang_string('pluginname', 'tiny_recordrtc')));
if ($ADMIN->fulltree) {
$defaulttimelimit = 120;
$url = parse_url($CFG->wwwroot);
$hostname = parse_url($CFG->wwwroot, PHP_URL_HOST);
$isvalid = in_array($hostname, ['localhost', '127.0.0.1', '::1']);
$isvalid = $isvalid || preg_match("/^.*\.localhost$/", $hostname);
if (!$isvalid && $url['scheme'] !== 'https') {
$warning = html_writer::div(get_string('insecurealert', 'tiny_recordrtc'), 'box py-3 generalbox alert alert-danger');
$setting = new admin_setting_description('tiny_recordrtc/warning', null, $warning);
$settings->add($setting);
}
// Types allowed.
$options = [
'both' => new lang_string('audioandvideo', 'tiny_recordrtc'),
'audio' => new lang_string('onlyaudio', 'tiny_recordrtc'),
'video' => new lang_string('onlyvideo', 'tiny_recordrtc')
];
$name = get_string('allowedtypes', 'tiny_recordrtc');
$desc = get_string('allowedtypes_desc', 'tiny_recordrtc');
$default = 'both';
$setting = new admin_setting_configselect('tiny_recordrtc/allowedtypes', $name, $desc, $default, $options);
$settings->add($setting);
// Audio bitrate.
$name = get_string('audiobitrate', 'tiny_recordrtc');
$desc = get_string('audiobitrate_desc', 'tiny_recordrtc');
$default = '128000';
$setting = new admin_setting_configtext('tiny_recordrtc/audiobitrate', $name, $desc, $default, PARAM_INT, 8);
$settings->add($setting);
// Video bitrate.
$name = get_string('videobitrate', 'tiny_recordrtc');
$desc = get_string('videobitrate_desc', 'tiny_recordrtc');
$default = '2500000';
$setting = new admin_setting_configtext('tiny_recordrtc/videobitrate', $name, $desc, $default, PARAM_INT, 8);
$settings->add($setting);
// Audio recording time limit.
$name = get_string('audiotimelimit', 'tiny_recordrtc');
$desc = get_string('audiotimelimit_desc', 'tiny_recordrtc');
// Validate audiotimelimit greater than 0.
$setting = new admin_setting_configduration('tiny_recordrtc/audiotimelimit', $name, $desc, $defaulttimelimit);
$setting->set_validate_function(function(int $value): string {
if ($value <= 0) {
return get_string('timelimitwarning', 'tiny_recordrtc');
}
return '';
});
$settings->add($setting);
// Video recording time limit.
$name = get_string('videotimelimit', 'tiny_recordrtc');
$desc = get_string('videotimelimit_desc', 'tiny_recordrtc');
// Validate videotimelimit greater than 0.
$setting = new admin_setting_configduration('tiny_recordrtc/videotimelimit', $name, $desc, $defaulttimelimit);
$setting->set_validate_function(function(int $value): string {
if ($value <= 0) {
return get_string('timelimitwarning', 'tiny_recordrtc');
}
return '';
});
$settings->add($setting);
}

View file

@ -0,0 +1,43 @@
.tiny_recordrtc,
.tiny_recordrtc div {
font-weight: normal;
line-height: 40px;
padding: 3px 0 3px 0;
}
.tiny_recordrtc .alert {
line-height: 40px;
text-align: center;
}
.tiny_recordrtc audio {
display: block;
width: 100%;
}
@media screen and (max-width: 670px) {
.tiny_recordrtc video {
display: block;
height: auto;
margin: 0 auto;
width: 100%;
}
}
@media screen and (min-width: 671px) {
.tiny_recordrtc video {
display: block;
height: 480px;
margin: 0 auto;
width: 640px;
}
}
.tiny_recordrtc #start-stop,
.tiny_recordrtc #upload {
white-space: normal;
}
.tiny_recordrtc video {
border-radius: 8px;
}

View file

@ -0,0 +1,78 @@
{{!
This file is part of Moodle - http://moodle.org/
Moodle is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Moodle is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
}}
{{!
@template tiny_recordrtc/audio_recorder
Audio recorder template.
Example context (json):
{
}
}}
{{!
Note:
The content of this template should ideally be shared with the video recorder by using a block template.
Unfortunately it is not possible to have nested block templates due to MDL-51145.
Once this issue is resolved, this template should be refactored to use a block template.
}}
{{< core/modal }}
{{$title}}{{#str}} audiotitle, tiny_recordrtc {{/str}}{{/title}}
{{$body}}
{{> core/local/toast/wrapper }}
<div class="tiny_recordrtc container-fluid">
<div data-purpose="security-warning" class="row hide">
<div class="col-12">
<div id="alert-danger" class="alert alert-danger">
<strong>
{{#str}} insecurealerttitle, tiny_recordrtc {{/str}}
</strong>
{{#str}} insecurealert, tiny_recordrtc {{/str}}
</div>
</div>
</div>
<div data-purpose="player-container" class="row">
<div class="col-1"></div>
<div class="col-10">
<audio></audio>
</div>
<div class="col-1"></div>
</div>
<div data-purpose="start-stop-container" class="row">
<div class="col-1"></div>
<div class="col-10">
<button class="btn btn-lg btn-outline-danger btn-block" data-action="startstop">
{{#str}} startrecording, tiny_recordrtc {{/str}}
</button>
</div>
<div class="col-1"></div>
</div>
<div data-purpose="upload-container" class="row hide">
<div class="col-3"></div>
<div class="col-6">
<button class="btn btn-primary btn-block" data-action="upload">
{{#str}} attachrecording, tiny_recordrtc {{/str}}
</button>
</div>
<div class="col-3"></div>
</div>
</div>
{{/body}}
{{$footer}}
<button type="button" class="btn btn-secondary" data-action="cancel">{{#str}} cancel {{/str}}</button>
{{/footer}}
{{/ core/modal }}

View file

@ -0,0 +1,29 @@
{{!
This file is part of Moodle - http://moodle.org/
Moodle is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Moodle is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
}}
{{!
@template tiny_recordrtc/embed_audio
Insert recording template.
Example context (json):
{
"source": "https://example.com/someaudio.mp3"
}
}}
<audio controls="true">
<source src="{{ source }}">{{ source }}</source>}}
</audio>

View file

@ -0,0 +1,29 @@
{{!
This file is part of Moodle - http://moodle.org/
Moodle is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Moodle is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
}}
{{!
@template tiny_recordrtc/embed_video
Insert recording template.
Example context (json):
{
"source": "https://example.com/someaudio.mp3"
}
}}
<video controls="true">
<source src="{{ source }}">{{ source }}</source>}}
</video>

View file

@ -0,0 +1,70 @@
{{!
This file is part of Moodle - http://moodle.org/
Moodle is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Moodle is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
}}
{{!
@template tiny_recordrtc/insert_recording
Insert recording template.
Example context (json):
{
}
}}
<div class="tiny_recordrtc container-fluid">
<div data-purpose="security-warning" class="row hide">
<div class="col-12">
<div id="alert-danger" class="alert alert-danger">
<strong>
{{#str}} insecurealerttitle, tiny_recordrtc {{/str}}
</strong>
{{#str}} insecurealert, tiny_recordrtc {{/str}}
</div>
</div>
</div>
<div data-purpose="player-container" class="row hide">
{{#isaudio}}
<div class="col-1"></div>
<div class="col-10">
<audio id="player"></audio>
</div>
<div class="col-1"></div>
{{/isaudio}}
{{^isaudio}}
<div class="col-12">
<video id="player"></video>
</div>
{{/isaudio}}
</div>
<div data-purpose="start-stop-container" class="row">
<div class="col-1"></div>
<div class="col-10">
<button id="start-stop" class="btn btn-lg btn-outline-danger btn-block" data-action-"startstop">
{{#str}} startrecording, tiny_recordrtc {{/str}}
</button>
</div>
<div class="col-1"></div>
</div>
<div data-purpose="attach-container" class="row hide">
<div class="col-3"></div>
<div class="col-6">
<button id="upload" class="btn btn-primary btn-block" data-action-"attach">
{{#str}} attachrecording, tiny_recordrtc {{/str}}
</button>
</div>
<div class="col-3"></div>
</div>
</div>

View file

@ -0,0 +1,28 @@
{{!
This file is part of Moodle - http://moodle.org/
Moodle is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Moodle is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
}}
{{!
@template tiny_recordrtc/timeremaining
Insert recording template.
Example context (json):
{
}
}}
<span>
{{#str}}stoprecording, tiny_recordrtc{{/str}} (<span data-type="minutes">{{minutes}}</span>:<span data-type="seconds">{{seconds}}</span>)
</span>

View file

@ -0,0 +1,78 @@
{{!
This file is part of Moodle - http://moodle.org/
Moodle is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Moodle is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
}}
{{!
@template tiny_recordrtc/video_recorder
Audio recorder template.
Example context (json):
{
}
}}
{{!
Note:
The content of this template should ideally be shared with the video recorder by using a block template.
Unfortunately it is not possible to have nested block templates due to MDL-51145.
Once this issue is resolved, this template should be refactored to use a block template.
}}
{{< core/modal }}
{{$title}}{{#str}} videotitle, tiny_recordrtc {{/str}}{{/title}}
{{$body}}
{{> core/local/toast/wrapper }}
<div class="tiny_recordrtc container-fluid">
<div data-purpose="security-warning" class="row hide">
<div class="col-12">
<div id="alert-danger" class="alert alert-danger">
<strong>
{{#str}} insecurealerttitle, tiny_recordrtc {{/str}}
</strong>
{{#str}} insecurealert, tiny_recordrtc {{/str}}
</div>
</div>
</div>
<div data-purpose="player-container" class="row">
<div class="col-1"></div>
<div class="col-10">
<video></video>
</div>
<div class="col-1"></div>
</div>
<div data-purpose="start-stop-container" class="row">
<div class="col-1"></div>
<div class="col-10">
<button class="btn btn-lg btn-outline-danger btn-block" data-action="startstop">
{{#str}} startrecording, tiny_recordrtc {{/str}}
</button>
</div>
<div class="col-1"></div>
</div>
<div data-purpose="upload-container" class="row hide">
<div class="col-1"></div>
<div class="col-10">
<button class="btn btn-lg btn-primary btn-block" data-action="upload">
{{#str}} attachrecording, tiny_recordrtc {{/str}}
</button>
</div>
<div class="col-3"></div>
</div>
</div>
{{/body}}
{{$footer}}
<button type="button" class="btn btn-secondary" data-action="cancel">{{#str}} cancel {{/str}}</button>
{{/footer}}
{{/ core/modal }}

View file

@ -0,0 +1,29 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Tiny media plugin version details.
*
* @package tiny_recordrtc
* @copyright 2022 Stevani Andolo <stevani@hotmail.com.au>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$plugin->version = 2022081800;
$plugin->requires = 2020061500;
$plugin->component = 'tiny_recordrtc';