diff --git a/.gitea/workflows/build-windows.yml b/.gitea/workflows/build-windows.yml index b4a3df4..47d6b8a 100644 --- a/.gitea/workflows/build-windows.yml +++ b/.gitea/workflows/build-windows.yml @@ -87,9 +87,21 @@ jobs: run: | $ErrorActionPreference = 'Stop' [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 + # Native command failures do NOT trip $ErrorActionPreference, so check + # $LASTEXITCODE explicitly — otherwise a broken install ships silently. & $env:PYTHON -m pip install --upgrade pip + if ($LASTEXITCODE -ne 0) { throw "pip upgrade failed ($LASTEXITCODE)" } & $env:PYTHON -m pip install -e . + if ($LASTEXITCODE -ne 0) { throw "pip install -e . failed ($LASTEXITCODE)" } & $env:PYTHON -m pip install pyinstaller + if ($LASTEXITCODE -ne 0) { throw "pip install pyinstaller failed ($LASTEXITCODE)" } + + - name: Verify runtime imports + shell: powershell + run: | + # Fail fast if any runtime dependency is missing before we bundle. + & $env:PYTHON -c "import PySide6.QtWidgets, fastapi, uvicorn, aiohttp, PIL, pystray, qrcode, pyautogui, pyperclip, websockets, multipart; print('deps OK')" + if ($LASTEXITCODE -ne 0) { throw "dependency import smoke test failed ($LASTEXITCODE)" } - name: Build Windows executable shell: powershell diff --git a/README.md b/README.md index f25fe59..cbfcb30 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,6 @@ A cross-platform macro management application with desktop and web interfaces. C - PyAutoGUI (Keyboard automation) - Pillow (Image processing) - pystray (System tray) -- netifaces (Network detection) - qrcode (QR code generation) - aiohttp (Relay server client) diff --git a/gui/main_window.py b/gui/main_window.py index 68eb75e..0b000ba 100644 --- a/gui/main_window.py +++ b/gui/main_window.py @@ -469,19 +469,39 @@ class MainWindow(QMainWindow): # QR/copied URL lets a LAN device authenticate against the API. token = self.settings_manager.get_web_auth_token() token_qs = f"?token={token}" if token else "" + ip = self._detect_lan_ip() + if ip: + self.ip_label.setText(f"http://{ip}:{DEFAULT_PORT}{token_qs}") + return + self.ip_label.setText(f"http://localhost:{DEFAULT_PORT}{token_qs}") + + @staticmethod + def _detect_lan_ip(): + """Best-effort primary LAN IPv4, with no third-party dependency. + + Uses a UDP socket to discover which local interface would be used to + reach the internet (no packets are actually sent), then falls back to + resolving the hostname. Returns None if only loopback is available. + """ + import socket try: - import netifaces - for iface in netifaces.interfaces(): - addrs = netifaces.ifaddresses(iface) - if netifaces.AF_INET in addrs: - for addr in addrs[netifaces.AF_INET]: - ip = addr.get('addr', '') - if ip and not ip.startswith('127.'): - self.ip_label.setText(f"http://{ip}:{DEFAULT_PORT}{token_qs}") - return + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + try: + s.connect(("8.8.8.8", 80)) + ip = s.getsockname()[0] + finally: + s.close() + if ip and not ip.startswith("127."): + return ip except Exception: pass - self.ip_label.setText(f"http://localhost:{DEFAULT_PORT}{token_qs}") + try: + for ip in socket.gethostbyname_ex(socket.gethostname())[2]: + if not ip.startswith("127."): + return ip + except Exception: + pass + return None def copy_url_to_clipboard(self): """Copy the web interface URL to clipboard.""" diff --git a/macropad.spec b/macropad.spec index 2e9a08b..bf1e87e 100644 --- a/macropad.spec +++ b/macropad.spec @@ -45,7 +45,6 @@ a = Analysis( 'pyautogui', 'pyperclip', 'pystray', - 'netifaces', 'websockets', 'multipart', # Relay client (imported lazily in the GUI, so declare explicitly) diff --git a/macropad_linux.spec b/macropad_linux.spec index b524543..4f79a04 100644 --- a/macropad_linux.spec +++ b/macropad_linux.spec @@ -49,7 +49,6 @@ a = Analysis( 'pyperclip', 'pystray', 'pystray._base', - 'netifaces', 'websockets', 'multipart', # Linux system tray diff --git a/macropad_macos.spec b/macropad_macos.spec index 930e1df..b43e6f8 100644 --- a/macropad_macos.spec +++ b/macropad_macos.spec @@ -45,7 +45,6 @@ a = Analysis( 'pyautogui', 'pyperclip', 'pystray', - 'netifaces', 'websockets', 'multipart', ], diff --git a/pyproject.toml b/pyproject.toml index 620222d..ac3e678 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,8 +23,6 @@ dependencies = [ "uvicorn>=0.24.0", "websockets>=12.0", "python-multipart>=0.0.6", # For file uploads - # Network utilities - "netifaces>=0.11.0", # QR code generation "qrcode>=7.4.2", # Desktop GUI