from starlette.responses import JSONResponse from . import router from fastapi import Request, Depends from ..auth import decodeJWT, JWTBearer from ..util import redis, ergo def pathParts(path): if len(path) == 2: channel = None user, token = path elif len(path) == 3: channel, user, token = path channel = "#" + channel else: return None, None, None return channel, user, token @router.get("/mediamtx/streams/{channel}", dependencies=[Depends(JWTBearer())]) async def mediamtxChannelStreams(request: Request, channel: str): inChannel = request.state.jwt.get("channel", "").lower() == "#" + channel.lower() results = [] async for result in redis.scan_iter(f"stream #{channel} *"): _, channel, user, token = result.decode("utf8").split() if inChannel or token == "public": results.append({ "user": user, "token": token }) return JSONResponse(status_code=200, content=results) @router.post("/mediamtx/auth", include_in_schema=False) async def mediamtxAuth(request: Request): if request.client.host != "127.0.0.1": return False body = await request.json() jwt = decodeJWT(body["query"][4:]) path = body["path"].split("/") reading = body["action"] == "read" channel, user, token = pathParts(path) if user is None: return JSONResponse(status_code=400, content={"error": "bad path"}) if " " in token: return JSONResponse(status_code=400, content={"error": "bad token"}) # the only time we don't care about JWT is if someone is watching a public stream if reading and token == "public": return JSONResponse(status_code=200, content={"success": True}) if len(jwt.keys()) == 0: return JSONResponse(status_code=403, content={"error": "bad jwt"}) # TODO: channel stream permissions # publishing if not reading: if user != jwt["account"]: return JSONResponse(status_code=403, content={"error": "nuh uh"}) if channel and jwt["channel"] != channel: return JSONResponse(status_code=403, content={"error": "nuh uh"}) return JSONResponse(status_code=200, content={"success": True}) @router.post("/mediamtx/add", include_in_schema=False) async def mediamtxAdd(request: Request): if request.client.host != "127.0.0.1": return False body = await request.json() path = body["env"]["MTX_PATH"].split("/") parts = [x for x in pathParts(path) if x] await redis.set("stream " + " ".join(parts), parts[2]) if len(parts) == 3: await ergo.broadcastTo(parts[0], "STREAMSTART", parts[0], parts[1], parts[2]) @router.post("/mediamtx/del", include_in_schema=False) async def mediamtxDelete(request: Request): if request.client.host != "127.0.0.1": return False body = await request.json() path = body["env"]["MTX_PATH"].split("/") parts = [x for x in pathParts(path) if x] await redis.delete("stream " + " ".join(parts)) if len(parts) == 3: await ergo.broadcastTo(parts[0], "STREAMEND", parts[0], parts[1], parts[2])