summaryrefslogtreecommitdiffstats
path: root/examples/upload.py
diff options
context:
space:
mode:
Diffstat (limited to 'examples/upload.py')
-rw-r--r--examples/upload.py59
1 files changed, 59 insertions, 0 deletions
diff --git a/examples/upload.py b/examples/upload.py
new file mode 100644
index 0000000..518b5ec
--- /dev/null
+++ b/examples/upload.py
@@ -0,0 +1,59 @@
+#!/usr/bin/env python3
+"""Upload one file to a MeshBay group, as a member, without a browser."""
+
+import asyncio
+import math
+import os
+
+from meshbay_common.groupbox import PURPOSE_UPLOAD, unseal
+from meshbay_common.protocol import file_upload_wire
+from meshbay_session import connect
+
+HUB = "https://meshbay.org"
+USERNAME = "alice"
+PASSPHRASE = "your passphrase"
+GROUP = "Family Photos" # the group's name, as shown in the sidebar
+NODE_ID = "" # empty: the first node online for this group
+LOCAL_FILE = "beach.jpg" # the file to send
+DEST_DIR = "shared/2024" # root name, then folders; the root must be writable
+
+CHUNK_SIZE = 48 * 1024 # what fits in one DataChannel message
+WINDOW = 32 # chunks sent ahead of the node's acks
+
+
+async def main() -> None:
+ session = await connect(HUB, USERNAME, PASSPHRASE, GROUP, NODE_ID)
+
+ filename = os.path.basename(LOCAL_FILE)
+ size = os.path.getsize(LOCAL_FILE)
+ chunks = max(1, math.ceil(size / CHUNK_SIZE))
+ tr = await session.open_transfer("upload", size, chunks)
+ upload_id = os.urandom(16).hex()
+
+ # The file name, the folder and the bytes are sealed under the group key.
+ # Only the upload id and the chunk numbers stay in clear.
+ acked = 0
+ ack = {}
+ with open(LOCAL_FILE, "rb") as f:
+ for i in range(chunks):
+ if i - acked >= WINDOW:
+ ack = await session.ch.recv("file_upload_ack")
+ acked += 1
+ msg = file_upload_wire(session.gek, session.group_id, upload_id=upload_id,
+ chunk_index=i, total_chunks=chunks, filename=filename,
+ data=f.read(CHUNK_SIZE), dir=DEST_DIR)
+ session.ch.send({**msg, "tr": tr})
+ print(f"\r{i + 1}/{chunks} chunks", end="", flush=True)
+ while acked < chunks:
+ ack = await session.ch.recv("file_upload_ack")
+ acked += 1
+ session.close_transfer(tr)
+
+ # The node never overwrites: on a name clash it picks a free one.
+ done = unseal(session.gek, PURPOSE_UPLOAD, "file_upload_ack", session.group_id, ack)
+ print(f"\nStored as {done['dir']}/{done['stored_as']} ({size} bytes)")
+ await session.close()
+
+
+if __name__ == "__main__":
+ asyncio.run(main())