From 50c49e18ad296358d614952f993792f93856ca47 Mon Sep 17 00:00:00 2001 From: rafaellehmkuhl Date: Sat, 24 Feb 2024 13:41:57 +0000 Subject: [PATCH] =?UTF-8?q?Deploying=20to=20gh-pages=20from=20@=20bluerobo?= =?UTF-8?q?tics/cockpit@1300b7cc9c1a8c51e384223cfe42b923c1622f22=20?= =?UTF-8?q?=F0=9F=9A=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- index.05ebf6d9.js => index.3a41ef80.js | 2 +- index.html | 2 +- sw.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) rename index.05ebf6d9.js => index.3a41ef80.js (99%) diff --git a/index.05ebf6d9.js b/index.3a41ef80.js similarity index 99% rename from index.05ebf6d9.js rename to index.3a41ef80.js index 2a2180358..e65f10510 100644 --- a/index.05ebf6d9.js +++ b/index.3a41ef80.js @@ -913,7 +913,7 @@ t=0 0\r `},t.getDirection=function(i,a){const r=t.splitLines(i);for(let n=0;n(i.candidate&&Object.defineProperty(i,"candidate",{value:new e.RTCIceCandidate(i.candidate),writable:"false"}),i))}function Sf(e){!e.RTCIceCandidate||e.RTCIceCandidate&&"relayProtocol"in e.RTCIceCandidate.prototype||Gn(e,"icecandidate",t=>{if(t.candidate){const i=j6.parseCandidate(t.candidate.candidate);i.type==="relay"&&(t.candidate.relayProtocol={0:"tls",1:"tcp",2:"udp"}[i.priority>>24])}return t})}function Cc(e,t){if(!e.RTCPeerConnection)return;"sctp"in e.RTCPeerConnection.prototype||Object.defineProperty(e.RTCPeerConnection.prototype,"sctp",{get(){return typeof this._sctp>"u"?null:this._sctp}});const i=function(s){if(!s||!s.sdp)return!1;const l=j6.splitSections(s.sdp);return l.shift(),l.some(d=>{const C=j6.parseMLine(d);return C&&C.kind==="application"&&C.protocol.indexOf("SCTP")!==-1})},a=function(s){const l=s.sdp.match(/mozilla...THIS_IS_SDPARTA-(\d+)/);if(l===null||l.length<2)return-1;const d=parseInt(l[1],10);return d!==d?-1:d},r=function(s){let l=65536;return t.browser==="firefox"&&(t.version<57?s===-1?l=16384:l=2147483637:t.version<60?l=t.version===57?65535:65536:l=2147483637),l},n=function(s,l){let d=65536;t.browser==="firefox"&&t.version===57&&(d=65535);const C=j6.matchPrefix(s.sdp,"a=max-message-size:");return C.length>0?d=parseInt(C[0].substring(19),10):t.browser==="firefox"&&l!==-1&&(d=2147483637),d},o=e.RTCPeerConnection.prototype.setRemoteDescription;e.RTCPeerConnection.prototype.setRemoteDescription=function(){if(this._sctp=null,t.browser==="chrome"&&t.version>=76){const{sdpSemantics:l}=this.getConfiguration();l==="plan-b"&&Object.defineProperty(this,"sctp",{get(){return typeof this._sctp>"u"?null:this._sctp},enumerable:!0,configurable:!0})}if(i(arguments[0])){const l=a(arguments[0]),d=r(l),C=n(arguments[0],l);let m;d===0&&C===0?m=Number.POSITIVE_INFINITY:d===0||C===0?m=Math.max(d,C):m=Math.min(d,C);const u={};Object.defineProperty(u,"maxMessageSize",{get(){return m}}),this._sctp=u}return o.apply(this,arguments)}}function mc(e){if(!(e.RTCPeerConnection&&"createDataChannel"in e.RTCPeerConnection.prototype))return;function t(a,r){const n=a.send;a.send=function(){const s=arguments[0],l=s.length||s.size||s.byteLength;if(a.readyState==="open"&&r.sctp&&l>r.sctp.maxMessageSize)throw new TypeError("Message too large (can send a maximum of "+r.sctp.maxMessageSize+" bytes)");return n.apply(a,arguments)}}const i=e.RTCPeerConnection.prototype.createDataChannel;e.RTCPeerConnection.prototype.createDataChannel=function(){const r=i.apply(this,arguments);return t(r,this),r},Gn(e,"datachannel",a=>(t(a.channel,a.target),a))}function Df(e){if(!e.RTCPeerConnection||"connectionState"in e.RTCPeerConnection.prototype)return;const t=e.RTCPeerConnection.prototype;Object.defineProperty(t,"connectionState",{get(){return{completed:"connected",checking:"connecting"}[this.iceConnectionState]||this.iceConnectionState},enumerable:!0,configurable:!0}),Object.defineProperty(t,"onconnectionstatechange",{get(){return this._onconnectionstatechange||null},set(i){this._onconnectionstatechange&&(this.removeEventListener("connectionstatechange",this._onconnectionstatechange),delete this._onconnectionstatechange),i&&this.addEventListener("connectionstatechange",this._onconnectionstatechange=i)},enumerable:!0,configurable:!0}),["setLocalDescription","setRemoteDescription"].forEach(i=>{const a=t[i];t[i]=function(){return this._connectionstatechangepoly||(this._connectionstatechangepoly=r=>{const n=r.target;if(n._lastConnectionState!==n.connectionState){n._lastConnectionState=n.connectionState;const o=new Event("connectionstatechange",r);n.dispatchEvent(o)}return r},this.addEventListener("iceconnectionstatechange",this._connectionstatechangepoly)),a.apply(this,arguments)}})}function yf(e,t){if(!e.RTCPeerConnection||t.browser==="chrome"&&t.version>=71||t.browser==="safari"&&t.version>=605)return;const i=e.RTCPeerConnection.prototype.setRemoteDescription;e.RTCPeerConnection.prototype.setRemoteDescription=function(r){if(r&&r.sdp&&r.sdp.indexOf(` a=extmap-allow-mixed`)!==-1){const n=r.sdp.split(` `).filter(o=>o.trim()!=="a=extmap-allow-mixed").join(` -`);e.RTCSessionDescription&&r instanceof e.RTCSessionDescription?arguments[0]=new e.RTCSessionDescription({type:r.type,sdp:n}):r.sdp=n}return i.apply(this,arguments)}}function uc(e,t){if(!(e.RTCPeerConnection&&e.RTCPeerConnection.prototype))return;const i=e.RTCPeerConnection.prototype.addIceCandidate;!i||i.length===0||(e.RTCPeerConnection.prototype.addIceCandidate=function(){return arguments[0]?(t.browser==="chrome"&&t.version<78||t.browser==="firefox"&&t.version<68||t.browser==="safari")&&arguments[0]&&arguments[0].candidate===""?Promise.resolve():i.apply(this,arguments):(arguments[1]&&arguments[1].apply(null),Promise.resolve())})}function hc(e,t){if(!(e.RTCPeerConnection&&e.RTCPeerConnection.prototype))return;const i=e.RTCPeerConnection.prototype.setLocalDescription;!i||i.length===0||(e.RTCPeerConnection.prototype.setLocalDescription=function(){let r=arguments[0]||{};if(typeof r!="object"||r.type&&r.sdp)return i.apply(this,arguments);if(r={type:r.type,sdp:r.sdp},!r.type)switch(this.signalingState){case"stable":case"have-local-offer":case"have-remote-pranswer":r.type="offer";break;default:r.type="answer";break}return r.sdp||r.type!=="offer"&&r.type!=="answer"?i.apply(this,[r]):(r.type==="offer"?this.createOffer:this.createAnswer).apply(this).then(o=>i.apply(this,[o]))})}const ue2=Object.freeze(Object.defineProperty({__proto__:null,shimRTCIceCandidate:dc,shimRTCIceCandidateRelayProtocol:Sf,shimMaxMessageSize:Cc,shimSendThrowTypeError:mc,shimConnectionState:Df,removeExtmapAllowMixed:yf,shimAddIceCandidateNullOrEmpty:uc,shimParameterlessSetLocalDescription:hc},Symbol.toStringTag,{value:"Module"}));function he2({window:e}={},t={shimChrome:!0,shimFirefox:!0,shimSafari:!0}){const i=kt1,a=ce2(e),r={browserDetails:a,commonShim:ue2,extractVersion:cc,disableLog:se2,disableWarnings:le2,sdp:me2};switch(a.browser){case"chrome":if(!ED||!vf||!t.shimChrome)return i("Chrome shim is not included in this adapter release."),r;if(a.version===null)return i("Chrome shim can not determine version, not shimming."),r;i("adapter.js shimming chrome."),r.browserShim=ED,uc(e,a),hc(e),zt1(e,a),Zt1(e),vf(e,a),Gt1(e),qt1(e,a),Wt1(e),Xt1(e),Yt1(e),Kt1(e,a),dc(e),Sf(e),Df(e),Cc(e,a),mc(e),yf(e,a);break;case"firefox":if(!PD||!_f||!t.shimFirefox)return i("Firefox shim is not included in this adapter release."),r;i("adapter.js shimming firefox."),r.browserShim=PD,uc(e,a),hc(e),jt1(e,a),_f(e,a),Qt1(e),t01(e),Jt1(e),e01(e),i01(e),a01(e),r01(e),n01(e),o01(e),dc(e),Df(e),Cc(e,a),mc(e);break;case"safari":if(!ID||!t.shimSafari)return i("Safari shim is not included in this adapter release."),r;i("adapter.js shimming safari."),r.browserShim=ID,uc(e,a),hc(e),m01(e),h01(e),c01(e),s01(e),l01(e),u01(e),d01(e),p01(e),dc(e),Sf(e),Cc(e,a),mc(e),yf(e,a);break;default:i("Unsupported browser!");break}return r}const pe2=he2({window:typeof window>"u"?void 0:window});class fe2{constructor(t,i,a,r,n,o=[],s,l,d){S1(this,"id");S1(this,"consumerId");S1(this,"stream");S1(this,"status");S1(this,"ended");S1(this,"signaller");S1(this,"peerConnection");S1(this,"availableICEIPs");S1(this,"selectedICEIPs");S1(this,"rtcConfiguration");S1(this,"onTrackAdded");S1(this,"onNewIceRemoteAddress");S1(this,"onClose");this.id=t,this.consumerId=i,this.stream=a,this.onTrackAdded=s,this.onNewIceRemoteAddress=l,this.onClose=d,this.status="",this.signaller=r,this.rtcConfiguration=n,this.ended=!1,this.availableICEIPs=[],this.selectedICEIPs=o,this.peerConnection=this.createRTCPeerConnection(n),this.updateStatus("[WebRTC] [Session] Creating Session...")}hasEnded(){return this.ended}isConnected(){return this.peerConnection.connectionState=="connected"}updateStatus(t){this.status=t}createRTCPeerConnection(t){console.debug("[WebRTC] [Session] Creating RTCPeerConnection");const i=new RTCPeerConnection(t);return i.addTransceiver("video",{direction:"recvonly"}),this.ended=!1,i.addEventListener("negotiationneeded",this.onNegotiationNeeded.bind(this)),i.addEventListener("track",this.onTrackAddedCallback.bind(this)),i.addEventListener("icecandidate",this.onIceCandidate.bind(this)),i.addEventListener("icecandidateerror",this.onIceCandidateError.bind(this)),i.addEventListener("iceconnectionstatechange",this.onIceConnectionStateChange.bind(this)),i.addEventListener("connectionstatechange",this.onConnectionStateChange.bind(this)),i.addEventListener("signalingstatechange",this.onSignalingStateChange.bind(this)),i.addEventListener("icegatheringstatechange",this.onIceGatheringStateChange.bind(this)),i}onIncomingSDP(t){this.peerConnection.setRemoteDescription(t).then(()=>{console.debug(`[WebRTC] [Session] Remote description set to ${JSON.stringify(t,null,4)}`),this.onRemoteDescriptionSet()}).catch(i=>console.error(`[WebRTC] [Session] Failed setting remote description ${t}. Reason: ${i}`))}onRemoteDescriptionSet(){this.peerConnection.createAnswer().then(t=>{console.debug(`[WebRTC] [Session] SDP Answer created as: ${JSON.stringify(t,null,4)}`),this.onAnswerCreated(t)}).catch(t=>console.error(`[WebRTC] [Session] Failed creating description answer. Reason: ${t}`))}onAnswerCreated(t){this.peerConnection.setLocalDescription(t).then(()=>{console.debug(`[WebRTC] [Session] Local description set as${JSON.stringify(t,null,4)}`),this.onLocalDescriptionSet()}).catch(function(i){console.error(`[WebRTC] [Session] Failed setting local description. Reason: ${i}`)})}onLocalDescriptionSet(){this.peerConnection.localDescription!==null&&this.signaller.sendMediaNegotiation(this.id,this.consumerId,this.stream.id,this.peerConnection.localDescription)}onIncomingICE(t){const i=/\b(?:\d{1,3}\.){3}\d{1,3}\b/g,r=(n=>{const o=n.match(i);return o==null?void 0:o.find(s=>!s.includes(":"))})(t.candidate);if(r&&!this.availableICEIPs.includes(r)&&this.onNewIceRemoteAddress&&(this.availableICEIPs.push(r),this.onNewIceRemoteAddress(this.availableICEIPs)),t.candidate&&Array.isArray(this.selectedICEIPs)&&!this.selectedICEIPs.isEmpty()&&!this.selectedICEIPs.some(n=>t.candidate.includes(n))){console.debug(`[WebRTC] [Session] ICE candidate ignored: ${JSON.stringify(t,null,4)}`);return}this.peerConnection.addIceCandidate(t).then(()=>console.debug(`[WebRTC] [Session] ICE candidate added: ${JSON.stringify(t,null,4)}`)).catch(n=>console.error(`[WebRTC] [Session] Failed adding ICE candidate ${t}. Reason: ${n}`))}onIceCandidate(t){!t.candidate||this.signaller.sendIceNegotiation(this.id,this.consumerId,this.stream.id,t.candidate)}onIceCandidateError(t){console.debug(`[WebRTC] [Session] ICE Candidate "${t.url}" negotiation failed`)}onTrackAddedCallback(t){var i;(i=this.onTrackAdded)==null||i.call(this,t)}onNegotiationNeeded(t){console.debug("[WebRTC] [Session] Peer Connection is waiting for negotiation...")}onIceConnectionStateChange(){const t=`ICEConnection state changed to "${this.peerConnection.iceConnectionState}"`;console.debug("[WebRTC] [Session] "+t),this.peerConnection.iceConnectionState==="failed"&&this.peerConnection.restartIce()}onConnectionStateChange(){var i;const t=`RTCPeerConnection state changed to "${this.peerConnection.connectionState}"`;console.debug("[WebRTC] [Session] "+t),this.peerConnection.connectionState==="failed"&&((i=this.onClose)==null||i.call(this,this.id,"PeerConnection failed"),this.end())}onSignalingStateChange(){const t=`Signalling state changed to "${this.peerConnection.iceConnectionState}"`;console.debug("[WebRTC] [Session] "+t)}onIceGatheringStateChange(){this.peerConnection.iceGatheringState==="complete"&&console.debug(`[WebRTC] [Session] ICE gathering completed for session ${this.id}`)}end(){this.endPeerConnection(),this.onTrackAdded=void 0,this.onNewIceRemoteAddress=void 0,this.onClose=void 0,this.ended=!0,console.debug(`[WebRTC] [Session] Session ${this.id} ended.`)}endPeerConnection(){this.peerConnection.removeEventListener("negotiationneeded",this.onNegotiationNeeded.bind(this)),this.peerConnection.removeEventListener("track",this.onTrackAddedCallback.bind(this)),this.peerConnection.removeEventListener("icecandidate",this.onIceCandidate.bind(this)),this.peerConnection.removeEventListener("icecandidateerror",this.onIceCandidateError.bind(this)),this.peerConnection.removeEventListener("iceconnectionstatechange",this.onIceConnectionStateChange.bind(this)),this.peerConnection.removeEventListener("connectionstatechange",this.onConnectionStateChange.bind(this)),this.peerConnection.removeEventListener("signalingstatechange",this.onSignalingStateChange.bind(this)),this.peerConnection.removeEventListener("icegatheringstatechange",this.onIceGatheringStateChange.bind(this)),this.peerConnection.close()}}class Ve2{constructor(t,i,a,r){S1(this,"ws");S1(this,"onOpen");S1(this,"onStatusChange");S1(this,"url");S1(this,"listeners");S1(this,"shouldReconnect");var o;this.onOpen=a,this.onStatusChange=r,this.listeners=new Map,this.shouldReconnect=i,this.url=t;const n=`Connecting to signalling server on ${t}`;console.debug("[WebRTC] [Signaller] "+n),(o=this.onStatusChange)==null||o.call(this,n);try{this.ws=this.connect()}catch(s){console.error(`Could not establish initial connection. ${s}`)}}addEventListener(t,i,a){this.listeners.has(t)||this.listeners.set(t,new Map),this.listeners.get(t).set(i,a),this.ws.addEventListener(t,i,a)}removeEventListener(t,i,a){if(this.ws.removeEventListener(t,i,a),!this.listeners.has(t))return;const r=this.listeners.get(t);if(!r.has(i)){console.warn(`[WebRTC] [Signaller] Failed removing listener named ${i.name} of type "${t}". Reason: not found`);return}const n=r.get(i);a&&n&&a!==n||r.delete(i)}removeAllListeners(t,i){if(!(!this.listeners.size||!this.listeners.has(t))){for(const[a,r]of this.listeners.get(t))this.ws.removeEventListener(t,a,r);i&&this.listeners.delete(t)}}requestConsumerId(t,i){const a=this;this.addEventListener("message",function n(o){try{const s=JSON.parse(o.data);if(s.type!=="answer")return;const l=s.content;if(l.type!=="peerId")return;console.debug("[WebRTC] [Signaller] Message accepted from requestConsumerId:",s),a.removeEventListener("message",n);const d=l.content.id;i==null||i(`Consumer Id arrived: ${d}`),t(d)}catch(s){const l=`Failed receiving PeerId Answer Message. Error: ${s}. Data: ${o.data}`;console.error("[WebRTC] [Signaller] "+l),i==null||i(l)}});const r={type:"question",content:{type:"peerId"}};try{this.ws.send(JSON.stringify(r)),console.debug("[WebRTC] [Signaller] Message sent:",r),i==null||i("Consumer Id requested, waiting answer...")}catch(n){const o=`Failed requesting peer id. Reason: ${n}`;console.error("[WebRTC] [Signaller] "+o),i==null||i(o)}}isConnected(){return this.ws.readyState===this.ws.OPEN}requestStreams(t){const i={type:"question",content:{type:"availableStreams"}};try{if(this.ws.readyState!==this.ws.OPEN)return;this.ws.send(JSON.stringify(i)),console.debug("[WebRTC] [Signaller] Message sent:",i),t==null||t("StreamsAvailable requested")}catch(a){const r=`Failed requesting available streams. Reason: ${a}`;console.error("[WebRTC] [Signaller] "+r),t==null||t(r)}}requestSessionId(t,i,a,r){const n=this;n.addEventListener("message",function s(l){try{const d=JSON.parse(l.data);if(d.type!=="answer")return;const C=d.content;if(C.type!=="startSession")return;console.debug("[WebRTC] [Signaller] Message accepted from requestSessionId:",d);const m=C.content.session_id;if(m===void 0)return;n.removeEventListener("message",s),r==null||r(`Session Id arrived: ${m}`),a(m)}catch(d){const C=`Failed receiving StartSession Answer Message. Error: ${d}. Data: ${l.data}`;console.error("[WebRTC] [Signaller] "+C),r==null||r(C);return}});const o={type:"question",content:{type:"startSession",content:{consumer_id:t,producer_id:i}}};try{this.ws.send(JSON.stringify(o)),console.debug("[WebRTC] [Signaller] Message sent:",o),r==null||r("Session Id requested, waiting answer...")}catch(s){const l=`Failed requesting Session Id. Reason: ${s}`;console.error("[WebRTC] [Signaller] "+l),r==null||r(l)}}sendIceNegotiation(t,i,a,r,n){const o={type:"negotiation",content:{type:"iceNegotiation",content:{session_id:t,consumer_id:i,producer_id:a,ice:r.toJSON()}}};console.debug(`[WebRTC] [Signaller] Sending ICE answer: ${JSON.stringify(o,null,4)}`);try{this.ws.send(JSON.stringify(o)),console.debug("[WebRTC] [Signaller] Message sent:",o),n==null||n("ICE Candidate sent")}catch(s){const l=`Failed sending ICE Candidate. Reason: ${s}`;console.error("[WebRTC] [Signaller] "+l),n==null||n(l)}}sendMediaNegotiation(t,i,a,r,n){const o={type:"negotiation",content:{type:"mediaNegotiation",content:{session_id:t,consumer_id:i,producer_id:a,sdp:r.toJSON()}}};try{this.ws.send(JSON.stringify(o)),console.debug("[WebRTC] [Signaller] Message sent:",o),n==null||n("ICE Candidate sent")}catch(s){const l=`Failed sending SDP. Reason: ${s}`;console.error("[WebRTC] [Signaller] "+l),n==null||n(l)}}parseSessionStartAnswer(t){const i=JSON.parse(t.data);if(i.type!=="answer")return;const a=i.content;if(a.type==="startSession")return a.content.session_id}parseEndSessionQuestion(t,i,a,r,n){console.debug(`[WebRTC] [Signaller] Registering parseEndSessionQuestion for Consumer "${t}", Producer "${i}", Session "${a}", with callbacks:`,r,n);const o=this;this.addEventListener("message",function s(l){try{const d=JSON.parse(l.data);if(d.type!=="question")return;const C=d.content;if(C.type!=="endSession")return;console.debug("[WebRTC] [Signaller] Message accepted from parseEndSessionQuestion:",d);const m=C.content;if(m.consumer_id!==t||m.producer_id!==i||m.session_id!==a)return;o.removeEventListener("message",s);const u=m.reason;n==null||n("EndSession arrived"),r==null||r(a,u)}catch(d){const C=`Failed parsing received Message. Error: ${d}. Data: ${l.data}`;console.error("[WebRTC] [Signaller] "+C),n==null||n(C);return}})}parseNegotiation(t,i,a,r,n,o){console.debug(`[WebRTC] [Signaller] Registering parseNegotiation for Consumer "${t}", Producer "${i}", Session "${a}", with callbacks:`,r,n,o),this.addEventListener("message",s=>{try{const l=JSON.parse(s.data);if(l.type!=="negotiation")return;console.debug("[WebRTC] [Signaller] Message accepted from parseNegotiation:",l);const d=l.content;if(d.content.consumer_id!==t||d.content.producer_id!==i||d.content.session_id!==a)return;switch(d.type){case"iceNegotiation":o==null||o("iceNegotiation arrived"),r==null||r(d.content.ice);break;case"mediaNegotiation":o==null||o("mediaNegotiation arrived"),n==null||n(d.content.sdp);break}}catch(l){const d=`Failed parsing received Message. Error: ${l}. Data: ${s.data}`;console.error("[WebRTC] [Signaller] "+d),o==null||o(d);return}})}parseAvailableStreamsAnswer(t,i){console.debug("[WebRTC] [Signaller] Registering parseAvailableStreamsAnswer with callbacks:",t,i);const a=this;this.addEventListener("message",function r(n){try{const o=JSON.parse(n.data);if(o.type!=="answer")return;const s=o.content;if(s.type!=="availableStreams")return;console.debug("[WebRTC] [Signaller] Message accepted from parseAvailableStreamsAnswer:",o),a.removeEventListener("message",r);const l=s.content;i==null||i("Available Streams arrived"),t==null||t(l)}catch(o){const s=`Failed parsing received Message. Error: ${o}. Data: ${n.data}`;console.error("[WebRTC] [Signaller] "+s),i==null||i(s);return}})}end(t){this.ws.removeEventListener("open",this.onOpenCallback.bind(this)),this.ws.removeEventListener("error",this.onErrorCallback.bind(this)),this.ws.removeEventListener("close",this.onCloseCallback.bind(this)),this.removeAllListeners("open",!1),this.removeAllListeners("error",!1),this.removeAllListeners("close",!1),this.removeAllListeners("message",!1),this.ws.readyState===this.ws.OPEN&&(console.debug(`[WebRTC] [Signaller] Closing WebSocket. Reason: ${t}`),this.ws.close())}connect(){const t=new WebSocket(this.url.toString());t.addEventListener("open",this.onOpenCallback.bind(this)),t.addEventListener("error",this.onErrorCallback.bind(this)),t.addEventListener("close",this.onCloseCallback.bind(this));for(const[i,a]of this.listeners)for(const[r,n]of a)t.addEventListener(i,r,n);return t}reconnect(){var a;const t="Reconnecting to signalling";console.debug("[WebRTC] [Signaller] "+t),(a=this.onStatusChange)==null||a.call(this,t),this.end("reconnect");const i=this.ws;i.onclose=null,i.onopen=null,i.onmessage=null,i.onerror=null;try{this.ws=this.connect()}catch(r){console.error(`[WebRTC] [Signaller] Could not reconnect. ${r}`)}}onOpenCallback(t){var a,r;const i="Signaller Connected";console.debug("[WebRTC] [Signaller] "+i,t),(a=this.onStatusChange)==null||a.call(this,i),(r=this.onOpen)==null||r.call(this,t)}onCloseCallback(t){var a;const i="Signaller connection closed";console.debug("[WebRTC] [Signaller] "+i,t),(a=this.onStatusChange)==null||a.call(this,i),this.shouldReconnect&&setTimeout(()=>{(this.ws.readyState===this.ws.CLOSED||this.ws.readyState===this.ws.CLOSING)&&this.reconnect()},1e3)}onErrorCallback(t){var a;const i="Signaller connection Error";console.debug("[WebRTC] [Signaller] "+i,t),(a=this.onStatusChange)==null||a.call(this,i)}}class OD{constructor(t,i){S1(this,"availableStreams",i1(new Array));S1(this,"availableICEIPs",i1(new Array));S1(this,"mediaStream",i1());S1(this,"signallerStatus",i1("waiting..."));S1(this,"streamStatus",i1("waiting..."));S1(this,"consumerId");S1(this,"streamName");S1(this,"session");S1(this,"rtcConfiguration");S1(this,"selectedICEIPs",[]);S1(this,"hasEnded",!1);S1(this,"signaller");S1(this,"waitingForAvailableStreamsAnswer",!1);S1(this,"waitingForSessionStart",!1);console.debug("[WebRTC] Trying to connect to signalling server."),this.rtcConfiguration=i,this.signaller=new Ve2(t,!0,()=>{this.startConsumer()},a=>this.updateSignallerStatus(a))}close(t){this.stopSession(t),this.signaller.end(t),this.hasEnded=!0}startStream(t,i){return this.selectedICEIPs=i.value,T1(t,(a,r)=>{if((a==null?void 0:a.id)===(r==null?void 0:r.id))return;const n=`Selected stream changed from "${r==null?void 0:r.id}" to "${a==null?void 0:a.id}".`;console.debug("[WebRTC] "+n),r!==void 0&&this.stopSession(n),a!==void 0&&(this.streamName=a.name,this.startSession())}),T1(i,(a,r)=>{if(a===r)return;const n=`Selected IPs changed from "${r}" to "${a}".`;console.debug("[WebRTC] "+n),this.selectedICEIPs=a,this.streamName!==void 0&&this.stopSession(n),this.streamName!==void 0&&this.startSession()}),{availableStreams:this.availableStreams,availableICEIPs:this.availableICEIPs,mediaStream:this.mediaStream,signallerStatus:this.signallerStatus,streamStatus:this.streamStatus}}updateStreamStatus(t){console.debug(`[WebRTC] Stream status updated from "${this.streamStatus.value}" to "${t}"`);const i=new Date().toTimeString().split(" ").first();this.streamStatus.value=`${t} (${i})`}updateSignallerStatus(t){console.debug(`[WebRTC] Signaller status updated from "${this.signallerStatus.value}" to "${t}"`);const i=new Date().toTimeString().split(" ").first();this.signallerStatus.value=`${t} (${i})`}startConsumer(){this.hasEnded=!1,this.consumerId===void 0&&this.signaller.requestConsumerId(t=>{this.consumerId=t},t=>this.updateStreamStatus(t)),this.availableStreams.value=[],this.updateStreamsAvailable()}updateStreamsAvailable(){if(this.waitingForAvailableStreamsAnswer){this.signaller.requestStreams();return}if(this.hasEnded){this.waitingForAvailableStreamsAnswer=!1;return}this.waitingForAvailableStreamsAnswer=!0,window.setTimeout(()=>{!this.waitingForAvailableStreamsAnswer||(this.signaller.parseAvailableStreamsAnswer(t=>{!this.waitingForAvailableStreamsAnswer||(this.waitingForAvailableStreamsAnswer=!1,this.availableStreams.value=t,this.updateStreamsAvailable())}),this.signaller.requestStreams())},1e3)}onTrackAdded(t){var r,n,o,s,l,d;const[i]=t.streams;this.mediaStream.value=i,this.mediaStream.value.getVideoTracks().filter(C=>C.kind==="video").forEach(C=>{if(!("contentHint"in C)){console.error("MediaStreamTrack contentHint attribute not supported.");return}C.contentHint="motion"}),console.debug("[WebRTC] Track added"),console.debug("Event:",t),console.debug("Settings:",(n=(r=t.track).getSettings)==null?void 0:n.call(r)),console.debug("Constraints:",(s=(o=t.track).getConstraints)==null?void 0:s.call(o)),console.debug("Capabilities:",(d=(l=t.track).getCapabilities)==null?void 0:d.call(l))}requestSession(t,i){console.debug("[WebRTC] Requesting stream:",t),this.signaller.requestSessionId(i,t.id,a=>{this.onSessionIdReceived(t,t.id,a)},a=>this.updateStreamStatus(a)),this.hasEnded=!1}startSession(){this.waitingForSessionStart||(this.waitingForSessionStart=!0,window.setTimeout(()=>{if(!this.waitingForSessionStart)return;const t=this.availableStreams.value.find(a=>a.name===this.streamName);if(t===void 0){const a=`Failed to start a new Session with "${this.streamName}". Reason: not available`;console.error("[WebRTC] "+a),this.updateStreamStatus(a),this.waitingForSessionStart=!1,this.startSession();return}const i=`Starting session with producer "${t.id}" ("${this.streamName}")`;if(this.updateStreamStatus(i),console.debug("[WebRTC] "+i),this.consumerId===void 0){const a=`Failed to start a new Session with producer"${t.id}" ("${this.streamName}"). Reason: undefined consumerId`;console.error("[WebRTC] "+a),this.updateStreamStatus(a),this.startConsumer(),this.startSession();return}this.requestSession(t,this.consumerId),this.waitingForSessionStart=!1},1e3))}onSessionClosed(t){this.stopSession(t),this.consumerId=void 0,this.startConsumer(),this.startSession()}onSessionIdReceived(t,i,a){this.session=new fe2(a,this.consumerId,t,this.signaller,this.rtcConfiguration,this.selectedICEIPs,n=>this.onTrackAdded(n),n=>this.availableICEIPs.value=n,(n,o)=>this.onSessionClosed(o)),this.signaller.parseEndSessionQuestion(this.consumerId,i,this.session.id,(n,o)=>{console.debug(`[WebRTC] Session ${n} ended. Reason: ${o}`),this.session=void 0,this.hasEnded=!0},n=>this.updateSignallerStatus(n)),this.signaller.parseNegotiation(this.consumerId,i,this.session.id,this.session.onIncomingICE.bind(this.session),this.session.onIncomingSDP.bind(this.session),n=>this.updateSignallerStatus(n));const r=`Session ${this.session.id} successfully started`;console.debug("[WebRTC] "+r),this.updateStreamStatus(r)}stopSession(t){if(this.session===void 0){console.debug("[WebRTC] Stopping an undefined session, probably it was already stopped?");return}const i=`Stopping session ${this.session.id}. Reason: ${t}`;this.updateStreamStatus(i),console.debug("[WebRTC] "+i),this.session.end(),this.session=void 0,this.hasEnded=!0}}const hM=er("video",()=>{const e=To(),t=QC(),{globalAddress:i,rtcConfiguration:a,webRTCSignallingURI:r}=r5();console.debug("[WebRTC] Using webrtc-adapter for",pe2.browserDetails);const n=R5("cockpit-allowed-stream-ips",[]),o=i1({}),s=new OD(r.val,a),{availableStreams:l}=s.startStream(i1(void 0),n),d=i1([]),C=R5("video-recovery-warning-already-shown",!1),m=q(()=>l.value.map(E=>E.name));T1(n,()=>{Object.keys(o.value).forEach(E=>o.value[E]=void 0)}),setInterval(()=>{Object.keys(o.value).forEach(E=>{if(o.value[E]===void 0)return;const O=o.value[E].webRtcManager.availableICEIPs.filter(F=>!d.value.includes(F));d.value=[...d.value,...O];const z=l.value.find(F=>F.name===E);qs(z,o.value[E].stream)||(console.log(`New stream for '${E}':`),console.log(JSON.stringify(z,null,2)),u(E),o.value[E].stream=z)})},300);const u=E=>{const O=i1(),z=new OD(r.val,a),{mediaStream:F}=z.startStream(O,n);o.value[E]={stream:O,webRtcManager:z,mediaStream:F,mediaRecorder:void 0,timeRecordingStart:void 0},console.debug(`Activated stream '${E}'.`)},p=E=>(o.value[E]===void 0&&u(E),o.value[E]),V=E=>(o.value[E]===void 0&&u(E),o.value[E].mediaStream),f=E=>(o.value[E]===void 0&&u(E),o.value[E].mediaRecorder!==void 0&&o.value[E].mediaRecorder.state==="recording"),M=E=>{o.value[E]===void 0&&u(E),o.value[E].mediaRecorder.stop(),t.pushAlert(new ii(_e.Success,`Stopped recording stream ${E}.`))},H=E=>{if(o.value[E]===void 0&&u(E),m.value.isEmpty()){O2.fire({text:"No streams available.",icon:"error"});return}if(o.value[E].mediaStream===void 0){O2.fire({text:"Media stream not defined.",icon:"error"});return}if(!o.value[E].mediaStream.active){O2.fire({text:"Media stream not yet active. Wait a second and try again.",icon:"error"});return}o.value[E].timeRecordingStart=new Date;const O=o.value[E],z=pt().slice(0,6),F=hn(O.timeRecordingStart,"LLL dd, yyyy - HH\uA789mm\uA789ss O"),J=`${e.missionName||"Cockpit"} (${F}) #${z}`;o.value[E].mediaRecorder=new MediaRecorder(O.mediaStream),A5.logging()||A5.startLogging();const W=O.mediaStream.getVideoTracks()[0],r1=W.getSettings().width||1920,$=W.getSettings().height||1080;o.value[E].mediaRecorder.start(1e3);let t1=0;o.value[E].mediaRecorder.ondataavailable=async l1=>{const V1=`${z}_${t1}`;if(await D.getItem(V1)){M(E);return}await D.setItem(V1,l1.data),t1++},o.value[E].mediaRecorder.onstop=async()=>{const l1=[];if(await D.iterate((m1,f1)=>{f1.includes(z)&&l1.push({blob:m1,name:f1})}),l1.length===0){O2.fire({text:"No video recording data found.",icon:"error"});return}const A1=l1.sort((m1,f1)=>{const L1=m1.name.split("_"),e1=f1.name.split("_");return Number(L1[L1.length-1])-Number(e1[e1.length-1])}).map(m1=>m1.blob).reduce((m1,f1)=>new Blob([m1,f1],{type:"video/webm"})),n1=await oe2(A1,Date.now()-O.timeRecordingStart.getTime());R.setItem(`${J}.webm`,n1);const M1=A5.getSlice(A5.currentCockpitLog,O.timeRecordingStart,new Date),X=A5.toAssOverlay(M1,r1,$,O.timeRecordingStart.getTime()),s1=new Blob([X],{type:"text/plain"});R.setItem(`${J}.ass`,s1),o.value[E].mediaRecorder=void 0},t.pushAlert(new ii(_e.Success,`Started recording stream ${E}.`))},g=async E=>{console.debug(`Discarding files from the video recovery database: ${E.join(", ")}`);for(const O of E)await R.removeItem(O)},_=async E=>{if(console.debug(`Downloading files from the video recovery database: ${E.join(", ")}`),E.length===1){const W=await R.getItem(E[0]);if(!W){O2.fire({text:"File not found.",icon:"error"});return}_a.exports.saveAs(W,E[0]);return}const O=new $22(new yt1("application/zip")),z=E.filter(async W=>await R.getItem(W)).map(async W=>{const r1=await R.getItem(W);return{filename:W,file:r1}}),F=await Promise.all(z);for(const{filename:W,file:r1}of F)await O.add(W,new y22(r1));const J=await O.close();_a.exports.saveAs(J,"Cockpit-Video-Recovery.zip")},v=async()=>{const E=[];await D.iterate((O,z)=>{E.push(z)});for(const O of E)await D.removeItem(O)},S=async()=>{let E=0;return await D.iterate(O=>{E+=O.size}),E},y=async E=>{const O=await R.getItem(E);return O?O.size:void 0},D=H4.createInstance({driver:H4.INDEXEDDB,name:"Cockpit - Temporary Video",storeName:"cockpit-temp-video-db",version:1,description:"Database for storing the chunks of an ongoing recording, to be merged afterwards."}),R=H4.createInstance({driver:H4.INDEXEDDB,name:"Cockpit - Video Recovery",storeName:"cockpit-video-recovery-db",version:1,description:"Local backups of Cockpit video recordings to be retrieved in case of failure."});R.length().then(E=>{E!==0&&(C.value||(O2.fire({title:"Download of video recordings",text:`Cockpit has video recordings waiting on the disk storage. +`);e.RTCSessionDescription&&r instanceof e.RTCSessionDescription?arguments[0]=new e.RTCSessionDescription({type:r.type,sdp:n}):r.sdp=n}return i.apply(this,arguments)}}function uc(e,t){if(!(e.RTCPeerConnection&&e.RTCPeerConnection.prototype))return;const i=e.RTCPeerConnection.prototype.addIceCandidate;!i||i.length===0||(e.RTCPeerConnection.prototype.addIceCandidate=function(){return arguments[0]?(t.browser==="chrome"&&t.version<78||t.browser==="firefox"&&t.version<68||t.browser==="safari")&&arguments[0]&&arguments[0].candidate===""?Promise.resolve():i.apply(this,arguments):(arguments[1]&&arguments[1].apply(null),Promise.resolve())})}function hc(e,t){if(!(e.RTCPeerConnection&&e.RTCPeerConnection.prototype))return;const i=e.RTCPeerConnection.prototype.setLocalDescription;!i||i.length===0||(e.RTCPeerConnection.prototype.setLocalDescription=function(){let r=arguments[0]||{};if(typeof r!="object"||r.type&&r.sdp)return i.apply(this,arguments);if(r={type:r.type,sdp:r.sdp},!r.type)switch(this.signalingState){case"stable":case"have-local-offer":case"have-remote-pranswer":r.type="offer";break;default:r.type="answer";break}return r.sdp||r.type!=="offer"&&r.type!=="answer"?i.apply(this,[r]):(r.type==="offer"?this.createOffer:this.createAnswer).apply(this).then(o=>i.apply(this,[o]))})}const ue2=Object.freeze(Object.defineProperty({__proto__:null,shimRTCIceCandidate:dc,shimRTCIceCandidateRelayProtocol:Sf,shimMaxMessageSize:Cc,shimSendThrowTypeError:mc,shimConnectionState:Df,removeExtmapAllowMixed:yf,shimAddIceCandidateNullOrEmpty:uc,shimParameterlessSetLocalDescription:hc},Symbol.toStringTag,{value:"Module"}));function he2({window:e}={},t={shimChrome:!0,shimFirefox:!0,shimSafari:!0}){const i=kt1,a=ce2(e),r={browserDetails:a,commonShim:ue2,extractVersion:cc,disableLog:se2,disableWarnings:le2,sdp:me2};switch(a.browser){case"chrome":if(!ED||!vf||!t.shimChrome)return i("Chrome shim is not included in this adapter release."),r;if(a.version===null)return i("Chrome shim can not determine version, not shimming."),r;i("adapter.js shimming chrome."),r.browserShim=ED,uc(e,a),hc(e),zt1(e,a),Zt1(e),vf(e,a),Gt1(e),qt1(e,a),Wt1(e),Xt1(e),Yt1(e),Kt1(e,a),dc(e),Sf(e),Df(e),Cc(e,a),mc(e),yf(e,a);break;case"firefox":if(!PD||!_f||!t.shimFirefox)return i("Firefox shim is not included in this adapter release."),r;i("adapter.js shimming firefox."),r.browserShim=PD,uc(e,a),hc(e),jt1(e,a),_f(e,a),Qt1(e),t01(e),Jt1(e),e01(e),i01(e),a01(e),r01(e),n01(e),o01(e),dc(e),Df(e),Cc(e,a),mc(e);break;case"safari":if(!ID||!t.shimSafari)return i("Safari shim is not included in this adapter release."),r;i("adapter.js shimming safari."),r.browserShim=ID,uc(e,a),hc(e),m01(e),h01(e),c01(e),s01(e),l01(e),u01(e),d01(e),p01(e),dc(e),Sf(e),Cc(e,a),mc(e),yf(e,a);break;default:i("Unsupported browser!");break}return r}const pe2=he2({window:typeof window>"u"?void 0:window});class fe2{constructor(t,i,a,r,n,o=[],s,l,d){S1(this,"id");S1(this,"consumerId");S1(this,"stream");S1(this,"status");S1(this,"ended");S1(this,"signaller");S1(this,"peerConnection");S1(this,"availableICEIPs");S1(this,"selectedICEIPs");S1(this,"rtcConfiguration");S1(this,"onTrackAdded");S1(this,"onNewIceRemoteAddress");S1(this,"onClose");this.id=t,this.consumerId=i,this.stream=a,this.onTrackAdded=s,this.onNewIceRemoteAddress=l,this.onClose=d,this.status="",this.signaller=r,this.rtcConfiguration=n,this.ended=!1,this.availableICEIPs=[],this.selectedICEIPs=o,this.peerConnection=this.createRTCPeerConnection(n),this.updateStatus("[WebRTC] [Session] Creating Session...")}hasEnded(){return this.ended}isConnected(){return this.peerConnection.connectionState=="connected"}updateStatus(t){this.status=t}createRTCPeerConnection(t){console.debug("[WebRTC] [Session] Creating RTCPeerConnection");const i=new RTCPeerConnection(t);return i.addTransceiver("video",{direction:"recvonly"}),this.ended=!1,i.addEventListener("negotiationneeded",this.onNegotiationNeeded.bind(this)),i.addEventListener("track",this.onTrackAddedCallback.bind(this)),i.addEventListener("icecandidate",this.onIceCandidate.bind(this)),i.addEventListener("icecandidateerror",this.onIceCandidateError.bind(this)),i.addEventListener("iceconnectionstatechange",this.onIceConnectionStateChange.bind(this)),i.addEventListener("connectionstatechange",this.onConnectionStateChange.bind(this)),i.addEventListener("signalingstatechange",this.onSignalingStateChange.bind(this)),i.addEventListener("icegatheringstatechange",this.onIceGatheringStateChange.bind(this)),i}onIncomingSDP(t){this.peerConnection.setRemoteDescription(t).then(()=>{console.debug(`[WebRTC] [Session] Remote description set to ${JSON.stringify(t,null,4)}`),this.onRemoteDescriptionSet()}).catch(i=>console.error(`[WebRTC] [Session] Failed setting remote description ${t}. Reason: ${i}`))}onRemoteDescriptionSet(){this.peerConnection.createAnswer().then(t=>{console.debug(`[WebRTC] [Session] SDP Answer created as: ${JSON.stringify(t,null,4)}`),this.onAnswerCreated(t)}).catch(t=>console.error(`[WebRTC] [Session] Failed creating description answer. Reason: ${t}`))}onAnswerCreated(t){this.peerConnection.setLocalDescription(t).then(()=>{console.debug(`[WebRTC] [Session] Local description set as${JSON.stringify(t,null,4)}`),this.onLocalDescriptionSet()}).catch(function(i){console.error(`[WebRTC] [Session] Failed setting local description. Reason: ${i}`)})}onLocalDescriptionSet(){this.peerConnection.localDescription!==null&&this.signaller.sendMediaNegotiation(this.id,this.consumerId,this.stream.id,this.peerConnection.localDescription)}onIncomingICE(t){const i=/\b(?:\d{1,3}\.){3}\d{1,3}\b/g,r=(n=>{const o=n.match(i);return o==null?void 0:o.find(s=>!s.includes(":"))})(t.candidate);if(r&&!this.availableICEIPs.includes(r)&&this.onNewIceRemoteAddress&&(this.availableICEIPs.push(r),this.onNewIceRemoteAddress(this.availableICEIPs)),t.candidate&&Array.isArray(this.selectedICEIPs)&&!this.selectedICEIPs.isEmpty()&&!this.selectedICEIPs.some(n=>t.candidate.includes(n))){console.debug(`[WebRTC] [Session] ICE candidate ignored: ${JSON.stringify(t,null,4)}`);return}this.peerConnection.addIceCandidate(t).then(()=>console.debug(`[WebRTC] [Session] ICE candidate added: ${JSON.stringify(t,null,4)}`)).catch(n=>console.error(`[WebRTC] [Session] Failed adding ICE candidate ${t}. Reason: ${n}`))}onIceCandidate(t){!t.candidate||this.signaller.sendIceNegotiation(this.id,this.consumerId,this.stream.id,t.candidate)}onIceCandidateError(t){console.debug(`[WebRTC] [Session] ICE Candidate "${t.url}" negotiation failed`)}onTrackAddedCallback(t){var i;(i=this.onTrackAdded)==null||i.call(this,t)}onNegotiationNeeded(t){console.debug("[WebRTC] [Session] Peer Connection is waiting for negotiation...")}onIceConnectionStateChange(){const t=`ICEConnection state changed to "${this.peerConnection.iceConnectionState}"`;console.debug("[WebRTC] [Session] "+t),this.peerConnection.iceConnectionState==="failed"&&this.peerConnection.restartIce()}onConnectionStateChange(){var i;const t=`RTCPeerConnection state changed to "${this.peerConnection.connectionState}"`;console.debug("[WebRTC] [Session] "+t),this.peerConnection.connectionState==="failed"&&((i=this.onClose)==null||i.call(this,this.id,"PeerConnection failed"),this.end())}onSignalingStateChange(){const t=`Signalling state changed to "${this.peerConnection.iceConnectionState}"`;console.debug("[WebRTC] [Session] "+t)}onIceGatheringStateChange(){this.peerConnection.iceGatheringState==="complete"&&console.debug(`[WebRTC] [Session] ICE gathering completed for session ${this.id}`)}end(){this.endPeerConnection(),this.onTrackAdded=void 0,this.onNewIceRemoteAddress=void 0,this.onClose=void 0,this.ended=!0,console.debug(`[WebRTC] [Session] Session ${this.id} ended.`)}endPeerConnection(){this.peerConnection.removeEventListener("negotiationneeded",this.onNegotiationNeeded.bind(this)),this.peerConnection.removeEventListener("track",this.onTrackAddedCallback.bind(this)),this.peerConnection.removeEventListener("icecandidate",this.onIceCandidate.bind(this)),this.peerConnection.removeEventListener("icecandidateerror",this.onIceCandidateError.bind(this)),this.peerConnection.removeEventListener("iceconnectionstatechange",this.onIceConnectionStateChange.bind(this)),this.peerConnection.removeEventListener("connectionstatechange",this.onConnectionStateChange.bind(this)),this.peerConnection.removeEventListener("signalingstatechange",this.onSignalingStateChange.bind(this)),this.peerConnection.removeEventListener("icegatheringstatechange",this.onIceGatheringStateChange.bind(this)),this.peerConnection.close()}}class Ve2{constructor(t,i,a,r){S1(this,"ws");S1(this,"onOpen");S1(this,"onStatusChange");S1(this,"url");S1(this,"listeners");S1(this,"shouldReconnect");var o;this.onOpen=a,this.onStatusChange=r,this.listeners=new Map,this.shouldReconnect=i,this.url=t;const n=`Connecting to signalling server on ${t}`;console.debug("[WebRTC] [Signaller] "+n),(o=this.onStatusChange)==null||o.call(this,n);try{this.ws=this.connect()}catch(s){console.error(`Could not establish initial connection. ${s}`)}}addEventListener(t,i,a){this.listeners.has(t)||this.listeners.set(t,new Map),this.listeners.get(t).set(i,a),this.ws.addEventListener(t,i,a)}removeEventListener(t,i,a){if(this.ws.removeEventListener(t,i,a),!this.listeners.has(t))return;const r=this.listeners.get(t);if(!r.has(i)){console.warn(`[WebRTC] [Signaller] Failed removing listener named ${i.name} of type "${t}". Reason: not found`);return}const n=r.get(i);a&&n&&a!==n||r.delete(i)}removeAllListeners(t,i){if(!(!this.listeners.size||!this.listeners.has(t))){for(const[a,r]of this.listeners.get(t))this.ws.removeEventListener(t,a,r);i&&this.listeners.delete(t)}}requestConsumerId(t,i){const a=this;this.addEventListener("message",function n(o){try{const s=JSON.parse(o.data);if(s.type!=="answer")return;const l=s.content;if(l.type!=="peerId")return;console.debug("[WebRTC] [Signaller] Message accepted from requestConsumerId:",s),a.removeEventListener("message",n);const d=l.content.id;i==null||i(`Consumer Id arrived: ${d}`),t(d)}catch(s){const l=`Failed receiving PeerId Answer Message. Error: ${s}. Data: ${o.data}`;console.error("[WebRTC] [Signaller] "+l),i==null||i(l)}});const r={type:"question",content:{type:"peerId"}};try{this.ws.send(JSON.stringify(r)),console.debug("[WebRTC] [Signaller] Message sent:",r),i==null||i("Consumer Id requested, waiting answer...")}catch(n){const o=`Failed requesting peer id. Reason: ${n}`;console.error("[WebRTC] [Signaller] "+o),i==null||i(o)}}isConnected(){return this.ws.readyState===this.ws.OPEN}requestStreams(t){const i={type:"question",content:{type:"availableStreams"}};try{if(this.ws.readyState!==this.ws.OPEN)return;this.ws.send(JSON.stringify(i)),console.debug("[WebRTC] [Signaller] Message sent:",i),t==null||t("StreamsAvailable requested")}catch(a){const r=`Failed requesting available streams. Reason: ${a}`;console.error("[WebRTC] [Signaller] "+r),t==null||t(r)}}requestSessionId(t,i,a,r){const n=this;n.addEventListener("message",function s(l){try{const d=JSON.parse(l.data);if(d.type!=="answer")return;const C=d.content;if(C.type!=="startSession")return;console.debug("[WebRTC] [Signaller] Message accepted from requestSessionId:",d);const m=C.content.session_id;if(m===void 0)return;n.removeEventListener("message",s),r==null||r(`Session Id arrived: ${m}`),a(m)}catch(d){const C=`Failed receiving StartSession Answer Message. Error: ${d}. Data: ${l.data}`;console.error("[WebRTC] [Signaller] "+C),r==null||r(C);return}});const o={type:"question",content:{type:"startSession",content:{consumer_id:t,producer_id:i}}};try{this.ws.send(JSON.stringify(o)),console.debug("[WebRTC] [Signaller] Message sent:",o),r==null||r("Session Id requested, waiting answer...")}catch(s){const l=`Failed requesting Session Id. Reason: ${s}`;console.error("[WebRTC] [Signaller] "+l),r==null||r(l)}}sendIceNegotiation(t,i,a,r,n){const o={type:"negotiation",content:{type:"iceNegotiation",content:{session_id:t,consumer_id:i,producer_id:a,ice:r.toJSON()}}};console.debug(`[WebRTC] [Signaller] Sending ICE answer: ${JSON.stringify(o,null,4)}`);try{this.ws.send(JSON.stringify(o)),console.debug("[WebRTC] [Signaller] Message sent:",o),n==null||n("ICE Candidate sent")}catch(s){const l=`Failed sending ICE Candidate. Reason: ${s}`;console.error("[WebRTC] [Signaller] "+l),n==null||n(l)}}sendMediaNegotiation(t,i,a,r,n){const o={type:"negotiation",content:{type:"mediaNegotiation",content:{session_id:t,consumer_id:i,producer_id:a,sdp:r.toJSON()}}};try{this.ws.send(JSON.stringify(o)),console.debug("[WebRTC] [Signaller] Message sent:",o),n==null||n("ICE Candidate sent")}catch(s){const l=`Failed sending SDP. Reason: ${s}`;console.error("[WebRTC] [Signaller] "+l),n==null||n(l)}}parseSessionStartAnswer(t){const i=JSON.parse(t.data);if(i.type!=="answer")return;const a=i.content;if(a.type==="startSession")return a.content.session_id}parseEndSessionQuestion(t,i,a,r,n){console.debug(`[WebRTC] [Signaller] Registering parseEndSessionQuestion for Consumer "${t}", Producer "${i}", Session "${a}", with callbacks:`,r,n);const o=this;this.addEventListener("message",function s(l){try{const d=JSON.parse(l.data);if(d.type!=="question")return;const C=d.content;if(C.type!=="endSession")return;console.debug("[WebRTC] [Signaller] Message accepted from parseEndSessionQuestion:",d);const m=C.content;if(m.consumer_id!==t||m.producer_id!==i||m.session_id!==a)return;o.removeEventListener("message",s);const u=m.reason;n==null||n("EndSession arrived"),r==null||r(a,u)}catch(d){const C=`Failed parsing received Message. Error: ${d}. Data: ${l.data}`;console.error("[WebRTC] [Signaller] "+C),n==null||n(C);return}})}parseNegotiation(t,i,a,r,n,o){console.debug(`[WebRTC] [Signaller] Registering parseNegotiation for Consumer "${t}", Producer "${i}", Session "${a}", with callbacks:`,r,n,o),this.addEventListener("message",s=>{try{const l=JSON.parse(s.data);if(l.type!=="negotiation")return;console.debug("[WebRTC] [Signaller] Message accepted from parseNegotiation:",l);const d=l.content;if(d.content.consumer_id!==t||d.content.producer_id!==i||d.content.session_id!==a)return;switch(d.type){case"iceNegotiation":o==null||o("iceNegotiation arrived"),r==null||r(d.content.ice);break;case"mediaNegotiation":o==null||o("mediaNegotiation arrived"),n==null||n(d.content.sdp);break}}catch(l){const d=`Failed parsing received Message. Error: ${l}. Data: ${s.data}`;console.error("[WebRTC] [Signaller] "+d),o==null||o(d);return}})}parseAvailableStreamsAnswer(t,i){console.debug("[WebRTC] [Signaller] Registering parseAvailableStreamsAnswer with callbacks:",t,i);const a=this;this.addEventListener("message",function r(n){try{const o=JSON.parse(n.data);if(o.type!=="answer")return;const s=o.content;if(s.type!=="availableStreams")return;console.debug("[WebRTC] [Signaller] Message accepted from parseAvailableStreamsAnswer:",o),a.removeEventListener("message",r);const l=s.content;i==null||i("Available Streams arrived"),t==null||t(l)}catch(o){const s=`Failed parsing received Message. Error: ${o}. Data: ${n.data}`;console.error("[WebRTC] [Signaller] "+s),i==null||i(s);return}})}end(t){this.ws.removeEventListener("open",this.onOpenCallback.bind(this)),this.ws.removeEventListener("error",this.onErrorCallback.bind(this)),this.ws.removeEventListener("close",this.onCloseCallback.bind(this)),this.removeAllListeners("open",!1),this.removeAllListeners("error",!1),this.removeAllListeners("close",!1),this.removeAllListeners("message",!1),this.ws.readyState===this.ws.OPEN&&(console.debug(`[WebRTC] [Signaller] Closing WebSocket. Reason: ${t}`),this.ws.close())}connect(){const t=new WebSocket(this.url.toString());t.addEventListener("open",this.onOpenCallback.bind(this)),t.addEventListener("error",this.onErrorCallback.bind(this)),t.addEventListener("close",this.onCloseCallback.bind(this));for(const[i,a]of this.listeners)for(const[r,n]of a)t.addEventListener(i,r,n);return t}reconnect(){var a;const t="Reconnecting to signalling";console.debug("[WebRTC] [Signaller] "+t),(a=this.onStatusChange)==null||a.call(this,t),this.end("reconnect");const i=this.ws;i.onclose=null,i.onopen=null,i.onmessage=null,i.onerror=null;try{this.ws=this.connect()}catch(r){console.error(`[WebRTC] [Signaller] Could not reconnect. ${r}`)}}onOpenCallback(t){var a,r;const i="Signaller Connected";console.debug("[WebRTC] [Signaller] "+i,t),(a=this.onStatusChange)==null||a.call(this,i),(r=this.onOpen)==null||r.call(this,t)}onCloseCallback(t){var a;const i="Signaller connection closed";console.debug("[WebRTC] [Signaller] "+i,t),(a=this.onStatusChange)==null||a.call(this,i),this.shouldReconnect&&setTimeout(()=>{(this.ws.readyState===this.ws.CLOSED||this.ws.readyState===this.ws.CLOSING)&&this.reconnect()},1e3)}onErrorCallback(t){var a;const i="Signaller connection Error";console.debug("[WebRTC] [Signaller] "+i,t),(a=this.onStatusChange)==null||a.call(this,i)}}class OD{constructor(t,i){S1(this,"availableStreams",i1(new Array));S1(this,"availableICEIPs",i1(new Array));S1(this,"mediaStream",i1());S1(this,"signallerStatus",i1("waiting..."));S1(this,"streamStatus",i1("waiting..."));S1(this,"consumerId");S1(this,"streamName");S1(this,"session");S1(this,"rtcConfiguration");S1(this,"selectedICEIPs",[]);S1(this,"hasEnded",!1);S1(this,"signaller");S1(this,"waitingForAvailableStreamsAnswer",!1);S1(this,"waitingForSessionStart",!1);console.debug("[WebRTC] Trying to connect to signalling server."),this.rtcConfiguration=i,this.signaller=new Ve2(t,!0,()=>{this.startConsumer()},a=>this.updateSignallerStatus(a))}close(t){this.stopSession(t),this.signaller.end(t),this.hasEnded=!0}startStream(t,i){return this.selectedICEIPs=i.value,T1(t,(a,r)=>{if((a==null?void 0:a.id)===(r==null?void 0:r.id))return;const n=`Selected stream changed from "${r==null?void 0:r.id}" to "${a==null?void 0:a.id}".`;console.debug("[WebRTC] "+n),r!==void 0&&this.stopSession(n),a!==void 0&&(this.streamName=a.name,this.startSession())}),T1(i,(a,r)=>{if(a===r)return;const n=`Selected IPs changed from "${r}" to "${a}".`;console.debug("[WebRTC] "+n),this.selectedICEIPs=a,this.streamName!==void 0&&this.stopSession(n),this.streamName!==void 0&&this.startSession()}),{availableStreams:this.availableStreams,availableICEIPs:this.availableICEIPs,mediaStream:this.mediaStream,signallerStatus:this.signallerStatus,streamStatus:this.streamStatus}}updateStreamStatus(t){console.debug(`[WebRTC] Stream status updated from "${this.streamStatus.value}" to "${t}"`);const i=new Date().toTimeString().split(" ").first();this.streamStatus.value=`${t} (${i})`}updateSignallerStatus(t){console.debug(`[WebRTC] Signaller status updated from "${this.signallerStatus.value}" to "${t}"`);const i=new Date().toTimeString().split(" ").first();this.signallerStatus.value=`${t} (${i})`}startConsumer(){this.hasEnded=!1,this.consumerId===void 0&&this.signaller.requestConsumerId(t=>{this.consumerId=t},t=>this.updateStreamStatus(t)),this.availableStreams.value=[],this.updateStreamsAvailable()}updateStreamsAvailable(){if(this.waitingForAvailableStreamsAnswer){this.signaller.requestStreams();return}if(this.hasEnded){this.waitingForAvailableStreamsAnswer=!1;return}this.waitingForAvailableStreamsAnswer=!0,window.setTimeout(()=>{!this.waitingForAvailableStreamsAnswer||(this.signaller.parseAvailableStreamsAnswer(t=>{!this.waitingForAvailableStreamsAnswer||(this.waitingForAvailableStreamsAnswer=!1,this.availableStreams.value=t,this.updateStreamsAvailable())}),this.signaller.requestStreams())},1e3)}onTrackAdded(t){var r,n,o,s,l,d;const[i]=t.streams;this.mediaStream.value=i,this.mediaStream.value.getVideoTracks().filter(C=>C.kind==="video").forEach(C=>{if(!("contentHint"in C)){console.error("MediaStreamTrack contentHint attribute not supported.");return}C.contentHint="motion"}),console.debug("[WebRTC] Track added"),console.debug("Event:",t),console.debug("Settings:",(n=(r=t.track).getSettings)==null?void 0:n.call(r)),console.debug("Constraints:",(s=(o=t.track).getConstraints)==null?void 0:s.call(o)),console.debug("Capabilities:",(d=(l=t.track).getCapabilities)==null?void 0:d.call(l))}requestSession(t,i){console.debug("[WebRTC] Requesting stream:",t),this.signaller.requestSessionId(i,t.id,a=>{this.onSessionIdReceived(t,t.id,a)},a=>this.updateStreamStatus(a)),this.hasEnded=!1}startSession(){this.waitingForSessionStart||(this.waitingForSessionStart=!0,window.setTimeout(()=>{if(!this.waitingForSessionStart)return;const t=this.availableStreams.value.find(a=>a.name===this.streamName);if(t===void 0){const a=`Failed to start a new Session with "${this.streamName}". Reason: not available`;console.error("[WebRTC] "+a),this.updateStreamStatus(a),this.waitingForSessionStart=!1,this.startSession();return}const i=`Starting session with producer "${t.id}" ("${this.streamName}")`;if(this.updateStreamStatus(i),console.debug("[WebRTC] "+i),this.consumerId===void 0){const a=`Failed to start a new Session with producer"${t.id}" ("${this.streamName}"). Reason: undefined consumerId`;console.error("[WebRTC] "+a),this.updateStreamStatus(a),this.startConsumer(),this.startSession();return}this.requestSession(t,this.consumerId),this.waitingForSessionStart=!1},1e3))}onSessionClosed(t){this.stopSession(t),this.consumerId=void 0,this.startConsumer(),this.startSession()}onSessionIdReceived(t,i,a){this.session=new fe2(a,this.consumerId,t,this.signaller,this.rtcConfiguration,this.selectedICEIPs,n=>this.onTrackAdded(n),n=>this.availableICEIPs.value=n,(n,o)=>this.onSessionClosed(o)),this.signaller.parseEndSessionQuestion(this.consumerId,i,this.session.id,(n,o)=>{console.debug(`[WebRTC] Session ${n} ended. Reason: ${o}`),this.session=void 0,this.hasEnded=!0},n=>this.updateSignallerStatus(n)),this.signaller.parseNegotiation(this.consumerId,i,this.session.id,this.session.onIncomingICE.bind(this.session),this.session.onIncomingSDP.bind(this.session),n=>this.updateSignallerStatus(n));const r=`Session ${this.session.id} successfully started`;console.debug("[WebRTC] "+r),this.updateStreamStatus(r)}stopSession(t){if(this.session===void 0){console.debug("[WebRTC] Stopping an undefined session, probably it was already stopped?");return}const i=`Stopping session ${this.session.id}. Reason: ${t}`;this.updateStreamStatus(i),console.debug("[WebRTC] "+i),this.session.end(),this.session=void 0,this.hasEnded=!0}}const hM=er("video",()=>{const e=To(),t=QC(),{globalAddress:i,rtcConfiguration:a,webRTCSignallingURI:r}=r5();console.debug("[WebRTC] Using webrtc-adapter for",pe2.browserDetails);const n=R5("cockpit-allowed-stream-ips",[]),o=i1({}),s=new OD(r.val,a),{availableStreams:l}=s.startStream(i1(void 0),n),d=i1([]),C=R5("video-recovery-warning-already-shown",!1),m=q(()=>l.value.map(E=>E.name));T1(n,()=>{Object.keys(o.value).forEach(E=>o.value[E]=void 0)}),setInterval(()=>{Object.keys(o.value).forEach(E=>{if(o.value[E]===void 0)return;const O=o.value[E].webRtcManager.availableICEIPs.filter(F=>!d.value.includes(F));d.value=[...d.value,...O];const z=l.value.find(F=>F.name===E);qs(z,o.value[E].stream)||(console.log(`New stream for '${E}':`),console.log(JSON.stringify(z,null,2)),u(E),o.value[E].stream=z)})},300);const u=E=>{const O=i1(),z=new OD(r.val,a),{mediaStream:F}=z.startStream(O,n);o.value[E]={stream:O,webRtcManager:z,mediaStream:F,mediaRecorder:void 0,timeRecordingStart:void 0},console.debug(`Activated stream '${E}'.`)},p=E=>(o.value[E]===void 0&&u(E),o.value[E]),V=E=>(o.value[E]===void 0&&u(E),o.value[E].mediaStream),f=E=>(o.value[E]===void 0&&u(E),o.value[E].mediaRecorder!==void 0&&o.value[E].mediaRecorder.state==="recording"),M=E=>{o.value[E]===void 0&&u(E),o.value[E].mediaRecorder.stop(),t.pushAlert(new ii(_e.Success,`Stopped recording stream ${E}.`))},H=E=>{if(o.value[E]===void 0&&u(E),m.value.isEmpty()){O2.fire({text:"No streams available.",icon:"error"});return}if(o.value[E].mediaStream===void 0){O2.fire({text:"Media stream not defined.",icon:"error"});return}if(!o.value[E].mediaStream.active){O2.fire({text:"Media stream not yet active. Wait a second and try again.",icon:"error"});return}o.value[E].timeRecordingStart=new Date;const O=o.value[E],z=pt().slice(0,6),F=hn(O.timeRecordingStart,"LLL dd, yyyy - HH\uA789mm\uA789ss O"),J=`${e.missionName||"Cockpit"} (${F}) #${z}`;o.value[E].mediaRecorder=new MediaRecorder(O.mediaStream),A5.logging()||A5.startLogging();const W=O.mediaStream.getVideoTracks()[0],r1=W.getSettings().width||1920,$=W.getSettings().height||1080;o.value[E].mediaRecorder.start(1e3);let t1=-1;o.value[E].mediaRecorder.ondataavailable=async l1=>{t1++;const V1=`${z}_${t1}`;if(await D.getItem(V1)){M(E);return}await D.setItem(V1,l1.data)},o.value[E].mediaRecorder.onstop=async()=>{const l1=[];if(await D.iterate((m1,f1)=>{f1.includes(z)&&l1.push({blob:m1,name:f1})}),l1.length===0){O2.fire({text:"No video recording data found.",icon:"error"});return}const A1=l1.sort((m1,f1)=>{const L1=m1.name.split("_"),e1=f1.name.split("_");return Number(L1[L1.length-1])-Number(e1[e1.length-1])}).map(m1=>m1.blob).reduce((m1,f1)=>new Blob([m1,f1],{type:"video/webm"})),n1=await oe2(A1,Date.now()-O.timeRecordingStart.getTime());R.setItem(`${J}.webm`,n1);const M1=A5.getSlice(A5.currentCockpitLog,O.timeRecordingStart,new Date),X=A5.toAssOverlay(M1,r1,$,O.timeRecordingStart.getTime()),s1=new Blob([X],{type:"text/plain"});R.setItem(`${J}.ass`,s1),o.value[E].mediaRecorder=void 0},t.pushAlert(new ii(_e.Success,`Started recording stream ${E}.`))},g=async E=>{console.debug(`Discarding files from the video recovery database: ${E.join(", ")}`);for(const O of E)await R.removeItem(O)},_=async E=>{if(console.debug(`Downloading files from the video recovery database: ${E.join(", ")}`),E.length===1){const W=await R.getItem(E[0]);if(!W){O2.fire({text:"File not found.",icon:"error"});return}_a.exports.saveAs(W,E[0]);return}const O=new $22(new yt1("application/zip")),z=E.filter(async W=>await R.getItem(W)).map(async W=>{const r1=await R.getItem(W);return{filename:W,file:r1}}),F=await Promise.all(z);for(const{filename:W,file:r1}of F)await O.add(W,new y22(r1));const J=await O.close();_a.exports.saveAs(J,"Cockpit-Video-Recovery.zip")},v=async()=>{const E=[];await D.iterate((O,z)=>{E.push(z)});for(const O of E)await D.removeItem(O)},S=async()=>{let E=0;return await D.iterate(O=>{E+=O.size}),E},y=async E=>{const O=await R.getItem(E);return O?O.size:void 0},D=H4.createInstance({driver:H4.INDEXEDDB,name:"Cockpit - Temporary Video",storeName:"cockpit-temp-video-db",version:1,description:"Database for storing the chunks of an ongoing recording, to be merged afterwards."}),R=H4.createInstance({driver:H4.INDEXEDDB,name:"Cockpit - Video Recovery",storeName:"cockpit-video-recovery-db",version:1,description:"Local backups of Cockpit video recordings to be retrieved in case of failure."});R.length().then(E=>{E!==0&&(C.value||(O2.fire({title:"Download of video recordings",text:`Cockpit has video recordings waiting on the disk storage. To download or discard those, please go to the video page, in the configuration menu. Remember that those recordings are only available on the device you used to record the videos, and that they take disk space. If you don't need them anymore, you can discard them. diff --git a/index.html b/index.html index fee99f84a..3914887b5 100644 --- a/index.html +++ b/index.html @@ -14,7 +14,7 @@ Cockpit - + diff --git a/sw.js b/sw.js index eb660ee37..695b758dd 100644 --- a/sw.js +++ b/sw.js @@ -1 +1 @@ -if(!self.define){let e,r={};const i=(i,n)=>(i=new URL(i+".js",n).href,r[i]||new Promise((r=>{if("document"in self){const e=document.createElement("script");e.src=i,e.onload=r,document.head.appendChild(e)}else e=i,importScripts(i),r()})).then((()=>{let e=r[i];if(!e)throw new Error(`Module ${i} didn’t register its module`);return e})));self.define=(n,s)=>{const l=e||("document"in self?document.currentScript.src:"")||location.href;if(r[l])return;let o={};const t=e=>i(e,l),c={module:{uri:l},exports:o,require:t};r[l]=Promise.all(n.map((e=>c[e]||t(e)))).then((e=>(s(...e),o)))}}define(["./workbox-06233651"],(function(e){"use strict";self.skipWaiting(),e.clientsClaim(),e.precacheAndRoute([{url:"electron/main.js",revision:"79f3bbfb05e945b35c338b1d717f0adf"},{url:"index.html",revision:"8be6882ac4b5e55248b9bf1585a936fc"},{url:"leaflet-src.esm.13473dcf.js",revision:null},{url:"marker-icon-2x.68378269.js",revision:null},{url:"marker-icon.753d5637.js",revision:null},{url:"marker-shadow.40fd21b3.js",revision:null},{url:"registerSW.js",revision:"402b66900e731ca748771b6fc5e7a068"},{url:"style.a9a3a478.css",revision:null},{url:"webfontloader.3696e2c4.js",revision:null},{url:"favicon.ico",revision:"b54531a824aa22f592590e347be8347c"},{url:"apple-touch-icon.png",revision:"4d6428d260d0f769a26ed6ce0387d0c1"},{url:"manifest.webmanifest",revision:"a28c2c0a5d92b960e17dd3757933e534"}],{}),e.cleanupOutdatedCaches(),e.registerRoute(new e.NavigationRoute(e.createHandlerBoundToURL("index.html")))})); +if(!self.define){let e,r={};const i=(i,n)=>(i=new URL(i+".js",n).href,r[i]||new Promise((r=>{if("document"in self){const e=document.createElement("script");e.src=i,e.onload=r,document.head.appendChild(e)}else e=i,importScripts(i),r()})).then((()=>{let e=r[i];if(!e)throw new Error(`Module ${i} didn’t register its module`);return e})));self.define=(n,s)=>{const l=e||("document"in self?document.currentScript.src:"")||location.href;if(r[l])return;let o={};const t=e=>i(e,l),c={module:{uri:l},exports:o,require:t};r[l]=Promise.all(n.map((e=>c[e]||t(e)))).then((e=>(s(...e),o)))}}define(["./workbox-06233651"],(function(e){"use strict";self.skipWaiting(),e.clientsClaim(),e.precacheAndRoute([{url:"electron/main.js",revision:"79f3bbfb05e945b35c338b1d717f0adf"},{url:"index.html",revision:"ea993e28330e2499b8abde804ac2b7bb"},{url:"leaflet-src.esm.13473dcf.js",revision:null},{url:"marker-icon-2x.68378269.js",revision:null},{url:"marker-icon.753d5637.js",revision:null},{url:"marker-shadow.40fd21b3.js",revision:null},{url:"registerSW.js",revision:"402b66900e731ca748771b6fc5e7a068"},{url:"style.a9a3a478.css",revision:null},{url:"webfontloader.3696e2c4.js",revision:null},{url:"favicon.ico",revision:"b54531a824aa22f592590e347be8347c"},{url:"apple-touch-icon.png",revision:"4d6428d260d0f769a26ed6ce0387d0c1"},{url:"manifest.webmanifest",revision:"a28c2c0a5d92b960e17dd3757933e534"}],{}),e.cleanupOutdatedCaches(),e.registerRoute(new e.NavigationRoute(e.createHandlerBoundToURL("index.html")))}));