-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbrowser_launcher.py
More file actions
133 lines (113 loc) · 4.36 KB
/
Copy pathbrowser_launcher.py
File metadata and controls
133 lines (113 loc) · 4.36 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
"""Open SkyCast in Google Chrome after the local server is ready."""
from pathlib import Path
from threading import Thread
from time import monotonic, sleep
from urllib.error import URLError
from urllib.parse import urljoin
from urllib.request import Request, urlopen
import os
import shutil
import subprocess
import webbrowser
def _chrome_candidates():
"""Yield likely Chrome executables in priority order."""
configured = os.getenv("SKYCAST_CHROME_PATH", "").strip()
if configured:
yield Path(configured).expanduser()
for variable in ("LOCALAPPDATA", "PROGRAMFILES", "PROGRAMFILES(X86)"):
base = os.getenv(variable, "").strip()
if base:
yield Path(base) / "Google" / "Chrome" / "Application" / "chrome.exe"
def _registry_chrome_path():
if os.name != "nt":
return None
try:
import winreg
subkey = r"SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\chrome.exe"
for root in (winreg.HKEY_CURRENT_USER, winreg.HKEY_LOCAL_MACHINE):
try:
with winreg.OpenKey(root, subkey) as key:
value, _ = winreg.QueryValueEx(key, None)
candidate = Path(value)
if candidate.is_file():
return candidate
except OSError:
continue
except (ImportError, OSError):
return None
return None
def find_chrome_executable():
"""Return the installed Chrome executable, or None when unavailable."""
seen = set()
for candidate in _chrome_candidates():
normalized = os.path.normcase(os.path.abspath(candidate))
if normalized in seen:
continue
seen.add(normalized)
if candidate.is_file():
return str(candidate)
for command in ("chrome.exe", "chrome", "google-chrome", "chromium"):
executable = shutil.which(command)
if executable:
return executable
registry_path = _registry_chrome_path()
return str(registry_path) if registry_path else None
def wait_for_server(health_url, timeout_seconds=20):
"""Wait until SkyCast's health endpoint returns HTTP 200."""
deadline = monotonic() + max(0, float(timeout_seconds))
request = Request(
health_url,
headers={"User-Agent": "SkyCast-local-launcher/1.0"},
)
while monotonic() <= deadline:
try:
with urlopen(request, timeout=1) as response:
if response.status == 200:
return True
except (OSError, URLError):
pass
sleep(0.2)
return False
def launch_browser(website_url):
"""Open a new Chrome window, falling back to the registered browser."""
chrome = find_chrome_executable()
if chrome:
try:
subprocess.Popen(
[chrome, "--new-window", website_url],
stdin=subprocess.DEVNULL,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
close_fds=True,
)
return "chrome"
except OSError:
pass
try:
return "default" if webbrowser.open(website_url, new=2) else None
except webbrowser.Error:
return None
def open_when_ready(website_url, timeout_seconds=20):
"""Open SkyCast only after the server reports itself healthy."""
health_url = urljoin(f"{website_url.rstrip('/')}/", "healthz")
if not wait_for_server(health_url, timeout_seconds=timeout_seconds):
print("SkyCast became unavailable before the browser could open.", flush=True)
return None
browser = launch_browser(website_url)
if browser == "chrome":
print("Opened SkyCast in Google Chrome.", flush=True)
elif browser == "default":
print("Chrome was unavailable; opened SkyCast in the default browser.", flush=True)
else:
print(f"Open SkyCast manually at {website_url}", flush=True)
return browser
def start_browser_launcher(website_url, timeout_seconds=20):
"""Start a daemon thread that opens SkyCast when startup completes."""
thread = Thread(
target=open_when_ready,
args=(website_url, timeout_seconds),
name="skycast-browser-launcher",
daemon=True,
)
thread.start()
return thread