diff --git a/.github/workflows/moodle-ci.yml b/.github/workflows/moodle-ci.yml index 73c65981..3142f7c1 100644 --- a/.github/workflows/moodle-ci.yml +++ b/.github/workflows/moodle-ci.yml @@ -28,7 +28,7 @@ jobs: fail-fast: false matrix: include: - - php: '8.0' + - php: '8.1' moodle-branch: 'master' database: 'pgsql' - php: '8.1' diff --git a/plugin/videojs/amd/build/videotime.min.js b/plugin/videojs/amd/build/videotime.min.js index 11f562c2..ac5d18d5 100644 --- a/plugin/videojs/amd/build/videotime.min.js +++ b/plugin/videojs/amd/build/videotime.min.js @@ -6,6 +6,6 @@ define("videotimeplugin_videojs/videotime",["exports","jquery","mod_videotime/vi * @module videotimeplugin_videojs/videotime * @copyright 2022 bdecent gmbh * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_jquery=_interopRequireDefault(_jquery),_videotime=_interopRequireDefault(_videotime),_log=_interopRequireDefault(_log),_notification=_interopRequireDefault(_notification),_videoLazy=_interopRequireDefault(_videoLazy);class VideoTime extends _videotime.default{initialize(){_log.default.debug("Initializing Video Time "+this.elementId);let instance=this.instance,options={autoplay:Number(instance.autoplay),controls:Number(instance.controls),sources:[{type:instance.type,src:instance.vimeo_url}],loop:Number(instance.option_loop),fluid:Number(instance.responsive),playbackRates:instance.speed?[.5,.75,1,1.25,1.5,2]:[1],muted:Number(instance.muted)};"video/youtube"===instance.type&&(options.techOrder=["youtube"]),!Number(instance.responsive)&&Number(instance.height)&&Number(instance.width)&&(options.height=Number(instance.height),options.width=Number(instance.width)),_log.default.debug("Initializing VideoJS player with options:"),_log.default.debug(options),this.player=new _videoLazy.default(this.elementId,options),this.player.on("loadedmetadata",(()=>{if(!instance.resume_playback||instance.resume_time<=0||this.resumed)return!0;let duration=this.getPlayer().duration(),resumeTime=instance.resume_time;return resumeTime+1>=Math.floor(duration)&&(_log.default.debug("VIDEO_TIME video finished, resuming at start of video."),resumeTime=0),_log.default.debug("VIDEO_TIME duration is "+duration),_log.default.debug("VIDEO_TIME resuming at "+resumeTime),resumeTime&&setTimeout((()=>{this.setCurrentPosition(resumeTime)}),10),!0}));let url=new URL(window.location.href),q=url.searchParams.get("q"),starttime=(url.searchParams.get("time")||"").match(/^([0-9]+:){0,2}([0-9]+)(\.[0-9]+)$/);starttime?this.setStartTime(starttime[0]).then((function(){return q&&window.find&&window.find(q),!0})).catch(_notification.default.exception):q&&window.find&&window.find(q),this.addListeners();for(let i=0;i(this.played||(this.hasPro&&this.startWatchInterval(),this.view()),!0))),this.hasPro&&(this.player.on("play",function(){this.playing=!0,_log.default.debug("VIDEO_TIME play")}.bind(this)),this.player.on("playing",function(){this.playing=!0,_log.default.debug("VIDEO_TIME playing")}.bind(this)),this.player.on("pause",function(){this.playing=!1,_log.default.debug("VIDEO_TIME pause")}.bind(this)),this.player.on("stalled",function(){this.playing=!1,_log.default.debug("VIDEO_TIME stalled")}.bind(this)),this.player.on("suspend",function(){this.playing=!1,_log.default.debug("VIDEO_TIME suspend")}.bind(this)),this.player.on("abort",function(){this.playing=!1,_log.default.debug("VIDEO_TIME abort")}.bind(this)),this.player.on("playbackrateschange",function(){this.playbackRate=this.player.playbackRate()}.bind(this)),this.player.on("timeupdate",function(){this.currentTime=this.player.currentTime(),this.percent=this.currentTime/this.player.duration(),_log.default.debug("VIDEO_TIME timeupdate. Percent: "+this.percent+". Current time: "+this.currentTime)}.bind(this)),this.player.on("ended",this.handleEnd.bind(this)),this.player.on("pause",this.handleEnd.bind(this)),this.player.options().responsive)){new ResizeObserver((()=>{this.player.height(this.player.videoHeight()/this.player.videoWidth()*this.player.currentWidth())})).observe(document.querySelector("#"+this.elementId))}}else _log.default.debug("Player was not properly initialized for course module "+this.cmId)}setStartTime(starttime){let time=starttime.match(/((([0-9]+):)?(([0-9]+):))?([0-9]+(\.[0-9]+))/);return time?(this.resumeTime=3600*Number(time[3]||0)+60*Number(time[5]||0)+Number(time[6]),this.player.currentTime(this.resumeTime)):(_log.default.debug("Set start time:"+starttime),this.player.currentTime())}getDuration(){return new Promise((resolve=>(resolve(this.player.duration()),!0)))}getPlaybackRate(){return new Promise((resolve=>(resolve(this.player.playbackRate()),!0)))}setCurrentPosition(secs){return new Promise((resolve=>(resolve(this.player.currentTime(secs)),!0)))}getCurrentPosition(){return new Promise((resolve=>(resolve(this.player.currentTime()),!0)))}}return _exports.default=VideoTime,_exports.default})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_jquery=_interopRequireDefault(_jquery),_videotime=_interopRequireDefault(_videotime),_log=_interopRequireDefault(_log),_notification=_interopRequireDefault(_notification),_videoLazy=_interopRequireDefault(_videoLazy);class VideoTime extends _videotime.default{initialize(){_log.default.debug("Initializing Video Time "+this.elementId);let instance=this.instance,options={autoplay:Number(instance.autoplay),controls:Number(instance.controls),sources:[{type:instance.type,src:instance.vimeo_url}],loop:Number(instance.option_loop),fluid:Number(instance.responsive),playbackRates:Number(instance.speed)?[.5,.75,1,1.25,1.5,2]:[1],muted:Number(instance.muted)};"video/youtube"===instance.type&&(options.techOrder=["youtube"]),!Number(instance.responsive)&&Number(instance.height)&&Number(instance.width)&&(options.height=Number(instance.height),options.width=Number(instance.width)),_log.default.debug("Initializing VideoJS player with options:"),_log.default.debug(options),this.player=new _videoLazy.default(this.elementId,options),this.player.on("loadedmetadata",(()=>{if(!instance.resume_playback||instance.resume_time<=0||this.resumed)return!0;let duration=this.getPlayer().duration(),resumeTime=instance.resume_time;return resumeTime+1>=Math.floor(duration)&&(_log.default.debug("VIDEO_TIME video finished, resuming at start of video."),resumeTime=0),_log.default.debug("VIDEO_TIME duration is "+duration),_log.default.debug("VIDEO_TIME resuming at "+resumeTime),resumeTime&&setTimeout((()=>{this.setCurrentPosition(resumeTime)}),10),!0}));let url=new URL(window.location.href),q=url.searchParams.get("q"),starttime=(url.searchParams.get("time")||"").match(/^([0-9]+:){0,2}([0-9]+)(\.[0-9]+)$/);starttime?this.setStartTime(starttime[0]).then((function(){return q&&window.find&&window.find(q),!0})).catch(_notification.default.exception):q&&window.find&&window.find(q),this.addListeners();for(let i=0;i(this.played||(this.hasPro&&this.startWatchInterval(),this.view()),!0))),this.hasPro&&(this.player.on("play",function(){this.playing=!0,_log.default.debug("VIDEO_TIME play")}.bind(this)),this.player.on("playing",function(){this.playing=!0,_log.default.debug("VIDEO_TIME playing")}.bind(this)),this.player.on("pause",function(){this.playing=!1,_log.default.debug("VIDEO_TIME pause")}.bind(this)),this.player.on("stalled",function(){this.playing=!1,_log.default.debug("VIDEO_TIME stalled")}.bind(this)),this.player.on("suspend",function(){this.playing=!1,_log.default.debug("VIDEO_TIME suspend")}.bind(this)),this.player.on("abort",function(){this.playing=!1,_log.default.debug("VIDEO_TIME abort")}.bind(this)),this.player.on("playbackrateschange",function(){this.playbackRate=this.player.playbackRate()}.bind(this)),this.player.on("timeupdate",function(){this.currentTime=this.player.currentTime(),this.percent=this.currentTime/this.player.duration(),_log.default.debug("VIDEO_TIME timeupdate. Percent: "+this.percent+". Current time: "+this.currentTime)}.bind(this)),this.player.on("ended",this.handleEnd.bind(this)),this.player.on("pause",this.handleEnd.bind(this)),this.player.options().responsive)){new ResizeObserver((()=>{this.player.height(this.player.videoHeight()/this.player.videoWidth()*this.player.currentWidth())})).observe(document.querySelector("#"+this.elementId))}}else _log.default.debug("Player was not properly initialized for course module "+this.cmId)}setStartTime(starttime){let time=starttime.match(/((([0-9]+):)?(([0-9]+):))?([0-9]+(\.[0-9]+))/);return time?(this.resumeTime=3600*Number(time[3]||0)+60*Number(time[5]||0)+Number(time[6]),this.player.currentTime(this.resumeTime)):(_log.default.debug("Set start time:"+starttime),this.player.currentTime())}getDuration(){return new Promise((resolve=>(resolve(this.player.duration()),!0)))}getPlaybackRate(){return new Promise((resolve=>(resolve(this.player.playbackRate()),!0)))}setCurrentPosition(secs){return new Promise((resolve=>(resolve(this.player.currentTime(secs)),!0)))}getCurrentPosition(){return new Promise((resolve=>(resolve(this.player.currentTime()),!0)))}}return _exports.default=VideoTime,_exports.default})); //# sourceMappingURL=videotime.min.js.map \ No newline at end of file diff --git a/plugin/videojs/amd/build/videotime.min.js.map b/plugin/videojs/amd/build/videotime.min.js.map index 6f1be700..ee2059ad 100644 --- a/plugin/videojs/amd/build/videotime.min.js.map +++ b/plugin/videojs/amd/build/videotime.min.js.map @@ -1 +1 @@ -{"version":3,"file":"videotime.min.js","sources":["../src/videotime.js"],"sourcesContent":["/*\n * Video time player specific js\n *\n * @package videotimeplugin_videojs\n * @module videotimeplugin_videojs/videotime\n * @copyright 2022 bdecent gmbh \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport $ from \"jquery\";\nimport VideoTimeBase from \"mod_videotime/videotime\";\nimport Log from \"core/log\";\nimport Notification from \"core/notification\";\nimport Player from \"media_videojs/video-lazy\";\nimport \"media_videojs/Youtube-lazy\";\n\nexport default class VideoTime extends VideoTimeBase {\n initialize() {\n Log.debug(\"Initializing Video Time \" + this.elementId);\n\n let instance = this.instance,\n options = {\n autoplay: Number(instance.autoplay),\n controls: Number(instance.controls),\n sources: [{type: instance.type, src: instance.vimeo_url}],\n loop: Number(instance.option_loop),\n fluid: Number(instance.responsive),\n playbackRates: instance.speed\n ? [0.5, 0.75, 1, 1.25, 1.5, 2]\n : [1],\n muted: Number(instance.muted)\n };\n if (instance.type === \"video/youtube\") {\n options.techOrder = [\"youtube\"];\n }\n if (!Number(instance.responsive) && Number(instance.height) && Number(instance.width)) {\n options.height = Number(instance.height);\n options.width = Number(instance.width);\n }\n Log.debug(\"Initializing VideoJS player with options:\");\n Log.debug(options);\n this.player = new Player(this.elementId, options);\n\n this.player.on(\"loadedmetadata\", () => {\n if (!instance.resume_playback || instance.resume_time <= 0 || this.resumed) {\n return true;\n }\n\n let duration = this.getPlayer().duration(),\n resumeTime = instance.resume_time;\n // Duration is often a little greater than a resume time at the end of the video.\n // A user may have watched 100 seconds when the video ends, but the duration may be\n // 100.56 seconds. BUT, sometimes the duration is rounded depending on when the\n // video loads, so it may be 101 seconds. Hence the +1 and Math.floor usage.\n if (resumeTime + 1 >= Math.floor(duration)) {\n Log.debug(\n \"VIDEO_TIME video finished, resuming at start of video.\"\n );\n resumeTime = 0;\n }\n Log.debug(\"VIDEO_TIME duration is \" + duration);\n Log.debug(\"VIDEO_TIME resuming at \" + resumeTime);\n if (resumeTime) {\n setTimeout(() => {\n this.setCurrentPosition(resumeTime);\n }, 10);\n }\n return true;\n });\n\n let url = new URL(window.location.href),\n q = url.searchParams.get(\"q\"),\n starttime = (url.searchParams.get(\"time\") || \"\").match(\n /^([0-9]+:){0,2}([0-9]+)(\\.[0-9]+)$/\n );\n if (starttime) {\n this.setStartTime(starttime[0])\n .then(function() {\n if (q && window.find) {\n window.find(q);\n }\n return true;\n })\n .catch(Notification.exception);\n } else if (q && window.find) {\n window.find(q);\n }\n\n this.addListeners();\n\n for (let i = 0; i < this.plugins.length; i++) {\n const plugin = this.plugins[i];\n plugin.initialize(this, instance);\n }\n\n return true;\n }\n\n /**\n * Register player events to respond to user interaction and play progress.\n */\n addListeners() {\n // If this is a tab play set time cues and listener.\n $($(\"#\" + this.elementId).closest(\".videotimetabs\")).each(\n function(i, tabs) {\n $(tabs)\n .find('[data-action=\"cue\"]')\n .each(\n function(index, anchor) {\n let starttime = anchor.getAttribute(\"data-start\"),\n time = starttime.match(\n /((([0-9]+):)?(([0-9]+):))?([0-9]+(\\.[0-9]+))/\n );\n if (time) {\n this.player\n .addCuePoint(\n 3600 * Number(time[3] || 0) +\n 60 * Number(time[5] || 0) +\n Number(time[6]),\n {\n starttime: starttime\n }\n )\n .catch(Notification.exeception);\n }\n }.bind(this)\n );\n\n this.player.on(\"cuepoint\", function(event) {\n if (event.data.starttime) {\n $(tabs)\n .find(\".videotime-highlight\")\n .removeClass(\"videotime-highlight\");\n $(tabs)\n .find(\n '[data-action=\"cue\"][data-start=\"' +\n event.data.starttime +\n '\"]'\n )\n .closest(\".row\")\n .addClass(\"videotime-highlight\");\n $(\".videotime-highlight\").each(function() {\n if (this.offsetTop) {\n this.parentNode.scrollTo({\n top: this.offsetTop - 50,\n left: 0,\n behavior: \"smooth\"\n });\n }\n });\n }\n });\n }.bind(this)\n );\n\n if (!this.player) {\n Log.debug(\n \"Player was not properly initialized for course module \" +\n this.cmId\n );\n return;\n }\n\n // Fire view event in Moodle on first play only.\n this.player.on(\"play\", () => {\n if (!this.played) {\n if (this.hasPro) {\n this.startWatchInterval();\n }\n // Free version can still mark completion on video time view.\n this.view();\n }\n return true;\n });\n\n // Features beyond this point are for pro only.\n if (!this.hasPro) {\n return;\n }\n\n // Note: Vimeo player does not support multiple events in a single on() call. Each requires it's own function.\n\n // Catch all events where video plays.\n this.player.on(\n \"play\",\n function() {\n this.playing = true;\n Log.debug(\"VIDEO_TIME play\");\n }.bind(this)\n );\n this.player.on(\n \"playing\",\n function() {\n this.playing = true;\n Log.debug(\"VIDEO_TIME playing\");\n }.bind(this)\n );\n\n // Catch all events where video stops.\n this.player.on(\n \"pause\",\n function() {\n this.playing = false;\n Log.debug(\"VIDEO_TIME pause\");\n }.bind(this)\n );\n this.player.on(\n \"stalled\",\n function() {\n this.playing = false;\n Log.debug(\"VIDEO_TIME stalled\");\n }.bind(this)\n );\n this.player.on(\n \"suspend\",\n function() {\n this.playing = false;\n Log.debug(\"VIDEO_TIME suspend\");\n }.bind(this)\n );\n this.player.on(\n \"abort\",\n function() {\n this.playing = false;\n Log.debug(\"VIDEO_TIME abort\");\n }.bind(this)\n );\n\n this.player.on(\n \"playbackrateschange\",\n function() {\n this.playbackRate = this.player.playbackRate();\n }.bind(this)\n );\n\n // Always update internal values for percent and current time watched.\n this.player.on(\n \"timeupdate\",\n function() {\n this.currentTime = this.player.currentTime();\n this.percent = this.currentTime / this.player.duration();\n Log.debug(\n \"VIDEO_TIME timeupdate. Percent: \" +\n this.percent +\n \". Current time: \" +\n this.currentTime\n );\n }.bind(this)\n );\n\n // Initiate video finish procedure.\n this.player.on(\"ended\", this.handleEnd.bind(this));\n this.player.on(\"pause\", this.handleEnd.bind(this));\n\n // Readjust height when responsive player is resized.\n if (this.player.options().responsive) {\n let observer = new ResizeObserver(() => {\n this.player.height(\n (this.player.videoHeight() / this.player.videoWidth()) *\n this.player.currentWidth()\n );\n });\n observer.observe(document.querySelector(\"#\" + this.elementId));\n }\n }\n\n /**\n * Parse start time and set player\n *\n * @param {string} starttime\n * @returns {Promise}\n */\n setStartTime(starttime) {\n let time = starttime.match(\n /((([0-9]+):)?(([0-9]+):))?([0-9]+(\\.[0-9]+))/\n );\n if (time) {\n this.resumeTime =\n 3600 * Number(time[3] || 0) +\n 60 * Number(time[5] || 0) +\n Number(time[6]);\n return this.player.currentTime(this.resumeTime);\n }\n Log.debug(\"Set start time:\" + starttime);\n return this.player.currentTime();\n }\n\n /**\n * Get play back rate\n *\n * @returns {Promise}\n */\n getDuration() {\n return new Promise(resolve => {\n resolve(this.player.duration());\n return true;\n });\n }\n\n /**\n * Get duration of video\n *\n * @returns {Promise}\n */\n getPlaybackRate() {\n return new Promise(resolve => {\n resolve(this.player.playbackRate());\n return true;\n });\n }\n\n /**\n * Set current time of player\n *\n * @param {float} secs time\n * @returns {Promise}\n */\n setCurrentPosition(secs) {\n return new Promise(resolve => {\n resolve(this.player.currentTime(secs));\n return true;\n });\n }\n\n /**\n * Get current time of player\n *\n * @returns {Promise}\n */\n getCurrentPosition() {\n return new Promise(resolve => {\n resolve(this.player.currentTime());\n return true;\n });\n }\n}\n"],"names":["VideoTime","VideoTimeBase","initialize","debug","this","elementId","instance","options","autoplay","Number","controls","sources","type","src","vimeo_url","loop","option_loop","fluid","responsive","playbackRates","speed","muted","techOrder","height","width","player","Player","on","resume_playback","resume_time","resumed","duration","getPlayer","resumeTime","Math","floor","setTimeout","setCurrentPosition","url","URL","window","location","href","q","searchParams","get","starttime","match","setStartTime","then","find","catch","Notification","exception","addListeners","i","plugins","length","closest","each","tabs","index","anchor","getAttribute","time","addCuePoint","exeception","bind","event","data","removeClass","addClass","offsetTop","parentNode","scrollTo","top","left","behavior","played","hasPro","startWatchInterval","view","playing","playbackRate","currentTime","percent","handleEnd","ResizeObserver","videoHeight","videoWidth","currentWidth","observe","document","querySelector","cmId","getDuration","Promise","resolve","getPlaybackRate","secs","getCurrentPosition"],"mappings":";;;;;;;;qTAgBqBA,kBAAkBC,mBACnCC,0BACQC,MAAM,2BAA6BC,KAAKC,eAExCC,SAAWF,KAAKE,SAChBC,QAAU,CACNC,SAAUC,OAAOH,SAASE,UAC1BE,SAAUD,OAAOH,SAASI,UAC1BC,QAAS,CAAC,CAACC,KAAMN,SAASM,KAAMC,IAAKP,SAASQ,YAC9CC,KAAMN,OAAOH,SAASU,aACtBC,MAAOR,OAAOH,SAASY,YACvBC,cAAeb,SAASc,MAClB,CAAC,GAAK,IAAM,EAAG,KAAM,IAAK,GAC1B,CAAC,GACPC,MAAOZ,OAAOH,SAASe,QAET,kBAAlBf,SAASM,OACTL,QAAQe,UAAY,CAAC,aAEpBb,OAAOH,SAASY,aAAeT,OAAOH,SAASiB,SAAWd,OAAOH,SAASkB,SAC3EjB,QAAQgB,OAASd,OAAOH,SAASiB,QACjChB,QAAQiB,MAAQf,OAAOH,SAASkB,qBAEhCrB,MAAM,0DACNA,MAAMI,cACLkB,OAAS,IAAIC,mBAAOtB,KAAKC,UAAWE,cAEpCkB,OAAOE,GAAG,kBAAkB,SACxBrB,SAASsB,iBAAmBtB,SAASuB,aAAe,GAAKzB,KAAK0B,eACxD,MAGPC,SAAW3B,KAAK4B,YAAYD,WAC5BE,WAAa3B,SAASuB,mBAKtBI,WAAa,GAAKC,KAAKC,MAAMJ,yBACzB5B,MACA,0DAEJ8B,WAAa,gBAEb9B,MAAM,0BAA4B4B,uBAClC5B,MAAM,0BAA4B8B,YAClCA,YACAG,YAAW,UACNC,mBAAmBJ,cACrB,KAEA,SAGPK,IAAM,IAAIC,IAAIC,OAAOC,SAASC,MAC9BC,EAAIL,IAAIM,aAAaC,IAAI,KACzBC,WAAaR,IAAIM,aAAaC,IAAI,SAAW,IAAIE,MAC7C,sCAEJD,eACKE,aAAaF,UAAU,IACvBG,MAAK,kBACEN,GAAKH,OAAOU,MACZV,OAAOU,KAAKP,IAET,KAEVQ,MAAMC,sBAAaC,WACjBV,GAAKH,OAAOU,MACnBV,OAAOU,KAAKP,QAGXW,mBAEA,IAAIC,EAAI,EAAGA,EAAInD,KAAKoD,QAAQC,OAAQF,IAAK,CAC3BnD,KAAKoD,QAAQD,GACrBrD,WAAWE,KAAME,iBAGrB,EAMXgD,uCAEM,mBAAE,IAAMlD,KAAKC,WAAWqD,QAAQ,mBAAmBC,KACjD,SAASJ,EAAGK,0BACNA,MACGV,KAAK,uBACLS,KACG,SAASE,MAAOC,YACRhB,UAAYgB,OAAOC,aAAa,cAChCC,KAAOlB,UAAUC,MACb,gDAEJiB,WACKvC,OACAwC,YACG,KAAOxD,OAAOuD,KAAK,IAAM,GACrB,GAAKvD,OAAOuD,KAAK,IAAM,GACvBvD,OAAOuD,KAAK,IAChB,CACIlB,UAAWA,YAGlBK,MAAMC,sBAAac,aAE9BC,KAAK/D,YAGVqB,OAAOE,GAAG,YAAY,SAASyC,OAC5BA,MAAMC,KAAKvB,gCACTc,MACGV,KAAK,wBACLoB,YAAY,2CACfV,MACGV,KACG,mCACIkB,MAAMC,KAAKvB,UACX,MAEPY,QAAQ,QACRa,SAAS,2CACZ,wBAAwBZ,MAAK,WACvBvD,KAAKoE,gBACAC,WAAWC,SAAS,CACrBC,IAAKvE,KAAKoE,UAAY,GACtBI,KAAM,EACNC,SAAU,mBAMhCV,KAAK/D,OAGNA,KAAKqB,gBASLA,OAAOE,GAAG,QAAQ,KACdvB,KAAK0E,SACF1E,KAAK2E,aACAC,0BAGJC,SAEF,KAIN7E,KAAK2E,cAOLtD,OAAOE,GACR,OACA,gBACSuD,SAAU,eACX/E,MAAM,oBACZgE,KAAK/D,YAENqB,OAAOE,GACR,UACA,gBACSuD,SAAU,eACX/E,MAAM,uBACZgE,KAAK/D,YAINqB,OAAOE,GACR,QACA,gBACSuD,SAAU,eACX/E,MAAM,qBACZgE,KAAK/D,YAENqB,OAAOE,GACR,UACA,gBACSuD,SAAU,eACX/E,MAAM,uBACZgE,KAAK/D,YAENqB,OAAOE,GACR,UACA,gBACSuD,SAAU,eACX/E,MAAM,uBACZgE,KAAK/D,YAENqB,OAAOE,GACR,QACA,gBACSuD,SAAU,eACX/E,MAAM,qBACZgE,KAAK/D,YAGNqB,OAAOE,GACR,sBACA,gBACSwD,aAAe/E,KAAKqB,OAAO0D,gBAClChB,KAAK/D,YAINqB,OAAOE,GACR,aACA,gBACSyD,YAAchF,KAAKqB,OAAO2D,mBAC1BC,QAAUjF,KAAKgF,YAAchF,KAAKqB,OAAOM,wBAC1C5B,MACA,mCACIC,KAAKiF,QACL,mBACAjF,KAAKgF,cAEfjB,KAAK/D,YAINqB,OAAOE,GAAG,QAASvB,KAAKkF,UAAUnB,KAAK/D,YACvCqB,OAAOE,GAAG,QAASvB,KAAKkF,UAAUnB,KAAK/D,OAGxCA,KAAKqB,OAAOlB,UAAUW,YAAY,CACnB,IAAIqE,gBAAe,UACzB9D,OAAOF,OACPnB,KAAKqB,OAAO+D,cAAgBpF,KAAKqB,OAAOgE,aACrCrF,KAAKqB,OAAOiE,mBAGfC,QAAQC,SAASC,cAAc,IAAMzF,KAAKC,+BA1G/CF,MACA,yDACIC,KAAK0F,MAkHrB9C,aAAaF,eACLkB,KAAOlB,UAAUC,MACjB,uDAEAiB,WACK/B,WACD,KAAOxB,OAAOuD,KAAK,IAAM,GACzB,GAAKvD,OAAOuD,KAAK,IAAM,GACvBvD,OAAOuD,KAAK,IACT5D,KAAKqB,OAAO2D,YAAYhF,KAAK6B,2BAEpC9B,MAAM,kBAAoB2C,WACvB1C,KAAKqB,OAAO2D,eAQvBW,qBACW,IAAIC,SAAQC,UACfA,QAAQ7F,KAAKqB,OAAOM,aACb,KASfmE,yBACW,IAAIF,SAAQC,UACfA,QAAQ7F,KAAKqB,OAAO0D,iBACb,KAUf9C,mBAAmB8D,aACR,IAAIH,SAAQC,UACfA,QAAQ7F,KAAKqB,OAAO2D,YAAYe,QACzB,KASfC,4BACW,IAAIJ,SAAQC,UACfA,QAAQ7F,KAAKqB,OAAO2D,gBACb"} \ No newline at end of file +{"version":3,"file":"videotime.min.js","sources":["../src/videotime.js"],"sourcesContent":["/*\n * Video time player specific js\n *\n * @package videotimeplugin_videojs\n * @module videotimeplugin_videojs/videotime\n * @copyright 2022 bdecent gmbh \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport $ from \"jquery\";\nimport VideoTimeBase from \"mod_videotime/videotime\";\nimport Log from \"core/log\";\nimport Notification from \"core/notification\";\nimport Player from \"media_videojs/video-lazy\";\nimport \"media_videojs/Youtube-lazy\";\n\nexport default class VideoTime extends VideoTimeBase {\n initialize() {\n Log.debug(\"Initializing Video Time \" + this.elementId);\n\n let instance = this.instance,\n options = {\n autoplay: Number(instance.autoplay),\n controls: Number(instance.controls),\n sources: [{type: instance.type, src: instance.vimeo_url}],\n loop: Number(instance.option_loop),\n fluid: Number(instance.responsive),\n playbackRates: Number(instance.speed)\n ? [0.5, 0.75, 1, 1.25, 1.5, 2]\n : [1],\n muted: Number(instance.muted)\n };\n if (instance.type === \"video/youtube\") {\n options.techOrder = [\"youtube\"];\n }\n if (!Number(instance.responsive) && Number(instance.height) && Number(instance.width)) {\n options.height = Number(instance.height);\n options.width = Number(instance.width);\n }\n Log.debug(\"Initializing VideoJS player with options:\");\n Log.debug(options);\n this.player = new Player(this.elementId, options);\n\n this.player.on(\"loadedmetadata\", () => {\n if (!instance.resume_playback || instance.resume_time <= 0 || this.resumed) {\n return true;\n }\n\n let duration = this.getPlayer().duration(),\n resumeTime = instance.resume_time;\n // Duration is often a little greater than a resume time at the end of the video.\n // A user may have watched 100 seconds when the video ends, but the duration may be\n // 100.56 seconds. BUT, sometimes the duration is rounded depending on when the\n // video loads, so it may be 101 seconds. Hence the +1 and Math.floor usage.\n if (resumeTime + 1 >= Math.floor(duration)) {\n Log.debug(\n \"VIDEO_TIME video finished, resuming at start of video.\"\n );\n resumeTime = 0;\n }\n Log.debug(\"VIDEO_TIME duration is \" + duration);\n Log.debug(\"VIDEO_TIME resuming at \" + resumeTime);\n if (resumeTime) {\n setTimeout(() => {\n this.setCurrentPosition(resumeTime);\n }, 10);\n }\n return true;\n });\n\n let url = new URL(window.location.href),\n q = url.searchParams.get(\"q\"),\n starttime = (url.searchParams.get(\"time\") || \"\").match(\n /^([0-9]+:){0,2}([0-9]+)(\\.[0-9]+)$/\n );\n if (starttime) {\n this.setStartTime(starttime[0])\n .then(function() {\n if (q && window.find) {\n window.find(q);\n }\n return true;\n })\n .catch(Notification.exception);\n } else if (q && window.find) {\n window.find(q);\n }\n\n this.addListeners();\n\n for (let i = 0; i < this.plugins.length; i++) {\n const plugin = this.plugins[i];\n plugin.initialize(this, instance);\n }\n\n return true;\n }\n\n /**\n * Register player events to respond to user interaction and play progress.\n */\n addListeners() {\n // If this is a tab play set time cues and listener.\n $($(\"#\" + this.elementId).closest(\".videotimetabs\")).each(\n function(i, tabs) {\n $(tabs)\n .find('[data-action=\"cue\"]')\n .each(\n function(index, anchor) {\n let starttime = anchor.getAttribute(\"data-start\"),\n time = starttime.match(\n /((([0-9]+):)?(([0-9]+):))?([0-9]+(\\.[0-9]+))/\n );\n if (time) {\n this.player\n .addCuePoint(\n 3600 * Number(time[3] || 0) +\n 60 * Number(time[5] || 0) +\n Number(time[6]),\n {\n starttime: starttime\n }\n )\n .catch(Notification.exeception);\n }\n }.bind(this)\n );\n\n this.player.on(\"cuepoint\", function(event) {\n if (event.data.starttime) {\n $(tabs)\n .find(\".videotime-highlight\")\n .removeClass(\"videotime-highlight\");\n $(tabs)\n .find(\n '[data-action=\"cue\"][data-start=\"' +\n event.data.starttime +\n '\"]'\n )\n .closest(\".row\")\n .addClass(\"videotime-highlight\");\n $(\".videotime-highlight\").each(function() {\n if (this.offsetTop) {\n this.parentNode.scrollTo({\n top: this.offsetTop - 50,\n left: 0,\n behavior: \"smooth\"\n });\n }\n });\n }\n });\n }.bind(this)\n );\n\n if (!this.player) {\n Log.debug(\n \"Player was not properly initialized for course module \" +\n this.cmId\n );\n return;\n }\n\n // Fire view event in Moodle on first play only.\n this.player.on(\"play\", () => {\n if (!this.played) {\n if (this.hasPro) {\n this.startWatchInterval();\n }\n // Free version can still mark completion on video time view.\n this.view();\n }\n return true;\n });\n\n // Features beyond this point are for pro only.\n if (!this.hasPro) {\n return;\n }\n\n // Note: Vimeo player does not support multiple events in a single on() call. Each requires it's own function.\n\n // Catch all events where video plays.\n this.player.on(\n \"play\",\n function() {\n this.playing = true;\n Log.debug(\"VIDEO_TIME play\");\n }.bind(this)\n );\n this.player.on(\n \"playing\",\n function() {\n this.playing = true;\n Log.debug(\"VIDEO_TIME playing\");\n }.bind(this)\n );\n\n // Catch all events where video stops.\n this.player.on(\n \"pause\",\n function() {\n this.playing = false;\n Log.debug(\"VIDEO_TIME pause\");\n }.bind(this)\n );\n this.player.on(\n \"stalled\",\n function() {\n this.playing = false;\n Log.debug(\"VIDEO_TIME stalled\");\n }.bind(this)\n );\n this.player.on(\n \"suspend\",\n function() {\n this.playing = false;\n Log.debug(\"VIDEO_TIME suspend\");\n }.bind(this)\n );\n this.player.on(\n \"abort\",\n function() {\n this.playing = false;\n Log.debug(\"VIDEO_TIME abort\");\n }.bind(this)\n );\n\n this.player.on(\n \"playbackrateschange\",\n function() {\n this.playbackRate = this.player.playbackRate();\n }.bind(this)\n );\n\n // Always update internal values for percent and current time watched.\n this.player.on(\n \"timeupdate\",\n function() {\n this.currentTime = this.player.currentTime();\n this.percent = this.currentTime / this.player.duration();\n Log.debug(\n \"VIDEO_TIME timeupdate. Percent: \" +\n this.percent +\n \". Current time: \" +\n this.currentTime\n );\n }.bind(this)\n );\n\n // Initiate video finish procedure.\n this.player.on(\"ended\", this.handleEnd.bind(this));\n this.player.on(\"pause\", this.handleEnd.bind(this));\n\n // Readjust height when responsive player is resized.\n if (this.player.options().responsive) {\n let observer = new ResizeObserver(() => {\n this.player.height(\n (this.player.videoHeight() / this.player.videoWidth()) *\n this.player.currentWidth()\n );\n });\n observer.observe(document.querySelector(\"#\" + this.elementId));\n }\n }\n\n /**\n * Parse start time and set player\n *\n * @param {string} starttime\n * @returns {Promise}\n */\n setStartTime(starttime) {\n let time = starttime.match(\n /((([0-9]+):)?(([0-9]+):))?([0-9]+(\\.[0-9]+))/\n );\n if (time) {\n this.resumeTime =\n 3600 * Number(time[3] || 0) +\n 60 * Number(time[5] || 0) +\n Number(time[6]);\n return this.player.currentTime(this.resumeTime);\n }\n Log.debug(\"Set start time:\" + starttime);\n return this.player.currentTime();\n }\n\n /**\n * Get play back rate\n *\n * @returns {Promise}\n */\n getDuration() {\n return new Promise(resolve => {\n resolve(this.player.duration());\n return true;\n });\n }\n\n /**\n * Get duration of video\n *\n * @returns {Promise}\n */\n getPlaybackRate() {\n return new Promise(resolve => {\n resolve(this.player.playbackRate());\n return true;\n });\n }\n\n /**\n * Set current time of player\n *\n * @param {float} secs time\n * @returns {Promise}\n */\n setCurrentPosition(secs) {\n return new Promise(resolve => {\n resolve(this.player.currentTime(secs));\n return true;\n });\n }\n\n /**\n * Get current time of player\n *\n * @returns {Promise}\n */\n getCurrentPosition() {\n return new Promise(resolve => {\n resolve(this.player.currentTime());\n return true;\n });\n }\n}\n"],"names":["VideoTime","VideoTimeBase","initialize","debug","this","elementId","instance","options","autoplay","Number","controls","sources","type","src","vimeo_url","loop","option_loop","fluid","responsive","playbackRates","speed","muted","techOrder","height","width","player","Player","on","resume_playback","resume_time","resumed","duration","getPlayer","resumeTime","Math","floor","setTimeout","setCurrentPosition","url","URL","window","location","href","q","searchParams","get","starttime","match","setStartTime","then","find","catch","Notification","exception","addListeners","i","plugins","length","closest","each","tabs","index","anchor","getAttribute","time","addCuePoint","exeception","bind","event","data","removeClass","addClass","offsetTop","parentNode","scrollTo","top","left","behavior","played","hasPro","startWatchInterval","view","playing","playbackRate","currentTime","percent","handleEnd","ResizeObserver","videoHeight","videoWidth","currentWidth","observe","document","querySelector","cmId","getDuration","Promise","resolve","getPlaybackRate","secs","getCurrentPosition"],"mappings":"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"} \ No newline at end of file diff --git a/plugin/videojs/amd/src/videotime.js b/plugin/videojs/amd/src/videotime.js index 358a6d68..5c70ab53 100644 --- a/plugin/videojs/amd/src/videotime.js +++ b/plugin/videojs/amd/src/videotime.js @@ -25,7 +25,7 @@ export default class VideoTime extends VideoTimeBase { sources: [{type: instance.type, src: instance.vimeo_url}], loop: Number(instance.option_loop), fluid: Number(instance.responsive), - playbackRates: instance.speed + playbackRates: Number(instance.speed) ? [0.5, 0.75, 1, 1.25, 1.5, 2] : [1], muted: Number(instance.muted) diff --git a/plugin/videojs/lib.php b/plugin/videojs/lib.php index ec1e4229..ff50d904 100644 --- a/plugin/videojs/lib.php +++ b/plugin/videojs/lib.php @@ -210,6 +210,7 @@ function videotimeplugin_videojs_embed_player($instance) { if ( empty(get_config('videotimeplugin_videojs', 'enabled')) + || empty($instance->vimeo_url) || mod_videotime_get_vimeo_id_from_link($instance->vimeo_url) ) { return null; @@ -232,7 +233,26 @@ function videotimeplugin_videojs_add_form_fields($mform, $formclass) { $mform->createElement('filemanager', 'mediafile', get_string('mediafile', 'videotimeplugin_videojs'), null, [ 'subdirs' => 0, 'maxfiles' => 1, - 'accepted_types' => ['audio', 'video'], + 'accepted_types' => [ + '.flac', + '.mp3', + '.mp4', + '.mov', + '.movie', + '.m3u', + '.m3u8', + '.m4a', + '.m4v', + '.mp4', + '.mpeg', + '.ogg', + '.oga', + '.ogv', + '.wav', + '.webm', + '.wma', + '.wmv', + ], ]), 'name' ); diff --git a/plugin/videojs/version.php b/plugin/videojs/version.php index e156f1c0..74f5ba52 100644 --- a/plugin/videojs/version.php +++ b/plugin/videojs/version.php @@ -26,7 +26,7 @@ $plugin->component = 'videotimeplugin_videojs'; $plugin->release = '1.7'; -$plugin->version = 2023011200; +$plugin->version = 2023011201; $plugin->requires = 2015111610; $plugin->maturity = MATURITY_STABLE; $plugin->dependencies = [