"""Persistent user configuration: the configured stream.jw.org link and the autostart-at-login toggle.""" import json import os import re import shutil XDG_CONFIG_HOME = os.environ.get("XDG_CONFIG_HOME", os.path.expanduser("~/.config")) CONFIG_DIR = os.path.join(XDG_CONFIG_HOME, "assembly-player") CONFIG_PATH = os.path.join(CONFIG_DIR, "config.json") AUTOSTART_DIR = os.path.join(XDG_CONFIG_HOME, "autostart") AUTOSTART_PATH = os.path.join(AUTOSTART_DIR, "assembly-player.desktop") DEFAULT_URL = "https://stream.jw.org/9021-8224-3277-8078" _URL_RE = re.compile(r"^https://stream\.jw\.org/[A-Za-z0-9-]+/?$") def is_valid_stream_url(url): return bool(_URL_RE.match(url.strip())) def assembly_code(url): """Extract the trailing code (e.g. 9021-8224-3277-8078) used as a state key.""" return url.strip().rstrip("/").rsplit("/", 1)[-1] def load(): if not os.path.exists(CONFIG_PATH): return {"url": DEFAULT_URL, "autostart": False, "start_minimized": False, "start_fullscreen": False} with open(CONFIG_PATH, "r", encoding="utf-8") as f: data = json.load(f) data.setdefault("url", DEFAULT_URL) data.setdefault("autostart", False) data.setdefault("start_minimized", False) data.setdefault("start_fullscreen", False) return data def save(config): os.makedirs(CONFIG_DIR, exist_ok=True) tmp_path = CONFIG_PATH + ".tmp" with open(tmp_path, "w", encoding="utf-8") as f: json.dump(config, f, indent=2, ensure_ascii=False) os.replace(tmp_path, CONFIG_PATH) def _find_installed_desktop_file(): """Locate the installed launcher .desktop file so autostart uses the exact same Exec= / Icon= the user installed (user prefix or system prefix).""" candidates = [ os.path.expanduser("~/.local/share/applications/assembly-player.desktop"), "/usr/local/share/applications/assembly-player.desktop", "/usr/share/applications/assembly-player.desktop", ] for path in candidates: if os.path.exists(path): return path return None def set_autostart(enabled): if not enabled: if os.path.exists(AUTOSTART_PATH): os.remove(AUTOSTART_PATH) return os.makedirs(AUTOSTART_DIR, exist_ok=True) source = _find_installed_desktop_file() if source is None: raise RuntimeError( "Aucun fichier .desktop installé n'a été trouvé. " "Lancez d'abord 'make install' avant d'activer le démarrage automatique." ) with open(source, "r", encoding="utf-8") as f: content = f.read() # Start hidden in the tray and silently resume playback, instead of # popping a window in the user's face right after login. content = content.replace("Exec=assembly-player", "Exec=assembly-player --minimized") content += "X-GNOME-Autostart-enabled=true\n" with open(AUTOSTART_PATH, "w", encoding="utf-8") as f: f.write(content) def is_autostart_enabled(): return os.path.exists(AUTOSTART_PATH)