This repository has been archived on 2026-09-04. You can view files and clone it, but you cannot make any changes to it's state, such as pushing and creating new issues, pull requests or comments.
theme-song-booth/REVIEW.md
Troll (Hermes Agent) a56f6e498b feat: add ntfy push notifications for new requests
- Add ntfy server/topic settings in /admin/settings next to Hermes API key
- POST a push notification to the configured topic on every new customer request
- Add a Send Test ntfy button to verify configuration
- Add requests dependency for ntfy HTTP calls
- Bump version to 0.5.6
2026-08-07 17:14:02 +00:00

8.5 KiB

REVIEW.md — Theme Song Booth

Quick reference for future work on this project.

One-sentence summary

Flask app that lets convention attendees request custom AI-generated theme songs, lets an operator manage the queue, and emails MP3s after payment.

Stack

  • Python 3.12 + Flask
  • SQLite (file-based, request-scoped connection via g)
  • Gunicorn in Docker
  • Portainer stack deployed from Gitea repo
  • SMTP (SSL port 465) for customer emails
  • Square Terminal/Reader for manual payment
  • mutagen for MP3 metadata tagging
  • flask-limiter for public form rate limiting
  • cryptography to encrypt the stored SMTP password

Repository

  • Gitea: https://gitlab.hallsworth.ca/yrtria/theme-song-booth (public)
  • Deployed at: https://booth.dionysismedia.ca

Key files and what they hold

File Notes
app.py All routes, helpers, email function, status labels, runtime settings, MP3 tagging, rate limiting, DB health, kiosk route, pricing route, sales report, ZIP backup, and the /api/prompt/<id> Hermes callback endpoint.
config.py Env vars. ADMIN_PASSWORD is plain text. HERMES_API_KEY can be overridden at runtime.
models.py SQLite schema + CRUD. player_token is a secret URL-safe token. revision_history table tracks each customer revision.
init_db.py Run once after deploy: python init_db.py.
templates/admin/request.html Biggest template. Customer info editing, revision history, operator notes, Suno prompt fields, upload/delivery, cancel request button.
templates/admin/dashboard.html Queue table + filters + auto-refresh + topbar links to Kiosk, Pricing, Sales, Settings.
templates/admin/settings.html SMTP config, MP3 metadata defaults, DB backup/restore/health, reset, kiosk mode, Hermes API key display.
templates/admin/pricing.html Fixed and custom pricing configuration.
templates/admin/sales.html Sales report of delivered requests.
templates/faq.html Customer FAQ page.
templates/status.html Customer order status lookup.
templates/closed.html Message shown on /request when the booth is marked closed.
templates/kiosk.html Public full-screen display: open/closed banner, QR code, price list, auto-refresh.
docker-compose.yml No env_file; variables come from Portainer.

Status meanings

pending → prompt_ready → songs_uploaded → awaiting_payment → paid → delivered

Branch states:

  • revisions_requested — customer asked for changes; current files are archived and a note is logged.
  • cancelled — operator cancelled the request.

Operator workflow

  1. Customer fills /request.
  2. Open /admin, click request row (or filter by status).
  3. On /admin/request/<id>, fix customer info if needed, then click Copy customer info for Hermes, paste result to Hermes.
  4. Hermes POSTs Title/Style/Lyrics back to the signed callback URL; the request becomes Prompt Ready.
  5. If the callback fails, paste Hermes' response into the Title/Style/Lyrics fields and click Save Prompt.
  6. Copy Lyrics, Style, Title into Suno Custom Mode in that order, generate two versions.
  7. Upload Version A and B MP3s.
  8. Click Send Preview Link.
  9. Customer receives email, visits player, picks version.
  10. Operator collects Square payment, enters reference, clicks Mark Paid & Deliver.
  11. Customer receives MP3 attachment(s) by email.

Environment variables that matter

APP_SECRET_KEY
ADMIN_PASSWORD
SMTP_PASS
PUBLIC_BASE_URL
BOOTH_NAME
HOST_PORT
INTERNAL_PORT
MAX_REVISIONS
HERMES_API_KEY

Most can be overridden at runtime from /admin/settings and stored in booth_settings.json.

