UNPKG

rtcmulticonnection

Version:

RTCMultiConnection is a WebRTC JavaScript wrapper library runs top over RTCPeerConnection API to support all possible peer-to-peer features.

761 lines (617 loc) 27.6 kB
<!-- Demo version: 2018.10.01 --> <!DOCTYPE html> <html lang="en" itemscope itemtype="http://schema.org/WebPage"> <head> <title>File Sharing using RTCMultiConnection</title> <script> if(!location.hash.replace('#', '').length) { location.href = location.href.split('#')[0] + '#' + (Math.random() * 100).toString().replace('.', ''); location.reload(); } </script> <meta name="description" content="Share files with multiple users using RTCMultiConnection" /> <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; } #logs p { padding: 2px 4px; border-bottom: 1px solid black; } header { position: absolute; text-align: center; width: 100%; height: 61px; top: 0; right: 0; left: 0; z-index: 7; padding-top: 8px; background: #EFEBEB; border-bottom: 1px solid white; } #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; } .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; left: 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; left: 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!important; } #number-of-users { right: 50px; } .android-app-icon, .chrome-web-store-icon { display: none; } } hr {border:0;} </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> // to get STUN/TURN URIs from xirsys.com window.getExternalIceServers = true; </script> <script src="/dist/RTCMultiConnection.min.js"></script> <script src="/socket.io/socket.io.js"></script> <script src="https://cdn.webrtc-experiment.com:443/FileBufferReader.js"></script> <script> window.onerror = console.error = function() { var error = JSON.stringify(arguments); if(error.indexOf('Blocked a frame with origin') !== -1) { return; } alert('Error:\n' + error); }; // 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 = document.querySelector('.btn-select-file').onclick = function() { document.getElementById('join-room').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('#', '')); } }); var btnSelectFile = document.querySelector('.btn-select-file'); var connection; function joinARoom(roomId) { var iframe = document.querySelector('iframe'); btnSelectFile.onclick = function(file) { if(file && (file instanceof File || file instanceof Blob) && file.size) { previewFile(file); onFileSelected(file); return; } var fileSelector = new FileSelector(); fileSelector.selectSingleFile(function(file) { previewFile(file); onFileSelected(file); }); }; 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.size + 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(isRoomExist, roomid) { var message = 'Successfully connected to room: <b>' + roomid + '</b><hr>Other users can join you on iPhone/Android using "' + roomid + '" or desktop (Windows/MacOSX/Ubuntu) users can join using this (secure/private) URL: <a href="./file-sharing.html#' + roomid + '" target="_blank">file-sharing.html#' + roomid + '</a>'; // 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; } } var socket = connection.getSocket(); socket.on('disconnect', function() { appendLog('Seems disconnected.', 'red'); }); socket.on('connect', function() { location.reload(); }); socket.on('error', function() { location.reload(); }); window.addEventListener('offline', function() { appendLog('Seems disconnected.', 'red'); }, false); }); window.connection = connection; } function setFileProgressBarHandlers(connection) { var progressHelper = {}; // www.RTCMultiConnection.org/docs/onFileStart/ connection.onFileStart = function(file) { if (connection.fileReceived[file.size + 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>'; if(file.userid !== connection.userid) { message += '<br><button id="resend">Receive Again?</button>'; } div.innerHTML = message; connection.filesContainer.insertBefore(div, connection.filesContainer.firstChild); if(file.userid !== connection.userid && div.querySelector('#resend')) { div.querySelector('#resend').onclick = function(e) { e.preventDefault(); this.onclick = function() {}; if(connection.fileReceived[file.size + file.name]) { delete connection.fileReceived[file.size + file.name]; } connection.send({ yesIWannaReceive: true, fileName: file.name }, file.userid); div.parentNode.removeChild(div); }; } 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.size + 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.size + 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.size + 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.size + 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, color) { var div = document.createElement('div'); div.innerHTML = '<p>' + html + '</p>'; logsDiv.insertBefore(div, logsDiv.firstChild); if(color) { div.style.color = color; } return div; } window.onerror = console.error = function() { var error = JSON.stringify(arguments); if(error.indexOf('Blocked a frame with origin') !== -1) { return; } appendLog('Error:<br>' + error, 'red') }; 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'; iframe.onload = function() { Array.prototype.slice.call(iframe.contentWindow.document.body.querySelectorAll('*')).forEach(function(element) { element.style.maxWidth = '100%'; }); if (!file.type || fileNameMatches || file.type.match(/image|video|audio|pdf/g) || iframe.src.indexOf('data:image/png') !== -1 || iframe.src.toLowerCase().search(/.png|.jpeg|.jpg|.gif/g) !== -1) { 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'; }; var fileNameMatches = (file.name || '').toLowerCase().match(/.webm|.wav|.pdf|.txt|.js|.css|.cs|.png|.jpg|.jpeg|.gif/g); if (fileNameMatches) { iframe.src = URL.createObjectURL(file); } else { iframe.src = 'https://webrtcweb.com/fs/unknown-file.png'; } } setupWebRTCConnection(); } window.addEventListener('online', function() { location.reload(); }, false); // drag-drop support document.addEventListener('dragover', function(e) { e.preventDefault(); e.stopPropagation(); e.dataTransfer.dropEffect = 'copy'; }, false); document.addEventListener('drop', function(e) { e.preventDefault(); e.stopPropagation(); if(!e.dataTransfer.files || !e.dataTransfer.files.length) { return; } var file = e.dataTransfer.files[0]; if(!connection) { document.getElementById('join-room').onclick(); } btnSelectFile.onclick(file); }, false); </script> </body> </html>