Upload stats
This commit is contained in:
		| @@ -215,8 +215,8 @@ class MatrixClient(nio.AsyncClient): | ||||
|  | ||||
|         try: | ||||
|             await self._send_file(item_uuid, room_id, path) | ||||
|         except asyncio.CancelledError: | ||||
|             del self.models[Upload, room_id][item_uuid] | ||||
|         except (nio.TransferCancelledError, asyncio.CancelledError): | ||||
|             del self.models[Upload, room_id][str(item_uuid)] | ||||
|  | ||||
|  | ||||
|     async def _send_file( | ||||
| @@ -235,18 +235,27 @@ class MatrixClient(nio.AsyncClient): | ||||
|  | ||||
|         task        = asyncio.Task.current_task() | ||||
|         upload_item = Upload(item_uuid, task, path, total_size=size) | ||||
|         self.models[Upload, room_id][upload_item.uuid] = upload_item | ||||
|         self.models[Upload, room_id][str(item_uuid)] = upload_item | ||||
|  | ||||
|         def on_transfered(transfered: int) -> None: | ||||
|             upload_item.uploaded  = transfered | ||||
|             upload_item.time_left = monitor.remaining_time | ||||
|  | ||||
|         def on_speed_change(speed: float) -> None: | ||||
|             upload_item.speed = speed | ||||
|  | ||||
|         monitor = nio.TransferMonitor(size, on_transfered, on_speed_change) | ||||
|  | ||||
|         try: | ||||
|             url, mime, crypt_dict = await self.upload( | ||||
|                 path, filename=path.name, encrypt=encrypt, | ||||
|                 path, filename=path.name, encrypt=encrypt, monitor=monitor, | ||||
|             ) | ||||
|         except (MatrixError, OSError) as err: | ||||
|             upload_item.status     = UploadStatus.Error | ||||
|             upload_item.error      = type(err) | ||||
|             upload_item.error_args = err.args | ||||
|  | ||||
|             # Wait for cancellation, see parent send_file() method | ||||
|             # Wait for cancellation from UI, see parent send_file() method | ||||
|             while True: | ||||
|                 await asyncio.sleep(0.1) | ||||
|  | ||||
| @@ -609,6 +618,7 @@ class MatrixClient(nio.AsyncClient): | ||||
|         mime:     Optional[str]                 = None, | ||||
|         filename: Optional[str]                 = None, | ||||
|         encrypt:  bool                          = False, | ||||
|         monitor:  Optional[nio.TransferMonitor] = None, | ||||
|     ) -> UploadReturn: | ||||
|  | ||||
|         mime = mime or await utils.guess_mime(data) | ||||
| @@ -618,6 +628,7 @@ class MatrixClient(nio.AsyncClient): | ||||
|             "application/octet-stream" if encrypt else mime, | ||||
|             filename, | ||||
|             encrypt, | ||||
|             monitor, | ||||
|         ) | ||||
|  | ||||
|         if isinstance(response, nio.UploadError): | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| import asyncio | ||||
| import re | ||||
| from dataclasses import dataclass, field | ||||
| from datetime import datetime | ||||
| from datetime import datetime, timedelta | ||||
| from pathlib import Path | ||||
| from typing import Any, Dict, List, Optional, Tuple, Type, Union | ||||
| from uuid import UUID | ||||
| @@ -117,9 +117,13 @@ class Upload(ModelItem): | ||||
|     uuid:     UUID         = field() | ||||
|     task:     asyncio.Task = field() | ||||
|     filepath: Path         = field() | ||||
|     status:     UploadStatus          = UploadStatus.Uploading | ||||
|  | ||||
|     total_size: int                 = 0 | ||||
|     uploaded:   int                 = 0 | ||||
|     speed:      float               = 0 | ||||
|     time_left:  Optional[timedelta] = None | ||||
|  | ||||
|     status:     UploadStatus          = UploadStatus.Uploading | ||||
|     error:      OptionalExceptionType = type(None) | ||||
|     error_args: Tuple[Any, ...]       = () | ||||
|  | ||||
|   | ||||
| @@ -7,6 +7,7 @@ import inspect | ||||
| import io | ||||
| import logging as log | ||||
| import xml.etree.cElementTree as xml_etree  # FIXME: bandit warning | ||||
| from datetime import timedelta | ||||
| from enum import Enum | ||||
| from enum import auto as autostr | ||||
| from pathlib import Path | ||||
| @@ -140,6 +141,9 @@ def serialize_value_for_qml(value: Any) -> Any: | ||||
|     if isinstance(value, UUID): | ||||
|         return str(value) | ||||
|  | ||||
|     if isinstance(value, timedelta): | ||||
|         return value.total_seconds() * 1000 | ||||
|  | ||||
|     if inspect.isclass(value): | ||||
|         return value.__name__ | ||||
|  | ||||
|   | ||||
| @@ -94,7 +94,7 @@ Rectangle { | ||||
|                             .arg(fileName) : | ||||
|  | ||||
|                             model.error === "IsADirectoryError" ? | ||||
|                             qsTr("Can't upload folders: %1") | ||||
|                             qsTr("Can't upload folders, need a file: %1") | ||||
|                             .arg(filePath) : | ||||
|  | ||||
|                             model.error === "FileNotFoundError" ? | ||||
| @@ -128,6 +128,13 @@ Rectangle { | ||||
|  | ||||
|                     readonly property string filePath: | ||||
|                         model.filepath.replace(/^file:\/\//, "") | ||||
|  | ||||
|                     HoverHandler { id: statusLabelHover } | ||||
|  | ||||
|                     HToolTip { | ||||
|                         text: parent.truncated ? parent.text : "" | ||||
|                         visible: text && statusLabelHover.hovered | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 HSpacer {} | ||||
| @@ -135,7 +142,11 @@ Rectangle { | ||||
|                 HLabel { | ||||
|                     id: uploadCountLabel | ||||
|                     visible: Layout.preferredWidth > 0 | ||||
|                     text: qsTr("%1") | ||||
|                     text: qsTr("-%1  %2/s  %3/%4") | ||||
|                           .arg(model.time_left ? | ||||
|                                Utils.formatDuration(msLeft) : "∞") | ||||
|                           .arg(CppUtils.formattedBytes(model.speed)) | ||||
|                           .arg(CppUtils.formattedBytes(uploaded)) | ||||
|                           .arg(CppUtils.formattedBytes(model.total_size)) | ||||
|  | ||||
|                     topPadding: theme.spacing / 2 | ||||
| @@ -146,6 +157,12 @@ Rectangle { | ||||
|                     Layout.preferredWidth: | ||||
|                         model.status === "Uploading" ? implicitWidth : 0 | ||||
|  | ||||
|                     property int msLeft: model.time_left || -1 | ||||
|                     property int uploaded: model.uploaded | ||||
|  | ||||
|                     Behavior on msLeft { HNumberAnimation { duration: 1000 } } | ||||
|                     Behavior on uploaded { HNumberAnimation { duration: 1000 }} | ||||
|  | ||||
|                     Behavior on Layout.preferredWidth { HNumberAnimation {} } | ||||
|                 } | ||||
|  | ||||
| @@ -153,20 +170,15 @@ Rectangle { | ||||
|                     onTapped: if (model.status !== "Error") | ||||
|                         statusLabel.expand = ! statusLabel.expand | ||||
|                 } | ||||
|  | ||||
|                 HoverHandler { id: infoRowHover } | ||||
|  | ||||
|                 HToolTip { | ||||
|                     id: statusToolTip | ||||
|                     text: statusLabel.truncated ? statusLabel.text : "" | ||||
|                     visible: text && infoRowHover.hovered | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             HProgressBar { | ||||
|                 id: progressBar | ||||
|                 visible: Layout.maximumHeight !== 0 | ||||
|                 indeterminate: true | ||||
|                 indeterminate: model.status !== "Uploading" | ||||
|                 value: model.uploaded | ||||
|                 to: model.total_size | ||||
|  | ||||
|                 foregroundColor: | ||||
|                     model.status === "Error" ? | ||||
|                     theme.controls.progressBar.errorForeground : | ||||
| @@ -176,6 +188,7 @@ Rectangle { | ||||
|                 Layout.maximumHeight: | ||||
|                     model.status === "Error" && indeterminate ? 0 : -1 | ||||
|  | ||||
|                 Behavior on value { HNumberAnimation { duration: 1000 } } | ||||
|                 Behavior on Layout.maximumHeight { HNumberAnimation {} } | ||||
|             } | ||||
|         } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	