]> source.dussan.org Git - nextcloud-server.git/commitdiff
Smooth time remaining, bitrate and stabilize user information
authorPhlogi <Phlogi@users.noreply.github.com>
Tue, 7 Dec 2021 21:05:13 +0000 (22:05 +0100)
committerFerdinand Thiessen <opensource@fthiessen.de>
Mon, 21 Aug 2023 21:18:45 +0000 (23:18 +0200)
Besides the existing moving average, a smoothing factor is introduced for the time remaining display as well as the bitrate.
Furthermore, half of the buffer needs to be filled before the first prediction is displayed to the user. This reduces volatile and jumping durations towards the user and improves usability.

Signed-off-by: Cyrill H. <phlogi@posteo.de>
Co-authored-by: Carl Schwan <carl@carlschwan.eu>
apps/files/js/file-upload.js

index f3a39e5861ad593f91c10474a73230e1e477375c..7a129a989d11b227fe4f707c3ee725befaf23f24 100644 (file)
@@ -955,6 +955,7 @@ OC.Uploader.prototype = _.extend({
                                type: 'PUT',
                                dropZone: options.dropZone, // restrict dropZone to content div
                                autoUpload: false,
+                               progressInterval: 300, // increased from the default of 100ms for more stable behvaviour when predicting remaining time
                                sequentialUploads: false,
                                limitConcurrentUploads: 4,
                                /**
@@ -1197,7 +1198,7 @@ OC.Uploader.prototype = _.extend({
 
                        if (this._supportAjaxUploadWithProgress()) {
                                //remaining time
-                               var lastUpdate, lastSize, bufferSize, buffer, bufferIndex, bufferIndex2, bufferTotal;
+                               var lastUpdate, lastSize, bufferSize, buffer, bufferIndex, bufferTotal, smoothRemainingSeconds, smoothBitrate;
 
                                var dragging = false;
 
@@ -1215,11 +1216,15 @@ OC.Uploader.prototype = _.extend({
                                        // initial remaining time variables
                                        lastUpdate   = new Date().getTime();
                                        lastSize     = 0;
-                                       bufferSize   = 20;
+                                       bufferSize   = 20; // length of the ring buffer
                                        buffer       = [];
-                                       bufferIndex  = 0;
-                                       bufferIndex2 = 0;
+                                       bufferIndex  = 0; // index of the ring buffer, runs from 0 to bufferSize continuously 
                                        bufferTotal  = 0;
+                                       newTotal     = 0;
+                                       smoothing    = 0.02; // smoothing factor for EMA
+                                       h            = '';
+                                       bufferFilled = false;
+
                                        for(var i = 0; i < bufferSize; i++){
                                                buffer[i]  = 0;
                                        }
@@ -1238,33 +1243,54 @@ OC.Uploader.prototype = _.extend({
                                        var diffUpdate = (thisUpdate - lastUpdate)/1000; // eg. 2s
                                        lastUpdate = thisUpdate;
                                        var diffSize = data.loaded - lastSize;
+                                       if (diffSize <= 0) {
+                                               diffSize = lastSize;
+                                       }
                                        lastSize = data.loaded;
                                        diffSize = diffSize / diffUpdate; // apply timing factor, eg. 1MiB/2s = 0.5MiB/s, unit is byte per second
                                        var remainingSeconds = ((total - data.loaded) / diffSize);
+
                                        if(remainingSeconds >= 0) {
+                                               // bufferTotal holds the sum of all entries in the buffer, initially 0 like the entries itself
+                                               // substract current entry from total and add the current value to total
                                                bufferTotal = bufferTotal - (buffer[bufferIndex]) + remainingSeconds;
+                                               // put current value to the entry
                                                buffer[bufferIndex] = remainingSeconds; //buffer to make it smoother
+
                                                bufferIndex = (bufferIndex + 1) % bufferSize;
-                                               bufferIndex2++;
                                        }
-                                       var smoothRemainingSeconds;
-                                       if (bufferIndex2 > 0 && bufferIndex2 < 20) {
-                                               smoothRemainingSeconds = bufferTotal / bufferIndex2;
-                                       } else if (bufferSize > 0) {
+                                       if (bufferIndex === bufferSize - 1) {
+                                               bufferFilled = true;
+                                       }
+                                       //console.log('#', ' idx: ',bufferIndex, ' Total: ', bufferTotal, ' remainSeconds: ', remainingSeconds, ' during: ', diffUpdate);
+
+                                       if (smoothRemainingSeconds === null) {
                                                smoothRemainingSeconds = bufferTotal / bufferSize;
-                                       } else {
-                                               smoothRemainingSeconds = 1;
+                                       } else{
+                                               smoothRemainingSeconds = smoothing * (bufferTotal / bufferSize) + ((1-smoothing) * smoothRemainingSeconds);
+                                       }
+
+                                       if (bufferIndex % 4 === 0) {
+                                               h = moment.duration(smoothRemainingSeconds, "seconds").humanize({m: 50, h: 50});
                                        }
 
-                                       var h = moment.duration(smoothRemainingSeconds, "seconds").humanize();
-                                       if (!(smoothRemainingSeconds >= 0 && smoothRemainingSeconds < 14400)) {
+                                       // wait for the buffer to be at least half filled, approximately takes 3s
+                                       if (!(smoothRemainingSeconds >= 0 && smoothRemainingSeconds < 14400) || (bufferFilled == false && bufferIndex < bufferSize/2)) {
                                                // show "Uploading ..." for durations longer than 4 hours
                                                h = t('files', 'Uploading …');
                                        }
+
+                                       // smooth bitrate
+                                       if (smoothBitrate === null) {
+                                               smoothBitrate = data.bitrate;
+                                       } else{
+                                               smoothBitrate = smoothing * data.bitrate + ((1-smoothing) * smoothBitrate);
+                                       }
                                        self._setProgressBarText(h, h, t('files', '{loadedSize} of {totalSize} ({bitrate})' , {
                                                        loadedSize: OC.Util.humanFileSize(data.loaded),
                                                        totalSize: OC.Util.humanFileSize(total),
-                                                       bitrate: OC.Util.humanFileSize(data.bitrate / 8) + '/s'
+                                                       bitrate: OC.Util.humanFileSize(smoothBitrate / 8) + '/s'
                                                }));
                                        self._setProgressBarValue(progress);
                                        self.trigger('progressall', e, data);