Browse Source

Workable pending transfer logic implementation!

With logging.
unifiedWindowUI
Luke Murphy 4 years ago
parent
commit
86f81a6122
No known key found for this signature in database GPG Key ID: 5E2EF5A63E3718CC
  1. 7
      dropship/constant.py
  2. 51
      dropship/dropship.py
  3. 35
      dropship/transfer.py
  4. 46
      dropship/ui_templates.py
  5. 37
      dropship/wormhole.py

7
dropship/constant.py

@ -0,0 +1,7 @@
from pathlib import Path
_CWD = Path(__file__).absolute().parent
UI_DIR = f"{_CWD}/ui"
DEFAULT_DROP_LABEL = "Drag a file to send"

51
dropship/dropship.py

@ -1,9 +1,8 @@
from os.path import basename from os.path import basename
from pathlib import Path from pathlib import Path
from subprocess import PIPE
from gi import require_version from gi import require_version
from trio import open_process, run_process from trio import CancelScope, open_process, run_process
from dropship import log from dropship import log
@ -12,9 +11,11 @@ require_version("Gdk", "3.0")
from gi.repository import Gdk, GLib, Gtk from gi.repository import Gdk, GLib, Gtk
from dropship import log
from dropship.constant import UI_DIR
from dropship.transfer import PendingTransfer
from dropship.ui_templates import PendingTransferRow from dropship.ui_templates import PendingTransferRow
from dropship.wormhole import wormhole_recv, wormhole_send
CWD = Path(__file__).absolute().parent
class DropShip: class DropShip:
@ -22,8 +23,8 @@ class DropShip:
def __init__(self, nursery): def __init__(self, nursery):
"""Object initialisation.""" """Object initialisation."""
self.GLADE_FILE = f"{CWD}/ui/dropship.ui" self.GLADE_FILE = f"{UI_DIR}/dropship.ui"
self.CSS_FILE = f"{CWD}/ui/dropship.css" self.CSS_FILE = f"{UI_DIR}/dropship.css"
self.clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD) self.clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
self.nursery = nursery self.nursery = nursery
@ -95,20 +96,20 @@ class DropShip:
self.files_to_send = files self.files_to_send = files
if len(files) == 1: if len(files) == 1:
fpath = files[0].replace("file://", "") fpath = files[0].replace("file://", "")
self.nursery.start_soon(self.wormhole_send, fpath) self.nursery.start_soon(self.send, fpath)
else: else:
log.info("Multiple file sending coming soon ™") log.info("Multiple file sending coming soon ™")
def on_recv(self, entry): def on_recv(self, entry):
"""Handler for receiving transfers.""" """Handler for receiving transfers."""
self.nursery.start_soon(self.wormhole_recv, entry.get_text()) self.nursery.start_soon(self.receive, entry.get_text())
def add_files(self, widget, event): def add_files(self, widget, event):
"""Handler for adding files with system interface""" """Handler for adding files with system interface"""
response = self.file_chooser.run() response = self.file_chooser.run()
if response == Gtk.ResponseType.OK: if response == Gtk.ResponseType.OK:
fpath = self.file_chooser.get_filenames()[0] fpath = self.file_chooser.get_filenames()[0]
self.nursery.start_soon(self.wormhole_send, fpath) self.nursery.start_soon(self.send, fpath)
self.file_chooser.hide() self.file_chooser.hide()
def _send_spinner_on(self): def _send_spinner_on(self):
@ -128,23 +129,27 @@ class DropShip:
self.drop_spinner.set_vexpand(False) self.drop_spinner.set_vexpand(False)
self.drop_spinner.set_visible(False) self.drop_spinner.set_visible(False)
def _create_pending_transfer(self, fpath, code): def _create_pending_transfer(self, fpath, code, scope):
"""Create a new pending transfer.""" """Create a new pending transfer."""
pending = PendingTransferRow(basename(fpath), code) transfer = PendingTransfer(fpath, code, scope)
self.pending_transfers_list.insert(pending, -1) template = PendingTransferRow(transfer, self)
self.pending_transfers_list.insert(template, -1)
def _remove_pending_transfer(self, code):
"""Remove pending transfer."""
for pending_transfer in self.pending_transfers_list:
if pending_transfer.code == code:
pending_transfer.cancel()
async def wormhole_send(self, fpath): async def send(self, fpath):
"""Run `wormhole send` on a local file path."""
self._send_spinner_on() self._send_spinner_on()
process = await open_process(["wormhole", "send", fpath], stderr=PIPE) code, scope = await self.nursery.start(wormhole_send, fpath)
output = await process.stderr.receive_some() self._create_pending_transfer(fpath, code, scope)
code = output.decode().split()[-1]
self._create_pending_transfer(fpath, code)
self.clipboard.set_text(code, -1) self.clipboard.set_text(code, -1)
self._send_spinner_off(code) self._send_spinner_off(code)
await process.wait() log.info(f"send: successfully initiated transfer send ({code})")
async def wormhole_recv(self, code): async def receive(self, code):
"""Run `wormhole receive` with a pending transfer code.""" await self.nursery.start(wormhole_recv, code)
command = ["wormhole", "receive", "--accept-file", code] self._remove_pending_transfer(code)
await run_process(command, stderr=PIPE) log.info(f"receive: successfully received transfer ({code})")

35
dropship/transfer.py

