"""Einstellungen und Pfade (plattformabhaengig, ohne Qt nutzbar).""" import json import os import platform import secrets import socket import sys from dataclasses import asdict, dataclass, field, fields from pathlib import Path APP = "NetClip" SYSTEM = platform.system() # Windows | Darwin | Linux def config_dir() -> Path: if SYSTEM == "Windows": return Path(os.environ.get("APPDATA", Path.home())) / APP if SYSTEM == "Darwin": return Path.home() / "Library" / "Application Support" / APP return Path(os.environ.get("XDG_CONFIG_HOME", Path.home() / ".config")) / "netclip" def cache_dir() -> Path: if SYSTEM == "Windows": return Path(os.environ.get("LOCALAPPDATA", Path.home())) / APP / "cache" if SYSTEM == "Darwin": return Path.home() / "Library" / "Caches" / APP return Path(os.environ.get("XDG_CACHE_HOME", Path.home() / ".cache")) / "netclip" def exe_dir() -> Path: if getattr(sys, "frozen", False): return Path(sys.executable).resolve().parent return Path(__file__).resolve().parent.parent @dataclass class Config: server: str = "" # z.B. https://clip.example.de token: str = "" # gemeinsamer Token aller eigenen Geraete server_key: str = "" # optional, siehe SERVER_KEY im Server verify_tls: bool = True # False = Zertifikat nicht pruefen (selbstsigniert, nur im LAN) ca_file: str = "" # alternativ: Pfad zum Server-Zertifikat (fullchain.pem) device_id: str = field(default_factory=lambda: secrets.token_hex(8)) device_name: str = field(default_factory=socket.gethostname) send_enabled: bool = True # lokale Zwischenablage hochladen receive_enabled: bool = True # fremde Clips automatisch in die Zwischenablage legen send_text: bool = True send_images: bool = True send_files: bool = True max_send_mb: int = 500 # groessere Dateien/Ordner werden nicht automatisch gesendet max_auto_receive_mb: int = 200 # groessere Clips nur auf Klick im Verlauf herunterladen keep_received_days: int = 7 # empfangene Dateien im Cache so lange aufheben notifications: bool = True autostart: bool = False @property def configured(self) -> bool: return bool(self.server and self.token) def config_file() -> Path: return config_dir() / "config.json" def load() -> Config: """Laedt config.json. Fuer die Verteilung: eine config.default.json neben der Programmdatei liefert Vorgaben (Server, Token, ...), die beim ersten Start uebernommen werden.""" data = {} default = exe_dir() / "config.default.json" for path in (default, config_file()): if path.exists(): try: values = json.loads(path.read_text(encoding="utf-8")) except (OSError, ValueError) as e: print(f"Konfiguration {path} nicht lesbar: {e}", file=sys.stderr) continue if path == default: values.pop("device_id", None) # muss pro Geraet eindeutig sein values.pop("device_name", None) data.update(values) known = {f.name for f in fields(Config)} cfg = Config(**{k: v for k, v in data.items() if k in known}) if not config_file().exists(): save(cfg) return cfg def save(cfg: Config): path = config_file() path.parent.mkdir(parents=True, exist_ok=True) tmp = path.with_suffix(".tmp") tmp.write_text(json.dumps(asdict(cfg), indent=2), encoding="utf-8") os.replace(tmp, path) if SYSTEM != "Windows": os.chmod(path, 0o600)