Replace free-text genre field with structured style dropdowns
The customer request form previously had an open 'Style / genre / mood' text field, which produced inconsistent genre descriptions and confused Suno. Replace it with three dropdowns: - Decade / era (required) — loaded from /mnt/Storage/Decades.txt - Basic style (required) — loaded from /mnt/Storage/Music Genres.txt - Additional style (optional) — same genre list The combined value is stored in the existing style_genre column as a comma-separated string (e.g. '1980's, Pop, Funk'), so no schema change is required. The admin request page exposes 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'. The canonical lists live in /mnt/Storage; bundled copies under lists/ act as a fallback when that mount is unavailable in the container. Bumps version to 0.4.6 and updates README.
This commit is contained in:
parent
d79c58691a
commit
10ee182558
7 changed files with 381 additions and 16 deletions
|
|
@ -182,8 +182,31 @@
|
|||
<label for="customer_notable_facts">Notable things</label>
|
||||
<textarea id="customer_notable_facts" name="notable_facts" rows="3">{{ req.notable_facts or '' }}</textarea>
|
||||
|
||||
<label for="customer_style_genre">Style / genre / mood</label>
|
||||
<input type="text" id="customer_style_genre" name="style_genre" value="{{ req.style_genre or '' }}">
|
||||
<label for="customer_style_genre_decade">Decade / era <span style="color:#f87171">*</span></label>
|
||||
<select id="customer_style_genre_decade" name="decade" required>
|
||||
<option value="" {% if not style_parts.decade %}selected{% endif %}>— Select decade —</option>
|
||||
{% for d in decades %}
|
||||
<option value="{{ d }}" {% if style_parts.decade == d %}selected{% endif %}>{{ d }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
|
||||
<label for="customer_style_genre_basic">Basic style <span style="color:#f87171">*</span></label>
|
||||
<select id="customer_style_genre_basic" name="basic_style" required>
|
||||
<option value="" {% if not style_parts.basic_style %}selected{% endif %}>— Select style —</option>
|
||||
{% for g in genres %}
|
||||
<option value="{{ g }}" {% if style_parts.basic_style == g %}selected{% endif %}>{{ g }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
|
||||
<label for="customer_style_genre_additional">Additional style (optional)</label>
|
||||
<select id="customer_style_genre_additional" name="additional_style">
|
||||
<option value="" {% if not style_parts.additional_style %}selected{% endif %}>— None —</option>
|
||||
{% for g in genres %}
|
||||
<option value="{{ g }}" {% if style_parts.additional_style == g %}selected{% endif %}>{{ g }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
|
||||
<p style="font-size:.85rem;color:#9ca3af;margin-top:.3rem">Stored style: {{ req.style_genre or '—' }}</p>
|
||||
|
||||
<label for="customer_vocal_gender">Preferred singer voice / gender</label>
|
||||
<input type="text" id="customer_vocal_gender" name="vocal_gender" value="{{ req.vocal_gender or '' }}">
|
||||
|
|
@ -407,17 +430,23 @@
|
|||
<script>
|
||||
function copyPromptForHermes() {
|
||||
const callbackUrl = {{ callback_url | tojson }};
|
||||
const styleGenre = {{ req.style_genre | tojson }};
|
||||
const styleParts = styleGenre ? styleGenre.split(', ') : [];
|
||||
const decade = styleParts[0] || '';
|
||||
const basic = styleParts[1] || '';
|
||||
const additional = styleParts.slice(2).join(', ') || '';
|
||||
const styleSentence = [decade && `${decade}-era`, basic, additional && `with ${additional} influences`].filter(Boolean).join(' ');
|
||||
const data = {
|
||||
request_id: {{ req.id | tojson }},
|
||||
email: {{ req.email | tojson }},
|
||||
name: {{ req.name | tojson }},
|
||||
hobbies: {{ req.hobbies | tojson }},
|
||||
notable_facts: {{ req.notable_facts | tojson }},
|
||||
style_genre: {{ req.style_genre | tojson }},
|
||||
style_genre: styleGenre,
|
||||
vocal_gender: {{ req.vocal_gender | tojson }},
|
||||
extra_requests: {{ req.extra_requests | 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}\\nHobbies: ${data.hobbies || '-'}\\nNotable facts: ${data.notable_facts || '-'}\\nStyle / genre: ${data.style_genre || '-'}\\nPreferred singer voice / gender: ${data.vocal_gender || 'No preference'}\\nExtra requests: ${data.extra_requests || '-'}\\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 a Suno Custom Mode prompt for this customer and POST it back to the Callback URL as JSON.\\n\\nCustomer data:\\nName: ${data.name}\\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 || '-'}\\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."));
|
||||
}
|
||||
|
||||
|
|
@ -435,17 +464,23 @@
|
|||
const currentLyrics = {{ req.suno_lyrics | tojson }};
|
||||
const revisionNote = {{ req.revision_note | tojson }};
|
||||
const revisionCount = {{ req.revision_count | tojson }};
|
||||
const styleGenre = {{ req.style_genre | tojson }};
|
||||
const styleParts = styleGenre ? styleGenre.split(', ') : [];
|
||||
const decade = styleParts[0] || '';
|
||||
const basic = styleParts[1] || '';
|
||||
const additional = styleParts.slice(2).join(', ') || '';
|
||||
const styleSentence = [decade && `${decade}-era`, basic, additional && `with ${additional} influences`].filter(Boolean).join(' ');
|
||||
const data = {
|
||||
request_id: {{ req.id | tojson }},
|
||||
email: {{ req.email | tojson }},
|
||||
name: {{ req.name | tojson }},
|
||||
hobbies: {{ req.hobbies | tojson }},
|
||||
notable_facts: {{ req.notable_facts | tojson }},
|
||||
style_genre: {{ req.style_genre | tojson }},
|
||||
style_genre: styleGenre,
|
||||
vocal_gender: {{ req.vocal_gender | tojson }},
|
||||
extra_requests: {{ req.extra_requests | 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}\\nHobbies: ${data.hobbies || '-'}\\nNotable facts: ${data.notable_facts || '-'}\\nStyle / genre: ${data.style_genre || '-'}\\nPreferred singer voice / gender: ${data.vocal_gender || 'No preference'}\\nExtra requests: ${data.extra_requests || '-'}\\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 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}\\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 || '-'}\\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>
|
||||
|
|
|
|||
Reference in a new issue