mirror of
https://github.com/SrIzan10/vdo.ninja.git
synced 2026-05-01 11:05:24 +00:00
minor fixes for v24
This commit is contained in:
60
check.html
60
check.html
@@ -425,68 +425,66 @@
|
||||
if ("stats" in e.data) {
|
||||
var out = "";
|
||||
|
||||
for (var someValue in e.data.stats.inbound_stats) {
|
||||
out += printValues(e.data.stats.inbound_stats[someValue]);
|
||||
if (e.data.stats.inbound_stats[someValue] && e.data.stats.inbound_stats[someValue].info){
|
||||
|
||||
for (var someValue in e.data.stats.inbound) {
|
||||
out += printValues(e.data.stats.inbound[someValue]);
|
||||
if (e.data.stats.inbound[someValue] && e.data.stats.inbound[someValue].info){
|
||||
if (!statsSent){
|
||||
statsSent = e.data.stats.inbound_stats[someValue];
|
||||
statsSent = e.data.stats.inbound[someValue];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var someValue in e.data.stats.outbound_stats) {
|
||||
if (e.data.stats.outbound_stats[someValue].quality_limitation_reason){
|
||||
if (quality_reason != e.data.stats.outbound_stats[someValue].quality_limitation_reason) {
|
||||
quality_reason = e.data.stats.outbound_stats[someValue].quality_limitation_reason;
|
||||
for (var someValue in e.data.stats.outbound) {
|
||||
if (e.data.stats.outbound[someValue].quality_limitation_reason){
|
||||
if (quality_reason != e.data.stats.outbound[someValue].quality_limitation_reason) {
|
||||
quality_reason = e.data.stats.outbound[someValue].quality_limitation_reason;
|
||||
logData({"QLR": quality_reason});
|
||||
}
|
||||
}
|
||||
|
||||
if (e.data.stats.outbound_stats[someValue].encoder){
|
||||
if (encoder != e.data.stats.outbound_stats[someValue].encoder) {
|
||||
encoder = e.data.stats.outbound_stats[someValue].encoder;
|
||||
if (e.data.stats.outbound[someValue].encoder){
|
||||
if (encoder != e.data.stats.outbound[someValue].encoder) {
|
||||
encoder = e.data.stats.outbound[someValue].encoder;
|
||||
logData({"encoder":encoder});
|
||||
}
|
||||
} else if (e.data.stats.outbound_stats[someValue].video_codec){
|
||||
if (encoder != e.data.stats.outbound_stats[someValue].video_codec) {
|
||||
encoder = e.data.stats.outbound_stats[someValue].video_codec;
|
||||
} else if (e.data.stats.outbound[someValue].video_codec){
|
||||
if (encoder != e.data.stats.outbound[someValue].video_codec) {
|
||||
encoder = e.data.stats.outbound[someValue].video_codec;
|
||||
logData({"encoder":encoder});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var key in e.data.stats.inbound_stats[streamID]){
|
||||
if (typeof e.data.stats.inbound_stats[streamID][key] == "object"){
|
||||
if ("Bitrate_in_kbps" in e.data.stats.inbound_stats[streamID][key]){
|
||||
var bitrate = e.data.stats.inbound_stats[streamID][key]["Bitrate_in_kbps"];
|
||||
for (var key in e.data.stats.inbound[streamID]){
|
||||
if (typeof e.data.stats.inbound[streamID][key] == "object"){
|
||||
if ("Bitrate_in_kbps" in e.data.stats.inbound[streamID][key]){
|
||||
var bitrate = e.data.stats.inbound[streamID][key]["Bitrate_in_kbps"];
|
||||
updateData("bitrate", bitrate);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
if ("Jitter_Buffer_ms" in e.data.stats.inbound_stats[streamID][key]){
|
||||
var buffer = e.data.stats.inbound_stats[streamID][key]["Jitter_Buffer_ms"];
|
||||
if ("Jitter_Buffer_ms" in e.data.stats.inbound[streamID][key]){
|
||||
var buffer = e.data.stats.inbound[streamID][key]["Jitter_Buffer_ms"];
|
||||
updateData("buffer", buffer);
|
||||
} else if ("Buffer_Delay_in_ms" in e.data.stats.inbound_stats[streamID][key]){
|
||||
var buffer = e.data.stats.inbound_stats[streamID][key]["Buffer_Delay_in_ms"];
|
||||
} else if ("Buffer_Delay_in_ms" in e.data.stats.inbound[streamID][key]){
|
||||
var buffer = e.data.stats.inbound[streamID][key]["Buffer_Delay_in_ms"];
|
||||
updateData("buffer", buffer);
|
||||
} else if ("Added_Buffer_Delay_ms" in e.data.stats.inbound_stats[streamID][key]){
|
||||
} else if ("Added_Buffer_Delay_ms" in e.data.stats.inbound[streamID][key]){
|
||||
console.log("Added_Buffer_Delay_ms");
|
||||
var buffer = e.data.stats.inbound_stats[streamID][key]["Added_Buffer_Delay_ms"];
|
||||
var buffer = e.data.stats.inbound[streamID][key]["Added_Buffer_Delay_ms"];
|
||||
updateData("buffer", buffer);
|
||||
}
|
||||
|
||||
if ("packetLoss_in_percentage" in e.data.stats.inbound_stats[streamID][key]){
|
||||
var packetloss = e.data.stats.inbound_stats[streamID][key]["packetLoss_in_percentage"];
|
||||
if ("packetLoss_in_percentage" in e.data.stats.inbound[streamID][key]){
|
||||
var packetloss = e.data.stats.inbound[streamID][key]["packetLoss_in_percentage"];
|
||||
if (packetloss != undefined) {
|
||||
packetloss = packetloss.toFixed(2);
|
||||
updateData("packetloss", packetloss);
|
||||
}
|
||||
}
|
||||
|
||||
if ("Resolution" in e.data.stats.inbound_stats[streamID][key]){
|
||||
var resolution = e.data.stats.inbound_stats[streamID][key]["Resolution"];
|
||||
if ("Resolution" in e.data.stats.inbound[streamID][key]){
|
||||
var resolution = e.data.stats.inbound[streamID][key]["Resolution"];
|
||||
|
||||
if (previousResolution != resolution) {
|
||||
previousResolution = resolution;
|
||||
|
||||
107
debug.html
Normal file
107
debug.html
Normal file
@@ -0,0 +1,107 @@
|
||||
<html>
|
||||
<body>
|
||||
<input type='text' id="inputbar" style="position:absolute;bottom:40px;width:88%"/>
|
||||
<input type='text' id="inputbar2" style="color:red;position:absolute;bottom:10px;width:88%"/>
|
||||
<script>
|
||||
var debugSocket = null;
|
||||
|
||||
var connecting = false;
|
||||
var failedCount = 0;
|
||||
|
||||
function getCurrentTimeFormatted() {
|
||||
const now = new Date();
|
||||
|
||||
const hours = String(now.getHours()).padStart(2, '0');
|
||||
const minutes = String(now.getMinutes()).padStart(2, '0');
|
||||
const seconds = String(now.getSeconds()).padStart(2, '0');
|
||||
const milliseconds = String(now.getMilliseconds()).padStart(3, '0');
|
||||
|
||||
return `${hours}:${minutes}:${seconds}.${milliseconds}`;
|
||||
}
|
||||
var lastTime = 0;
|
||||
function connect(){
|
||||
clearTimeout(connecting);
|
||||
if (debugSocket){
|
||||
if (debugSocket.readyState === debugSocket.OPEN){return;}
|
||||
try{
|
||||
debugSocket.close();
|
||||
} catch(e){}
|
||||
}
|
||||
debugSocket = new WebSocket("wss://debug.vdo.ninja:443");
|
||||
|
||||
debugSocket.onclose = function (){
|
||||
clearTimeout(connecting);
|
||||
connecting = setTimeout(function(){connect();},100);
|
||||
};
|
||||
|
||||
debugSocket.addEventListener('message', function (event) {
|
||||
|
||||
var msg = JSON.parse(event.data);
|
||||
var delta = false;
|
||||
if (msg.time){
|
||||
delta = msg.time - lastTime;
|
||||
lastTime = msg.time;
|
||||
}
|
||||
if (msg.type){
|
||||
if (msg.log){
|
||||
console.log(getCurrentTimeFormatted()+" " + delta + ": ", msg.log);
|
||||
} else if (msg.warn){
|
||||
console.warn(getCurrentTimeFormatted()+" " + delta + ": ", msg.warn);
|
||||
} else if (msg.error){
|
||||
console.error(getCurrentTimeFormatted()+" " + delta + ": ", msg.error);
|
||||
} else if (msg.type=="log"){
|
||||
console.log(getCurrentTimeFormatted()+" " + delta + ": ", msg.msg);
|
||||
} else if (msg.type=="warn"){
|
||||
console.warn(getCurrentTimeFormatted()+" " + delta + ": ", msg.msg);
|
||||
} else if (msg.error=="error"){
|
||||
console.error(getCurrentTimeFormatted()+" " + delta + ": ", msg.msg);
|
||||
} else {
|
||||
console.log(msg);
|
||||
}
|
||||
} else {
|
||||
console.error(msg);
|
||||
}
|
||||
if (msg.line){
|
||||
console.log(msg.line);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
connect();
|
||||
|
||||
function send(cmd){
|
||||
debugSocket.send(JSON.stringify({cmd:cmd}));
|
||||
}
|
||||
function log(cmd){
|
||||
debugSocket.send(JSON.stringify({err:cmd}));
|
||||
}
|
||||
|
||||
var input = document.getElementById("inputbar");
|
||||
|
||||
// Execute a function when the user presses a key on the keyboard
|
||||
input.addEventListener("keypress", function(event) {
|
||||
// If the user presses the "Enter" key on the keyboard
|
||||
if (event.key === "Enter") {
|
||||
// Cancel the default action, if needed
|
||||
event.preventDefault();
|
||||
// Trigger the button element with a click
|
||||
send(event.target.value);
|
||||
}
|
||||
});
|
||||
|
||||
var input2 = document.getElementById("inputbar2");
|
||||
|
||||
// Execute a function when the user presses a key on the keyboard
|
||||
input2.addEventListener("keypress", function(event) {
|
||||
// If the user presses the "Enter" key on the keyboard
|
||||
if (event.key === "Enter") {
|
||||
// Cancel the default action, if needed
|
||||
event.preventDefault();
|
||||
// Trigger the button element with a click
|
||||
log(event.target.value);
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -127,7 +127,7 @@
|
||||
|
||||
if ("stats" in e.data) {
|
||||
var now = new Date(); //Used for "Added" column and to remove stale viewers
|
||||
for (var viewer in e.data.stats.outbound_stats) {
|
||||
for (var viewer in e.data.stats.outbound) {
|
||||
//Check to see if a row exists for this viewier, if not then its a new viewer and we should create a row
|
||||
if ($("#vdon_viewer_" + viewer).length == 0) {
|
||||
var h = now.getHours();
|
||||
@@ -137,26 +137,26 @@
|
||||
}
|
||||
//Insert/update stats
|
||||
//Initially objects can be available but without any attributes, check they exist and ignore till the basics are available
|
||||
if (e.data.stats.outbound_stats[viewer] == undefined) continue;
|
||||
if (e.data.stats.outbound_stats[viewer].info == undefined) continue;
|
||||
if (e.data.stats.outbound[viewer] == undefined) continue;
|
||||
if (e.data.stats.outbound[viewer].info == undefined) continue;
|
||||
//Checking these exist as not all attributes are available straight away when stats are created
|
||||
if (e.data.stats.outbound_stats[viewer].info.label != undefined) {
|
||||
$("#vdon_viewer_" + viewer).find('.vdon_viewer_label').text(e.data.stats.outbound_stats[viewer].info.label);
|
||||
if (e.data.stats.outbound[viewer].info.label != undefined) {
|
||||
$("#vdon_viewer_" + viewer).find('.vdon_viewer_label').text(e.data.stats.outbound[viewer].info.label);
|
||||
}
|
||||
if (e.data.stats.outbound_stats[viewer].quality_Limitation_Reason != undefined) {
|
||||
$("#vdon_viewer_" + viewer).find('.vdon_viewer_qlr').text(e.data.stats.outbound_stats[viewer].quality_Limitation_Reason);
|
||||
if (e.data.stats.outbound[viewer].quality_Limitation_Reason != undefined) {
|
||||
$("#vdon_viewer_" + viewer).find('.vdon_viewer_qlr').text(e.data.stats.outbound[viewer].quality_Limitation_Reason);
|
||||
}
|
||||
if (e.data.stats.outbound_stats[viewer].resolution != undefined) {
|
||||
$("#vdon_viewer_" + viewer).find('.vdon_viewer_resolution').text(e.data.stats.outbound_stats[viewer].resolution);
|
||||
if (e.data.stats.outbound[viewer].resolution != undefined) {
|
||||
$("#vdon_viewer_" + viewer).find('.vdon_viewer_resolution').text(e.data.stats.outbound[viewer].resolution);
|
||||
}
|
||||
if (e.data.stats.outbound_stats[viewer].info.platform != undefined) {
|
||||
$("#vdon_viewer_" + viewer).find('.vdon_viewer_platform').text(e.data.stats.outbound_stats[viewer].info.platform);
|
||||
if (e.data.stats.outbound[viewer].info.platform != undefined) {
|
||||
$("#vdon_viewer_" + viewer).find('.vdon_viewer_platform').text(e.data.stats.outbound[viewer].info.platform);
|
||||
}
|
||||
if (e.data.stats.outbound_stats[viewer].encoder != undefined) {
|
||||
$("#vdon_viewer_" + viewer).find('.vdon_viewer_encoder').text(e.data.stats.outbound_stats[viewer].encoder);
|
||||
if (e.data.stats.outbound[viewer].encoder != undefined) {
|
||||
$("#vdon_viewer_" + viewer).find('.vdon_viewer_encoder').text(e.data.stats.outbound[viewer].encoder);
|
||||
}
|
||||
if (e.data.stats.outbound_stats[viewer].info.useragent != undefined) {
|
||||
$("#vdon_viewer_" + viewer).find('.vdon_viewer_useragent').text(e.data.stats.outbound_stats[viewer].info.useragent);
|
||||
if (e.data.stats.outbound[viewer].info.useragent != undefined) {
|
||||
$("#vdon_viewer_" + viewer).find('.vdon_viewer_useragent').text(e.data.stats.outbound[viewer].info.useragent);
|
||||
}
|
||||
$("#vdon_viewer_" + viewer).data('last', now.getTime()); //Used below to remove old viewers
|
||||
}
|
||||
|
||||
@@ -167,9 +167,9 @@ function loadIframe(url=false){ // this is pretty important if you want to avoi
|
||||
var out = "<br />total_inbound_connections:"+e.data.stats.total_inbound_connections;
|
||||
out += "<br />total_outbound_connections:"+e.data.stats.total_outbound_connections;
|
||||
|
||||
for (var streamID in e.data.stats.inbound_stats){
|
||||
for (var streamID in e.data.stats.inbound){
|
||||
out += "<br /><br /><b>streamID:</b> "+streamID+"<br />";
|
||||
out += printValues(e.data.stats.inbound_stats[streamID]);
|
||||
out += printValues(e.data.stats.inbound[streamID]);
|
||||
}
|
||||
|
||||
outputWindow.innerHTML = out;
|
||||
|
||||
@@ -126,9 +126,9 @@
|
||||
var out = "<br />total_inbound_connections:"+e.data.stats.total_inbound_connections;
|
||||
out += "<br />total_outbound_connections:"+e.data.stats.total_outbound_connections;
|
||||
|
||||
for (var streamID in e.data.stats.inbound_stats){
|
||||
for (var streamID in e.data.stats.inbound){
|
||||
out += "<br /><br /><b>streamID:</b> "+streamID+"<br />";
|
||||
out += printValues(e.data.stats.inbound_stats[streamID]);
|
||||
out += printValues(e.data.stats.inbound[streamID]);
|
||||
}
|
||||
logOutput(out)
|
||||
consolelog = false;
|
||||
|
||||
@@ -92,7 +92,7 @@
|
||||
|
||||
<script type="text/javascript" crossorigin="anonymous" src="./thirdparty/CodecsHandler.js?ver=9"></script>
|
||||
<script type="text/javascript" crossorigin="anonymous" src="./thirdparty/aes.js"></script>
|
||||
<script type="text/javascript" crossorigin="anonymous" src="./webrtc.js?ver=738"></script>
|
||||
<script type="text/javascript" crossorigin="anonymous" src="./webrtc.js?ver=761"></script>
|
||||
<input id="zoomSlider" type="range" style="display: none;" />
|
||||
<span id="electronDragZone" style="pointer-events: none; z-index:-10; position:absolute;top:0;left:0;width:100%;height:2%;-webkit-app-region: drag;min-height:20px;"></span>
|
||||
<div id="header">
|
||||
@@ -2567,7 +2567,7 @@
|
||||
// if (!window.location.search){document.body.innerHTML = "";} // uncomment this line, if you wish to try it.
|
||||
|
||||
var session = WebRTC.Media; // session is a required global variable if configuring manually. Run before loading main.js but after webrtc.js.
|
||||
session.version = "24.4";
|
||||
session.version = "24.5";
|
||||
session.streamID = session.generateStreamID(); // randomly generates a streamID for this session. You can set your own programmatically if needed
|
||||
|
||||
session.defaultPassword = "someEncryptionKey123"; // Change this password if self-deploying for added security/privacy
|
||||
@@ -2684,11 +2684,11 @@
|
||||
// session.decrypted = session.decodeInvite("U2FsdGVkX1+d58DFIHoO3EQZSuX86ch4PqW2ouztnJ0="); // get a code from invite.cam
|
||||
|
||||
</script>
|
||||
<script type="text/javascript" crossorigin="anonymous" id="lib-js" src="./lib.js?ver=1060"></script>
|
||||
<script type="text/javascript" crossorigin="anonymous" id="lib-js" src="./lib.js?ver=1066"></script>
|
||||
<!--
|
||||
// If you wish to change branding, blank offers a good clean start.
|
||||
<script type="text/javascript" id="main-js" src="./main.js" data-translation="blank"></script>
|
||||
-->
|
||||
<script type="text/javascript" crossorigin="anonymous" id="main-js" src="./main.js?ver=785"></script>
|
||||
<script type="text/javascript" crossorigin="anonymous" id="main-js" src="./main.js?ver=789"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
72
lib.js
72
lib.js
@@ -3388,7 +3388,9 @@ function setupIncomingScreenTracking(v, UUID){ // SCREEN element.
|
||||
v.addEventListener('volumechange',function(e){
|
||||
var muteState = checkMuteState(UUID);
|
||||
if (this.muted && (this.muted !== muteState)){
|
||||
this.usermuted = true;
|
||||
this.usermuted = 1;
|
||||
} else if (!this.muted && (this.muted !== muteState)){
|
||||
this.usermuted = 2;
|
||||
} else if (!this.muted){
|
||||
this.usermuted = false;
|
||||
}
|
||||
@@ -3784,7 +3786,9 @@ function setupIncomingVideoTracking(v, UUID){ // video element.
|
||||
v.addEventListener('volumechange',function(e){
|
||||
var muteState = checkMuteState(UUID);
|
||||
if (this.muted && (this.muted !== muteState)){
|
||||
this.usermuted = true;
|
||||
this.usermuted = 1;
|
||||
} else if (!this.muted && (this.muted !== muteState)){
|
||||
this.usermuted = 2;
|
||||
} else if (!this.muted){
|
||||
this.usermuted = false;
|
||||
}
|
||||
@@ -10375,11 +10379,12 @@ function printValues(obj,sort=false) { // see: printViewStats
|
||||
value = "<a href='https://whatismyipaddress.com/ip/" + value + "' target='_blank'>" + value + "</a>";
|
||||
}
|
||||
else if ((key == 'local_ip_blocking') && value){
|
||||
console.warn("Your system or connection is blocking p2p traffic");
|
||||
value = "⚠️ You're blocking";
|
||||
hint = 'no direct p2p connection made because of YOUR browser or system setting';
|
||||
}
|
||||
else if ((key == 'remote_ip_blocking') && value) {
|
||||
console.error("??");
|
||||
console.warn("A remote client is blocking p2p traffic");
|
||||
value = "⚠️ They're blocking"
|
||||
hint = 'no direct p2p connection made because of THEIR browser or system setting';
|
||||
}
|
||||
@@ -12073,6 +12078,7 @@ function postMessageIframe(iFrameEle, message){ // iframes seem to only have the
|
||||
|
||||
function toggleSpeakerMute(apply = false) { // TODO: I need to have this be MUTE, toggle, with volume not touched.
|
||||
|
||||
|
||||
if (CtrlPressed) {
|
||||
resetupAudioOut();
|
||||
}
|
||||
@@ -14332,7 +14338,8 @@ function applyMuteState(UUID){ // this is the mute state of PLAYBACK audio; not
|
||||
}
|
||||
|
||||
if (session.rpcs[UUID].videoElement){
|
||||
if (session.rpcs[UUID].videoElement && session.rpcs[UUID].videoElement.usermuted===true){return "usermuted true";}
|
||||
if (session.rpcs[UUID].videoElement && session.rpcs[UUID].videoElement.usermuted===1){return "usermuted 1";}
|
||||
if (session.rpcs[UUID].videoElement && session.rpcs[UUID].videoElement.usermuted===2){return "usermuted 2";}
|
||||
session.rpcs[UUID].videoElement.muted = muteOutcome;
|
||||
}
|
||||
|
||||
@@ -14694,8 +14701,8 @@ async function publishScreen() {
|
||||
getById("screensharebutton").className = "float";
|
||||
}
|
||||
getById("controlButtons").classList.remove("hidden");
|
||||
getById("helpbutton").style.display = "inherit";
|
||||
getById("reportbutton").style.display = "";
|
||||
//getById("helpbutton").style.display = "inherit";
|
||||
//getById("reportbutton").style.display = "";
|
||||
} else if (session.cleanish && session.recordLocal!==false){
|
||||
getById("recordLocalbutton").classList.remove("hidden");
|
||||
getById("mutebutton").classList.add("hidden");
|
||||
@@ -15157,8 +15164,8 @@ function publishWebcam(btn = false, miconly=false) {
|
||||
}
|
||||
}
|
||||
getById("controlButtons").classList.remove("hidden");
|
||||
getById("helpbutton").style.display = "inherit";
|
||||
getById("reportbutton").style.display = "";
|
||||
//getById("helpbutton").style.display = "inherit";
|
||||
//getById("reportbutton").style.display = "";
|
||||
} else if (session.cleanish && session.recordLocal!==false){
|
||||
getById("recordLocalbutton").classList.remove("hidden");
|
||||
getById("mutebutton").classList.add("hidden");
|
||||
@@ -15764,8 +15771,8 @@ session.publishIFrame = function(iframeURL){
|
||||
getById("hangupbutton").className="float";
|
||||
getById("controlButtons").classList.remove("hidden");
|
||||
getById('sharefilebutton').classList.remove("hidden"); // we won't override "display:none", if set, though.
|
||||
getById("helpbutton").style.display = "inherit";
|
||||
getById("reportbutton").style.display = "";
|
||||
//getById("helpbutton").style.display = "inherit";
|
||||
//getById("reportbutton").style.display = "";
|
||||
} else {
|
||||
getById("controlButtons").classList.add("hidden");
|
||||
}
|
||||
@@ -20048,7 +20055,7 @@ function gotDevicesNew(deviceInfos, miconly=false) {
|
||||
};
|
||||
|
||||
audioInputSelect.onmousedown = function(event) {
|
||||
console.log("mouse down");
|
||||
//console.log("mouse down");
|
||||
if (!this.classList.contains("expanded")){
|
||||
event.preventDefault();
|
||||
this.classList.add('expanded');
|
||||
@@ -20060,7 +20067,7 @@ function gotDevicesNew(deviceInfos, miconly=false) {
|
||||
};
|
||||
|
||||
audioInputSelect.onmouseup = function(event) {
|
||||
console.log("mouse up");
|
||||
//console.log("mouse up");
|
||||
if (CtrlPressed || audioInputSelect.init) {
|
||||
this.focus();
|
||||
event.preventDefault();
|
||||
@@ -20109,7 +20116,7 @@ function gotDevicesNew(deviceInfos, miconly=false) {
|
||||
event.stopPropagation();
|
||||
|
||||
if (!audioInputSelect.init){
|
||||
console.log("utSelect.init iS : "+ audioInputSelect.init);
|
||||
// console.log("utSelect.init iS : "+ audioInputSelect.init);
|
||||
event1 = new Event('change', {
|
||||
'bubbles': true, // This ensures the event bubbles up through the DOM
|
||||
'cancelable': true // This allows the event to be cancelable
|
||||
@@ -20118,7 +20125,7 @@ function gotDevicesNew(deviceInfos, miconly=false) {
|
||||
|
||||
}
|
||||
audioInputSelect.init = null;
|
||||
console.log("utSelect.init false");
|
||||
// console.log("utSelect.init false");
|
||||
};
|
||||
|
||||
audioInputSelect.onchange = function() {
|
||||
@@ -20180,7 +20187,7 @@ function gotDevicesNew(deviceInfos, miconly=false) {
|
||||
|
||||
option.onmouseover = function(){
|
||||
if (MousePressed && !CtrlPressed && !audioInputSelect.init && !audioInputSelect.CtrlPressed){
|
||||
console.log("MousePressed 2:"+MousePressed);
|
||||
//console.log("MousePressed 2:"+MousePressed);
|
||||
//Array.from(audioInputSelect.options).forEach(opt => opt.selected = false);
|
||||
//this.selected = true;
|
||||
if (audioInputSelect.init==2){
|
||||
@@ -20208,7 +20215,7 @@ function gotDevicesNew(deviceInfos, miconly=false) {
|
||||
audioInputSelect.appendChild(option);
|
||||
option.onmouseover = function(){
|
||||
if (MousePressed && !CtrlPressed && !audioInputSelect.CtrlPressed){
|
||||
console.log("MousePressed 1:"+MousePressed);
|
||||
//console.log("MousePressed 1:"+MousePressed);
|
||||
//Array.from(audioInputSelect.options).forEach(opt => opt.selected = false);
|
||||
//this.selected = true;
|
||||
if (audioInputSelect.init==2){
|
||||
@@ -20216,7 +20223,7 @@ function gotDevicesNew(deviceInfos, miconly=false) {
|
||||
} else if (audioInputSelect.init){
|
||||
audioInputSelect.init = 2;
|
||||
}
|
||||
console.log("utSelect.init false");
|
||||
//console.log("utSelect.init false");
|
||||
}
|
||||
audioInputSelect.CtrlPressed = audioInputSelect.CtrlPressed || CtrlPressed;
|
||||
}
|
||||
@@ -26461,8 +26468,8 @@ session.hostFile = function(ele, event){ // webcam stream is used to generated a
|
||||
getById('sharefilebutton').classList.remove("hidden"); // we won't override "display:none", if set, though.
|
||||
getById("hangupbutton").className="float";
|
||||
getById("controlButtons").classList.remove("hidden");
|
||||
getById("helpbutton").style.display = "inherit";
|
||||
getById("reportbutton").style.display = "";
|
||||
//getById("helpbutton").style.display = "inherit";
|
||||
//getById("reportbutton").style.display = "";
|
||||
} else {
|
||||
getById("controlButtons").classList.add("hidden");
|
||||
}
|
||||
@@ -26632,8 +26639,8 @@ session.publishFile = function(ele, event){ // webcam stream is used to generate
|
||||
getById('sharefilebutton').classList.remove("hidden"); // we won't override "display:none", if set, though.
|
||||
getById("hangupbutton").className="float";
|
||||
getById("controlButtons").classList.remove("hidden");
|
||||
getById("helpbutton").style.display = "inherit";
|
||||
getById("reportbutton").style.display = "";
|
||||
//getById("helpbutton").style.display = "inherit";
|
||||
//getById("reportbutton").style.display = "";
|
||||
} else {
|
||||
getById("controlButtons").classList.add("hidden");
|
||||
}
|
||||
@@ -33076,7 +33083,7 @@ function sendChatMessage(chatMsg = false, bc = false) { // filtered + visual
|
||||
var listMsg = null;
|
||||
for (var UUID in session.rpcs){
|
||||
if (session.rpcs[UUID].label){
|
||||
listMsg = UUID+": "+session.rpcs[UUID].label
|
||||
listMsg = UUID+": "+session.rpcs[UUID].label;
|
||||
} else if (session.directorList.indexOf(UUID)>=0){
|
||||
listMsg = UUID+": Director";
|
||||
} else {
|
||||
@@ -35422,12 +35429,12 @@ async function recordLocalVideo(action = null, videoKbps = false, remote=false,
|
||||
} else {
|
||||
var video = session.videoElement;
|
||||
}
|
||||
log(video.id);
|
||||
|
||||
if (!video){
|
||||
errorlog("video not found");
|
||||
warnlog("video not found");
|
||||
return;
|
||||
}
|
||||
log(video.id);
|
||||
|
||||
if ("recording" in video) {
|
||||
if (action == "estop") {
|
||||
@@ -40043,6 +40050,21 @@ function setupCommands(){
|
||||
// sendChatMessage // this would add it to the chat message
|
||||
return true;
|
||||
};
|
||||
|
||||
commands.sendChatMessage = function(value=null,value2=null){
|
||||
sendChatMessage(value);
|
||||
return true;
|
||||
};
|
||||
|
||||
commands.showChatMessage = function(value=null,value2=null){
|
||||
getChatMessage(value);
|
||||
return true;
|
||||
};
|
||||
|
||||
commands.showChatOverlay = function(value=null,value2=null){
|
||||
getChatMessage(value,false,false,true);
|
||||
return true;
|
||||
};
|
||||
|
||||
commands.startRoomTimer = function(value=null,value2=null){
|
||||
getById("globalTimerDirectorToggle").value = 0; // reset
|
||||
@@ -41862,4 +41884,4 @@ function createControlBoxScreenshare(UUID, soloLink, streamID) {
|
||||
|
||||
initSceneList(UUID);
|
||||
pokeIframeAPI("control-box", true, UUID);
|
||||
}
|
||||
}
|
||||
9
main.js
9
main.js
@@ -404,10 +404,7 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
|
||||
if (session.whepWait<0){
|
||||
session.whepWait = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
if (urlParams.has('whippush') || urlParams.has('whipout') || urlParams.has('pushwhip')) { // URL or data:base64 image. Becomes local to this viewer only. This is like &avatar, but slightly different. Just CSS in this case
|
||||
session.whipOutput = urlParams.get('whippush') || urlParams.get('whipout') || urlParams.get('pushwhip') || null;
|
||||
@@ -5737,11 +5734,11 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
|
||||
if (("getFreshStats" in e.data)){ // takes a second to query.
|
||||
var stats = {};
|
||||
try {
|
||||
stats.inbound_stats = {};
|
||||
stats.inbound = {};
|
||||
stats.total_outbound_connections = Object.keys(session.pcs).length;
|
||||
stats.total_inbound_connections = Object.keys(session.rpcs).length;
|
||||
for (var i in session.rpcs) {
|
||||
stats.inbound_stats[session.rpcs[i].streamID] = session.rpcs[i].stats;
|
||||
stats.inbound[session.rpcs[i].streamID] = session.rpcs[i].stats;
|
||||
}
|
||||
for (var uuid in session.pcs) {
|
||||
setTimeout(function(UUID) {
|
||||
|
||||
@@ -406,62 +406,62 @@
|
||||
if ("stats" in e.data) {
|
||||
var out = "";
|
||||
|
||||
for (var someValue in e.data.stats.inbound_stats) {
|
||||
out += printValues(e.data.stats.inbound_stats[someValue]);
|
||||
for (var someValue in e.data.stats.inbound) {
|
||||
out += printValues(e.data.stats.inbound[someValue]);
|
||||
}
|
||||
|
||||
for (var someValue in e.data.stats.outbound_stats) {
|
||||
if (e.data.stats.outbound_stats[someValue].quality_limitation_reason){
|
||||
if (quality_reason != e.data.stats.outbound_stats[someValue].quality_limitation_reason) {
|
||||
quality_reason = e.data.stats.outbound_stats[someValue].quality_limitation_reason;
|
||||
for (var someValue in e.data.stats.outbound) {
|
||||
if (e.data.stats.outbound[someValue].quality_limitation_reason){
|
||||
if (quality_reason != e.data.stats.outbound[someValue].quality_limitation_reason) {
|
||||
quality_reason = e.data.stats.outbound[someValue].quality_limitation_reason;
|
||||
logData("Quality Limitation Reason", quality_reason);
|
||||
}
|
||||
}
|
||||
|
||||
if (e.data.stats.outbound_stats[someValue].encoder){
|
||||
if (encoder != e.data.stats.outbound_stats[someValue].encoder) {
|
||||
encoder = e.data.stats.outbound_stats[someValue].encoder;
|
||||
if (e.data.stats.outbound[someValue].encoder){
|
||||
if (encoder != e.data.stats.outbound[someValue].encoder) {
|
||||
encoder = e.data.stats.outbound[someValue].encoder;
|
||||
logData("Encoder used", encoder);
|
||||
}
|
||||
} else if (e.data.stats.outbound_stats[someValue].video_codec){
|
||||
if (encoder != e.data.stats.outbound_stats[someValue].video_codec) {
|
||||
encoder = e.data.stats.outbound_stats[someValue].video_codec;
|
||||
} else if (e.data.stats.outbound[someValue].video_codec){
|
||||
if (encoder != e.data.stats.outbound[someValue].video_codec) {
|
||||
encoder = e.data.stats.outbound[someValue].video_codec;
|
||||
logData("Encoder used", encoder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var key in e.data.stats.inbound_stats[streamID]){
|
||||
if (typeof e.data.stats.inbound_stats[streamID][key] == "object"){
|
||||
//console.error(e.data.stats.inbound_stats[streamID][key]);
|
||||
if ("Bitrate_in_kbps" in e.data.stats.inbound_stats[streamID][key]){
|
||||
var bitrate = e.data.stats.inbound_stats[streamID][key]["Bitrate_in_kbps"];
|
||||
for (var key in e.data.stats.inbound[streamID]){
|
||||
if (typeof e.data.stats.inbound[streamID][key] == "object"){
|
||||
//console.error(e.data.stats.inbound[streamID][key]);
|
||||
if ("Bitrate_in_kbps" in e.data.stats.inbound[streamID][key]){
|
||||
var bitrate = e.data.stats.inbound[streamID][key]["Bitrate_in_kbps"];
|
||||
updateData("bitrate", bitrate);
|
||||
}
|
||||
|
||||
if ("Jitter_Buffer_ms" in e.data.stats.inbound_stats[streamID][key]){
|
||||
var buffer = e.data.stats.inbound_stats[streamID][key]["Jitter_Buffer_ms"];
|
||||
if ("Jitter_Buffer_ms" in e.data.stats.inbound[streamID][key]){
|
||||
var buffer = e.data.stats.inbound[streamID][key]["Jitter_Buffer_ms"];
|
||||
updateData("buffer", buffer);
|
||||
} else if ("Buffer_Delay_in_ms" in e.data.stats.inbound_stats[streamID][key]){
|
||||
var buffer = e.data.stats.inbound_stats[streamID][key]["Buffer_Delay_in_ms"];
|
||||
} else if ("Buffer_Delay_in_ms" in e.data.stats.inbound[streamID][key]){
|
||||
var buffer = e.data.stats.inbound[streamID][key]["Buffer_Delay_in_ms"];
|
||||
updateData("buffer", buffer);
|
||||
} else if ("Added_Buffer_Delay_ms" in e.data.stats.inbound_stats[streamID][key]){
|
||||
} else if ("Added_Buffer_Delay_ms" in e.data.stats.inbound[streamID][key]){
|
||||
console.log("Added_Buffer_Delay_ms");
|
||||
var buffer = e.data.stats.inbound_stats[streamID][key]["Added_Buffer_Delay_ms"];
|
||||
var buffer = e.data.stats.inbound[streamID][key]["Added_Buffer_Delay_ms"];
|
||||
updateData("buffer", buffer);
|
||||
}
|
||||
|
||||
|
||||
if ("packetLoss_in_percentage" in e.data.stats.inbound_stats[streamID][key]){
|
||||
var packetloss = e.data.stats.inbound_stats[streamID][key]["packetLoss_in_percentage"];
|
||||
if ("packetLoss_in_percentage" in e.data.stats.inbound[streamID][key]){
|
||||
var packetloss = e.data.stats.inbound[streamID][key]["packetLoss_in_percentage"];
|
||||
if (packetloss != undefined) {
|
||||
packetloss = packetloss.toFixed(2);
|
||||
updateData("packetloss", packetloss);
|
||||
}
|
||||
}
|
||||
|
||||
if ("Resolution" in e.data.stats.inbound_stats[streamID][key]){
|
||||
var resolution = e.data.stats.inbound_stats[streamID][key]["Resolution"];
|
||||
if ("Resolution" in e.data.stats.inbound[streamID][key]){
|
||||
var resolution = e.data.stats.inbound[streamID][key]["Resolution"];
|
||||
if (previousResolution != resolution) {
|
||||
previousResolution = resolution;
|
||||
logData("Resolution", resolution);
|
||||
|
||||
717
teststreams.html
Normal file
717
teststreams.html
Normal file
@@ -0,0 +1,717 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>IFRAME Example</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
||||
<meta content="text/html;charset=utf-8" http-equiv="Content-Type" />
|
||||
<style>
|
||||
body{
|
||||
padding:0;
|
||||
margin:0;
|
||||
background-color: rgb(68 72 74);
|
||||
}
|
||||
iframe {
|
||||
border:0;
|
||||
margin:0;
|
||||
padding:0;
|
||||
display:none;
|
||||
}
|
||||
#viewlink {
|
||||
width:400px;
|
||||
}
|
||||
#container {
|
||||
display:block;
|
||||
padding:0px;
|
||||
}
|
||||
input{
|
||||
padding:5px;
|
||||
margin:5px;
|
||||
}
|
||||
button{
|
||||
padding:5px;
|
||||
margin:5px;
|
||||
}
|
||||
video{
|
||||
max-width:300px;
|
||||
max-height:100px;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
|
||||
function loadIframe(){ // this is pretty important if you want to avoid camera permission popup problems. You can also call it automatically via: <body onload=>loadIframe();"> , but don't call it before the page loads.
|
||||
|
||||
var iframe = document.createElement("iframe");
|
||||
var iframeContainer = document.createElement("div");
|
||||
var iframesrc = document.getElementById("viewlink").value;
|
||||
iframe.allow = "document-domain;encrypted-media;sync-xhr;usb;web-share;cross-origin-isolated;accelerometer;midi;geolocation;autoplay;camera;microphone;fullscreen;picture-in-picture;display-capture;";
|
||||
|
||||
if (iframesrc==""){
|
||||
iframesrc="./";
|
||||
}
|
||||
|
||||
<!-- if (document.getElementById("clean").checked){ -->
|
||||
<!-- if (iframesrc.includes("?")){ -->
|
||||
<!-- iframesrc+='&'; -->
|
||||
<!-- } else { -->
|
||||
<!-- iframesrc+='?'; -->
|
||||
<!-- } -->
|
||||
<!-- iframesrc+="cleanoutput"; -->
|
||||
<!-- } -->
|
||||
<!-- if (document.getElementById("transparent").checked){ -->
|
||||
<!-- if (iframesrc.includes("?")){ -->
|
||||
<!-- iframesrc+='&'; -->
|
||||
<!-- } else { -->
|
||||
<!-- iframesrc+='?'; -->
|
||||
<!-- } -->
|
||||
<!-- iframesrc+="transparent"; -->
|
||||
<!-- } -->
|
||||
|
||||
<!-- if (document.getElementById("hidemenu").checked){ -->
|
||||
<!-- if (iframesrc.includes("?")){ -->
|
||||
<!-- iframesrc+='&'; -->
|
||||
<!-- } else { -->
|
||||
<!-- iframesrc+='?'; -->
|
||||
<!-- } -->
|
||||
<!-- iframesrc+="hidemenu"; -->
|
||||
<!-- } -->
|
||||
|
||||
iframe.src = iframesrc;
|
||||
|
||||
iframeContainer.appendChild(iframe);
|
||||
|
||||
var editURL = document.createElement("input");
|
||||
editURL.type = "text";
|
||||
editURL.value = iframesrc;
|
||||
iframeContainer.appendChild(editURL);
|
||||
|
||||
var button = document.createElement("button");
|
||||
button.innerHTML = "Update URL and reload";
|
||||
// button.onclick = function(){}; // "speaker" also works in the same way.
|
||||
iframeContainer.appendChild(button);
|
||||
|
||||
document.getElementById("container").appendChild(iframeContainer);
|
||||
|
||||
|
||||
<!-- var h3 = document.createElement("h3"); -->
|
||||
<!-- h3.innerText = "The following commands are exclusive to the IFRAME API."; -->
|
||||
<!-- iframeContainer.appendChild(h3); -->
|
||||
|
||||
var button = document.createElement("button");
|
||||
button.innerHTML = "Mute Speaker";
|
||||
button.onclick = function(){iframe.contentWindow.postMessage({"mute":true}, '*');}; // "speaker" also works in the same way.
|
||||
iframeContainer.appendChild(button);
|
||||
|
||||
var button = document.createElement("button");
|
||||
button.innerHTML = "Un-Mute Speaker";
|
||||
button.onclick = function(){iframe.contentWindow.postMessage({"mute":false}, '*');};
|
||||
iframeContainer.appendChild(button);
|
||||
|
||||
var button = document.createElement("button");
|
||||
button.innerHTML = "Toggle Speaker";
|
||||
button.onclick = function(){iframe.contentWindow.postMessage({"mute":"toggle"}, '*');}; // open to a better suggestion here.
|
||||
iframeContainer.appendChild(button);
|
||||
|
||||
var button = document.createElement("button");
|
||||
button.innerHTML = "Mute Mic";
|
||||
button.onclick = function(){iframe.contentWindow.postMessage({"mic":false}, '*');};
|
||||
iframeContainer.appendChild(button);
|
||||
|
||||
var button = document.createElement("button");
|
||||
button.innerHTML = "Un-Mute Mic";
|
||||
button.onclick = function(){iframe.contentWindow.postMessage({"mic":true}, '*');};
|
||||
iframeContainer.appendChild(button);
|
||||
|
||||
var button = document.createElement("button");
|
||||
button.innerHTML = "Toggle Mic";
|
||||
button.onclick = function(){iframe.contentWindow.postMessage({"mic":"toggle"}, '*');};
|
||||
iframeContainer.appendChild(button);
|
||||
|
||||
var button = document.createElement("button");
|
||||
button.innerHTML = "Mute Camera";
|
||||
button.onclick = function(){iframe.contentWindow.postMessage({"camera":false}, '*');};
|
||||
iframeContainer.appendChild(button);
|
||||
|
||||
var button = document.createElement("button");
|
||||
button.innerHTML = "Unmute Camera";
|
||||
button.onclick = function(){iframe.contentWindow.postMessage({"camera":true}, '*');};
|
||||
iframeContainer.appendChild(button);
|
||||
|
||||
var button = document.createElement("button");
|
||||
button.innerHTML = "Toggle Camera";
|
||||
button.onclick = function(){iframe.contentWindow.postMessage({"camera":"toggle"}, '*');};
|
||||
iframeContainer.appendChild(button);
|
||||
|
||||
var button = document.createElement("button");
|
||||
button.innerHTML = "Disconnect";
|
||||
button.onclick = function(){iframe.contentWindow.postMessage({"close":true}, '*');};
|
||||
iframeContainer.appendChild(button);
|
||||
|
||||
<!-- var button = document.createElement("button"); -->
|
||||
<!-- button.innerHTML = "0 Bitrate"; -->
|
||||
<!-- button.onclick = function(){iframe.contentWindow.postMessage({"bitrate":0, "target": "*"}, '*');}; -->
|
||||
<!-- iframeContainer.appendChild(button); -->
|
||||
|
||||
<!-- var button = document.createElement("button"); -->
|
||||
<!-- button.innerHTML = "Low Bitrate"; -->
|
||||
<!-- button.onclick = function(){iframe.contentWindow.postMessage({"bitrate":30, "target": "*"}, '*');}; -->
|
||||
<!-- iframeContainer.appendChild(button); -->
|
||||
|
||||
<!-- var button = document.createElement("button"); -->
|
||||
<!-- button.innerHTML = "High Bitrate"; -->
|
||||
<!-- button.onclick = function(){iframe.contentWindow.postMessage({"bitrate":5000, "target": "*"}, '*');}; -->
|
||||
<!-- iframeContainer.appendChild(button); -->
|
||||
|
||||
<!-- var button = document.createElement("button"); -->
|
||||
<!-- button.innerHTML = "Default Bitrate"; -->
|
||||
<!-- button.onclick = function(){iframe.contentWindow.postMessage({"bitrate":-1, "target": "*"}, '*');}; -->
|
||||
<!-- iframeContainer.appendChild(button); -->
|
||||
|
||||
var button = document.createElement("button");
|
||||
button.innerHTML = "Reload";
|
||||
button.onclick = function(){iframe.contentWindow.postMessage({"reload":true}, '*');};
|
||||
iframeContainer.appendChild(button);
|
||||
|
||||
<!-- var button = document.createElement("button"); -->
|
||||
<!-- button.innerHTML = "50% Volume"; -->
|
||||
<!-- button.onclick = function(){iframe.contentWindow.postMessage({"volume":0.5}, '*');}; -->
|
||||
<!-- iframeContainer.appendChild(button); -->
|
||||
|
||||
<!-- var button = document.createElement("button"); -->
|
||||
<!-- button.innerHTML = "100% Volume"; -->
|
||||
<!-- button.onclick = function(){iframe.contentWindow.postMessage({"volume":1.0}, '*');}; -->
|
||||
<!-- iframeContainer.appendChild(button); -->
|
||||
|
||||
<!-- var button = document.createElement("button"); -->
|
||||
<!-- button.innerHTML = "Pan Left"; -->
|
||||
<!-- button.title = "Requires &panning to be added to the view link"; -->
|
||||
<!-- button.onclick = function(){iframe.contentWindow.postMessage({"panning":0}, '*');}; -->
|
||||
<!-- iframeContainer.appendChild(button); -->
|
||||
|
||||
<!-- var button = document.createElement("button"); -->
|
||||
<!-- button.innerHTML = "Pan right"; -->
|
||||
<!-- button.title = "Requires &panning to be added to the view link"; -->
|
||||
<!-- button.onclick = function(){iframe.contentWindow.postMessage({"panning":180}, '*');}; -->
|
||||
<!-- iframeContainer.appendChild(button); -->
|
||||
|
||||
<!-- var button = document.createElement("button"); -->
|
||||
<!-- button.innerHTML = "Pan Center"; -->
|
||||
<!-- button.title = "Requires &panning to be added to the view link"; -->
|
||||
<!-- button.onclick = function(){iframe.contentWindow.postMessage({"panning":90}, '*');}; -->
|
||||
<!-- iframeContainer.appendChild(button); -->
|
||||
|
||||
<!-- var button = document.createElement("button"); -->
|
||||
<!-- button.innerHTML = "Request Loudness Levels"; -->
|
||||
<!-- button.onclick = function(){iframe.contentWindow.postMessage({"getLoudness":true}, '*');}; -->
|
||||
<!-- iframeContainer.appendChild(button); -->
|
||||
|
||||
<!-- var button = document.createElement("button"); -->
|
||||
<!-- button.innerHTML = "Stop Sending Loudness Levels"; -->
|
||||
<!-- button.onclick = function(){iframe.contentWindow.postMessage({"getLoudness":false}, '*');}; -->
|
||||
<!-- iframeContainer.appendChild(button); -->
|
||||
|
||||
<!-- var button = document.createElement("button"); -->
|
||||
<!-- button.innerHTML = "Get detailed state (if Director)"; -->
|
||||
<!-- button.onclick = function(){iframe.contentWindow.postMessage({"getDetailedState":true}, '*');}; -->
|
||||
<!-- iframeContainer.appendChild(button); -->
|
||||
|
||||
<!-- var button = document.createElement("button"); -->
|
||||
<!-- button.innerHTML = "Start Recording"; -->
|
||||
<!-- button.onclick = function(){iframe.contentWindow.postMessage({"record":true}, '*');}; -->
|
||||
<!-- iframeContainer.appendChild(button); -->
|
||||
|
||||
<!-- var button = document.createElement("button"); -->
|
||||
<!-- button.innerHTML = "Stop Recording"; -->
|
||||
<!-- button.onclick = function(){iframe.contentWindow.postMessage({"record":false}, '*');}; -->
|
||||
<!-- iframeContainer.appendChild(button); -->
|
||||
|
||||
<!-- var button = document.createElement("button"); -->
|
||||
<!-- button.innerHTML = "Say Hello"; -->
|
||||
<!-- button.onclick = function(){iframe.contentWindow.postMessage({"sendChat":"Hello!"}, '*');}; -->
|
||||
<!-- iframeContainer.appendChild(button); -->
|
||||
|
||||
<!-- var button = document.createElement("button"); -->
|
||||
<!-- button.innerHTML = "Send Keyframe"; -->
|
||||
<!-- button.onclick = function(){iframe.contentWindow.postMessage({"keyframe":true}, '*');}; -->
|
||||
<!-- iframeContainer.appendChild(button); -->
|
||||
|
||||
<!-- var button = document.createElement("button"); -->
|
||||
<!-- button.innerHTML = "Insert Style Sheet"; -->
|
||||
<!-- var stylesheet = "#main { zoom: 0.5;} video {float: left; margin: 0; padding: 0; } #info {display:none;}"; -->
|
||||
<!-- button.onclick = function(){iframe.contentWindow.postMessage({"style":stylesheet}, '*');}; -->
|
||||
<!-- iframeContainer.appendChild(button); -->
|
||||
|
||||
<!-- var button = document.createElement("button"); -->
|
||||
<!-- button.innerHTML = "get StreamIDs and labels"; -->
|
||||
<!-- button.onclick = function(){iframe.contentWindow.postMessage({"getStreamIDs":true}, '*');}; -->
|
||||
<!-- iframeContainer.appendChild(button); -->
|
||||
|
||||
<!-- var button = document.createElement("button"); -->
|
||||
<!-- button.innerHTML = "get media device list"; -->
|
||||
<!-- button.onclick = function(){iframe.contentWindow.postMessage({"getDeviceList":true}, '*');}; -->
|
||||
<!-- iframeContainer.appendChild(button); -->
|
||||
|
||||
<!-- var button = document.createElement("button"); -->
|
||||
<!-- button.innerHTML = "Start AutoMixer"; -->
|
||||
<!-- button.onclick = function(){iframe.contentWindow.postMessage({"automixer":true}, '*');}; -->
|
||||
<!-- iframeContainer.appendChild(button); -->
|
||||
|
||||
<!-- var button = document.createElement("button"); -->
|
||||
<!-- button.innerHTML = "Stop AutoMixer"; -->
|
||||
<!-- button.onclick = function(){iframe.contentWindow.postMessage({"automixer":false}, '*');}; -->
|
||||
<!-- iframeContainer.appendChild(button); -->
|
||||
|
||||
<!-- var button = document.createElement("button"); -->
|
||||
<!-- button.innerHTML = "ENABLE TALLY LIGHT"; -->
|
||||
<!-- button.onclick = function(){iframe.contentWindow.postMessage({"sceneState":true}, '*');}; -->
|
||||
<!-- iframeContainer.appendChild(button); -->
|
||||
|
||||
<!-- var button = document.createElement("button"); -->
|
||||
<!-- button.innerHTML = "STOP TALLY LIGHT"; -->
|
||||
<!-- button.onclick = function(){iframe.contentWindow.postMessage({"sceneState":false}, '*');}; -->
|
||||
<!-- iframeContainer.appendChild(button); -->
|
||||
|
||||
<!-- var button = document.createElement("button"); -->
|
||||
<!-- button.innerHTML = "Add Target Video"; -->
|
||||
<!-- button.onclick = function(){iframe.contentWindow.postMessage({"target":"*", "add":true, "settings": {"style": "width:640px;height:360px;float:left;border:10px solid red;display:block;"}}, '*');}; // target can be a stream ID or * for all. -->
|
||||
<!-- iframeContainer.appendChild(button); -->
|
||||
|
||||
<!-- var button = document.createElement("button"); -->
|
||||
<!-- button.innerHTML = "Remove Target Video"; -->
|
||||
<!-- button.onclick = function(){iframe.contentWindow.postMessage({"target":"*", "remove": true}, '*');}; // target can be a stream ID or * for all. -->
|
||||
<!-- iframeContainer.appendChild(button); -->
|
||||
|
||||
<!-- var button = document.createElement("button"); -->
|
||||
<!-- button.innerHTML = "previewWebcam()"; -->
|
||||
<!-- button.onclick = function(){iframe.contentWindow.postMessage({"function":"previewWebcam"}, '*');}; // publishScreen -->
|
||||
<!-- iframeContainer.appendChild(button); -->
|
||||
|
||||
<!-- var button = document.createElement("button"); -->
|
||||
<!-- button.innerHTML = "Change to Camera #2"; -->
|
||||
<!-- button.onclick = function(){iframe.contentWindow.postMessage({"changeVideoDevice":2}, '*');}; // change text of add camera button -->
|
||||
<!-- iframeContainer.appendChild(button); -->
|
||||
|
||||
<!-- var button = document.createElement("button"); -->
|
||||
<!-- button.innerHTML = "Change to Microphone #4"; -->
|
||||
<!-- button.onclick = function(){iframe.contentWindow.postMessage({"changeAudioDevice":4}, '*');}; // change text of add camera button -->
|
||||
<!-- iframeContainer.appendChild(button); -->
|
||||
|
||||
<!-- var button = document.createElement("button"); -->
|
||||
<!-- button.innerHTML = "eval('alert(\"DANGERUS\")'"; -->
|
||||
<!-- button.onclick = function(){iframe.contentWindow.postMessage({"function":"eval", "value":'alert(\"DANGERUS\")'}, '*');}; // publishScreen -->
|
||||
<!-- iframeContainer.appendChild(button); -->
|
||||
|
||||
<!-- var button = document.createElement("button"); -->
|
||||
<!-- button.innerHTML = "Change Add Camera text"; -->
|
||||
<!-- button.onclick = function(){iframe.contentWindow.postMessage({"function":"changeHTML", "target":"add_camera", "value":"NEW CAMERA TEXT"}, '*');}; // change text of add camera button -->
|
||||
<!-- iframeContainer.appendChild(button); -->
|
||||
|
||||
var button = document.createElement("button");
|
||||
button.innerHTML = "CLOSE IFRAME";
|
||||
button.onclick = function(){iframeContainer.parentNode.removeChild(iframeContainer);};
|
||||
iframeContainer.appendChild(button);
|
||||
|
||||
//////////// LISTEN FOR EVENTS
|
||||
|
||||
var eventMethod = window.addEventListener ? "addEventListener" : "attachEvent";
|
||||
var eventer = window[eventMethod];
|
||||
var messageEvent = eventMethod === "attachEvent" ? "onmessage" : "message";
|
||||
|
||||
var media = {};
|
||||
media.tracks = {};
|
||||
media.streams = {};
|
||||
|
||||
window.addEventListener('messageerror', e => {
|
||||
console.error(e);
|
||||
});
|
||||
|
||||
eventer(messageEvent, function (e) {
|
||||
if (e.source != iframe.contentWindow){return} // reject messages send from other iframes
|
||||
|
||||
if (e.data.frame){ // add `&sendframes` to the view link to trigger this event; it lets you capture video/audio from the parent window
|
||||
if (!media.tracks[e.data.trackID]){
|
||||
media.tracks[e.data.trackID] = {};
|
||||
media.tracks[e.data.trackID].generator = new MediaStreamTrackGenerator({kind:e.data.kind});
|
||||
media.tracks[e.data.trackID].stream = new MediaStream([media.tracks[e.data.trackID].generator]);
|
||||
media.tracks[e.data.trackID].frameWriter = media.tracks[e.data.trackID].generator.writable.getWriter();
|
||||
|
||||
media.tracks[e.data.trackID].frameWriter.write(e.data.frame);
|
||||
|
||||
if (!media.streams[e.data.streamID]){
|
||||
media.streams[e.data.streamID] = document.createElement("video");
|
||||
media.streams[e.data.streamID].id = "video_"+e.data.streamID;
|
||||
media.streams[e.data.streamID].autoplay = true;
|
||||
// media.streams[e.data.streamID].controls = true;
|
||||
media.streams[e.data.streamID].srcObject = media.tracks[e.data.trackID].stream;
|
||||
iframeContainer.appendChild(media.streams[e.data.streamID]);
|
||||
} else {
|
||||
if (e.data.kind=="video"){
|
||||
media.streams[e.data.streamID].srcObject.getVideoTracks().forEach(trk=>{
|
||||
media.streams[e.data.streamID].srcObject.removeTrack(trk);
|
||||
});
|
||||
} else if (e.data.kind=="audio"){
|
||||
media.streams[e.data.streamID].srcObject.getAudioTracks().forEach(trk=>{
|
||||
media.streams[e.data.streamID].srcObject.removeTrack(trk);
|
||||
});
|
||||
}
|
||||
media.tracks[e.data.trackID].stream.getTracks().forEach(trk=>{
|
||||
media.streams[e.data.streamID].srcObject.addTrack(trk);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
media.tracks[e.data.trackID].frameWriter.write(e.data.frame);
|
||||
}
|
||||
return;
|
||||
} // end of video/audio capture
|
||||
|
||||
if ("stats" in e.data){
|
||||
var outputWindow = document.createElement("div");
|
||||
console.log(e.data.stats);
|
||||
|
||||
|
||||
var out = "<br />total_inbound_connections:"+e.data.stats.total_inbound_connections;
|
||||
out += "<br />total_outbound_connections:"+e.data.stats.total_outbound_connections;
|
||||
|
||||
for (var streamID in e.data.stats.inbound){
|
||||
out += "<br /><br /><b>streamID:</b> "+streamID+"<br />";
|
||||
out += printValues(e.data.stats.inbound[streamID]);
|
||||
}
|
||||
|
||||
outputWindow.innerHTML = out;
|
||||
iframeContainer.appendChild(outputWindow);
|
||||
}
|
||||
|
||||
if ("gotChat" in e.data){
|
||||
var outputWindow = document.createElement("div");
|
||||
outputWindow.innerHTML = e.data.gotChat.msg;
|
||||
outputWindow.style.border="1px dotted black";
|
||||
iframeContainer.appendChild(outputWindow);
|
||||
}
|
||||
|
||||
if ("action" in e.data){
|
||||
var outputWindow = document.createElement("div");
|
||||
outputWindow.innerHTML = "child-page-action: "+e.data.action+"<br />";
|
||||
outputWindow.style.border="1px dotted black";
|
||||
iframeContainer.appendChild(outputWindow);
|
||||
}
|
||||
|
||||
|
||||
if ("streamIDs" in e.data){
|
||||
var outputWindow = document.createElement("div");
|
||||
outputWindow.innerHTML = "child-page-action: streamIDs<br />";
|
||||
for (var key in e.data.streamIDs) {
|
||||
outputWindow.innerHTML += "streamID: " + key + ", label:"+e.data.streamIDs[key] + "\n";
|
||||
}
|
||||
outputWindow.style.border="1px dotted black";
|
||||
iframeContainer.appendChild(outputWindow);
|
||||
}
|
||||
|
||||
if ("deviceList" in e.data){
|
||||
var outputWindow = document.createElement("div");
|
||||
outputWindow.innerHTML = "child-page-action: deviceList<br />";
|
||||
for (var i = 0;i<e.data.deviceList.length;i++){
|
||||
outputWindow.innerHTML += e.data.deviceList[i].label + "<br />";
|
||||
}
|
||||
outputWindow.style.border="1px dotted black";
|
||||
iframeContainer.appendChild(outputWindow);
|
||||
}
|
||||
|
||||
|
||||
if ("loudness" in e.data){
|
||||
console.log(e.data);
|
||||
if (document.getElementById("loudness")){
|
||||
outputWindow = document.getElementById("loudness");
|
||||
} else {
|
||||
var outputWindow = document.createElement("div");
|
||||
outputWindow.style.border="1px dotted black";
|
||||
iframeContainer.appendChild(outputWindow);
|
||||
outputWindow.id = "loudness";
|
||||
}
|
||||
outputWindow.innerHTML = "child-page-action: loudness<br />";
|
||||
for (var key in e.data.loudness) {
|
||||
outputWindow.innerHTML += key + " Loudness: " + e.data.loudness[key] + "\n";
|
||||
}
|
||||
outputWindow.style.border="1px black";
|
||||
}
|
||||
|
||||
if ("detailedState" in e.data){
|
||||
var outputWindow = document.createElement("div");
|
||||
outputWindow.innerHTML = "child-page-action: detailedState<br />"+JSON.stringify(e.data.detailedState)+"<br />";
|
||||
outputWindow.style.border="1px dotted black";
|
||||
iframeContainer.appendChild(outputWindow);
|
||||
}
|
||||
|
||||
if ("sensors" in e.data){
|
||||
console.log(e.data);
|
||||
if (document.getElementById("sensors")){
|
||||
outputWindow = document.getElementById("sensors");
|
||||
} else {
|
||||
var outputWindow = document.createElement("div");
|
||||
outputWindow.style.border="1px dotted black";
|
||||
iframeContainer.appendChild(outputWindow);
|
||||
outputWindow.id = "sensors";
|
||||
}
|
||||
outputWindow.innerHTML = "child-page-action: sensors<br /><br />";
|
||||
|
||||
for (var key in e.data.sensors.lin) {
|
||||
outputWindow.innerHTML += key + " linear: " + e.data.sensors.lin[key] + "<br />";
|
||||
}
|
||||
for (var key in e.data.sensors.acc) {
|
||||
outputWindow.innerHTML += key + " acceleration: " + e.data.sensors.acc[key] + "<br />";
|
||||
}
|
||||
for (var key in e.data.sensors.gyro) {
|
||||
outputWindow.innerHTML += key + " gyro: " + e.data.sensors.gyro[key] + "<br />";
|
||||
}
|
||||
for (var key in e.data.sensors.mag) {
|
||||
outputWindow.innerHTML += key + " magnet: " + e.data.sensors.mag[key] + "<br />";
|
||||
}
|
||||
outputWindow.style.border="1px black";
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
function loadSelfCommands(){
|
||||
var commands = {}
|
||||
commands.speaker = function(value){sendSelfCommand("speaker",value)}; // "speaker" also works in the same way
|
||||
|
||||
commands.mic = function(value){sendSelfCommand("mic",value)};
|
||||
|
||||
commands.camera = function(value){sendSelfCommand("camera",value)};
|
||||
|
||||
commands.bitrate = function(value){sendSelfCommand("bitrate",value)};
|
||||
|
||||
commands.volume = function(value){sendSelfCommand("volume",value)};
|
||||
|
||||
commands.record = function(value){sendSelfCommand("record",value)};
|
||||
|
||||
commands.sayHello = function(value){sendSelfCommand("sendChat","Hello")};
|
||||
|
||||
var target_self = document.body;
|
||||
|
||||
var hr = document.createElement("hr");
|
||||
target_self.appendChild(hr);
|
||||
|
||||
var h3 = document.createElement("h3");
|
||||
h3.innerText = "The following commands re-use the Companion.Ninja HTTP/WSS API, except you can send them via this Iframe interface.";
|
||||
target_self.appendChild(h3);
|
||||
|
||||
var a = document.createElement("a");
|
||||
a.innerText = "More details of the below IFRAME API details are here: https://github.com/steveseguin/Companion-Ninja#api-commands";
|
||||
a.href = "https://github.com/steveseguin/Companion-Ninja#api-commands";
|
||||
a.target = "_blank";
|
||||
target_self.appendChild(a);
|
||||
|
||||
var hr = document.createElement("hr");
|
||||
target_self.appendChild(hr);
|
||||
|
||||
|
||||
for (var k in commands) {
|
||||
|
||||
var button = document.createElement("button");
|
||||
button.innerHTML = k + ":<br />TRUE";
|
||||
button.dataset.command = k;
|
||||
button.onclick = function(){commands[this.dataset.command](true);}
|
||||
target_self.appendChild(button);
|
||||
|
||||
var button = document.createElement("button");
|
||||
button.innerHTML = k + ":<br />FALSE";
|
||||
button.dataset.command = k;
|
||||
button.onclick = function(){commands[this.dataset.command](false);}
|
||||
target_self.appendChild(button);
|
||||
|
||||
if (k=="mic" || k=="camera" || k=="record" || k=="speaker"){
|
||||
var button = document.createElement("button");
|
||||
button.innerHTML = k + ":<br />TOGGLE";
|
||||
button.dataset.command = k;
|
||||
button.onclick = function(){commands[this.dataset.command]("toggle");}
|
||||
target_self.appendChild(button);
|
||||
}
|
||||
|
||||
} // list available commands to console
|
||||
|
||||
commands.reload = function(){sendSelfCommand("reload",true);};
|
||||
|
||||
commands.hangup = function(){sendSelfCommand("hangup",true);};
|
||||
|
||||
|
||||
var button = document.createElement("button");
|
||||
button.innerHTML = k + ":<br />TRUE";
|
||||
button.onclick = function(){commands["reload"](true);}
|
||||
target_self.appendChild(button);
|
||||
|
||||
|
||||
var button = document.createElement("button");
|
||||
button.innerHTML = k + ":<br />TRUE";
|
||||
button.onclick = function(){commands["hangup"](true);}
|
||||
target_self.appendChild(button);
|
||||
|
||||
return commands;
|
||||
|
||||
}
|
||||
|
||||
function loadGuestCommands(guestid, streamID=false){ // this is the same as the Companion API, but can be issued to the IFRAME API
|
||||
|
||||
var container = document.createElement("div");
|
||||
container.id = "guest_"+guestid+"_container";
|
||||
document.body.appendChild(container);
|
||||
|
||||
var hr = document.createElement("hr");
|
||||
container.appendChild(hr);
|
||||
var h3 = document.createElement("h3");
|
||||
if (streamID){
|
||||
h3.innerHTML = "These target guest with the streamID: <i>"+guestid+ "</i> (if a director)";
|
||||
} else {
|
||||
h3.innerText = "These target the guest in slot "+guestid+ " (if you are director)";
|
||||
}
|
||||
container.appendChild(h3);
|
||||
|
||||
var button = document.createElement("button");
|
||||
button.innerHTML = "transfer popup";
|
||||
button.onclick = function(){sendGuestCommand(guestid, "forward");};
|
||||
container.appendChild(button);
|
||||
|
||||
var button = document.createElement("button");
|
||||
button.innerHTML = "transfer to 'room321'";
|
||||
button.onclick = function(){sendGuestCommand(guestid, "forward", 'room321');};
|
||||
container.appendChild(button);
|
||||
|
||||
var button = document.createElement("button");
|
||||
button.innerHTML = "scene 1";
|
||||
button.onclick = function(){sendGuestCommand(guestid, "addScene");}; /// SCENE 1 or specify a custom scene name as a value
|
||||
container.appendChild(button);
|
||||
|
||||
var button = document.createElement("button");
|
||||
button.innerHTML = "mute in scene";
|
||||
button.onclick = function(){sendGuestCommand(guestid, "muteScene");};
|
||||
container.appendChild(button);
|
||||
|
||||
var button = document.createElement("button");
|
||||
button.innerHTML = "mute everywhere";
|
||||
button.onclick = function(){sendGuestCommand(guestid, "mic");};
|
||||
container.appendChild(button);
|
||||
|
||||
var button = document.createElement("button");
|
||||
button.innerHTML = "hang up";
|
||||
button.onclick = function(){sendGuestCommand(guestid, "hangup");};
|
||||
container.appendChild(button);
|
||||
|
||||
var button = document.createElement("button");
|
||||
button.innerHTML = "solo chat";
|
||||
button.onclick = function(){sendGuestCommand(guestid, "soloChat");};
|
||||
container.appendChild(button);
|
||||
|
||||
var button = document.createElement("button");
|
||||
button.innerHTML = "remote speaker";
|
||||
button.onclick = function(){sendGuestCommand(guestid, "speaker");};
|
||||
container.appendChild(button);
|
||||
|
||||
var button = document.createElement("button");
|
||||
button.innerHTML = "remote display";
|
||||
button.onclick = function(){sendGuestCommand(guestid, "display");};
|
||||
container.appendChild(button);
|
||||
|
||||
var button = document.createElement("button");
|
||||
button.innerHTML = "rainbow puke fix";
|
||||
button.onclick = function(){sendGuestCommand(guestid, "forceKeyframe");};
|
||||
container.appendChild(button);
|
||||
|
||||
var button = document.createElement("button");
|
||||
button.innerHTML = "highlight";
|
||||
button.onclick = function(){sendGuestCommand(guestid, "soloVideo");};
|
||||
container.appendChild(button);
|
||||
|
||||
var button = document.createElement("button");
|
||||
button.innerHTML = "scene 2";
|
||||
button.onclick = function(){sendGuestCommand(guestid, "addScene", 2);};
|
||||
container.appendChild(button);
|
||||
|
||||
var button = document.createElement("button");
|
||||
button.innerHTML = "scene 3";
|
||||
button.onclick = function(){sendGuestCommand(guestid, "addScene", 3);};
|
||||
container.appendChild(button);
|
||||
|
||||
var button = document.createElement("button");
|
||||
button.innerHTML = "scene 4";
|
||||
button.onclick = function(){sendGuestCommand(guestid, "addScene", 4);};
|
||||
container.appendChild(button);
|
||||
|
||||
var button = document.createElement("button");
|
||||
button.innerHTML = "scene 5";
|
||||
button.onclick = function(){sendGuestCommand(guestid, "addScene", 5);};
|
||||
container.appendChild(button);
|
||||
|
||||
var button = document.createElement("button");
|
||||
button.innerHTML = "scene 6";
|
||||
button.onclick = function(){sendGuestCommand(guestid, "addScene", 6);};
|
||||
container.appendChild(button);
|
||||
|
||||
var button = document.createElement("button");
|
||||
button.innerHTML = " scene 7";
|
||||
button.onclick = function(){sendGuestCommand(guestid, "addScene", 7);};
|
||||
container.appendChild(button);
|
||||
|
||||
var button = document.createElement("button");
|
||||
button.innerHTML = "scene 8";
|
||||
button.onclick = function(){sendGuestCommand(guestid, "addScene", 8);};
|
||||
container.appendChild(button);
|
||||
|
||||
var button = document.createElement("button");
|
||||
button.innerHTML = "scene 'test'";
|
||||
button.onclick = function(){sendGuestCommand(guestid, "addScene", 'test');}; // specifying a custom scene; it needs to be active for this to work..
|
||||
container.appendChild(button);
|
||||
|
||||
|
||||
var input = document.createElement("label");
|
||||
input.innerHTML = "mic volume:";
|
||||
container.appendChild(input);
|
||||
var input = document.createElement("input");
|
||||
input.type = "range";
|
||||
input.title = "volume";
|
||||
input.min = 0;
|
||||
input.max = 200;
|
||||
input.value = 100;
|
||||
input.onchange = function(){sendGuestCommand(guestid, "volume", this.value);};
|
||||
container.appendChild(input);
|
||||
|
||||
}
|
||||
|
||||
function sendGuestCommand(target, action, value=null){ //
|
||||
iframe.contentWindow.postMessage({"target":target, "action":action, "value":value}, '*'); //
|
||||
//sendMessage(JSON.stringify({"target":target, "action":action, "value":value})); // if using the Companion API..
|
||||
}
|
||||
|
||||
function sendSelfCommand(action, value=null){
|
||||
iframe.contentWindow.postMessage({"target":null, "action":action, "value":value}, '*');
|
||||
//sendMessage(JSON.stringify({"target":target, "action":action, "value":value})); // if using the Companion API..
|
||||
}
|
||||
|
||||
//loadSelfCommands();
|
||||
//loadGuestCommands(1);
|
||||
//loadGuestCommands(2);
|
||||
//loadGuestCommands(3);
|
||||
//var randomGuest = Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 10);
|
||||
//loadGuestCommands(randomGuest, true);
|
||||
|
||||
}
|
||||
function printValues( obj) {
|
||||
var out = "";
|
||||
for (var key in obj) {
|
||||
if (typeof obj[key] === "object") {
|
||||
out +="<br />";
|
||||
out += printValues(obj[key]);
|
||||
} else {
|
||||
out +="<b>"+key+"</b>: "+obj[key]+"<br />";
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Test Stream Manager</h1>
|
||||
|
||||
|
||||
<div id="container">
|
||||
</div>
|
||||
<input placeholder="Enter an VDO.Ninja View URL here" id="viewlink" />
|
||||
<button onclick="loadIframe();">ADD</button>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user