's'
This commit is contained in:
@@ -10,3 +10,9 @@ PORT=8001
|
|||||||
|
|
||||||
# 开发模式时 Next 端口(一般无需改)
|
# 开发模式时 Next 端口(一般无需改)
|
||||||
# NEXT_DEV_PORT=3000
|
# NEXT_DEV_PORT=3000
|
||||||
|
|
||||||
|
# scrcpy-server 自动下载(安卓 Web 真流):默认同版本走 GitHub;内网可镜像后设下列之一(支持 {tag} / {version})
|
||||||
|
# SCRCPY_SERVER_DOWNLOAD_URL=https://你的镜像/scrcpy-server-v{tag}
|
||||||
|
# SCRCPY_SERVER_DOWNLOAD_URLS=https://a/v{tag},https://b/v{tag}
|
||||||
|
# SCRCPY_SERVER_DOWNLOAD_NO_GITHUB=1
|
||||||
|
# SKIP_SCRCPY_SERVER_FETCH=1
|
||||||
|
|||||||
@@ -34,8 +34,19 @@ Use this when the device should only run the Douyin or YouTube business stack.
|
|||||||
- Installs the Android web panel based on `web-scrcpy`
|
- Installs the Android web panel based on `web-scrcpy`
|
||||||
- Starts SRS
|
- Starts SRS
|
||||||
- Builds the web console and enables `live-console.service`
|
- Builds the web console and enables `live-console.service`
|
||||||
|
- Installs **Caddy** `live-edge.service` so **`http://live.local`** (port 80) reverse-proxies to the control plane (same as hub)
|
||||||
- Generates the hardware profile for adaptive ARM or X86 defaults
|
- Generates the hardware profile for adaptive ARM or X86 defaults
|
||||||
|
|
||||||
|
### `http://live.local` returns 502
|
||||||
|
|
||||||
|
502 means the edge proxy reached nothing usable on the upstream port (default `127.0.0.1:8001`).
|
||||||
|
|
||||||
|
1. Check the control plane: `sudo systemctl status live-console.service` and `journalctl -u live-console.service -n 80 --no-pager`.
|
||||||
|
2. Check the edge: `sudo systemctl status live-edge.service` and confirm `config/system-stack.env` **`PORT`** matches what Caddy uses (regenerate with `sudo bash scripts/linux/reinstall_live_edge.sh` from the repo root).
|
||||||
|
3. Quick probe: `curl -sf http://127.0.0.1:8001/health` — if this fails, fix `live-console` first (Python venv, `web-console/out`, or `scripts/launch.py` errors in the journal).
|
||||||
|
|
||||||
|
**Older business installs** (before `install_live_edge_proxy` was included) never installed Caddy; if you still see 502 on port 80, you likely had a prior hub/edge install—run `sudo bash scripts/linux/reinstall_live_edge.sh` after pulling the latest repo.
|
||||||
|
|
||||||
### PM2:单频道与多频道 Pro(Linux ARM / x86_64)
|
### PM2:单频道与多频道 Pro(Linux ARM / x86_64)
|
||||||
|
|
||||||
- **单频道**:在项目根启用一个业务进程即可,例如取消注释 `ecosystem.config.cjs` 里的 `youtube` 或 `tiktok`,执行 `pm2 start ecosystem.config.cjs && pm2 save`。
|
- **单频道**:在项目根启用一个业务进程即可,例如取消注释 `ecosystem.config.cjs` 里的 `youtube` 或 `tiktok`,执行 `pm2 start ecosystem.config.cjs && pm2 save`。
|
||||||
|
|||||||
@@ -30,4 +30,6 @@ install_browser_stack
|
|||||||
install_neko_browser
|
install_neko_browser
|
||||||
prepare_srs
|
prepare_srs
|
||||||
run_hardware_probe
|
run_hardware_probe
|
||||||
|
# 与 install-hub 一致:装 Caddy live-edge,http://live.local:80 → 本机控制平面 PORT(默认 8001)
|
||||||
|
install_live_edge_proxy
|
||||||
print_summary "business"
|
print_summary "business"
|
||||||
|
|||||||
@@ -67,14 +67,14 @@ def scrcpy_stream_info() -> dict[str, Any]:
|
|||||||
"server_version": ver,
|
"server_version": ver,
|
||||||
"remote_jar": REMOTE_JAR,
|
"remote_jar": REMOTE_JAR,
|
||||||
"hint_zh": (
|
"hint_zh": (
|
||||||
"服务启动时会尝试从 Genymobile 官方 Release 拉取与 vendor/scrcpy-server.version 匹配的 scrcpy-server;"
|
"启动时会拉取与 vendor/scrcpy-server.version 匹配的 scrcpy-server(默认同源 GitHub);"
|
||||||
"离线环境可预置 vendor/scrcpy-server.jar 或设置 SCRCPY_SERVER_JAR。"
|
"内网可设 SCRCPY_SERVER_DOWNLOAD_URL(支持 {tag});离线可预置 jar 或 SCRCPY_SERVER_JAR。"
|
||||||
if not jar
|
if not jar
|
||||||
else "scrcpy-server 已就绪。"
|
else "scrcpy-server 已就绪。"
|
||||||
),
|
),
|
||||||
"hint_en": (
|
"hint_en": (
|
||||||
"On startup the hub tries to download the matching scrcpy-server from Genymobile releases; "
|
"Startup fetches scrcpy-server for vendor/scrcpy-server.version (default: GitHub). "
|
||||||
"for offline use place vendor/scrcpy-server.jar or set SCRCPY_SERVER_JAR."
|
"Use SCRCPY_SERVER_DOWNLOAD_URL with {tag} for mirrors; offline: vendor jar or SCRCPY_SERVER_JAR."
|
||||||
if not jar
|
if not jar
|
||||||
else "scrcpy-server is ready."
|
else "scrcpy-server is ready."
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
import time
|
||||||
import urllib.error
|
import urllib.error
|
||||||
import urllib.request
|
import urllib.request
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
@@ -10,6 +11,8 @@ from pathlib import Path
|
|||||||
# 官方发行资产名:scrcpy-server-v{version}(无扩展名,实为 JAR)
|
# 官方发行资产名:scrcpy-server-v{version}(无扩展名,实为 JAR)
|
||||||
_GITHUB_DL = "https://github.com/Genymobile/scrcpy/releases/download/v{tag}/scrcpy-server-v{tag}"
|
_GITHUB_DL = "https://github.com/Genymobile/scrcpy/releases/download/v{tag}/scrcpy-server-v{tag}"
|
||||||
_MIN_BYTES = 8_000
|
_MIN_BYTES = 8_000
|
||||||
|
_PER_URL_ATTEMPTS = 3
|
||||||
|
_PER_URL_DELAY_S = 2.0
|
||||||
|
|
||||||
|
|
||||||
def _read_version(repo_root: Path) -> str:
|
def _read_version(repo_root: Path) -> str:
|
||||||
@@ -24,10 +27,70 @@ def _read_version(repo_root: Path) -> str:
|
|||||||
return "3.3.3"
|
return "3.3.3"
|
||||||
|
|
||||||
|
|
||||||
|
def _expand_download_url(template: str, tag: str) -> str:
|
||||||
|
return template.replace("{tag}", tag).replace("{version}", tag)
|
||||||
|
|
||||||
|
|
||||||
|
def _download_urls_to_try(tag: str) -> list[str]:
|
||||||
|
"""顺序:SCRCPY_SERVER_DOWNLOAD_URL → SCRCPY_SERVER_DOWNLOAD_URLS →(默认可选)GitHub。"""
|
||||||
|
urls: list[str] = []
|
||||||
|
single = (os.environ.get("SCRCPY_SERVER_DOWNLOAD_URL") or "").strip()
|
||||||
|
if single:
|
||||||
|
urls.append(_expand_download_url(single, tag))
|
||||||
|
raw = (os.environ.get("SCRCPY_SERVER_DOWNLOAD_URLS") or "").strip()
|
||||||
|
if raw:
|
||||||
|
for part in raw.split(","):
|
||||||
|
u = part.strip()
|
||||||
|
if not u:
|
||||||
|
continue
|
||||||
|
uu = _expand_download_url(u, tag)
|
||||||
|
if uu not in urls:
|
||||||
|
urls.append(uu)
|
||||||
|
no_github = os.environ.get("SCRCPY_SERVER_DOWNLOAD_NO_GITHUB", "").strip().lower() in (
|
||||||
|
"1",
|
||||||
|
"true",
|
||||||
|
"yes",
|
||||||
|
"on",
|
||||||
|
)
|
||||||
|
gh = _GITHUB_DL.format(tag=tag)
|
||||||
|
if not no_github and gh not in urls:
|
||||||
|
urls.append(gh)
|
||||||
|
seen: set[str] = set()
|
||||||
|
out: list[str] = []
|
||||||
|
for u in urls:
|
||||||
|
if u not in seen:
|
||||||
|
seen.add(u)
|
||||||
|
out.append(u)
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
def _fetch_url(url: str) -> tuple[bool, bytes | None, str]:
|
||||||
|
req = urllib.request.Request(
|
||||||
|
url,
|
||||||
|
headers={"User-Agent": "douyinyoutube-live-hub/scrcpy-bootstrap"},
|
||||||
|
method="GET",
|
||||||
|
)
|
||||||
|
last_err = ""
|
||||||
|
for attempt in range(1, _PER_URL_ATTEMPTS + 1):
|
||||||
|
try:
|
||||||
|
with urllib.request.urlopen(req, timeout=120) as resp:
|
||||||
|
data = resp.read()
|
||||||
|
return True, data, ""
|
||||||
|
except (urllib.error.HTTPError, urllib.error.URLError, TimeoutError, OSError) as e:
|
||||||
|
last_err = str(e)
|
||||||
|
if attempt < _PER_URL_ATTEMPTS:
|
||||||
|
time.sleep(_PER_URL_DELAY_S)
|
||||||
|
return False, None, last_err
|
||||||
|
|
||||||
|
|
||||||
def ensure_scrcpy_jar(repo_root: Path) -> tuple[bool, str]:
|
def ensure_scrcpy_jar(repo_root: Path) -> tuple[bool, str]:
|
||||||
"""
|
"""
|
||||||
若 vendor/scrcpy-server.jar 不存在或过小,则从 Genymobile GitHub Release 下载。
|
若 vendor/scrcpy-server.jar 不存在或过小,则按 URL 列表下载。
|
||||||
可通过环境变量 SKIP_SCRCPY_SERVER_FETCH=1 跳过。
|
环境变量:
|
||||||
|
SKIP_SCRCPY_SERVER_FETCH=1 — 跳过
|
||||||
|
SCRCPY_SERVER_DOWNLOAD_URL — 优先下载地址,可用 {tag}/{version}
|
||||||
|
SCRCPY_SERVER_DOWNLOAD_URLS — 逗号分隔多条,先于 GitHub
|
||||||
|
SCRCPY_SERVER_DOWNLOAD_NO_GITHUB=1 — 仅用上面自定义 URL,不回退 GitHub
|
||||||
"""
|
"""
|
||||||
if os.environ.get("SKIP_SCRCPY_SERVER_FETCH", "").strip().lower() in ("1", "true", "yes", "on"):
|
if os.environ.get("SKIP_SCRCPY_SERVER_FETCH", "").strip().lower() in ("1", "true", "yes", "on"):
|
||||||
return True, "skip fetch (SKIP_SCRCPY_SERVER_FETCH)"
|
return True, "skip fetch (SKIP_SCRCPY_SERVER_FETCH)"
|
||||||
@@ -44,34 +107,32 @@ def ensure_scrcpy_jar(repo_root: Path) -> tuple[bool, str]:
|
|||||||
return True, "vendor/scrcpy-server.jar ok"
|
return True, "vendor/scrcpy-server.jar ok"
|
||||||
|
|
||||||
tag = _read_version(repo_root)
|
tag = _read_version(repo_root)
|
||||||
url = _GITHUB_DL.format(tag=tag)
|
urls = _download_urls_to_try(tag)
|
||||||
|
if not urls:
|
||||||
|
return False, "未配置任何下载源(SCRCPY_SERVER_DOWNLOAD_NO_GITHUB=1 且未设置 SCRCPY_SERVER_DOWNLOAD_URL(S))"
|
||||||
vendor_dir.mkdir(parents=True, exist_ok=True)
|
vendor_dir.mkdir(parents=True, exist_ok=True)
|
||||||
tmp = dest.with_suffix(".jar.part")
|
tmp = dest.with_suffix(".jar.part")
|
||||||
|
|
||||||
try:
|
last_fail = ""
|
||||||
req = urllib.request.Request(
|
for url in urls:
|
||||||
url,
|
ok, data, err = _fetch_url(url)
|
||||||
headers={"User-Agent": "douyinyoutube-live-hub/scrcpy-bootstrap"},
|
if not ok or data is None:
|
||||||
method="GET",
|
last_fail = f"{url}: {err}"
|
||||||
)
|
continue
|
||||||
with urllib.request.urlopen(req, timeout=120) as resp:
|
if len(data) < _MIN_BYTES:
|
||||||
data = resp.read()
|
last_fail = f"{url}: too small ({len(data)} bytes)"
|
||||||
except (urllib.error.HTTPError, urllib.error.URLError, TimeoutError, OSError) as e:
|
continue
|
||||||
return False, f"download failed ({url}): {e}"
|
if not data.startswith(b"PK"):
|
||||||
|
last_fail = f"{url}: not a JAR (missing PK header)"
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
tmp.write_bytes(data)
|
||||||
|
os.replace(tmp, dest)
|
||||||
|
except OSError as e:
|
||||||
|
return False, f"write vendor/scrcpy-server.jar failed: {e}"
|
||||||
|
return True, f"fetched scrcpy-server v{tag} -> vendor/scrcpy-server.jar (via {url})"
|
||||||
|
|
||||||
if len(data) < _MIN_BYTES:
|
return False, f"download failed after {len(urls)} source(s): {last_fail}"
|
||||||
return False, f"download too small ({len(data)} bytes), check version {tag}"
|
|
||||||
|
|
||||||
if not data.startswith(b"PK"):
|
|
||||||
return False, "downloaded file does not look like a JAR (expected PK header)"
|
|
||||||
|
|
||||||
try:
|
|
||||||
tmp.write_bytes(data)
|
|
||||||
os.replace(tmp, dest)
|
|
||||||
except OSError as e:
|
|
||||||
return False, f"write vendor/scrcpy-server.jar failed: {e}"
|
|
||||||
|
|
||||||
return True, f"fetched scrcpy-server v{tag} -> vendor/scrcpy-server.jar"
|
|
||||||
|
|
||||||
|
|
||||||
def main() -> int:
|
def main() -> int:
|
||||||
|
|||||||
@@ -882,8 +882,8 @@ export function LiveAndroidStreamConsole({
|
|||||||
{scrcpyInfo && !scrcpyInfo.server_jar ? (
|
{scrcpyInfo && !scrcpyInfo.server_jar ? (
|
||||||
<p className="mt-2 text-[10px] text-amber-200/80">
|
<p className="mt-2 text-[10px] text-amber-200/80">
|
||||||
{t(
|
{t(
|
||||||
"未检测到 scrcpy-server:服务启动时会自动从官方 Release 拉取;若仍失败请检查宿主机出网或重启 web 进程。",
|
"未检测到 scrcpy-server:启动时会自动拉取;内网可在 runtime.env 配置 SCRCPY_SERVER_DOWNLOAD_URL(支持 {tag})。",
|
||||||
"scrcpy-server missing: it is fetched on hub startup from official releases; check outbound HTTPS or restart web.",
|
"scrcpy-server missing: auto-fetched on startup; set SCRCPY_SERVER_DOWNLOAD_URL ({tag}) for mirrors.",
|
||||||
)}
|
)}
|
||||||
{scrcpyInfo.server_version ? ` (${t("期望版本", "version")} ${scrcpyInfo.server_version})` : null}
|
{scrcpyInfo.server_version ? ` (${t("期望版本", "version")} ${scrcpyInfo.server_version})` : null}
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
Reference in New Issue
Block a user