From 247edda3eb901b140d6d7cbc70034c0d19468231 Mon Sep 17 00:00:00 2001 From: javis-bot Date: Mon, 15 Jun 2026 13:13:11 +0900 Subject: [PATCH] fix: google anti-bot flag + persistent/safe settings apply + TTS engine wiring - Chrome: --disable-blink-features=AutomationControlled (+ ko-KR) so Google shows results, not the /sorry/ automation block. - Settings persist to /data/jarvis-settings.json (survives recreate; entrypoint re-merges it) AND the runtime config; apply restarts via a DETACHED process so the HTTP response isn't dropped when the bridge restarts. - Bridge reads tts_engine from the settings config so the TTS-engine choice actually applies. --- bridge/server.py | 15 +++++++++++- bridge/settings_web.py | 55 ++++++++++++++++++++++++++++++------------ docker/entrypoint.sh | 16 ++++++++++++ docker/run-chrome.sh | 3 +++ 4 files changed, 73 insertions(+), 16 deletions(-) diff --git a/bridge/server.py b/bridge/server.py index a8a3fe8..daf2a8a 100644 --- a/bridge/server.py +++ b/bridge/server.py @@ -90,7 +90,20 @@ STT_LANGUAGE = os.environ.get("STT_LANGUAGE", "ko").strip() or None # TTS engine: "melo" (MeloTTS Korean speaker, the warm worker) is the primary # voice; Piper is kept as a fallback if the worker is unreachable. Set # TTS_ENGINE=piper to disable MeloTTS entirely. -TTS_ENGINE = os.environ.get("TTS_ENGINE", "melo").strip().lower() +def _tts_engine_setting() -> str: + """TTS engine: settings-UI value (runtime config JSON) wins, else env, else + melo. Read at startup; the settings UI restarts the bridge on apply.""" + try: + _cp = os.environ.get("JARVIS_CONFIG_PATH", "/app/config/jarvis.json") + _v = json.loads(open(_cp, encoding="utf-8").read()).get("tts_engine") + if _v: + return str(_v).strip().lower() + except Exception: + pass + return os.environ.get("TTS_ENGINE", "melo").strip().lower() + + +TTS_ENGINE = _tts_engine_setting() MELO_WORKER_URL = os.environ.get("MELO_WORKER_URL", "http://127.0.0.1:8770") MELO_TIMEOUT = float(os.environ.get("MELO_TIMEOUT", "30")) # When MeloTTS is the engine, do NOT silently fall back to the English Piper diff --git a/bridge/settings_web.py b/bridge/settings_web.py index 042336a..bdb97af 100644 --- a/bridge/settings_web.py +++ b/bridge/settings_web.py @@ -37,6 +37,12 @@ def _config_path() -> Path: return Path(p).expanduser() if p else (Path.home() / ".config" / "jarvis" / "config.json") +def _persist_path() -> Path: + """Persistent overrides on the data volume — survive container recreate. + entrypoint.sh merges this back onto the env-rendered config at startup.""" + return Path(os.environ.get("JARVIS_SETTINGS_PATH") or "/data/jarvis-settings.json") + + def _read_config() -> Dict[str, Any]: try: return json.loads(_config_path().read_text("utf-8")) @@ -67,8 +73,8 @@ def _ollama_models() -> list[str]: return [] -def _save(updates: Dict[str, Any]) -> None: - cfg = _read_config() +def _coerce(updates: Dict[str, Any]) -> Dict[str, Any]: + clean: Dict[str, Any] = {} for k, v in updates.items(): if k not in _KEYS: continue @@ -84,22 +90,41 @@ def _save(updates: Dict[str, Any]) -> None: continue elif k == "llm_thinking_enabled": v = str(v).lower() in ("1", "true", "on", "yes") - cfg[k] = v - p = _config_path() - p.parent.mkdir(parents=True, exist_ok=True) - p.write_text(json.dumps(cfg, ensure_ascii=False, indent=2), "utf-8") + clean[k] = v + return clean + + +def _write_merge(path: Path, clean: Dict[str, Any]) -> None: + cur: Dict[str, Any] = {} + try: + cur = json.loads(path.read_text("utf-8")) + except Exception: + cur = {} + cur.update(clean) + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text(json.dumps(cur, ensure_ascii=False, indent=2), "utf-8") + + +def _save(updates: Dict[str, Any]) -> None: + clean = _coerce(updates) + # 1) persistent overrides (survive `docker compose up` recreate) + _write_merge(_persist_path(), clean) + # 2) runtime config so a bridge/worker restart picks it up immediately + _write_merge(_config_path(), clean) def _apply() -> str: - # Restart the bridge (re-reads config) and the TTS worker (picks up speed). - out = [] - for prog in ("melo-worker", "bridge"): - try: - r = subprocess.run(["supervisorctl", "restart", prog], capture_output=True, text=True, timeout=30) - out.append(f"{prog}: {(r.stdout or r.stderr).strip()}") - except Exception as e: # pragma: no cover - out.append(f"{prog}: {e}") - return " | ".join(out) + # Restart melo + bridge AFTER this response is sent. Detached (new session) + # so the bridge being killed mid-restart doesn't drop the restart itself, + # and the HTTP client still receives this response. + try: + subprocess.Popen( + ["sh", "-c", "sleep 1; supervisorctl restart melo-worker bridge"], + start_new_session=True, + ) + return "1초 후 브리지/TTS 워커가 재시작되어 반영됩니다." + except Exception as e: # pragma: no cover + return str(e) _PAGE = """ diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh index 16f83a7..a64f319 100755 --- a/docker/entrypoint.sh +++ b/docker/entrypoint.sh @@ -47,6 +47,22 @@ chmod 600 /root/.vnc/passwd # --- Render jarvis brain config from template --- envsubst < /app/docker/jarvis-config.template.json > /app/config/jarvis.json export JARVIS_CONFIG_PATH=/app/config/jarvis.json +# Merge persistent settings from the settings UI (on the /data volume) on top of +# the env-rendered config, so changes survive container recreate. +if [ -f /data/jarvis-settings.json ]; then + python3 - <<'PY' || true +import json +try: + base = json.load(open("/app/config/jarvis.json")) + ov = json.load(open("/data/jarvis-settings.json")) + if isinstance(base, dict) and isinstance(ov, dict): + base.update(ov) + json.dump(base, open("/app/config/jarvis.json", "w"), ensure_ascii=False, indent=2) + print("[entrypoint] merged persistent settings overrides") +except Exception as e: + print(f"[entrypoint] settings merge skipped: {e}") +PY +fi # --- Ensure the Piper voice exists (best effort) --- bash /app/docker/download-piper.sh || echo "[entrypoint] piper download failed; TTS may be unavailable" diff --git a/docker/run-chrome.sh b/docker/run-chrome.sh index 76f527a..bec305e 100755 --- a/docker/run-chrome.sh +++ b/docker/run-chrome.sh @@ -14,6 +14,9 @@ export DISPLAY=:1 exec google-chrome \ --no-sandbox --no-first-run --disable-dev-shm-usage \ --test-type \ + --disable-blink-features=AutomationControlled \ + --disable-features=AutomationControlled \ + --lang=ko-KR \ --disable-infobars \ --no-default-browser-check \ --disable-translate \