From 895dafc0e08b903f5bafa39e9609ae0c84608c4f Mon Sep 17 00:00:00 2001 From: eric Date: Sun, 29 Mar 2026 03:08:07 -0500 Subject: [PATCH] 's' --- .../live-product/LiveAndroidStreamConsole.tsx | 20 ++++++++++----- web.py | 25 +++++++++++++++++++ 2 files changed, 39 insertions(+), 6 deletions(-) diff --git a/web-console/components/live-product/LiveAndroidStreamConsole.tsx b/web-console/components/live-product/LiveAndroidStreamConsole.tsx index 0faf894..052de3c 100644 --- a/web-console/components/live-product/LiveAndroidStreamConsole.tsx +++ b/web-console/components/live-product/LiveAndroidStreamConsole.tsx @@ -142,6 +142,13 @@ type Props = { const SCRCPY_URL = "https://github.com/Genymobile/scrcpy"; const ESCRCPY_URL = "https://github.com/viarotel-org/escrcpy"; +/** 与 LiveControlApp 一致:apiBase 为空时用根相对路径(同域 FastAPI 托管静态页)。 */ +function apiUrl(apiBase: string, path: string): string { + const p = path.startsWith("/") ? path : `/${path}`; + const b = (apiBase || "").trim().replace(/\/$/, ""); + return b ? `${b}${p}` : p; +} + export function LiveAndroidStreamConsole({ t, apiBase, @@ -188,10 +195,9 @@ export function LiveAndroidStreamConsole({ const activeTransport = devices.find((d) => d.serial === serial)?.transport; const useFastShot = liveMs > 0; - const shotUrl = - apiBase && serial - ? `${apiBase}/android/screenshot?serial=${encodeURIComponent(serial)}&ts=${frameTs}${useFastShot ? "&fast=1" : ""}` - : ""; + const shotUrl = serial + ? `${apiUrl(apiBase, "/android/screenshot")}?serial=${encodeURIComponent(serial)}&ts=${frameTs}${useFastShot ? "&fast=1" : ""}` + : ""; const bumpFrame = useCallback(() => { setFrameTs((n) => n + 1); @@ -230,7 +236,9 @@ export function LiveAndroidStreamConsole({ let cancelled = false; void (async () => { try { - const res = await fetch(`${apiBase}/android/display_metrics?serial=${encodeURIComponent(serial)}`); + const res = await fetch( + `${apiUrl(apiBase, "/android/display_metrics")}?serial=${encodeURIComponent(serial)}`, + ); const data = (await res.json()) as { width?: number; height?: number }; if (!res.ok || cancelled) return; if (typeof data.width === "number" && typeof data.height === "number") { @@ -259,7 +267,7 @@ export function LiveAndroidStreamConsole({ let cancelled = false; void (async () => { try { - const res = await fetch(`${apiBase}/android/scrcpy/info`); + const res = await fetch(apiUrl(apiBase, "/android/scrcpy/info")); const data = (await res.json()) as { server_jar?: boolean; adb_ok?: boolean; diff --git a/web.py b/web.py index e370667..322bac0 100644 --- a/web.py +++ b/web.py @@ -216,6 +216,28 @@ def _pro_script_head(pm2: str) -> Optional[str]: return head.lower() if head else None +def _resolve_pro_style_script(head_l: str) -> Optional[str]: + """多频道 / Electron 风格 PM2 名前缀(如 youtube2__、tiktok_live__)映射到仓库内脚本。 + + 不能仅用 stem 相等判断:youtube2 != youtube.py 的 stem「youtube」。 + """ + if not head_l: + return None + h = head_l.lower() + py_scripts = [p["script"] for p in PROCESS_MONITOR if str(p.get("script", "")).endswith(".py")] + matches: list[str] = [] + if h.startswith("douyin_youtube"): + matches = [s for s in py_scripts if Path(s).stem.lower().startswith("douyin_youtube")] + elif h.startswith("youtube"): + matches = [s for s in py_scripts if Path(s).stem.lower().startswith("youtube")] + elif h.startswith("tiktok"): + matches = [s for s in py_scripts if Path(s).stem.lower().startswith("tiktok")] + if not matches: + return None + matches.sort(key=lambda s: (len(Path(s).stem), s)) + return matches[0] + + def script_for_pm2_or_pro(pm2: str) -> Optional[str]: s = script_for_pm2(pm2) if s: @@ -223,6 +245,9 @@ def script_for_pm2_or_pro(pm2: str) -> Optional[str]: head_l = _pro_script_head(pm2) if not head_l: return None + resolved = _resolve_pro_style_script(head_l) + if resolved: + return resolved for p in PROCESS_MONITOR: if Path(p["script"]).stem.lower() == head_l: return p["script"]