import json import os import subprocess from datetime import datetime import httpx from dotenv import load_dotenv load_dotenv() HOST = os.environ["HOST"] PRIVATE_BASE = f"https://{HOST}/proxy/protect/api" USERNAME = os.environ["UNIFI_USERNAME"] PASSWORD = os.environ["UNIFI_PASSWORD"] CAMERA_NAME = "G6 Pro 360" OUTPUT_DIR = "./data" def main(): body = json.dumps({"username": USERNAME, "password": PASSWORD, "rememberMe": False}).encode() # Accept-Encoding must be suppressed at request level — server returns 403 when present with httpx.Client(verify=False, http2=True) as client: client.post( f"https://{HOST}/api/auth/login", content=body, headers={"Content-Type": "application/json", "Accept-Encoding": ""}, ).raise_for_status() cameras = client.get(f"{PRIVATE_BASE}/cameras").raise_for_status().json() camera = next(c for c in cameras if CAMERA_NAME in c["name"]) rtsp_alias = next(ch["rtspAlias"] for ch in camera["channels"] if ch.get("isRtspEnabled")) rtsp_url = f"rtsps://{HOST}:7441/{rtsp_alias}?enableSrtp" timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") output = f"{OUTPUT_DIR}/recording_{timestamp}.wav" print(f"Recording from: {camera['name']}") print(f"Output: {output}") print("Press Ctrl+C to stop.") proc = subprocess.Popen([ "ffmpeg", "-y", "-rtsp_transport", "tcp", "-i", rtsp_url, "-vn", "-acodec", "pcm_s16le", output, ]) try: proc.wait() except KeyboardInterrupt: proc.terminate() proc.wait() print(f"\nSaved to {output}") if __name__ == "__main__": main()