Add music test duration control and preserve audio player during polling

This commit is contained in:
Mikei386 committed 2026-10-01 21:12:00 +02:00
1 parent 96c394cd6c
commit 3779c846db
2 files changed
+12 -4

No files matched your search

+6
View File
@@ -248,3 +248,9 @@ die 5080 nicht frei genug ist. Eine feste Auswahl fällt nicht still auf eine
andere Karte zurück. Auch die Separator-Weboberfläche beachtet diese Einstellungen. andere Karte zurück. Auch die Separator-Weboberfläche beachtet diese Einstellungen.
Profileinstellungen werden im verschlüsselten Backup gespeichert. Die GPU-Prüfung Profileinstellungen werden im verschlüsselten Backup gespeichert. Die GPU-Prüfung
ist eine Vorprüfung; ein tatsächlicher OOM wird weiterhin als Auftragsfehler angezeigt. ist eine Vorprüfung; ein tatsächlicher OOM wird weiterhin als Auftragsfehler angezeigt.
Der YuE2-Test bietet eine maximale Länge in Sekunden (1,28–360 s). Bei der aktuellen
VAE-Konfiguration entsprechen 25 semantische Tokens einer Sekunde; die erweiterten
Token- und Sekundenfelder werden synchronisiert. Das Modell kann vor dem Limit enden.
Der Ergebnisplayer bleibt beim Status-Polling bestehen, damit die Wiedergabe nicht
alle 1,5 Sekunden zurückgesetzt wird.
+6 -4
View File
@@ -1,10 +1,12 @@
window.MusicUI=(()=>{ window.MusicUI=(()=>{
const e=v=>String(v??'').replace(/[&<>"']/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c])); const e=v=>String(v??'').replace(/[&<>"']/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]));
async function api(path,data){const r=await fetch('/api/v1/'+path,data===undefined?{}:{method:'POST',headers:{'Content-Type':'application/json','X-Athena-Deck':'1'},body:JSON.stringify(data)});const x=await r.json();if(!r.ok)throw Error(x.error||'Musikanfrage fehlgeschlagen');return x;} async function api(path,data){const r=await fetch('/api/v1/'+path,data===undefined?{}:{method:'POST',headers:{'Content-Type':'application/json','X-Athena-Deck':'1'},body:JSON.stringify(data)});const x=await r.json();if(!r.ok)throw Error(x.error||'Musikanfrage fehlgeschlagen');return x;}
function html(){return '<section id="music-test" class="card"><h2>Musik ausprobieren</h2><p>YuE2 über audio.cpp. Englische Songtexte werden empfohlen. Ein kurzer Test beginnt mit höchstens 400 semantischen Tokens; dies ist keine feste Dauer in Sekunden.</p><p id="music-state" role="status"></p><p id="music-error" role="alert"></p><div id="music-progress"></div><form id="music-form"><label>Profil<select id="music-profile" required></select></label><label>Musikstil<textarea id="music-style" maxlength="2000" required placeholder="English, acoustic pop, warm vocals, gentle guitar"></textarea></label><label>Songtext<textarea id="music-lyrics" maxlength="8000" rows="6" required placeholder="[Verse] … [Chorus] …"></textarea></label><details><summary>Testparameter</summary><label>Maximale semantische Tokens<input type="number" id="music-tokens" min="32" max="9000" value="400"></label><label>NAR-Schritte<input type="number" id="music-steps" min="1" max="32" value="8"></label><label>Seed<input type="number" id="music-seed" min="0" max="2147483647" value="1234"></label></details><button id="music-start">Musik erzeugen</button><button type="button" class="secondary" id="music-cancel">Abbrechen</button></form><div id="music-result"></div><p>RTX 5080 bevorzugt. Deck-Aufträge werden koordiniert. Nach der Anfrage wird das Modell entladen. Text und Stil werden nicht dauerhaft gespeichert.</p></section>';} function html(){return '<section id="music-test" class="card"><h2>Musik ausprobieren</h2><p>YuE2 über audio.cpp. Englische Songtexte werden empfohlen. Die maximale Länge begrenzt den Test. YuE2 kann früher enden; eine exakt feste Titellänge ist damit nicht garantiert.</p><p id="music-state" role="status"></p><p id="music-error" role="alert"></p><div id="music-progress"></div><form id="music-form"><label>Profil<select id="music-profile" required></select></label><label>Musikstil<textarea id="music-style" maxlength="2000" required placeholder="English, acoustic pop, warm vocals, gentle guitar"></textarea></label><label>Songtext<textarea id="music-lyrics" maxlength="8000" rows="6" required placeholder="[Verse] … [Chorus] …"></textarea></label><label>Maximale Länge (Sekunden)<input type="number" id="music-duration" min="2" max="360" step="1" value="16" required></label><p id="music-length-hint" class="small">Bis etwa 16 Sekunden · 400 semantische Tokens. YuE2 verwendet hier ungefähr 25 Tokens pro Sekunde.</p><details><summary>Erweiterte Testparameter</summary><label>Maximale semantische Tokens<input type="number" id="music-tokens" min="32" max="9000" value="400"></label><label>NAR-Schritte<input type="number" id="music-steps" min="1" max="32" value="8"></label><label>Seed<input type="number" id="music-seed" min="0" max="2147483647" value="1234"></label></details><button id="music-start">Musik erzeugen</button><button type="button" class="secondary" id="music-cancel">Abbrechen</button></form><div id="music-result"></div><p>RTX 5080 bevorzugt. Deck-Aufträge werden koordiniert. Nach der Anfrage wird das Modell entladen. Text und Stil werden nicht dauerhaft gespeichert.</p></section>';}
function bind(){const root=document.querySelector('#music-test'),el=id=>root.querySelector('#'+id);let ready=false,busy=false; function bind(){const root=document.querySelector('#music-test'),el=id=>root.querySelector('#'+id);let ready=false,busy=false,resultId=null;
async function poll(){if(!root.isConnected)return;try{const s=await api('music');if(!root.isConnected)return;const j=s.job,running=j?.state==='running';el('music-state').textContent=(s.mode!=='music'?'Bitte in der Übersicht auf Musik umschalten.':j?.phase||'Profil wählen und Musikstil sowie Songtext eingeben.')+(running?' · '+Math.round((Date.now()/1000)-j.started_at)+' s':'');el('music-progress').innerHTML=running?'<progress aria-label="Musikgenerierung läuft"></progress>':'';el('music-start').disabled=busy||running||!ready||s.mode!=='music';el('music-cancel').disabled=!running;if(j?.state==='complete')el('music-result').innerHTML=`<h3>Ergebnis · ${e(j.duration_seconds)} Sekunden</h3><audio controls src="/api/v1/music/audio?id=${e(j.id)}"></audio><p><a download="athena-musik.wav" href="/api/v1/music/audio?id=${e(j.id)}">WAV herunterladen</a></p>`;if(j?.state==='failed')el('music-error').textContent=j.phase;}catch(x){if(root.isConnected)el('music-error').textContent=x.message;}if(root.isConnected)setTimeout(poll,1500);} async function poll(){if(!root.isConnected)return;try{const s=await api('music');if(!root.isConnected)return;const j=s.job,running=j?.state==='running';el('music-state').textContent=(s.mode!=='music'?'Bitte in der Übersicht auf Musik umschalten.':j?.phase||'Profil wählen und Musikstil sowie Songtext eingeben.')+(running?' · '+Math.round((Date.now()/1000)-j.started_at)+' s':'');el('music-progress').innerHTML=running?'<progress aria-label="Musikgenerierung läuft"></progress>':'';el('music-start').disabled=busy||running||!ready||s.mode!=='music';el('music-cancel').disabled=!running;if(!busy&&j?.state==='complete'&&j.id!==resultId){resultId=j.id;el('music-result').innerHTML=`<h3>Ergebnis · ${e(j.duration_seconds)} Sekunden</h3><audio controls src="/api/v1/music/audio?id=${e(j.id)}"></audio><p><a download="athena-musik.wav" href="/api/v1/music/audio?id=${e(j.id)}">WAV herunterladen</a></p>`;}if(j?.state==='failed')el('music-error').textContent=j.phase;}catch(x){if(root.isConnected)el('music-error').textContent=x.message;}if(root.isConnected)setTimeout(poll,1500);}
function updateLength(fromTokens=false){const tokens=fromTokens?Number(el('music-tokens').value):Math.round(Number(el('music-duration').value)*25);if(!fromTokens)el('music-tokens').value=tokens;else el('music-duration').value=(tokens/25).toFixed(2);el('music-length-hint').textContent=`Bis etwa ${(tokens/25).toFixed(2)} Sekunden · ${tokens} semantische Tokens. Das Modell kann früher enden.`;}
el('music-duration').step='0.04';el('music-duration').min='1.28';el('music-duration').oninput=()=>updateLength();el('music-tokens').oninput=()=>updateLength(true);
api('profiles').then(s=>{if(!root.isConnected)return;const profiles=s.profiles.filter(p=>p.kind==='music'&&p.runnable);ready=profiles.length>0;el('music-profile').innerHTML=profiles.map(p=>`<option value="${e(p.id)}">${e(p.name)}</option>`).join('')||'<option value="">Zuerst Profil und Komponenten einrichten</option>';}).catch(x=>el('music-error').textContent=x.message); api('profiles').then(s=>{if(!root.isConnected)return;const profiles=s.profiles.filter(p=>p.kind==='music'&&p.runnable);ready=profiles.length>0;el('music-profile').innerHTML=profiles.map(p=>`<option value="${e(p.id)}">${e(p.name)}</option>`).join('')||'<option value="">Zuerst Profil und Komponenten einrichten</option>';}).catch(x=>el('music-error').textContent=x.message);
el('music-form').onsubmit=async event=>{event.preventDefault();busy=true;el('music-start').disabled=true;el('music-error').textContent='';el('music-result').replaceChildren();try{await api('music/start',{profile_id:el('music-profile').value,lyrics:el('music-lyrics').value,style:el('music-style').value,seed:Number(el('music-seed').value),max_tokens:Number(el('music-tokens').value),steps:Number(el('music-steps').value)});}catch(x){el('music-error').textContent=x.message;}finally{busy=false;}};el('music-cancel').onclick=()=>api('music/cancel',{}).catch(x=>el('music-error').textContent=x.message);poll();} el('music-form').onsubmit=async event=>{event.preventDefault();busy=true;el('music-start').disabled=true;el('music-error').textContent='';el('music-result').replaceChildren();resultId=null;try{await api('music/start',{profile_id:el('music-profile').value,lyrics:el('music-lyrics').value,style:el('music-style').value,seed:Number(el('music-seed').value),max_tokens:Number(el('music-tokens').value),steps:Number(el('music-steps').value)});}catch(x){el('music-error').textContent=x.message;}finally{busy=false;}};el('music-cancel').onclick=()=>api('music/cancel',{}).catch(x=>el('music-error').textContent=x.message);poll();}
return {html,bind}; return {html,bind};
})(); })();