Define techno-ethnic taste lane and notify when generation is ready.

Bonobo, Jamaica dub, Sahara, Mongolia overtone, and Urdu colour in settings and DJ prompts. Generate runs in background with polling, ready toast, optional browser notification, and autoplay of the new track.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-07 16:27:07 +01:00
parent e15b5e9f98
commit 5f90945b97
9 changed files with 308 additions and 186 deletions
+94 -10
View File
@@ -299,7 +299,30 @@
line-height: 1.4; line-height: 1.4;
} }
.status.generating { color: var(--accent); } .status.generating { color: var(--accent); }
.status.ready-flash { color: #6bcf8e; font-weight: 600; }
.status.error { color: #ff5c7a; } .status.error { color: #ff5c7a; }
.gen-ready-toast {
position: fixed;
top: 1rem;
left: 50%;
transform: translateX(-50%);
z-index: 9999;
background: linear-gradient(135deg, #1e3a2f, #2a4a3a);
border: 1px solid #6bcf8e;
color: #e8fff0;
padding: 0.85rem 1.25rem;
border-radius: 12px;
font-size: 0.85rem;
font-weight: 600;
box-shadow: 0 8px 32px rgba(0,0,0,0.45);
animation: toast-in 0.35s ease;
max-width: 90vw;
text-align: center;
}
@keyframes toast-in {
from { opacity: 0; transform: translateX(-50%) translateY(-12px); }
to { opacity: 1; transform: translateX(-50%) translateY(0); }
}
.stack { .stack {
font-size: 0.7rem; font-size: 0.7rem;
color: #555568; color: #555568;
@@ -626,6 +649,7 @@
</div> </div>
<div class="status" id="status">Connecting…</div> <div class="status" id="status">Connecting…</div>
<div id="genReadyToast" class="gen-ready-toast" hidden></div>
<button type="button" class="booth-toggle" id="boothToggle">🎙 Vocal booth — record your layer (Chrome)</button> <button type="button" class="booth-toggle" id="boothToggle">🎙 Vocal booth — record your layer (Chrome)</button>
@@ -674,7 +698,10 @@
const moodEl = document.getElementById('mood'); const moodEl = document.getElementById('mood');
const djEl = document.getElementById('dj'); const djEl = document.getElementById('dj');
const statusEl = document.getElementById('status'); const statusEl = document.getElementById('status');
const genReadyToast = document.getElementById('genReadyToast');
const genBtn = document.getElementById('genBtn'); const genBtn = document.getElementById('genBtn');
let pollStartedAt = null;
let lastNotifiedCompletedAt = null;
const skipBtn = document.getElementById('skipBtn'); const skipBtn = document.getElementById('skipBtn');
const refreshBtn = document.getElementById('refreshBtn'); const refreshBtn = document.getElementById('refreshBtn');
const chatLog = document.getElementById('chatLog'); const chatLog = document.getElementById('chatLog');
@@ -978,8 +1005,11 @@
const costs = stats.costs || {}; const costs = stats.costs || {};
statSpent.textContent = fmtUsd(today.estimated_usd); statSpent.textContent = fmtUsd(today.estimated_usd);
statPerTrack.textContent = `~${fmtUsd(costs.per_track_estimate_usd)} / track`; statPerTrack.textContent = `~${fmtUsd(costs.per_track_estimate_usd)} / track`;
statGenerated.textContent = `${today.generated || 0} / ${today.max_per_day || 10}`; const unlimited = today.unlimited || today.max_per_day === 0;
statRemaining.textContent = `${today.remaining ?? 0} remaining`; statGenerated.textContent = unlimited
? `${today.generated || 0} (unlimited)`
: `${today.generated || 0} / ${today.max_per_day || 10}`;
statRemaining.textContent = unlimited ? '∞' : `${today.remaining ?? 0} remaining`;
statMaxBudget.textContent = fmtUsd(stats.projected_daily_max_usd); statMaxBudget.textContent = fmtUsd(stats.projected_daily_max_usd);
if (stats.playback) { if (stats.playback) {
radioSettings.playback = stats.playback; radioSettings.playback = stats.playback;
@@ -1161,22 +1191,63 @@
} }
let pollTimer = null; let pollTimer = null;
let toastTimer = null;
function showReadyToast(title) {
genReadyToast.textContent = `Ready · ${title}`;
genReadyToast.hidden = false;
clearTimeout(toastTimer);
toastTimer = setTimeout(() => { genReadyToast.hidden = true; }, 8000);
}
function notifyTrackReady(track, gen) {
const title = track?.title || gen?.last_completed_title || 'New track';
statusEl.textContent = `Ready · playing ${title}`;
statusEl.classList.remove('generating', 'error');
statusEl.classList.add('ready-flash');
setTimeout(() => statusEl.classList.remove('ready-flash'), 5000);
showReadyToast(title);
addBubble('dj', `Fresh cut ready: ${title} — on air now.`);
if (typeof Notification !== 'undefined' && Notification.permission === 'granted') {
new Notification('Live Ozan Radio', { body: `Ready: ${title}`, tag: 'track-ready' });
}
lastNotifiedCompletedAt = gen?.last_completed_at || null;
}
function pollForNewTrack() { function pollForNewTrack() {
let n = 0; let n = 0;
pollStartedAt = Date.now();
clearInterval(pollTimer); clearInterval(pollTimer);
pollTimer = setInterval(async () => { pollTimer = setInterval(async () => {
n += 1; n += 1;
const stats = await loadStats(true); const stats = await loadStats(true);
if (stats?.generation?.busy) return; const gen = stats?.generation;
if (stats?.generation?.error) { if (gen?.busy) return;
if (gen?.error) {
clearInterval(pollTimer);
statusEl.textContent = gen.error;
statusEl.classList.add('error');
addBubble('dj', `Couldn't finish that track: ${gen.error}`);
return;
}
const completedAt = gen?.last_completed_at;
const isNewCompletion = completedAt
&& completedAt !== lastNotifiedCompletedAt
&& (!pollStartedAt || new Date(completedAt).getTime() >= pollStartedAt - 2000);
await refreshNow();
const now = await fetch(`${API}/api/now`).then(r => r.json()).catch(() => null);
if (isNewCompletion && now?.track) {
applyTrack(now.track, 'generated');
notifyTrackReady(now.track, gen);
clearInterval(pollTimer); clearInterval(pollTimer);
return; return;
} }
await refreshNow();
const now = await fetch(`${API}/api/now`).then(r => r.json()).catch(() => null);
if (now?.track?.track_id !== currentTrackId) clearInterval(pollTimer); if (now?.track?.track_id !== currentTrackId) clearInterval(pollTimer);
if (n > 60) clearInterval(pollTimer); if (n > 90) {
}, 3000); clearInterval(pollTimer);
statusEl.textContent = 'Generation taking longer than usual — still polling…';
}
}, 2000);
} }
async function refreshNow() { async function refreshNow() {
@@ -1197,22 +1268,35 @@
} }
async function generate() { async function generate() {
if (typeof Notification !== 'undefined' && Notification.permission === 'default') {
Notification.requestPermission();
}
genBtn.disabled = true; genBtn.disabled = true;
statusEl.textContent = 'DeepSeek is planning… Lyria is composing…'; statusEl.textContent = 'Starting… DeepSeek plans, then Lyria composes (~3090s)';
statusEl.classList.add('generating');
try { try {
const res = await fetch(`${API}/api/generate`, { method: 'POST' }); const res = await fetch(`${API}/api/generate`, { method: 'POST' });
const data = await res.json(); const data = await res.json();
if (data.status === 'busy') { if (data.status === 'busy') {
statusEl.textContent = data.message; statusEl.textContent = data.message;
applyGenerationState(data.generation);
pollForNewTrack();
} else if (data.status === 'limit') { } else if (data.status === 'limit') {
statusEl.textContent = data.message || 'Daily limit reached'; statusEl.textContent = data.message || 'Daily limit reached';
statusEl.classList.remove('generating');
updateDashboard({ today: data.budget, costs: radioSettings.costs }); updateDashboard({ today: data.budget, costs: radioSettings.costs });
} else if (data.status === 'error') { } else if (data.status === 'error') {
statusEl.textContent = data.message || 'Generation failed'; statusEl.textContent = data.message || 'Generation failed';
statusEl.classList.add('error'); statusEl.classList.add('error');
applyGenerationState(data.generation || { error: data.message }); applyGenerationState(data.generation || { error: data.message });
} else if (data.status === 'accepted' || data.generating) {
applyGenerationState(data.generation || { busy: true, phase: 'planning' });
pollForNewTrack();
} else if (data.status === 'ok') { } else if (data.status === 'ok') {
if (data.track) applyTrack(data.track, data.source || 'generated'); if (data.track) {
applyTrack(data.track, data.source || 'generated');
notifyTrackReady(data.track, data.generation);
}
await loadStats(); await loadStats();
} else { } else {
statusEl.textContent = 'Generation failed — check server logs'; statusEl.textContent = 'Generation failed — check server logs';
Before
After
+38 -42
View File
@@ -1,71 +1,67 @@
{ {
"listener": "ozan", "listener": "ozan",
"profile_note": "Modern electronica meets ethnic fusion — Jon Hopkins Singularity, Karsh Kale, Transglobal Underground. Not Turkish folk. Updated 2026-06-07.", "profile_note": "Techno-ethnic station — Bonobo, Jon Hopkins, Jamaica dub, Sahel, Mongolia overtone vocals, Urdu colour. Beautiful cross-cultural blends. Not Turkish folk. Updated 2026-06-07.",
"station": "Live Ozan Radio", "station": "Live Ozan Radio",
"taste": { "taste": {
"summary": "Modern electronica meets ethnic fusion — Jon Hopkins Singularity lush synth builds, Karsh Kale tabla breaks, Transglobal dervish-trance. Electronic production first, world percussion and organic colour woven in. Not Turkish folk.", "summary": "Techno-ethnic — beautiful electronica meets world dub. Bonobo lush downtempo, Jon Hopkins Singularity builds, Jamaica spring-reverb dub, Sahara/Sahel warmth, Mongolian overtone throat textures, Urdu vocal colour, Karsh Kale tabla breaks. Many cultures woven into one gorgeous track — not one country alone.",
"genres": [ "genres": [
"techno-ethnic",
"ethnic dub",
"bonobo-style downtempo electronica",
"jamaican dub and reggae-dub",
"modern electronica", "modern electronica",
"ambient techno meets world", "sahel desert electronica",
"ethnic fusion", "mongolian overtone electronica",
"dervish trance", "urdu-world fusion",
"tabla electronica",
"asian underground",
"global dub", "global dub",
"dubtronica", "dubtronica",
"world electronica", "trip-hop dub",
"cinematic electronic", "tabla electronica",
"trip-hop dub" "cinematic electronic"
], ],
"mood": [ "mood": [
"beautiful",
"hypnotic", "hypnotic",
"trance-like pulse",
"warm", "warm",
"spacious", "spacious",
"late-night", "late-night",
"cinematic gothic", "lush and melodic",
"meditative but danceable", "meditative but danceable",
"euphoric electronic builds", "euphoric builds",
"immersive and cinematic", "immersive",
"cross-cultural blend" "cross-cultural wonder"
], ],
"instruments": [ "instruments": [
"lush synth pads and arpeggiated electronica (Singularity-style)", "lush Bonobo-style synths and melodic electronica",
"tabla and Indian percussion driving the groove", "Jamaica dub — spring reverb, one-drop feel, sub bass culture",
"sitar and synth leads blended", "Sahel kora and desert percussion accents",
"organic hand percussion meets digital pulse", "Mongolian overtone throat singing as wordless texture layer",
"oud and kora as colour accents", "Urdu male vocal colour — poetic, not Bollywood pop",
"sub bass and sidechain warmth", "tabla and hand percussion meets digital pulse",
"dub delay and spring reverb", "dub delay and tape echo",
"wordless vocal textures", "Jon Hopkins arpeggiated builds",
"Gregorian-adjacent choral layers (Frostbite lane)", "organic samples woven through electronic production"
"ceremonial ritual chants (Chac's Dub lane)",
"breakbeat or four-on-floor-lite hypnotic pulse",
"analog warmth and wide stereo immersion"
], ],
"tempo_bpm": [90, 124], "tempo_bpm": [88, 118],
"references": "Jon Hopkins Singularity — modern electronica with organic world percussion and euphoric synth builds; Karsh Kale Milan (tabla electronica); Transglobal Underground Dervish Trans; Thievery Corporation lounge; Shye Ben Tzur / Anoushka Shankar Indian fusion; Baaba Maal Sahel accent; Glass Beams psychedelic tint. Electronic production leads — world colour is woven in, not folk-first.", "references": "Bonobo — lush melodic downtempo, beautiful ethnic electronica; Jon Hopkins Singularity; Jamaica dub (Kingston spring reverb, Mungo's Hi Fi); Baaba Maal / Sahara Sahel warmth; Karsh Kale Milan; Transglobal Dervish Trans; Thievery Corporation; Shye Ben Tzur Urdu-Indian fusion without pop. We are inventing techno-ethnic — electronic beauty first, world voices as colour.",
"avoid": [ "avoid": [
"Turkish-specific tracks — Ney Vakti, Anatolian night, Ottoman court, bağlama/saz as the identity", "Turkish folk — Ney Vakti, Anatolian, Ottoman, bağlama/saz as identity",
"Turkish language lead vocals or Turkish folk melody as the hook", "Indian pop / Rajasthani folk dub",
"Anadolu psych or Altın Gün-style as the main flavour", "Japanese enka shamisen slow-burn",
"Indian pop or Rajasthani folk dub — dholak, kartal, sarangi Bollywood-adjacent feel", "Armenian duduk hymn slow sacred chant",
"Japanese enka shamisen slow-burn under 88 BPM — Kurai Yoru too slow", "single-country homages",
"Armenian duduk hymn dub — slow mournful sacred chant, Duduk Zomari too slow", "slow sluggish tracks under 88 BPM",
"single-country homages of any nation", "West African highlife palm-wine guitar",
"lead vocals locked to one national folk tradition",
"West African highlife and palm-wine guitar",
"slow ney-and-bağlama meditation",
"big-room EDM drops", "big-room EDM drops",
"four-on-the-floor house", "four-on-the-floor house",
"fuzz electric guitar on griot or lead vocal tracks", "fuzz electric guitar on vocal tracks"
"sluggish tempo under 88 BPM"
] ]
}, },
"dj": { "dj": {
"variety": true, "variety": true,
"default_length": "1-2 minutes", "default_length": "1-2 minutes",
"prefer_fusion_over_locale": true "prefer_fusion_over_locale": true,
"station_mission": "techno-ethnic — Bonobo-beautiful blends across Jamaica, Sahara, Mongolia, Urdu, and beyond"
}, },
"playback": { "playback": {
"shuffle": true, "shuffle": true,
+20 -17
View File
@@ -1,26 +1,29 @@
{ {
"updated": "2026-06-07", "updated": "2026-06-07",
"description": "Station genre taxonomy — tags on each track; categories are broader browse buckets.", "description": "Techno-ethnic taxonomy — Bonobo-beautiful electronica meets world dub and cross-cultural vocals.",
"station_lane": "techno-ethnic",
"categories": { "categories": {
"fusion-electronic": "Jon Hopkins / Karsh Kale — modern electronica meets world percussion", "techno-ethnic": "Bonobo / Hopkins — lush electronic beauty meets world colour",
"jamaica-dub": "Kingston spring reverb, reggae-dub bass culture",
"sahara-sahel": "Desert warmth, kora, Sahel pulse",
"mongolia-overtone": "Throat singing as wordless ethereal layer",
"urdu-fusion": "Urdu poetic vocal colour in electronica",
"dub-space": "Dub delay, sub bass, spacious mixes", "dub-space": "Dub delay, sub bass, spacious mixes",
"ceremonial-world": "Ritual chant, temple/ceremony energy — Chac's Dub lane", "ceremonial-world": "Ritual chant energy — Chac's Dub",
"cinematic-gothic": "Cold ether, choral depth, Only Lovers Left Alive mood — Frostbite / Gregorian lane", "cinematic-gothic": "Gregorian ether — Frostbite Dub"
"desert-dub": "Sahel warmth, caravan pulse, desert blues texture",
"vocal-ethnic": "Wordless or blended ethnic vocals, not folk-pop"
}, },
"genres": { "genres": {
"modern-electronica": { "category": "fusion-electronic", "label": "Modern electronica meets world" }, "techno-ethnic": { "category": "techno-ethnic", "label": "Core station lane — beautiful blended electronica" },
"tabla-electronica": { "category": "fusion-electronic", "label": "Karsh Kale / Milan-style" }, "bonobo-downtempo": { "category": "techno-ethnic", "label": "Lush melodic Bonobo-style electronica" },
"dervish-trance": { "category": "fusion-electronic", "label": "Transglobal hypnotic breaks" }, "modern-electronica": { "category": "techno-ethnic", "label": "Jon Hopkins Singularity builds" },
"ceremonial-dub": { "category": "ceremonial-world", "label": "Ceremonial chant + dub space" }, "jamaican-dub": { "category": "jamaica-dub", "label": "Jamaica dub and reggae-dub space" },
"mesoamerican-electronica": { "category": "ceremonial-world", "label": "Clay flute, ritual pulse" }, "sahel-electronica": { "category": "sahara-sahel", "label": "Sahara / Sahel desert electronica" },
"gregorian-ether": { "category": "cinematic-gothic", "label": "Gregorian-adjacent choral depth" }, "mongolian-overtone": { "category": "mongolia-overtone", "label": "Overtone throat textures in mix" },
"nordic-dub": { "category": "cinematic-gothic", "label": "Icelandic ether + sub bass" }, "urdu-electronica": { "category": "urdu-fusion", "label": "Urdu vocal colour, not Bollywood" },
"cinematic-gothic": { "category": "cinematic-gothic", "label": "Cold cinematic gothic dub" }, "tabla-electronica": { "category": "techno-ethnic", "label": "Karsh Kale / Milan-style" },
"desert-dub": { "category": "desert-dub", "label": "Desert dubtronica" }, "ceremonial-dub": { "category": "ceremonial-world", "label": "Ceremonial chant + dub" },
"sahel-blues": { "category": "desert-dub", "label": "Sahel griot warmth" }, "gregorian-ether": { "category": "cinematic-gothic", "label": "Gregorian-adjacent choral dub" },
"gnawa-dub": { "category": "dub-space", "label": "Gnawa meets dubtronica" }, "desert-dub": { "category": "sahara-sahel", "label": "Desert dubtronica" },
"world-dub": { "category": "dub-space", "label": "Pan-ethnic dub lounge" } "world-dub": { "category": "dub-space", "label": "Pan-ethnic dub lounge" }
} }
} }
+11 -9
View File
@@ -1,7 +1,7 @@
{ {
"updated": "2026-06-07", "updated": "2026-06-07",
"listener": "ozan", "listener": "ozan",
"summary": "Batch purge 2026-06-07 — kept original 5 + Chac's Dub + Frostbite Dub only. Lane: Hopkins/Karsh Kale fusion, not slow country-specific gens.", "summary": "Techno-ethnic station — Bonobo, Jamaica dub, Sahara, Mongolia overtone, Urdu, Hopkins. Seven keepers. Beautiful cross-cultural blends, not single-nation folk.",
"genre_taxonomy": "songs/genres.json", "genre_taxonomy": "songs/genres.json",
"global_avoid": [ "global_avoid": [
"fuzz electric guitar on griot/vocal tracks", "fuzz electric guitar on griot/vocal tracks",
@@ -19,14 +19,16 @@
"Armenian duduk hymn dub — slow sacred chant, listener hated Duduk Zomari (same problems)" "Armenian duduk hymn dub — slow sacred chant, listener hated Duduk Zomari (same problems)"
], ],
"global_love": [ "global_love": [
"Jon Hopkins Singularity — modern electronica meets organic world percussion", "Bonobo — lush beautiful melodic downtempo ethnic electronica",
"ceremonial dub — Chac's Dub temple-step energy", "techno-ethnic blends — Jamaica dub + Sahara + Mongolia + Urdu in one track",
"Gregorian-adjacent choral ether — Frostbite Dub (listener used to love Gregorian)", "Jamaica spring-reverb dub and reggae-dub bass culture",
"lush arpeggiated synths and euphoric cinematic builds", "Sahara / Sahel desert warmth as electronic colour",
"Karsh Kale / Milan-style tabla electronica", "Mongolian overtone throat singing as wordless ethereal layer",
"Transglobal Underground dervish-trance pulse", "Urdu poetic male vocal colour — fusion not Bollywood pop",
"sub bass pulse and warm analog dub space", "Jon Hopkins Singularity builds",
"wordless vocal textures" "ceremonial dub — Chac's Dub",
"Gregorian ether — Frostbite Dub",
"sub bass and warm dub space"
], ],
"gold_standard_id": "1c1d7b8a", "gold_standard_id": "1c1d7b8a",
"tracks": [ "tracks": [
+1 -1
View File
@@ -1,5 +1,5 @@
{ {
"index": 6, "index": 0,
"count": 7, "count": 7,
"tracks": [ "tracks": [
{ {
+54 -64
View File
@@ -1,94 +1,84 @@
"""Curated batch directionsJon Hopkins Singularity meets ethnic fusion, not Turkish folk.""" """Techno-ethnic batchBonobo-beautiful blends: Jamaica, Sahara, Mongolia, Urdu, and more."""
VOCAL_BATCH: list[str] = [ VOCAL_BATCH: list[str] = [
( (
"Jon Hopkins Singularity-style — lush arpeggiated synths, organic tabla and " "Bonobo-style techno-ethnic — lush melodic synths, soft breakbeat, "
"hand percussion, euphoric build, sub bass pulse, wordless vocal textures, 118 BPM. " "Jamaica dub spring reverb on percussion, 96 BPM. Beautiful and spacious."
"Modern electronica meets world colour."
), ),
( (
"Karsh Kale-style tabla electronica — driving tabla breakbeat, sitar riff " "Jamaica meets Sahara dub — Kingston one-drop sub bass, kora accents, "
"with synth pad, sub bass, wordless male vocal textures, 100 BPM. Indian-world fusion." "wordless Sahel male chant, tape delay, Mungo's Hi Fi warmth, 92 BPM."
), ),
( (
"Transglobal Underground dervish-trance — hypnotic ethnic breakbeat, oud and " "Mongolia overtone electronica — wordless throat-singing texture layer "
"synth swirl, wordless call-and-response vocals, dub delay, 96 BPM pulse." "over Bonobo lush synth pad, tabla pulse, sub bass, 94 BPM ethereal blend."
), ),
( (
"Milan-style fusion dub — tabla and sitar over downtempo break, male wordless " "Urdu-world Bonobo dub — male poetic Urdu vocal colour over downtempo "
"vocal hums, bass pulse, spring reverb, Thievery Corporation lounge energy." "electronica, tabla, dub delay, NOT Bollywood pop, 90 BPM beautiful."
), ),
( (
"Sahel meets Asian underground — kora and tabla together, deep sub bass, " "Jon Hopkins meets Jamaica — arpeggiated synth build, reggae-dub bass, "
"wordless griot-style chants, hand percussion, 94 BPM. No Turkish instruments lead." "spring reverb, hand percussion, euphoric forward pulse 110 BPM."
), ),
( (
"Ethiopian-jazz world breaks — krar, breathy wordless vocals, piano, synth pad, " "Sahara Sahel techno-ethnic — desert kora and talking drum with electronic "
"tabla accents, sub bass, 90 BPM. Pan-ethnic not national." "kick, wordless griot chant, Bonobo melodic warmth, 95 BPM."
), ),
( (
"Persian-Sahel lounge fusion — oud and tar with sitar colour, baritone wordless " "Karsh Kale Milan energy — tabla breakbeat, sitar-synth hook, sub bass, "
"vocal, tabla groove, hand drums, cinematic ether 92 BPM." "Urdu wordless alaap texture blended in, 100 BPM."
), ),
( (
"Balkan-brass world breaks — female vocalise, muted trumpet, tabla and darbuka " "Transglobal dervish-trance — hypnotic ethnic breakbeat, oud swirl, "
"blend, sub bass, 98 BPM dark danceable mix." "wordless vocals, dub space, 98 BPM danceable."
),
(
"Atlantic-Desert electronica — soft wordless vocals, fingerpicked warmth, "
"tabla pulse, oceanic reverb, sub bass fusion lounge 88 BPM."
),
(
"Sahel-Asian underground fusion — kora and tabla with synth arpeggio, "
"wordless male chants, sub bass 93 BPM. No Rajasthani folk or sarangi pop."
),
(
"Desert caravan dub — Bambara wordless chants, kora and oud, tabla breakbeat "
"from bar one, sub bass, NO bağlama or ney as lead, 95 BPM."
),
(
"Mediterranean dubtronica mix — bouzouki colour as accent only, tabla groove, "
"wordless vocals, sub bass, 92 BPM. Not country-specific."
),
(
"Kurdish-Sahel storytelling dub — tanbur drone as texture, tabla pulse, "
"wordless narrative vocal, synth pad, 91 BPM cross-cultural."
),
(
"Nordic-desert ether fusion — breathy female wordless vocals, piano, synth, "
"tabla accents, sub bass, cold cinematic dub 90 BPM."
),
(
"East-West break fusion — sitar hook with tabla breakbeat at 96 BPM, "
"synth arpeggio, wordless vocal hum. No shamisen or enka slow-burn."
),
(
"Indo-dub lounge — female wordless alaap, tabla and sitar, synth bass, "
"Karsh Kale energy, 97 BPM. Fusion not Bollywood-only."
),
(
"Synth-pad world breaks — tabla groove, wordless male chant, sub bass, "
"96 BPM forward pulse. No duduk hymn or slow sacred woodwind lead."
), ),
( (
"Chac's Dub successor — ceremonial world dub, clay flute ritual chants, " "Chac's Dub successor — ceremonial world dub, clay flute ritual chants, "
"baritone chest voice, darbuka, sub bass, temple-step energy 85 BPM." "baritone chest voice, darbuka, sub bass, Bonobo spacious mix 85 BPM."
), ),
( (
"Frostbite / Gregorian-ether dub — choral wordless female vocals, piano, " "Frostbite Gregorian-ether — choral wordless vocals, piano, sub bass, "
"sub bass, cinematic gothic cold warmth, sacred choral through dub filter 90 BPM." "cinematic gothic through Bonobo-style electronic warmth 90 BPM."
), ),
( (
"Emerald Rush energy — driving Jon Hopkins electronica, pulsing synth arpeggio, " "Jamaica-Mongolia fusion — dub bass and spring reverb, overtone throat "
"organic percussion layer, immersive forward momentum, sub bass, 120 BPM. " "texture as ethereal pad, organic hand drums, lush synth, 93 BPM."
"Electronic-first with subtle world accents."
), ),
( (
"Atlas-Indian dub — oud and sitar blended, tabla driving, wordless chant, " "Urdu-Sahel Bonobo lounge — poetic male Urdu hum, kora sparkle, "
"darbuka accents, sub bass 96 BPM. Multi-region not Turkish." "downtempo electronica, dub echo, beautiful late-night 88 BPM."
), ),
( (
"Afro-Asian highlife-dub fusion — wordless call-and-response vocals, clean " "Ethiopian-jazz techno-ethnic — krar, breathy vocals, piano, Bonobo synth "
"plucked strings, talking drum with tabla, ney as light accent only, 94 BPM." "pad, forward pulse 91 BPM."
),
(
"Persian-Sahel Bonobo blend — oud and tar with lush electronica, wordless "
"baritone, tabla groove, 94 BPM."
),
(
"Balkan-brass world breaks — female vocalise, muted trumpet, tabla, "
"sub bass, 98 BPM dark beautiful mix."
),
(
"Indo-dub Bonobo — female wordless alaap, tabla, sitar colour, "
"lush melodic synth, 97 BPM not pop."
),
(
"Ceremonial techno-ethnic — ritual wordless chants, clay flute, "
"Bonobo downtempo pulse, deep sub, 92 BPM."
),
(
"Emerald Rush energy — driving Hopkins electronica, Jamaica dub bass, "
"Sahel percussion layer, 118 BPM immersive."
),
(
"Atlas-Indian dub banger — oud and sitar with tabla, wordless chant, "
"Bonobo beauty, punchy sub 96 BPM."
),
(
"Pan-world Bonobo finale — Jamaica dub + Sahara kora + Mongolia overtone "
"texture + Urdu vocal colour + lush synths, 95 BPM gorgeous blend."
), ),
] ]
+7 -8
View File
@@ -45,14 +45,13 @@ Your job:
"melodic call-and-response". Avoid "vocal sample" / "griot sample" phrasing. "melodic call-and-response". Avoid "vocal sample" / "griot sample" phrasing.
9. Read listener curation metadata — clone what they loved, hard-avoid what they disliked 9. Read listener curation metadata — clone what they loved, hard-avoid what they disliked
(e.g. fuzz electric guitar on vocal tracks, long guitar-only intros before saz). (e.g. fuzz electric guitar on vocal tracks, long guitar-only intros before saz).
10. Positive references: Jon Hopkins Singularity (modern electronica meets organic 10. Station mission: TECHNO-ETHNIC — beautiful electronica meets world dub. Big
world — lush synth arpeggios, euphoric builds, immersive pulse); Karsh Kale (Milan); inspirations: Bonobo (lush melodic downtempo), Jon Hopkins Singularity, Jamaica
Transglobal Underground (Dervish Trans). Electronic production leads, ethnic colour woven in. Also clone keepers by genre: dub (spring reverb, bass culture), Sahara/Sahel warmth, Mongolian overtone throat
ceremonial-dub (Chac's Dub), gregorian-ether / cinematic-gothic (Frostbite Dub). textures (wordless ethereal layer), Urdu poetic vocal colour. Blend many cultures
11. Listener likes ETHNIC FUSION MIXES but NOT TURKISH — no Ney Vakti, no Anatolian in one gorgeous track — Jamaica + Sahara + Mongolia + Urdu + synth beauty.
folk, no Ottoman court, no bağlama/saz as the lead identity, no Turkish vocals. 11. NOT Turkish folk, NOT slow country homages, NOT Indian pop. Clone keepers:
Blend Sahel, Indian fusion, Middle Eastern accents, and dub. Vocals: wordless ceremonial-dub (Chac's Dub), gregorian-ether (Frostbite Dub). Forward tempo 88+ BPM.
textures only, not national folk traditions.
Respond with JSON only: Respond with JSON only:
{ {
+57 -6
View File
@@ -3,6 +3,7 @@ from __future__ import annotations
import asyncio import asyncio
import json import json
import random import random
from datetime import datetime, timezone
from pathlib import Path from pathlib import Path
from fastapi import BackgroundTasks, FastAPI, HTTPException from fastapi import BackgroundTasks, FastAPI, HTTPException
@@ -35,7 +36,15 @@ app.add_middleware(
_config: Config | None = None _config: Config | None = None
_queue: RadioQueue | None = None _queue: RadioQueue | None = None
_generating = False _generating = False
_generation_state: dict = {"busy": False, "phase": None, "error": None, "track_title": None} _generation_state: dict = {
"busy": False,
"phase": None,
"error": None,
"track_title": None,
"last_completed_at": None,
"last_completed_title": None,
"last_completed_id": None,
}
_chat = ChatStore() _chat = ChatStore()
@@ -101,14 +110,29 @@ def _play_library_entry(q: RadioQueue, cfg: Config, entry: dict) -> dict:
return {"status": "ok", "source": "library", "track": np.__dict__ if np else None} return {"status": "ok", "source": "library", "track": np.__dict__ if np else None}
def _set_generation(*, busy: bool, phase: str | None = None, error: str | None = None, title: str | None = None) -> None: def _set_generation(
*,
busy: bool,
phase: str | None = None,
error: str | None = None,
title: str | None = None,
completed_id: str | None = None,
) -> None:
global _generation_state global _generation_state
_generation_state = { state = {
"busy": busy, "busy": busy,
"phase": phase, "phase": phase,
"error": error, "error": error,
"track_title": title, "track_title": title,
"last_completed_at": _generation_state.get("last_completed_at"),
"last_completed_title": _generation_state.get("last_completed_title"),
"last_completed_id": _generation_state.get("last_completed_id"),
} }
if completed_id and title:
state["last_completed_at"] = datetime.now(timezone.utc).isoformat()
state["last_completed_title"] = title
state["last_completed_id"] = completed_id
_generation_state = state
async def _compose_track(request: str | None = None, *, check_limit: bool = True) -> dict: async def _compose_track(request: str | None = None, *, check_limit: bool = True) -> dict:
@@ -138,12 +162,17 @@ async def _compose_track(request: str | None = None, *, check_limit: bool = True
_set_generation(busy=True, phase="composing", title=plan.title) _set_generation(busy=True, phase="composing", title=plan.title)
track = LyriaEngine(cfg).generate(plan) track = LyriaEngine(cfg).generate(plan)
q.add(track) q.add(track)
q.play_id(track.plan.id)
rs = load_radio_settings(lyria_model=cfg.lyria_model) rs = load_radio_settings(lyria_model=cfg.lyria_model)
cost = cost_per_track(cfg.lyria_model, rs.costs.__dict__) cost = cost_per_track(cfg.lyria_model, rs.costs.__dict__)
record_generation(cfg.output_dir, cost, track.plan.id, track.plan.title) record_generation(cfg.output_dir, cost, track.plan.id, track.plan.title)
np = q.now_playing() np = q.now_playing()
_, budget = _can_generate_today(cfg) _, budget = _can_generate_today(cfg)
_set_generation(busy=False) _set_generation(
busy=False,
title=track.plan.title,
completed_id=track.plan.id,
)
return { return {
"status": "ok", "status": "ok",
"source": "generated", "source": "generated",
@@ -262,8 +291,30 @@ async def patch_settings(body: SettingsPatch) -> dict:
@app.post("/api/generate") @app.post("/api/generate")
async def generate_track() -> dict: async def generate_track(background: BackgroundTasks) -> dict:
return await _compose_track() """Start generation in background — poll /api/stats for progress and ready state."""
cfg = _get_config()
if _generating:
return {
"status": "busy",
"message": "Already generating a track",
"generation": _generation_state,
}
ok, budget = _can_generate_today(cfg)
if not ok:
return {
"status": "limit",
"message": f"Daily limit reached ({budget['max_per_day']} new songs)",
"budget": budget,
}
background.add_task(_background_compose, None)
return {
"status": "accepted",
"generating": True,
"message": "Generating — planning then Lyria compose (~3090s)",
"budget": budget,
"generation": _generation_state,
}
@app.post("/api/shuffle/next") @app.post("/api/shuffle/next")
+26 -29
View File
@@ -9,64 +9,61 @@
"Your Top Songs 2025" "Your Top Songs 2025"
], ],
"artists": [ "artists": [
"Bonobo",
"Jon Hopkins",
"Karsh Kale", "Karsh Kale",
"Transglobal Underground", "Transglobal Underground",
"Baaba Maal", "Baaba Maal",
"Thievery Corporation", "Thievery Corporation",
"Mungo's Hi Fi",
"islandman", "islandman",
"Kaya Project", "Kaya Project",
"Blanco White",
"Shye Ben Tzur", "Shye Ben Tzur",
"Anoushka Shankar", "Anoushka Shankar",
"Jon Hopkins",
"Glass Beams", "Glass Beams",
"Francis Bebey", "Francis Bebey",
"Mungo's Hi Fi",
"Matthew Halsall" "Matthew Halsall"
], ],
"albums": [ "albums": [
{ "title": "Garip", "artist": "Altın Gün", "vibe": "Anadolu psych-folk, bağlama, fuzzy warmth" }, { "title": "Migration", "artist": "Bonobo", "vibe": "lush melodic downtempo, beautiful ethnic electronica, organic samples" },
{ "title": "Singularity", "artist": "Jon Hopkins", "vibe": "modern electronica meets organic world, euphoric builds" },
{ "title": "Estuaire", "artist": "Ablaye Cissoko", "vibe": "kora, West African elegance, spacious" }, { "title": "Estuaire", "artist": "Ablaye Cissoko", "vibe": "kora, West African elegance, spacious" },
{ "title": "Mirage", "artist": "Glass Beams", "vibe": "psychedelic instrumental, Middle Eastern tint" }, { "title": "Mirage", "artist": "Glass Beams", "vibe": "psychedelic instrumental, Middle Eastern tint" },
{ "title": "African Electronic Music 1975-1982", "artist": "Francis Bebey", "vibe": "early African electronic, playful hypnotic" }, { "title": "African Electronic Music 1975-1982", "artist": "Francis Bebey", "vibe": "early African electronic, playful hypnotic" }
{ "title": "Singularity", "artist": "Jon Hopkins", "vibe": "modern electronica meets organic world — lush synth builds, immersive pulse, euphoric cinematic" }
], ],
"preference": "Modern electronica meets ethnic fusion — Jon Hopkins Singularity, Karsh Kale, Dervish Trans. Turkish folk no.", "preference": "Techno-ethnic — Bonobo-beautiful electronica meets Jamaica dub, Sahara, Mongolia overtone vocals, Urdu colour. Cross-cultural blends, not single-nation folk.",
"reference_tracks": [ "reference_tracks": [
{ "title": "Singularity", "artist": "Jon Hopkins", "vibe": "lush arpeggiated synths, organic percussion, euphoric builds, modern electronic meets world" }, { "title": "Kerala", "artist": "Bonobo", "vibe": "lush downtempo, beautiful world electronica" },
{ "title": "Emerald Rush", "artist": "Jon Hopkins", "vibe": "driving hypnotic electronica, pulsing bass, immersive forward momentum" }, { "title": "Singularity", "artist": "Jon Hopkins", "vibe": "lush synths, organic percussion, euphoric electronic meets world" },
{ "title": "Milan", "artist": "Karsh Kale", "vibe": "tabla electronica, sitar-synth fusion" },
{ "title": "Dervish Trans", "artist": "Transglobal Underground", "vibe": "dervish trance breaks, hypnotic ethnic pulse" }, { "title": "Dervish Trans", "artist": "Transglobal Underground", "vibe": "dervish trance breaks, hypnotic ethnic pulse" },
{ "title": "Milan", "artist": "Karsh Kale", "vibe": "tabla electronica, sitar-synth fusion, driving world breakbeat" } { "title": "Boboyillo", "artist": "Baaba Maal", "vibe": "Sahara Sahel warmth, rolling desert pulse" }
],
"cultural_palette": [
"Jamaica — dub spring reverb, reggae-dub bass culture",
"Sahara / Sahel — desert warmth, kora, griot colour",
"Mongolia — overtone throat singing as ethereal wordless layer",
"Urdu — poetic male vocal colour in fusion (not Bollywood pop)",
"and more — always blended, always beautiful"
], ],
"genres": [ "genres": [
"techno-ethnic",
"bonobo-style downtempo",
"ethnic dub",
"jamaican dub",
"sahel electronica",
"modern electronica", "modern electronica",
"ambient techno meets world",
"ethnic fusion",
"ethnic chill",
"dubtronica", "dubtronica",
"world dub", "world dub",
"trip-hop", "trip-hop",
"global fusion jazz", "global fusion"
"afro-dub",
"cinematic electronic",
"psychedelic world"
], ],
"tracks": [ "tracks": [
{ {
"title": "Boboyillo", "title": "Boboyillo",
"artists": ["Baaba Maal", "Rougi"], "artists": ["Baaba Maal", "Rougi"],
"album": "Being", "album": "Being",
"vibe": "Sahel warmth, rolling desert pulse, call-and-response, spiritual but danceable" "vibe": "Sahara Sahel warmth, rolling desert pulse, spiritual but danceable"
},
{
"title": "Olalla",
"artists": ["Blanco White"],
"vibe": "melancholic indie folk, Spanish ether, intimate late-night"
},
{
"title": "Daily Mix 01",
"artists": ["BALTHVS", "Pentagram", "Cem Karaca"],
"vibe": "Turkish rock and psych crossover"
}, },
{ {
"title": "Daily Mix 03", "title": "Daily Mix 03",
@@ -76,7 +73,7 @@
{ {
"title": "Daily Mix 04", "title": "Daily Mix 04",
"artists": ["Shye Ben Tzur", "Anoushka Shankar"], "artists": ["Shye Ben Tzur", "Anoushka Shankar"],
"vibe": "Indian classical fusion, devotional texture, world spiritual" "vibe": "Urdu-Indian fusion, devotional texture, world spiritual — not pop"
} }
] ]
} }