""" Fortune Cookie v1.1 - Python Backend Module Framework 1.8 Standard Audio-Steuerung in QML (keine Qt-Python-Bindings benoetigt) Last-State Speicherung mit Datei-basiertem Speicher Einstellungen: Lautstaerke, Spruchlisten """ import os import random import json import platform # ============================================================================ # APP-METADATEN # ============================================================================ APP_NAME = "fortunecookie" APP_VERSION = "1.1.0" MAINTAINER = "darklithium " # ============================================================================ # PLATTFORM-ERKENNUNG (Framework 1.7 Standard) # ============================================================================ def get_platform(): """Gibt die aktuelle Plattform zurueck (arm64/amd64).""" machine = platform.machine().lower() if "arm" in machine or "aarch" in machine: return "arm64" return "amd64" # ============================================================================ # FORTUNE-LISTEN VERWALTUNG # ============================================================================ # Verfuegbare Spruchlisten AVAILABLE_FORTUNE_LISTS = [ "classic", # Standard Glückskeks-Sprueche "farmer_wisdom", # Bauernweisheiten "unfortune", # Gothic/UNfortune-Sprueche ] # Aktuelle Spruchliste (Standard: classic) _current_fortune_list = "classic" _current_fortune = "" _fortunes = {} _initialized = False def _init(): """Initialisiert das Modul (wird beim ersten Aufruf ausgefuehrt).""" global _fortunes, _initialized, _current_fortune_list if _initialized: return True # Lade alle Spruchlisten _load_all_fortune_lists() # Lade aktuelle Liste aus Einstellungen _current_fortune_list = load_setting("fortune_list", default_value="classic") if _current_fortune_list not in _fortunes: _current_fortune_list = "classic" _initialized = True return True def _load_all_fortune_lists(): """Laedt alle verfuegbaren Spruchlisten.""" global _fortunes if _fortunes: return try: base_path = os.path.join(os.path.dirname(__file__), "..", "assets", "fortunes") for list_name in AVAILABLE_FORTUNE_LISTS: try: json_path = os.path.join(base_path, f"{list_name}.json") if os.path.exists(json_path): with open(json_path, "r", encoding="utf-8") as f: _fortunes[list_name] = json.load(f) else: # Fallback: Leere Liste _fortunes[list_name] = [] except Exception as e: print(f"WARN: Spruchliste {list_name} nicht geladen: {e}") _fortunes[list_name] = [] except Exception as e: print(f"ERROR: Spruchlisten nicht geladen: {e}") # Fallback: Standard-Sprueche _fortunes = { "classic": [ "Ein guter Tag beginnt mit einem Laecheln.", "Das Glueck liegt in den kleinen Dingen.", "Geduld ist eine Tugend.", ], "farmer_wisdom": [], "unfortune": [] } def _get_random_fortune(): """Gibt einen zufaelligen Spruch aus der aktuellen Liste zurueck.""" global _current_fortune_list, _fortunes _init() if _current_fortune_list not in _fortunes or not _fortunes[_current_fortune_list]: # Fallback auf classic _current_fortune_list = "classic" if not _fortunes.get("classic"): return "Keine Sprueche verfguebar." return random.choice(_fortunes[_current_fortune_list]) def get_initial_fortune(): """Gibt einen zufaelligen Spruch fuer den Start zurueck.""" global _current_fortune _init() _current_fortune = _get_random_fortune() return _current_fortune def open_fortune(): """Oeffnet den Fortune Cookie (neuer Spruch aus aktueller Liste).""" global _current_fortune _init() _current_fortune = _get_random_fortune() return True def get_current_fortune(): """Gibt den aktuellen Spruch zurueck.""" _init() global _current_fortune return _current_fortune def get_new_fortune(): """Gibt einen neuen Spruch zurueck (Cookie schliesst sich).""" global _current_fortune _init() _current_fortune = _get_random_fortune() return _current_fortune # ============================================================================ # EINSTELLUNGEN (Settings) # ============================================================================ def _get_config_dir(): """Gibt das Konfigurationsverzeichnis zurück (Click-App kompatibel).""" try: home = os.path.expanduser("~") app_name = APP_NAME + ".darklithium" cache_dir = os.path.join(home, ".cache", app_name) os.makedirs(cache_dir, exist_ok=True) return cache_dir except Exception: return os.path.join("/tmp", APP_NAME + "_config") def _get_setting_file(setting_name): """Gibt den Pfad zu einer Einstellungsdatei zurück.""" return os.path.join(_get_config_dir(), f"{setting_name}.txt") def load_setting(setting_name, default_value=None): """Laedt eine Einstellung aus einer Datei. Args: setting_name (str): Name der Einstellung default_value: Standardwert, wenn Datei nicht existiert Rückgabe: Wert der Einstellung (Typ hängt von default_value ab) """ try: setting_file = _get_setting_file(setting_name) if os.path.exists(setting_file): with open(setting_file, "r") as f: content = f.read().strip() # Try to convert to appropriate type if default_value is not None: if isinstance(default_value, bool): return content.lower() == "true" elif isinstance(default_value, int): return int(content) elif isinstance(default_value, float): return float(content) return content except Exception as e: print(f"WARN: Einstellung nicht geladen ({setting_name}): {e}") return default_value def save_setting(setting_name, value): """Speichert eine Einstellung in einer Datei. Args: setting_name (str): Name der Einstellung value: Zu speichernder Wert (wird zu String konvertiert) Rückgabe: bool: True bei Erfolg """ try: config_dir = _get_config_dir() os.makedirs(config_dir, exist_ok=True) setting_file = _get_setting_file(setting_name) with open(setting_file, "w") as f: f.write(str(value)) return True except Exception as e: print(f"WARN: Einstellung nicht gespeichert ({setting_name}): {e}") return False # ============================================================================ # LAUTSTÄRKE-EINSTELLUNGEN # ============================================================================ def get_music_volume(): """Gibt die Musik-Lautstärke zurück (0.0 - 1.0).""" return float(load_setting("music_volume", default_value=0.5)) def set_music_volume(volume): """Setzt die Musik-Lautstärke (0.0 - 1.0).""" # Clamp value between 0.0 and 1.0 volume = max(0.0, min(1.0, float(volume))) return save_setting("music_volume", volume) def get_crack_volume(): """Gibt die Knack-Lautstärke zurück (0.0 - 1.0).""" return float(load_setting("crack_volume", default_value=1.0)) def set_crack_volume(volume): """Setzt die Knack-Lautstärke (0.0 - 1.0).""" volume = max(0.0, min(1.0, float(volume))) return save_setting("crack_volume", volume) # ============================================================================ # SPRUCHLISTEN-EINSTELLUNGEN # ============================================================================ def get_fortune_lists(): """Gibt die Liste der verfuegbaren Spruchlisten zurück.""" _init() return AVAILABLE_FORTUNE_LISTS def get_current_fortune_list(): """Gibt den Namen der aktuellen Spruchliste zurück.""" _init() return load_setting("fortune_list", default_value="classic") def set_fortune_list(list_name): """Setzt die aktuelle Spruchliste. Args: list_name (str): Name der Spruchliste (classic, farmer_wisdom, unfortune) Rückgabe: bool: True bei Erfolg """ if list_name not in AVAILABLE_FORTUNE_LISTS: return False return save_setting("fortune_list", list_name) # ============================================================================ # MUSIK EIN/AUS (Last-State) # ============================================================================ def get_music_enabled(): """Gibt zurück, ob Musik aktiviert ist (Last-State).""" return load_setting("music_enabled", default_value=True) def set_music_enabled(enabled): """Setzt den Musik-Status und speichert ihn persistent.""" return save_setting("music_enabled", bool(enabled)) # ============================================================================ # PLATTFORM-SPEZIFISCHE PFADERMITTLUNG # ============================================================================ def get_asset_path(filename): """Gibt den Pfad zu einer Asset-Datei zurueck (funktioniert in Click & Desktop).""" possible_paths = [ os.path.join(os.path.dirname(__file__), "..", "assets", filename), os.path.join("assets", filename), ] for path in possible_paths: if os.path.exists(path): return path return os.path.join("assets", filename)