diff --git a/backend/main.py b/backend/main.py index b4ce74f..55625c9 100644 --- a/backend/main.py +++ b/backend/main.py @@ -774,6 +774,39 @@ async def api_supprimer_animation(nom: str): return JSONResponse({"erreur": "Fichier introuvable"}, status_code=404) +# --- API Photostation --- + +FLAG_PHOTOSTATION = Path("/tmp/photostation_running") +PHOTOSTATION_DIR = Path("/opt/photostation") + + +@app.get("/api/photostation/disponible") +async def api_photostation_disponible(): + disponible = (PHOTOSTATION_DIR / "src" / "main.py").exists() + return {"disponible": disponible} + + +@app.post("/api/photostation/lancer") +async def api_photostation_lancer(): + if not (PHOTOSTATION_DIR / "src" / "main.py").exists(): + return JSONResponse({"erreur": "Photostation non installée"}, status_code=404) + asyncio.create_task(_lancer_photostation()) + return {"succes": True} + + +async def _lancer_photostation(): + FLAG_PHOTOSTATION.touch() + await asyncio.sleep(1) + # Fermer Chromium (la boucle kiosk attend le flag avant de le relancer) + await asyncio.create_subprocess_exec("pkill", "chromium") + await asyncio.sleep(2) + env = {**__import__("os").environ, "DISPLAY": ":0"} + cmd = f"source {PHOTOSTATION_DIR}/venv/bin/activate && python {PHOTOSTATION_DIR}/src/main.py --kiosk" + proc = await asyncio.create_subprocess_exec("bash", "-c", cmd, env=env) + await proc.wait() + FLAG_PHOTOSTATION.unlink(missing_ok=True) + + # --- API Systeme --- @app.post("/api/systeme/redemarrer") diff --git a/frontend/css/style.css b/frontend/css/style.css index bd6b011..4e462ea 100644 --- a/frontend/css/style.css +++ b/frontend/css/style.css @@ -135,6 +135,26 @@ html, body { color: rgba(255,255,255,0.4); } +.btn-photostation { + position: absolute; + bottom: 12px; + left: 12px; + width: 56px; + height: 56px; + display: flex; + align-items: center; + justify-content: center; + font-size: 1.5rem; + color: rgba(255,255,255,0.15); + cursor: pointer; + border-radius: 50%; + transition: color 0.3s; +} + +.btn-photostation:hover, .btn-photostation:active { + color: rgba(255,255,255,0.4); +} + /* Popup mot de passe */ .popup-overlay { position: fixed; diff --git a/frontend/index.html b/frontend/index.html index 977515a..c89fbd1 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -42,8 +42,34 @@
⚙
+ +
🖼
+ + +

Choisissez votre mode

diff --git a/frontend/js/app.js b/frontend/js/app.js index e606052..e934dae 100644 --- a/frontend/js/app.js +++ b/frontend/js/app.js @@ -15,6 +15,7 @@ async function init() { setupModes(); setupOnglets(); chargerBoothInfo(); + verifierPhotostationDisponible(); } function appliquerConfig() { @@ -137,6 +138,8 @@ function setupEcranAccueil() { // --- Admin : icone + mot de passe --- const MDP_ADMIN = '1234'; +const PIN_PHOTOSTATION = '1234'; +let _pinPhotostationSaisi = ''; function ouvrirAdmin() { const popup = document.getElementById('popup-mdp'); @@ -161,6 +164,59 @@ function paveEffacer() { input.value = input.value.slice(0, -1); } +// --- Photostation --- + +async function verifierPhotostationDisponible() { + const data = await apiGet('/api/photostation/disponible').catch(() => null); + if (data?.disponible) { + document.getElementById('btn-photostation').classList.remove('cache'); + } +} + +function ouvrirPhotostation() { + _pinPhotostationSaisi = ''; + _majAffichagePin(); + document.getElementById('pin-photostation-erreur').classList.add('cache'); + document.getElementById('popup-pin-photostation').classList.remove('cache'); +} + +function fermerPopupPhotostation() { + document.getElementById('popup-pin-photostation').classList.add('cache'); + _pinPhotostationSaisi = ''; +} + +function pinPhotostationTouche(c) { + if (_pinPhotostationSaisi.length >= 4) return; + _pinPhotostationSaisi += c; + _majAffichagePin(); + if (_pinPhotostationSaisi.length === 4) { + setTimeout(_validerPinPhotostation, 150); + } +} + +function pinPhotostationEffacer() { + _pinPhotostationSaisi = _pinPhotostationSaisi.slice(0, -1); + _majAffichagePin(); +} + +function _majAffichagePin() { + const n = _pinPhotostationSaisi.length; + const el = document.getElementById('pin-photostation-affichage'); + el.textContent = '●'.repeat(n) + '○'.repeat(4 - n); +} + +async function _validerPinPhotostation() { + if (_pinPhotostationSaisi !== PIN_PHOTOSTATION) { + document.getElementById('pin-photostation-erreur').classList.remove('cache'); + _pinPhotostationSaisi = ''; + _majAffichagePin(); + return; + } + fermerPopupPhotostation(); + afficherStatut('Lancement de la station d\'impression...', 'succes'); + await apiPost('/api/photostation/lancer', {}); +} + function validerMdpAdmin() { const input = document.getElementById('input-mdp-admin'); if (input.value === MDP_ADMIN) { diff --git a/scripts/install-photostation.sh b/scripts/install-photostation.sh new file mode 100644 index 0000000..0f6280a --- /dev/null +++ b/scripts/install-photostation.sh @@ -0,0 +1,37 @@ +#!/bin/bash +# Installe la photostation sur la Surface pour intégration photobooth +set -e + +DEST="/opt/photostation" +REPO="https://git.copydev.fr/jules/photostation.git" + +echo "=== Installation Photostation ===" + +# Dépendances système +sudo apt-get install -y \ + python3-pyqt6 python3-pyqt6.qtsvg \ + python3-pillow python3-flask \ + python3-qrcode python3-cups \ + libgl1 libglib2.0-0 2>&1 | grep -E 'Install|Upgrade|already' + +# Cloner ou mettre à jour +if [ -d "$DEST/.git" ]; then + echo "Mise à jour du repo..." + git -C "$DEST" pull +else + echo "Clonage du repo..." + sudo git clone "$REPO" "$DEST" + sudo chown -R jules:jules "$DEST" +fi + +# Venv avec accès aux paquets système (PyQt6 est en paquet système) +if [ ! -d "$DEST/venv" ]; then + python3 -m venv --system-site-packages "$DEST/venv" +fi + +# Dépendances Python supplémentaires +"$DEST/venv/bin/pip" install --quiet qrcode[pil] 2>/dev/null || true + +echo "" +echo "=== Installation terminée ===" +echo "Test : DISPLAY=:0 bash -c 'source $DEST/venv/bin/activate && python $DEST/src/main.py'" diff --git a/scripts/kiosk-session.sh b/scripts/kiosk-session.sh index 3de7372..cfdd69b 100644 --- a/scripts/kiosk-session.sh +++ b/scripts/kiosk-session.sh @@ -22,7 +22,9 @@ for i in $(seq 1 15); do done # Chromium en boucle : redémarre automatiquement s'il plante ou se ferme +# Attend si la photostation est en cours d'exécution while true; do + while [ -f /tmp/photostation_running ]; do sleep 1; done chromium \ --kiosk \ --noerrdialogs \