Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Companion stream upload unknown size files #5489

Open
wants to merge 12 commits into
base: main
Choose a base branch
from

Conversation

mifi
Copy link
Contributor

@mifi mifi commented Oct 21, 2024

behind a new option streamingUploadSizeless / COMPANION_STREAMING_UPLOAD_SIZELESS

originally thought for Tus, but italso seems to be working with other protocols

closes #5305

one issue is that the progress shows X kB of 0 B * 0s left:
Screenshot 2024-10-21 at 12 03 01

update: will now show better progress. in the following example, i upload 2 local files of known length, and 2 URLs (companion) with unknown size:

1
2
3
4
5
6
7

behind a new option streamingUploadSizeless
COMPANION_STREAMING_UPLOAD_SIZELESS
for tus
seems to be working
closes #5305
Copy link
Contributor

github-actions bot commented Oct 21, 2024

Diff output files
diff --git a/packages/@uppy/companion-client/lib/RequestClient.js b/packages/@uppy/companion-client/lib/RequestClient.js
index 70e2b15..419aaf9 100644
--- a/packages/@uppy/companion-client/lib/RequestClient.js
+++ b/packages/@uppy/companion-client/lib/RequestClient.js
@@ -6,7 +6,6 @@ var id = 0;
 function _classPrivateFieldLooseKey(e) {
   return "__private_" + id++ + "_" + e;
 }
-import emitSocketProgress from "@uppy/utils/lib/emitSocketProgress";
 import ErrorWithCause from "@uppy/utils/lib/ErrorWithCause";
 import fetchWithNetworkError from "@uppy/utils/lib/fetchWithNetworkError";
 import getSocketHost from "@uppy/utils/lib/getSocketHost";
@@ -59,6 +58,22 @@ async function handleJSONResponse(res) {
     message: errMsg,
   });
 }
+function emitSocketProgress(uploader, progressData, file) {
+  const {
+    progress,
+    bytesUploaded,
+    bytesTotal,
+  } = progressData;
+  if (progress) {
+    var _file$progress$upload;
+    uploader.uppy.log(`Upload progress: ${progress}`);
+    uploader.uppy.emit("upload-progress", file, {
+      uploadStarted: (_file$progress$upload = file.progress.uploadStarted) != null ? _file$progress$upload : 0,
+      bytesUploaded,
+      bytesTotal,
+    });
+  }
+}
 var _companionHeaders = _classPrivateFieldLooseKey("companionHeaders");
 var _getUrl = _classPrivateFieldLooseKey("getUrl");
 var _requestSocketToken = _classPrivateFieldLooseKey("requestSocketToken");
diff --git a/packages/@uppy/companion/lib/config/companion.d.ts b/packages/@uppy/companion/lib/config/companion.d.ts
index 3924a64..cb9fcf4 100644
--- a/packages/@uppy/companion/lib/config/companion.d.ts
+++ b/packages/@uppy/companion/lib/config/companion.d.ts
@@ -15,6 +15,7 @@ export namespace defaultOptions {
     let allowLocalUrls: boolean;
     let periodicPingUrls: any[];
     let streamingUpload: boolean;
+    let streamingUploadSizeless: boolean;
     let clientSocketConnectTimeout: number;
     let metrics: boolean;
 }
diff --git a/packages/@uppy/companion/lib/config/companion.js b/packages/@uppy/companion/lib/config/companion.js
index 542f378..67706a6 100644
--- a/packages/@uppy/companion/lib/config/companion.js
+++ b/packages/@uppy/companion/lib/config/companion.js
@@ -21,6 +21,7 @@ const defaultOptions = {
   allowLocalUrls: false,
   periodicPingUrls: [],
   streamingUpload: true,
+  streamingUploadSizeless: false,
   clientSocketConnectTimeout: 60000,
   metrics: true,
 };
