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