""" modules/app_monitor.py — FRIDAY Tracks What's Playing Monitors active applications and detects: - What app is in focus - If video/audio is playing - Browser video detection - Media state detection """ import time import os import json import subprocess from config import DATA_DIR MONITOR_FILE = os.path.join(DATA_DIR, "app_monitor.json") def _load() -> dict: if not os.path.exists(MONITOR_FILE): return {"active": "", "title": "", "last_change": 0, "media_playing": False} try: with open(MONITOR_FILE, "r") as f: return json.load(f) except Exception: return {"active": "", "title": "", "last_change": 0, "media_playing": False} def _save(data: dict): os.makedirs(DATA_DIR, exist_ok=True) try: with open(MONITOR_FILE, "w") as f: json.dump(data, f, indent=2) except Exception: pass # ── Get Active Window ─────────────────────────────────────────────── def get_active_window() -> dict: """Get currently active window info.""" try: import psutil # Get foreground window foreground = psutil.Process()._proc.info.get('name') # Use Windows API for window title import ctypes from ctypes import wintypes user32 = ctypes.windll.user32 hwnd = user32.GetForegroundWindow() length = user32.GetWindowTextLengthW(hwnd) if length > 0: buffer = ctypes.create_unicode_buffer(length + 1) user32.GetWindowTextW(hwnd, buffer, length + 1) title = buffer.value else: title = "" return {"hwnd": hwnd, "title": title[:100], "time": time.time()} except Exception: return {"title": "", "time": time.time()} # ── Detect Video Playing ───────────────────────────────────── def is_video_playing() -> bool: """Detect if video/media is playing.""" data = _load() title = data.get("title", "").lower() active = data.get("active", "").lower() # Browser video detection video_patterns = ["youtube", "netflix", "prime", "disney", "hulu", "spotify", "twitch"] for pattern in video_patterns: if pattern in title or pattern in active: return True return is_media_playing() def get_current_app() -> str: """Get current active application.""" return _load().get("active", "") def get_current_title() -> str: """Get current window title.""" return _load().get("title", "") def get_current_app() -> str: """Get current active application.""" return _load().get("active", "") def get_current_title() -> str: """Get current window title.""" return _load().get("title", "") # ── Background Monitor ───────────────────────────────────────── def check_active(): """Check and update active window.""" try: window = get_active_window() title = window.get("title", "") # Determine app from title app = "unknown" if "youtube" in title.lower(): app = "YouTube" elif "chrome" in title.lower(): app = "Chrome" elif "edge" in title.lower(): app = "Edge" elif "spotify" in title.lower(): app = "Spotify" elif "discord" in title.lower(): app = "Discord" elif "code" in title.lower(): app = "VS Code" elif "game" in title.lower(): app = "Game" data = _load() data["active"] = app data["title"] = title[:100] data["last_change"] = time.time() data["media_playing"] = title.lower().find("playing") > 0 or title.lower().find("youtube") > 0 _save(data) return data except Exception: return _load() # ── Media State ─────────────────────────────────────────────── def set_media_state(playing: bool): """Set media playing state.""" data = _load() data["media_playing"] = playing data["media_time"] = time.time() _save(data) def is_media_playing() -> bool: """Check if media is playing.""" return _load().get("media_playing", False) # ── Background Loop ────────────────────────────────────────── def start_monitor(): """Start app monitor in background.""" import threading import time def _loop(): while True: try: check_active() except Exception: pass time.sleep(2) # Check every 2 seconds t = threading.Thread(target=_loop, daemon=True) t.start() # ── Brain Integration ───────────────────────────────────────── def get_app_context() -> str: """Get app context for brain.""" data = _load() parts = [] active = data.get("active", "") title = data.get("title", "") if active: parts.append(f"On: {active}") if is_media_playing(): parts.append("Media playing") if parts: return "[APP] " + " | ".join(parts) return ""