voice2text/index.php

308 lines
No EOL
15 KiB
PHP
Executable file

<?php
require_once 'config.php';
// Check if this might be an MSAL redirect callback
// MSAL uses hash fragments for tokens, so we need to let the page load
// and let JavaScript handle the callback before PHP redirects
$isPotentialMSALCallback = !empty($_SERVER['HTTP_REFERER']) &&
strpos($_SERVER['HTTP_REFERER'], 'login.microsoftonline.com') !== false;
// If not authenticated and not a potential MSAL callback, redirect to login
if (!$isPotentialMSALCallback && !isAuthenticated()) {
header('Location: login.php');
exit;
}
// Get current user info (may be null if MSAL callback is being processed)
$user = getCurrentUser();
$isAuthenticated = isAuthenticated();
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Voice to Text</title>
<link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@400;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="style.css">
<script src="https://cdn.jsdelivr.net/npm/dompurify@2.3.3/dist/purify.min.js"></script>
</head>
<body>
<div class="app-container" id="appContainer">
<?php if ($isAuthenticated): ?>
<?php if (DEV_MODE): ?>
<div class="dev-mode-banner">
🔧 DEV MODE ACTIVE - Authentication Bypassed
</div>
<?php endif; ?>
<div class="user-header">
<div class="user-info">
<span class="user-name"><?php echo htmlspecialchars($user['name']); ?></span>
<span class="user-email"><?php echo htmlspecialchars($user['email']); ?></span>
</div>
<a href="logout.php" class="logout-btn">Logout</a>
</div>
<img src="V2T.svg" alt="Voice to Text" class="logo">
<div id="initialInstruction" class="initial-instruction">
Before we start, select output format and upload the Voice File (Max 350 Megabytes in size)
</div>
<div class="format-selection">
<label for="outputFormat">Output Format:</label>
<select id="outputFormat" name="outputFormat">
<option value="txt">Text Document</option>
<option value="vtt">VTT (WebVTT)</option>
<option value="srt">SRT (SubRip)</option>
</select>
</div>
<div class="translation-section">
<div class="translation-toggle">
<label class="toggle-label">
<input type="checkbox" id="enableTranslation" name="enableTranslation">
<span class="toggle-text">Translate with DeepL</span>
</label>
</div>
<div id="languageSelector" class="language-selector" style="display: none;">
<label for="targetLanguage">Translate to:</label>
<select id="targetLanguage" name="targetLanguage">
<option value="BG">Bulgarian</option>
<option value="CS">Czech</option>
<option value="DA">Danish</option>
<option value="DE">German</option>
<option value="EL">Greek</option>
<option value="EN-GB">English (British)</option>
<option value="EN-US" selected>English (American)</option>
<option value="ES">Spanish</option>
<option value="ET">Estonian</option>
<option value="FI">Finnish</option>
<option value="FR">French</option>
<option value="HU">Hungarian</option>
<option value="ID">Indonesian</option>
<option value="IT">Italian</option>
<option value="JA">Japanese</option>
<option value="KO">Korean</option>
<option value="LT">Lithuanian</option>
<option value="LV">Latvian</option>
<option value="NB">Norwegian (Bokmål)</option>
<option value="NL">Dutch</option>
<option value="PL">Polish</option>
<option value="PT-BR">Portuguese (Brazilian)</option>
<option value="PT-PT">Portuguese (European)</option>
<option value="RO">Romanian</option>
<option value="RU">Russian</option>
<option value="SK">Slovak</option>
<option value="SL">Slovenian</option>
<option value="SV">Swedish</option>
<option value="TR">Turkish</option>
<option value="UK">Ukrainian</option>
<option value="ZH">Chinese (simplified)</option>
</select>
</div>
</div>
<div class="file-upload-container">
<label for="fileUpload" class="file-upload-label">Upload Voice File</label>
<input type="file" id="fileUpload" name="voiceFile" hidden>
</div>
<div id="chatArea" class="chat-area"></div>
<button id="downloadButton" style="display: none;">Download Response</button>
<?php else: ?>
<!-- Loading/Authentication in progress -->
<div style="text-align: center; padding: 40px;">
<div style="color: #FFC407; font-size: 18px; margin-bottom: 20px;">
Processing authentication...
</div>
<div class="progress-bar">
<div class="progress-bar-fill"></div>
</div>
</div>
<?php endif; ?>
</div>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="https://alcdn.msauth.net/browser/2.38.1/js/msal-browser.min.js"></script>
<script>
// MSAL Configuration for handling OAuth redirect callback
const msalConfig = {
auth: {
clientId: '<?php echo AZURE_CLIENT_ID; ?>',
authority: '<?php echo AZURE_AUTHORITY; ?>',
redirectUri: '<?php echo AZURE_REDIRECT_URI; ?>'
},
cache: {
cacheLocation: 'sessionStorage',
storeAuthStateInCookie: false
}
};
const msalInstance = new msal.PublicClientApplication(msalConfig);
// Handle redirect callback from Azure AD
// This runs when Azure redirects back after authentication
msalInstance.handleRedirectPromise()
.then(async (response) => {
if (response) {
// Just authenticated - have access token
console.log('Authentication successful, creating session...');
try {
// Send token to PHP backend to create session
const validateResponse = await fetch('validate_token.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
accessToken: response.accessToken
})
});
const result = await validateResponse.json();
if (result.success) {
console.log('Session created, reloading page...');
// Session created - reload page to show app
window.location.href = 'index.php';
} else {
alert('Session creation failed: ' + result.error);
window.location.href = 'login.php';
}
} catch (error) {
console.error('Session creation error:', error);
alert('Failed to create session: ' + error.message);
window.location.href = 'login.php';
}
}
})
.catch((error) => {
console.error('MSAL redirect error:', error);
});
</script>
<script>
<?php if ($isAuthenticated): ?>
// Application functionality - only load if authenticated
$(document).ready(function() {
// Toggle language selector when translation is enabled/disabled
$('#enableTranslation').on('change', function() {
if ($(this).is(':checked')) {
$('#languageSelector').slideDown(300);
} else {
$('#languageSelector').slideUp(300);
}
});
$('#fileUpload').on('change', function() {
var file = this.files[0];
if (file) {
var formData = new FormData();
formData.append('voiceFile', file);
formData.append('outputFormat', $('#outputFormat').val());
formData.append('enableTranslation', $('#enableTranslation').is(':checked') ? '1' : '0');
formData.append('targetLanguage', $('#targetLanguage').val());
$('#chatArea').html('<div class="processing-container"><div class="processing-text">Processing audio file...</div><div class="progress-bar"><div class="progress-bar-fill"></div></div></div>');
$.ajax({
url: 'process.php',
type: 'POST',
data: formData,
processData: false,
contentType: false,
success: function(response) {
console.log('Server response:', response);
var data = JSON.parse(response);
console.log('Parsed data:', data);
if (data.success) {
var message = '<div class="message bot-message">';
// Show original transcription (limit size for display)
if (data.response) {
message += '<div class="transcription-section">';
message += '<h3 style="color: #FFC407; margin-bottom: 10px;">Original Transcription:</h3>';
// Truncate if too long for display
var truncatedResponse = data.response;
if (truncatedResponse.length > 10000) {
truncatedResponse = data.response.substring(0, 10000) + '<div style="color: #FFC407; font-style: italic; margin-top: 10px;">... (content truncated for display, download file for full content)</div>';
}
message += truncatedResponse;
message += '</div>';
}
// Show download link for original
if (data.fileUrl) {
message += '<div style="margin-top: 15px; margin-bottom: 15px;">';
message += '<a href="' + data.fileUrl + '" class="download-link" target="_blank">📥 Download Original ' + data.format.toUpperCase() + ' File</a>';
message += '</div>';
}
// Show translated transcription (limit size for display)
if (data.translatedResponse) {
message += '<div class="transcription-section" style="margin-top: 25px; padding-top: 20px; border-top: 1px solid #333;">';
message += '<h3 style="color: #FFC407; margin-bottom: 10px;">Translated Transcription:</h3>';
// Truncate if too long for display
var truncatedTranslated = data.translatedResponse;
if (truncatedTranslated.length > 10000) {
truncatedTranslated = data.translatedResponse.substring(0, 10000) + '<div style="color: #FFC407; font-style: italic; margin-top: 10px;">... (content truncated for display, download file for full content)</div>';
}
message += truncatedTranslated;
message += '</div>';
}
// Show download link for translated
if (data.translatedFileUrl) {
message += '<div style="margin-top: 15px; margin-bottom: 15px;">';
message += '<a href="' + data.translatedFileUrl + '" class="download-link" target="_blank">📥 Download Translated ' + data.format.toUpperCase() + ' File</a>';
message += '</div>';
}
message += '</div>';
$('#chatArea').html(message);
} else {
$('#chatArea').html('<div class="message error-message">' + data.error + '</div>');
}
},
error: function(xhr, status, error) {
console.error('AJAX Error:', status, error);
console.error('Response:', xhr.responseText);
var errorMsg = '<div class="message error-message">';
errorMsg += '<strong>Error processing file:</strong><br>';
if (xhr.status === 0) {
errorMsg += 'Cannot connect to server. Make sure the Python API is running on port 5010.<br>';
errorMsg += 'Run: <code>./start_api.sh</code>';
} else {
errorMsg += 'Status: ' + xhr.status + '<br>';
errorMsg += 'Error: ' + error;
}
errorMsg += '</div>';
$('#chatArea').html(errorMsg);
}
});
}
});
$('#downloadButton').on('click', function() {
var responseText = $('.bot-message').text();
var blob = new Blob([responseText], { type: 'text/plain' });
var url = URL.createObjectURL(blob);
var a = document.createElement('a');
a.href = url;
a.download = 'voice_to_text_response.txt';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
});
});
<?php endif; ?>
</script>
</body>
</html>