Mango.Nop.Plugins/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Views/Extras/VoiceRecorder.cshtml

240 lines
10 KiB
Plaintext

@{
Layout = "_ConfigurePlugin";
}
@await Component.InvokeAsync("StoreScopeConfiguration")
<form asp-action="ReceiveVoiceRecording" asp-controller="FruitBankAdmin" method="post">
@Html.AntiForgeryToken()
</form>
<div class="card card-default">
<div class="card-body">
<h4>Voice Recorder</h4>
<p>Click the button below to start recording your voice message.</p>
<div class="alert alert-info">
<i class="fas fa-info-circle"></i> <strong>Important:</strong> When you click "Start Recording", your browser will ask for permission to use your microphone. Please click "Allow" to enable voice recording.
</div>
<div id="permissionInstructions" class="alert alert-warning" style="display:none;">
<strong><i class="fas fa-exclamation-triangle"></i> Microphone Permission Required</strong>
<p class="mb-2">Your browser blocked microphone access. To enable it:</p>
<ul class="mb-2">
<li><strong>Chrome/Edge:</strong> Click the camera/microphone icon in the address bar (or the lock icon) → Site settings → Allow Microphone</li>
<li><strong>Firefox:</strong> Click the microphone icon in the address bar → Click the X to remove the block → Refresh the page</li>
<li><strong>Safari:</strong> Safari menu → Settings for This Website → Microphone → Allow</li>
</ul>
<p class="mb-0">After allowing permission, <strong>refresh this page</strong> and try again.</p>
</div>
<div class="form-group row">
<div class="col-md-9">
<button type="button" id="recordButton" class="btn btn-primary">
<i class="fas fa-microphone"></i> Start Recording
</button>
<button type="button" id="stopButton" class="btn btn-danger" style="display:none;">
<i class="fas fa-stop"></i> Stop Recording
</button>
<span id="recordingStatus" style="margin-left: 15px; font-weight: bold;"></span>
</div>
</div>
<div class="form-group row" id="audioPlaybackSection" style="display:none;">
<div class="col-md-9">
<audio id="audioPlayback" controls style="width: 100%;"></audio>
</div>
</div>
<div class="form-group row" id="sendSection" style="display:none;">
<div class="col-md-9">
<button type="button" id="sendButton" class="btn btn-success">
<i class="fas fa-paper-plane"></i> Send to API
</button>
</div>
</div>
<div class="form-group row">
<div class="col-md-9">
<div id="responseMessage" class="alert" style="display:none;"></div>
<div id="transcriptionResult" style="display:none; margin-top: 15px;">
<h5>Transcription:</h5>
<div class="card">
<div class="card-body">
<p id="transcriptionText" style="white-space: pre-wrap;"></p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
let mediaRecorder;
let audioChunks = [];
let audioBlob;
const recordButton = document.getElementById('recordButton');
const stopButton = document.getElementById('stopButton');
const sendButton = document.getElementById('sendButton');
const recordingStatus = document.getElementById('recordingStatus');
const audioPlayback = document.getElementById('audioPlayback');
const audioPlaybackSection = document.getElementById('audioPlaybackSection');
const sendSection = document.getElementById('sendSection');
const responseMessage = document.getElementById('responseMessage');
const transcriptionResult = document.getElementById('transcriptionResult');
const transcriptionText = document.getElementById('transcriptionText');
const permissionInstructions = document.getElementById('permissionInstructions');
recordButton.addEventListener('click', async () => {
try {
// Hide permission instructions if shown
permissionInstructions.style.display = 'none';
// Check if we're on HTTPS or localhost
const isSecure = window.location.protocol === 'https:' ||
window.location.hostname === 'localhost' ||
window.location.hostname === '127.0.0.1';
if (!isSecure) {
showMessage('Error: Microphone access requires HTTPS connection. Please access this page via HTTPS.', 'danger');
return;
}
// Check if getUserMedia is supported
if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
showMessage('Error: Your browser does not support audio recording. Please use a modern browser like Chrome, Firefox, or Edge.', 'danger');
return;
}
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
mediaRecorder = new MediaRecorder(stream);
audioChunks = [];
mediaRecorder.ondataavailable = (event) => {
audioChunks.push(event.data);
};
mediaRecorder.onstop = () => {
audioBlob = new Blob(audioChunks, { type: 'audio/webm' });
const audioUrl = URL.createObjectURL(audioBlob);
audioPlayback.src = audioUrl;
audioPlaybackSection.style.display = 'block';
sendSection.style.display = 'block';
// Stop all tracks to release the microphone
stream.getTracks().forEach(track => track.stop());
};
mediaRecorder.start();
recordButton.style.display = 'none';
stopButton.style.display = 'inline-block';
recordingStatus.textContent = 'Recording...';
recordingStatus.style.color = 'red';
audioPlaybackSection.style.display = 'none';
sendSection.style.display = 'none';
} catch (error) {
console.error('Error accessing microphone:', error);
let errorMessage = 'Error: Could not access microphone. ';
let showInstructions = false;
if (error.name === 'NotAllowedError' || error.name === 'PermissionDeniedError') {
errorMessage = 'Microphone access was denied. Please see the instructions below to enable microphone access.';
showInstructions = true;
} else if (error.name === 'NotFoundError' || error.name === 'DevicesNotFoundError') {
errorMessage += 'No microphone found. Please connect a microphone and try again.';
} else if (error.name === 'NotReadableError' || error.name === 'TrackStartError') {
errorMessage += 'Microphone is already in use by another application. Please close other applications using the microphone.';
} else if (error.name === 'OverconstrainedError' || error.name === 'ConstraintNotSatisfiedError') {
errorMessage += 'Microphone does not meet the required constraints.';
} else if (error.name === 'NotSupportedError') {
errorMessage += 'This page must be accessed via HTTPS to use the microphone.';
} else if (error.name === 'TypeError') {
errorMessage += 'This page must be accessed via HTTPS to use the microphone.';
} else {
errorMessage += error.message || 'Unknown error occurred.';
}
showMessage(errorMessage, 'danger');
// Show permission instructions if needed
if (showInstructions) {
permissionInstructions.style.display = 'block';
// Scroll to instructions
permissionInstructions.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
}
}
});
stopButton.addEventListener('click', () => {
if (mediaRecorder && mediaRecorder.state !== 'inactive') {
mediaRecorder.stop();
recordButton.style.display = 'inline-block';
stopButton.style.display = 'none';
recordingStatus.textContent = 'Recording stopped';
recordingStatus.style.color = 'green';
}
});
sendButton.addEventListener('click', async () => {
if (!audioBlob) {
showMessage('No audio recorded yet!', 'warning');
return;
}
const formData = new FormData();
formData.append('audioFile', audioBlob, 'recording.webm');
try {
sendButton.disabled = true;
sendButton.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Sending...';
// Get the antiforgery token
const token = document.querySelector('input[name="__RequestVerificationToken"]').value;
const response = await fetch('@Url.Action("ReceiveVoiceRecording", "FruitBankAdmin")', {
method: 'POST',
headers: {
'RequestVerificationToken': token
},
body: formData
});
const result = await response.json();
if (response.ok && result.success) {
showMessage('Audio transcribed successfully!', 'success');
// Display transcription
if (result.transcription) {
transcriptionText.textContent = result.transcription;
transcriptionResult.style.display = 'block';
}
} else {
showMessage('Error: ' + (result.message || 'Failed to transcribe audio'), 'danger');
}
} catch (error) {
console.error('Error sending audio:', error);
showMessage('Error: Failed to send audio to server', 'danger');
} finally {
sendButton.disabled = false;
sendButton.innerHTML = '<i class="fas fa-paper-plane"></i> Send to API';
}
});
function showMessage(message, type) {
responseMessage.textContent = message;
responseMessage.className = 'alert alert-' + type;
responseMessage.style.display = 'block';
setTimeout(() => {
responseMessage.style.display = 'none';
}, 5000);
}
</script>