File size: 4,162 Bytes
7224c63
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
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