Audiofy – Musikwünsche für Partys

Server (Website, Admin-Bereich, Player-API) und Player (raspotify +
Controller) mit Install-Skripten für Debian-Systeme.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-17 19:16:39 +01:00
co-authored by Claude Opus 5.5
commit 3610de7bae
45 changed files with 5666 additions and 0 deletions
+71
View File
@@ -0,0 +1,71 @@
#!/usr/bin/env python3
"""Audiofy-Web – Verwaltung auf der Kommandozeile.
geheimnisse SESSION_SECRET und PLAYER_API_TOKEN erzeugen (nur falls noch nicht vorhanden)
passwort Admin-Passwort setzen (verdeckte Eingabe, gespeichert wird nur der argon2-Hash)
turnstile Cloudflare-Turnstile-Schlüssel setzen (Secret-Key verdeckt)
Auf dem Server als Dienst-Benutzer ausführen, damit die .env ihm gehört:
sudo -u audiofy /opt/audiofy/.venv/bin/python /opt/audiofy/app/verwaltung.py passwort
"""
import argparse
import getpass
import os
import secrets
import sys
from pathlib import Path
from argon2 import PasswordHasher
sys.path.insert(0, str(Path(__file__).resolve().parent))
from app.config import env_lesen, env_wert_setzen # noqa: E402
MIN_LAENGE = 12
def geheimnisse(env: Path) -> None:
werte = env_lesen(env) if env.is_file() else {}
if werte.get("PI_API_TOKEN") and not werte.get("PLAYER_API_TOKEN"):
# alten Namen übernehmen, damit ein bereits eingerichteter Player weiterläuft
env_wert_setzen(env, "PLAYER_API_TOKEN", werte["PI_API_TOKEN"])
werte["PLAYER_API_TOKEN"] = werte["PI_API_TOKEN"]
print("PLAYER_API_TOKEN: aus PI_API_TOKEN übernommen")
for key in ("SESSION_SECRET", "PLAYER_API_TOKEN"):
if werte.get(key):
print(f"{key}: schon vorhanden – unverändert")
else:
env_wert_setzen(env, key, secrets.token_urlsafe(48))
print(f"{key}: neu erzeugt")
def passwort(env: Path) -> None:
erstes = getpass.getpass("Neues Admin-Passwort: ")
if len(erstes) < MIN_LAENGE:
sys.exit(f"Zu kurz – mindestens {MIN_LAENGE} Zeichen.")
if getpass.getpass("Wiederholen: ") != erstes:
sys.exit("Die Eingaben stimmen nicht überein.")
env_wert_setzen(env, "ADMIN_PASSWORD_HASH", PasswordHasher().hash(erstes))
print(f"Passwort-Hash in {env} gespeichert. Bitte den Dienst neu starten (audiofy-web.service).")
def turnstile(env: Path) -> None:
site_key = input("Turnstile Site-Key (öffentlich): ").strip()
secret = getpass.getpass("Turnstile Secret-Key (verdeckt): ").strip()
if not site_key or not secret:
sys.exit("Beide Schlüssel sind nötig.")
env_wert_setzen(env, "TURNSTILE_SITE_KEY", site_key)
env_wert_setzen(env, "TURNSTILE_SECRET_KEY", secret)
print(f"Turnstile-Schlüssel in {env} gespeichert. Bitte den Dienst neu starten (audiofy-web.service).")
def main() -> None:
parser = argparse.ArgumentParser(description="Audiofy-Web – Verwaltung")
parser.add_argument("aktion", choices=("geheimnisse", "passwort", "turnstile"))
parser.add_argument("--env", type=Path, default=Path(os.environ.get("AUDIOFY_ENV", "/opt/audiofy/.env")))
args = parser.parse_args()
{"geheimnisse": geheimnisse, "passwort": passwort, "turnstile": turnstile}[args.aktion](args.env)
if __name__ == "__main__":
main()