Integrate MusicGPT API generation into booth-musicgpt v0.7.0

This commit is contained in:
Troll (Hermes Agent) 2026-08-10 22:01:08 +00:00
parent 99855b1212
commit 2ca148be9b
12 changed files with 721 additions and 44 deletions

View file

@ -164,6 +164,11 @@
<h1>Request #{{ req.id }} — {{ req.name }}</h1>
<div class="status-row">
<p style="margin:0"><strong>Status:</strong> <span class="status-badge">{{ statuses[req.status] }}</span></p>
{% if req.musicgpt_status %}
<p style="margin:0"><strong>MusicGPT:</strong> <span class="status-badge {% if req.musicgpt_status == 'COMPLETED' %}ok{% elif req.musicgpt_status in ('FAILED','ERROR','CANCELLED') %}missing{% endif %}">{{ req.musicgpt_status }}</span>
{% if req.musicgpt_cost is not none %}({{ format_musicgpt_cost(req.musicgpt_cost) }}){% endif %}
</p>
{% endif %}
{% if req.status != 'cancelled' %}
<form method="POST" onsubmit="return confirm('Are you sure you want to cancel request #{{ req.id }}?')" style="margin:0">
<input type="hidden" name="action" value="cancel_request">
@ -172,6 +177,12 @@
{% endif %}
</div>
{% if req.album_cover_url %}
<div class="section" style="text-align:center">
<img src="{{ req.album_cover_url }}" alt="Album cover" style="max-width:320px;border-radius:.5rem">
</div>
{% endif %}
{% with messages = get_flashed_messages(with_categories=true) %}
{% for category, message in messages %}
<div class="flash {{ category }}">{{ message }}</div>
@ -322,11 +333,15 @@
<!-- RIGHT COLUMN -->
<div class="right">
<!-- Generate Suno Prompt -->
<!-- Generate Music Prompt -->
<div class="section">
<h2>1. Generate &amp; Save Suno Prompt</h2>
<h2>1. Generate & Save Music Prompt</h2>
<button type="button" onclick="copyPromptForHermes()">Copy customer info for Hermes</button>
<p class="copy-hint">Paste Hermes' Title, Style, and Lyrics directly into the fields below, then save.</p>
<p class="copy-hint">Paste Hermes' Title, Style, and Lyrics directly into the fields below, then save or generate.</p>
{% if req.musicgpt_error %}
<div class="flash error">Error: {{ req.musicgpt_error }}</div>
{% endif %}
<form method="POST">
<input type="hidden" name="action" value="save_prompt">
@ -343,32 +358,67 @@
<button type="submit">Save Prompt</button>
</div>
</form>
<hr style="border-color:#374151;margin:1rem 0">
<form method="POST">
<input type="hidden" name="action" value="generate_musicgpt">
<input type="hidden" name="suno_title" value="{{ req.suno_title or '' }}">
<input type="hidden" name="suno_style" value="{{ req.suno_style or '' }}">
<input type="hidden" name="suno_lyrics" value="{{ req.suno_lyrics or '' }}">
<label for="musicgpt_model">MusicGPT Model</label>
<select id="musicgpt_model" name="musicgpt_model">
{% for m in musicgpt_models %}
<option value="{{ m }}" {% if m == musicgpt_default_model %}selected{% endif %}>{{ m }}</option>
{% endfor %}
</select>
<label for="deliver_wav_gen" style="display:flex;align-items:center;gap:.5rem;margin-top:1rem;cursor:pointer;">
<input type="checkbox" id="deliver_wav_gen" name="deliver_wav" value="1" {% if req.deliver_wav %}checked{% endif %} style="width:auto;margin:0">
Include WAV files with delivery
</label>
{% set in_progress = req.musicgpt_status in ('IN_QUEUE', 'IN_PROGRESS') %}
<div class="actions">
<button type="submit" {% if in_progress %}disabled{% endif %}>Generate A/B with MusicGPT</button>
{% if in_progress %}
<button type="submit" name="action" value="cancel_musicgpt" class="danger">Cancel Generation</button>
{% endif %}
</div>
</form>
</div>
<!-- Upload Songs -->
<div class="section">
<h2>2. Upload Songs</h2>
<h2>2. Songs</h2>
<form method="POST">
<input type="hidden" name="action" value="delete_songs">
<div class="song-row">
<input type="checkbox" id="delete_a" name="delete_song" value="a"
{% if not (req.song_a_path and file_exists(req.song_a_path)) %}disabled{% endif %}>
<label for="delete_a">Version A:
<label for="delete_a">Version A MP3:
{% if req.song_a_path and file_exists(req.song_a_path) %}
<span class="status-badge ok">✅ Uploaded</span> {{ basename(req.song_a_path) }}
<span class="status-badge ok">✅ Ready</span> {{ basename(req.song_a_path) }}
{% if req.song_a_wav_path and file_exists(req.song_a_wav_path) %}
<br><span class="status-badge ok">WAV</span> {{ basename(req.song_a_wav_path) }}
{% endif %}
{% else %}
<span class="status-badge missing">❌ Not uploaded</span>
<span class="status-badge missing">❌ Not ready</span>
{% endif %}
</label>
</div>
<div class="song-row">
<input type="checkbox" id="delete_b" name="delete_song" value="b"
{% if not (req.song_b_path and file_exists(req.song_b_path)) %}disabled{% endif %}>
<label for="delete_b">Version B:
<label for="delete_b">Version B MP3:
{% if req.song_b_path and file_exists(req.song_b_path) %}
<span class="status-badge ok">✅ Uploaded</span> {{ basename(req.song_b_path) }}
<span class="status-badge ok">✅ Ready</span> {{ basename(req.song_b_path) }}
{% if req.song_b_wav_path and file_exists(req.song_b_wav_path) %}
<br><span class="status-badge ok">WAV</span> {{ basename(req.song_b_wav_path) }}
{% endif %}
{% else %}
<span class="status-badge missing">❌ Not uploaded</span>
<span class="status-badge missing">❌ Not ready</span>
{% endif %}
</label>
</div>
@ -381,14 +431,45 @@
<form method="POST" enctype="multipart/form-data">
<input type="hidden" name="action" value="upload_songs">
<label for="song_a">Version A MP3</label>
<label for="song_a">Version A MP3 (override)</label>
<input type="file" id="song_a" name="song_a" accept="audio/mpeg,.mp3">
<label for="song_b">Version B MP3</label>
<label for="song_b">Version B MP3 (override)</label>
<input type="file" id="song_b" name="song_b" accept="audio/mpeg,.mp3">
<div class="actions">
<button type="submit">Upload Songs</button>
</div>
</form>
<hr style="border-color:#374151;margin:1rem 0">
<!-- Stems -->
<form method="POST">
<input type="hidden" name="action" value="generate_stems">
<label for="stems_source_url">Stems Source Audio URL</label>
<input type="url" id="stems_source_url" name="stems_source_url" placeholder="https://.../song.mp3" value="{{ req.song_a_path or '' }}">
<p class="copy-hint">Vocal + instrumental extraction: ~$0.018$0.084 USD per song on Pro plan.</p>
<label for="stems_status">Stems Status:</label>
<p>
{% if req.stems_status %}
<span class="status-badge {% if req.stems_status == 'COMPLETED' %}ok{% elif req.stems_status in ('FAILED','ERROR') %}missing{% endif %}">{{ req.stems_status }}</span>
{% if req.stems_cost is not none %}({{ format_musicgpt_cost(req.stems_cost) }}){% endif %}
{% else %}
<span class="status-badge missing">Not requested</span>
{% endif %}
</p>
{% if req.stems_error %}
<div class="flash error">{{ req.stems_error }}</div>
{% endif %}
{% if req.stems_url %}
<p><a href="{{ req.stems_url }}" target="_blank">Download stems →</a></p>
{% endif %}
{% set stems_in_progress = req.stems_status in ('IN_QUEUE', 'IN_PROGRESS') %}
<div class="actions">
<button type="submit" {% if stems_in_progress %}disabled{% endif %}>Generate Stems</button>
</div>
</form>
</div>
<!-- Notify Customer -->
@ -449,6 +530,20 @@
<hr style="border-color:#374151;margin:1rem 0">
<p style="color:#93c5fd;font-weight:600">Delivery options:</p>
<form method="POST" style="margin-bottom:.5rem">
<input type="hidden" name="action" value="save_delivery_options">
<label for="deliver_wav" style="display:flex;align-items:center;gap:.5rem;cursor:pointer;">
<input type="checkbox" id="deliver_wav" name="deliver_wav" value="1" {% if req.deliver_wav %}checked{% endif %} style="width:auto;margin:0">
Include WAV files in delivery email
</label>
<div class="actions">
<button type="submit" class="secondary">Save Delivery Options</button>
</div>
</form>
<hr style="border-color:#374151;margin:1rem 0">
<p style="color:#93c5fd;font-weight:600">Select files to deliver:</p>
{% set all_files = [] %}
{% if req.song_a_path and file_exists(req.song_a_path) %}
@ -457,6 +552,14 @@
{% if req.song_b_path and file_exists(req.song_b_path) %}
{% set _ = all_files.append(req.song_b_path) %}
{% endif %}
{% if req.deliver_wav %}
{% if req.song_a_wav_path and file_exists(req.song_a_wav_path) %}
{% set _ = all_files.append(req.song_a_wav_path) %}
{% endif %}
{% if req.song_b_wav_path and file_exists(req.song_b_wav_path) %}
{% set _ = all_files.append(req.song_b_wav_path) %}
{% endif %}
{% endif %}
{% set files_to_show = all_files + extra_files %}
{% for fpath in files_to_show %}
@ -505,7 +608,7 @@
extra_requests: {{ req.extra_requests | tojson }},
stems_interest: {{ (req.stems_interest or 0) | tojson }}
};
const text = `Booth request: ${data.request_id}\\nCustomer email: ${data.email}\\nCallback URL: ${callbackUrl}\\n\\nGenerate a Suno Custom Mode prompt for this customer and POST it back to the Callback URL as JSON.\\n\\nCustomer data:\\nName: ${data.name}\\nPronouns: ${data.pronouns || '-'}\\nHobbies: ${data.hobbies || '-'}\\nNotable facts: ${data.notable_facts || '-'}\\nStyle / genre: ${styleSentence || styleGenre || '-'}\\nPreferred singer voice / gender: ${data.vocal_gender || 'No preference'}\\nExtra requests: ${data.extra_requests || '-'}\\nInterested in STEMS: ${data.stems_interest ? 'Yes' : 'No'}\\n\\nExpected JSON response format:\\n{\\n \\"request_id\\": ${data.request_id},\\n \\"email\\": \\"${data.email}\\",\\n \\"suno_title\\": \\"...\\",\\n \\"suno_style\\": \\"...\\",\\n \\"suno_lyrics\\": \\"...\\"\\n}`;
const text = `Booth request: ${data.request_id}\\nCustomer email: ${data.email}\\nCallback URL: ${callbackUrl}\\n\\nGenerate an AI music prompt for this customer and POST it back to the Callback URL as JSON.\\n\\nCustomer data:\\nName: ${data.name}\\nPronouns: ${data.pronouns || '-'}\\nHobbies: ${data.hobbies || '-'}\\nNotable facts: ${data.notable_facts || '-'}\\nStyle / genre: ${styleSentence || styleGenre || '-'}\\nPreferred singer voice / gender: ${data.vocal_gender || 'No preference'}\\nExtra requests: ${data.extra_requests || '-'}\\nInterested in STEMS: ${data.stems_interest ? 'Yes' : 'No'}\\n\\nExpected JSON response format:\\n{\\n \\"request_id\\": ${data.request_id},\\n \\"email\\": \\"${data.email}\\",\\n \\"suno_title\\": \\"...\\",\\n \\"suno_style\\": \\"...\\",\\n \\"suno_lyrics\\": \\"...\\"\\n}`;
navigator.clipboard.writeText(text).then(() => alert("Copied! Paste into Hermes. Hermes will POST the generated prompt back to the Callback URL."));
}
@ -541,7 +644,7 @@
extra_requests: {{ req.extra_requests | tojson }},
stems_interest: {{ (req.stems_interest or 0) | tojson }}
};
const text = `Booth request: ${data.request_id}\\nCustomer email: ${data.email}\\nCallback URL: ${callbackUrl}\\n\\nThis is REVISION #${revisionCount || 1} for this customer.\\n\\nGenerate a NEW Suno Custom Mode prompt that addresses the following revision request, while keeping the same overall theme/persona and matching the customer's original brief as closely as possible.\\n\\nOriginal brief:\\nName: ${data.name}\\nPronouns: ${data.pronouns || '-'}\\nHobbies: ${data.hobbies || '-'}\\nNotable facts: ${data.notable_facts || '-'}\\nStyle / genre: ${styleSentence || styleGenre || '-'}\\nPreferred singer voice / gender: ${data.vocal_gender || 'No preference'}\\nExtra requests: ${data.extra_requests || '-'}\\nInterested in STEMS: ${data.stems_interest ? 'Yes' : 'No'}\\n\\nPreviously generated prompt (do not copy verbatim; adapt and improve):\\nTitle: ${currentTitle || '-'}\\nStyle: ${currentStyle || '-'}\\nLyrics:\\n${currentLyrics || '-'}\\n\\nRevision request from customer:\\n${revisionNote || '-'}\\n\\nPOST the new prompt back to the Callback URL as JSON with this shape:\\n{\\n \\"request_id\\": ${data.request_id},\\n \\"email\\": \\"${data.email}\\",\\n \\"suno_title\\": \\"...\\",\\n \\"suno_style\\": \\"...\\",\\n \\"suno_lyrics\\": \\"...\\"\\n}`;
const text = `Booth request: ${data.request_id}\\nCustomer email: ${data.email}\\nCallback URL: ${callbackUrl}\\n\\nThis is REVISION #${revisionCount || 1} for this customer.\\n\\nGenerate a NEW AI music prompt that addresses the following revision request, while keeping the same overall theme/persona and matching the customer's original brief as closely as possible.\\n\\nOriginal brief:\\nName: ${data.name}\\nPronouns: ${data.pronouns || '-'}\\nHobbies: ${data.hobbies || '-'}\\nNotable facts: ${data.notable_facts || '-'}\\nStyle / genre: ${styleSentence || styleGenre || '-'}\\nPreferred singer voice / gender: ${data.vocal_gender || 'No preference'}\\nExtra requests: ${data.extra_requests || '-'}\\nInterested in STEMS: ${data.stems_interest ? 'Yes' : 'No'}\\n\\nPreviously generated prompt (do not copy verbatim; adapt and improve):\\nTitle: ${currentTitle || '-'}\\nStyle: ${currentStyle || '-'}\\nLyrics:\\n${currentLyrics || '-'}\\n\\nRevision request from customer:\\n${revisionNote || '-'}\\n\\nPOST the new prompt back to the Callback URL as JSON with this shape:\\n{\\n \\"request_id\\": ${data.request_id},\\n \\"email\\": \\"${data.email}\\",\\n \\"suno_title\\": \\"...\\",\\n \\"suno_style\\": \\"...\\",\\n \\"suno_lyrics\\": \\"...\\"\\n}`;
navigator.clipboard.writeText(text).then(() => alert("Revision prompt copied! Paste into Hermes. Hermes will POST the revised prompt back to the Callback URL."));
}
</script>