Add DJ curation metadata, public auto-play radio, and Lyria web controls.

Extensive per-track meta feeds DeepSeek planning. Caravan of the Night kept with electric guitar marked disliked. Sahara Saz remains gold standard. Gateway index.html auto-plays on tinqs.com.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-07 15:20:10 +01:00
parent 4b2003866d
commit b2aad43a44
25 changed files with 1869 additions and 75 deletions
+156
View File
@@ -0,0 +1,156 @@
"""Lyria 3 capabilities — synced from Google Gemini API docs + live model list."""
from __future__ import annotations
from dataclasses import dataclass
from google import genai
from google.genai import errors as genai_errors
DOCS_URL = "https://ai.google.dev/gemini-api/docs/music-generation"
VOCAL_MODES = [
{
"id": "instrumental",
"label": "Instrumental only",
"hint": "Lyria tip: include 'Instrumental only, no vocals' in every prompt.",
},
{
"id": "mix",
"label": "Mix (textures + chants)",
"hint": "Wordless vocals, Sahel chants, whispers — no full lead lyrics unless chat asks.",
},
{
"id": "vocals",
"label": "Full vocals",
"hint": "Lead vocals and lyrics; language follows prompt or Lyria language setting.",
},
]
LANGUAGES = [
{"id": "auto", "label": "Auto (match prompt / taste)"},
{"id": "en", "label": "English"},
{"id": "hi", "label": "Hindi"},
{"id": "tr", "label": "Turkish"},
{"id": "fr", "label": "French"},
{"id": "ar", "label": "Arabic"},
{"id": "es", "label": "Spanish"},
{"id": "pt", "label": "Portuguese"},
{"id": "de", "label": "German"},
]
SINGER_PROFILES = [
{"id": "", "label": "Default (model picks)"},
{"id": "male_baritone", "label": "Male baritone — warm, desert blues"},
{"id": "male_tenor", "label": "Male tenor — bright, energetic"},
{"id": "female_alto", "label": "Female alto — smoky, soulful"},
{"id": "female_soprano", "label": "Female soprano — airy, soaring"},
{"id": "weathered_rocker", "label": "Weathered rocker — raspy 90s grit"},
]
OUTPUT_FORMATS = [
{"id": "mp3", "label": "MP3 (default)"},
{"id": "wav", "label": "WAV (Pro only, larger files)"},
]
STATIC_MODELS = [
{
"id": "lyria-3-pro-preview",
"label": "Lyria 3 Pro",
"duration": "12 minutes",
"cost_key": "lyria_pro_usd",
"supports_wav": True,
"supports_timestamps": True,
"supports_image_input": True,
},
{
"id": "lyria-3-clip-preview",
"label": "Lyria 3 Clip",
"duration": "30 seconds",
"cost_key": "lyria_clip_usd",
"supports_wav": False,
"supports_timestamps": False,
"supports_image_input": True,
},
]
@dataclass
class LyriaApiStatus:
ok: bool
message: str
models_available: list[str]
key_configured: bool
def static_capabilities() -> dict:
return {
"docs_url": DOCS_URL,
"models": STATIC_MODELS,
"vocal_modes": VOCAL_MODES,
"languages": LANGUAGES,
"singer_profiles": SINGER_PROFILES,
"output_formats": OUTPUT_FORMATS,
"features": [
"Text-to-music via generateContent",
"Vocals + timed lyrics (Pro)",
"Instrumental-only prompts",
"Custom lyrics with [Verse]/[Chorus] tags",
"Timestamp structure [0:00 - 0:30]",
"Prompt language steers lyric language",
"Image-to-music (multimodal)",
"44.1 kHz stereo MP3 or WAV (Pro)",
],
"prompt_tips": [
"Iterate with Clip (30s) before burning a Pro generation.",
"Be specific: BPM, key, instruments, mood, structure.",
"For instrumentals, always say 'Instrumental only, no vocals'.",
"For vocals, describe singer gender, timbre, and range.",
"Avoid 'vocal sample' / 'griot sample' — use 'Sahel blues chants'.",
],
}
def probe_lyria_api(api_key: str | None) -> dict:
if not api_key:
return LyriaApiStatus(
ok=False,
message="GEMINI_API_KEY not set",
models_available=[],
key_configured=False,
).__dict__
try:
client = genai.Client(api_key=api_key)
found: list[str] = []
for model in client.models.list():
name = (getattr(model, "name", "") or "").replace("models/", "")
if "lyria" in name.lower():
found.append(name)
if not found:
return LyriaApiStatus(
ok=False,
message="API key works but no Lyria models visible on this project",
models_available=[],
key_configured=True,
).__dict__
return LyriaApiStatus(
ok=True,
message=f"Lyria ready — {len(found)} model(s) available",
models_available=sorted(found),
key_configured=True,
).__dict__
except genai_errors.ClientError as exc:
return LyriaApiStatus(
ok=False,
message=f"Gemini API error: {exc}",
models_available=[],
key_configured=True,
).__dict__
except Exception as exc:
return LyriaApiStatus(
ok=False,
message=f"Probe failed: {exc}",
models_available=[],
key_configured=True,
).__dict__