diff --git a/packages/app-launcher-server/app-launcher-server.py b/packages/app-launcher-server/app-launcher-server.py index 287a136..5dd3bb4 100644 --- a/packages/app-launcher-server/app-launcher-server.py +++ b/packages/app-launcher-server/app-launcher-server.py @@ -7,6 +7,7 @@ import subprocess import sys from http.server import BaseHTTPRequestHandler, HTTPServer from urllib.parse import urlparse +import psutil # Configure logging logging.basicConfig( @@ -21,6 +22,60 @@ ALLOWED_APPS = { 'kodi': 'kodi' } +def is_app_running(app_name): + """Check if an application is already running, returns (is_running, pid)""" + command = ALLOWED_APPS.get(app_name) + if not command: + return False, None + + logger.debug(f"Looking for processes related to app '{app_name}' (command: '{command}')") + + for proc in psutil.process_iter(['name', 'cmdline', 'pid']): + try: + proc_name = proc.info['name'] + cmdline = proc.info['cmdline'] or [] + + logger.debug(f"Checking process PID {proc.info['pid']}: name='{proc_name}', cmdline={cmdline}") + + # Check multiple patterns for the application: + # 1. Process name exactly matches command + # 2. Process name contains the command (e.g., "kodi.bin" contains "kodi") + # 3. Command line starts with the command + # 4. Command line contains the wrapped version (e.g., ".kodi-wrapped") + # 5. Any command line argument ends with the command executable + + matches = False + match_reason = "" + + if proc_name == command: + matches = True + match_reason = f"exact process name match: '{proc_name}'" + elif command in proc_name: + matches = True + match_reason = f"process name contains command: '{proc_name}' contains '{command}'" + elif cmdline and cmdline[0] == command: + matches = True + match_reason = f"exact cmdline match: '{cmdline[0]}'" + elif cmdline and cmdline[0].endswith('/' + command): + matches = True + match_reason = f"cmdline path ends with command: '{cmdline[0]}'" + elif cmdline and any(f'.{command}-wrapped' in arg for arg in cmdline): + matches = True + match_reason = f"wrapped command in cmdline: {cmdline}" + elif cmdline and any(f'{command}.bin' in arg for arg in cmdline): + matches = True + match_reason = f"binary command in cmdline: {cmdline}" + + if matches: + logger.info(f"Found running {app_name} process: PID {proc.info['pid']} ({match_reason})") + return True, proc.info['pid'] + + except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess): + continue + + logger.debug(f"No running process found for {app_name}") + return False, None + class AppLauncherHandler(BaseHTTPRequestHandler): def log_message(self, format, *args): logger.info(format % args) @@ -56,6 +111,22 @@ class AppLauncherHandler(BaseHTTPRequestHandler): command = ALLOWED_APPS[app_name] + # Check if app is already running + is_running, existing_pid = is_app_running(app_name) + if is_running: + logger.info(f"Application {app_name} is already running (PID: {existing_pid}), skipping launch") + self.send_response(200) + self.send_header('Content-type', 'application/json') + self.end_headers() + response = { + 'status': 'success', + 'message': f'{app_name} is already running', + 'pid': existing_pid, + 'already_running': True + } + self.wfile.write(json.dumps(response).encode()) + return + try: # Launch the application in the background # Ensure we have the proper environment for GUI apps @@ -76,7 +147,8 @@ class AppLauncherHandler(BaseHTTPRequestHandler): response = { 'status': 'success', 'message': f'Successfully launched {app_name}', - 'pid': process.pid + 'pid': process.pid, + 'already_running': False } self.wfile.write(json.dumps(response).encode()) diff --git a/packages/app-launcher-server/default.nix b/packages/app-launcher-server/default.nix index c86209a..c8228dc 100644 --- a/packages/app-launcher-server/default.nix +++ b/packages/app-launcher-server/default.nix @@ -1,5 +1,10 @@ { pkgs }: +let + python = pkgs.python3.withPackages (ps: with ps; [ + psutil + ]); +in pkgs.writeShellScriptBin "app-launcher-server" '' - exec ${pkgs.python3}/bin/python3 ${./app-launcher-server.py} "$@" + exec ${python}/bin/python3 ${./app-launcher-server.py} "$@" '' \ No newline at end of file