diff --git a/packages/@uppy/companion/lib/server/Uploader.d.ts b/packages/@uppy/companion/lib/server/Uploader.d.ts
index 945e941..989d8d3 100644
--- a/packages/@uppy/companion/lib/server/Uploader.d.ts
+++ b/packages/@uppy/companion/lib/server/Uploader.d.ts
@@ -94,10 +94,11 @@ declare class Uploader {
     downloadedBytes: number;
     readStream: import("stream").Readable | fs.ReadStream;
     abortReadStream(err: any): void;
+    _getUploadProtocol(): string;
     _uploadByProtocol(req: any): Promise<any>;
     _downloadStreamAsFile(stream: any): Promise<void>;
     tmpPath: string;
-    _needDownloadFirst(): boolean;
+    _canStream(): any;
     /**
      *
      * @param {import('stream').Readable} stream
diff --git a/packages/@uppy/companion/lib/server/Uploader.js b/packages/@uppy/companion/lib/server/Uploader.js
index 8368897..8fce1fe 100644
--- a/packages/@uppy/companion/lib/server/Uploader.js
+++ b/packages/@uppy/companion/lib/server/Uploader.js
@@ -192,10 +192,13 @@ class Uploader {
       this.readStream.destroy(err);
     }
   }
-  async _uploadByProtocol(req) {
+  _getUploadProtocol() {
     // todo a default protocol should not be set. We should ensure that the user specifies their protocol.
     // after we drop old versions of uppy client we can remove this
-    const protocol = this.options.protocol || PROTOCOLS.multipart;
+    return this.options.protocol || PROTOCOLS.multipart;
+  }
+  async _uploadByProtocol(req) {
+    const protocol = this._getUploadProtocol();
     switch (protocol) {
       case PROTOCOLS.multipart:
         return this.#uploadMultipart(this.readStream);
@@ -226,8 +229,10 @@ class Uploader {
     const fileStream = createReadStream(this.tmpPath);
     this.readStream = fileStream;
   }
-  _needDownloadFirst() {
-    return !this.options.size || !this.options.companionOptions.streamingUpload;
+  _canStream() {
+    return this.options.companionOptions.streamingUpload && (this.options.size
+      // only tus uploads can be streamed without size, TODO: add also others
+      || this.options.companionOptions.streamingUploadSizeless);
   }
   /**
    * @param {import('stream').Readable} stream
@@ -243,7 +248,7 @@ class Uploader {
       }
       this.#uploadState = states.uploading;
       this.readStream = stream;
-      if (this._needDownloadFirst()) {
+      if (!this._canStream()) {
         logger.debug("need to download the whole file first", "controller.get.provider.size", this.shortToken);
         // Some streams need to be downloaded entirely first, because we don't know their size from the provider
         // This is true for zoom and drive (exported files) or some URL downloads.
@@ -385,7 +390,7 @@ class Uploader {
     // If fully downloading before uploading, combine downloaded and uploaded bytes
     // This will make sure that the user sees half of the progress before upload starts (while downloading)
     let combinedBytes = bytesUploaded;
-    if (this._needDownloadFirst()) {
+    if (!this._canStream()) {
       combinedBytes = Math.floor((combinedBytes + (this.downloadedBytes || 0)) / 2);
     }
     // Prevent divide by zero
@@ -536,7 +541,7 @@ class Uploader {
       const httpMethod = (this.options.httpMethod || "").toUpperCase() === "PUT" ? "put" : "post";
       const runRequest = (await got)[httpMethod];
       const response = await runRequest(url, reqOptions);
-      if (bytesUploaded !== this.size) {
+      if (this.size != null && bytesUploaded !== this.size) {
         const errMsg = `uploaded only ${bytesUploaded} of ${this.size} with status: ${response.statusCode}`;
         logger.error(errMsg, "upload.multipart.mismatch.error");
         throw new Error(errMsg);
diff --git a/packages/@uppy/companion/lib/standalone/helper.js b/packages/@uppy/companion/lib/standalone/helper.js
index edaa622..fecf501 100644
--- a/packages/@uppy/companion/lib/standalone/helper.js
+++ b/packages/@uppy/companion/lib/standalone/helper.js
@@ -180,6 +180,9 @@ const getConfigFromEnv = () => {
     streamingUpload: process.env.COMPANION_STREAMING_UPLOAD
       ? process.env.COMPANION_STREAMING_UPLOAD === "true"
       : undefined,
+    streamingUploadSizeless: process.env.COMPANION_STREAMING_UPLOAD_SIZELESS
+      ? process.env.COMPANION_STREAMING_UPLOAD_SIZELESS === "true"
+      : undefined,
     maxFileSize: process.env.COMPANION_MAX_FILE_SIZE ? parseInt(process.env.COMPANION_MAX_FILE_SIZE, 10) : undefined,
     chunkSize: process.env.COMPANION_CHUNK_SIZE ? parseInt(process.env.COMPANION_CHUNK_SIZE, 10) : undefined,
     clientSocketConnectTimeout: process.env.COMPANION_CLIENT_SOCKET_CONNECT_TIMEOUT
diff --git a/packages/@uppy/core/lib/Uppy.js b/packages/@uppy/core/lib/Uppy.js
index abb82f2..db50f57 100644
--- a/packages/@uppy/core/lib/Uppy.js
+++ b/packages/@uppy/core/lib/Uppy.js
@@ -24,6 +24,7 @@ const packageJson = {
 import locale from "./locale.js";
 const defaultUploadState = {
   totalProgress: 0,
+  progress: null,
   allowNewUpload: true,
   error: null,
   recoveredState: null,
@@ -42,6 +43,10 @@ var _assertNewUploadAllowed = _classPrivateFieldLooseKey("assertNewUploadAllowed
 var _transformFile = _classPrivateFieldLooseKey("transformFile");
 var _startIfAutoProceed = _classPrivateFieldLooseKey("startIfAutoProceed");
 var _checkAndUpdateFileState = _classPrivateFieldLooseKey("checkAndUpdateFileState");
+var _handleUploadProgress = _classPrivateFieldLooseKey("handleUploadProgress");
+var _updateTotalProgress = _classPrivateFieldLooseKey("updateTotalProgress");
+var _updateTotalProgressThrottled = _classPrivateFieldLooseKey("updateTotalProgressThrottled");
+var _calculateTotalProgress = _classPrivateFieldLooseKey("calculateTotalProgress");
 var _addListeners = _classPrivateFieldLooseKey("addListeners");
 var _updateOnlineStatus = _classPrivateFieldLooseKey("updateOnlineStatus");
 var _requestClientById = _classPrivateFieldLooseKey("requestClientById");
@@ -66,6 +71,12 @@ export class Uppy {
     Object.defineProperty(this, _addListeners, {
       value: _addListeners2,
     });
+    Object.defineProperty(this, _calculateTotalProgress, {
+      value: _calculateTotalProgress2,
+    });
+    Object.defineProperty(this, _updateTotalProgress, {
+      value: _updateTotalProgress2,
+    });
     Object.defineProperty(this, _checkAndUpdateFileState, {
       value: _checkAndUpdateFileState2,
     });
@@ -117,9 +128,10 @@ export class Uppy {
     });
     this.scheduledAutoProceed = null;
     this.wasOffline = false;
-    this.calculateProgress = throttle(
-      (file, data) => {
-        const fileInState = this.getFile(file == null ? void 0 : file.id);
+    Object.defineProperty(this, _handleUploadProgress, {
+      writable: true,
+      value: (file, progress) => {
+        const fileInState = file ? this.getFile(file.id) : undefined;
         if (file == null || !fileInState) {
           this.log(`Not setting progress for a file that has been removed: ${file == null ? void 0 : file.id}`);
           return;
@@ -128,23 +140,38 @@ export class Uppy {
           this.log(`Not setting progress for a file that has been already uploaded: ${file.id}`);
           return;
         }
-        const canHavePercentage = Number.isFinite(data.bytesTotal) && data.bytesTotal > 0;
-        this.setFileState(file.id, {
-          progress: {
-            ...fileInState.progress,
-            bytesUploaded: data.bytesUploaded,
-            bytesTotal: data.bytesTotal,
-            percentage: canHavePercentage ? Math.round(data.bytesUploaded / data.bytesTotal * 100) : 0,
-          },
-        });
-        this.calculateTotalProgress();
+        const newProgress = {
+          bytesTotal: progress.bytesTotal,
+          percentage: progress.bytesTotal != null && Number.isFinite(progress.bytesTotal) && progress.bytesTotal > 0
+            ? Math.round(progress.bytesUploaded / progress.bytesTotal * 100)
+            : undefined,
+        };
+        if (fileInState.progress.uploadStarted != null) {
+          this.setFileState(file.id, {
+            progress: {
+              ...fileInState.progress,
+              ...newProgress,
+              bytesUploaded: progress.bytesUploaded,
+            },
+          });
+        } else {
+          this.setFileState(file.id, {
+            progress: {
+              ...fileInState.progress,
+              ...newProgress,
+            },
+          });
+        }
+        _classPrivateFieldLooseBase(this, _updateTotalProgressThrottled)[_updateTotalProgressThrottled]();
       },
-      500,
-      {
+    });
+    Object.defineProperty(this, _updateTotalProgressThrottled, {
+      writable: true,
+      value: throttle(() => _classPrivateFieldLooseBase(this, _updateTotalProgress)[_updateTotalProgress](), 500, {
         leading: true,
         trailing: true,
-      },
-    );
+      }),
+    });
     Object.defineProperty(this, _updateOnlineStatus, {
       writable: true,
       value: this.updateOnlineStatus.bind(this),
@@ -416,7 +443,7 @@ export class Uppy {
   getObjectOfFilesPerState() {
     const {
       files: filesObject,
-      totalProgress,
+      progress: totalProgress,
       error,
     } = this.getState();
     const files = Object.values(filesObject);
@@ -626,7 +653,7 @@ export class Uppy {
       stateUpdate.recoveredState = null;
     }
     this.setState(stateUpdate);
-    this.calculateTotalProgress();
+    _classPrivateFieldLooseBase(this, _updateTotalProgressThrottled)[_updateTotalProgressThrottled]();
     const removedFileIDs = Object.keys(removedFiles);
     removedFileIDs.forEach(fileID => {
       this.emit("file-removed", removedFiles[fileID]);
@@ -752,52 +779,8 @@ export class Uppy {
       (_provider = plugin.provider) == null || _provider.logout == null || _provider.logout();
     });
   }
-  calculateTotalProgress() {
-    const files = this.getFiles();
-    const inProgress = files.filter(file => {
-      return file.progress.uploadStarted || file.progress.preprocess || file.progress.postprocess;
-    });
-    if (inProgress.length === 0) {
-      this.emit("progress", 0);
-      this.setState({
-        totalProgress: 0,
-      });
-      return;
-    }
-    const sizedFiles = inProgress.filter(file => file.progress.bytesTotal != null);
-    const unsizedFiles = inProgress.filter(file => file.progress.bytesTotal == null);
-    if (sizedFiles.length === 0) {
-      const progressMax = inProgress.length * 100;
-      const currentProgress = unsizedFiles.reduce((acc, file) => {
-        return acc + file.progress.percentage;
-      }, 0);
-      const totalProgress = Math.round(currentProgress / progressMax * 100);
-      this.setState({
-        totalProgress,
-      });
-      return;
-    }
-    let totalSize = sizedFiles.reduce((acc, file) => {
-      var _file$progress$bytesT;
-      return acc + ((_file$progress$bytesT = file.progress.bytesTotal) != null ? _file$progress$bytesT : 0);
-    }, 0);
-    const averageSize = totalSize / sizedFiles.length;
-    totalSize += averageSize * unsizedFiles.length;
-    let uploadedSize = 0;
-    sizedFiles.forEach(file => {
-      uploadedSize += file.progress.bytesUploaded;
-    });
-    unsizedFiles.forEach(file => {
-      uploadedSize += averageSize * (file.progress.percentage || 0) / 100;
-    });
-    let totalProgress = totalSize === 0 ? 0 : Math.round(uploadedSize / totalSize * 100);
-    if (totalProgress > 100) {
-      totalProgress = 100;
-    }
-    this.setState({
-      totalProgress,
-    });
-    this.emit("progress", totalProgress);
+  [Symbol.for("uppy test: updateTotalProgress")]() {
+    return _classPrivateFieldLooseBase(this, _updateTotalProgress)[_updateTotalProgress]();
   }
   updateOnlineStatus() {
     var _window$navigator$onL;
@@ -1239,6 +1222,48 @@ function _checkAndUpdateFileState2(filesToAdd) {
     errors,
   };
 }
+function _updateTotalProgress2() {
+  var _totalProgressPercent, _totalProgressPercent2;
+  const totalProgress = _classPrivateFieldLooseBase(this, _calculateTotalProgress)[_calculateTotalProgress]();
+  let totalProgressPercent = null;
+  if (totalProgress != null) {
+    totalProgressPercent = Math.round(totalProgress * 100);
+    if (totalProgressPercent > 100) totalProgressPercent = 100;
+    else if (totalProgressPercent < 0) totalProgressPercent = 0;
+  }
+  this.emit("progress", (_totalProgressPercent = totalProgressPercent) != null ? _totalProgressPercent : 0);
+  this.setState({
+    totalProgress: (_totalProgressPercent2 = totalProgressPercent) != null ? _totalProgressPercent2 : 0,
+    progress: totalProgressPercent,
+  });
+}
+function _calculateTotalProgress2() {
+  const files = this.getFiles();
+  const filesInProgress = files.filter(file => {
+    return file.progress.uploadStarted || file.progress.preprocess || file.progress.postprocess;
+  });
+  if (filesInProgress.length === 0) {
+    return 0;
+  }
+  if (filesInProgress.every(file => file.progress.uploadComplete)) {
+    return 1;
+  }
+  const isSizedFile = file => file.progress.bytesTotal != null && file.progress.bytesTotal !== 0;
+  const sizedFilesInProgress = filesInProgress.filter(isSizedFile);
+  const unsizedFilesInProgress = filesInProgress.filter(file => !isSizedFile(file));
+  if (
+    sizedFilesInProgress.every(file => file.progress.uploadComplete) && unsizedFilesInProgress.length > 0
+    && !unsizedFilesInProgress.every(file => file.progress.uploadComplete)
+  ) {
+    return null;
+  }
+  const totalFilesSize = sizedFilesInProgress.reduce((acc, file) => {
+    var _file$progress$bytesT;
+    return acc + ((_file$progress$bytesT = file.progress.bytesTotal) != null ? _file$progress$bytesT : 0);
+  }, 0);
+  const totalUploadedSize = sizedFilesInProgress.reduce((acc, file) => acc + (file.progress.bytesUploaded || 0), 0);
+  return totalFilesSize === 0 ? 0 : totalUploadedSize / totalFilesSize;
+}
 function _addListeners2() {
   const errorHandler = (error, file, response) => {
     let errorMsg = error.message || "Unknown error";
@@ -1312,7 +1337,6 @@ function _addListeners2() {
       progress: {
         uploadStarted: Date.now(),
         uploadComplete: false,
-        percentage: 0,
         bytesUploaded: 0,
         bytesTotal: file.size,
       },
@@ -1320,7 +1344,7 @@ function _addListeners2() {
     this.patchFilesState(filesState);
   };
   this.on("upload-start", onUploadStarted);
-  this.on("upload-progress", this.calculateProgress);
+  this.on("upload-progress", _classPrivateFieldLooseBase(this, _handleUploadProgress)[_handleUploadProgress]);
   this.on("upload-success", (file, uploadResp) => {
     if (file == null || !this.getFile(file.id)) {
       this.log(`Not setting progress for a file that has been removed: ${file == null ? void 0 : file.id}`);
@@ -1348,7 +1372,7 @@ function _addListeners2() {
         size: uploadResp.bytesUploaded || currentProgress.bytesTotal,
       });
     }
-    this.calculateTotalProgress();
+    _classPrivateFieldLooseBase(this, _updateTotalProgressThrottled)[_updateTotalProgressThrottled]();
   });
   this.on("preprocess-progress", (file, progress) => {
     if (file == null || !this.getFile(file.id)) {
@@ -1413,7 +1437,7 @@ function _addListeners2() {
     });
   });
   this.on("restored", () => {
-    this.calculateTotalProgress();
+    _classPrivateFieldLooseBase(this, _updateTotalProgressThrottled)[_updateTotalProgressThrottled]();
   });
   this.on("dashboard:file-edit-complete", file => {
     if (file) {
diff --git a/packages/@uppy/dashboard/lib/Dashboard.js b/packages/@uppy/dashboard/lib/Dashboard.js
index ea21a5f..ee2fc09 100644
--- a/packages/@uppy/dashboard/lib/Dashboard.js
+++ b/packages/@uppy/dashboard/lib/Dashboard.js
@@ -848,7 +848,7 @@ export default class Dashboard extends UIPlugin {
         isAllComplete,
         isAllPaused,
         totalFileCount: Object.keys(files).length,
-        totalProgress: state.totalProgress,
+        totalProgress: state.progress,
         allowNewUpload,
         acquirers,
         theme,
diff --git a/packages/@uppy/progress-bar/lib/ProgressBar.js b/packages/@uppy/progress-bar/lib/ProgressBar.js
index 45a8401..1d088dd 100644
--- a/packages/@uppy/progress-bar/lib/ProgressBar.js
+++ b/packages/@uppy/progress-bar/lib/ProgressBar.js
@@ -19,8 +19,10 @@ export default class ProgressBar extends UIPlugin {
     this.render = this.render.bind(this);
   }
   render(state) {
-    const progress = state.totalProgress || 0;
-    const isHidden = (progress === 0 || progress === 100) && this.opts.hideAfterFinish;
+    const {
+      progress,
+    } = state;
+    const isHidden = (progress == null || progress === 0 || progress === 100) && this.opts.hideAfterFinish;
     return h(
       "div",
       {
diff --git a/packages/@uppy/status-bar/lib/Components.js b/packages/@uppy/status-bar/lib/Components.js
index 0267dfb..b6f7dff 100644
--- a/packages/@uppy/status-bar/lib/Components.js
+++ b/packages/@uppy/status-bar/lib/Components.js
@@ -243,6 +243,7 @@ function ProgressDetails(props) {
     i18n,
   } = props;
   const ifShowFilesUploadedOfTotal = numUploads > 1;
+  const totalUploadedSizeStr = prettierBytes(totalUploadedSize);
   return h(
     "div",
     {
@@ -258,12 +259,14 @@ function ProgressDetails(props) {
         className: "uppy-StatusBar-additionalInfo",
       },
       ifShowFilesUploadedOfTotal && renderDot(),
-      i18n("dataUploadedOfTotal", {
-        complete: prettierBytes(totalUploadedSize),
-        total: prettierBytes(totalSize),
-      }),
+      totalSize != null
+        ? i18n("dataUploadedOfTotal", {
+          complete: totalUploadedSizeStr,
+          total: prettierBytes(totalSize),
+        })
+        : totalUploadedSizeStr,
       renderDot(),
-      i18n("xTimeLeft", {
+      totalETA != null && i18n("xTimeLeft", {
         time: prettyETA(totalETA),
       }),
     ),
@@ -379,7 +382,7 @@ function ProgressBarUploading(props) {
       },
       h("div", {
         className: "uppy-StatusBar-statusPrimary",
-      }, supportsUploadProgress ? `${title}: ${totalProgress}%` : title),
+      }, supportsUploadProgress && totalProgress != null ? `${title}: ${totalProgress}%` : title),
       renderProgressDetails(),
       showUploadNewlyAddedFiles
         ? h(UploadNewlyAddedFiles, {
diff --git a/packages/@uppy/status-bar/lib/StatusBar.js b/packages/@uppy/status-bar/lib/StatusBar.js
index 9a199d9..eb6e418 100644
--- a/packages/@uppy/status-bar/lib/StatusBar.js
+++ b/packages/@uppy/status-bar/lib/StatusBar.js
@@ -125,7 +125,7 @@ export default class StatusBar extends UIPlugin {
       capabilities,
       files,
       allowNewUpload,
-      totalProgress,
+      progress: totalProgress,
       error,
       recoveredState,
     } = state;
@@ -142,16 +142,21 @@ export default class StatusBar extends UIPlugin {
     const newFilesOrRecovered = recoveredState ? Object.values(files) : newFiles;
     const resumableUploads = !!capabilities.resumableUploads;
     const supportsUploadProgress = capabilities.uploadProgress !== false;
-    let totalSize = 0;
+    let totalSize = null;
     let totalUploadedSize = 0;
+    if (startedFiles.every(f => f.progress.bytesTotal != null && f.progress.bytesTotal !== 0)) {
+      totalSize = 0;
+      startedFiles.forEach(file => {
+        totalSize += file.progress.bytesTotal || 0;
+        totalUploadedSize += file.progress.bytesUploaded || 0;
+      });
+    }
     startedFiles.forEach(file => {
-      totalSize += file.progress.bytesTotal || 0;
       totalUploadedSize += file.progress.bytesUploaded || 0;
     });
     const totalETA = _classPrivateFieldLooseBase(this, _computeSmoothETA)[_computeSmoothETA]({
       uploaded: totalUploadedSize,
       total: totalSize,
-      remaining: totalSize - totalUploadedSize,
     });
     return StatusBarUI({
       error,
@@ -213,8 +218,12 @@ export default class StatusBar extends UIPlugin {
 }
 function _computeSmoothETA2(totalBytes) {
   var _classPrivateFieldLoo, _classPrivateFieldLoo2;
-  if (totalBytes.total === 0 || totalBytes.remaining === 0) {
-    return 0;
+  if (totalBytes.total == null || totalBytes.total === 0) {
+    return null;
+  }
+  const remaining = totalBytes.total - totalBytes.uploaded;
+  if (remaining <= 0) {
+    return null;
   }
   (_classPrivateFieldLoo2 =
       (_classPrivateFieldLoo = _classPrivateFieldLooseBase(this, _lastUpdateTime))[_lastUpdateTime]) != null
@@ -250,7 +259,7 @@ function _computeSmoothETA2(totalBytes) {
       dt,
     );
   _classPrivateFieldLooseBase(this, _previousSpeed)[_previousSpeed] = filteredSpeed;
-  const instantETA = totalBytes.remaining / filteredSpeed;
+  const instantETA = remaining / filteredSpeed;
   const updatedPreviousETA = Math.max(_classPrivateFieldLooseBase(this, _previousETA)[_previousETA] - dt, 0);
   const filteredETA = _classPrivateFieldLooseBase(this, _previousETA)[_previousETA] == null
     ? instantETA

@Murderlon
Copy link
Member

one issue is that the progress shows X kB of 0 B * 0s left:

I think we should at least have a plan for this before we merge this? Perhaps we can detect unknown file sizes in Uppy and exclude it from the total progress, once there is only the unknown file left we show some sort of continuous loading state without progress?

and fix bug where progress was not always emitted
only do it on total progress
- only show progress percent and total bytes for files that we know the size of. (but all files will still be included in number of files)
- use `null` as an unknown value for progress and ETA, allowing us to remove ETA from UI when unknown
- `percentage` make use of `undefined` when progress is not yet known - don't show percentage in UI when unknown
- add a new state field `progress` that's the same as `totalProgress` but can also be `null`
if not, then it leaves zombie companion instances running in the background when e2e stops
have to be manually killed before running e2e again
Copy link

New and removed dependencies detected. Learn more about Socket for GitHub ↗︎

Package New capabilities Transitives Size Publisher
npm/@types/[email protected] None 0 57.8 kB types
npm/[email protected] filesystem 0 393 kB sgravrock
npm/[email protected] None 0 84.9 kB typescript-bot
npm/[email protected] None 0 2.09 MB google-wombot

🚮 Removed packages: npm/@ampproject/[email protected], npm/@babel/[email protected], npm/@babel/[email protected], npm/@babel/[email protected], npm/@babel/[email protected], npm/@babel/[email protected], npm/@babel/[email protected], npm/@babel/[email protected], npm/@babel/[email protected], npm/@babel/[email protected], npm/@babel/[email protected], npm/@babel/[email protected], npm/@jridgewell/[email protected], npm/@jridgewell/[email protected], npm/@jridgewell/[email protected], npm/@jridgewell/[email protected], npm/[email protected], npm/[email protected], npm/[email protected], npm/[email protected], npm/[email protected], npm/[email protected], npm/[email protected], npm/[email protected], npm/[email protected], npm/[email protected], npm/[email protected], npm/[email protected], npm/[email protected], npm/[email protected], npm/[email protected], npm/[email protected], npm/[email protected], npm/[email protected], npm/[email protected], npm/[email protected], npm/[email protected], npm/[email protected], npm/[email protected], npm/[email protected], npm/[email protected], npm/[email protected], npm/[email protected], npm/[email protected], npm/[email protected], npm/[email protected], npm/[email protected], npm/[email protected], npm/[email protected], npm/[email protected], npm/[email protected], npm/[email protected], npm/[email protected], npm/[email protected], npm/[email protected], npm/[email protected], npm/[email protected], npm/[email protected], npm/[email protected], npm/[email protected], npm/[email protected], npm/[email protected], npm/[email protected], npm/[email protected], npm/[email protected], npm/[email protected], npm/[email protected], npm/[email protected], npm/[email protected], npm/[email protected], npm/[email protected]

View full report↗︎

@mifi mifi requested a review from Murderlon November 12, 2024 14:59
@@ -640,6 +641,15 @@ enabled, it will lead to _faster uploads_ because companion will start uploading
at the same time as downloading using `stream.pipe`. If `false`, files will be
fully downloaded first, then uploaded. Defaults to `true`.

#### `streamingUploadSizeless` `COMPANION_STREAMING_UPLOAD_SIZELESS`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens if it's not enabled? Hard crash? Silent ignore?

Why would we put it behind on option and not simply also allow it and return the error from the tus server if it doesn't support it (which is highly unlikely, all tus servers I know support it)?

@@ -794,7 +794,7 @@ const state = {
capabilities: {
resumableUploads: false,
},
totalProgress: 0,
progress: null,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a fan of this change. Let's keep it totalProgress and reduce the tech debt, confusion, and PR diff.

@Murderlon
Copy link
Member

The new status bar behavior it not clear to me yet.

This is how it normally looks:

Screenshot 2024-11-18 at 12 16 03

I would simply replace "X KB of Y KB" with "X KB of unknown".


Have you tested with showProgressDetails set to false?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Allow streaming upload also for unknown length streams
2 participants