booth-musicgpt/README.md
Troll (Hermes Agent) 9575464be2 v0.8.8: Fix stems extraction — upload MP3 file instead of audio_url
The MusicGPT Extraction API's audio_url field only accepts YouTube URLs,
not direct MP3 links. Sending our /api/audio-source/ endpoint URL resulted
in 'Error fetching audio length from YouTube' because MusicGPT tried to
parse it as a YouTube video.

Switch to the audio_file upload option: open the local MP3 and upload it
directly as multipart/form-data to the Extraction API.

Bumped timeout from 30s to 60s for file upload.
2026-08-12 03:37:43 +00:00

21 KiB

Theme Song Booth (MusicGPT Edition)

Version

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 prompts via Hermes, queue generations through the MusicGPT API, and deliver final songs by email.

The MusicGPT fork automates song generation: instead of manually creating songs in Suno and uploading MP3s, the operator saves a title/style/lyrics prompt and clicks Generate A/B with MusicGPT. The API returns two MP3 versions (plus WAVs and an album cover) and posts status updates to a webhook. Stems are extracted on demand and uploaded to a Gokapi file-sharing server for expiring download links.


Table of contents

  1. What the booth does
  2. Customer-facing pages
  3. Operator / admin pages
  4. Status flow
  5. MusicGPT workflow
  6. Stems extraction
  7. Settings page explained
  8. Docker installation
  9. Environment variables
  10. File layout
  11. Local development
  12. Common troubleshooting
  13. 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 prompt for MusicGPT (manually or via the /api/prompt Hermes callback).
  3. The operator saves the title, style, and lyrics to the request.
  4. The operator clicks Generate A/B with MusicGPT, selects a model, and waits for MusicGPT to finish. MusicGPT produces two MP3 versions, optional WAVs, and an album cover.
  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) — optionally with WAV files and the album cover — by email.
  7. Optional STEMS can be generated for an extra fee. The operator selects which MP3 version to extract stems from via radio buttons, and the completed stems are automatically zipped and uploaded to Gokapi as a single expiring download link.

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. Includes a Refresh MusicGPT Status button to manually poll all in-flight tasks.
Request detail /admin/request/<id> Full control of one request: edit customer info, save prompt, copy Hermes callback, queue MusicGPT generation, poll a single MusicGPT task, view status/cost, view revision history, upload MP3 overrides, send preview, record payment, deliver files (MP3/WAV/cover), add operator notes, cancel generation, and generate STEMS.
Pricing /admin/pricing Configure fixed prices and up to 5 custom items. Also shows running MusicGPT cost totals in USD.
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, MusicGPT webhook URL, album cover delivery toggle, automatic MusicGPT polling toggle, 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 prompt.
prompt_ready Prompt saved; ready to generate songs.
songs_uploaded Both MP3s are ready; 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.

MusicGPT workflow

Supported models

The available MusicGPT models are limited by the API key. The deployed key supports v6 and v6-pro; v6-pro is the default. The model dropdown on /admin/request/<id> only shows models returned by the MusicGPT API for the configured key.

Generating songs

  1. On /admin/request/<id>, enter or paste the Title, Style, and Lyrics.
  2. Click Save Prompt.
  3. Choose a model and whether to include WAV files.
  4. Click Generate A/B with MusicGPT.

The app queues two conversions (Version A and Version B) with MusicGPT. It stores the task ID, both conversion IDs, the webhook URL, and an estimated cost.

Webhook

MusicGPT POSTs to /api/musicgpt/webhook when each conversion completes. The handler:

  • Matches the payload to the request by task_id and conversion_id.
  • Updates the request status and cost. Per-version costs are accumulated into a total (musicgpt_cost), with per-version breakdowns stored in musicgpt_cost_a and musicgpt_cost_b.
  • Downloads album cover, MP3s, and WAVs when the payload includes URLs.
  • Webhooks often arrive before audio URLs are ready; missing files are filled in by polling.

Polling

Three ways to poll MusicGPT:

Method How When to use
Automatic Background job hits /admin/musicgpt/autopoll every 3 minutes Set-and-forget; enabled by default and can be turned off in /admin/settings.
Dashboard refresh Refresh MusicGPT Status button on /admin Manual check of all in-flight tasks.
Per-request Poll MusicGPT button on /admin/request/<id> Force a single request to update now.

All polling paths call musicgpt_poll_status() and run download_musicgpt_outputs() when the API reports COMPLETED, so MP3s/WAVs arrive without further operator action once they are available.

Cost tracking

The per-request LLM Cost display on /admin/request/<id> shows the combined total of song generation costs and stems extraction costs, with a breakdown in smaller text:

LLM Cost: $0.2740 USD (songs: $0.1370 USD + stems: $0.1370 USD)

Per-version costs are accumulated from separate webhook payloads rather than overwritten, so the total reflects both Version A and Version B. The /admin/pricing page shows aggregate MusicGPT costs across all requests in USD.

WAV delivery

A per-request checkbox Include WAV files with delivery stores deliver_wav=1. When enabled, WAV files are downloaded alongside MP3s and attached to the delivery email.

Album cover

A global toggle in /admin/settings controls whether the generated album cover is attached to delivery emails. The cover is always downloaded and shown on the admin request page.


Stems extraction

Generating stems

  1. On /admin/request/<id>, scroll to the Stems section in the Songs panel.
  2. Select the source MP3 using the radio buttons — Version A or Version B. Only versions with files ready are selectable; unavailable versions are greyed out.
  3. Click Generate Stems.

