mirror of
https://github.com/Ylianst/MeshCentral.git
synced 2024-11-25 20:51:23 +03:00
Added partial file transfers in MeshMessenger
This commit is contained in:
parent
de76f812d5
commit
13fffd4f02
BIN
public/images/MeshComm64b.png
Normal file
BIN
public/images/MeshComm64b.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 27 KiB |
Binary file not shown.
Before Width: | Height: | Size: 6.2 KiB After Width: | Height: | Size: 6.5 KiB |
Binary file not shown.
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
Binary file not shown.
Before Width: | Height: | Size: 25 KiB |
@ -14,7 +14,7 @@
|
|||||||
<div id="fileButton" class="icon4 topButton" title="Share a file" style="display:none" onclick="fileButtonClick()"></div>
|
<div id="fileButton" class="icon4 topButton" title="Share a file" style="display:none" onclick="fileButtonClick()"></div>
|
||||||
<div id="camButton" class="icon2 topButton" title="Activate camera & microphone" style="display:none" onclick="camButtonClick()"></div>
|
<div id="camButton" class="icon2 topButton" title="Activate camera & microphone" style="display:none" onclick="camButtonClick()"></div>
|
||||||
<div id="micButton" class="icon6 topButton" title="Activate microphone" style="display:none" onclick="micButtonClick()"></div>
|
<div id="micButton" class="icon6 topButton" title="Activate microphone" style="display:none" onclick="micButtonClick()"></div>
|
||||||
<div id="hangupButton" class="icon11 topButton" title="Hang up" style="display:none" onclick="hangUpButtonClick(1)"></div>
|
<div id="hangupButton" class="icon11 topRedButton" title="Hang up" style="display:none" onclick="hangUpButtonClick(1)"></div>
|
||||||
<div style="display:inline-block;width:2px"></div>
|
<div style="display:inline-block;width:2px"></div>
|
||||||
<div style="padding-top:9px;padding-left:6px;font-size:20px;display:inline-block"><b>MeshMessenger<span id="xtitle"></span></b></div>
|
<div style="padding-top:9px;padding-left:6px;font-size:20px;display:inline-block"><b>MeshMessenger<span id="xtitle"></span></b></div>
|
||||||
</div>
|
</div>
|
||||||
@ -36,6 +36,7 @@
|
|||||||
<div style="position:absolute;right:0;left:0;top:2.5px;text-align:center">Local</div>
|
<div style="position:absolute;right:0;left:0;top:2.5px;text-align:center">Local</div>
|
||||||
<video id="localVideoCanvas" autoplay muted style="position:absolute;top:20px;left:0;width:100%;height:calc(100% - 30px);background-color:black"></video>
|
<video id="localVideoCanvas" autoplay muted style="position:absolute;top:20px;left:0;width:100%;height:calc(100% - 30px);background-color:black"></video>
|
||||||
</div>
|
</div>
|
||||||
|
<input id="uploadFileInput" type="file" style="display:none">
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
var userInputFocus = 0;
|
var userInputFocus = 0;
|
||||||
var args = parseUriArgs();
|
var args = parseUriArgs();
|
||||||
@ -47,11 +48,18 @@
|
|||||||
var webrtcconfiguration = null; //{ "iceServers": [ { 'urls': 'stun:stun.services.mozilla.com' }, { 'urls': 'stun:stun.l.google.com:19302' } ] };
|
var webrtcconfiguration = null; //{ "iceServers": [ { 'urls': 'stun:stun.services.mozilla.com' }, { 'urls': 'stun:stun.l.google.com:19302' } ] };
|
||||||
var localStream = null;
|
var localStream = null;
|
||||||
var remoteStream = null;
|
var remoteStream = null;
|
||||||
var senders = null;
|
var fileUploads = [];
|
||||||
|
var fileDownloads = {};
|
||||||
|
var currentFileUpload = null;
|
||||||
|
var currentFileDownload = null;
|
||||||
|
|
||||||
// Set the title
|
// Set the title
|
||||||
if (args.title) { QH('xtitle', ' - ' + args.title); document.title = document.title + ' - ' + args.title; }
|
if (args.title) { QH('xtitle', ' - ' + args.title); document.title = document.title + ' - ' + args.title; }
|
||||||
|
|
||||||
|
document.addEventListener('dragover', haltEvent, false);
|
||||||
|
document.addEventListener('dragleave', haltEvent, false);
|
||||||
|
document.addEventListener('drop', fileDrop, false);
|
||||||
|
|
||||||
document.onkeyup = function ondockeypress(e) {
|
document.onkeyup = function ondockeypress(e) {
|
||||||
if (state == 2) {
|
if (state == 2) {
|
||||||
if ((e.keyCode == 8) && (userInputFocus == 0)) {
|
if ((e.keyCode == 8) && (userInputFocus == 0)) {
|
||||||
@ -119,6 +127,7 @@
|
|||||||
QE('sendButton', state == 2);
|
QE('sendButton', state == 2);
|
||||||
QE('clearButton', state == 2);
|
QE('clearButton', state == 2);
|
||||||
QE('xouttext', state == 2);
|
QE('xouttext', state == 2);
|
||||||
|
QV('fileButton', state == 2);
|
||||||
QV('camButton', webchannel && webchannel.ok && !localStream);
|
QV('camButton', webchannel && webchannel.ok && !localStream);
|
||||||
QV('micButton', webchannel && webchannel.ok && !localStream);
|
QV('micButton', webchannel && webchannel.ok && !localStream);
|
||||||
QV('hangupButton', webchannel && webchannel.ok && localStream);
|
QV('hangupButton', webchannel && webchannel.ok && localStream);
|
||||||
@ -195,6 +204,7 @@
|
|||||||
function disconnect() {
|
function disconnect() {
|
||||||
if (state > 0) { displayControl('Connection closed.'); }
|
if (state > 0) { displayControl('Connection closed.'); }
|
||||||
if (state > 1) { setTimeout(start, 500); }
|
if (state > 1) { setTimeout(start, 500); }
|
||||||
|
cancelAllFileTransfers();
|
||||||
hangUpButtonClick(0, true); // Data channel
|
hangUpButtonClick(0, true); // Data channel
|
||||||
hangUpButtonClick(1, true); // Local audio/video
|
hangUpButtonClick(1, true); // Local audio/video
|
||||||
hangUpButtonClick(2, true); // Remote audio/video
|
hangUpButtonClick(2, true); // Remote audio/video
|
||||||
@ -208,7 +218,7 @@
|
|||||||
if (state != 2) return; // If not in connected state, ignore this.
|
if (state != 2) return; // If not in connected state, ignore this.
|
||||||
if (typeof data == 'object') { data = JSON.stringify(data); } // If this is an object, convert it to a string.
|
if (typeof data == 'object') { data = JSON.stringify(data); } // If this is an object, convert it to a string.
|
||||||
if (webchannel && webchannel.ok) { if (webchannel.xoutBuffer != null) { webchannel.xoutBuffer.push(data); } else { webchannel.send(data); } } // If WebRTC channel is possible, use it or hold until we can use it.
|
if (webchannel && webchannel.ok) { if (webchannel.xoutBuffer != null) { webchannel.xoutBuffer.push(data); } else { webchannel.send(data); } } // If WebRTC channel is possible, use it or hold until we can use it.
|
||||||
else { if (socket != null) { socket.send(data); } } // If a websocket channel is present, use that.
|
else { if (socket != null) { try { socket.send(data); } catch (ex) { } } } // If a websocket channel is present, use that.
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send data over the websocket transport (WebSocket only)
|
// Send data over the websocket transport (WebSocket only)
|
||||||
@ -240,6 +250,39 @@
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case 'file': { startFileDownload(data); break; }
|
||||||
|
case 'fileUploadCancel': { cancelFileTransfer(data.id); break; }
|
||||||
|
case 'fileUploadStart': {
|
||||||
|
if (fileDownloads[data.id]) {
|
||||||
|
currentFileDownload = fileDownloads[data.id];
|
||||||
|
currentFileDownload.data = '';
|
||||||
|
changeFileInfo(data.id, 2, 0);
|
||||||
|
continueFileDownload(data);
|
||||||
|
send({ action: 'fileUploadAck', id: data.id });
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
case 'fileUploadEnd': {
|
||||||
|
if (currentFileDownload && (currentFileDownload.id == data.id)) {
|
||||||
|
changeFileInfo(data.id, 3, 200);
|
||||||
|
currentFileDownload.done = 1;
|
||||||
|
currentFileDownload = null;
|
||||||
|
send({ action: 'fileUploadAck', id: data.id });
|
||||||
|
}
|
||||||
|
currentFileDownload = null;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'fileUploadAck': {
|
||||||
|
continueFileUpload();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'fileData': {
|
||||||
|
if (currentFileDownload && (currentFileDownload.id == data.id)) {
|
||||||
|
currentFileDownload.data += data.data;
|
||||||
|
changeFileInfo(data.id, 2, (currentFileDownload.data.length * 200 / currentFileDownload.size));
|
||||||
|
send({ action: 'fileUploadAck', id: data.id });
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
default: { console.log('Unhandled object data', data); break; }
|
default: { console.log('Unhandled object data', data); break; }
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -249,7 +292,122 @@
|
|||||||
|
|
||||||
// File sharing button
|
// File sharing button
|
||||||
function fileButtonClick() {
|
function fileButtonClick() {
|
||||||
|
var chooser = Q('uploadFileInput');
|
||||||
|
if (chooser.getAttribute("eventset") != 1) {
|
||||||
|
chooser.setAttribute("eventset", "1");
|
||||||
|
chooser.addEventListener("change", fileSelect, false);
|
||||||
|
}
|
||||||
|
chooser.value = null;
|
||||||
|
chooser.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
function fileSelect() {
|
||||||
|
if (state != 2) return;
|
||||||
|
var x = Q('uploadFileInput');
|
||||||
|
if (x.files.length != 1) return;
|
||||||
|
var reader = new FileReader();
|
||||||
|
reader.onload = function (e) { this.xfile.data = e.target.result; startFileUpload(this.xfile); };
|
||||||
|
reader.xfile = x.files[0];
|
||||||
|
reader.readAsBinaryString(x.files[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function fileDrop(e) {
|
||||||
|
haltEvent(e);
|
||||||
|
if (state != 2) return;
|
||||||
|
if (e.dataTransfer == null || e.dataTransfer.files.length != 1) return;
|
||||||
|
var reader = new FileReader();
|
||||||
|
reader.onload = function (e) { this.xfile.data = e.target.result; startFileUpload(this.xfile); };
|
||||||
|
reader.xfile = e.dataTransfer.files[0];
|
||||||
|
reader.readAsBinaryString(e.dataTransfer.files[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function startFileUpload(file) {
|
||||||
|
if (state != 2) return;
|
||||||
|
file.id = Math.random();
|
||||||
|
fileUploads.push(file);
|
||||||
|
//console.log('XXX', file);
|
||||||
|
QA('xmsg', '<div style="clear:both"></div><div id="FILEUP-' + file.id + '" class="localBubble" style="width:240px;cursor:pointer" onclick="cancelFileTransfer(\'' + file.id + '\')"><div id="FILEUP-ICON-' + file.id + '" class="fileicon" style="float:left;width:32px;height:32px"></div><div><div id="FILEUP-NAME-' + file.id + '" style="height:16px;overflow:hidden">' + file.name + '</div><div style="width:200px;background-color:lightgray;margin-left:32px;border-radius:3px;margin-top:3px;height:11px"><div id="FILEUP-PROGRESS-' + file.id + '" style="width:0px;background-color:green;border-radius:3px;height:11px"> </div></div></div></div>');
|
||||||
|
Q('xmsg').scrollTop = Q('xmsg').scrollHeight;
|
||||||
|
send({ action: 'file', size: file.size, id: file.id, type: file.type, name: file.name });
|
||||||
|
if (currentFileUpload == null) continueFileUpload();
|
||||||
|
}
|
||||||
|
|
||||||
|
function startFileDownload(file) {
|
||||||
|
if (state != 2) return;
|
||||||
|
fileDownloads[file.id] = file;
|
||||||
|
QA('xmsg', '<div style="clear:both"></div><div id="FILEUP-' + file.id + '" class="remoteBubble" style="width:240px;cursor:pointer"><div id="FILEUP-ICON-' + file.id + '" class="fileicon" style="float:left;width:32px;height:32px"></div><div><div id="FILEUP-NAME-' + file.id + '" style="height:16px;overflow:hidden">' + file.name + '</div><div style="width:200px;background-color:lightgray;margin-left:32px;border-radius:3px;margin-top:3px;height:11px"><div id="FILEUP-PROGRESS-' + file.id + '" style="width:0px;background-color:green;border-radius:3px;height:11px"> </div></div></div></div>');
|
||||||
|
Q('xmsg').scrollTop = Q('xmsg').scrollHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Change the file icon and progress
|
||||||
|
function changeFileInfo(id, icon, progress, progressColor) {
|
||||||
|
if (icon) {
|
||||||
|
Q('FILEUP-ICON-' + id).classList.remove('fileicon');
|
||||||
|
Q('FILEUP-ICON-' + id).classList.remove('fileiconx');
|
||||||
|
Q('FILEUP-ICON-' + id).classList.remove('fileicontransfer');
|
||||||
|
Q('FILEUP-ICON-' + id).classList.remove('fileicondone');
|
||||||
|
Q('FILEUP-ICON-' + id).classList.add(['fileicon', 'fileiconx', 'fileicontransfer', 'fileicondone'][icon]);
|
||||||
|
}
|
||||||
|
if (progress) { QS('FILEUP-PROGRESS-' + id)['width'] = progress + 'px'; }
|
||||||
|
if (progressColor) { QS('FILEUP-PROGRESS-' + id)['background-color'] = progressColor; }
|
||||||
|
}
|
||||||
|
|
||||||
|
function cancelFileTransfer(id) {
|
||||||
|
if ((currentFileUpload != null) && (currentFileUpload.id == id)) { currentFileUpload = null; }
|
||||||
|
if ((currentFileDownload != null) && (currentFileDownload.id == id)) { currentFileDownload = null; }
|
||||||
|
|
||||||
|
var found = false;
|
||||||
|
if (fileDownloads[id] && (fileDownloads[id].done != 1)) {
|
||||||
|
delete fileDownloads[id];
|
||||||
|
found = true;
|
||||||
|
} else {
|
||||||
|
for (var i in fileUploads) {
|
||||||
|
if (fileUploads[i].id == id) {
|
||||||
|
send({ action: 'fileUploadCancel', id: id });
|
||||||
|
fileUploads.splice(i, 1);
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (found) { changeFileInfo(id, 1, 200, 'gray'); } // Only cancel a file if it was in the file queue.
|
||||||
|
}
|
||||||
|
|
||||||
|
function cancelAllFileTransfers() {
|
||||||
|
for (var i in fileDownloads) { cancelFileTransfer(fileDownloads[i].id); }
|
||||||
|
for (var i in fileUploads) { cancelFileTransfer(fileUploads[i].id); }
|
||||||
|
}
|
||||||
|
|
||||||
|
function continueFileUpload() {
|
||||||
|
if (currentFileUpload == null) {
|
||||||
|
// Select the next file to upload
|
||||||
|
if (fileUploads.length == 0) { return; } // Nothing to do
|
||||||
|
currentFileUpload = fileUploads[0];
|
||||||
|
currentFileUpload.ptr = 0;
|
||||||
|
|
||||||
|
// Indicate that we are sending this file
|
||||||
|
send({ action: 'fileUploadStart', size: currentFileUpload.size, id: currentFileUpload.id, type: currentFileUpload.type, name: currentFileUpload.name });
|
||||||
|
} else {
|
||||||
|
if (currentFileUpload.size <= currentFileUpload.ptr) {
|
||||||
|
// If we are done, send the end marker
|
||||||
|
send({ action: 'fileUploadEnd', size: currentFileUpload.size, id: currentFileUpload.id, type: currentFileUpload.type, name: currentFileUpload.name });
|
||||||
|
changeFileInfo(currentFileUpload.id, 3, 200);
|
||||||
|
fileUploads.splice(0, 1);
|
||||||
|
currentFileUpload = null;
|
||||||
|
continueFileUpload(); // Send the next file
|
||||||
|
} else {
|
||||||
|
// Send the next block
|
||||||
|
var nextBlockLen = Math.min(4000, currentFileUpload.data.length - currentFileUpload.ptr);
|
||||||
|
var data = currentFileUpload.data.substring(currentFileUpload.ptr, currentFileUpload.ptr + nextBlockLen);
|
||||||
|
send({ action: 'fileData', id: currentFileUpload.id, data: data });
|
||||||
|
currentFileUpload.ptr += nextBlockLen;
|
||||||
|
changeFileInfo(currentFileUpload.id, 0, (currentFileUpload.ptr * 200 / currentFileUpload.size));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function continueFileDownload(msg) {
|
||||||
|
send({ action: 'fileUploadAck', id: msg.id });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Camera button
|
// Camera button
|
||||||
@ -337,9 +495,9 @@
|
|||||||
socket.onclose = function () { disconnect(); }
|
socket.onclose = function () { disconnect(); }
|
||||||
socket.onmessage = function (msg) {
|
socket.onmessage = function (msg) {
|
||||||
if ((state < 2) && (typeof msg.data == 'string') && (msg.data == 'c')) {
|
if ((state < 2) && (typeof msg.data == 'string') && (msg.data == 'c')) {
|
||||||
hangUpButtonClick(0);
|
hangUpButtonClick(0, true);
|
||||||
hangUpButtonClick(1);
|
hangUpButtonClick(1, true);
|
||||||
hangUpButtonClick(2);
|
hangUpButtonClick(2, true);
|
||||||
displayControl('Connected.');
|
displayControl('Connected.');
|
||||||
state = 2;
|
state = 2;
|
||||||
updateControls();
|
updateControls();
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
.topButton {
|
.topButton {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
border: none;
|
border: none;
|
||||||
margin: 3px;
|
margin: 2px;
|
||||||
float: right;
|
float: right;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
height: 32px;
|
height: 32px;
|
||||||
@ -12,6 +12,19 @@
|
|||||||
background-color: lightgray;
|
background-color: lightgray;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.topRedButton {
|
||||||
|
cursor: pointer;
|
||||||
|
border: none;
|
||||||
|
margin: 2px;
|
||||||
|
float: right;
|
||||||
|
border-radius: 3px;
|
||||||
|
height: 32px;
|
||||||
|
width: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.topRedButton:hover {
|
||||||
|
background-color: orangered;
|
||||||
|
}
|
||||||
|
|
||||||
.remoteBubble {
|
.remoteBubble {
|
||||||
background-color: #00cc99;
|
background-color: #00cc99;
|
||||||
@ -87,5 +100,29 @@
|
|||||||
|
|
||||||
.icon11 {
|
.icon11 {
|
||||||
background: url(../images/messenger32.png) -320px 0px;
|
background: url(../images/messenger32.png) -320px 0px;
|
||||||
background-color: gray;
|
background-color: orange;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.fileicon {
|
||||||
|
background: url(../images/messenger32.png) -96px 0px;
|
||||||
|
height: 32px;
|
||||||
|
width: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fileiconx {
|
||||||
|
background: url(../images/messenger32.png) -352px 0px;
|
||||||
|
height: 32px;
|
||||||
|
width: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fileicontransfer {
|
||||||
|
background: url(../images/messenger32.png) -288px 0px;
|
||||||
|
height: 32px;
|
||||||
|
width: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fileicondone {
|
||||||
|
background: url(../images/messenger32.png) -256px 0px;
|
||||||
|
height: 32px;
|
||||||
|
width: 32px;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user