- 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
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
mutagenfor MP3 metadata taggingflask-limiterfor public form rate limitingcryptographyto 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
- Customer fills
/request. - Open
/admin, click request row (or filter by status). - On
/admin/request/<id>, fix customer info if needed, then click Copy customer info for Hermes, paste result to Hermes. - Hermes POSTs Title/Style/Lyrics back to the signed callback URL; the request becomes Prompt Ready.
- If the callback fails, paste Hermes' response into the Title/Style/Lyrics fields and click Save Prompt.
- Copy Lyrics, Style, Title into Suno Custom Mode in that order, generate two versions.
- Upload Version A and B MP3s.
- Click Send Preview Link.
- Customer receives email, visits player, picks version.
- Operator collects Square payment, enters reference, clicks Mark Paid & Deliver.
- 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.htmlmust stay properly closed; nested forms break buttons. upload_songsform needsenctype="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 resetssqlite_sequence. - Runtime settings are stored in the persistent uploads volume (
booth_settings.json). - The
booth_opensetting controls whether/requestand/kioskshow 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_KEYand 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/settings→ Fix Database Schema after redeploying a schema change. __pycache__and local.envfiles are already ignored by.gitignore; make sure they never get committed.
How to redeploy
- Push changes to Gitea
main. - In Portainer: Stacks →
theme-song-booth→ Pull and redeploy. - If schema changed, open container console and run
python init_db.py, or use/admin/settings→ Fix 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
pronounscolumn. - 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 —
/kioskcan cycle through QR, pricing, and active-queue slides based onkiosk_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/saleslists all delivered requests with payment references. - Pricing page —
/admin/pricingconfigures fixed prices plus up to 5 custom items; used by/kiosk. - Public kiosk —
/kioskis a full-screen tablet display with QR code and price list cycling. - Music ZIP backup —
/admin/settingscan 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.ymlis 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:
- Syntax check job — install Python dependencies and run
python -m py_compile app.py models.py config.py init_db.pyto catch SyntaxErrors before they reach Portainer. - (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 bannerstatic/DM-Logo_email.png— email signature logostatic/qr-code.png— kiosk QR code pointing to/request