- 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
154 lines
8.5 KiB
Markdown
154 lines
8.5 KiB
Markdown
# 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/settings` → **Fix 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-booth` → **Pull and redeploy**.
|
|
3. 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 `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`
|