rtcmulticonnection-v3
Version:
RTCMultiConnection is a WebRTC JavaScript wrapper library runs top over RTCPeerConnection API to support all possible peer-to-peer features.
750 lines (625 loc) • 27.3 kB
HTML
<html lang="en" itemscope itemtype="http://schema.org/WebPage">
<head>
<title>File Sharing using RTCMultiConnection-v3.0</title>
<meta name="description" content="Share files with multiple users using RTCMultiConnection v3.0" />
<meta name="keywords" content="WebRTC,RTCMultiConnection,Demos,Experiments,Samples,Examples" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<link rel="author" type="text/html" href="https://plus.google.com/+MuazKhan">
<link href='https://fonts.googleapis.com/css?family=Open+Sans:300,600' rel='stylesheet' type='text/css'>
<style>
* {
margin: 0;
padding: 0;
}
::-webkit-scrollbar {
height: 0;
overflow: visible;
width: 10px;
border-left:1px solid rgb(229, 229, 229);
}
::-webkit-scrollbar-thumb {
background-color: rgba(0, 0, 0, .2);
background-clip: padding-box;
min-height: 28px;
padding: 100px 0 0;
box-shadow: inset 1px 1px 0 rgba(0,0,0,.1),inset 0 -1px 0 rgba(0,0,0,.07);
border-width: 1px 1px 1px 6px;
}
::-webkit-scrollbar-button {
height: 0;
width: 0;
}
::-webkit-scrollbar-track {
background-clip: padding-box;
border: solid transparent;
border-width: 0 0 0 4px;
}
::-webkit-scrollbar-corner {
background: transparent;
}
html, body {
background-color: black;
font-family: 'Open Sans', 'Segoe UI Light','Segoe UI',Verdana,Arial;
font-size: 1.1em;
overflow: hidden;
}
.overlay {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: black;
z-index: 1;
}
.btn-select-file {
position: absolute;
z-index: 2;
left: 40%;
top: 40%;
width: 100px;
height: 100px;
-webkit-user-select: none;
border-radius: 50%;
text-shadow: 2px 2px white;
border: 1px solid #1B1B1B;
background-color: #D5D8D8;
cursor: pointer;
background-image: url(https://cdn.webrtc-experiment.com/images/btn-select-file.png);
outline: none;
}
.btn-select-file:hover {
background-color: #BBBBBB;
}
.btn-select-file:active {
background-color: #7F7B7B;
}
iframe {
position: absolute;
width: 80%;
height: 96%;
top: 87px;
left: 0;
right: 20;
bottom: 0;
border: 0;
outline: 0;
z-index: 5;
display: none;
}
#logs {
position: absolute;
background: white;
border-left: 1px solid black;
width: 20%;
top: 87px;
right: 0;
bottom: 0;
z-index: 6;
font-size: medium;
overflow: auto;
height: 100%;
border-top-left-radius: 5px;
font-size: 22px;
background: #a8a1a1;
background: -webkit-gradient(linear, left top, left bottom, from(#a8a1a1), to(#4d4d4d));
background: -webkit-linear-gradient(top, #a8a1a1, #4d4d4d);
background: -moz-linear-gradient(top, #a8a1a1, #4d4d4d);
background: -ms-linear-gradient(top, #a8a1a1, #4d4d4d);
background: -o-linear-gradient(top, #a8a1a1, #4d4d4d);
background-image: -ms-linear-gradient(top, #a8a1a1 0%, #4d4d4d 100%);
-webkit-box-shadow: rgba(000,000,000,0.3) 0 1px 1px;
-moz-box-shadow: rgba(000,000,000,0.3) 0 1px 1px;
box-shadow: rgba(000,000,000,0.3) 0 1px 1px;
}
#logs p {
padding: 2px 4px;
border-bottom: 1px solid black;
}
header {
position: absolute;
text-align: center;
width: 100%;
height: 32px;
top: 0;
right: 0;
left: 0;
z-index: 7;
padding-top: 8px;
}
#number-of-users {
position: absolute;
right: 60px;
top: 16px;
width: 36px;
height: 30px;
-webkit-user-select: none;
border-radius: 5px;
border: 1px solid #FFFFFF;
background-color: #E83930;
z-index: 8;
text-align: center;
padding-top: 6px;
color: white;
}
#room-id {
outline: none;
border: 1px solid black;
padding: 1px 3px;
font-size: 100%;
background: rgba(255, 255, 255, 0.28);
border-top-left-radius: 5px;
border-bottom-left-radius: 5px;
}
#join-room {
border: 1px solid black;
background: rgba(255, 255, 255, 0.28);
padding: 1px 3px;
border-left: 0;
font-size: 100%;
border-top-right-radius: 5px;
border-bottom-right-radius: 5px;
}
.ribbon {
width: 94%;
position: absolute;
text-align: center;
font-size: 20px;
background: #a8a1a1;
background: -webkit-gradient(linear, left top, left bottom, from(#a8a1a1), to(#4d4d4d));
background: -webkit-linear-gradient(top, #a8a1a1, #4d4d4d);
background: -moz-linear-gradient(top, #a8a1a1, #4d4d4d);
background: -ms-linear-gradient(top, #a8a1a1, #4d4d4d);
background: -o-linear-gradient(top, #a8a1a1, #4d4d4d);
background-image: -ms-linear-gradient(top, #a8a1a1 0%, #4d4d4d 100%);
-webkit-box-shadow: rgba(000,000,000,0.3) 0 1px 1px;
-moz-box-shadow: rgba(000,000,000,0.3) 0 1px 1px;
box-shadow: rgba(000,000,000,0.3) 0 1px 1px;
font-family: 'Helvetica Neue',Helvetica, sans-serif;
margin-left: 40px;
}
.ribbon h1 {
font-size: 32px;
color: #000000;
text-shadow: #b0b0b0 0 1px 0;
margin:0px;
padding: 6px 6px;
}
.ribbon:before, .ribbon:after {
content: '';
position: absolute;
display: block;
bottom: -1em;
border: 1.5em solid #595959;
z-index: -1;
}
.ribbon:before {
left: -2em;
border-right-width: 1.5em;
border-left-color: transparent;
-webkit-box-shadow: rgba(000,000,000,0.4) 1px 1px 1px;
-moz-box-shadow: rgba(000,000,000,0.4) 1px 1px 1px;
box-shadow: rgba(000,000,000,0.4) 1px 1px 1px;
}
.ribbon:after {
right: -2em;
border-left-width: 1.5em;
border-right-color: transparent;
-webkit-box-shadow: rgba(000,000,000,0.4) -1px 1px 1px;
-moz-box-shadow: rgba(000,000,000,0.4) -1px 1px 1px;
box-shadow: rgba(000,000,000,0.4) -1px 1px 1px;
}
.ribbon .ribbon-content:before, .ribbon .ribbon-content:after {
border-color: #292929 transparent transparent transparent;
position: absolute;
display: block;
border-style: solid;
bottom: -1em;
content: '';
}
.ribbon .ribbon-content:before {
left: 0;
border-width: 1em 0 0 1em;
}
.ribbon .ribbon-content:after {
right: 0;
border-width: 1em 1em 0 0;
}
.ribbon-stitches-top {
margin-top:2px;
border-top: 0px dashed rgba(0, 0, 0, 0.2);
-moz-box-shadow: 0px 0px 2px rgba(0, 0, 0, 0.5);
-webkit-box-shadow: 0px 0px 2px rgba(0, 0, 0, 0.5);
box-shadow: 0px 0px 2px rgba(255, 255, 255, 0.5);
}
.ribbon-stitches-bottom {
margin-bottom:2px;
border-top: 0px dashed rgba(0, 0, 0, 0.2);
-moz-box-shadow: 0px 0px 2px rgba(0, 0, 0, 0.3);
-webkit-box-shadow: 0px 0px 2px rgba(0, 0, 0, 0.3);
box-shadow: 0px 0px 2px rgba(255, 255, 255, 0.3);
}
.android-app-icon {
background-repeat: no-repeat;
background-position: center center;
background-size: 64px 64px;
background-image: url(https://cdn.webrtc-experiment.com/images/android-app-icon.png);
width: 64px;
height: 64px;
position: absolute;
bottom: 5px;
right: 15px;
z-index: 9999;
}
.chrome-web-store-icon {
background-repeat: no-repeat;
background-position: center center;
background-size: 64px 64px;
background-image: url(https://cdn.webrtc-experiment.com/images/chrome-web-store-icon.png);
width: 64px;
height: 64px;
position: absolute;
bottom: 5px;
right: 90px;
z-index: 9999;
}
@media all and (max-width: 500px) {
#logs {
position: fixed;
bottom: 0;
left: 0;
width: 100%;
top: auto;
height: 40%;
border-left: 0;
border-top: 1px solid black;
font-size: 18px;
}
.btn-select-file {
top: 20%;
left: 40%;
}
#room-id {
width: 40%;
}
.ribbon{
height: 150%;
width: 80%;
}
.ribbon h1 {
font-size: 20px;
}
#number-of-users {
right: 50px;
}
.android-app-icon, .chrome-web-store-icon {
display: none;
}
}
</style>
</head>
<body>
<div class="overlay"></div>
<button class="btn-select-file"></button>
<iframe></iframe>
<header>
<div class="ribbon"><div class="ribbon-stitches-top"></div><strong class="ribbon-content"><h1>
<input type="text" id="room-id" placeholder="room-id"><button id="join-room">Join</button>
</h1></strong><div class="ribbon-stitches-bottom"></div></div>
</header>
<div id="number-of-users" title="Number of online users.">0</div>
<div id="logs">
<p>
Peer-to-Peer (private) file sharing.
</p>
<p>
You can share/receive files from any platform/device e.g. destkop operating systems, Android, iOS etc.
</p>
<p>
Create or join a room & select file using "+" button.
</p>
</div>
<a class="chrome-web-store-icon" href="https://chrome.google.com/webstore/detail/webrtc-file-sharing/nbnncbdkhpmbnkfngmkdbepoemljbnfo" target="_blank" title="Install Chrome Desktop Extension"></a>
<a class="android-app-icon" href="https://play.google.com/store/apps/details?id=com.webrtc.experiment" target="_blank" title="Install Android App"></a>
<!-- <script src="/RTCMultiConnection.min.js"></script> -->
<script>
// to get STUN/TURN URIs from xirsys.com
window.getExternalIceServers = true;
</script>
<script src="https://cdn.webrtc-experiment.com:443/rmc3.min.js"></script>
<!-- <script src="/dev/FileBufferReader.js"></script> -->
<script src="https://cdn.webrtc-experiment.com:443/rmc3.fbr.min.js"></script>
<!-- socket.io for signaling -->
<script src="/socket.io/socket.io.js"></script>
<script>
// Muaz Khan - https://github.com/muaz-khan
// MIT License - https://www.WebRTC-Experiment.com/licence/
// Source Code - https://github.com/muaz-khan/RTCMultiConnection
window.addEventListener('load', function() {
if (window.localStorage.getItem('room-id')) {
document.getElementById('room-id').value = window.localStorage.getItem('room-id');
} else {
document.getElementById('room-id').value = (Math.random() * 100).toString().replace('.', '');
}
if(location.hash.length > 1) {
document.getElementById('room-id').value = location.hash.replace('#', '');
}
document.getElementById('join-room').onclick = function() {
this.disabled = true;
document.getElementById('room-id').disabled = true;
var roomId = document.getElementById('room-id').value;
window.localStorage.setItem('room-id', roomId);
joinARoom(roomId);
};
if(location.hash.length > 1) {
document.getElementById('join-room').disabled = true;
document.getElementById('room-id').disabled = true;
joinARoom(location.hash.replace('#', ''));
}
});
function joinARoom(roomId) {
var iframe = document.querySelector('iframe');
var btnSelectFile = document.querySelector('.btn-select-file');
btnSelectFile.onclick = function() {
var fileSelector = new FileSelector();
fileSelector.selectSingleFile(function(file) {
previewFile(file);
onFileSelected(file);
});
};
var connection;
var lastSelectedFile;
var room_id = '';
// 60k -- assuming receiving client is chrome
var chunk_size = 60 * 1000;
function setupWebRTCConnection() {
if (connection) {
return;
}
// www.RTCMultiConnection.org/docs/
connection = new RTCMultiConnection();
// to make sure, "connection-reconnect" doesn't sends files again
connection.fileReceived = {};
// by default, socket.io server is assumed to be deployed on your own URL
// connection.socketURL = '/';
// comment-out below line if you do not have your own socket.io server
connection.socketURL = 'https://rtcmulticonnection.herokuapp.com:443/';
connection.socketMessageEvent = 'file-sharing-demo';
connection.chunkSize = chunk_size;
connection.sdpConstraints.mandatory = {
OfferToReceiveAudio: false,
OfferToReceiveVideo: false
};
connection.enableFileSharing = true;
if (room_id && room_id.length) {
connection.userid = room_id;
}
connection.channel = connection.sessionid = roomId;
connection.session = {
data: true,
// oneway: true --- to make it one-to-many
};
connection.filesContainer = logsDiv;
connection.connectedWith = {};
connection.onmessage = function(event) {
if(event.data.doYouWannaReceiveThisFile) {
if(!connection.fileReceived[event.data.fileName]) {
connection.send({
yesIWannaReceive:true,
fileName: event.data.fileName
});
}
}
if(event.data.yesIWannaReceive && !!lastSelectedFile) {
connection.shareFile(lastSelectedFile, event.userid);
}
};
connection.onopen = function(e) {
try {
chrome.power.requestKeepAwake('display');
}
catch(e) {}
if (connection.connectedWith[e.userid]) return;
connection.connectedWith[e.userid] = true;
var message = '<b>' + e.userid + '</b><br>is connected.';
appendLog(message);
if (!lastSelectedFile) return;
// already shared the file
var file = lastSelectedFile;
setTimeout(function() {
appendLog('Sharing file<br><b>' + file.name + '</b><br>Size: <b>' + bytesToSize(file.size) + '<b><br>With <b>' + connection.getAllParticipants().length + '</b> users');
connection.send({
doYouWannaReceiveThisFile: true,
fileName: file.name
});
}, 500);
};
connection.onclose = function(e) {
incrementOrDecrementUsers();
if (connection.connectedWith[e.userid]) return;
appendLog('Data connection has been closed between you and <b>' + e.userid + '</b>. Re-Connecting..');
connection.join(roomId);
};
connection.onerror = function(e) {
if (connection.connectedWith[e.userid]) return;
appendLog('Data connection failed. between you and <b>' + e.userid + '</b>. Retrying..');
};
setFileProgressBarHandlers(connection);
connection.onUserStatusChanged = function(user) {
incrementOrDecrementUsers();
};
connection.onleave = function(user) {
user.status = 'offline';
connection.onUserStatusChanged(user);
incrementOrDecrementUsers();
};
var message = 'Connecting room:<br><b>' + connection.channel + '</b>';
appendLog(message);
connection.openOrJoin(connection.channel, function(isRoomExists, roomid) {
var message = 'Successfully connected to room:<br><b>' + roomid + '</b>';
// if (isRoomEists) { }
appendLog(message);
if(document.getElementById('room-id')) {
if(innerWidth > 500) {
document.getElementById('room-id').parentNode.innerHTML = 'Joined room: ' + roomid;
}
else {
document.getElementById('room-id').parentNode.innerHTML = 'Joined room:<br>' + roomid;
}
}
});
window.connection = connection;
}
function setFileProgressBarHandlers(connection) {
var progressHelper = {};
// www.RTCMultiConnection.org/docs/onFileStart/
connection.onFileStart = function(file) {
if (connection.fileReceived[file.name]) return;
var div = document.createElement('div');
div.style.borderBottom = '1px solid black';
div.style.padding = '2px 4px';
div.id = file.uuid;
var message = '';
if (file.userid == connection.userid) {
message += 'Sharing with:' + file.remoteUserId;
} else {
message += 'Receiving from:' + file.userid;
}
message += '<br><b>' + file.name + '</b>.';
message += '<br>Size: <b>' + bytesToSize(file.size) + '</b>';
message += '<br><label>0%</label> <progress></progress>';
div.innerHTML = message;
connection.filesContainer.insertBefore(div, connection.filesContainer.firstChild);
if (!file.remoteUserId) {
progressHelper[file.uuid] = {
div: div,
progress: div.querySelector('progress'),
label: div.querySelector('label')
};
progressHelper[file.uuid].progress.max = file.maxChunks;
return;
}
if (!progressHelper[file.uuid]) {
progressHelper[file.uuid] = {};
}
progressHelper[file.uuid][file.remoteUserId] = {
div: div,
progress: div.querySelector('progress'),
label: div.querySelector('label')
};
progressHelper[file.uuid][file.remoteUserId].progress.max = file.maxChunks;
};
// www.RTCMultiConnection.org/docs/onFileProgress/
connection.onFileProgress = function(chunk) {
if (connection.fileReceived[chunk.name]) return;
var helper = progressHelper[chunk.uuid];
if (!helper) {
return;
}
if (chunk.remoteUserId) {
helper = progressHelper[chunk.uuid][chunk.remoteUserId];
if (!helper) {
return;
}
}
helper.progress.value = chunk.currentPosition || chunk.maxChunks || helper.progress.max;
updateLabel(helper.progress, helper.label);
};
// www.RTCMultiConnection.org/docs/onFileEnd/
connection.onFileEnd = function(file) {
if (connection.fileReceived[file.name]) return;
var div = document.getElementById(file.uuid);
if (div) {
div.parentNode.removeChild(div);
}
if (file.remoteUserId === connection.userid) {
previewFile(file);
connection.fileReceived[file.name] = file;
var message = 'Successfully received file';
message += '<br><b>' + file.name + '</b>.';
message += '<br>Size: <b>' + bytesToSize(file.size) + '</b>.';
message += '<br><a href="' + file.url + '" target="_blank" download="' + file.name + '">Download</a>';
var div = appendLog(message);
return;
}
var message = 'Successfully shared file';
message += '<br><b>' + file.name + '</b>.';
message += '<br>With: <b>' + file.remoteUserId + '</b>.';
message += '<br>Size: <b>' + bytesToSize(file.size) + '</b>.';
appendLog(message);
};
function updateLabel(progress, label) {
if (progress.position === -1) {
return;
}
var position = +progress.position.toFixed(2).split('.')[1] || 100;
label.innerHTML = position + '%';
}
}
function bytesToSize(bytes) {
var k = 1000;
var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
if (bytes === 0) {
return '0 Bytes';
}
var i = parseInt(Math.floor(Math.log(bytes) / Math.log(k)), 10);
return (bytes / Math.pow(k, i)).toPrecision(3) + ' ' + sizes[i];
}
function onFileSelected(file) {
var innerHTML = 'You selected:<br><b>' + file.name + '</b><br>Size: <b>' + bytesToSize(file.size) + '</b>';
appendLog(innerHTML);
lastSelectedFile = file;
if (connection) {
connection.send({
doYouWannaReceiveThisFile: true,
fileName: file.name
});
}
}
var numberOfUsers = document.getElementById('number-of-users');
function incrementOrDecrementUsers() {
numberOfUsers.innerHTML = connection ? connection.getAllParticipants().length : 0;
}
var logsDiv = document.getElementById('logs');
function appendLog(html) {
var div = document.createElement('div');
div.innerHTML = '<p>' + html + '</p>';
logsDiv.insertBefore(div, logsDiv.firstChild);
return div;
}
function previewFile(file) {
btnSelectFile.style.left = '5px';
btnSelectFile.style.right = 'auto';
btnSelectFile.style.zIndex = 10;
btnSelectFile.style.top = '5px';
btnSelectFile.style.outline = 'none';
document.querySelector('.overlay').style.display = 'none';
iframe.style.display = 'block';
if (file.type.match(/image|video|audio|pdf|txt|javascript|css|php|py/g)) {
iframe.src = URL.createObjectURL(file);
} else {
iframe.src = 'https://cdn.webrtc-experiment.com/images/folder-icon.png';
}
iframe.onload = function() {
Array.prototype.slice.call(iframe.contentWindow.document.body.querySelectorAll('*')).forEach(function(element) {
element.style.maxWidth = '100%';
});
if (file.type.match(/image|video|audio|pdf/g)) {
iframe.contentWindow.document.body.style.textAlign = 'center';
iframe.contentWindow.document.body.style.background = 'black';
iframe.contentWindow.document.body.style.color = 'white';
return;
}
iframe.contentWindow.document.body.style.textAlign = 'left';
iframe.contentWindow.document.body.style.background = 'white';
iframe.contentWindow.document.body.style.color = 'black';
};
}
setupWebRTCConnection();
}
</script>
</body>
</html>