The final delivery email now appends a Song Lyrics section in the same format shown on the customer player page: section headers like [Chorus] are stripped, blank lines are preserved as stanza breaks, and the lyric lines are rendered as plain text. Bumps version to 0.4.8.
270 lines
13 KiB
Markdown
270 lines
13 KiB
Markdown
# Theme Song Booth
|
|
|
|
**Version:** `v0.4.8`
|
|
|
|
A Flask web application for running a convention booth where visitors request a custom AI-generated theme song. Operators manage the queue from an admin dashboard, generate Suno prompts, upload MP3 previews, collect payment, and deliver final songs by email.
|
|
|
|
---
|
|
|
|
## Table of contents
|
|
|
|
1. [What the booth does](#what-the-booth-does)
|
|
2. [Customer-facing pages](#customer-facing-pages)
|
|
3. [Operator / admin pages](#operator--admin-pages)
|
|
4. [Status flow](#status-flow)
|
|
5. [Settings page explained](#settings-page-explained)
|
|
6. [Docker installation](#docker-installation)
|
|
7. [Environment variables](#environment-variables)
|
|
8. [File layout](#file-layout)
|
|
9. [Local development](#local-development)
|
|
10. [Common troubleshooting](#common-troubleshooting)
|
|
11. [License / ownership](#license--ownership)
|
|
|
|
---
|
|
|
|
## What the booth does
|
|
|
|
1. A visitor fills out a short form at `/request`.
|
|
2. The operator reviews the request in the admin dashboard and generates a Suno Custom Mode prompt.
|
|
3. The operator (or an AI assistant via the `/api/prompt` callback) saves the prompt to the request.
|
|
4. The operator creates two song versions in Suno, downloads them, and uploads **Version A** and **Version B** to the request page.
|
|
5. The operator sends a preview email; the customer visits their private player page, listens to both versions, and either approves one/both or requests changes.
|
|
6. After the customer approves and pays, the operator records the payment reference and delivers the selected MP3(s) by email.
|
|
7. Optional stems / extras can be delivered via a share link that appears on the player page after delivery.
|
|
|
|
---
|
|
|
|
## Customer-facing pages
|
|
|
|
| Page | Path | Purpose |
|
|
|------|------|---------|
|
|
| Request form | `/request` | Visitors enter email, name, pronouns, hobbies, notable facts, and extra requests. The music style is picked from three dropdowns: **Decade** (required), **Basic style** (required), and **Additional style** (optional). Decades and genres are loaded from `/mnt/Storage/Decades.txt` and `/mnt/Storage/Music Genres.txt`. Rate limited to 5 submissions per minute per IP. |
|
|
| Closed page | `/request` (when booth is closed) | Shows a friendly closed banner instead of the form when the operator marks the booth closed. |
|
|
| Thanks | `/thanks/<id>` | Confirmation page shown after a request is submitted. |
|
|
| Order status | `/status` | Customers enter their email to see all their requests and statuses. |
|
|
| FAQ | `/faq` | Answers common customer questions. |
|
|
| Private player | `/play/<token>` | Secret link emailed to the customer. Streams Version A and B, lets them approve or request revisions, and later download delivered files / stems. |
|
|
| Kiosk | `/kiosk` | Public full-screen display for a booth tablet. Cycles between a QR code for `/request` and the configured price list. Updates automatically when pricing or booth state changes. |
|
|
|
|
### Style selection
|
|
|
|
The request form no longer has a free-text genre field. Instead, customers choose:
|
|
|
|
1. **Decade / era** — required (e.g. `1980's`).
|
|
2. **Basic style** — required (e.g. `Pop`).
|
|
3. **Additional style** — optional (e.g. `Funk`).
|
|
|
|
These are stored together in the `style_genre` column as a comma-separated string (e.g. `1980's, Pop, Funk`) so no schema change is required. The admin request page shows the same dropdowns for corrections, and the copy-to-Hermes prompt formats the style as a clean sentence like "1980's-era Pop with Funk influences" for better Suno results.
|
|
|
|
### Pronouns
|
|
|
|
A required **Pronouns** dropdown is shown just below the name field, with options:
|
|
|
|
- He/Him/His
|
|
- She/Her/Hers
|
|
- They/Them/Their
|
|
|
|
The selected pronouns are stored in the `pronouns` column and included in confirmation emails and Hermes prompt copy.
|
|
|
|
---
|
|
|
|
## Operator / admin pages
|
|
|
|
| Page | Path | Purpose |
|
|
|------|------|---------|
|
|
| Login | `/admin/login` | Simple session-based login. Password comes from the `ADMIN_PASSWORD` environment variable. |
|
|
| Dashboard | `/admin` | Main queue. Filter by status and auto-refresh at a configurable interval. |
|
|
| Request detail | `/admin/request/<id>` | Full control of one request: edit customer info (including pronouns and structured style), save prompt, copy Hermes callback, view revision history, upload MP3s, send preview, record payment, deliver files, add operator notes, and cancel. |
|
|
| Pricing | `/admin/pricing` | Configure fixed prices (one song, both songs, WAV per song, STEMs per song) and up to 5 custom items. |
|
|
| Sales | `/admin/sales` | Report of all delivered requests with customer details and Square payment references. |
|
|
| Settings | `/admin/settings` | Database health, backups, SMTP config, MP3 metadata defaults, revision limit, auto-refresh interval, kiosk mode, booth open/closed switch, Hermes API key management, and system reset. |
|
|
| Reset | `/admin/reset` | Clears all requests and uploaded files. Requires admin password confirmation. |
|
|
|
|
---
|
|
|
|
## Status flow
|
|
|
|
```
|
|
pending → prompt_ready → songs_uploaded → awaiting_payment → paid → delivered
|
|
```
|
|
|
|
| Status | Meaning |
|
|
|--------|---------|
|
|
| `pending` | Customer submitted; waiting for a Suno prompt. |
|
|
| `prompt_ready` | Prompt saved; ready to generate songs. |
|
|
| `songs_uploaded` | Both MP3s uploaded; preview link can be sent. |
|
|
| `revisions_requested` | Customer asked for changes; current files archived. |
|
|
| `awaiting_payment` | Customer approved a version; waiting for payment. |
|
|
| `paid` | Payment recorded. |
|
|
| `delivered` | Final MP3(s) emailed to the customer. |
|
|
| `cancelled` | Request cancelled by the operator. |
|
|
|
|
---
|
|
|
|
## Settings page explained
|
|
|
|
The `/admin/settings` page is split into functional sections:
|
|
|
|
### Booth state
|
|
- **Booth open / closed** — When closed, `/request` and `/kiosk` show the closed banner.
|
|
|
|
### Hermes API key
|
|
- Displays whether a key is configured.
|
|
- **Regenerate API Key** creates a new random key stored in runtime settings.
|
|
- The key is used by the `/api/prompt/<id>` callback and by the `/api/key-test` diagnostic endpoint.
|
|
- Copy this key into your Hermes skill or AI assistant config.
|
|
|
|
### Customer revision limit
|
|
- Maximum number of times a customer can click **Request Changes** on the player page.
|
|
- Default is controlled by `MAX_REVISIONS` env var; can be overridden here.
|
|
|
|
### Dashboard refresh
|
|
- How often `/admin` reloads automatically (10, 20, or 30 seconds).
|
|
|
|
### Kiosk display
|
|
- **QR only** — shows the QR code permanently.
|
|
- **Pricing only** — shows the price list permanently.
|
|
- **Cycle every N seconds** — alternates between QR and pricing.
|
|
|
|
### SMTP settings
|
|
- Host, port, username, from address, and password for sending customer emails.
|
|
- The password is encrypted using `APP_SECRET_KEY` before being saved.
|
|
- **Send Test Email** verifies the configuration.
|
|
|
|
### MP3 metadata defaults
|
|
- Artist, album, year, and comment tags applied automatically to uploaded MP3s.
|
|
- The title tag is taken from the saved Suno prompt.
|
|
|
|
### Database maintenance
|
|
- **Health Check** — verifies all expected tables and columns exist.
|
|
- **Fix Database Schema** — adds missing tables/columns without deleting data.
|
|
- **Download Database Backup** — downloads the SQLite file.
|
|
- **Restore Database Backup** — replaces the live DB with an uploaded backup.
|
|
- **Download Uploads Backup** — ZIPs all uploaded MP3s for offline storage.
|
|
- **System Reset** — deletes all requests and uploaded files for a fresh event.
|
|
|
|
---
|
|
|
|
## Docker installation
|
|
|
|
### 1. Prepare environment variables
|
|
|
|
Generate values for the required secrets:
|
|
|
|
```bash
|
|
python3 -c "import secrets; print(secrets.token_hex(32))"
|
|
```
|
|
|
|
Use the output for `APP_SECRET_KEY`.
|
|
|
|
### 2. Deploy with Portainer
|
|
|
|
1. Log in to Portainer.
|
|
2. Go to **Stacks** → **Add stack**.
|
|
3. Choose **Repository**:
|
|
- URL: `https://gitlab.hallsworth.ca/yrtria/theme-song-booth.git`
|
|
- Branch: `main`
|
|
- Compose path: `docker-compose.yml`
|
|
4. Add the environment variables listed in the section below.
|
|
5. Deploy the stack.
|
|
6. Open a console in the running `theme-song-booth` container and run once:
|
|
|
|
```bash
|
|
python init_db.py
|
|
```
|
|
|
|
7. Point your reverse proxy at the host port you chose (default `127.0.0.1:8000`).
|
|
8. Visit `/admin/settings` and click **Regenerate API Key**.
|
|
9. Copy the key to your Hermes skill / AI assistant.
|
|
10. Print or display a QR code pointing to `https://your-domain/request`.
|
|
|
|
### Updating the deployment
|
|
|
|
After each push to GitLab:
|
|
|
|
```text
|
|
Portainer → Stacks → theme-song-booth → Pull and redeploy
|
|
```
|
|
|
|
Persistent volumes keep the database and uploads safe across redeploys.
|
|
|
|
---
|
|
|
|
## Environment variables
|
|
|
|
| Variable | Required | Default | Description |
|
|
|----------|----------|---------|-------------|
|
|
| `APP_SECRET_KEY` | Yes | — | Long random string for Flask sessions and for encrypting stored settings such as the SMTP password and legacy stored API key. |
|
|
| `ADMIN_PASSWORD` | Yes | — | Password used to log in to `/admin`. |
|
|
| `SMTP_PASS` | Yes | — | Password for the SMTP account used to send customer emails. |
|
|
| `PUBLIC_BASE_URL` | Yes | — | Public HTTPS URL of the booth, e.g. `https://booth.dionysismedia.ca`. Used in player links, emails, and callback URLs. |
|
|
| `SMTP_HOST` | No | `mailroot8.namespro.ca` | SMTP server hostname. |
|
|
| `SMTP_PORT` | No | `465` | SMTP server port. |
|
|
| `SMTP_USER` | No | `ai@hallsworth.ca` | SMTP username. |
|
|
| `SMTP_FROM` | No | `ai@hallsworth.ca` | From address for customer emails. |
|
|
| `BOOTH_NAME` | No | `Trollgorithm Theme Songs` | Display name used in email subjects and page titles. |
|
|
| `HOST_PORT` | No | `127.0.0.1:8000` | Host-side `ip:port` mapping for the container. |
|
|
| `INTERNAL_PORT` | No | `8000` | Port gunicorn binds to inside the container. |
|
|
| `PRICE_PER_VERSION` | No | `10.00` | Legacy price label shown in some templates; current pricing is configured from `/admin/pricing`. |
|
|
| `CURRENCY` | No | `CAD` | Currency label shown with prices. |
|
|
| `MAX_REVISIONS` | No | `2` | Default customer revision limit before an operator override. |
|
|
| `HERMES_API_KEY` | No | — | API key for the `/api/prompt` callback. If omitted, generate one from `/admin/settings`. |
|
|
| `DATABASE` | No | `/app/data/booth.db` | Path to the SQLite database inside the container. |
|
|
| `UPLOAD_FOLDER` | No | `/app/uploads` | Path to uploaded MP3 storage inside the container. |
|
|
|
|
---
|
|
|
|
## File layout
|
|
|
|
| File | Purpose |
|
|
|------|---------|
|
|
| `app.py` | Flask routes, helpers, email layer, runtime settings, MP3 tagging, rate limiting, database maintenance, Hermes callback, kiosk, pricing, and sales report. |
|
|
| `config.py` | Environment-variable based configuration with sensible defaults. |
|
|
| `models.py` | SQLite schema, CRUD helpers, and revision history. |
|
|
| `init_db.py` | Standalone script to create or migrate the database. |
|
|
| `templates/` | Jinja2 templates for customer pages, admin pages, and kiosk display. |
|
|
| `static/` | Banner images, closed banner, email logo, and kiosk QR code. |
|
|
| `lists/` | Bundled copies of `decades.txt` and `music_genres.txt` used as fallback for the style dropdowns. |
|
|
| `Dockerfile` | Production container image definition. |
|
|
| `docker-compose.yml` | Portainer stack definition. |
|
|
| `requirements.txt` | Python dependencies. |
|
|
| `.env.example` | Local development environment template. |
|
|
|
|
---
|
|
|
|
## Local development
|
|
|
|
```bash
|
|
cd /home/jess/workspace/theme-song-booth
|
|
python3 -m venv .venv
|
|
.venv/bin/pip install -r requirements.txt
|
|
cp .env.example .env
|
|
# Edit .env and set APP_SECRET_KEY, ADMIN_PASSWORD, SMTP_PASS, PUBLIC_BASE_URL
|
|
.venv/bin/python init_db.py
|
|
.venv/bin/python -m flask --app app run --host=0.0.0.0
|
|
```
|
|
|
|
Visit:
|
|
- Customer form: http://127.0.0.1:5000/request
|
|
- Admin login: http://127.0.0.1:5000/admin
|
|
- Kiosk: http://127.0.0.1:5000/kiosk
|
|
|
|
---
|
|
|
|
## Common troubleshooting
|
|
|
|
| Problem | Cause | Fix |
|
|
|---------|-------|-----|
|
|
| Emails not arriving | SMTP settings wrong or messages in spam. | Use **Send Test Email** on `/admin/settings`; verify host/port/password. |
|
|
| Can't reach app through domain | Reverse proxy points to wrong host port. | Match `HOST_PORT` to your proxy upstream. |
|
|
| Static banner not showing | Browser cached old image. | Hard-refresh or redeploy stack. |
|
|
| Logo missing from email | Logo file missing from `static/`. | Ensure `static/DM-Logo_email.png` is in the container. |
|
|
| Database schema mismatch | New column/table added but old DB not migrated. | Go to `/admin/settings` and click **Fix Database Schema**, or run `python init_db.py`. |
|
|
| Kiosk shows old prices | Page auto-refreshes every 30s; check `/admin/pricing`. | Verify pricing values and redeploy if templates changed. |
|
|
| `/api/prompt` returns 401 | Callback token expired or API key mismatch. | Copy a fresh callback URL from `/admin/request/<id>` and verify the key with `/api/key-test`. |
|
|
|
|
---
|
|
|
|
## License / ownership
|
|
|
|
Built for Jess's Trollgorithm theme-song booth. All code and assets are private to that project.
|