-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathstart.py
More file actions
156 lines (121 loc) · 4.49 KB
/
Copy pathstart.py
File metadata and controls
156 lines (121 loc) · 4.49 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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
#!/usr/bin/env python3
"""
MTG Board State Tracker - Cross-platform launcher.
Starts the FastAPI/uvicorn server and opens the browser. If a system-tray
backend is available (always on Windows; on Linux/macOS only when ``pystray``
and its GUI backend are installed), a tray icon is shown with quick links and
a Quit entry. Otherwise the server runs in the foreground and is stopped with
Ctrl+C — no tray required.
"""
import os
import sys
import threading
import time
import webbrowser
import uvicorn
from backend.main import app
BASE_URL = "http://localhost:8000"
HOST = "0.0.0.0"
PORT = 8000
ICON_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), "mtg_tray_icon.png")
# ------------------------------------------------------------------
# Server
# ------------------------------------------------------------------
_server_instance: "uvicorn.Server | None" = None
_tray_icon = None # set in main() when a tray is available
def run_server():
"""Run uvicorn in the current thread (blocking)."""
global _server_instance
config = uvicorn.Config(
app,
host=HOST,
port=PORT,
log_level="info",
)
_server_instance = uvicorn.Server(config)
_server_instance.run()
def stop_server():
"""Signal the uvicorn server to shut down gracefully."""
if _server_instance is not None:
_server_instance.should_exit = True
def request_shutdown():
"""Fully quit the app: stop the server and the tray icon (if running).
Registered on ``app.state`` so the web ``POST /api/app/shutdown`` endpoint
can trigger a clean shutdown regardless of how the app was launched.
"""
stop_server()
if _tray_icon is not None:
try:
_tray_icon.stop()
except Exception:
pass
def open_browser_delayed(delay: float = 2.0):
"""Open the landing page after a short delay (server needs a moment)."""
def _open():
time.sleep(delay)
webbrowser.open(f"{BASE_URL}/")
threading.Thread(target=_open, daemon=True).start()
# ------------------------------------------------------------------
# Optional system tray
# ------------------------------------------------------------------
def _build_tray():
"""Return a configured pystray.Icon, or None if a tray isn't available.
Importing pystray on Linux pulls in a GUI backend (AppIndicator/GTK or
Xlib). If none is installed the import or Icon construction raises — in
that case we fall back to the terminal launcher.
"""
try:
import pystray
from PIL import Image
except Exception:
return None
try:
if os.path.exists(ICON_PATH):
icon_image = Image.open(ICON_PATH)
else:
icon_image = Image.new("RGB", (64, 64), (30, 30, 40))
def open_url(path):
return lambda icon, item: webbrowser.open(f"{BASE_URL}{path}")
def quit_app(icon, item):
stop_server()
icon.stop()
menu = pystray.Menu(
pystray.MenuItem("Landing Page", open_url("/")),
pystray.MenuItem("New Game", open_url("/setup")),
pystray.MenuItem("Open Board", open_url("/board")),
pystray.MenuItem("Open Command Center", open_url("/command")),
pystray.Menu.SEPARATOR,
pystray.MenuItem("Quit", quit_app),
)
return pystray.Icon(
name="mtg-tracker",
icon=icon_image,
title="MTG Board State Tracker",
menu=menu,
)
except Exception:
return None
# ------------------------------------------------------------------
# Main
# ------------------------------------------------------------------
def main():
global _tray_icon
# Let the web shutdown endpoint trigger a clean quit (server + tray).
app.state.request_shutdown = request_shutdown
icon = _build_tray()
_tray_icon = icon
if icon is not None:
# Tray mode: server runs in the background, icon.run() owns the main thread.
threading.Thread(target=run_server, daemon=True).start()
open_browser_delayed()
icon.run() # blocks until Quit
else:
# Terminal mode: open the browser, then run the server in the foreground.
print(f"MTG Board State Tracker — open {BASE_URL}/ (Ctrl+C to quit)")
open_browser_delayed()
try:
run_server() # blocks; Ctrl+C triggers uvicorn's graceful shutdown
except KeyboardInterrupt:
stop_server()
if __name__ == "__main__":
main()