Add public kiosk page with QR/pricing cycle and configurable slide timing

This commit is contained in:
Troll (Hermes Agent) 2026-08-04 17:41:33 +00:00
parent a25bf3a9c9
commit c726525c8e
5 changed files with 351 additions and 1 deletions

92
app.py
View file

@ -282,6 +282,32 @@ def get_refresh_seconds():
return val if val in (10, 20, 30) else 10
def get_kiosk_cycle_seconds():
"""Return the kiosk slide cycle interval in seconds. 0 = static price list; 5+ cycles QR and pricing."""
cfg = load_booth_settings()
try:
val = int(cfg.get('kiosk_cycle_seconds', 10))
except (ValueError, TypeError):
val = 10
if val == 0:
return 0
return max(5, val)
def get_kiosk_mode():
"""Return 'cycle', 'qr', or 'prices' based on kiosk_cycle_seconds setting."""
cfg = load_booth_settings()
try:
val = int(cfg.get('kiosk_cycle_seconds', 10))
except (ValueError, TypeError):
val = 10
if val == -1:
return 'qr'
if val == 0:
return 'prices'
return 'cycle'
def get_hermes_api_key():
"""
Return the effective Hermes API key.
@ -847,6 +873,49 @@ def admin_pricing():
return render_template('admin/pricing.html', fixed=fixed, customs=customs)
@app.route('/kiosk')
def kiosk():
"""
Public kiosk display page for the booth.
Shows open/closed banner, pricing, and optionally cycles with a QR code.
Auto-refreshes so pricing updates are picked up quickly.
"""
cfg = load_booth_settings()
pricing = cfg.get('pricing', {})
fixed_keys = {
'one_song': 'One Song',
'both_songs': 'Both Songs',
'wav_per_song': 'WAV files / song',
'stems_per_song': 'STEM files / song'
}
price_items = []
for key, label in fixed_keys.items():
val = pricing.get(key, '').strip()
if val:
price_items.append({'label': label, 'price': val})
for i in range(1, 6):
entry = pricing.get(f'custom_{i}')
if isinstance(entry, dict):
name = entry.get('name', '').strip()
price = entry.get('price', '').strip()
if name and price:
price_items.append({'label': name, 'price': price})
cycle_seconds = get_kiosk_cycle_seconds()
mode = get_kiosk_mode()
booth_open = get_booth_open()
return render_template(
'kiosk.html',
booth_open=booth_open,
price_items=price_items,
cycle_seconds=cycle_seconds,
mode=mode,
refresh_seconds=30
)
@app.route('/admin/request/<int:rid>', methods=['GET', 'POST'])
def admin_request(rid):
"""
@ -1248,6 +1317,28 @@ def admin_settings():
flash(f'Dashboard auto-refresh set to {val} seconds.', 'success')
return redirect(url_for('admin_settings'))
elif action == 'save_kiosk_cycle':
# Update kiosk slide cycle interval. Special values:
# -1 = show QR only, 0 = show pricing only, 5+ = cycle every N seconds.
raw = request.form.get('kiosk_cycle_seconds', '10').strip()
try:
val = int(raw)
except ValueError:
val = 10
if val not in (-1, 0) and val < 5:
val = 5
cfg = load_booth_settings()
cfg['kiosk_cycle_seconds'] = val
save_booth_settings(cfg)
if val == -1:
label = 'QR code only'
elif val == 0:
label = 'pricing only'
else:
label = f'cycle every {val} seconds'
flash(f'Kiosk mode set to {label}.', 'success')
return redirect(url_for('admin_settings'))
elif action == 'save_booth_open':
# Toggle whether the public request form is accepting submissions.
cfg = load_booth_settings()
@ -1327,6 +1418,7 @@ def admin_settings():
upload_path=str(upload_root),
current_max_revisions=current_max_revisions,
current_refresh_seconds=current_refresh_seconds,
current_kiosk_cycle_seconds=get_kiosk_cycle_seconds(),
booth_open=booth_open,
email_form=email_form,
metadata={