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:
Troll (Hermes Agent) 2026-09-20 16:11:42 +00:00
parent 07e92a522a
commit 868b59077f
7 changed files with 360 additions and 21 deletions

View file

@ -50,9 +50,9 @@ from config import Config
# 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')
_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'
@ -72,9 +72,38 @@ def _load_list(primary: Path, fallback: Path) -> list[str]:
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)
# 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:
"""