v2.2 前端增强: - 站内 HLS 收听页 (#pageLive),hls.js 本地化 vendor/hls.min.js - 设置页 (缓存/配置/日志) - 上传进度百分比 (XHR upload.onprogress) - 资源列表排序 (时间/热度/名称 ×升降) - 面板正在播放实时化 (接 playlist-update/song-finished,删死监听) - 死代码清理 + forgetSong 内存泄漏修复 + CORS 非法头移除 - start.cmd 托管启动脚本 (set "VAR=val" 避免尾部空格) v2.3 UI 去 AI 味 (依据 ibelick/ui-skills baseline-ui): - 频道管理+切换合并为单一入口 #btnStreamMgr.channel-btn (删除 header 的 #streamSelect 下拉,弹窗内统一切换/删除/新建) - 清除全部 CSS 渐变与 glow: 橙红 #ff5f6d→#ffc371、紫 #8b5cf6、 径向光晕、缓存条渐变 → 全部改为 accent/token 纯色 - live 区、modal 区硬编码色统一归 CSS 变量 token 测试: api 16/16, e2e 17/17, write 5/5 全绿
63 lines
3.0 KiB
Python
63 lines
3.0 KiB
Python
import urllib.request, urllib.error, http.cookiejar, json, sys, time
|
|
|
|
BASE = "http://127.0.0.1:3000"
|
|
|
|
def opener_with_cookies():
|
|
cj = http.cookiejar.CookieJar()
|
|
return urllib.request.build_opener(urllib.request.HTTPCookieProcessor(cj))
|
|
|
|
def call(op, path, method="GET", data=None):
|
|
url = BASE + path
|
|
body = json.dumps(data).encode() if data is not None else None
|
|
req = urllib.request.Request(url, data=body, method=method)
|
|
if body:
|
|
req.add_header("Content-Type", "application/json")
|
|
try:
|
|
r = op.open(req, timeout=10)
|
|
return r.status, r.read()
|
|
except urllib.error.HTTPError as e:
|
|
return e.code, e.read()
|
|
|
|
anon = opener_with_cookies()
|
|
auth = opener_with_cookies()
|
|
results = []
|
|
def check(name, cond, extra=""):
|
|
results.append((name, cond)); print(("PASS" if cond else "FAIL"), name, extra)
|
|
|
|
# 鉴权
|
|
st, _ = call(anon, "/api/resources"); check("未登录API=401", st == 401, f"got {st}")
|
|
st, body = call(anon, "/login"); check("登录页200", st == 200, f"got {st}")
|
|
st, body = call(anon, "/"); check("未登录/=登录页", st == 200 and b"loginForm" in body, f"got {st}")
|
|
st, _ = call(anon, "/index.html"); check("未登录index.html=登录页", st == 200 and b"loginForm" in _, f"got {st}")
|
|
st, _ = call(auth, "/api/auth/login", "POST", {"password": "wrong"}); check("错密码401", st == 401, f"got {st}")
|
|
st, body = call(auth, "/api/auth/login", "POST", {"password": "test123"}); check("登录成功", st == 200 and json.loads(body).get("ok"), f"got {st}")
|
|
|
|
# 资源与流
|
|
st, body = call(auth, "/api/resources"); d = json.loads(body)
|
|
check("资源列表非空", st == 200 and len(d["list"]) >= 1, f"{len(d.get('list',[]))}项")
|
|
first = d["list"][0]["file"]
|
|
|
|
tid = "t%d" % int(time.time())
|
|
st, body = call(auth, "/api/streams", "POST", {"id": tid, "name": "临时测试流"})
|
|
check("创建流", st == 200 and json.loads(body).get("ok"), body[:80].decode("utf-8","ignore"))
|
|
st, body = call(anon, "/hls/%s/index.m3u8" % tid); check("新流m3u8可达", st == 200, f"got {st}")
|
|
st, body = call(auth, "/api/streams/%s/playlist/add" % tid, "POST", {"file": first})
|
|
check("新流加歌", st == 200, f"got {st}")
|
|
st, body = call(auth, "/api/streams"); d = json.loads(body)
|
|
check("流列表含新流", any(s["id"] == tid for s in d["list"]), "")
|
|
# 清理
|
|
st, body = call(auth, "/api/streams/%s" % tid, "DELETE", {})
|
|
check("删除流", st == 200, f"got {st}")
|
|
|
|
# 缓存/日志
|
|
st, body = call(auth, "/api/system/cache"); d = json.loads(body)
|
|
check("缓存信息", st == 200 and "total" in d, f"{d.get('total')}B")
|
|
st, body = call(auth, "/api/system/logs?tail=5")
|
|
check("日志接口", st == 200 and len(json.loads(body).get("lines", [])) > 0, "")
|
|
|
|
# 登出
|
|
st, _ = call(auth, "/api/auth/logout", "POST"); check("登出", st == 200, f"got {st}")
|
|
st, _ = call(auth, "/api/resources"); check("登出后401", st == 401, f"got {st}")
|
|
|
|
print("\n=== 汇总:", sum(1 for _, c in results if c), "/", len(results), "通过 ===")
|
|
sys.exit(0 if all(c for _, c in results) else 1) |