Initial fork from theme-song-booth
This commit is contained in:
commit
99855b1212
33 changed files with 5723 additions and 0 deletions
270
README.md
Normal file
270
README.md
Normal file
|
|
@ -0,0 +1,270 @@
|
|||
# Theme Song Booth
|
||||
|
||||
**Version:** `v0.6.3`
|
||||
|
||||
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 Gitea:
|
||||
|
||||
```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.
|
||||
Loading…
Add table
Add a link
Reference in a new issue