# -*- coding: utf-8 -*-
"""
Mock 浏览器捕获客户端（用于离线验证服务端 WS→ffmpeg→HLS→延迟 全链路）

不依赖真实浏览器：
  1. 用 ffmpeg 生成一个示例 FLV（若已存在则复用）
  2. 作为"扩展"连上 ws://127.0.0.1:8091/ws/capture
  3. 发送 init，再把 FLV 字节分块当成媒体流推过去
服务端会把它当捕获流转成 HLS，前端/测试脚本即可拉取 /hls/<cid>/delayed_<秒>.m3u8

用法：
  python mock_capture_client.py                # 默认生成+推送 60s 示例流
  python mock_capture_client.py --cid mycap     # 指定 captureId
  python mock_capture_client.py --loop 3        # 循环推送次数
"""
import sys, os, time, struct, base64, hashlib, socket, subprocess, argparse

def find_ffmpeg():
    import shutil
    p = shutil.which("ffmpeg")
    if p:
        return p
    # 常见位置
    for d in [r"C:\ffmpeg\bin", os.path.expanduser("~/ffmpeg/bin"),
              r"D:\ffmpeg\bin", r"E:\VLLM\ffmpeg\ffmpeg-8.1.1-full_build\bin"]:
        c = os.path.join(d, "ffmpeg.exe" if os.name == "nt" else "ffmpeg")
        if os.path.exists(c):
            return c
    return None

def gen_sample(flv_path, ffmpeg):
    print("[mock] 生成示例 FLV: %s" % flv_path)
    cmd = [ffmpeg, "-loglevel", "error",
           "-f", "lavfi", "-i", "testsrc=size=640x360:rate=25",
           "-f", "lavfi", "-i", "sine=frequency=440:sample_rate=44100",
           "-t", "30", "-c:v", "flv", "-c:a", "aac", "-ar", "44100",
           "-f", "flv", flv_path, "-y"]
    subprocess.run(cmd, check=True)
    print("[mock] 示例 FLV 生成完成")

# ---------- 极简 WebSocket 客户端（客户端必须掩码） ----------
def ws_connect(host, port, path):
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((host, port))
    key = base64.b64encode(os.urandom(16)).decode()
    req = ("GET %s HTTP/1.1\r\nHost: %s\r\nUpgrade: websocket\r\n"
           "Connection: Upgrade\r\nSec-WebSocket-Version: 13\r\n"
           "Sec-WebSocket-Key: %s\r\n\r\n" % (path, host, key))
    s.sendall(req.encode())
    buf = b""
    while b"\r\n\r\n" not in buf:
        buf += s.recv(4096)
    # 校验 101
    if b"101" not in buf.split(b"\r\n", 1)[0]:
        raise RuntimeError("WS 握手失败: %r" % buf[:80])
    return s

def _send_frame(s, opcode, data):
    mask = os.urandom(4)
    b0 = 0x80 | opcode
    ln = len(data)
    if ln < 126:
        header = bytes([b0, 0x80 | ln])
    elif ln < 65536:
        header = bytes([b0, 0x80 | 126]) + struct.pack(">H", ln)
    else:
        header = bytes([b0, 0x80 | 127]) + struct.pack(">Q", ln)
    masked = bytes(data[i] ^ mask[i % 4] for i in range(ln))
    s.sendall(header + mask + masked)

def ws_send_text(s, text):
    _send_frame(s, 0x1, text.encode("utf-8"))

def ws_send_bin(s, data):
    _send_frame(s, 0x2, data)

def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("--host", default="127.0.0.1")
    ap.add_argument("--port", type=int, default=8091)
    ap.add_argument("--cid", default="mockcapt")
    ap.add_argument("--delay", type=int, default=60)
    ap.add_argument("--loop", type=int, default=2)
    ap.add_argument("--hold", type=int, default=6, help="推送完成后保持连接秒数")
    ap.add_argument("--sample", default=None, help="示例 FLV 路径（默认 scripts/sample.flv）")
    args = ap.parse_args()

    base = os.path.dirname(os.path.abspath(__file__))
    flv = args.sample or os.path.join(base, "sample.flv")

    ffmpeg = find_ffmpeg()
    if not ffmpeg:
        print("[mock] 未找到 ffmpeg，无法生成示例流"); sys.exit(1)
    if not os.path.exists(flv):
        gen_sample(flv, ffmpeg)

    print("[mock] 连接 ws://%s:%d/ws/capture ..." % (args.host, args.port))
    s = ws_connect(args.host, args.port, "/ws/capture")
    print("[mock] WS 已连接")

    init = {"type": "init", "captureId": args.cid, "format": "flv",
            "title": "Mock 示例流", "url": "mock://local/sample.flv",
            "delay_seconds": args.delay, "max_cache_seconds": 1800}
    ws_send_text(s, json_dumps(init))

    print("[mock] 开始推送 FLV 字节（captureId=%s, loop=%d）..." % (args.cid, args.loop))
    with open(flv, "rb") as f:
        data = f.read()
    chunk = 32 * 1024
    for _ in range(args.loop):
        off = 0
        while off < len(data):
            ws_send_bin(s, data[off:off + chunk])
            off += chunk
            time.sleep(0.005)
    # 保持连接，让服务端 ffmpeg 持续产出 HLS
    print("[mock] 推送完成，保持连接 %d 秒以便服务端生成分片..." % args.hold)
    time.sleep(args.hold)
    ws_send_text(s, json_dumps({"type": "stop", "captureId": args.cid}))
    s.close()
    print("[mock] 完成。可用浏览器打开 http://localhost:8090 → 浏览器捕获 验证。")

def json_dumps(o):
    import json
    return json.dumps(o)

if __name__ == "__main__":
    main()