The app uploads the selected MP3 file directly to the MusicGPT Extraction API (the API's audio_url field only accepts YouTube URLs, so we use the audio_file upload option). The Extraction API separates vocals and instrumental tracks and posts the result to the webhook.

Stems webhook and Gokapi upload

When the Extraction webhook fires with COMPLETED status:

  1. The app parses the individual stem URLs (vocals, instrumental) from the webhook payload.
  2. Downloads each stem file from the MusicGPT CDN.
  3. Bundles them into a single zip archive named stems_<song title>.zip.
  4. Uploads the zip to Gokapi (GOKAPI_URL) with a configurable expiry (default 30 days).
  5. Saves the Gokapi download URL to stems_link in the database.

The Download Stems button on the admin request page links directly to the Gokapi download page. The stems_link is also included in the delivery email when stems are part of the order.

Error recovery

If stems extraction fails, the error message is displayed on the admin request page but is automatically cleared on the next retry attempt. The Generate Stems button remains enabled when the status is ERROR, so the operator can retry without navigating away.

Re-uploading existing stems

If the webhook completed before Gokapi was configured (or the Gokapi upload failed), a Re-upload to Gokapi button appears in the Stems / Extras Link section. This manually triggers the download-zip-upload flow for the existing stem files.

Manual download fallback

If stems_link is empty but stems_url contains raw CDN URLs, the Download Stems button falls back to the /admin/request/<id>/download-stems endpoint, which downloads the stem files on the fly, zips them, and serves the zip as a browser download.


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.
  • The key is used by the /api/prompt/<id> callback.
  • 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 downloaded MP3s.
  • The title tag is taken from the saved Suno/MusicGPT prompt title.

MusicGPT settings

  • API key configured indicator.
  • Webhook URL — shown for reference; sent per-conversion with every generation request.
  • Album cover delivery — attach cover to delivery emails.
  • Automatic MusicGPT polling — enable/disable the background poll that downloads ready files without operator action.

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:

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 StacksAdd stack.
  3. Choose Repository:
    • URL: https://gitlab.hallsworth.ca/yrtria/booth-musicgpt.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 booth-musicgpt container and run once:
python init_db.py
  1. Point your reverse proxy at the host port you chose (default 0.0.0.0:8500).
  2. Visit /admin/settings, confirm the MusicGPT webhook URL, and configure the MusicGPT account webhook endpoint to the same URL.
  3. Copy the Hermes API key to your Hermes skill / AI assistant.
  4. Print or display a QR code pointing to https://your-domain/request.

Updating the deployment

After each push to Gitea:

Portainer → Stacks → booth-musicgpt → 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.
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://music.dionysismedia.ca. Used in player links, emails, callback URLs, and the MusicGPT webhook URL.
MUSICGPT_API_KEY Yes API key for MusicGPT generation, polling, and stems extraction.
GOKAPI_URL No Base URL of the Gokapi file-sharing server, e.g. https://files.dionysismedia.ca. Required for automatic stems upload.
GOKAPI_API_KEY No API key for the Gokapi REST API. Required for automatic stems upload.
GOKAPI_EXPIRY_DAYS No 30 Number of days before uploaded stems files expire on Gokapi.
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 0.0.0.0:8500 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.
DATABASE No /app/data/booth.db Path to the SQLite database inside the container.
UPLOAD_FOLDER No /app/uploads Path to uploaded/downloaded song storage inside the container.

File layout

File Purpose
app.py Flask routes, runtime settings, email layer, MP3 tagging, rate limiting, database maintenance, Hermes callback, kiosk, pricing, sales report, MusicGPT webhooks, polling, file downloads, stems download-zip endpoint, audio-source endpoint, and Gokapi upload trigger.
config.py Environment-variable based configuration with sensible defaults. Includes MusicGPT and Gokapi settings.
helpers.py MusicGPT API client, polling, downloads, email helpers, MP3 metadata, settings persistence, Gokapi upload, stems-to-Gokapi processing, cost formatting, and utility functions.
models.py SQLite schema, CRUD helpers, and revision history. Includes per-version cost columns (musicgpt_cost_a, musicgpt_cost_b).
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.
VERSION Current version string (read by config.py and displayed on /admin/settings).

Local development

cd /home/jess/workspace/booth-musicgpt
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, MUSICGPT_API_KEY
# Optional: GOKAPI_URL, GOKAPI_API_KEY, GOKAPI_EXPIRY_DAYS
.venv/bin/python init_db.py
.venv/bin/python -m flask --app app run --host=0.0.0.0

Visit:


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.
MusicGPT generation fails API key lacks access to the selected model. Only v6 and v6-pro are supported by the current key; choose from the dropdown.
Webhook arrives but no files download MusicGPT webhook fires before audio URLs are ready. Wait for the automatic background poll, click Poll MusicGPT on the request, or use Refresh MusicGPT Status on the dashboard.
Stems extraction returns HTTP 500 Transient MusicGPT API error. Retry by clicking Generate Stems again. The error message is cleared automatically on the next attempt.
Stems completed but no Gokapi link Webhook fired before Gokapi was configured, or Gokapi upload failed. Click Re-upload to Gokapi in the Stems / Extras Link section.
LLM Cost shows $0 for songs Old webhook zeroed the cost estimate before the accumulation fix. New requests will accumulate correctly. Historical data cannot be recovered.

License / ownership

Built for Jess's Trollgorithm theme-song booth. All code and assets are private to that project.