Integrate MusicGPT API generation into booth-musicgpt v0.7.0
This commit is contained in:
parent
99855b1212
commit
2ca148be9b
12 changed files with 721 additions and 44 deletions
|
|
@ -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 & 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>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue