Intégration station d'impression photo (photostation)
- Icône discrète coin bas-gauche, visible seulement si photostation installée - Popup PIN (4 chiffres) avant lancement - Backend : /api/photostation/disponible + /api/photostation/lancer - kiosk-session.sh : Chromium attend le flag /tmp/photostation_running avant redémarrage - Script install-photostation.sh pour déploiement sur la Surface Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -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")
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -42,8 +42,34 @@
|
||||
</div>
|
||||
<!-- Icone admin coin bas-droit -->
|
||||
<div id="btn-admin" class="btn-admin" onclick="ouvrirAdmin()">⚙</div>
|
||||
<!-- Icone photostation coin bas-gauche (visible seulement si installée) -->
|
||||
<div id="btn-photostation" class="btn-photostation cache" onclick="ouvrirPhotostation()">🖼</div>
|
||||
</section>
|
||||
|
||||
<!-- Popup PIN photostation -->
|
||||
<div id="popup-pin-photostation" class="popup-mdp cache">
|
||||
<div class="popup-mdp-contenu">
|
||||
<h3>Station d'impression</h3>
|
||||
<p class="aide">Entrez le code PIN</p>
|
||||
<div id="pin-photostation-affichage" class="mdp-affichage">●●●●</div>
|
||||
<div id="pin-photostation-erreur" class="mdp-erreur cache">Code incorrect</div>
|
||||
<div class="pave-numerique">
|
||||
<button onclick="pinPhotostationTouche('1')">1</button>
|
||||
<button onclick="pinPhotostationTouche('2')">2</button>
|
||||
<button onclick="pinPhotostationTouche('3')">3</button>
|
||||
<button onclick="pinPhotostationTouche('4')">4</button>
|
||||
<button onclick="pinPhotostationTouche('5')">5</button>
|
||||
<button onclick="pinPhotostationTouche('6')">6</button>
|
||||
<button onclick="pinPhotostationTouche('7')">7</button>
|
||||
<button onclick="pinPhotostationTouche('8')">8</button>
|
||||
<button onclick="pinPhotostationTouche('9')">9</button>
|
||||
<button onclick="pinPhotostationEffacer()">←</button>
|
||||
<button onclick="pinPhotostationTouche('0')">0</button>
|
||||
<button onclick="fermerPopupPhotostation()">✕</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Choix du mode -->
|
||||
<section id="ecran-mode" class="ecran">
|
||||
<h2>Choisissez votre mode</h2>
|
||||
|
||||
@@ -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) {
|
||||
|
||||
37
scripts/install-photostation.sh
Normal file
37
scripts/install-photostation.sh
Normal file
@@ -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'"
|
||||
@@ -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 \
|
||||
|
||||
Reference in New Issue
Block a user