Runtime settings stored in booth_settings.json

  • SMTP host/port/user/from and encrypted password
  • Dashboard auto-refresh interval
  • Max revisions allowed per customer
  • Booth open/closed state
  • Default MP3 metadata tags (artist, album, year, comment)
  • Hermes API key
  • Pricing: fixed items (One Song, Both Songs, WAV per song, STEMs per song) and up to 5 custom named items
  • Kiosk cycle mode (QR only, pricing only, or N seconds per slide)

These survive redeploys because booth_settings.json lives in the persistent uploads volume.

Gotchas

  • Multiple forms on admin/request.html must stay properly closed; nested forms break buttons.
  • upload_songs form needs enctype="multipart/form-data" and a matching </form>.
  • The dashboard uses basename() as a function, not a Jinja filter.
  • Reset System deletes DB rows and all files under UPLOAD_FOLDER, then resets sqlite_sequence.
  • Runtime settings are stored in the persistent uploads volume (booth_settings.json).
  • The booth_open setting controls whether /request and /kiosk show the open banner or the closed banner.
  • Container cannot read host paths; all static assets used at runtime (logo, banners, QR code) must be in static/ or a mounted volume.
  • The Hermes callback URL is signed with APP_SECRET_KEY and expires after 7 days.
  • If you regenerate the Hermes API key, update the Hermes skill/config immediately; old key requests will 401.
  • New columns/tables are added via models.py. Use /admin/settingsFix Database Schema after redeploying a schema change.
  • __pycache__ and local .env files are already ignored by .gitignore; make sure they never get committed.

How to redeploy

  1. Push changes to Gitea main.
  2. In Portainer: Stacks → theme-song-boothPull and redeploy.
  3. If schema changed, open container console and run python init_db.py, or use /admin/settingsFix Database Schema.

Recent major additions

  • Structured style dropdowns — customer form now uses Decade / Basic / Additional style dropdowns instead of a free-text genre field. Values are stored as a comma-separated string in style_genre.
  • Pronouns field — required pronouns dropdown on the customer request form; stored in the pronouns column.
  • Lyrics in delivery email — final delivery email includes the generated lyrics in the same format as the player page.
  • Delete uploaded songs — admin request page can delete selected Version A / B uploads and reset the request to prompt_ready.
  • Live queue kiosk slide/kiosk can cycle through QR, pricing, and active-queue slides based on kiosk_cycle_seconds.
  • Cancelled status — operators can mark any request as cancelled from the top of /admin/request/<id>.
  • Revision history log — each customer revision is recorded with revision count, note, and archived file names.
  • Stems / Extras link — operators paste a file-share link on the request page; customers see a download button after delivery.
  • Sales report/admin/sales lists all delivered requests with payment references.
  • Pricing page/admin/pricing configures fixed prices plus up to 5 custom items; used by /kiosk.
  • Public kiosk/kiosk is a full-screen tablet display with QR code and price list cycling.
  • Music ZIP backup/admin/settings can download all uploaded MP3s as a ZIP.
  • Database schema repair — health check detects missing columns and tables and can repair them.

Project state notes

  • No .gitlab-ci.yml is currently in the repo; old pipeline records from an earlier CI config are still visible in Gitea but are not actionable because no runners are attached. Add a CI skeleton (see below) if you want automated checks back.
  • No automated tests exist yet.

CI skeleton (optional)

A CI skeleton is the smallest Gitea CI config that gives you useful automated checks on every push without needing a heavy test suite. For this project it would be a .gitlab-ci.yml with one or two jobs:

  1. Syntax check job — install Python dependencies and run python -m py_compile app.py models.py config.py init_db.py to catch SyntaxErrors before they reach Portainer.
  2. (Optional) Test job — run a minimal pytest suite once tests are written. Right now this would be a placeholder that skips if no tests exist, so the pipeline stays green while you decide whether to add tests.

It needs a Gitea runner to execute. Your Gitea instance has no runners attached, which is why the old pipelines are stuck/canceled. The skeleton just defines what to run; a runner is still required for it to actually execute.

Static assets to keep in the repo

  • static/Trollgorithm_booth.jpg — open banner (request page and kiosk)
  • static/Booth_closed.png — closed banner
  • static/DM-Logo_email.png — email signature logo
  • static/qr-code.png — kiosk QR code pointing to /request