diff --git a/update.py b/update.py index 2ca2e79..219ad94 100644 --- a/update.py +++ b/update.py @@ -66,20 +66,28 @@ def ensure_config(): return cfg, first +def _git(*a): + return subprocess.run(["git", "-C", HERE, *a], capture_output=True, text=True) + + def git_pull(): - """Return True if new commits were pulled.""" + """Force the working tree to the latest remote commit (reset --hard). This is + bulletproof vs. `git pull` on Windows, which fails on CRLF/dirty trees. The + gitignored config.ini is never touched. Returns True if the code changed.""" if not os.path.isdir(os.path.join(HERE, ".git")): - print("• not a git clone — can't auto-update. Reinstall via:\n" - " git clone https://git.armoredarmadillo.com/brockdarnold/eve-watcher.git") + print("• not a git clone — can't auto-update. Reinstall via the one-liner.") return False - r = subprocess.run(["git", "pull", "--ff-only"], cwd=HERE, - capture_output=True, text=True) - out = (r.stdout + r.stderr).strip() - print(f" git: {out.splitlines()[-1] if out else '(no output)'}") - if r.returncode != 0: - print("! git pull failed (local edits to tracked files?). config.ini is safe.") + before = _git("rev-parse", "HEAD").stdout.strip() + if _git("fetch", "-q", "origin").returncode != 0: + print("! git fetch failed (offline?). Keeping current version.") return False - return "Already up to date" not in out and "Already up-to-date" not in out + _git("reset", "--hard", "-q", "origin/main") + after = _git("rev-parse", "HEAD").stdout.strip() + if before and after and before != after: + print(f" updated: {before[:7]} -> {after[:7]}") + return True + print(" already on latest code.") + return False def deps(): @@ -88,6 +96,41 @@ def deps(): run([sys.executable, "-m", "pip", "install", "--quiet", "-r", req]) +def ensure_tesseract(cfg_path): + """OCR needs the Tesseract binary. Detect it; if missing, try a silent winget + install (best-effort — may raise a one-time UAC prompt). Writes tesseract_cmd + into config.ini when found. Non-fatal: timer/combat/chat work without it.""" + if not WIN: + return + import shutil as _sh + std = r"C:\Program Files\Tesseract-OCR\tesseract.exe" + path = _sh.which("tesseract") or (std if os.path.exists(std) else None) + if not path: + print("• Tesseract not found — installing (one-time, may prompt UAC)...") + try: + subprocess.run(["winget", "install", "--id", "UB-Mannheim.TesseractOCR", + "-e", "--silent", "--accept-package-agreements", + "--accept-source-agreements"], + capture_output=True, text=True, timeout=600) + except Exception as ex: + print(f" (winget install skipped: {ex})") + path = _sh.which("tesseract") or (std if os.path.exists(std) else None) + if not path: + print("• Tesseract still missing — live OCR % off until it's installed " + "(combat/chat/phone alerts still work).") + return + # record the path so pytesseract finds it even if not on PATH + cp = configparser.ConfigParser() + cp.read(cfg_path, encoding="utf-8-sig") + if not cp.has_section("ocr"): + cp.add_section("ocr") + if cp.get("ocr", "tesseract_cmd", fallback="").strip() != path: + cp["ocr"]["tesseract_cmd"] = path + with open(cfg_path, "w") as f: + cp.write(f) + print(f"• Tesseract ready: {path}") + + def watchers_running(): if not WIN: return 0 @@ -116,9 +159,8 @@ def start_watchers(cfg_path): cp.read(cfg_path, encoding="utf-8-sig") if cp.get("watcher", "mode", fallback="ocr") == "ocr" and \ not cp.get("ocr", "region", fallback="").strip(): - print("\n >> ONE-TIME (per PC) for hold/compress/stall alerts, run once:\n" - " python eve_orehold_watcher.py --snip\n" - " (drag a box around the Ore Hold fill bar; saved forever after.)") + print("\n >> The live hold % reader auto-calibrates: just have your in-game\n" + " Ore Hold window OPEN and it finds the readout itself (no snip).") def ensure_logon_autostart(): @@ -167,6 +209,7 @@ def main(): changed = git_pull() if changed or first: deps() + ensure_tesseract(cfg) if changed or first or watchers_running() == 0: start_watchers(cfg) else: