updated based on T's feedback

This commit is contained in:
2025-07-21 12:37:32 +02:00
parent 85ffeb480d
commit c960316774
4 changed files with 196 additions and 230 deletions

View File

@@ -74,6 +74,33 @@ import html from '../utils/html.js';
}
display_element.innerHTML = `
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
.key-icon {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 28px;
height: 24px;
padding: 2px 6px;
background: linear-gradient(145deg, #f0f0f0, #e0e0e0);
border: 1px solid #ccc;
border-radius: 4px;
box-shadow: 0 2px 2px rgba(0,0,0,0.1), inset 0 1px 0 rgba(255,255,255,0.6);
font-family: 'Segoe UI', Arial, sans-serif;
font-size: 11px;
font-weight: 600;
color: #333;
margin: 0 2px;
vertical-align: middle;
}
.key-icon.space {
min-width: 60px;
}
.key-icon i {
font-size: 12px;
}
</style>
<div style="text-align: center; padding: 20px;">
<div style="margin: 20px 0;">
<audio style="display: none;" id="playback-audio">
@@ -83,23 +110,55 @@ import html from '../utils/html.js';
<div style="margin: 30px 0;">
<p style="font-weight: bold; margin-bottom: 15px;">Listen to your recording and mark when you would lift:</p>
<p style="margin: 10px 0; font-size: 14px; color: #666;">You can use keyboard controls (space and arrow keys) to play and skip the recording.</p>
<p style="margin: 10px 0; font-size: 14px; color: #666;">Adjust playback speed for more precise control.</p>
<div class="space-y-4 text-sm text-gray-700">
<p class="text-base font-semibold text-black">
🎧 Listen to your recording and mark where you would lift:
</p>
<ul class="list-disc list-inside space-y-2">
<li>
Pause the audio at the lift point and click
<span class="italic">'Mark this as lifting point'</span> or press
<span class="key-icon">ENTER</span> to save it.
</li>
<li>
You can use the keyboard or the buttons below to mark lift points.
</li>
<li>
Keyboard shortcuts:
<ul class="list-inside list-disc ml-5 mt-1 space-y-1 text-gray-600">
<li><span class="key-icon space">SPACE</span> Play/Pause</li>
<li><span class="key-icon"><i class="fas fa-arrow-left"></i></span> Skip back 0.1s</li>
<li><span class="key-icon"><i class="fas fa-arrow-right"></i></span> Skip forward 0.1s</li>
<li><span class="key-icon">ENTER</span> Mark lift point</li>
</ul>
</li>
<li>
Adjust playback speed if you need more precise control.
</li>
</ul>
</div>
<div style="max-width: 500px; margin: 0 auto; background: #f9f9f9; padding: 20px; border-radius: 10px;">
<div style="margin-bottom: 20px; display: flex; gap: 10px; align-items: center; justify-content: center; flex-wrap: wrap;">
<button id="play-pause-btn" style="
padding: 12px 24px;
font-size: 16px;
background-color: #4caf50;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
min-width: 100px;
">▶ Play</button>
<div style="margin-bottom: 20px; text-align: center;">
<div style="margin-bottom: 15px; font-size: 14px; color: #666;">
Playing at 0.5x speed
</div>
<div style="display: flex; gap: 10px; align-items: center; justify-content: center; flex-wrap: wrap;">
<button id="play-pause-btn" style="
padding: 12px 24px;
font-size: 16px;
background-color: #4caf50;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
min-width: 140px;
">▶ Play <span class="key-icon space">SPACE</span></button>
</div>
<!--
<div style="display: flex; gap: 5px; align-items: center;">
<span style="font-size: 14px; color: #666;">Speed:</span>
<button id="speed-025" class="speed-btn" data-speed="0.25" style="
@@ -130,6 +189,7 @@ import html from '../utils/html.js';
cursor: pointer;
">1x</button>
</div>
-->
</div>
<div style="margin: 20px 0; display: flex; gap: 10px; align-items: center; justify-content: center; flex-wrap: wrap;">
@@ -141,7 +201,7 @@ import html from '../utils/html.js';
border: none;
border-radius: 5px;
cursor: pointer;
">⏪ Skip Back 5%</button>
">⏪ Skip Back 0.1s <span class="key-icon"><i class="fas fa-arrow-left"></i></span></button>
<button id="skip-forward-btn" style="
padding: 8px 16px;
@@ -151,9 +211,10 @@ import html from '../utils/html.js';
border: none;
border-radius: 5px;
cursor: pointer;
">Skip Forward 5% ⏩</button>
">Skip Forward 0.1s <span class="key-icon"><i class="fas fa-arrow-right"></i></span> ⏩</button>
</div>
<!--
<div id="current-time-display" style="
font-size: 18px;
font-weight: bold;
@@ -167,21 +228,32 @@ import html from '../utils/html.js';
<span>Duration: <span id="duration-display">0:00</span></span>
<span>Progress: <span id="progress-display">0%</span></span>
</div>
-->
<div style="font-size: 11px; color: #888; text-align: center; margin-bottom: 15px; line-height: 1.4;">
<strong>Keyboard shortcuts:</strong> Spacebar = Play/Pause | ← → = Skip Back/Forward | Enter = Mark Lift Point
</div>
<div style="margin: 20px 0; text-align: center;">
<button id="mark-lift-point-btn" style="
padding: 12px 24px;
font-size: 16px;
background-color: #ff9800;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
">Mark This as Lift Point</button>
<div style="margin: 20px 0;">
<!-- Progress Bar -->
<div style="margin-bottom: 20px;">
<div style="display: flex; justify-content: space-between; font-size: 12px; color: #666; margin-bottom: 5px;">
<span id="start-time">0:00</span>
<span id="current-time">0:00</span>
<span id="end-time">0:00</span>
</div>
<div style="width: 100%; background-color: #ddd; border-radius: 10px; height: 8px; position: relative;">
<div id="progress-bar" style="background-color: #007cba; height: 100%; border-radius: 10px; width: 0%; transition: width 0.1s ease;"></div>
</div>
</div>
<div style="text-align: center;">
<button id="mark-lift-point-btn" style="
padding: 12px 24px;
font-size: 16px;
background-color: #ff9800;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
">Mark This as Lift Point <span class="key-icon">ENTER</span></button>
</div>
</div>
<div id="marked-point-display" style="
@@ -220,9 +292,11 @@ import html from '../utils/html.js';
setupMarkingEvents(recordingData, audioBlob) {
const audio = document.getElementById('playback-audio');
const playPauseBtn = document.getElementById('play-pause-btn');
const currentTimeDisplay = document.getElementById('current-time-display');
const durationDisplay = document.getElementById('duration-display');
const progressDisplay = document.getElementById('progress-display');
// Progress bar elements
const startTimeDisplay = document.getElementById('start-time');
const currentTimeDisplay = document.getElementById('current-time');
const endTimeDisplay = document.getElementById('end-time');
const progressBar = document.getElementById('progress-bar');
const skipBackBtn = document.getElementById('skip-back-btn');
const skipForwardBtn = document.getElementById('skip-forward-btn');
const markLiftPointBtn = document.getElementById('mark-lift-point-btn');
@@ -250,15 +324,16 @@ import html from '../utils/html.js';
// Update display function
const updateDisplay = () => {
const currentTime = audio.currentTime;
const progress = audioDuration > 0 ? (currentTime / audioDuration * 100).toFixed(1) : 0;
const progress = audioDuration > 0 ? (currentTime / audioDuration * 100) : 0;
currentTimeDisplay.textContent = `Current position: ${formatPreciseTime(currentTime)}`;
progressDisplay.textContent = `${progress}%`;
currentTimeDisplay.textContent = formatTime(currentTime);
progressBar.style.width = `${progress}%`;
};
// Update submit button state
let isPlaybackConfirmed = false;
const updateSubmitButton = () => {
if (liftPointTime !== null) {
if (liftPointTime !== null && isPlaybackConfirmed) {
submitButton.disabled = false;
submitButton.style.opacity = '1';
submitButton.style.cursor = 'pointer';
@@ -269,6 +344,58 @@ import html from '../utils/html.js';
}
};
// Function to play back from marked point for confirmation
const playbackFromMarkedPoint = () => {
if (liftPointTime === null) return;
// Store current position to restore later
const originalPosition = audio.currentTime;
// Show confirmation message
markedPointDisplay.innerHTML = `
<div style="text-align: center; padding: 10px; background: #fff3cd; border: 1px solid #ffd93d; border-radius: 5px; margin: 10px 0;">
<p style="margin: 5px 0; color: #856404;">🔊 Playing back from marked point...</p>
<p style="margin: 5px 0; font-size: 14px; color: #856404;">If this sounds right, press Submit. Otherwise, choose a new mark point.</p>
</div>
`;
// Set audio to marked point and play
audio.currentTime = liftPointTime;
updateDisplay();
// Play for a few seconds (3 seconds or until end)
const playbackDuration = 3;
const endTime = Math.min(liftPointTime + playbackDuration, audioDuration);
audio.play();
// Set up event to stop playback after duration and restore position
const onTimeUpdate = () => {
if (audio.currentTime >= endTime) {
audio.pause();
audio.removeEventListener('timeupdate', onTimeUpdate);
// Restore original position
audio.currentTime = originalPosition;
updateDisplay();
// Update display to show confirmation
markedPointDisplay.innerHTML = `
<div style="text-align: center; padding: 10px; background: #d1edff; border: 1px solid #3498db; border-radius: 5px; margin: 10px 0;">
<p style="margin: 5px 0; color: #2c3e50;">✓ Lift point marked at: ${formatPreciseTime(liftPointTime)}</p>
<p style="margin: 5px 0; font-size: 14px; color: #2c3e50;">Preview completed. Press Submit to confirm, <span class="key-icon">ENTER</span> to play again, or mark a new point.</p>
</div>
`;
// Enable submit button
isPlaybackConfirmed = true;
updateSubmitButton();
}
};
audio.addEventListener('timeupdate', onTimeUpdate);
};
// Check if we have a stored duration from the recording
const storedDuration = recordingData.audio_duration;
@@ -282,7 +409,8 @@ import html from '../utils/html.js';
// Use stored duration if available and valid
if (storedDuration && isFinite(storedDuration) && storedDuration > 0) {
audioDuration = storedDuration;
durationDisplay.textContent = formatTime(audioDuration);
startTimeDisplay.textContent = '0:00';
endTimeDisplay.textContent = formatTime(audioDuration);
updateDisplay();
updateSubmitButton();
console.log('Using stored duration:', audioDuration);
@@ -292,7 +420,8 @@ import html from '../utils/html.js';
// Otherwise try to get duration from audio element
if (audio.duration && isFinite(audio.duration) && audio.duration > 0) {
audioDuration = audio.duration;
durationDisplay.textContent = formatTime(audioDuration);
startTimeDisplay.textContent = '0:00';
endTimeDisplay.textContent = formatTime(audioDuration);
updateDisplay();
updateSubmitButton();
console.log('Audio controls setup complete, duration:', audioDuration);
@@ -305,7 +434,8 @@ import html from '../utils/html.js';
// Fallback: estimate duration based on blob size (rough approximation)
const estimatedDuration = Math.max(1, audioBlob.size / 8000); // ~8KB per second rough estimate
audioDuration = estimatedDuration;
durationDisplay.textContent = formatTime(audioDuration) + ' (est)';
startTimeDisplay.textContent = '0:00';
endTimeDisplay.textContent = formatTime(audioDuration);
updateDisplay();
updateSubmitButton();
console.log('Using estimated duration:', audioDuration);
@@ -336,7 +466,8 @@ import html from '../utils/html.js';
// Force load the audio
audio.load();
// Speed control buttons
// Speed control buttons (commented out - using fixed 0.5x speed)
/*
const speedButtons = document.querySelectorAll('.speed-btn');
speedButtons.forEach(btn => {
btn.addEventListener('click', () => {
@@ -354,16 +485,17 @@ import html from '../utils/html.js';
console.log('Playback speed set to:', speed);
});
});
*/
// Skip buttons (5% of total duration)
// Skip buttons (0.1 second increments)
skipBackBtn.addEventListener('click', () => {
const skipAmount = audioDuration * 0.05; // 5% of duration
const skipAmount = 0.1; // 0.1 seconds
audio.currentTime = Math.max(0, audio.currentTime - skipAmount);
updateDisplay();
});
skipForwardBtn.addEventListener('click', () => {
const skipAmount = audioDuration * 0.05; // 5% of duration
const skipAmount = 0.1; // 0.1 seconds
audio.currentTime = Math.min(audioDuration, audio.currentTime + skipAmount);
updateDisplay();
});
@@ -380,14 +512,14 @@ import html from '../utils/html.js';
// Audio play event
audio.addEventListener('play', () => {
isPlaying = true;
playPauseBtn.textContent = '⏸ Pause';
playPauseBtn.innerHTML = '⏸ Pause <span class="key-icon space">SPACE</span>';
playPauseBtn.style.backgroundColor = '#f44336';
});
// Audio pause event
audio.addEventListener('pause', () => {
isPlaying = false;
playPauseBtn.textContent = '▶ Play';
playPauseBtn.innerHTML = '▶ Play <span class="key-icon space">SPACE</span>';
playPauseBtn.style.backgroundColor = '#4caf50';
});
@@ -414,16 +546,16 @@ import html from '../utils/html.js';
case 'ArrowLeft':
e.preventDefault();
// Skip back with left arrow
const skipBackAmount = audioDuration * 0.05;
// Skip back with left arrow (0.1 seconds)
const skipBackAmount = 0.1;
audio.currentTime = Math.max(0, audio.currentTime - skipBackAmount);
updateDisplay();
break;
case 'ArrowRight':
e.preventDefault();
// Skip forward with right arrow
const skipForwardAmount = audioDuration * 0.05;
// Skip forward with right arrow (0.1 seconds)
const skipForwardAmount = 0.1;
audio.currentTime = Math.min(audioDuration, audio.currentTime + skipForwardAmount);
updateDisplay();
break;
@@ -432,8 +564,9 @@ import html from '../utils/html.js';
e.preventDefault();
// Mark lift point with Enter
liftPointTime = audio.currentTime;
markedPointDisplay.textContent = `✓ Lift point marked at: ${formatPreciseTime(liftPointTime)}`;
isPlaybackConfirmed = false;
updateSubmitButton();
playbackFromMarkedPoint();
break;
}
});
@@ -441,8 +574,9 @@ import html from '../utils/html.js';
// Mark lift point button
markLiftPointBtn.addEventListener('click', () => {
liftPointTime = audio.currentTime;
markedPointDisplay.textContent = `✓ Lift point marked at: ${formatPreciseTime(liftPointTime)}`;
isPlaybackConfirmed = false;
updateSubmitButton();
playbackFromMarkedPoint();
});
// Submit button