from __future__ import annotations import json from pathlib import Path from typing import Any DATA_DIR = Path("data") FALLBACK_ERAS = { "eras": [ { "id": "1980s", "label": "1980s", "display_name": "1980s: Synth-pop", "summary": "Bright synths, drum machines, and glossy nostalgia.", "genres": [ { "id": "synth_pop", "label": "Synth-pop", "safe_prompt_style": "bright analog synth chords, electronic drums, pulsing bassline", "bpm_range": [95, 130], "instruments": ["analog synth", "drum machine"], "sonic_traits": ["glossy", "bright"], "avoid": "Do not imitate specific songs or artists.", } ], } ] } FALLBACK_PERSONAS = { "dj_personas": [ { "id": "neon_signal_host", "name": "The Neon Signal Host", "description": "A fictional cyber-radio host.", "voice_direction": "cool, futuristic, precise", "intro_template": "Signal detected. We are translating {source_era} {source_genre} through {remix_era} {remix_genre}.", "best_for": ["electronic"], "avoid": "Do not imitate real people.", } ] } FALLBACK_TEXTURES = { "audio_textures": [ { "id": "clean_digital", "label": "Clean digital master", "description": "Clean, bright audio.", "prompt_terms": ["clean digital master", "wide stereo"], "post_processing_hint": "Normalize only.", } ] } def load_json(path: str | Path, fallback: dict[str, Any] | None = None) -> dict[str, Any]: try: return json.loads(Path(path).read_text(encoding="utf-8")) except Exception: return fallback or {} def load_music_eras(path: str = "data/music_eras.json") -> dict[str, Any]: return load_json(path, FALLBACK_ERAS) def load_dj_personas(path: str = "data/dj_personas.json") -> dict[str, Any]: return load_json(path, FALLBACK_PERSONAS) def load_audio_textures(path: str = "data/audio_textures.json") -> dict[str, Any]: return load_json(path, FALLBACK_TEXTURES) def load_languages(path: str = "data/languages.json") -> dict[str, Any]: return load_json(path, {"languages": []}) def load_lyric_themes(path: str = "data/lyric_themes.json") -> dict[str, Any]: return load_json(path, {"themes": []}) def get_era_by_id(eras: list[dict[str, Any]], era_id: str) -> dict[str, Any]: return next((era for era in eras if era.get("id") == era_id), eras[0]) def get_genres_for_era(era: dict[str, Any]) -> list[dict[str, Any]]: return era.get("genres") or [] def get_genre_by_id(era: dict[str, Any], genre_id: str) -> dict[str, Any]: genres = get_genres_for_era(era) return next((genre for genre in genres if genre.get("id") == genre_id), genres[0]) def get_persona_by_id(personas: list[dict[str, Any]], persona_id: str) -> dict[str, Any]: return next((persona for persona in personas if persona.get("id") == persona_id), personas[0]) def get_texture_by_id(textures: list[dict[str, Any]], texture_id: str) -> dict[str, Any]: return next((texture for texture in textures if texture.get("id") == texture_id), textures[-1]) def get_language_by_id(languages: list[dict[str, Any]], language_id: str) -> dict[str, Any]: return next((language for language in languages if language.get("id") == language_id), languages[0]) def get_theme_by_id(themes: list[dict[str, Any]], theme_id: str) -> dict[str, Any]: return next((theme for theme in themes if theme.get("id") == theme_id), themes[0]) def make_label_id_maps(items: list[dict[str, Any]], label_key: str = "label") -> tuple[dict[str, str], dict[str, str]]: label_to_id = {} id_to_label = {} for item in items: item_id = item.get("id", "") label = item.get(label_key) or item.get("name") or item_id label_to_id[label] = item_id id_to_label[item_id] = label return label_to_id, id_to_label