Fix upload pause/cancel
This commit is contained in:
parent
cdb79d11aa
commit
d1e42a72a0
1
TODO.md
1
TODO.md
|
@ -50,7 +50,6 @@
|
||||||
by switching to another room and coming back
|
by switching to another room and coming back
|
||||||
- First sent message in E2E room is sometimes undecryptable
|
- First sent message in E2E room is sometimes undecryptable
|
||||||
|
|
||||||
- Pause upload, switch to other room, then come back → wrong state displayed
|
|
||||||
- Pausing uploads doesn't work well, servers end up dropping the connection
|
- Pausing uploads doesn't work well, servers end up dropping the connection
|
||||||
|
|
||||||
- In the "Leave me" room, "join > Hi > left" aren't combined
|
- In the "Leave me" room, "join > Hi > left" aren't combined
|
||||||
|
|
|
@ -112,8 +112,11 @@ class MatrixClient(nio.AsyncClient):
|
||||||
self.profile_task: Optional[asyncio.Future] = None
|
self.profile_task: Optional[asyncio.Future] = None
|
||||||
self.sync_task: Optional[asyncio.Future] = None
|
self.sync_task: Optional[asyncio.Future] = None
|
||||||
self.load_rooms_task: Optional[asyncio.Future] = None
|
self.load_rooms_task: Optional[asyncio.Future] = None
|
||||||
self.first_sync_done: asyncio.Event = asyncio.Event()
|
|
||||||
self.first_sync_date: Optional[datetime] = None
|
self.upload_monitors: Dict[UUID, nio.TransferMonitor] = {}
|
||||||
|
|
||||||
|
self.first_sync_done: asyncio.Event = asyncio.Event()
|
||||||
|
self.first_sync_date: Optional[datetime] = None
|
||||||
|
|
||||||
self.past_tokens: Dict[str, str] = {} # {room_id: token}
|
self.past_tokens: Dict[str, str] = {} # {room_id: token}
|
||||||
self.fully_loaded_rooms: Set[str] = set() # {room_id}
|
self.fully_loaded_rooms: Set[str] = set() # {room_id}
|
||||||
|
@ -276,6 +279,25 @@ class MatrixClient(nio.AsyncClient):
|
||||||
await self._send_message(room_id, content)
|
await self._send_message(room_id, content)
|
||||||
|
|
||||||
|
|
||||||
|
async def toggle_pause_upload(
|
||||||
|
self, room_id: str, uuid: Union[str, UUID],
|
||||||
|
) -> None:
|
||||||
|
if isinstance(uuid, str):
|
||||||
|
uuid = UUID(uuid)
|
||||||
|
|
||||||
|
pause = not self.upload_monitors[uuid].pause
|
||||||
|
|
||||||
|
self.upload_monitors[uuid].pause = pause
|
||||||
|
self.models[room_id, "uploads"][str(uuid)].paused = pause
|
||||||
|
|
||||||
|
|
||||||
|
async def cancel_upload(self, uuid: Union[str, UUID]) -> None:
|
||||||
|
if isinstance(uuid, str):
|
||||||
|
uuid = UUID(uuid)
|
||||||
|
|
||||||
|
self.upload_monitors[uuid].cancel = True
|
||||||
|
|
||||||
|
|
||||||
async def send_file(self, room_id: str, path: Union[Path, str]) -> None:
|
async def send_file(self, room_id: str, path: Union[Path, str]) -> None:
|
||||||
"""Send a `m.file`, `m.image`, `m.audio` or `m.video` message."""
|
"""Send a `m.file`, `m.image`, `m.audio` or `m.video` message."""
|
||||||
|
|
||||||
|
@ -285,6 +307,7 @@ class MatrixClient(nio.AsyncClient):
|
||||||
await self._send_file(item_uuid, room_id, path)
|
await self._send_file(item_uuid, room_id, path)
|
||||||
except (nio.TransferCancelledError, asyncio.CancelledError):
|
except (nio.TransferCancelledError, asyncio.CancelledError):
|
||||||
log.info("Deleting item for cancelled upload %s", item_uuid)
|
log.info("Deleting item for cancelled upload %s", item_uuid)
|
||||||
|
del self.upload_monitors[item_uuid]
|
||||||
del self.models[room_id, "uploads"][str(item_uuid)]
|
del self.models[room_id, "uploads"][str(item_uuid)]
|
||||||
|
|
||||||
|
|
||||||
|
@ -306,9 +329,10 @@ class MatrixClient(nio.AsyncClient):
|
||||||
# This error will be caught again by the try block later below
|
# This error will be caught again by the try block later below
|
||||||
size = 0
|
size = 0
|
||||||
|
|
||||||
task = asyncio.Task.current_task()
|
|
||||||
monitor = nio.TransferMonitor(size)
|
monitor = nio.TransferMonitor(size)
|
||||||
upload_item = Upload(item_uuid, task, monitor, path, total_size=size)
|
upload_item = Upload(item_uuid, path, total_size=size)
|
||||||
|
|
||||||
|
self.upload_monitors[item_uuid] = monitor
|
||||||
self.models[room_id, "uploads"][str(item_uuid)] = upload_item
|
self.models[room_id, "uploads"][str(item_uuid)] = upload_item
|
||||||
|
|
||||||
def on_transferred(transferred: int) -> None:
|
def on_transferred(transferred: int) -> None:
|
||||||
|
@ -325,8 +349,14 @@ class MatrixClient(nio.AsyncClient):
|
||||||
url, mime, crypt_dict = await self.upload(
|
url, mime, crypt_dict = await self.upload(
|
||||||
lambda *_: path,
|
lambda *_: path,
|
||||||
filename = path.name,
|
filename = path.name,
|
||||||
encrypt = encrypt, monitor=monitor,
|
encrypt = encrypt,
|
||||||
|
monitor = monitor,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# FIXME: nio might not catch the cancel in time
|
||||||
|
if monitor.cancel:
|
||||||
|
raise nio.TransferCancelledError()
|
||||||
|
|
||||||
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)
|
||||||
|
@ -388,12 +418,21 @@ class MatrixClient(nio.AsyncClient):
|
||||||
upload_item.total_size = len(thumb_data)
|
upload_item.total_size = len(thumb_data)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
# The total_size passed to the monitor only considers
|
||||||
|
# the file itself, and not the thumbnail.
|
||||||
|
monitor.on_transferred = None
|
||||||
|
|
||||||
thumb_url, _, thumb_crypt_dict = await self.upload(
|
thumb_url, _, thumb_crypt_dict = await self.upload(
|
||||||
lambda *_: thumb_data,
|
lambda *_: thumb_data,
|
||||||
filename =
|
filename =
|
||||||
f"{path.stem}_sample{''.join(path.suffixes)}",
|
f"{path.stem}_sample{''.join(path.suffixes)}",
|
||||||
encrypt = encrypt,
|
encrypt = encrypt,
|
||||||
|
monitor = monitor,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# FIXME: nio might not catch the cancel in time
|
||||||
|
if monitor.cancel:
|
||||||
|
raise nio.TransferCancelledError()
|
||||||
except MatrixError as err:
|
except MatrixError as err:
|
||||||
log.warning(f"Failed uploading thumbnail {path}: {err}")
|
log.warning(f"Failed uploading thumbnail {path}: {err}")
|
||||||
else:
|
else:
|
||||||
|
@ -451,6 +490,7 @@ class MatrixClient(nio.AsyncClient):
|
||||||
content["msgtype"] = "m.file"
|
content["msgtype"] = "m.file"
|
||||||
content["filename"] = path.name
|
content["filename"] = path.name
|
||||||
|
|
||||||
|
del self.upload_monitors[item_uuid]
|
||||||
del self.models[room_id, "uploads"][str(upload_item.id)]
|
del self.models[room_id, "uploads"][str(upload_item.id)]
|
||||||
|
|
||||||
await self._local_echo(
|
await self._local_echo(
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
"""`ModelItem` subclasses definitions."""
|
"""`ModelItem` subclasses definitions."""
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import json
|
import json
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
|
@ -136,18 +135,17 @@ class UploadStatus(AutoStrEnum):
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Upload(ModelItem): # XXX
|
class Upload(ModelItem):
|
||||||
"""Represent a running or failed file upload operation."""
|
"""Represent a running or failed file upload operation."""
|
||||||
|
|
||||||
id: UUID = field()
|
id: UUID = field()
|
||||||
task: asyncio.Task = field()
|
filepath: Path = field()
|
||||||
monitor: nio.TransferMonitor = field()
|
|
||||||
filepath: Path = field()
|
|
||||||
|
|
||||||
total_size: int = 0
|
total_size: int = 0
|
||||||
uploaded: int = 0
|
uploaded: int = 0
|
||||||
speed: float = 0
|
speed: float = 0
|
||||||
time_left: timedelta = timedelta(0)
|
time_left: timedelta = timedelta(0)
|
||||||
|
paused: bool = False
|
||||||
|
|
||||||
status: UploadStatus = UploadStatus.Uploading
|
status: UploadStatus = UploadStatus.Uploading
|
||||||
error: OptionalExceptionType = type(None)
|
error: OptionalExceptionType = type(None)
|
||||||
|
|
|
@ -9,13 +9,12 @@ HColumnLayout {
|
||||||
id: transfer
|
id: transfer
|
||||||
|
|
||||||
|
|
||||||
property bool paused: false
|
|
||||||
|
|
||||||
property int msLeft: model.time_left || 0
|
property int msLeft: model.time_left || 0
|
||||||
property int uploaded: model.uploaded
|
property int uploaded: model.uploaded
|
||||||
readonly property int speed: model.speed
|
readonly property int speed: model.speed
|
||||||
readonly property int totalSize: model.total_size
|
readonly property int totalSize: model.total_size
|
||||||
readonly property string status: model.status
|
readonly property string status: model.status
|
||||||
|
readonly property bool paused: model.paused
|
||||||
|
|
||||||
|
|
||||||
function cancel() {
|
function cancel() {
|
||||||
|
@ -23,12 +22,13 @@ HColumnLayout {
|
||||||
// immediate visual feedback
|
// immediate visual feedback
|
||||||
transfer.height = 0
|
transfer.height = 0
|
||||||
// Python will delete this model item on cancel
|
// Python will delete this model item on cancel
|
||||||
py.call(py.getattr(model.task, "cancel"))
|
py.callClientCoro(chat.userId, "cancel_upload", [model.id])
|
||||||
}
|
}
|
||||||
|
|
||||||
function pause() {
|
function toggle_pause() {
|
||||||
transfer.paused = ! transfer.paused
|
py.callClientCoro(
|
||||||
py.setattr(model.monitor, "pause", transfer.paused)
|
chat.userId, "toggle_pause_upload", [chat.roomId, model.id],
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -139,7 +139,7 @@ HColumnLayout {
|
||||||
toolTip.text: transfer.paused ?
|
toolTip.text: transfer.paused ?
|
||||||
qsTr("Resume") : qsTr("Pause")
|
qsTr("Resume") : qsTr("Pause")
|
||||||
|
|
||||||
onClicked: transfer.pause()
|
onClicked: transfer.toggle_pause()
|
||||||
|
|
||||||
Layout.preferredWidth:
|
Layout.preferredWidth:
|
||||||
status === "Uploading" ?
|
status === "Uploading" ?
|
||||||
|
|
Loading…
Reference in New Issue
Block a user