class FileDownloadProgress { int chunksDownloaded = 0; int chunksTotal = 1; bool complete = false; bool gotManifest = false; bool _interrupted = false; // we keep track of both an explicit interrupt flag (for when a request fails or is explicitly cancelled) set interrupted(isInterrupted) { this._interrupted = isInterrupted; } // but we have a fuzzy get which depends on lastUpdate, if the file isn't complete, but the last update was more // that 30 seconds ago, we consider this download as failed. get interrupted => _interrupted || (DateTime.now().difference(lastUpdate).abs().inSeconds > 30 && !complete); String? downloadedTo; DateTime? timeStart; DateTime? timeEnd; DateTime? requested; DateTime lastUpdate = DateTime.fromMillisecondsSinceEpoch(0); FileDownloadProgress(this.chunksTotal, this.timeStart); double progress() { return 1.0 * chunksDownloaded / chunksTotal; } void markUpdate() { lastUpdate = DateTime.now(); } } String prettyBytes(int bytes) { if (bytes > 1000000000) { return (1.0 * bytes / 1000000000).toStringAsFixed(1) + " GB"; } else if (bytes > 1000000) { return (1.0 * bytes / 1000000).toStringAsFixed(1) + " MB"; } else if (bytes > 1000) { return (1.0 * bytes / 1000).toStringAsFixed(1) + " kB"; } else { return bytes.toString() + " B"; } }