EFFECTS LIVE: 21 Effekte in der Pipeline (FX1/FX2 je Layer, 16/16 E2E)
Echte GPU-nahe GStreamer-Effekte, zur Laufzeit pro Layer schaltbar: - FX_CATALOG: 21 verfuegbare Effekte (gst-inspect verifiziert): blur, sharpen, color, contrast, brightness, gamma, pixelate, solarize, vertigo, edge, ripple, radioactv, revtv, quark, op, burn, dilate, warp, streak, aging, exclusion - FX1 + FX2 pro Layer (Kette: videoconvert ! FX1 ! videoconvert ! FX2 ! videoconvert - Muster gegen gst-launch verifiziert) - Intensitaet je FX live regelbar OHNE Rebuild (Property-Update direkt am Element, Frames laufen weiter 118->120) - Strukturwechsel (FX waehlen/entfernen) -> atomarer Rebuild mit Rollback; Intensitaeten werden auf neue Pipeline uebertragen - DMX: fx1_channel/fx2_channel optional konfigurierbar -> steuert FX-Intensitaet live (E2E: Kanal 20 = 255 -> Intensitaet 1.0) - UI: Layer-Editor mit FX1/FX2-Dropdown (Katalog via /api/fx) + Intensitaets-Slider je FX - /api/fx: Effekt-Katalog mit Label/Bereich fuer die UI - config.py: fx1_channel/fx2_channel in Validierung (optional, 1-512) - Ungueltige FX-IDs werden abgewiesen (fallback None) E2E-BEWEIS (16/16 PASS, im Container): 1 Health · 2 Katalog 21 Effekte · 3-8 alle Kern-FX enthalten · 9 Layer · 10 FX1 blur -> Rebuild laeuft (118 Frames) · 11 Intensitaet live ohne Ruckler · 12 FX2 color dazu (161 Frames) · 13 FX entfernen ok (180 Frames) · 14 fx1_channel konfiguriert · 15 DMX steuert FX-Intensitaet auf 1.0 · 16 ungueltiger FX abgewiesen Regression: kompletter App-E2E weiterhin 14/14 PASS (app=0 fx=0)
This commit is contained in:
@@ -192,6 +192,10 @@ function renderLayerEdit(ls){const e=$('ledit');
|
||||
<div class="frow"><label>Position X</label><input type="number" id="lx${L.id}" value="${L.x}" onchange="api('layers/update',{id:${L.id},x:this.value})"> Y <input type="number" id="ly${L.id}" value="${L.y}" onchange="api('layers/update',{id:${L.id},y:this.value})"></div>
|
||||
<div class="frow"><label>Größe</label><input type="number" id="lw${L.id}" value="${L.width}" onchange="api('layers/update',{id:${L.id},width:this.value})"> × <input type="number" id="lh${L.id}" value="${L.height}" onchange="api('layers/update',{id:${L.id},height:this.value})"> px</div>
|
||||
<div class="frow"><label>Z-Order</label><input type="number" id="lz${L.id}" min="0" max="15" value="${L.z}" onchange="api('layers/update',{id:${L.id},z:this.value})"></div>
|
||||
<div class="frow"><label>Effekt 1</label><select id="fx1-${L.id}" onchange="api('layers/update',{id:${L.id},fx1:this.value||null})"></select></div>
|
||||
<div class="frow"><label>FX1 Intensität</label><input type="range" id="fx1i-${L.id}" min="0" max="100" value="${Math.round((L.fx1_intensity??0.5)*100)}" oninput="api('layers/update',{id:${L.id},fx1_intensity:this.value/100})"></div>
|
||||
<div class="frow"><label>Effekt 2</label><select id="fx2-${L.id}" onchange="api('layers/update',{id:${L.id},fx2:this.value||null})"></select></div>
|
||||
<div class="frow"><label>FX2 Intensität</label><input type="range" id="fx2i-${L.id}" min="0" max="100" value="${Math.round((L.fx2_intensity??0.5)*100)}" oninput="api('layers/update',{id:${L.id},fx2_intensity:this.value/100})"></div>
|
||||
</div>`).join('')||'<p style="color:var(--dim)">Keine aktiven Layer. Medien-Tab öffnen und Datei als Layer hinzufügen.</p>'}}
|
||||
|
||||
// Medien
|
||||
@@ -253,6 +257,17 @@ async function saveSettings(){const body={
|
||||
if(d.saved){$('setmsg').textContent='Gespeichert ✓';setTimeout(()=>$('setmsg').textContent='',2500);
|
||||
if(d.hint)toast(d.hint)}}
|
||||
|
||||
let FX=[];
|
||||
async function loadFx(){try{FX=await(await fetch('/api/fx')).json();fillFxSelects()}catch(e){}}
|
||||
function fillFxSelects(){document.querySelectorAll('select[id^="fx1-"],select[id^="fx2-"]').forEach(sel=>{
|
||||
const cur=sel.dataset.cur||'';const isFx1=sel.id.startsWith('fx1-');
|
||||
const key=isFx1?'fx1':'fx2';const lay=sel.dataset.lay;
|
||||
const val=(lastStatus?.layers||[]).find(l=>String(l.id)===lay)?.[key]||'';
|
||||
sel.innerHTML='<option value="">– keiner –</option>'+FX.map(f=>`<option value="${f.id}"${f.id===val?' selected':''}>${f.label}</option>`).join('');});}
|
||||
// Selects bei jedem Layer-Render nachfuellen + dataset setzen
|
||||
const _origRender=renderLayerEdit;
|
||||
renderLayerEdit=function(ls){_origRender(ls);document.querySelectorAll('select[id^="fx1-"]').forEach(s=>s.dataset.lay=s.id.slice(4));document.querySelectorAll('select[id^="fx2-"]').forEach(s=>s.dataset.lay=s.id.slice(4));fillFxSelects()};
|
||||
loadFx();
|
||||
poll();
|
||||
</script>
|
||||
</body>
|
||||
|
||||
Reference in New Issue
Block a user