c4e7d4bd55
- Settings.qml mit Lautstärke-Slidern für Musik und Knack-Geräusch - Spruchlisten-Auswahl (classic, farmer_wisdom, unfortune) - StackLayout für Navigation zwischen Hauptseite und Einstellungen - Python: load_setting/save_setting für generische Einstellungen - Python: Volume-Einstellungen und Spruchlisten-Verwaltung - ASSET_REQUIREMENTS.md für Grafik-Spezifikationen - JSON-Dateien: classic.json, farmer_wisdom.json, unfortune.json Generated by Mistral Vibe. Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
313 lines
9.5 KiB
Python
313 lines
9.5 KiB
Python
"""
|
|
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 <dev@darklithium.de>"
|
|
|
|
|
|
# ============================================================================
|
|
# 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)
|