diff --git a/app.py b/app.py index 3254053..66932f8 100644 --- a/app.py +++ b/app.py @@ -222,6 +222,14 @@ def control_page(): return render_template('control.html', config=current_config) +@app.route('/soundboard') +def soundboard(): + if current_config is None or 'sounds' not in current_config: + return redirect(url_for('index')) + + sounds = current_config.get('sounds', []) + return render_template('soundboard.html', sounds=sounds, config=current_config) + # ── Admin ──────────────────────────────────────────────────────────────────── @app.route('/admin') @@ -513,6 +521,54 @@ def api_reconnect(): return jsonify({"success": False, "message": str(e)}), 500 +import pygame +import threading + +# pygame einmalig initialisieren (am besten global) +pygame.mixer.init() + +@app.route('/api/play_sound', methods=['POST']) +def api_play_sound(): + try: + data = request.get_json() + sound_id = data.get('sound_id') + + if not sound_id: + return jsonify({"success": False, "message": "Kein sound_id angegeben"}), 400 + + # Sound aus aktueller Config suchen + if current_config is None or 'sounds' not in current_config: + return jsonify({"success": False, "message": "Keine Sounds in der aktuellen Konfiguration"}), 400 + + sound_entry = next((s for s in current_config['sounds'] if s['id'] == sound_id), None) + if not sound_entry: + return jsonify({"success": False, "message": f"Sound mit ID '{sound_id}' nicht gefunden"}), 404 + + file_path = os.path.join('sounds', sound_entry['file']) + if not os.path.exists(file_path): + logger.error(f"Sound-Datei nicht gefunden: {file_path}") + return jsonify({"success": False, "message": "Sound-Datei nicht gefunden"}), 404 + + # Sound in separatem Thread abspielen (blockiert nicht) + def play(): + try: + pygame.mixer.music.load(file_path) + pygame.mixer.music.play() + while pygame.mixer.music.get_busy(): + time.sleep(0.1) + except Exception as e: + logger.error(f"Fehler beim Abspielen von {file_path}: {e}") + + threading.Thread(target=play, daemon=True).start() + + logger.info(f"Spiele Sound: {sound_entry['name']} ({sound_id})") + return jsonify({"success": True, "message": f"Spiele: {sound_entry['name']}"}) + + except Exception as e: + logger.exception("Play-Sound-Fehler") + return jsonify({"success": False, "message": str(e)}), 500 + + if __name__ == '__main__': app.run(host='0.0.0.0', port=5000, debug=True) diff --git a/configs/dampflok1.json b/configs/dampflok1.json index f9b55f3..47b8184 100644 --- a/configs/dampflok1.json +++ b/configs/dampflok1.json @@ -8,5 +8,25 @@ {"port": "B", "type": "motor", "name": "Unterstützung", "invert": false, "negative_only": false}, {"port": "C", "type": "light", "name": "Licht vorne", "on_value": 1.0, "off_value": 0.0, "negative_only": false}, {"port": "D", "type": "fogger", "name": "Dampf", "on_value": -1.0, "off_value": 0.0, "negative_only": true} + ], + "sounds": [ + { + "id": "pfeife1", + "file": "salamisound-3180602-dampflokpfeife-1-mal-lange.mp3", + "name": "Pfeife (lang)", + "description": "Langgezogene Dampflok-Pfeife" + }, + { + "id": "pfeife2", + "file": "salamisound-8089794-dampflokpfeife-2-mal.mp3", + "name": "Pfeife (2x lang)", + "description": "Dampfpfeife 2 mal" + }, + { + "id": "pfeife3", + "file": "salamisound-6633853-dampflokpfeife-3-mal-kurz.mp3", + "name": "Pfeife (3x kurz)", + "description": "3x Kurzer Signalton" + } ] } \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 3515814..f4fde05 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,5 @@ flask==3.0.3 # Deine mkconnect-lib – je nach Installationsweg git+https://git.avalon-skynet.work/oberon/mkconnect-lib.git -# oder falls lokal gebaut: ./path/to/mkconnect-lib \ No newline at end of file +# oder falls lokal gebaut: ./path/to/mkconnect-lib +pygame \ No newline at end of file diff --git a/static/css/custom.css b/static/css/custom.css index 7d488a3..cf64b84 100644 --- a/static/css/custom.css +++ b/static/css/custom.css @@ -39,18 +39,23 @@ pre code.hljs { .hljs-symbol { color: #d16969; } .hljs-literal { color: #d16969; } - pre code { - counter-reset: line; - } - pre code span { - counter-increment: line; - } - /* Optional: Zeilennummern (braucht CSS) */ - pre code::before { - content: counter(line) " "; - display: inline-block; - width: 3em; - text-align: right; - color: #6a9955; - user-select: none; - } \ No newline at end of file +/* Zeilennummern verbessern */ +pre { + counter-reset: line; + position: relative; +} + +pre code { + padding-left: 4.5em !important; +} + +pre code::before { + content: counter(line) " "; + counter-increment: line; + position: absolute; + left: 0.8em; + color: #858585; + text-align: right; + width: 3em; + user-select: none; +} \ No newline at end of file diff --git a/templates/base.html b/templates/base.html index bb7d311..328ec23 100644 --- a/templates/base.html +++ b/templates/base.html @@ -47,6 +47,10 @@ Admin +
Wähle einen Sound aus – wird direkt über den Raspberry Pi ausgegeben.
+ + {% if sounds %} +{{ sound.description }}
+ {% endif %} + +