97 lines
3.6 KiB
Python
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)
|