From 957ae91569a3f4bb9b350b3ecf418374ce5b55bd Mon Sep 17 00:00:00 2001 From: Daniel Thies Date: Wed, 6 Sep 2023 05:08:25 -0500 Subject: [PATCH] VID-656: Improve publishing interface --- plugin/live/amd/build/videotime.min.js | 2 +- plugin/live/amd/build/videotime.min.js.map | 2 +- plugin/live/amd/src/videotime.js | 115 ++++++++++++++---- plugin/live/classes/external/publish_feed.php | 6 - plugin/live/classes/janus_room.php | 21 +++- plugin/live/lib.php | 4 + plugin/live/styles.css | 15 ++- plugin/live/templates/controls.mustache | 36 +++--- 8 files changed, 141 insertions(+), 60 deletions(-) diff --git a/plugin/live/amd/build/videotime.min.js b/plugin/live/amd/build/videotime.min.js index 4cacc10f..94eece3d 100644 --- a/plugin/live/amd/build/videotime.min.js +++ b/plugin/live/amd/build/videotime.min.js @@ -6,6 +6,6 @@ define("videotimeplugin_live/videotime",["exports","core/ajax","mod_videotime/vi * @module videotimeplugin_live/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,_ajax=_interopRequireDefault(_ajax),_videotime=_interopRequireDefault(_videotime),_janusGateway=_interopRequireDefault(_janusGateway),_log=_interopRequireDefault(_log),_notification=_interopRequireDefault(_notification),_publish=_interopRequireDefault(_publish),_subscribe=_interopRequireDefault(_subscribe),_socket=_interopRequireDefault(_socket);var rooms={};class Publish extends _publish.default{register(pluginHandle){return _ajax.default.call([{args:{handle:pluginHandle.getId(),id:Number(this.contextid),plugin:pluginHandle.plugin,room:this.roomid,ptype:"publish"==this.ptype,session:pluginHandle.session.getSessionId()},contextid:this.contextid,fail:_notification.default.exception,methodname:"videotimeplugin_live_join_room"}])[0].then((response=>{this.feed=response.id})).catch(_notification.default.exception)}publishFeed(){return _ajax.default.call([{args:{id:Number(this.feed),room:this.roomid},contextid:this.contextid,fail:_notification.default.exception,methodname:"videotimeplugin_live_publish_feed"}])[0]}unpublish(){return _ajax.default.call([{args:{id:Number(this.feed),publish:!1,room:this.roomid},contextid:this.contextid,fail:_notification.default.exception,methodname:"videotimeplugin_live_publish_feed"}])[0]}handleClose(){document.querySelectorAll('[data-contextid="'+this.contextid+'"][data-action="publish"]').forEach((button=>{button.classList.remove("hidden")})),this.janus.destroy(),[this.currentCamera||Promise.resolve(null),this.currentDisplay||Promise.resolve(null)].forEach((videoInput=>{videoInput.then((videoStream=>(videoStream&&videoStream.getVideoTracks().forEach((track=>{track.enabled=!1,track.stop()})),null))).catch(_notification.default.exception)}))}onLocalTrack(track,on){const remoteStream=new MediaStream([track]);on&&"audio"!=track.kind&&(remoteStream.mid=track.mid,_log.default.debug(on),_log.default.debug(remoteStream),_janusGateway.default.attachMediaStream(document.getElementById("video-controls-"+this.tracks[track.id]),remoteStream))}handleClick(e){const button=e.target.closest('[data-contextid="'+this.contextid+'"][data-action="publish"], [data-contextid="'+this.contextid+'"][data-action="mute"], [data-contextid="'+this.contextid+'"][data-action="unmute"], [data-contextid="'+this.contextid+'"][data-action="unpublish"]');if(button){const action=button.getAttribute("data-action"),type=button.getAttribute("data-type")||"camera",transceiver=this.getTransceiver("audio");switch(e.stopPropagation(),e.preventDefault(),document.querySelectorAll('[data-region="deft-venue"] [data-action="publish"], [data-region="deft-venue"] [data-action="unpublish"]').forEach((button=>{button.getAttribute("data-action")==action&&button.getAttribute("data-type")==type||button.classList.remove("hidden")})),action){case"mute":transceiver&&(transceiver.sender.track.enabled=!1);break;case"unmute":transceiver&&(transceiver.sender.track.enabled=!0);break;case"publish":_log.default.debug(type),"display"==type?this.shareDisplay():this.shareCamera(),this.processStream([]);break;case"unpublish":this.unpublish()}}return!0}shareCamera(){const videoInput=this.videoInput,currentCamera=this.currentCamera||Promise.resolve(null);this.videoInput=currentCamera.then((videoStream=>{if(videoStream)return videoStream;{const cameraInput=navigator.mediaDevices.getUserMedia({video:!0,audio:!0});return this.currentCamera=cameraInput.catch((()=>currentCamera)),cameraInput.then((videoStream=>(this.tracks=this.tracks||{},videoStream.getTracks().forEach((track=>{this.tracks[track.id]="camera"})),videoStream))).catch((e=>(_log.default.debug(e),videoInput)))}}))}shareDisplay(){const videoInput=this.videoInput,currentDisplay=this.currentDisplay||Promise.resolve(null),displayInput=navigator.mediaDevices.getDisplayMedia({video:!0,audio:!0});this.videoInput=displayInput.then((videoStream=>(videoInput&&videoInput.then((videoStream=>(videoStream&&videoStream.getTracks().forEach((track=>{_log.default.debug(track)})),videoStream))).catch(_notification.default.exception),this.tracks=this.tracks||{},videoStream.getTracks().forEach((track=>{this.tracks[track.id]="display"})),videoStream))).catch((e=>(_log.default.debug(e),videoInput))),this.currentDisplay=displayInput.then((videoStream=>(currentDisplay.then((videoStream=>{videoStream&&videoStream.getTracks().forEach((track=>{_log.default.debug("stop track"),_log.default.debug(track),track.stop()}))})),videoStream))).catch((e=>(_log.default.debug(e),currentDisplay)))}processStream(tracks){this.videoInput.then((videoStream=>{if(this.tracks=this.tracks||{},videoStream){const audiotransceiver=this.getTransceiver("audio"),videotransceiver=this.getTransceiver("video");if(videoStream.getVideoTracks().forEach((track=>{track.addEventListener("ended",(()=>{this.selectedTrack!=track.id&&this.unpublish()})),this.selectedTrack=track,videotransceiver?this.videoroom.replaceTracks({tracks:[{type:"video",mid:videotransceiver.mid,capture:track}],error:_notification.default.exception}):tracks.push({type:"video",capture:track,recv:!1})})),videoStream.getAudioTracks().forEach((track=>{track.addEventListener("ended",(()=>{this.selectedTrack!=track.id&&this.unpublish()})),document.querySelector('.hidden[data-action="mute"][data-contextid="'+this.contextid+'"]')&&(track.enabled=!1),audiotransceiver?this.videoroom.replaceTracks({tracks:[{type:"audio",mid:audiotransceiver.mid,capture:track}],error:_notification.default.exception}):tracks.push({type:"audio",capture:track,recv:!1})})),!tracks.length)return videoStream;this.videoroom.createOffer({tracks:tracks,success:jsep=>{this.videoroom.send({message:{request:"configure",video:!0,audio:!0},jsep:jsep})},error:function(error){_notification.default.alert("WebRTC error... ",error.message)}})}return videoStream})).catch(_notification.default.exception)}}class VideoTime extends _videotime.default{initialize(contextid,token,peerid){return _log.default.debug("Initializing Video Time "+this.elementId),this.contextid=contextid,this.peerid=peerid,_ajax.default.call([{methodname:"videotimeplugin_live_get_room",args:{contextid:contextid},done:response=>{const socket=new _socket.default(contextid,token);this.iceservers=JSON.parse(response.iceservers),this.roomid=response.roomid,this.server=response.server,rooms[String(contextid)]={contextid:contextid,peerid:peerid,roomid:response.roomid,server:response.server,iceServers:JSON.parse(response.iceservers)},this.roomid=response.roomid,document.querySelector('[data-contextid="'+this.contextid+'"] .videotime-control').classList.remove("hidden"),socket.subscribe((()=>{_ajax.default.call([{methodname:"videotimeplugin_live_get_feed",args:{contextid:contextid},done:response=>{const room=rooms[String(contextid)];room.publish&&room.publish.restart&&(response.feed==peerid&&this.unpublish(),room.publish=null),this.subscribeTo(Number(response.feed))},fail:_notification.default.exception}])}))},fail:_notification.default.exception}]),this.addListeners(),!0}addListeners(){document.querySelector("body").removeEventListener("click",handleClick),document.querySelector("body").addEventListener("click",handleClick)}subscribeTo(source){_log.default.debug(source);const room=rooms[String(this.contextid)];if(document.querySelectorAll('[data-contextid="'+this.contextid+'"][data-action="publish"]').forEach((button=>{Number(this.peerid),button.classList.remove("hidden")})),document.querySelectorAll('[data-contextid="'+this.contextid+'"][data-action="unpublish"]').forEach((button=>{Number(this.peerid),button.classList.remove("hidden")})),_log.default.debug(source),!this.remoteFeed||this.remoteFeed.creatingSubscription||this.remoteFeed.restart)this.remoteFeed&&this.remoteFeed.restart?this.remoteFeed.current!=source&&(this.remoteFeed=null,this.subscribeTo(source)):this.remoteFeed?setTimeout((()=>{this.subscribeTo(source)}),500):source&&(this.remoteFeed=new Subscribe(this.contextid,this.iceservers,this.roomid,this.server,this.peerid,source),this.remoteFeed.remoteVideo=document.getElementById(this.elementId),this.remoteFeed.remoteAudio=document.getElementById(this.elementId).parentNode.querySelector("audio"),this.remoteFeed.muteAudio=room.publish&&room.publish.feed===source,this.remoteFeed.startConnection(source),document.querySelectorAll('[data-contextid="'+this.contextid+'"] img.poster-img').forEach((img=>{img.classList.add("hidden")})),document.querySelectorAll('[data-contextid="'+this.contextid+'"] video').forEach((img=>{img.classList.remove("hidden")})));else{const update={request:"update",subscribe:[{feed:Number(source)}],unsubscribe:[{feed:Number(this.remoteFeed.current)}]};!source&&this.remoteFeed.current?delete update.subscribe:source&&!this.remoteFeed.current&&delete update.unsubscribe,this.remoteFeed.current!=source&&(this.remoteFeed.muteAudio=room.publish&&room.publish.feed===source,this.remoteFeed.videoroom.send({message:update}),this.remoteFeed.audioTrack&&(this.remoteFeed.audioTrack.enabled=!this.remoteFeed.muteAudio),room.publish&&this.remoteFeed.current==room.publish.feed&&(room.publish.handleClose(),room.publish=null),this.remoteFeed.current=source,!source&&this.remoteFeed&&(this.remoteFeed.handleClose(),this.remoteFeed=null),_log.default.debug('[data-contextid="'+this.contextid+'"] img.poster-img'),Number(source)?(document.querySelectorAll('[data-contextid="'+this.contextid+'"] img.poster-img').forEach((img=>{img.classList.add("hidden")})),document.querySelectorAll('[data-contextid="'+this.contextid+'"] video').forEach((video=>{video.classList.remove("hidden")}))):(document.querySelectorAll('[data-contextid="'+this.contextid+'"] img.poster-img').forEach((img=>{img.classList.remove("hidden")})),document.querySelectorAll('[data-contextid="'+this.contextid+'"] video').forEach((video=>{video.classList.add("hidden")}))))}}}_exports.default=VideoTime;const handleClick=function(e){const button=e.target.closest('[data-roomid] [data-action="publish"], [data-roomid] [data-action="unpublish"],[data-roomid] [data-action="mute"], [data-roomid] [data-action="unmute"]');if(button){const action=button.getAttribute("data-action"),contextid=e.target.closest("[data-contextid]").getAttribute("data-contextid"),room=rooms[String(contextid)],iceServers=room.iceServers,peerid=room.peerid,roomid=room.roomid,server=room.server,type=button.getAttribute("data-type");e.stopPropagation(),e.preventDefault(),"publish"!=action||room.publish&&!room.publish.restart?("mute"!=action&&"unmute"!=action||(button.classList.add("hidden"),button.parentNode.querySelectorAll('[data-action="mute"], [data-action="unmute"]').forEach((button=>{button.getAttribute("data-action")!=action&&button.classList.remove("hidden")}))),room.publish&&room.publish.handleClick(e)):(room.publish=new Publish(contextid,iceServers,roomid,server,peerid),"display"==type?room.publish.shareDisplay():room.publish.shareCamera(),room.publish.startConnection())}};class Subscribe extends _subscribe.default{register(pluginHandle){return _ajax.default.call([{args:{handle:pluginHandle.getId(),id:Number(this.contextid),plugin:pluginHandle.plugin,room:this.roomid,ptype:!1,feed:this.feed,session:pluginHandle.session.getSessionId()},contextid:this.contextid,fail:_notification.default.exception,methodname:"videotimeplugin_live_join_room"}])[0]}attachAudio(audioStream){_janusGateway.default.attachMediaStream(this.remoteVideo.parentNode.querySelector("audio"),audioStream),audioStream.getTracks().forEach((track=>{this.audioTrack=track,track.enabled=!this.muteAudio}))}attachVideo(videoStream){_janusGateway.default.attachMediaStream(this.remoteVideo,videoStream)}}return _exports.default})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_ajax=_interopRequireDefault(_ajax),_videotime=_interopRequireDefault(_videotime),_janusGateway=_interopRequireDefault(_janusGateway),_log=_interopRequireDefault(_log),_notification=_interopRequireDefault(_notification),_publish=_interopRequireDefault(_publish),_subscribe=_interopRequireDefault(_subscribe),_socket=_interopRequireDefault(_socket);var rooms={};class Publish extends _publish.default{register(pluginHandle){return _ajax.default.call([{args:{handle:pluginHandle.getId(),id:Number(this.contextid),plugin:pluginHandle.plugin,room:this.roomid,ptype:"publish"==this.ptype,session:pluginHandle.session.getSessionId()},contextid:this.contextid,fail:_notification.default.exception,methodname:"videotimeplugin_live_join_room"}])[0].then((response=>{this.feed=response.id})).catch(_notification.default.exception)}publishFeed(){return _ajax.default.call([{args:{id:Number(this.feed),room:this.roomid},contextid:this.contextid,fail:_notification.default.exception,methodname:"videotimeplugin_live_publish_feed"}])[0]}unpublish(){return document.querySelectorAll("#video-controls-camera, #video-controls-display").forEach((video=>{video.srcObject=null,video.parentNode.classList.add("hidden")})),_ajax.default.call([{args:{id:Number(this.feed),publish:!1,room:this.roomid},contextid:this.contextid,fail:_notification.default.exception,methodname:"videotimeplugin_live_publish_feed"}])[0]}handleClose(){document.querySelectorAll('[data-contextid="'+this.contextid+'"][data-action="publish"]').forEach((button=>{button.classList.remove("hidden")})),this.janus.destroy(),[this.currentCamera||Promise.resolve(null),this.currentDisplay||Promise.resolve(null)].forEach((videoInput=>{videoInput.then((videoStream=>(videoStream&&videoStream.getTracks().forEach((track=>{track.enabled=!1,track.stop()})),null))).catch(_notification.default.exception)}))}onLocalTrack(track,on){const remoteStream=new MediaStream([track]);on&&"audio"!=track.kind&&(remoteStream.mid=track.mid,_log.default.debug(on),_log.default.debug(remoteStream),_janusGateway.default.attachMediaStream(document.getElementById("video-controls-"+this.tracks[track.id]),remoteStream))}handleClick(e){const button=e.target.closest('[data-contextid="'+this.contextid+'"][data-action="publish"], [data-contextid="'+this.contextid+'"][data-action="close"], [data-contextid="'+this.contextid+'"][data-action="mute"], [data-contextid="'+this.contextid+'"][data-action="unmute"], [data-contextid="'+this.contextid+'"][data-action="switch"], [data-contextid="'+this.contextid+'"][data-action="unpublish"]');if(button){const action=button.getAttribute("data-action"),type=button.getAttribute("data-type")||"camera";switch(e.stopPropagation(),e.preventDefault(),document.querySelectorAll('[data-region="deft-venue"] [data-action="publish"], [data-region="deft-venue"] [data-action="unpublish"]').forEach((button=>{button.getAttribute("data-action")==action&&button.getAttribute("data-type")==type||button.classList.remove("hidden")})),action){case"close":document.getElementById("video-controls-"+type).srcObject=null,document.getElementById("video-controls-"+type).parentNode.classList.add("hidden"),this.tracks[this.selectedTrack.id]==type&&this.unpublish();break;case"mute":case"unmute":("display"==type?this.currentDisplay:this.currentCamera).then((videoStream=>(videoStream&&videoStream.getAudioTracks().forEach((track=>{track.enabled="unmute"==action})),videoStream))).catch(_notification.default.exception);break;case"publish":_log.default.debug(type),"display"==type?this.shareDisplay():this.shareCamera(),document.querySelectorAll("#video-controls-camera, #video-controls-display").forEach((video=>{video.parentNode.classList.remove("selected")})),document.getElementById("video-controls-"+type).parentNode.classList.remove("hidden"),document.getElementById("video-controls-"+type).parentNode.classList.add("selected"),this.processStream([]);break;case"switch":document.querySelectorAll("#video-controls-camera, #video-controls-display").forEach((video=>{video.parentNode.classList.remove("selected")})),this.videoInput="display"==type?this.currentDisplay:this.currentCamera,document.getElementById("video-controls-"+type).parentNode.classList.remove("hidden"),document.getElementById("video-controls-"+type).parentNode.classList.add("selected"),this.processStream([]);break;case"unpublish":this.unpublish()}}return!0}shareCamera(){const videoInput=this.videoInput,currentCamera=this.currentCamera||Promise.resolve(null);this.videoInput=currentCamera.then((videoStream=>{if(videoStream)return videoStream;{const cameraInput=navigator.mediaDevices.getUserMedia({video:!0,audio:!0});return this.currentCamera=cameraInput.catch((()=>currentCamera)),cameraInput.then((videoStream=>(this.tracks=this.tracks||{},videoStream.getTracks().forEach((track=>{this.tracks[track.id]="camera"})),videoStream))).catch((e=>(_log.default.debug(e),videoInput)))}}))}shareDisplay(){const videoInput=this.videoInput,currentDisplay=this.currentDisplay||Promise.resolve(null),displayInput=navigator.mediaDevices.getDisplayMedia({video:!0,audio:!0});this.videoInput=displayInput.then((videoStream=>(videoInput&&videoInput.then((videoStream=>(videoStream&&videoStream.getTracks().forEach((track=>{_log.default.debug(track)})),videoStream))).catch(_notification.default.exception),this.tracks=this.tracks||{},videoStream.getTracks().forEach((track=>{this.tracks[track.id]="display"})),videoStream))).catch((e=>(_log.default.debug(e),videoInput))),this.currentDisplay=displayInput.then((videoStream=>(currentDisplay.then((videoStream=>{videoStream&&videoStream.getTracks().forEach((track=>{_log.default.debug("stop track"),_log.default.debug(track),track.stop()}))})),videoStream))).catch((e=>(_log.default.debug(e),currentDisplay)))}processStream(tracks){this.videoInput.then((videoStream=>{if(this.tracks=this.tracks||{},videoStream){const audiotransceiver=this.getTransceiver("audio"),videotransceiver=this.getTransceiver("video");if(videoStream.getVideoTracks().forEach((track=>{track.addEventListener("ended",(()=>{this.selectedTrack.id==track.id?this.unpublish():document.getElementById("video-controls-"+this.tracks[track.id]).parentNode.classList.add("hidden")})),this.selectedTrack=track,videotransceiver?this.videoroom.replaceTracks({tracks:[{type:"video",mid:videotransceiver.mid,capture:track}],error:_notification.default.exception}):tracks.push({type:"video",capture:track,recv:!1})})),videoStream.getAudioTracks().forEach((track=>{document.querySelector('.hidden[data-action="mute"][data-contextid="'+this.contextid+'"][data-type="'+this.tracks[this.selectedTrack.id]+'"]')&&(track.enabled=!1),audiotransceiver?this.videoroom.replaceTracks({tracks:[{type:"audio",mid:audiotransceiver.mid,capture:track}],error:_notification.default.exception}):tracks.push({type:"audio",capture:track,recv:!1})})),!tracks.length)return videoStream;this.videoroom.createOffer({tracks:tracks,success:jsep=>{this.videoroom.send({message:{request:"configure",video:!0,audio:!0},jsep:jsep})},error:function(error){_notification.default.alert("WebRTC error... ",error.message)}})}return videoStream})).catch(_notification.default.exception)}}class VideoTime extends _videotime.default{initialize(contextid,token,peerid){return _log.default.debug("Initializing Video Time "+this.elementId),this.contextid=contextid,this.peerid=peerid,_ajax.default.call([{methodname:"videotimeplugin_live_get_room",args:{contextid:contextid},done:response=>{const socket=new _socket.default(contextid,token);this.iceservers=JSON.parse(response.iceservers),this.roomid=response.roomid,this.server=response.server,rooms[String(contextid)]={contextid:contextid,peerid:peerid,roomid:response.roomid,server:response.server,iceServers:JSON.parse(response.iceservers)},this.roomid=response.roomid,document.querySelector('[data-contextid="'+this.contextid+'"] .videotime-control').classList.remove("hidden"),socket.subscribe((()=>{_ajax.default.call([{methodname:"videotimeplugin_live_get_feed",args:{contextid:contextid},done:response=>{const room=rooms[String(contextid)];room.publish&&room.publish.restart&&(response.feed==peerid&&this.unpublish(),room.publish=null),this.subscribeTo(Number(response.feed))},fail:_notification.default.exception}])}))},fail:_notification.default.exception}]),this.addListeners(),!0}addListeners(){document.querySelector("body").removeEventListener("click",handleClick),document.querySelector("body").addEventListener("click",handleClick)}subscribeTo(source){_log.default.debug(source);const room=rooms[String(this.contextid)];if(document.querySelectorAll('[data-contextid="'+this.contextid+'"][data-action="publish"]').forEach((button=>{Number(this.peerid),button.classList.remove("hidden")})),document.querySelectorAll('[data-contextid="'+this.contextid+'"][data-action="unpublish"]').forEach((button=>{Number(this.peerid),button.classList.remove("hidden")})),_log.default.debug(source),!this.remoteFeed||this.remoteFeed.creatingSubscription||this.remoteFeed.restart)this.remoteFeed&&this.remoteFeed.restart?this.remoteFeed.current!=source&&(this.remoteFeed=null,this.subscribeTo(source)):this.remoteFeed?setTimeout((()=>{this.subscribeTo(source)}),500):source&&(this.remoteFeed=new Subscribe(this.contextid,this.iceservers,this.roomid,this.server,this.peerid),this.remoteFeed.remoteVideo=document.getElementById(this.elementId),this.remoteFeed.remoteAudio=document.getElementById(this.elementId).parentNode.querySelector("audio"),this.remoteFeed.muteAudio=room.publish&&room.publish.feed===source,this.remoteFeed.startConnection(source),document.querySelectorAll('[data-contextid="'+this.contextid+'"] img.poster-img').forEach((img=>{img.classList.add("hidden")})),document.querySelectorAll('[data-contextid="'+this.contextid+'"] video').forEach((img=>{img.classList.remove("hidden")})));else{const update={request:"update",subscribe:[{feed:Number(source)}],unsubscribe:[{feed:Number(this.remoteFeed.current)}]};!source&&this.remoteFeed.current?delete update.subscribe:source&&!this.remoteFeed.current&&delete update.unsubscribe,this.remoteFeed.current!=source&&(this.remoteFeed.muteAudio=room.publish&&room.publish.feed===source,this.remoteFeed.videoroom.send({message:update}),this.remoteFeed.audioTrack&&(this.remoteFeed.audioTrack.enabled=!this.remoteFeed.muteAudio),room.publish&&this.remoteFeed.current==room.publish.feed&&(room.publish.handleClose(),room.publish=null),this.remoteFeed.current=source,!source&&this.remoteFeed&&(this.remoteFeed.handleClose(),this.remoteFeed=null),_log.default.debug('[data-contextid="'+this.contextid+'"] img.poster-img'),Number(source)?(document.querySelectorAll('[data-contextid="'+this.contextid+'"] .videotime-embed img.poster-img').forEach((img=>{img.classList.add("hidden")})),document.querySelectorAll('[data-contextid="'+this.contextid+'"] .videotime-embed video').forEach((video=>{video.classList.remove("hidden")}))):(document.querySelectorAll('[data-contextid="'+this.contextid+'"] .videotime-embed img.poster-img').forEach((img=>{img.classList.remove("hidden")})),document.querySelectorAll('[data-contextid="'+this.contextid+'"] .videotime-embed video').forEach((video=>{video.srcObject=null,video.classList.add("hidden")}))))}}}_exports.default=VideoTime;const handleClick=function(e){const button=e.target.closest('[data-roomid] [data-action="publish"], [data-roomid] [data-action="unpublish"],[data-roomid] [data-action="close"], [data-roomid] [data-action="switch"], [data-roomid] [data-action="mute"], [data-roomid] [data-action="unmute"]');if(button){const action=button.getAttribute("data-action"),contextid=e.target.closest("[data-contextid]").getAttribute("data-contextid"),room=rooms[String(contextid)],iceServers=room.iceServers,peerid=room.peerid,roomid=room.roomid,server=room.server,type=button.getAttribute("data-type");e.stopPropagation(),e.preventDefault(),"publish"!=action||room.publish&&!room.publish.restart?("mute"!=action&&"unmute"!=action||(button.classList.add("hidden"),button.parentNode.querySelectorAll('[data-action="mute"], [data-action="unmute"]').forEach((button=>{button.getAttribute("data-action")!=action&&button.classList.remove("hidden")}))),room.publish&&room.publish.handleClick(e)):(room.publish=new Publish(contextid,iceServers,roomid,server,peerid),"display"==type?room.publish.shareDisplay():room.publish.shareCamera(),room.publish.startConnection(),document.getElementById("video-controls-"+(type||"camera")).parentNode.classList.remove("hidden"),document.getElementById("video-controls-"+(type||"camera")).parentNode.classList.add("selected"))}};class Subscribe extends _subscribe.default{register(pluginHandle){return _ajax.default.call([{args:{handle:pluginHandle.getId(),id:Number(this.contextid),plugin:pluginHandle.plugin,room:this.roomid,ptype:!1,feed:this.feed,session:pluginHandle.session.getSessionId()},contextid:this.contextid,fail:_notification.default.exception,methodname:"videotimeplugin_live_join_room"}])[0]}attachAudio(audioStream){_janusGateway.default.attachMediaStream(this.remoteVideo.parentNode.querySelector("audio"),audioStream),audioStream.getTracks().forEach((track=>{this.audioTrack=track,track.enabled=!this.muteAudio}))}attachVideo(videoStream){_janusGateway.default.attachMediaStream(this.remoteVideo,videoStream)}}return _exports.default})); //# sourceMappingURL=videotime.min.js.map \ No newline at end of file diff --git a/plugin/live/amd/build/videotime.min.js.map b/plugin/live/amd/build/videotime.min.js.map index bf8b930a..d3538ee3 100644 --- a/plugin/live/amd/build/videotime.min.js.map +++ b/plugin/live/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_live\n * @module videotimeplugin_live/videotime\n * @copyright 2022 bdecent gmbh \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Ajax from \"core/ajax\";\nimport VideoTimeBase from \"mod_videotime/videotime\";\nimport Janus from 'block_deft/janus-gateway';\nimport Log from \"core/log\";\nimport Notification from \"core/notification\";\nimport PublishBase from \"block_deft/publish\";\nimport SubscribeBase from \"block_deft/subscribe\";\nimport Socket from \"videotimeplugin_live/socket\";\n\nvar rooms = {};\n\nclass Publish extends PublishBase {\n /**\n * Register the room\n *\n * @param {object} pluginHandle\n * @return {Promise}\n */\n register(pluginHandle) {\n // Try a registration\n return Ajax.call([{\n args: {\n handle: pluginHandle.getId(),\n id: Number(this.contextid),\n plugin: pluginHandle.plugin,\n room: this.roomid,\n ptype: this.ptype == 'publish',\n session: pluginHandle.session.getSessionId()\n },\n contextid: this.contextid,\n fail: Notification.exception,\n methodname: 'videotimeplugin_live_join_room'\n }])[0].then(response => {\n this.feed = response.id;\n }).catch(Notification.exception);\n }\n\n /**\n * Publish current video feed\n */\n publishFeed() {\n return Ajax.call([{\n args: {\n id: Number(this.feed),\n room: this.roomid,\n },\n contextid: this.contextid,\n fail: Notification.exception,\n methodname: 'videotimeplugin_live_publish_feed'\n }])[0];\n }\n\n\n /**\n * Stop video feed\n */\n unpublish() {\n return Ajax.call([{\n args: {\n id: Number(this.feed),\n publish: false,\n room: this.roomid\n },\n contextid: this.contextid,\n fail: Notification.exception,\n methodname: 'videotimeplugin_live_publish_feed'\n }])[0];\n }\n\n handleClose() {\n document.querySelectorAll(\n '[data-contextid=\"' + this.contextid + '\"][data-action=\"publish\"]'\n ).forEach(button => {\n button.classList.remove('hidden');\n });\n\n this.janus.destroy();\n\n [\n this.currentCamera || Promise.resolve(null),\n this.currentDisplay || Promise.resolve(null),\n ].forEach(videoInput => {\n videoInput.then(videoStream => {\n if (videoStream) {\n videoStream.getVideoTracks().forEach(track => {\n track.enabled = false;\n track.stop();\n });\n }\n\n return null;\n }).catch(Notification.exception);\n });\n }\n\n onLocalTrack(track, on) {\n const remoteStream = new MediaStream([track]);\n if (!on || (track.kind == 'audio')) {\n return;\n }\n remoteStream.mid = track.mid;\n Log.debug(on);\n Log.debug(remoteStream);\n Janus.attachMediaStream(\n document.getElementById('video-controls-' + this.tracks[track.id]),\n remoteStream\n );\n }\n\n handleClick(e) {\n const button = e.target.closest(\n '[data-contextid=\"' + this.contextid + '\"][data-action=\"publish\"], [data-contextid=\"'\n + this.contextid + '\"][data-action=\"mute\"], [data-contextid=\"'\n + this.contextid + '\"][data-action=\"unmute\"], [data-contextid=\"'\n + this.contextid + '\"][data-action=\"unpublish\"]'\n );\n if (button) {\n const action = button.getAttribute('data-action'),\n type = button.getAttribute('data-type') || 'camera',\n transceiver = this.getTransceiver('audio');\n e.stopPropagation();\n e.preventDefault();\n document.querySelectorAll(\n '[data-region=\"deft-venue\"] [data-action=\"publish\"], [data-region=\"deft-venue\"] [data-action=\"unpublish\"]'\n ).forEach(button => {\n if ((button.getAttribute('data-action') != action) || (button.getAttribute('data-type') != type)) {\n button.classList.remove('hidden');\n }\n });\n switch (action) {\n case 'mute':\n if (transceiver) {\n transceiver.sender.track.enabled = false;\n }\n break;\n case 'unmute':\n if (transceiver) {\n transceiver.sender.track.enabled = true;\n }\n break;\n case 'publish':\n Log.debug(type);\n if (type == 'display') {\n this.shareDisplay();\n } else {\n this.shareCamera();\n }\n\n this.processStream([]);\n break;\n case 'unpublish':\n this.unpublish();\n }\n }\n\n return true;\n }\n\n /**\n * Set video source to user camera\n */\n shareCamera() {\n const videoInput = this.videoInput,\n currentCamera = this.currentCamera || Promise.resolve(null);\n\n this.videoInput = currentCamera.then(videoStream => {\n if (videoStream) {\n return videoStream;\n } else {\n const cameraInput = navigator.mediaDevices.getUserMedia({\n video: true,\n audio: true\n });\n\n this.currentCamera = cameraInput.catch(() => {\n return currentCamera;\n });\n\n return cameraInput.then(videoStream => {\n this.tracks = this.tracks || {};\n videoStream.getTracks().forEach(track => {\n this.tracks[track.id] = 'camera';\n });\n\n return videoStream;\n }).catch((e) => {\n Log.debug(e);\n\n return videoInput;\n });\n }\n });\n }\n\n /**\n * Set video source to display surface\n */\n shareDisplay() {\n const videoInput = this.videoInput,\n currentDisplay = this.currentDisplay || Promise.resolve(null),\n displayInput = navigator.mediaDevices.getDisplayMedia({\n video: true,\n audio: true\n });\n\n this.videoInput = displayInput.then(videoStream => {\n if (videoInput) {\n videoInput.then(videoStream => {\n if (videoStream) {\n videoStream.getTracks().forEach(track => {\n Log.debug(track); //track.stop();\n });\n }\n return videoStream;\n }).catch(Notification.exception);\n }\n this.tracks = this.tracks || {};\n videoStream.getTracks().forEach(track => {\n this.tracks[track.id] = 'display';\n });\n\n return videoStream;\n }).catch((e) => {\n Log.debug(e);\n\n return videoInput;\n });\n\n this.currentDisplay = displayInput.then(videoStream => {\n currentDisplay.then(videoStream => {\n if (videoStream) {\n videoStream.getTracks().forEach(track => {\n Log.debug('stop track');\n Log.debug(track);\n track.stop();\n });\n }\n });\n\n return videoStream;\n }).catch((e) => {\n Log.debug(e);\n\n return currentDisplay;\n });\n }\n\n processStream(tracks) {\n this.videoInput.then(videoStream => {\n this.tracks = this.tracks || {};\n if (videoStream) {\n const audiotransceiver = this.getTransceiver('audio'),\n videotransceiver = this.getTransceiver('video');\n videoStream.getVideoTracks().forEach(track => {\n track.addEventListener('ended', () => {\n if (this.selectedTrack != track.id) {\n this.unpublish();\n }\n });\n this.selectedTrack = track;\n if (videotransceiver) {\n this.videoroom.replaceTracks({\n tracks: [{\n type: 'video',\n mid: videotransceiver.mid,\n capture: track\n }],\n error: Notification.exception\n });\n\n return;\n }\n tracks.push({\n type: 'video',\n capture: track,\n recv: false\n });\n });\n videoStream.getAudioTracks().forEach(track => {\n track.addEventListener('ended', () => {\n if (this.selectedTrack != track.id) {\n this.unpublish();\n }\n });\n if (document.querySelector('.hidden[data-action=\"mute\"][data-contextid=\"' + this.contextid + '\"]')) {\n track.enabled = false;\n }\n\n if (audiotransceiver) {\n this.videoroom.replaceTracks({\n tracks: [{\n type: 'audio',\n mid: audiotransceiver.mid,\n capture: track\n }],\n error: Notification.exception\n });\n\n return;\n }\n tracks.push({\n type: 'audio',\n capture: track,\n recv: false\n });\n });\n if (!tracks.length) {\n return videoStream;\n }\n this.videoroom.createOffer({\n tracks: tracks,\n success: (jsep) => {\n const publish = {\n request: \"configure\",\n video: true,\n audio: true\n };\n this.videoroom.send({\n message: publish,\n jsep: jsep\n });\n },\n error: function(error) {\n Notification.alert(\"WebRTC error... \", error.message);\n }\n });\n }\n\n return videoStream;\n }).catch(Notification.exception);\n }\n}\n\nexport default class VideoTime extends VideoTimeBase {\n initialize(contextid, token, peerid) {\n Log.debug(\"Initializing Video Time \" + this.elementId);\n\n this.contextid = contextid;\n this.peerid = peerid;\n\n Ajax.call([{\n methodname: 'videotimeplugin_live_get_room',\n args: {contextid: contextid},\n done: (response) => {\n const socket = new Socket(contextid, token);\n\n this.iceservers = JSON.parse(response.iceservers);\n this.roomid = response.roomid;\n this.server = response.server;\n\n rooms[String(contextid)] = {\n contextid: contextid,\n peerid: peerid,\n roomid: response.roomid,\n server: response.server,\n iceServers: JSON.parse(response.iceservers)\n };\n this.roomid = response.roomid;\n\n document.querySelector('[data-contextid=\"' + this.contextid + '\"] .videotime-control').classList.remove('hidden');\n\n socket.subscribe(() => {\n Ajax.call([{\n methodname: 'videotimeplugin_live_get_feed',\n args: {contextid: contextid},\n done: (response) => {\n const room = rooms[String(contextid)];\n if (room.publish && room.publish.restart) {\n if (response.feed == peerid) {\n this.unpublish();\n }\n room.publish = null;\n }\n this.subscribeTo(Number(response.feed));\n },\n fail: Notification.exception\n }]);\n });\n },\n fail: Notification.exception\n }]);\n\n this.addListeners();\n\n return true;\n }\n\n /**\n * Register player events to respond to user interaction and play progress.\n */\n addListeners() {\n document.querySelector('body').removeEventListener('click', handleClick);\n document.querySelector('body').addEventListener('click', handleClick);\n return;\n }\n\n /**\n * Subscribe to feed\n *\n * @param {int} source Feed to subscribe\n */\n subscribeTo(source) {\n Log.debug(source);\n const room = rooms[String(this.contextid)];\n document.querySelectorAll('[data-contextid=\"' + this.contextid + '\"][data-action=\"publish\"]').forEach(button => {\n if (source == Number(this.peerid)) {\n button.classList.remove('hidden');\n } else {\n button.classList.remove('hidden');\n }\n });\n document.querySelectorAll('[data-contextid=\"' + this.contextid + '\"][data-action=\"unpublish\"]').forEach(button => {\n if (source == Number(this.peerid)) {\n button.classList.remove('hidden');\n } else {\n button.classList.remove('hidden');\n }\n });\n Log.debug(source);\n\n if (this.remoteFeed && !this.remoteFeed.creatingSubscription && !this.remoteFeed.restart) {\n const update = {\n request: 'update',\n subscribe: [{\n feed: Number(source)\n }],\n unsubscribe: [{\n feed: Number(this.remoteFeed.current)\n }]\n };\n\n if (!source && this.remoteFeed.current) {\n delete update.subscribe;\n } else if (source && !this.remoteFeed.current) {\n delete update.unsubscribe;\n }\n\n if (this.remoteFeed.current != source) {\n this.remoteFeed.muteAudio = room.publish && (room.publish.feed === source);\n this.remoteFeed.videoroom.send({message: update});\n if (this.remoteFeed.audioTrack) {\n this.remoteFeed.audioTrack.enabled = !this.remoteFeed.muteAudio;\n }\n\n if (room.publish && this.remoteFeed.current == room.publish.feed) {\n room.publish.handleClose();\n room.publish = null;\n }\n this.remoteFeed.current = source;\n if (!source && this.remoteFeed) {\n this.remoteFeed.handleClose();\n this.remoteFeed = null;\n }\n Log.debug('[data-contextid=\"' + this.contextid + '\"] img.poster-img');\n if (Number(source)) {\n document.querySelectorAll('[data-contextid=\"' + this.contextid + '\"] img.poster-img').forEach(img => {\n img.classList.add('hidden');\n });\n document.querySelectorAll('[data-contextid=\"' + this.contextid + '\"] video').forEach(video => {\n video.classList.remove('hidden');\n });\n } else {\n document.querySelectorAll('[data-contextid=\"' + this.contextid + '\"] img.poster-img').forEach(img => {\n img.classList.remove('hidden');\n });\n document.querySelectorAll('[data-contextid=\"' + this.contextid + '\"] video').forEach(video => {\n video.classList.add('hidden');\n });\n }\n }\n } else if (this.remoteFeed && this.remoteFeed.restart) {\n if (this.remoteFeed.current != source) {\n this.remoteFeed = null;\n this.subscribeTo(source);\n }\n } else if (this.remoteFeed) {\n setTimeout(() => {\n this.subscribeTo(source);\n }, 500);\n } else if (source) {\n this.remoteFeed = new Subscribe(this.contextid, this.iceservers, this.roomid, this.server, this.peerid, source);\n this.remoteFeed.remoteVideo = document.getElementById(this.elementId);\n this.remoteFeed.remoteAudio = document.getElementById(this.elementId).parentNode.querySelector('audio');\n this.remoteFeed.muteAudio = room.publish && (room.publish.feed === source);\n this.remoteFeed.startConnection(source);\n document.querySelectorAll('[data-contextid=\"' + this.contextid + '\"] img.poster-img').forEach(img => {\n img.classList.add('hidden');\n });\n document.querySelectorAll('[data-contextid=\"' + this.contextid + '\"] video').forEach(img => {\n img.classList.remove('hidden');\n });\n }\n }\n}\n\nconst handleClick = function(e) {\n const button = e.target.closest(\n '[data-roomid] [data-action=\"publish\"], [data-roomid] [data-action=\"unpublish\"],'\n + '[data-roomid] [data-action=\"mute\"], [data-roomid] [data-action=\"unmute\"]'\n );\n if (button) {\n const action = button.getAttribute('data-action'),\n contextid = e.target.closest('[data-contextid]').getAttribute('data-contextid'),\n room = rooms[String(contextid)],\n iceServers = room.iceServers,\n peerid = room.peerid,\n roomid = room.roomid,\n server = room.server,\n type = button.getAttribute('data-type');\n e.stopPropagation();\n e.preventDefault();\n if ((action == 'publish') && (!room.publish || room.publish.restart)) {\n room.publish = new Publish(contextid, iceServers, roomid, server, peerid);\n if (type == 'display') {\n room.publish.shareDisplay();\n } else {\n room.publish.shareCamera();\n }\n room.publish.startConnection();\n } else {\n if ((action == 'mute') || (action == 'unmute')) {\n button.classList.add('hidden');\n button.parentNode.querySelectorAll('[data-action=\"mute\"], [data-action=\"unmute\"]').forEach(button => {\n if (button.getAttribute('data-action') != action) {\n button.classList.remove('hidden');\n }\n });\n }\n if (room.publish) {\n room.publish.handleClick(e);\n }\n }\n }\n};\n\nclass Subscribe extends SubscribeBase {\n /**\n * Register the room\n *\n * @param {object} pluginHandle\n * @return {Promise}\n */\n register(pluginHandle) {\n // Try a registration\n return Ajax.call([{\n args: {\n handle: pluginHandle.getId(),\n id: Number(this.contextid),\n plugin: pluginHandle.plugin,\n room: this.roomid,\n ptype: false,\n feed: this.feed,\n session: pluginHandle.session.getSessionId()\n },\n contextid: this.contextid,\n fail: Notification.exception,\n methodname: 'videotimeplugin_live_join_room'\n }])[0];\n }\n\n attachAudio(audioStream) {\n Janus.attachMediaStream(\n this.remoteVideo.parentNode.querySelector('audio'),\n audioStream\n );\n audioStream.getTracks().forEach(track => {\n this.audioTrack = track;\n track.enabled = !this.muteAudio;\n });\n }\n\n attachVideo(videoStream) {\n Janus.attachMediaStream(\n this.remoteVideo,\n videoStream\n );\n }\n}\n"],"names":["rooms","Publish","PublishBase","register","pluginHandle","Ajax","call","args","handle","getId","id","Number","this","contextid","plugin","room","roomid","ptype","session","getSessionId","fail","Notification","exception","methodname","then","response","feed","catch","publishFeed","unpublish","publish","handleClose","document","querySelectorAll","forEach","button","classList","remove","janus","destroy","currentCamera","Promise","resolve","currentDisplay","videoInput","videoStream","getVideoTracks","track","enabled","stop","onLocalTrack","on","remoteStream","MediaStream","kind","mid","debug","attachMediaStream","getElementById","tracks","handleClick","e","target","closest","action","getAttribute","type","transceiver","getTransceiver","stopPropagation","preventDefault","sender","shareDisplay","shareCamera","processStream","cameraInput","navigator","mediaDevices","getUserMedia","video","audio","getTracks","displayInput","getDisplayMedia","audiotransceiver","videotransceiver","addEventListener","selectedTrack","videoroom","replaceTracks","capture","error","push","recv","getAudioTracks","querySelector","length","createOffer","success","jsep","send","message","request","alert","VideoTime","VideoTimeBase","initialize","token","peerid","elementId","done","socket","Socket","iceservers","JSON","parse","server","String","iceServers","subscribe","restart","subscribeTo","addListeners","removeEventListener","source","remoteFeed","creatingSubscription","current","setTimeout","Subscribe","remoteVideo","remoteAudio","parentNode","muteAudio","startConnection","img","add","update","unsubscribe","audioTrack","SubscribeBase","attachAudio","audioStream","attachVideo"],"mappings":"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"} \ 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_live\n * @module videotimeplugin_live/videotime\n * @copyright 2022 bdecent gmbh \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Ajax from \"core/ajax\";\nimport VideoTimeBase from \"mod_videotime/videotime\";\nimport Janus from 'block_deft/janus-gateway';\nimport Log from \"core/log\";\nimport Notification from \"core/notification\";\nimport PublishBase from \"block_deft/publish\";\nimport SubscribeBase from \"block_deft/subscribe\";\nimport Socket from \"videotimeplugin_live/socket\";\n\nvar rooms = {};\n\nclass Publish extends PublishBase {\n /**\n * Register the room\n *\n * @param {object} pluginHandle\n * @return {Promise}\n */\n register(pluginHandle) {\n // Try a registration\n return Ajax.call([{\n args: {\n handle: pluginHandle.getId(),\n id: Number(this.contextid),\n plugin: pluginHandle.plugin,\n room: this.roomid,\n ptype: this.ptype == 'publish',\n session: pluginHandle.session.getSessionId()\n },\n contextid: this.contextid,\n fail: Notification.exception,\n methodname: 'videotimeplugin_live_join_room'\n }])[0].then(response => {\n this.feed = response.id;\n }).catch(Notification.exception);\n }\n\n /**\n * Publish current video feed\n */\n publishFeed() {\n return Ajax.call([{\n args: {\n id: Number(this.feed),\n room: this.roomid,\n },\n contextid: this.contextid,\n fail: Notification.exception,\n methodname: 'videotimeplugin_live_publish_feed'\n }])[0];\n }\n\n\n /**\n * Stop video feed\n */\n unpublish() {\n document.querySelectorAll('#video-controls-camera, #video-controls-display').forEach(video => {\n video.srcObject = null;\n video.parentNode.classList.add('hidden');\n });\n return Ajax.call([{\n args: {\n id: Number(this.feed),\n publish: false,\n room: this.roomid\n },\n contextid: this.contextid,\n fail: Notification.exception,\n methodname: 'videotimeplugin_live_publish_feed'\n }])[0];\n }\n\n handleClose() {\n document.querySelectorAll(\n '[data-contextid=\"' + this.contextid + '\"][data-action=\"publish\"]'\n ).forEach(button => {\n button.classList.remove('hidden');\n });\n\n this.janus.destroy();\n\n [\n this.currentCamera || Promise.resolve(null),\n this.currentDisplay || Promise.resolve(null),\n ].forEach(videoInput => {\n videoInput.then(videoStream => {\n if (videoStream) {\n videoStream.getTracks().forEach(track => {\n track.enabled = false;\n track.stop();\n });\n }\n\n return null;\n }).catch(Notification.exception);\n });\n }\n\n onLocalTrack(track, on) {\n const remoteStream = new MediaStream([track]);\n if (!on || (track.kind == 'audio')) {\n return;\n }\n remoteStream.mid = track.mid;\n Log.debug(on);\n Log.debug(remoteStream);\n Janus.attachMediaStream(\n document.getElementById('video-controls-' + this.tracks[track.id]),\n remoteStream\n );\n }\n\n handleClick(e) {\n const button = e.target.closest(\n '[data-contextid=\"' + this.contextid + '\"][data-action=\"publish\"], [data-contextid=\"'\n + this.contextid + '\"][data-action=\"close\"], [data-contextid=\"'\n + this.contextid + '\"][data-action=\"mute\"], [data-contextid=\"'\n + this.contextid + '\"][data-action=\"unmute\"], [data-contextid=\"'\n + this.contextid + '\"][data-action=\"switch\"], [data-contextid=\"'\n + this.contextid + '\"][data-action=\"unpublish\"]'\n );\n if (button) {\n const action = button.getAttribute('data-action'),\n type = button.getAttribute('data-type') || 'camera';\n e.stopPropagation();\n e.preventDefault();\n document.querySelectorAll(\n '[data-region=\"deft-venue\"] [data-action=\"publish\"], [data-region=\"deft-venue\"] [data-action=\"unpublish\"]'\n ).forEach(button => {\n if ((button.getAttribute('data-action') != action) || (button.getAttribute('data-type') != type)) {\n button.classList.remove('hidden');\n }\n });\n switch (action) {\n case 'close':\n document.getElementById('video-controls-' + type).srcObject = null;\n document.getElementById('video-controls-' + type).parentNode.classList.add('hidden');\n if (this.tracks[this.selectedTrack.id] == type) {\n this.unpublish();\n }\n break;\n case 'mute':\n case 'unmute':\n ((type == 'display') ? this.currentDisplay : this.currentCamera)\n .then(videoStream => {\n if (videoStream) {\n videoStream.getAudioTracks().forEach(track => {\n track.enabled = (action == 'unmute');\n });\n }\n return videoStream;\n }).catch(Notification.exception);\n break;\n case 'publish':\n Log.debug(type);\n if (type == 'display') {\n this.shareDisplay();\n } else {\n this.shareCamera();\n }\n document.querySelectorAll('#video-controls-camera, #video-controls-display').forEach(video => {\n video.parentNode.classList.remove('selected');\n });\n document\n .getElementById('video-controls-' + type)\n .parentNode\n .classList\n .remove('hidden');\n document\n .getElementById('video-controls-' + type)\n .parentNode\n .classList\n .add('selected');\n\n this.processStream([]);\n break;\n case 'switch':\n document.querySelectorAll('#video-controls-camera, #video-controls-display').forEach(video => {\n video.parentNode.classList.remove('selected');\n });\n if (type == 'display') {\n this.videoInput = this.currentDisplay;\n } else {\n this.videoInput = this.currentCamera;\n }\n document\n .getElementById('video-controls-' + type)\n .parentNode\n .classList\n .remove('hidden');\n document\n .getElementById('video-controls-' + type)\n .parentNode\n .classList\n .add('selected');\n this.processStream([]);\n break;\n case 'unpublish':\n this.unpublish();\n }\n }\n\n return true;\n }\n\n /**\n * Set video source to user camera\n */\n shareCamera() {\n const videoInput = this.videoInput,\n currentCamera = this.currentCamera || Promise.resolve(null);\n\n this.videoInput = currentCamera.then(videoStream => {\n if (videoStream) {\n return videoStream;\n } else {\n const cameraInput = navigator.mediaDevices.getUserMedia({\n video: true,\n audio: true\n });\n\n this.currentCamera = cameraInput.catch(() => {\n return currentCamera;\n });\n\n return cameraInput.then(videoStream => {\n this.tracks = this.tracks || {};\n videoStream.getTracks().forEach(track => {\n this.tracks[track.id] = 'camera';\n });\n\n return videoStream;\n }).catch((e) => {\n Log.debug(e);\n\n return videoInput;\n });\n }\n });\n }\n\n /**\n * Set video source to display surface\n */\n shareDisplay() {\n const videoInput = this.videoInput,\n currentDisplay = this.currentDisplay || Promise.resolve(null),\n displayInput = navigator.mediaDevices.getDisplayMedia({\n video: true,\n audio: true\n });\n\n this.videoInput = displayInput.then(videoStream => {\n if (videoInput) {\n videoInput.then(videoStream => {\n if (videoStream) {\n videoStream.getTracks().forEach(track => {\n Log.debug(track); //track.stop();\n });\n }\n return videoStream;\n }).catch(Notification.exception);\n }\n this.tracks = this.tracks || {};\n videoStream.getTracks().forEach(track => {\n this.tracks[track.id] = 'display';\n });\n\n return videoStream;\n }).catch((e) => {\n Log.debug(e);\n\n return videoInput;\n });\n\n this.currentDisplay = displayInput.then(videoStream => {\n currentDisplay.then(videoStream => {\n if (videoStream) {\n videoStream.getTracks().forEach(track => {\n Log.debug('stop track');\n Log.debug(track);\n track.stop();\n });\n }\n });\n\n return videoStream;\n }).catch((e) => {\n Log.debug(e);\n\n return currentDisplay;\n });\n }\n\n processStream(tracks) {\n this.videoInput.then(videoStream => {\n this.tracks = this.tracks || {};\n if (videoStream) {\n const audiotransceiver = this.getTransceiver('audio'),\n videotransceiver = this.getTransceiver('video');\n videoStream.getVideoTracks().forEach(track => {\n track.addEventListener('ended', () => {\n if (this.selectedTrack.id == track.id) {\n this.unpublish();\n } else {\n document\n .getElementById('video-controls-' + this.tracks[track.id])\n .parentNode\n .classList\n .add('hidden');\n }\n });\n this.selectedTrack = track;\n if (videotransceiver) {\n this.videoroom.replaceTracks({\n tracks: [{\n type: 'video',\n mid: videotransceiver.mid,\n capture: track\n }],\n error: Notification.exception\n });\n\n return;\n }\n tracks.push({\n type: 'video',\n capture: track,\n recv: false\n });\n });\n videoStream.getAudioTracks().forEach(track => {\n if (\n document.querySelector('.hidden[data-action=\"mute\"][data-contextid=\"' + this.contextid + '\"][data-type=\"'\n + this.tracks[this.selectedTrack.id] + '\"]'\n )) {\n track.enabled = false;\n }\n\n if (audiotransceiver) {\n this.videoroom.replaceTracks({\n tracks: [{\n type: 'audio',\n mid: audiotransceiver.mid,\n capture: track\n }],\n error: Notification.exception\n });\n\n return;\n }\n tracks.push({\n type: 'audio',\n capture: track,\n recv: false\n });\n });\n if (!tracks.length) {\n return videoStream;\n }\n this.videoroom.createOffer({\n tracks: tracks,\n success: (jsep) => {\n const publish = {\n request: \"configure\",\n video: true,\n audio: true\n };\n this.videoroom.send({\n message: publish,\n jsep: jsep\n });\n },\n error: function(error) {\n Notification.alert(\"WebRTC error... \", error.message);\n }\n });\n }\n\n return videoStream;\n }).catch(Notification.exception);\n }\n}\n\nexport default class VideoTime extends VideoTimeBase {\n initialize(contextid, token, peerid) {\n Log.debug(\"Initializing Video Time \" + this.elementId);\n\n this.contextid = contextid;\n this.peerid = peerid;\n\n Ajax.call([{\n methodname: 'videotimeplugin_live_get_room',\n args: {contextid: contextid},\n done: (response) => {\n const socket = new Socket(contextid, token);\n\n this.iceservers = JSON.parse(response.iceservers);\n this.roomid = response.roomid;\n this.server = response.server;\n\n rooms[String(contextid)] = {\n contextid: contextid,\n peerid: peerid,\n roomid: response.roomid,\n server: response.server,\n iceServers: JSON.parse(response.iceservers)\n };\n this.roomid = response.roomid;\n\n document.querySelector('[data-contextid=\"' + this.contextid + '\"] .videotime-control').classList.remove('hidden');\n\n socket.subscribe(() => {\n Ajax.call([{\n methodname: 'videotimeplugin_live_get_feed',\n args: {contextid: contextid},\n done: (response) => {\n const room = rooms[String(contextid)];\n if (room.publish && room.publish.restart) {\n if (response.feed == peerid) {\n this.unpublish();\n }\n room.publish = null;\n }\n this.subscribeTo(Number(response.feed));\n },\n fail: Notification.exception\n }]);\n });\n },\n fail: Notification.exception\n }]);\n\n this.addListeners();\n\n return true;\n }\n\n /**\n * Register player events to respond to user interaction and play progress.\n */\n addListeners() {\n document.querySelector('body').removeEventListener('click', handleClick);\n document.querySelector('body').addEventListener('click', handleClick);\n return;\n }\n\n /**\n * Subscribe to feed\n *\n * @param {int} source Feed to subscribe\n */\n subscribeTo(source) {\n Log.debug(source);\n const room = rooms[String(this.contextid)];\n document.querySelectorAll('[data-contextid=\"' + this.contextid + '\"][data-action=\"publish\"]').forEach(button => {\n if (source == Number(this.peerid)) {\n button.classList.remove('hidden');\n } else {\n button.classList.remove('hidden');\n }\n });\n document.querySelectorAll('[data-contextid=\"' + this.contextid + '\"][data-action=\"unpublish\"]').forEach(button => {\n if (source == Number(this.peerid)) {\n button.classList.remove('hidden');\n } else {\n button.classList.remove('hidden');\n }\n });\n Log.debug(source);\n\n if (this.remoteFeed && !this.remoteFeed.creatingSubscription && !this.remoteFeed.restart) {\n const update = {\n request: 'update',\n subscribe: [{\n feed: Number(source)\n }],\n unsubscribe: [{\n feed: Number(this.remoteFeed.current)\n }]\n };\n\n if (!source && this.remoteFeed.current) {\n delete update.subscribe;\n } else if (source && !this.remoteFeed.current) {\n delete update.unsubscribe;\n }\n\n if (this.remoteFeed.current != source) {\n this.remoteFeed.muteAudio = room.publish && (room.publish.feed === source);\n this.remoteFeed.videoroom.send({message: update});\n if (this.remoteFeed.audioTrack) {\n this.remoteFeed.audioTrack.enabled = !this.remoteFeed.muteAudio;\n }\n\n if (room.publish && this.remoteFeed.current == room.publish.feed) {\n room.publish.handleClose();\n room.publish = null;\n }\n this.remoteFeed.current = source;\n if (!source && this.remoteFeed) {\n this.remoteFeed.handleClose();\n this.remoteFeed = null;\n }\n Log.debug('[data-contextid=\"' + this.contextid + '\"] img.poster-img');\n if (Number(source)) {\n document.querySelectorAll(\n '[data-contextid=\"' + this.contextid + '\"] .videotime-embed img.poster-img'\n ).forEach(img => {\n img.classList.add('hidden');\n });\n document.querySelectorAll(\n '[data-contextid=\"' + this.contextid + '\"] .videotime-embed video'\n ).forEach(video => {\n video.classList.remove('hidden');\n });\n } else {\n document.querySelectorAll(\n '[data-contextid=\"' + this.contextid + '\"] .videotime-embed img.poster-img'\n ).forEach(img => {\n img.classList.remove('hidden');\n });\n document.querySelectorAll(\n '[data-contextid=\"' + this.contextid + '\"] .videotime-embed video'\n ).forEach(video => {\n video.srcObject = null;\n video.classList.add('hidden');\n });\n }\n }\n } else if (this.remoteFeed && this.remoteFeed.restart) {\n if (this.remoteFeed.current != source) {\n this.remoteFeed = null;\n this.subscribeTo(source);\n }\n } else if (this.remoteFeed) {\n setTimeout(() => {\n this.subscribeTo(source);\n }, 500);\n } else if (source) {\n this.remoteFeed = new Subscribe(this.contextid, this.iceservers, this.roomid, this.server, this.peerid);\n this.remoteFeed.remoteVideo = document.getElementById(this.elementId);\n this.remoteFeed.remoteAudio = document.getElementById(this.elementId).parentNode.querySelector('audio');\n this.remoteFeed.muteAudio = room.publish && (room.publish.feed === source);\n this.remoteFeed.startConnection(source);\n document.querySelectorAll('[data-contextid=\"' + this.contextid + '\"] img.poster-img').forEach(img => {\n img.classList.add('hidden');\n });\n document.querySelectorAll('[data-contextid=\"' + this.contextid + '\"] video').forEach(img => {\n img.classList.remove('hidden');\n });\n }\n }\n}\n\nconst handleClick = function(e) {\n const button = e.target.closest(\n '[data-roomid] [data-action=\"publish\"], [data-roomid] [data-action=\"unpublish\"],'\n + '[data-roomid] [data-action=\"close\"], '\n + '[data-roomid] [data-action=\"switch\"], '\n + '[data-roomid] [data-action=\"mute\"], [data-roomid] [data-action=\"unmute\"]'\n );\n if (button) {\n const action = button.getAttribute('data-action'),\n contextid = e.target.closest('[data-contextid]').getAttribute('data-contextid'),\n room = rooms[String(contextid)],\n iceServers = room.iceServers,\n peerid = room.peerid,\n roomid = room.roomid,\n server = room.server,\n type = button.getAttribute('data-type');\n e.stopPropagation();\n e.preventDefault();\n if ((action == 'publish') && (!room.publish || room.publish.restart)) {\n room.publish = new Publish(contextid, iceServers, roomid, server, peerid);\n if (type == 'display') {\n room.publish.shareDisplay();\n } else {\n room.publish.shareCamera();\n }\n room.publish.startConnection();\n document\n .getElementById('video-controls-' + (type || 'camera'))\n .parentNode\n .classList\n .remove('hidden');\n document\n .getElementById('video-controls-' + (type || 'camera'))\n .parentNode\n .classList\n .add('selected');\n } else {\n if ((action == 'mute') || (action == 'unmute')) {\n button.classList.add('hidden');\n button.parentNode.querySelectorAll('[data-action=\"mute\"], [data-action=\"unmute\"]').forEach(button => {\n if (button.getAttribute('data-action') != action) {\n button.classList.remove('hidden');\n }\n });\n }\n if (room.publish) {\n room.publish.handleClick(e);\n }\n }\n }\n};\n\nclass Subscribe extends SubscribeBase {\n /**\n * Register the room\n *\n * @param {object} pluginHandle\n * @return {Promise}\n */\n register(pluginHandle) {\n // Try a registration\n return Ajax.call([{\n args: {\n handle: pluginHandle.getId(),\n id: Number(this.contextid),\n plugin: pluginHandle.plugin,\n room: this.roomid,\n ptype: false,\n feed: this.feed,\n session: pluginHandle.session.getSessionId()\n },\n contextid: this.contextid,\n fail: Notification.exception,\n methodname: 'videotimeplugin_live_join_room'\n }])[0];\n }\n\n attachAudio(audioStream) {\n Janus.attachMediaStream(\n this.remoteVideo.parentNode.querySelector('audio'),\n audioStream\n );\n audioStream.getTracks().forEach(track => {\n this.audioTrack = track;\n track.enabled = !this.muteAudio;\n });\n }\n\n attachVideo(videoStream) {\n Janus.attachMediaStream(\n this.remoteVideo,\n videoStream\n );\n }\n}\n"],"names":["rooms","Publish","PublishBase","register","pluginHandle","Ajax","call","args","handle","getId","id","Number","this","contextid","plugin","room","roomid","ptype","session","getSessionId","fail","Notification","exception","methodname","then","response","feed","catch","publishFeed","unpublish","document","querySelectorAll","forEach","video","srcObject","parentNode","classList","add","publish","handleClose","button","remove","janus","destroy","currentCamera","Promise","resolve","currentDisplay","videoInput","videoStream","getTracks","track","enabled","stop","onLocalTrack","on","remoteStream","MediaStream","kind","mid","debug","attachMediaStream","getElementById","tracks","handleClick","e","target","closest","action","getAttribute","type","stopPropagation","preventDefault","selectedTrack","getAudioTracks","shareDisplay","shareCamera","processStream","cameraInput","navigator","mediaDevices","getUserMedia","audio","displayInput","getDisplayMedia","audiotransceiver","getTransceiver","videotransceiver","getVideoTracks","addEventListener","videoroom","replaceTracks","capture","error","push","recv","querySelector","length","createOffer","success","jsep","send","message","request","alert","VideoTime","VideoTimeBase","initialize","token","peerid","elementId","done","socket","Socket","iceservers","JSON","parse","server","String","iceServers","subscribe","restart","subscribeTo","addListeners","removeEventListener","source","remoteFeed","creatingSubscription","current","setTimeout","Subscribe","remoteVideo","remoteAudio","muteAudio","startConnection","img","update","unsubscribe","audioTrack","SubscribeBase","attachAudio","audioStream","attachVideo"],"mappings":"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"} \ No newline at end of file diff --git a/plugin/live/amd/src/videotime.js b/plugin/live/amd/src/videotime.js index dfde1da9..9a3f996c 100644 --- a/plugin/live/amd/src/videotime.js +++ b/plugin/live/amd/src/videotime.js @@ -64,6 +64,10 @@ class Publish extends PublishBase { * Stop video feed */ unpublish() { + document.querySelectorAll('#video-controls-camera, #video-controls-display').forEach(video => { + video.srcObject = null; + video.parentNode.classList.add('hidden'); + }); return Ajax.call([{ args: { id: Number(this.feed), @@ -91,7 +95,7 @@ class Publish extends PublishBase { ].forEach(videoInput => { videoInput.then(videoStream => { if (videoStream) { - videoStream.getVideoTracks().forEach(track => { + videoStream.getTracks().forEach(track => { track.enabled = false; track.stop(); }); @@ -119,14 +123,15 @@ class Publish extends PublishBase { handleClick(e) { const button = e.target.closest( '[data-contextid="' + this.contextid + '"][data-action="publish"], [data-contextid="' + + this.contextid + '"][data-action="close"], [data-contextid="' + this.contextid + '"][data-action="mute"], [data-contextid="' + this.contextid + '"][data-action="unmute"], [data-contextid="' + + this.contextid + '"][data-action="switch"], [data-contextid="' + this.contextid + '"][data-action="unpublish"]' ); if (button) { const action = button.getAttribute('data-action'), - type = button.getAttribute('data-type') || 'camera', - transceiver = this.getTransceiver('audio'); + type = button.getAttribute('data-type') || 'camera'; e.stopPropagation(); e.preventDefault(); document.querySelectorAll( @@ -137,15 +142,24 @@ class Publish extends PublishBase { } }); switch (action) { - case 'mute': - if (transceiver) { - transceiver.sender.track.enabled = false; + case 'close': + document.getElementById('video-controls-' + type).srcObject = null; + document.getElementById('video-controls-' + type).parentNode.classList.add('hidden'); + if (this.tracks[this.selectedTrack.id] == type) { + this.unpublish(); } break; + case 'mute': case 'unmute': - if (transceiver) { - transceiver.sender.track.enabled = true; - } + ((type == 'display') ? this.currentDisplay : this.currentCamera) + .then(videoStream => { + if (videoStream) { + videoStream.getAudioTracks().forEach(track => { + track.enabled = (action == 'unmute'); + }); + } + return videoStream; + }).catch(Notification.exception); break; case 'publish': Log.debug(type); @@ -154,9 +168,43 @@ class Publish extends PublishBase { } else { this.shareCamera(); } + document.querySelectorAll('#video-controls-camera, #video-controls-display').forEach(video => { + video.parentNode.classList.remove('selected'); + }); + document + .getElementById('video-controls-' + type) + .parentNode + .classList + .remove('hidden'); + document + .getElementById('video-controls-' + type) + .parentNode + .classList + .add('selected'); this.processStream([]); break; + case 'switch': + document.querySelectorAll('#video-controls-camera, #video-controls-display').forEach(video => { + video.parentNode.classList.remove('selected'); + }); + if (type == 'display') { + this.videoInput = this.currentDisplay; + } else { + this.videoInput = this.currentCamera; + } + document + .getElementById('video-controls-' + type) + .parentNode + .classList + .remove('hidden'); + document + .getElementById('video-controls-' + type) + .parentNode + .classList + .add('selected'); + this.processStream([]); + break; case 'unpublish': this.unpublish(); } @@ -262,8 +310,14 @@ class Publish extends PublishBase { videotransceiver = this.getTransceiver('video'); videoStream.getVideoTracks().forEach(track => { track.addEventListener('ended', () => { - if (this.selectedTrack != track.id) { + if (this.selectedTrack.id == track.id) { this.unpublish(); + } else { + document + .getElementById('video-controls-' + this.tracks[track.id]) + .parentNode + .classList + .add('hidden'); } }); this.selectedTrack = track; @@ -286,12 +340,10 @@ class Publish extends PublishBase { }); }); videoStream.getAudioTracks().forEach(track => { - track.addEventListener('ended', () => { - if (this.selectedTrack != track.id) { - this.unpublish(); - } - }); - if (document.querySelector('.hidden[data-action="mute"][data-contextid="' + this.contextid + '"]')) { + if ( + document.querySelector('.hidden[data-action="mute"][data-contextid="' + this.contextid + '"][data-type="' + + this.tracks[this.selectedTrack.id] + '"]' + )) { track.enabled = false; } @@ -462,17 +514,26 @@ export default class VideoTime extends VideoTimeBase { } Log.debug('[data-contextid="' + this.contextid + '"] img.poster-img'); if (Number(source)) { - document.querySelectorAll('[data-contextid="' + this.contextid + '"] img.poster-img').forEach(img => { + document.querySelectorAll( + '[data-contextid="' + this.contextid + '"] .videotime-embed img.poster-img' + ).forEach(img => { img.classList.add('hidden'); }); - document.querySelectorAll('[data-contextid="' + this.contextid + '"] video').forEach(video => { + document.querySelectorAll( + '[data-contextid="' + this.contextid + '"] .videotime-embed video' + ).forEach(video => { video.classList.remove('hidden'); }); } else { - document.querySelectorAll('[data-contextid="' + this.contextid + '"] img.poster-img').forEach(img => { + document.querySelectorAll( + '[data-contextid="' + this.contextid + '"] .videotime-embed img.poster-img' + ).forEach(img => { img.classList.remove('hidden'); }); - document.querySelectorAll('[data-contextid="' + this.contextid + '"] video').forEach(video => { + document.querySelectorAll( + '[data-contextid="' + this.contextid + '"] .videotime-embed video' + ).forEach(video => { + video.srcObject = null; video.classList.add('hidden'); }); } @@ -487,7 +548,7 @@ export default class VideoTime extends VideoTimeBase { this.subscribeTo(source); }, 500); } else if (source) { - this.remoteFeed = new Subscribe(this.contextid, this.iceservers, this.roomid, this.server, this.peerid, source); + this.remoteFeed = new Subscribe(this.contextid, this.iceservers, this.roomid, this.server, this.peerid); this.remoteFeed.remoteVideo = document.getElementById(this.elementId); this.remoteFeed.remoteAudio = document.getElementById(this.elementId).parentNode.querySelector('audio'); this.remoteFeed.muteAudio = room.publish && (room.publish.feed === source); @@ -505,6 +566,8 @@ export default class VideoTime extends VideoTimeBase { const handleClick = function(e) { const button = e.target.closest( '[data-roomid] [data-action="publish"], [data-roomid] [data-action="unpublish"],' + + '[data-roomid] [data-action="close"], ' + + '[data-roomid] [data-action="switch"], ' + '[data-roomid] [data-action="mute"], [data-roomid] [data-action="unmute"]' ); if (button) { @@ -526,6 +589,16 @@ const handleClick = function(e) { room.publish.shareCamera(); } room.publish.startConnection(); + document + .getElementById('video-controls-' + (type || 'camera')) + .parentNode + .classList + .remove('hidden'); + document + .getElementById('video-controls-' + (type || 'camera')) + .parentNode + .classList + .add('selected'); } else { if ((action == 'mute') || (action == 'unmute')) { button.classList.add('hidden'); diff --git a/plugin/live/classes/external/publish_feed.php b/plugin/live/classes/external/publish_feed.php index 4f8fb94f..5c72b7d9 100644 --- a/plugin/live/classes/external/publish_feed.php +++ b/plugin/live/classes/external/publish_feed.php @@ -16,7 +16,6 @@ namespace videotimeplugin_live\external; -use block_deft\janus; use videotimeplugin_live\socket; use context; use context_module; @@ -84,11 +83,6 @@ public static function execute($id, $publish, $room): array { require_capability('videotimeplugin/live:sharevideo', $context); } - $janus = new janus($session); - $janusroom = new janus_room($record->itemid); - - $token = $janusroom->get_token(); - $data = json_decode($record->data) ?? new stdClass(); if (!$publish && !empty($data->feed) && $data->feed == $id) { $data->feed = 0; diff --git a/plugin/live/classes/janus_room.php b/plugin/live/classes/janus_room.php index f3d469e7..7b338dcd 100644 --- a/plugin/live/classes/janus_room.php +++ b/plugin/live/classes/janus_room.php @@ -86,11 +86,24 @@ public function __construct (int $id) { $this->roomid = $record->roomid ?? 0; $this->secret = $record->secret ?? ''; $this->server = $record->server ?? ''; - $this->session = new janus(); - $this->audiobridge = $this->session->attach('janus.plugin.audiobridge'); - $this->textroom = $this->session->attach('janus.plugin.textroom'); - $this->videoroom = $this->session->attach('janus.plugin.videoroom'); $this->init_room(); } + + /** + * Check room availabity and create if necessary + */ + protected function init_room() { + $exists = [ + 'request' => 'exists', + 'room' => $this->roomid, + ]; + + $response = $this->videoroom_send($exists); + if (!$response->plugindata->data->exists) { + return $this->create_room(); + } + + $this->set_token(); + } } diff --git a/plugin/live/lib.php b/plugin/live/lib.php index f3b3e8c2..2c03be97 100644 --- a/plugin/live/lib.php +++ b/plugin/live/lib.php @@ -260,6 +260,10 @@ function videotimeplugin_live_setup_page($instance, $cm) { $bc->content = $OUTPUT->render_from_template('videotimeplugin_live/controls', [ 'contextid' => $context->id, 'instance' => $instance, + 'types' => [ + ['type' => 'camera'], + ['type' => 'display'], + ], ]); $defaultregion = $PAGE->blocks->get_default_region(); diff --git a/plugin/live/styles.css b/plugin/live/styles.css index 552670d7..7eb8c69a 100644 --- a/plugin/live/styles.css +++ b/plugin/live/styles.css @@ -1,9 +1,8 @@ -.videotime-embed .vjs-layout-tiny, -.videotime-embed .vjs-layout-x-small, -.videotime-embed .vjs-layout-large, -.videotime-embed .vjs-layout-small, -.videotime-embed .vjs-layout-medium, -.videotime-embed .vjs-layout-x-large, -.videotime-embed .vjs-layout-huge { - width: 100%; +.videotime-local-stream .fa-toggle-on, +.videotime-local-stream .selected .fa-toggle-off { + display: none; +} + +.videotime-local-stream .selected .fa-toggle-on { + display: inline; } diff --git a/plugin/live/templates/controls.mustache b/plugin/live/templates/controls.mustache index 8987ab33..68d0842c 100644 --- a/plugin/live/templates/controls.mustache +++ b/plugin/live/templates/controls.mustache @@ -36,29 +36,28 @@ }} {{# instance }} -
-
- {{# posterurl }} - - {{/ posterurl }} - -
-
- {{# posterurl }} - - {{/ posterurl }} +
+ {{# types }} + + {{/ types }}