aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-cli/src/meshbay_cli/client/transfers.py
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-10-15 10:17:29 +0200
committerChristophe Besson <cbesson@gmail.com>2026-10-15 10:17:29 +0200
commita0be6bfcac07a256949082e935e1be06753ab872 (patch)
treec6f531254e0abf8303080ba6287cf1fc405a72c1 /packages/meshbay-cli/src/meshbay_cli/client/transfers.py
parent2f90f4b60cd50f7e9649daaf8e76a120e608cff6 (diff)
downloadmeshbay-a0be6bfcac07a256949082e935e1be06753ab872.tar.gz
feat(cli): stream — a film or a song played in a media player
`meshbay-cli stream PATH` serves one video or sound file over HTTP on 127.0.0.1 for VLC, mpv or any player that reads HTTP; `--player PROGRAM` starts the player, and closing it stops the stream, as Ctrl+C does. The file goes as the node stores it: no remux, no transcode, no stream slot on the node. Byte ranges are answered from leaseless file_req chunks decrypted in memory, eight ahead and the last 24 kept. Loopback only, a random token in the address, a foreign Host refused (DNS rebinding), an audio or video type with nosniff. The connection is pinged and reopened when it dies; a refusal by the node stops the stream (exit 5). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'packages/meshbay-cli/src/meshbay_cli/client/transfers.py')
-rw-r--r--packages/meshbay-cli/src/meshbay_cli/client/transfers.py14
1 files changed, 14 insertions, 0 deletions
diff --git a/packages/meshbay-cli/src/meshbay_cli/client/transfers.py b/packages/meshbay-cli/src/meshbay_cli/client/transfers.py
index 3f23c85..2cbf4cd 100644
--- a/packages/meshbay-cli/src/meshbay_cli/client/transfers.py
+++ b/packages/meshbay-cli/src/meshbay_cli/client/transfers.py
@@ -268,6 +268,20 @@ async def _fetch_chunks(session: GroupSession, lease: Lease, file_id: str, file_
return written
+async def read_chunk(session: GroupSession, entry: dict, i: int) -> bytes:
+ """
+ Chunk `i` of a file, decrypted, asked for without a lease: what the
+ application's players do, and what the node bounds (a dozen files at once
+ per connection). A refusal raises `NodeRefusal`; a connection that failed,
+ another `CliError`.
+ """
+ msg = await session.ch.request(
+ {"type": "file_req", "v": MNP_VERSION, "file_id": entry["id"], "chunk_index": i},
+ "file_chunk", timeout=CHUNK_TIMEOUT)
+ return decrypt_chunk_aes(chunk_key_aes(session.gek, bytes.fromhex(entry["id"]), i),
+ msg["nonce"], msg["ct"])
+
+
def mark_of_the_web(path: Path) -> None:
"""ZoneId 3, as a browser marks a download, so SmartScreen and Protected View
apply when it is opened. FAT and exFAT have no alternate streams: nothing to do."""