$ git clone http://ion.nu/git/webchat
commit 9288e09ed1e19b83c6dad3e4c45b61a771c2fbd3
Author: Alicia <...>
Date:   Wed Dec 30 01:23:44 2015 +0100

    Added options to manually open and close (hide) media streams

diff --git a/cams.js b/cams.js
index 1c38c99..597c548 100644
--- a/cams.js
+++ b/cams.js
@@ -97,6 +97,7 @@ function opencam(user)
   cam.video.style.height='100%';
   cam.video.poster='mic.png';
   cam.sourcebuffer=false;
+  user.cam=cam;
   if(!window.MediaSource)
   {
     chatnote('MediaSource missing, using httpcam fallback');
@@ -145,20 +146,16 @@ function handlecamdata(user, data)
 }
 function closecam(user)
 {
-  var cam=false;
-  var i=0;
-  while(i<cams.length)
-  {
-    if(cams[i].user==user){cam=cams[i]; break;}
-    ++i;
-  }
+  connection.send('mediaunsubscribe:'+user.nickname);
+  var cam=user.cam;
   if(!cam){return;}
+  user.cam=false;
+  cam.video.pause(); // Just in case
   document.getElementById('cams').removeChild(cam.cambox);
   cam.cambox.removeChild(cam.video);
   cams.splice(cams.indexOf(cam), 1);
   // TODO: check that a video isn't playing
   if(cams.length==0){hidecams();}
-//  connection.send('mediaunsubscribe:'+cam.user.username); (probably do this from elsewhere)
 }
 function showcams()
 {
diff --git a/chat.js b/chat.js
index 276ca6f..a271a44 100644
--- a/chat.js
+++ b/chat.js
@@ -39,6 +39,7 @@ function User(name)
   };
   this.color='000000';
   this.modprivileges=0;
+  this.broadcasting=false;
   document.getElementById('userlist').appendChild(this.listlabel);
 }
 function finduser(name)
@@ -194,18 +195,33 @@ function usermenu(user, x, y)
     connection.send('whois:'+user.nickname);
     document.getElementById('usermenu').style.display='none';
   }
-  // Close media stream
-  var cam=false;
-  if(me.modprivileges&PRIV_CLOSECAM)
+  // Show/hide media streams
+  if(user.broadcasting)
   {
-    var i=0;
-    while(i<cams.length)
+    if(user.cam)
     {
-      if(cams[i].user==user){cam=cams[i]; break;}
-      ++i;
+      document.getElementById('usermenu').children.showmedia.style.display='none';
+      document.getElementById('usermenu').children.hidemedia.style.display='block';
+      document.getElementById('usermenu').children.hidemedia.onclick=function()
+      {
+        closecam(user);
+        document.getElementById('usermenu').style.display='none';
+      }
+    }else{
+      document.getElementById('usermenu').children.hidemedia.style.display='none';
+      document.getElementById('usermenu').children.showmedia.style.display='block';
+      document.getElementById('usermenu').children.showmedia.onclick=function()
+      {
+        opencam(user);
+        document.getElementById('usermenu').style.display='none';
+      }
     }
+  }else{
+    document.getElementById('usermenu').children.showmedia.style.display='none';
+    document.getElementById('usermenu').children.hidemedia.style.display='none';
   }
-  if(cam)
+  // Close media stream
+  if(me.modprivileges&PRIV_CLOSECAM && user.broadcasting)
   {
     document.getElementById('usermenu').children.closemedia.style.display='block';
     document.getElementById('usermenu').children.closemedia.onclick=function()
diff --git a/chat.php b/chat.php
index 534bdc3..cda80d4 100644
--- a/chat.php
+++ b/chat.php
@@ -48,6 +48,8 @@
   <div id="usermenu">
     <div name="pm" class="usermenuoption">Send private message</div>
     <div name="whois" class="usermenuoption">Account</div>
+    <div name="hidemedia" class="usermenuoption" title="(also saves bandwidth)">Hide media stream</div>
+    <div name="showmedia" class="usermenuoption">Show media stream</div>
     <div name="closemedia" class="usermenuoption">Close media stream</div>
     <div name="ban" class="usermenuoption">Ban</div>
   </div>
diff --git a/proto.js b/proto.js
index 8cb2ea7..fad673e 100644
--- a/proto.js
+++ b/proto.js
@@ -121,13 +121,17 @@ function handlecommands(data)
   {
     var user=data.data.substring(11, data.data.length);
     chatnote(user+' started broadcasting');
-    opencam(finduser(user));
+    user=finduser(user);
+    user.broadcasting=true;
+    opencam(user);
   }
   else if(data.data.substring(0,10)=='mediastop:')
   {
     var user=data.data.substring(10, data.data.length);
     chatnote(user+' stopped broadcasting');
-    closecam(finduser(user));
+    user=finduser(user);
+    user.broadcasting=false;
+    closecam(user);
   }
   else if(data.data.substring(0,6)=='media:')
   {