#!/usr/bin/env python3 """Download one file from a MeshBay group, as a member, without a browser.""" import asyncio import math import sys from meshbay_common.webcrypto import chunk_key_aes, decrypt_chunk_aes 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 FILE_PATH = "shared/2024/beach.jpg" # root name, then folders, then the file name OUTPUT = "beach.jpg" CHUNK_SIZE = 1024 * 1024 # the node serves files in 1 MiB chunks async def main() -> None: session = await connect(HUB, USERNAME, PASSPHRASE, GROUP, NODE_ID) entry = next((e for e in await session.index() if f"{e['path']}/{e['name']}" == FILE_PATH), None) if entry is None: sys.exit(f"No file {FILE_PATH!r} in this group") # Each chunk is encrypted under its own key, derived from the group key. chunks = max(1, math.ceil(entry["size"] / CHUNK_SIZE)) tr = await session.open_transfer("download", entry["size"], chunks) file_hash = bytes.fromhex(entry["id"]) with open(OUTPUT, "wb") as out: for i in range(chunks): session.ch.send({"type": "file_req", "v": "0.1", "file_id": entry["id"], "chunk_index": i, "tr": tr}) chunk = await session.ch.recv("file_chunk") out.write(decrypt_chunk_aes(chunk_key_aes(session.gek, file_hash, i), chunk["nonce"], chunk["ct"])) print(f"\r{i + 1}/{chunks} chunks", end="", flush=True) session.close_transfer(tr) print(f"\nSaved {OUTPUT} ({entry['size']} bytes)") await session.close() if __name__ == "__main__": asyncio.run(main())