#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
car-ktv —— 车载 KTV 遥控（走服务器转接池 /srv/ktv-pool）

  car-ktv search <词> [条数]      搜歌（跨源聚合，返回原唱/伴奏/歌词直链）
  car-ktv play <词>              搜歌 + 直接在车机播（IJK 播伴奏/原唱直链）
  car-ktv add <歌曲id>           加进点歌列表（车机 KTV app 会拉）
  car-ktv box                    看点歌列表
  car-ktv del <deviceSongsUid>   从点歌列表删
  car-ktv cats                   分类列表
  car-ktv cat <分类id> [条数]     某个分类下的歌
  car-ktv lyric <歌词url>        取歌词（KRC）
  car-ktv sources                源列表 + 健康
  car-ktv open                   拉起车机 KTV app
  car-ktv raw <路径> [k=v&..]    直接打池子的原始接口（调试用）
"""
import json
import sys
import time
import urllib.parse
import urllib.request

POOL = "http://127.0.0.1:8099"
PKG = "com.aske.carktv.xz"
IJK = "com.juyoudao.videosearch/.IjkVideoPlayerActivity"
CAR_TUN = ("127.0.0.1", "15556")   # 15555 现在是 adbd（adb 协议），命令通道是 15556


def get(path, params=None, timeout=70):
    url = POOL + path
    if params:
        url += "?" + urllib.parse.urlencode(params)
    try:
        with urllib.request.urlopen(url, timeout=timeout) as r:
            body = r.read().decode("utf-8", "replace")
            try:
                return json.loads(body)
            except Exception:
                return body
    except Exception as e:
        return {"error": repr(e)}


def post(path, payload, timeout=70):
    req = urllib.request.Request(POOL + path,
                                 data=json.dumps(payload).encode(),
                                 headers={"Content-Type": "application/json"},
                                 method="POST")
    try:
        with urllib.request.urlopen(req, timeout=timeout) as r:
            return json.loads(r.read().decode("utf-8", "replace"))
    except Exception as e:
        return {"error": repr(e)}


def car(cmd, timeout=20):
    """车机 root shell（经 frp 隧道）"""
    try:
        # 末尾补 exit：让车机那侧 shell 立刻退出、连接马上断（否则要等 nc 空闲超时 20 秒）
        p = subprocess_run(["nc", "-w", str(timeout), CAR_TUN[0], CAR_TUN[1]],
                           input=cmd + "\nexit\n")
        return p
    except Exception as e:
        return "ERR %r" % e


def subprocess_run(cmd, input=None):
    import subprocess
    p = subprocess.run(cmd, input=input, capture_output=True, text=True, timeout=40)
    return (p.stdout or "") + (p.stderr or "")


def show_songs(songs, limit=10):
    if not songs:
        print("没搜到")
        return
    for i, s in enumerate(songs[:limit]):
        print("[%d] %s - %s  (id=%s, 时长=%s, 源=%s)"
              % (i, s.get("title", "?"), s.get("singer", "?"), s.get("id", ""),
                 s.get("duration", "?"), s.get("source", "")))
        if s.get("url"):
            print("     原唱: %s" % s["url"])
        if s.get("accomp"):
            print("     伴奏: %s" % s["accomp"])
        if s.get("lyric"):
            print("     歌词: %s" % s["lyric"])


def main():
    if len(sys.argv) < 2:
        print(__doc__)
        return 1
    cmd = sys.argv[1]
    args = sys.argv[2:]

    if cmd == "search":
        kw = " ".join(args[:-1]) if len(args) > 1 and args[-1].isdigit() else " ".join(args)
        limit = int(args[-1]) if args and args[-1].isdigit() else 10
        r = get("/api/ktv/search", {"kw": kw, "limit": limit})
        if r.get("error") or not r.get("songs"):
            print("搜不到: %s" % (r.get("errors") or r.get("error") or ""))
            return 1
        print("共 %d 首 (源=%s)" % (r["count"], r.get("source")))
        show_songs(r["songs"], limit)
        return 0

    if cmd == "play":
        kw = " ".join(args)
        # 1) 先在 KTV 自有曲库（carapp）里搜 —— 它的歌才有伴奏+KRC歌词，app 才唱得了
        r = get("/api/ktv/play", {"kw": kw, "source": "carapp"})
        if not r.get("best"):
            # carapp 没有 → 退回全源（网易云伴奏版），只能走播放器
            r = get("/api/ktv/play", {"kw": kw})
        if not r.get("best"):
            print("搜不到: %s" % (r.get("error") or r.get("errors") or ""))
            return 1
        b = r["best"]
        print("选中: %s - %s" % (b.get("title"), b.get("singer")))

        # 2) 拉起 KTV 点歌台 + 广播 PLAY_ORIGINAL（app 用 MediaPlayer 播原唱，不弹视频播放器）
        car("am start -n %s/com.lehu.mystyle.boardktv.ui.activity.SplashActivity" % PKG)
        time.sleep(3)
        car("am broadcast -a com.laowu.ktvctl.PLAY_ORIGINAL --es kw '%s'" % kw)
        time.sleep(4)
        log = car("busybox tail -4 /sdcard/ktvlog/ktv_ctl.log 2>/dev/null; "
                  "busybox tail -4 /sdcard/Android/data/%s/files/ktv_ctl.log 2>/dev/null" % PKG).strip()
        if "play " in log:
            print("已在车载KTV播原唱: %s" % b.get("title"))
            return 0
        if "没搜到" in log:
            print("车机KTV没搜到: %s" % b.get("title"))
            return 1
        print("已下发播原唱指令（车机日志还没回报）: %s" % b.get("title"))
        return 0

        # 2b) 旧路：走 app 自己的演唱页（点歌→置顶→开唱，带歌词界面）
        if b.get("id"):
            sg = post("/api/ktv/sing", {"id": b.get("id")})
            if sg.get("ok"):
                out = car("am start -n %s/com.lehu.mystyle.boardktv.ui.activity.SplashActivity"
                          % PKG).strip()
                if not out:
                    print("!! 车机没回显，KTV 可能没拉起来（通道问题）")
                    return 1
                print("已在车载KTV点歌台开唱: %s" % b.get("title"))
                return 0
            print("（KTV 开唱没成功，%s）" % (sg.get("error") or ""))

        # 3) 兜底：KTV 曲库里没这首 → 才用播放器放伴奏（并明说）
        url = b.get("accomp") or b.get("url")
        if not url:
            print("没有可播的直链")
            return 1
        print("直链: %s" % url)
        print("（KTV 曲库里没有这首歌，改用播放器放伴奏）")
        out = car("am start -n %s -e video_url '%s' -e ep_title '%s'"
                  % (IJK, url, b.get("title", "KTV"))).strip()
        if not out:
            # 车机没回显 = 通道没通 / 命令没执行。必须当失败，不许假装成功。
            print("!! 车机没回显，命令可能没执行（通道问题）")
            return 1
        print(out)
        return 0

    if cmd == "add":
        if not args:
            print("用法: car-ktv add <歌曲id>")
            return 1
        r = post("/api/ktv/box/add", {"id": args[0]})
        print(json.dumps(r, ensure_ascii=False)[:500])
        return 0 if r.get("ok") else 1

    if cmd == "box":
        r = get("/api/ktv/box")
        if not r.get("songs"):
            print("点歌列表为空/不可用: %s" % (r.get("error") or ""))
            return 1
        print("点歌列表 (%d):" % len(r["songs"]))
        show_songs(r["songs"], 50)
        return 0

    if cmd == "del":
        if not args:
            print("用法: car-ktv del <deviceSongsUid>")
            return 1
        r = post("/api/ktv/box/del", {"uid": args[0]})
        print(json.dumps(r, ensure_ascii=False)[:500])
        return 0 if r.get("ok") else 1

    if cmd == "cats":
        r = get("/api/ktv/categories")
        for c in r.get("categories", []):
            print("[%s] %s   %s" % (c.get("id"), c.get("name"), c.get("remark") or ""))
        return 0

    if cmd == "cat":
        if not args:
            print("用法: car-ktv cat <分类id> [条数]")
            return 1
        limit = int(args[1]) if len(args) > 1 else 30
        r = get("/api/ktv/category-songs", {"id": args[0], "limit": limit})
        show_songs(r.get("songs", []), limit)
        return 0

    if cmd == "lyric":
        if not args:
            print("用法: car-ktv lyric <url>")
            return 1
        try:
            with urllib.request.urlopen(args[0], timeout=20) as rr:
                txt = rr.read().decode("utf-8", "replace")
            print(txt[:3000])
        except Exception as e:
            print("取歌词失败: %r" % e)
            return 1
        return 0

    if cmd == "sources":
        r = get("/api/ktv/sources")
        for s in r.get("sources", []):
            h = s.get("health", {})
            print("%-12s %-40s %s" % (s.get("name"), s.get("label"),
                                      "OK" if h.get("ok") else "异常 %s" % h))
        return 0

    if cmd == "open":
        out = car("am start -n %s/com.lehu.mystyle.boardktv.ui.activity.SplashActivity" % PKG).strip()
        if not out:
            print("!! 车机没回显，KTV 可能没拉起来（通道问题）")
            return 1
        print(out)
        return 0

    if cmd == "raw":
        if not args:
            print("用法: car-ktv raw <路径> [k=v&k2=v2]")
            return 1
        path = args[0]
        q = args[1] if len(args) > 1 else ""
        url = POOL + path + (("?" + q) if q else "")
        print(json.dumps(get(path, dict(urllib.parse.parse_qsl(q)) if q else None),
                         ensure_ascii=False, indent=1)[:3000])
        return 0

    print(__doc__)
    return 1


if __name__ == "__main__":
    sys.exit(main())
