Per-track scenes: auto-apply effect + text + image when a playlist track starts

Each playlist track gets an optional scene (🎬) bound to it: pick the current
effect, a ticker text and an overlay image. When the track plays, the scene is
applied automatically — a cue/queue per song. Scene image is a dedicated output
layer (also captured in recordings).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
lucianoandClaude Opus 4.8 committed 2026-06-19 16:08:50 +02:00
1 parent 67a4a32dea
commit 8925fa50f9
5 files changed
+113 -1

No files matched your search

+11
View File
@@ -88,6 +88,11 @@ function composeFrame() {
recCtx.globalAlpha = 1;
recCtx.globalCompositeOperation = 'source-over';
if (sceneImage.classList.contains('show') && sceneImage.src) {
const r = sceneImage.getBoundingClientRect();
try { recCtx.drawImage(sceneImage, r.x * sx, r.y * sy, r.width * sx, r.height * sy); } catch (e) {}
}
for (const l of logos) {
if (!l.classList.contains('show')) continue;
const r = l.getBoundingClientRect();
@@ -187,6 +192,8 @@ function loadCustomTexture(dataUrl) {
img.src = dataUrl;
}
const sceneImage = $('#scene-image');
// Two independent logos.
const logos = [$('#logo-0'), $('#logo-1')];
function setLogo(i, url) {
@@ -294,6 +301,10 @@ djv.onControl(async (m) => {
case 'imgNext': showImage(imgIndex + 1); break;
case 'imgPrev': showImage(imgIndex - 1); break;
case 'sceneImage':
if (m.path) { sceneImage.src = toFileURL(m.path); sceneImage.classList.add('show'); hideHint(); }
else { sceneImage.classList.remove('show'); sceneImage.removeAttribute('src'); }
break;
case 'logo': setLogo(m.index, m.path ? toFileURL(m.path) : ''); break;
case 'logoX': logos[m.index].style.left = m.value + '%'; break;
case 'logoY': logos[m.index].style.top = m.value + '%'; break;