updated based on T's feedback
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user