Group genre dropdowns into non-selectable optgroup sections
- Load grouped genre list (music_genres_grouped.txt) with section headings - Render headings as <optgroup> labels (visible, not selectable) - Add Disney under CHILDREN'S & HOLIDAY (was missing from grouped file) - Bump version to 0.9.3
This commit is contained in:
parent
07e92a522a
commit
868b59077f
7 changed files with 360 additions and 21 deletions
|
|
@ -1,6 +1,6 @@
|
||||||
# Theme Song Booth (MusicGPT Edition)
|
# Theme Song Booth (MusicGPT Edition)
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
|
|
@ -42,7 +42,7 @@ The MusicGPT fork automates song generation: instead of manually creating songs
|
||||||
|
|
||||||
| Page | Path | Purpose |
|
| 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. |
|
| 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_grouped.txt` (a grouped file where section headings are rendered as non-selectable dropdown groups). 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. |
|
| 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. |
|
| 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. |
|
| Order status | `/status` | Customers enter their email to see all their requests and statuses. |
|
||||||
|
|
|
||||||
2
VERSION
2
VERSION
|
|
@ -1 +1 @@
|
||||||
0.9.2
|
0.9.3
|
||||||
|
|
|
||||||
14
app.py
14
app.py
|
|
@ -60,7 +60,7 @@ from models import SCHEMA, get_requests_by_email, log_revision, list_revision_hi
|
||||||
|
|
||||||
# Helpers (extracted from app.py to keep the route file manageable)
|
# Helpers (extracted from app.py to keep the route file manageable)
|
||||||
from helpers import (
|
from helpers import (
|
||||||
MUSIC_GENRES, DECADES, parse_style_genre, build_style_genre,
|
GENRE_GROUPS, DECADES, parse_style_genre, build_style_genre,
|
||||||
is_admin, require_admin, admin_password_ok, is_valid_email,
|
is_admin, require_admin, admin_password_ok, is_valid_email,
|
||||||
allowed_file, upload_path, save_upload,
|
allowed_file, upload_path, save_upload,
|
||||||
apply_mp3_tags,
|
apply_mp3_tags,
|
||||||
|
|
@ -186,13 +186,13 @@ def request_form():
|
||||||
pronouns = request.form.get('pronouns', '').strip()
|
pronouns = request.form.get('pronouns', '').strip()
|
||||||
if not pronouns:
|
if not pronouns:
|
||||||
flash('Please select your pronouns.', 'error')
|
flash('Please select your pronouns.', 'error')
|
||||||
return render_template('request.html', form=request.form, decades=DECADES, genres=MUSIC_GENRES), 400
|
return render_template('request.html', form=request.form, decades=DECADES, genre_groups=GENRE_GROUPS), 400
|
||||||
if not decade:
|
if not decade:
|
||||||
flash('Please select a decade.', 'error')
|
flash('Please select a decade.', 'error')
|
||||||
return render_template('request.html', form=request.form, decades=DECADES, genres=MUSIC_GENRES), 400
|
return render_template('request.html', form=request.form, decades=DECADES, genre_groups=GENRE_GROUPS), 400
|
||||||
if not basic_style:
|
if not basic_style:
|
||||||
flash('Please select a basic style.', 'error')
|
flash('Please select a basic style.', 'error')
|
||||||
return render_template('request.html', form=request.form, decades=DECADES, genres=MUSIC_GENRES), 400
|
return render_template('request.html', form=request.form, decades=DECADES, genre_groups=GENRE_GROUPS), 400
|
||||||
form_data = {
|
form_data = {
|
||||||
'name': request.form.get('name', '').strip(),
|
'name': request.form.get('name', '').strip(),
|
||||||
'email': request.form.get('email', '').strip().lower(),
|
'email': request.form.get('email', '').strip().lower(),
|
||||||
|
|
@ -206,7 +206,7 @@ def request_form():
|
||||||
}
|
}
|
||||||
if not is_valid_email(form_data['email']):
|
if not is_valid_email(form_data['email']):
|
||||||
flash('Please enter a valid email address.', 'error')
|
flash('Please enter a valid email address.', 'error')
|
||||||
return render_template('request.html', form=form_data, decades=DECADES, genres=MUSIC_GENRES), 400
|
return render_template('request.html', form=form_data, decades=DECADES, genre_groups=GENRE_GROUPS), 400
|
||||||
rid = create_request(**form_data)
|
rid = create_request(**form_data)
|
||||||
|
|
||||||
# Notify operator via ntfy when a new request comes in.
|
# Notify operator via ntfy when a new request comes in.
|
||||||
|
|
@ -251,7 +251,7 @@ def request_form():
|
||||||
|
|
||||||
flash('Your request has been submitted! Check your email soon.', 'success')
|
flash('Your request has been submitted! Check your email soon.', 'success')
|
||||||
return redirect(url_for('thanks', rid=rid))
|
return redirect(url_for('thanks', rid=rid))
|
||||||
return render_template('request.html', form=None, decades=DECADES, genres=MUSIC_GENRES)
|
return render_template('request.html', form=None, decades=DECADES, genre_groups=GENRE_GROUPS)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/ping', methods=['GET'])
|
@app.route('/api/ping', methods=['GET'])
|
||||||
|
|
@ -1521,7 +1521,7 @@ def admin_request(rid):
|
||||||
req=req,
|
req=req,
|
||||||
style_parts=parse_style_genre(req.get('style_genre')),
|
style_parts=parse_style_genre(req.get('style_genre')),
|
||||||
decades=DECADES,
|
decades=DECADES,
|
||||||
genres=MUSIC_GENRES,
|
genre_groups=GENRE_GROUPS,
|
||||||
statuses=STATUS_LABELS,
|
statuses=STATUS_LABELS,
|
||||||
file_exists=file_exists,
|
file_exists=file_exists,
|
||||||
basename=basename,
|
basename=basename,
|
||||||
|
|
|
||||||
35
helpers.py
35
helpers.py
|
|
@ -50,9 +50,9 @@ from config import Config
|
||||||
# Genre / decade helpers
|
# Genre / decade helpers
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
_GENRES_PATH = Path('/mnt/Storage/Music Genres.txt')
|
_GENRES_PATH = Path('/mnt/Storage/music_genres_grouped.txt')
|
||||||
_DECADES_PATH = Path('/mnt/Storage/Decades.txt')
|
_DECADES_PATH = Path('/mnt/Storage/Decades.txt')
|
||||||
_FALLBACK_GENRES_PATH = Path(__file__).parent / 'lists' / 'music_genres.txt'
|
_FALLBACK_GENRES_PATH = Path(__file__).parent / 'lists' / 'music_genres_grouped.txt'
|
||||||
_FALLBACK_DECADES_PATH = Path(__file__).parent / 'lists' / 'decades.txt'
|
_FALLBACK_DECADES_PATH = Path(__file__).parent / 'lists' / 'decades.txt'
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -72,9 +72,38 @@ def _load_list(primary: Path, fallback: Path) -> list[str]:
|
||||||
return _load_lines(fallback)
|
return _load_lines(fallback)
|
||||||
|
|
||||||
|
|
||||||
MUSIC_GENRES = _load_list(_GENRES_PATH, _FALLBACK_GENRES_PATH)
|
def _load_genre_groups(primary: Path, fallback: Path) -> list[dict]:
|
||||||
|
"""Parse a grouped genre file into [{'label': str, 'genres': [str, ...]}, ...].
|
||||||
|
|
||||||
|
A blank line ends the current group; the first line after a blank line (or
|
||||||
|
the very first line) is the group heading, and every following line until
|
||||||
|
the next blank line is a genre under that heading. Headings are rendered as
|
||||||
|
non-selectable <optgroup> labels, so they are visible but not clickable.
|
||||||
|
"""
|
||||||
|
path = primary if primary.exists() else fallback
|
||||||
|
if not path.exists():
|
||||||
|
return []
|
||||||
|
groups = []
|
||||||
|
current = None
|
||||||
|
for raw in path.read_text(encoding='utf-8').splitlines():
|
||||||
|
line = raw.strip()
|
||||||
|
if not line:
|
||||||
|
current = None
|
||||||
|
continue
|
||||||
|
if current is None:
|
||||||
|
current = {'label': line, 'genres': []}
|
||||||
|
groups.append(current)
|
||||||
|
else:
|
||||||
|
current['genres'].append(line)
|
||||||
|
return groups
|
||||||
|
|
||||||
|
|
||||||
|
GENRE_GROUPS = _load_genre_groups(_GENRES_PATH, _FALLBACK_GENRES_PATH)
|
||||||
DECADES = _load_list(_DECADES_PATH, _FALLBACK_DECADES_PATH)
|
DECADES = _load_list(_DECADES_PATH, _FALLBACK_DECADES_PATH)
|
||||||
|
|
||||||
|
# Flat list of every genre, derived from the grouped list so the two stay in sync.
|
||||||
|
MUSIC_GENRES = [g for group in GENRE_GROUPS for g in group['genres']]
|
||||||
|
|
||||||
|
|
||||||
def parse_style_genre(style_genre: str | None) -> dict:
|
def parse_style_genre(style_genre: str | None) -> dict:
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
294
lists/music_genres_grouped.txt
Executable file
294
lists/music_genres_grouped.txt
Executable file
|
|
@ -0,0 +1,294 @@
|
||||||
|
ROCK
|
||||||
|
Alternative Rock
|
||||||
|
Art Rock
|
||||||
|
Blues Rock
|
||||||
|
Britpop
|
||||||
|
Classic Rock
|
||||||
|
Folk Rock
|
||||||
|
Garage Rock
|
||||||
|
Glam Rock
|
||||||
|
Gothic Rock
|
||||||
|
Grunge
|
||||||
|
Hard Rock
|
||||||
|
Indie
|
||||||
|
Indie Rock
|
||||||
|
J-Rock
|
||||||
|
Krautrock
|
||||||
|
Math Rock
|
||||||
|
Neue Deutsche Welle
|
||||||
|
New Romantic
|
||||||
|
New Wave
|
||||||
|
Post-Rock
|
||||||
|
Power Pop
|
||||||
|
Progressive Rock
|
||||||
|
Psychedelic Rock
|
||||||
|
Rock
|
||||||
|
Rock & Roll
|
||||||
|
Rockabilly
|
||||||
|
Shoegaze
|
||||||
|
Soft Rock
|
||||||
|
Southern Rock
|
||||||
|
Space Rock
|
||||||
|
Stoner Rock
|
||||||
|
Surf Rock
|
||||||
|
Symphonic Rock
|
||||||
|
Trop Rock
|
||||||
|
Yacht Rock
|
||||||
|
|
||||||
|
METAL
|
||||||
|
Black Metal
|
||||||
|
Death Metal
|
||||||
|
Deathcore
|
||||||
|
Doom Metal
|
||||||
|
Folk Metal
|
||||||
|
Glam Metal
|
||||||
|
Gothic Metal
|
||||||
|
Heavy Metal
|
||||||
|
Industrial Metal
|
||||||
|
Metal
|
||||||
|
Metalcore
|
||||||
|
Nu Metal
|
||||||
|
Power Metal
|
||||||
|
Progressive Metal
|
||||||
|
Symphonic Metal
|
||||||
|
Thrash Metal
|
||||||
|
|
||||||
|
PUNK & EMO
|
||||||
|
Afropunk
|
||||||
|
Emo
|
||||||
|
Hardcore Punk
|
||||||
|
Midwest Emo
|
||||||
|
Pop Punk
|
||||||
|
Post-Hardcore
|
||||||
|
Post-Punk
|
||||||
|
Punk Rock
|
||||||
|
Screamo
|
||||||
|
Ska Punk
|
||||||
|
|
||||||
|
POP
|
||||||
|
Adult Contemporary
|
||||||
|
Art Pop
|
||||||
|
Ballad
|
||||||
|
Baroque Pop
|
||||||
|
Bedroom Pop
|
||||||
|
Bubblegum Pop
|
||||||
|
C-Pop
|
||||||
|
Cantopop
|
||||||
|
City Pop
|
||||||
|
Dance Pop
|
||||||
|
Dream Pop
|
||||||
|
Electropop
|
||||||
|
Folk Pop
|
||||||
|
Indie Pop
|
||||||
|
J-Pop
|
||||||
|
K-Pop
|
||||||
|
Latin Pop
|
||||||
|
Piano Ballad
|
||||||
|
Pop
|
||||||
|
Pop Rock
|
||||||
|
Power Ballad
|
||||||
|
Synthpop
|
||||||
|
|
||||||
|
HIP-HOP & RAP
|
||||||
|
Boom Bap
|
||||||
|
Cloud Rap
|
||||||
|
Conscious Hip-Hop
|
||||||
|
Crunk
|
||||||
|
Drill
|
||||||
|
G-Funk
|
||||||
|
Gangsta Rap
|
||||||
|
Grime
|
||||||
|
Hip-Hop
|
||||||
|
Latin Trap
|
||||||
|
Lo-Fi Hip-Hop
|
||||||
|
Miami Bass
|
||||||
|
Phonk
|
||||||
|
Trap
|
||||||
|
UK Drill
|
||||||
|
|
||||||
|
R&B & SOUL
|
||||||
|
Alternative R&B
|
||||||
|
Doo-Wop
|
||||||
|
Funk
|
||||||
|
Motown
|
||||||
|
Neo-Soul
|
||||||
|
New Jack Swing
|
||||||
|
R&B
|
||||||
|
Slow Jam
|
||||||
|
Soul
|
||||||
|
|
||||||
|
ELECTRONIC & DANCE
|
||||||
|
Acid House
|
||||||
|
Afro House
|
||||||
|
Amapiano
|
||||||
|
Ambient
|
||||||
|
Bass Music
|
||||||
|
Big Beat
|
||||||
|
Big Room House
|
||||||
|
Breakbeat
|
||||||
|
Chicago House
|
||||||
|
Chillout
|
||||||
|
Chillwave
|
||||||
|
Chiptune
|
||||||
|
Dance
|
||||||
|
Dark Ambient
|
||||||
|
Darkwave
|
||||||
|
Deep House
|
||||||
|
Detroit Techno
|
||||||
|
Disco
|
||||||
|
Downtempo
|
||||||
|
Drum & Bass
|
||||||
|
Dubstep
|
||||||
|
EBM
|
||||||
|
Electro
|
||||||
|
Electro House
|
||||||
|
Electroclash
|
||||||
|
Electronic
|
||||||
|
Eurobeat
|
||||||
|
Eurodance
|
||||||
|
Future Bass
|
||||||
|
Future Funk
|
||||||
|
Goa Trance
|
||||||
|
Happy Hardcore
|
||||||
|
Hardstyle
|
||||||
|
House
|
||||||
|
Hyperpop
|
||||||
|
IDM
|
||||||
|
Industrial
|
||||||
|
Italo Disco
|
||||||
|
Jungle
|
||||||
|
Lo-Fi
|
||||||
|
Melodic Techno
|
||||||
|
Minimal Techno
|
||||||
|
Moombahton
|
||||||
|
New Age
|
||||||
|
Nu-Disco
|
||||||
|
Progressive House
|
||||||
|
Progressive Trance
|
||||||
|
Psybient
|
||||||
|
Psytrance
|
||||||
|
Synthwave
|
||||||
|
Tech House
|
||||||
|
Techno
|
||||||
|
Trance
|
||||||
|
Trip-Hop
|
||||||
|
Tropical House
|
||||||
|
UK Garage
|
||||||
|
Uplifting Trance
|
||||||
|
Vaporwave
|
||||||
|
|
||||||
|
JAZZ
|
||||||
|
Acid Jazz
|
||||||
|
Bebop
|
||||||
|
Big Band
|
||||||
|
Cool Jazz
|
||||||
|
Dixieland
|
||||||
|
Free Jazz
|
||||||
|
Jam Band
|
||||||
|
Jazz
|
||||||
|
Jazz Funk
|
||||||
|
Jazz Fusion
|
||||||
|
Latin Jazz
|
||||||
|
Ragtime
|
||||||
|
Smooth Jazz
|
||||||
|
Swing
|
||||||
|
|
||||||
|
BLUES
|
||||||
|
Blues
|
||||||
|
|
||||||
|
FOLK, COUNTRY & AMERICANA
|
||||||
|
Acoustic
|
||||||
|
Alt-Country
|
||||||
|
Americana
|
||||||
|
Bluegrass
|
||||||
|
Celtic
|
||||||
|
Country
|
||||||
|
Country Pop
|
||||||
|
Country Rock
|
||||||
|
Folk
|
||||||
|
Honky Tonk
|
||||||
|
Indie Folk
|
||||||
|
Irish Folk
|
||||||
|
Outlaw Country
|
||||||
|
Sea Shanty
|
||||||
|
Singer-Songwriter
|
||||||
|
Western Swing
|
||||||
|
Zydeco
|
||||||
|
|
||||||
|
WORLD & REGIONAL
|
||||||
|
Afrobeat
|
||||||
|
Afrobeats
|
||||||
|
Bhangra
|
||||||
|
Bollywood
|
||||||
|
Highlife
|
||||||
|
Klezmer
|
||||||
|
Native American
|
||||||
|
Polka
|
||||||
|
Qawwali
|
||||||
|
World Music
|
||||||
|
|
||||||
|
LATIN
|
||||||
|
Bachata
|
||||||
|
Bossa Nova
|
||||||
|
Corrido
|
||||||
|
Cumbia
|
||||||
|
Flamenco
|
||||||
|
Latin
|
||||||
|
Mariachi
|
||||||
|
Merengue
|
||||||
|
Norteño
|
||||||
|
Reggaeton
|
||||||
|
Regional Mexican
|
||||||
|
Salsa
|
||||||
|
Samba
|
||||||
|
Tango
|
||||||
|
Tejano
|
||||||
|
|
||||||
|
REGGAE & CARIBBEAN
|
||||||
|
Calypso
|
||||||
|
Dancehall
|
||||||
|
Dub
|
||||||
|
Reggae
|
||||||
|
Roots Reggae
|
||||||
|
Ska
|
||||||
|
Soca
|
||||||
|
|
||||||
|
CLASSICAL & ORCHESTRAL
|
||||||
|
Baroque
|
||||||
|
Chamber Music
|
||||||
|
Classical
|
||||||
|
Neoclassical
|
||||||
|
Opera
|
||||||
|
Orchestral
|
||||||
|
|
||||||
|
CHRISTIAN & GOSPEL
|
||||||
|
Christian Hip-Hop
|
||||||
|
Christian Rock
|
||||||
|
Contemporary Christian
|
||||||
|
Gospel
|
||||||
|
Worship
|
||||||
|
|
||||||
|
SOUNDTRACK, CINEMATIC & THEATRE
|
||||||
|
Anime
|
||||||
|
Cabaret
|
||||||
|
Cinematic
|
||||||
|
Musical Theatre
|
||||||
|
Soundtrack
|
||||||
|
|
||||||
|
CHILDREN'S & HOLIDAY
|
||||||
|
Children's Music
|
||||||
|
Christmas
|
||||||
|
Disney
|
||||||
|
Lullaby
|
||||||
|
|
||||||
|
EASY LISTENING & TRADITIONAL VOCAL
|
||||||
|
A Cappella
|
||||||
|
Barbershop
|
||||||
|
Chanson
|
||||||
|
Easy Listening
|
||||||
|
Lounge
|
||||||
|
|
||||||
|
OTHER / EXPERIMENTAL
|
||||||
|
Avant-Garde
|
||||||
|
Experimental
|
||||||
|
Noise
|
||||||
|
|
@ -133,17 +133,25 @@
|
||||||
<label for="customer_style_genre_basic">Basic style <span class="required">*</span></label>
|
<label for="customer_style_genre_basic">Basic style <span class="required">*</span></label>
|
||||||
<select id="customer_style_genre_basic" name="basic_style" required>
|
<select id="customer_style_genre_basic" name="basic_style" required>
|
||||||
<option value="" {% if not style_parts.basic_style %}selected{% endif %}>— Select style —</option>
|
<option value="" {% if not style_parts.basic_style %}selected{% endif %}>— Select style —</option>
|
||||||
{% for g in genres %}
|
{% for group in genre_groups %}
|
||||||
|
<optgroup label="{{ group.label }}">
|
||||||
|
{% for g in group.genres %}
|
||||||
<option value="{{ g }}" {% if style_parts.basic_style == g %}selected{% endif %}>{{ g }}</option>
|
<option value="{{ g }}" {% if style_parts.basic_style == g %}selected{% endif %}>{{ g }}</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
</optgroup>
|
||||||
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<label for="customer_style_genre_additional">Additional style (optional)</label>
|
<label for="customer_style_genre_additional">Additional style (optional)</label>
|
||||||
<select id="customer_style_genre_additional" name="additional_style">
|
<select id="customer_style_genre_additional" name="additional_style">
|
||||||
<option value="" {% if not style_parts.additional_style %}selected{% endif %}>— None —</option>
|
<option value="" {% if not style_parts.additional_style %}selected{% endif %}>— None —</option>
|
||||||
{% for g in genres %}
|
{% for group in genre_groups %}
|
||||||
|
<optgroup label="{{ group.label }}">
|
||||||
|
{% for g in group.genres %}
|
||||||
<option value="{{ g }}" {% if style_parts.additional_style == g %}selected{% endif %}>{{ g }}</option>
|
<option value="{{ g }}" {% if style_parts.additional_style == g %}selected{% endif %}>{{ g }}</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
</optgroup>
|
||||||
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<p class="copy-hint">Stored style: {{ req.style_genre or '—' }}</p>
|
<p class="copy-hint">Stored style: {{ req.style_genre or '—' }}</p>
|
||||||
|
|
|
||||||
|
|
@ -62,17 +62,25 @@
|
||||||
<label for="basic_style">Basic style <span class="required">*</span></label>
|
<label for="basic_style">Basic style <span class="required">*</span></label>
|
||||||
<select id="basic_style" name="basic_style" required>
|
<select id="basic_style" name="basic_style" required>
|
||||||
<option value="" {% if not form or not form.basic_style %}selected{% endif %}>— Select style —</option>
|
<option value="" {% if not form or not form.basic_style %}selected{% endif %}>— Select style —</option>
|
||||||
{% for g in genres %}
|
{% for group in genre_groups %}
|
||||||
|
<optgroup label="{{ group.label }}">
|
||||||
|
{% for g in group.genres %}
|
||||||
<option value="{{ g }}" {% if form and form.basic_style == g %}selected{% endif %}>{{ g }}</option>
|
<option value="{{ g }}" {% if form and form.basic_style == g %}selected{% endif %}>{{ g }}</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
</optgroup>
|
||||||
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<label for="additional_style">Additional style (optional)</label>
|
<label for="additional_style">Additional style (optional)</label>
|
||||||
<select id="additional_style" name="additional_style">
|
<select id="additional_style" name="additional_style">
|
||||||
<option value="" {% if not form or not form.additional_style %}selected{% endif %}>— None —</option>
|
<option value="" {% if not form or not form.additional_style %}selected{% endif %}>— None —</option>
|
||||||
{% for g in genres %}
|
{% for group in genre_groups %}
|
||||||
|
<optgroup label="{{ group.label }}">
|
||||||
|
{% for g in group.genres %}
|
||||||
<option value="{{ g }}" {% if form and form.additional_style == g %}selected{% endif %}>{{ g }}</option>
|
<option value="{{ g }}" {% if form and form.additional_style == g %}selected{% endif %}>{{ g }}</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
</optgroup>
|
||||||
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<p class="copy-hint">Selected style: <span id="style-preview">{% if form %}{{ form.decade or '' }}{% if form.decade and form.basic_style %}, {% endif %}{{ form.basic_style or '' }}{% if form.basic_style and form.additional_style %}, {% endif %}{{ form.additional_style or '' }}{% else %}—{% endif %}</span></p>
|
<p class="copy-hint">Selected style: <span id="style-preview">{% if form %}{{ form.decade or '' }}{% if form.decade and form.basic_style %}, {% endif %}{{ form.basic_style or '' }}{% if form.basic_style and form.additional_style %}, {% endif %}{{ form.additional_style or '' }}{% else %}—{% endif %}</span></p>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue