mirror of
https://github.com/schlagmichdoch/PairDrop.git
synced 2025-04-21 15:26:17 -04:00
Add transfer notes: Speed + Time left
This commit is contained in:
parent
00f1a20177
commit
e29ea44025
2 changed files with 159 additions and 44 deletions
|
@ -350,6 +350,14 @@ class Peer {
|
||||||
this._state = Peer.STATE_IDLE;
|
this._state = Peer.STATE_IDLE;
|
||||||
this._busy = false;
|
this._busy = false;
|
||||||
|
|
||||||
|
clearInterval(this._transferStatusInterval);
|
||||||
|
|
||||||
|
this._transferStatusInterval = null;
|
||||||
|
this._bytesTotal = 0;
|
||||||
|
this._bytesReceivedFiles = 0;
|
||||||
|
this._timeStart = null;
|
||||||
|
this._byteLogs = [];
|
||||||
|
|
||||||
// tidy up sender
|
// tidy up sender
|
||||||
this._filesRequested = null;
|
this._filesRequested = null;
|
||||||
this._chunker = null;
|
this._chunker = null;
|
||||||
|
@ -357,7 +365,6 @@ class Peer {
|
||||||
// tidy up receiver
|
// tidy up receiver
|
||||||
this._pendingRequest = null;
|
this._pendingRequest = null;
|
||||||
this._acceptedRequest = null;
|
this._acceptedRequest = null;
|
||||||
this._totalBytesReceived = 0;
|
|
||||||
this._digester = null;
|
this._digester = null;
|
||||||
this._filesReceived = [];
|
this._filesReceived = [];
|
||||||
|
|
||||||
|
@ -623,6 +630,79 @@ class Peer {
|
||||||
this._reset();
|
this._reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_addLog(bytesReceivedCurrentFile) {
|
||||||
|
const now = Date.now();
|
||||||
|
|
||||||
|
// Add log
|
||||||
|
this._byteLogs.push({
|
||||||
|
time: now,
|
||||||
|
bytesReceived: this._bytesReceivedFiles + bytesReceivedCurrentFile
|
||||||
|
});
|
||||||
|
|
||||||
|
// Always include at least 5 entries (2.5 MB) to increase precision
|
||||||
|
if (this._byteLogs.length < 5) return;
|
||||||
|
|
||||||
|
// Move running average to calculate with a window of 20s
|
||||||
|
while (now - this._byteLogs[0].time > 20000) {
|
||||||
|
this._byteLogs.shift();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_setTransferStatus(status) {
|
||||||
|
const secondsSinceStart = Math.round((Date.now() - this._timeStartTransferComplete) / 1000);
|
||||||
|
|
||||||
|
// Wait for 10s to only show info on longer transfers and to increase precision
|
||||||
|
if (secondsSinceStart < 10) return;
|
||||||
|
|
||||||
|
// mode: 0 -> speed, 1 -> time left, 2 -> receive/transfer
|
||||||
|
const mode = Math.round((secondsSinceStart - 10) / 5) % 3;
|
||||||
|
|
||||||
|
if (mode === 0) {
|
||||||
|
status = this._getSpeedString();
|
||||||
|
}
|
||||||
|
else if (mode === 1) {
|
||||||
|
status = this._getTimeString();
|
||||||
|
}
|
||||||
|
|
||||||
|
this._transferStatusString = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
_calculateSpeedKbPerSecond() {
|
||||||
|
const timeDifferenceSeconds = (this._byteLogs[this._byteLogs.length - 1].time - this._byteLogs[0].time) / 1000;
|
||||||
|
const bytesDifferenceKB = (this._byteLogs[this._byteLogs.length - 1].bytesReceived - this._byteLogs[0].bytesReceived) / 1000;
|
||||||
|
return bytesDifferenceKB / timeDifferenceSeconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
_calculateBytesLeft() {
|
||||||
|
return this._bytesTotal - this._byteLogs[this._byteLogs.length - 1].bytesReceived;
|
||||||
|
}
|
||||||
|
|
||||||
|
_calculateSecondsLeft() {
|
||||||
|
return Math.ceil(this._calculateBytesLeft() / this._calculateSpeedKbPerSecond() / 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
_getSpeedString() {
|
||||||
|
const speedKBs = this._calculateSpeedKbPerSecond();
|
||||||
|
if (speedKBs >= 1000) {
|
||||||
|
let speedMBs = Math.round(speedKBs / 100) / 10;
|
||||||
|
return `${speedMBs} MB/s`; // e.g. "2.2 MB/s"
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${speedKBs} kB/s`; // e.g. "522 kB/s"
|
||||||
|
}
|
||||||
|
|
||||||
|
_getTimeString() {
|
||||||
|
const seconds = this._calculateSecondsLeft();
|
||||||
|
if (seconds > 60) {
|
||||||
|
let minutes = Math.floor(seconds / 60);
|
||||||
|
let secondsLeft = Math.floor(seconds % 60);
|
||||||
|
return `${minutes} min ${secondsLeft}s`; // e.g. // "1min 20s"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return `${seconds}s`; // e.g. "35s"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// File Sender Only
|
// File Sender Only
|
||||||
async _sendFileTransferRequest(files) {
|
async _sendFileTransferRequest(files) {
|
||||||
this._state = Peer.STATE_PREPARE;
|
this._state = Peer.STATE_PREPARE;
|
||||||
|
@ -655,6 +735,7 @@ class Peer {
|
||||||
Events.fire('set-progress', {peerId: this._peerId, progress: 0, status: 'wait'});
|
Events.fire('set-progress', {peerId: this._peerId, progress: 0, status: 'wait'});
|
||||||
|
|
||||||
this._filesRequested = files;
|
this._filesRequested = files;
|
||||||
|
this._bytesTotal = totalSize;
|
||||||
|
|
||||||
this._sendMessage({type: 'transfer-request',
|
this._sendMessage({type: 'transfer-request',
|
||||||
header: header,
|
header: header,
|
||||||
|
@ -691,7 +772,18 @@ class Peer {
|
||||||
this._filesQueue.push(this._filesRequested[i]);
|
this._filesQueue.push(this._filesRequested[i]);
|
||||||
}
|
}
|
||||||
this._filesRequested = null
|
this._filesRequested = null
|
||||||
|
|
||||||
if (this._busy) return;
|
if (this._busy) return;
|
||||||
|
|
||||||
|
this._byteLogs = [];
|
||||||
|
this._bytesReceivedFiles = 0;
|
||||||
|
this._timeStartTransferComplete = Date.now();
|
||||||
|
|
||||||
|
Events.fire('set-progress', {peerId: this._peerId, progress: 0, status: 'transfer'});
|
||||||
|
|
||||||
|
this._transferStatusString = 'transfer';
|
||||||
|
this._transferStatusInterval = setInterval(() => this._setTransferStatus('transfer'), 1000);
|
||||||
|
|
||||||
this._dequeueFile();
|
this._dequeueFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -727,7 +819,7 @@ class Peer {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Events.fire('set-progress', {peerId: this._peerId, progress: progress, status: 'transfer'});
|
Events.fire('set-progress', {peerId: this._peerId, progress: progress, status: this._transferStatusString});
|
||||||
}
|
}
|
||||||
|
|
||||||
_onReceiveConfirmation(bytesReceived) {
|
_onReceiveConfirmation(bytesReceived) {
|
||||||
|
@ -736,6 +828,8 @@ class Peer {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this._chunker._onReceiveConfirmation(bytesReceived);
|
this._chunker._onReceiveConfirmation(bytesReceived);
|
||||||
|
|
||||||
|
this._addLog(bytesReceived);
|
||||||
}
|
}
|
||||||
|
|
||||||
_onFileReceiveComplete(message) {
|
_onFileReceiveComplete(message) {
|
||||||
|
@ -744,6 +838,8 @@ class Peer {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._bytesReceivedFiles += this._chunker._file.size;
|
||||||
|
|
||||||
this._chunker = null;
|
this._chunker = null;
|
||||||
|
|
||||||
if (!message.success) {
|
if (!message.success) {
|
||||||
|
@ -762,7 +858,7 @@ class Peer {
|
||||||
|
|
||||||
// No more files in queue. Transfer is complete
|
// No more files in queue. Transfer is complete
|
||||||
this._reset();
|
this._reset();
|
||||||
Events.fire('set-progress', {peerId: this._peerId, progress: 0, status: 'transfer-complete'});
|
Events.fire('set-progress', {peerId: this._peerId, progress: 1, status: 'transfer-complete'});
|
||||||
Events.fire('notify-user', Localization.getTranslation("notifications.file-transfer-completed"));
|
Events.fire('notify-user', Localization.getTranslation("notifications.file-transfer-completed"));
|
||||||
Events.fire('files-sent'); // used by 'Snapdrop & PairDrop for Android' app
|
Events.fire('files-sent'); // used by 'Snapdrop & PairDrop for Android' app
|
||||||
}
|
}
|
||||||
|
@ -822,16 +918,25 @@ class Peer {
|
||||||
message.reason = reason;
|
message.reason = reason;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._sendMessage(message);
|
|
||||||
|
|
||||||
if (accepted) {
|
if (accepted) {
|
||||||
this._state = Peer.STATE_RECEIVE_PROCEEDING;
|
this._state = Peer.STATE_RECEIVE_PROCEEDING;
|
||||||
this._busy = true;
|
this._busy = true;
|
||||||
|
this._byteLogs = [];
|
||||||
|
this._filesReceived = [];
|
||||||
this._acceptedRequest = this._pendingRequest;
|
this._acceptedRequest = this._pendingRequest;
|
||||||
this._lastProgress = 0;
|
this._lastProgress = 0;
|
||||||
this._totalBytesReceived = 0;
|
|
||||||
this._filesReceived = [];
|
this._bytesTotal = this._acceptedRequest.totalSize;
|
||||||
|
this._bytesReceivedFiles = 0;
|
||||||
|
|
||||||
|
Events.fire('set-progress', {peerId: this._peerId, progress: 0, status: 'receive'});
|
||||||
|
|
||||||
|
this._timeStartTransferComplete = Date.now();
|
||||||
|
this._transferStatusString = 'receive';
|
||||||
|
this._transferStatusInterval = setInterval(() => this._setTransferStatus('receive'), 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._sendMessage(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
_onTransferHeader(header) {
|
_onTransferHeader(header) {
|
||||||
|
@ -847,7 +952,7 @@ class Peer {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._timeStart = Date.now();
|
this._timeStartTransferFile = Date.now();
|
||||||
|
|
||||||
this._addFileDigester(header);
|
this._addFileDigester(header);
|
||||||
}
|
}
|
||||||
|
@ -859,8 +964,10 @@ class Peer {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
_sendReceiveConfirmation(bytesReceived) {
|
_sendReceiveConfirmation(bytesReceivedCurrentFile) {
|
||||||
this._sendMessage({type: 'receive-confirmation', bytesReceived: bytesReceived});
|
this._sendMessage({type: 'receive-confirmation', bytesReceived: bytesReceivedCurrentFile});
|
||||||
|
|
||||||
|
this._addLog(bytesReceivedCurrentFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
_sendResendRequest(offset) {
|
_sendResendRequest(offset) {
|
||||||
|
@ -892,10 +999,10 @@ class Peer {
|
||||||
|
|
||||||
// While transferring -> round progress to 4th digit. After transferring, set it to 1.
|
// While transferring -> round progress to 4th digit. After transferring, set it to 1.
|
||||||
let progress = this._digester
|
let progress = this._digester
|
||||||
? Math.floor(1e4 * (this._totalBytesReceived + this._digester._bytesReceived) / this._acceptedRequest.totalSize) / 1e4
|
? Math.floor(1e4 * (this._bytesReceivedFiles + this._digester._bytesReceived) / this._acceptedRequest.totalSize) / 1e4
|
||||||
: 1;
|
: 1;
|
||||||
|
|
||||||
Events.fire('set-progress', {peerId: this._peerId, progress: progress, status: 'receive'});
|
Events.fire('set-progress', {peerId: this._peerId, progress: progress, status: this._transferStatusString});
|
||||||
|
|
||||||
// occasionally notify sender about our progress
|
// occasionally notify sender about our progress
|
||||||
if (progress - this._lastProgress >= 0.005 || progress === 1) {
|
if (progress - this._lastProgress >= 0.005 || progress === 1) {
|
||||||
|
@ -946,9 +1053,9 @@ class Peer {
|
||||||
this._digester._sendReceiveConfimationCallback = null;
|
this._digester._sendReceiveConfimationCallback = null;
|
||||||
this._digester = null;
|
this._digester = null;
|
||||||
|
|
||||||
this._totalBytesReceived += file.size;
|
this._bytesReceivedFiles += file.size;
|
||||||
|
|
||||||
const duration = (Date.now() - this._timeStart) / 1000; // s
|
const duration = (Date.now() - this._timeStartTransferFile) / 1000; // s
|
||||||
const size = Math.round(10 * file.size / 1e6) / 10; // MB
|
const size = Math.round(10 * file.size / 1e6) / 10; // MB
|
||||||
const speed = Math.round(100 * size / duration) / 100; // MB/s
|
const speed = Math.round(100 * size / duration) / 100; // MB/s
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,7 @@ class PeersUI {
|
||||||
Events.on('peer-connecting', e => this._onPeerConnecting(e.detail));
|
Events.on('peer-connecting', e => this._onPeerConnecting(e.detail));
|
||||||
Events.on('peer-disconnected', e => this._onPeerDisconnected(e.detail));
|
Events.on('peer-disconnected', e => this._onPeerDisconnected(e.detail));
|
||||||
Events.on('peers', e => this._onPeers(e.detail));
|
Events.on('peers', e => this._onPeers(e.detail));
|
||||||
Events.on('set-progress', e => this._onSetProgress(e.detail));
|
Events.on('set-progress', e => this._onSetProgress(e.detail.peerId, e.detail.progress, e.detail.status));
|
||||||
|
|
||||||
Events.on('drop', e => this._onDrop(e));
|
Events.on('drop', e => this._onDrop(e));
|
||||||
Events.on('keydown', e => this._onKeyDown(e));
|
Events.on('keydown', e => this._onKeyDown(e));
|
||||||
|
@ -185,12 +185,12 @@ class PeersUI {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
_onSetProgress(progress) {
|
_onSetProgress(peerId, progress, status) {
|
||||||
const peerUI = this.peerUIs[progress.peerId];
|
const peerUI = this.peerUIs[peerId];
|
||||||
|
|
||||||
if (!peerUI) return;
|
if (!peerUI) return;
|
||||||
|
|
||||||
peerUI.setProgressOrQueue(progress.progress, progress.status);
|
peerUI.setProgressOrQueue(progress, status);
|
||||||
}
|
}
|
||||||
|
|
||||||
_onDrop(e) {
|
_onDrop(e) {
|
||||||
|
@ -692,18 +692,19 @@ class PeerUI {
|
||||||
|
|
||||||
setProgressOrQueue(progress, status) {
|
setProgressOrQueue(progress, status) {
|
||||||
if (this._progressQueue.length > 0) {
|
if (this._progressQueue.length > 0) {
|
||||||
// add to queue
|
if (progress) {
|
||||||
this._progressQueue.push({progress: progress, status: status});
|
// if progress is higher than progress in queue -> overwrite in queue and cut queue at this position
|
||||||
|
for (let i = 0; i < this._progressQueue.length; i++) {
|
||||||
for (let i = 0; i < this._progressQueue.length; i++) {
|
if (this._progressQueue[i].progress <= progress) {
|
||||||
if (this._progressQueue[i].progress <= progress) {
|
this._progressQueue[i].progress = progress;
|
||||||
// if progress is higher than progress in queue -> overwrite in queue and cut queue at this position
|
this._progressQueue[i].status = status;
|
||||||
this._progressQueue[i].progress = progress;
|
this._progressQueue.splice(i + 1);
|
||||||
this._progressQueue[i].status = status;
|
return;
|
||||||
this._progressQueue = this._progressQueue.slice(0, i + 1);
|
}
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// add to queue
|
||||||
|
this._progressQueue.push({progress: progress, status: status});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -711,17 +712,19 @@ class PeerUI {
|
||||||
}
|
}
|
||||||
|
|
||||||
setNextProgress() {
|
setNextProgress() {
|
||||||
if (this._progressQueue.length > 0) {
|
if (!this._progressQueue.length) return;
|
||||||
setTimeout(() => {
|
|
||||||
let next = this._progressQueue.shift()
|
setTimeout(() => {
|
||||||
this.setProgress(next.progress, next.status);
|
let next = this._progressQueue.shift()
|
||||||
}, 250); // 200 ms animation + buffer
|
this.setProgress(next.progress, next.status);
|
||||||
}
|
}, 250); // 200 ms animation + buffer
|
||||||
}
|
}
|
||||||
|
|
||||||
setProgress(progress, status) {
|
setProgress(progress, status) {
|
||||||
this.setStatus(status);
|
this.setStatus(status);
|
||||||
|
|
||||||
|
if (progress === null) return;
|
||||||
|
|
||||||
const progressSpillsOverHalf = this._currentProgress < 0.5 && 0.5 < progress; // 0.5 slips through
|
const progressSpillsOverHalf = this._currentProgress < 0.5 && 0.5 < progress; // 0.5 slips through
|
||||||
const progressSpillsOverFull = progress <= 0.5 && 0.5 <= this._currentProgress && this._currentProgress < 1;
|
const progressSpillsOverFull = progress <= 0.5 && 0.5 <= this._currentProgress && this._currentProgress < 1;
|
||||||
|
|
||||||
|
@ -763,7 +766,7 @@ class PeerUI {
|
||||||
this.$progress.classList.add('animate');
|
this.$progress.classList.add('animate');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._currentProgress < progress) {
|
if (progress > this._currentProgress) {
|
||||||
this.$progress.classList.add('animate');
|
this.$progress.classList.add('animate');
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -772,29 +775,30 @@ class PeerUI {
|
||||||
|
|
||||||
this.$progress.style.setProperty('--progress', `rotate(${360 * progress}deg)`);
|
this.$progress.style.setProperty('--progress', `rotate(${360 * progress}deg)`);
|
||||||
|
|
||||||
this._currentProgress = progress
|
|
||||||
|
|
||||||
if (progress === 1) {
|
if (progress === 1) {
|
||||||
// reset progress
|
// reset progress
|
||||||
this._progressQueue.unshift({progress: 0, status: status});
|
this._progressQueue.unshift({progress: 0, status: status});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._currentProgress = progress;
|
||||||
|
|
||||||
this.setNextProgress();
|
this.setNextProgress();
|
||||||
}
|
}
|
||||||
|
|
||||||
setStatus(status) {
|
setStatus(status) {
|
||||||
if (status === this._currentStatus) return;
|
if (status === this._currentStatus) return;
|
||||||
|
|
||||||
|
this._currentStatus = status;
|
||||||
|
|
||||||
clearTimeout(this.statusTimeout);
|
clearTimeout(this.statusTimeout);
|
||||||
|
|
||||||
if (!status) {
|
if (!status) {
|
||||||
this.$el.removeAttribute('status');
|
this.$el.removeAttribute('status');
|
||||||
this.$el.querySelector('.status').innerHTML = '';
|
this.$el.querySelector('.status').innerText = '';
|
||||||
this._currentStatus = null;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let statusName = {
|
let statusText = {
|
||||||
"connect": Localization.getTranslation("peer-ui.connecting"),
|
"connect": Localization.getTranslation("peer-ui.connecting"),
|
||||||
"prepare": Localization.getTranslation("peer-ui.preparing"),
|
"prepare": Localization.getTranslation("peer-ui.preparing"),
|
||||||
"transfer": Localization.getTranslation("peer-ui.transferring"),
|
"transfer": Localization.getTranslation("peer-ui.transferring"),
|
||||||
|
@ -806,11 +810,15 @@ class PeerUI {
|
||||||
"error": Localization.getTranslation("peer-ui.error")
|
"error": Localization.getTranslation("peer-ui.error")
|
||||||
}[status];
|
}[status];
|
||||||
|
|
||||||
this.$el.setAttribute('status', status);
|
if (statusText) {
|
||||||
this.$el.querySelector('.status').innerText = statusName;
|
this.$el.setAttribute('status', status);
|
||||||
this._currentStatus = status;
|
this.$el.querySelector('.status').innerText = statusText;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.$el.querySelector('.status').innerText = status;
|
||||||
|
}
|
||||||
|
|
||||||
if (["transfer-complete", "receive-complete", "error"].includes(status)) {
|
if (status.endsWith("-complete") || status === "error") {
|
||||||
this.statusTimeout = setTimeout(() => {
|
this.statusTimeout = setTimeout(() => {
|
||||||
this.setProgress(0, null);
|
this.setProgress(0, null);
|
||||||
}, 10000);
|
}, 10000);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue