1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
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())
|