@ -0,0 +1,35 @@
from os.path import basename, getsize
class PendingTransfer:
"""A pending transfer context and config."""
def __init__(self, fpath, code, scope):
self.fpath = fpath
self.code = code
self.scope = scope
@property
def fname(self):
"""Filename being transferred."""
return basename(self.fpath)
@property
def size(self):
"""Filename size."""
return self._human_readable_size(getsize(self.fpath))
def cancel(self):
"""Cancel the transfer."""
self.scope.cancel()
def _human_readable_size(self, num, suffix="B"):
"""Convert file size to human readable format.
Thanks Fred. See https://stackoverflow.com/a/1094933.
"""
for unit in ["", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi"]:
if abs(num) < 1024.0:
return "%3.1f%s%s" % (num, unit, suffix)
num /= 1024.0
return "%.1f%s%s" % (num, "Yi", suffix)

46
dropship/ui_templates.py

@ -6,41 +6,51 @@ gi.require_version("Gtk", "3.0")
from gi.repository import Gdk, GLib, Gtk from gi.repository import Gdk, GLib, Gtk
CWD = Path(__file__).absolute().parent from dropship import log
from dropship.constant import DEFAULT_DROP_LABEL, UI_DIR
@Gtk.Template.from_file(f"{CWD}/ui/pendingTransferRow.ui") @Gtk.Template.from_file(f"{UI_DIR}/pendingTransferRow.ui")
class PendingTransferRow(Gtk.ListBoxRow): class PendingTransferRow(Gtk.ListBoxRow):
# Note(decentral1se): must match ui/pendingTransferRow.ui name
__gtype_name__ = "pendingTransferRow" __gtype_name__ = "pendingTransferRow"
fileNameLabel = Gtk.Template.Child() fileNameLabel = Gtk.Template.Child()
fileNameMetadata = Gtk.Template.Child() fileNameMetadata = Gtk.Template.Child()
transferCodeButton = Gtk.Template.Child() transferCodeButton = Gtk.Template.Child()
cancelTransfer = Gtk.Template.Child() cancelTransfer = Gtk.Template.Child()
pendingTransferRow = Gtk.Template.Child()
def __init__(self, fileName, transferCode, *args, **kwargs): def __init__(self, transfer, parent, *args, **kwargs):
Gtk.ListBoxRow.__init__(self, *args, **kwargs) Gtk.ListBoxRow.__init__(self, *args, **kwargs)
self.fileNameLabel.set_text(fileName) self.transfer = transfer
self.transferCodeButton.set_label(transferCode) self.parent = parent
self.fileNameLabel.set_text(self.transfer.fname)
self.fileNameMetadata.set_text(self.transfer.size)
self.transferCodeButton.set_label(self.transfer.code)
@property
def code(self):
"""Retrieve code of underyling transfer."""
return self.transfer.code
@Gtk.Template.Callback("copy_transfer_code") @Gtk.Template.Callback("copy_transfer_code")
def copy_transfer_code(self, widget): def copy_transfer_code(self, widget):
""" """Copy transfer code to clipboard."""
what to do when we press the button:
copy the code again to clipboard
"""
print("click")
code = widget.get_label() code = widget.get_label()
clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD) clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
clipboard.set_text(code, -1) # -1 is auto-size clipboard.set_text(code, -1) # -1 is auto-size
@Gtk.Template.Callback("cancel_transfer") @Gtk.Template.Callback("cancel_transfer")
def cancel_transfer(self, widget): def cancel_transfer(self, widget):
""" """Callback entrypoint for transfer cancellation."""
cancel the transfer self.cancel()
destroy thread
remove the object from the list def cancel(self):
""" """Internal programmatic API for transfer cancellation."""
print("poof!") self.transfer.cancel()
self.pendingTransferRow.destroy()
self.parent.drop_label.set_text(DEFAULT_DROP_LABEL)
self.parent.drop_label.set_selectable(False)
log.info(
f"PendingTransferRow: successfully cancelled transfer ({self.transfer.code})"
)

37
dropship/wormhole.py

@ -0,0 +1,37 @@
from subprocess import PIPE
from trio import TASK_STATUS_IGNORED, CancelScope, open_process, run_process
from dropship import log
async def wormhole_send(fpath, task_status=TASK_STATUS_IGNORED):
"""Run `wormhole send` on a local file path."""
with CancelScope() as scope:
command = ["wormhole", "send", fpath]
process = await open_process(command, stderr=PIPE)
output = await process.stderr.receive_some()
code = output.decode().split()[-1]
task_status.started((code, scope,))
log.info(f"wormhole_send: now waiting for other side ({code})")
await process.wait()
log.info(f"wormhole_send: succesfully transfered ({code})")
if scope.cancel_called:
process.terminate()
log.info(f"wormhole_send: succesfully terminated process ({code})")
async def wormhole_recv(code, task_status=TASK_STATUS_IGNORED):
"""Run `wormhole receive` on a pending transfer code."""
with CancelScope() as scope:
command = ["wormhole", "receive", "--accept-file", code]
process = await open_process(command, stderr=PIPE)
task_status.started((scope,))
log.info(f"wormhole_recv: now starting receiving process ({code})")
await process.wait()
log.info(f"wormhole_recv: succesfully received ({code})")
if scope.cancel_called:
process.terminate()
log.info(f"wormhole_recv: succesfully terminated process ({code})")
Loading…
Cancel
Save