From d68538d285db38b62af95bbd7bd5218a0d707955 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20Kr=C3=BChlmann?= Date: Wed, 10 Dec 2025 23:00:18 +0100 Subject: [PATCH] Fix audio controls --- src/main/resources/static/js/admin.js | 76 +++++++++++++++++++++---- src/main/resources/templates/admin.html | 50 +++++++++------- 2 files changed, 93 insertions(+), 33 deletions(-) diff --git a/src/main/resources/static/js/admin.js b/src/main/resources/static/js/admin.js index 04bc1b2..6e92a6a 100644 --- a/src/main/resources/static/js/admin.js +++ b/src/main/resources/static/js/admin.js @@ -46,6 +46,8 @@ const audioDelayInput = document.getElementById('asset-audio-delay'); const audioSpeedInput = document.getElementById('asset-audio-speed'); const audioSpeedLabel = document.getElementById('asset-audio-speed-label'); const audioPitchInput = document.getElementById('asset-audio-pitch'); +const audioDelayLabel = document.getElementById('asset-audio-delay-label'); +const audioPitchLabel = document.getElementById('asset-audio-pitch-label'); const controlsPlaceholder = document.getElementById('asset-controls-placeholder'); const fileNameLabel = document.getElementById('asset-file-name'); const assetInspector = document.getElementById('asset-inspector'); @@ -56,6 +58,8 @@ const selectedAssetIdLabel = document.getElementById('selected-asset-id'); const selectedAssetBadges = document.getElementById('selected-asset-badges'); const selectedVisibilityBtn = document.getElementById('selected-asset-visibility'); const selectedDeleteBtn = document.getElementById('selected-asset-delete'); +const assetActionRow = document.getElementById('asset-actions'); +const assetActionButtons = Array.from(assetActionRow?.querySelectorAll('button') ?? []); const canvasResolutionLabel = document.getElementById('canvas-resolution'); const canvasScaleLabel = document.getElementById('canvas-scale'); const aspectLockState = new Map(); @@ -174,6 +178,27 @@ function setAudioSpeedLabel(percentValue) { audioSpeedLabel.textContent = `${formatted}x`; } +function formatDelayLabel(ms) { + const numeric = Math.max(0, parseInt(ms, 10) || 0); + if (numeric >= 1000) { + const seconds = numeric / 1000; + const decimals = Number.isInteger(seconds) ? 0 : 1; + return `${seconds.toFixed(decimals)}s`; + } + return `${numeric}ms`; +} + +function setAudioDelayLabel(value) { + if (!audioDelayLabel) return; + audioDelayLabel.textContent = formatDelayLabel(value); +} + +function setAudioPitchLabel(percentValue) { + if (!audioPitchLabel) return; + const numeric = Math.round(Math.max(0, percentValue)); + audioPitchLabel.textContent = `${numeric}%`; +} + function clamp(value, min, max) { return Math.min(max, Math.max(min, value)); } @@ -226,9 +251,18 @@ if (heightInput) heightInput.addEventListener('change', () => commitSizeChange() if (speedInput) speedInput.addEventListener('input', updatePlaybackFromInputs); if (volumeInput) volumeInput.addEventListener('input', updateVolumeFromInput); if (audioLoopInput) audioLoopInput.addEventListener('change', updateAudioSettingsFromInputs); -if (audioDelayInput) audioDelayInput.addEventListener('change', updateAudioSettingsFromInputs); -if (audioSpeedInput) audioSpeedInput.addEventListener('change', updateAudioSettingsFromInputs); -if (audioPitchInput) audioPitchInput.addEventListener('change', updateAudioSettingsFromInputs); +if (audioDelayInput) audioDelayInput.addEventListener('input', () => { + setAudioDelayLabel(audioDelayInput.value); + updateAudioSettingsFromInputs(); +}); +if (audioSpeedInput) audioSpeedInput.addEventListener('input', () => { + setAudioSpeedLabel(audioSpeedInput.value); + updateAudioSettingsFromInputs(); +}); +if (audioPitchInput) audioPitchInput.addEventListener('input', () => { + setAudioPitchLabel(audioPitchInput.value); + updateAudioSettingsFromInputs(); +}); if (selectedVisibilityBtn) { selectedVisibilityBtn.addEventListener('click', () => { const asset = getSelectedAsset(); @@ -1411,8 +1445,8 @@ function updateSelectedAssetControls(asset = getSelectedAsset()) { aspectLockInput.checked = isAspectLocked(asset.id); aspectLockInput.onchange = () => setAspectLock(asset.id, aspectLockInput.checked); } + const hideLayout = isAudioAsset(asset); if (layoutSection) { - const hideLayout = isAudioAsset(asset); layoutSection.classList.toggle('hidden', hideLayout); const layoutControls = layoutSection.querySelectorAll('input, button'); layoutControls.forEach((control) => { @@ -1420,6 +1454,14 @@ function updateSelectedAssetControls(asset = getSelectedAsset()) { control.classList.toggle('disabled', hideLayout); }); } + if (assetActionButtons.length) { + assetActionButtons.forEach((button) => { + const allowForAudio = button.dataset.audioEnabled === 'true'; + const disableButton = hideLayout && !allowForAudio; + button.disabled = disableButton; + button.classList.toggle('disabled', disableButton); + }); + } if (speedInput) { const percent = Math.round((asset.speed ?? 1) * 100); speedInput.value = Math.min(1000, Math.max(0, percent)); @@ -1455,10 +1497,15 @@ function updateSelectedAssetControls(asset = getSelectedAsset()) { }); if (showAudio) { audioLoopInput.checked = !!asset.audioLoop; - audioDelayInput.value = Math.max(0, asset.audioDelayMillis ?? 0); - audioSpeedInput.value = Math.round(Math.max(0.25, asset.audioSpeed ?? 1) * 100); - setAudioSpeedLabel(audioSpeedInput.value); - audioPitchInput.value = Math.round(Math.max(0.5, asset.audioPitch ?? 1) * 100); + const delayMs = clamp(Math.max(0, asset.audioDelayMillis ?? 0), 0, 30000); + audioDelayInput.value = delayMs; + setAudioDelayLabel(delayMs); + const audioSpeedPercent = clamp(Math.round(Math.max(0.25, asset.audioSpeed ?? 1) * 100), 25, 400); + audioSpeedInput.value = audioSpeedPercent; + setAudioSpeedLabel(audioSpeedPercent); + const pitchPercent = clamp(Math.round(Math.max(0.5, asset.audioPitch ?? 1) * 100), 50, 200); + audioPitchInput.value = pitchPercent; + setAudioPitchLabel(pitchPercent); } } } @@ -1587,11 +1634,18 @@ function updateAudioSettingsFromInputs() { const asset = getSelectedAsset(); if (!asset || !isAudioAsset(asset)) return; asset.audioLoop = !!audioLoopInput?.checked; - asset.audioDelayMillis = Math.max(0, parseInt(audioDelayInput?.value || '0', 10)); - const nextAudioSpeedPercent = Math.max(25, parseInt(audioSpeedInput?.value || '100', 10)); + const delayMs = clamp(Math.max(0, parseInt(audioDelayInput?.value || '0', 10)), 0, 30000); + asset.audioDelayMillis = delayMs; + setAudioDelayLabel(delayMs); + if (audioDelayInput) audioDelayInput.value = delayMs; + const nextAudioSpeedPercent = clamp(Math.max(25, parseInt(audioSpeedInput?.value || '100', 10)), 25, 400); setAudioSpeedLabel(nextAudioSpeedPercent); + if (audioSpeedInput) audioSpeedInput.value = nextAudioSpeedPercent; asset.audioSpeed = Math.max(0.25, (nextAudioSpeedPercent / 100)); - asset.audioPitch = Math.max(0.5, (parseInt(audioPitchInput?.value || '100', 10) / 100)); + const nextAudioPitchPercent = clamp(Math.max(50, parseInt(audioPitchInput?.value || '100', 10)), 50, 200); + setAudioPitchLabel(nextAudioPitchPercent); + if (audioPitchInput) audioPitchInput.value = nextAudioPitchPercent; + asset.audioPitch = Math.max(0.5, (nextAudioPitchPercent / 100)); const controller = ensureAudioController(asset); applyAudioSettings(controller, asset); persistTransform(asset); diff --git a/src/main/resources/templates/admin.html b/src/main/resources/templates/admin.html index 0578dd0..8925830 100644 --- a/src/main/resources/templates/admin.html +++ b/src/main/resources/templates/admin.html @@ -92,21 +92,6 @@ -
- - - - - - - - - -
@@ -151,10 +136,14 @@
-
- Delay (ms) - +
+
+
+ Delay + 0ms
+ +
0ms30s
@@ -164,13 +153,30 @@
0.25x4x
-
-
- Pitch (%) - +
+
+ Pitch + 100%
+ +
50%200%
+
+ + + + + + + + + +