Files
2026-10-16 15:55:14 +02:00

97 lines
3.6 KiB
Python

"""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)