ai-video / templates /index.html
ruv's picture
Update templates/index.html
74aac0f verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Media Stream Capture and Analysis</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
<style>
body {
background-color: #f8f9fa;
}
.container {
max-width: 720px;
margin-top: 20px;
}
#video {
width: 100%;
height: auto;
background-color: #ddd;
margin-bottom: 20px;
}
.button-container {
display: flex;
flex-direction: column;
align-items: center;
}
.btn-group-toggle {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
}
.btn-group-toggle .btn {
width: auto;
margin: 0 10px;
}
.btn-group {
margin-bottom: 10px;
}
.btn-secondary.active {
background-color: #6c757d;
border-color: #6c757d;
color: white;
}
.settings-panel {
display: none;
margin-bottom: 20px;
}
.settings-panel .nav-tabs {
margin-bottom: 10px;
}
#formattedMarkdown {
background-color: #fff;
border: 1px solid #ddd;
padding: 10px;
margin-top: 20px;
border-radius: 5px;
max-height: 200px;
overflow-y: auto;
}
</style>
</head>
<body>
<div class="container text-center">
<h2 class="my-4">GPT-4o Media Stream Capture & Analysis</h2>
<p class="mb-4">A web app to capture and analyze media streams from your webcam, desktop, or specific apps. Easily switch sources and capture frames at intervals. Use AI for insightful frame analysis and summaries.</p>
<video id="video" class="mb-4" autoplay></video>
<div class="button-container">
<div class="btn-group btn-group-toggle mb-4" data-toggle="buttons">
<label class="btn btn-secondary">
<input type="radio" name="options" id="shareWebcam" autocomplete="off"> Share Webcam
</label>
<label class="btn btn-secondary">
<input type="radio" name="options" id="shareScreen" autocomplete="off"> Share Screen
</label>
<label class="btn btn-secondary">
<input type="radio" name="options" id="shareApplication" autocomplete="off"> Share Application
</label>
</div>
<div class="btn-group mb-4">
<button id="startCapture" class="btn btn-primary">📸 Start Capture</button>
<button id="stopCapture" class="btn btn-danger">🛑 Stop Capture</button>
<button id="toggleSettings" class="btn btn-info">⚙️ Settings</button>
</div>
</div>
<div class="settings-panel card card-body">
<ul class="nav nav-tabs" id="settingsTabs" role="tablist">
<li class="nav-item">
<a class="nav-link active" id="prompt-tab" data-toggle="tab" href="#prompt" role="tab" aria-controls="prompt" aria-selected="true">Prompt</a>
</li>
<li class="nav-item">
<a class="nav-link" id="settings-tab" data-toggle="tab" href="#settings" role="tab" aria-controls="settings" aria-selected="false">Settings</a>
</li>
<li class="nav-item">
<a class="nav-link" id="api-tab" data-toggle="tab" href="#api" role="tab" aria-controls="api" aria-selected="false">API Config</a>
</li>
</ul>
<div class="tab-content" id="settingsTabsContent">
<div class="tab-pane fade show active" id="prompt" role="tabpanel" aria-labelledby="prompt-tab">
<textarea id="customPrompt" class="form-control" rows="3" placeholder="Enter custom prompt here...">Analyze this frame</textarea>
</div>
<div class="tab-pane fade" id="settings" role="tabpanel" aria-labelledby="settings-tab">
<input type="number" id="refreshRate" class="form-control" placeholder="Refresh rate in seconds" value="15">
</div>
<div class="tab-pane fade" id="api" role="tabpanel" aria-labelledby="api-tab">
<input type="password" id="apiKey" class="form-control" placeholder="OpenAI API Key">
<input type="checkbox" id="showApiKey"> Show API Key
</div>
<button id="saveSettings" class="btn btn-success mt-3">Save Settings</button>
</div>
</div>
<div id="formattedMarkdown" class="text-left"></div>
</div>
<div class="container text-center">
created by rUv
</div>
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@popperjs/[email protected]/dist/umd/popper.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
<script src="/static/script.js"></script>
<script>
const video = document.getElementById('video');
const formattedMarkdownDiv = document.getElementById('formattedMarkdown');
const toggleSettingsButton = document.getElementById('toggleSettings');
const settingsPanel = document.querySelector('.settings-panel');
const saveSettingsButton = document.getElementById('saveSettings');
const startCaptureButton = document.getElementById('startCapture');
const stopCaptureButton = document.getElementById('stopCapture');
const showApiKeyCheckbox = document.getElementById('showApiKey');
const apiKeyInput = document.getElementById('apiKey');
let captureInterval;
let refreshRate = 15;
let customPrompt = "Analyze this frame";
let apiKey = "";
let activeStream = null;
let isProcessing = false; // Flag to track if processing is in progress
function logMessage(message) {
const p = document.createElement('p');
p.textContent = message;
formattedMarkdownDiv.appendChild(p);
formattedMarkdownDiv.scrollTop = formattedMarkdownDiv.scrollHeight; // Scroll to bottom
}
async function startWebcamStream() {
activeStream = await navigator.mediaDevices.getUserMedia({ video: true });
logMessage("Webcam stream started.");
video.srcObject = activeStream;
}
async function startScreenShare() {
activeStream = await navigator.mediaDevices.getDisplayMedia({ video: true });
logMessage("Screen share started.");
video.srcObject = activeStream;
}
async function startApplicationShare() {
activeStream = await navigator.mediaDevices.getDisplayMedia({
video: {
cursor: "always",
displaySurface: "application"
}
});
logMessage("Application share started.");
video.srcObject = activeStream;
}
async function handleStreamSelection(event) {
if (isProcessing) {
event.stopImmediatePropagation(); // Stop event propagation immediately
return; // Terminate the function
}
isProcessing = true; // Set the flag to indicate processing is in progress
// Remove active class from all buttons
document.querySelectorAll('.btn-group-toggle .btn').forEach(btn => btn.classList.remove('active'));
// Add active class to the clicked button
event.target.closest('.btn').classList.add('active');
const selectedButton = event.target.closest('.btn').querySelector('input');
if (!selectedButton) {
console.error('No input element found inside the clicked button.');
isProcessing = false; // Reset the flag
return;
}
const selectedButtonId = selectedButton.id;
if (activeStream) {
// Stop the previous stream
let tracks = activeStream.getTracks();
tracks.forEach(track => track.stop());
activeStream = null;
}
if (selectedButtonId === 'shareWebcam') {
await startWebcamStream();
} else if (selectedButtonId === 'shareScreen') {
await startScreenShare();
} else if (selectedButtonId === 'shareApplication') {
await startApplicationShare();
}
isProcessing = false; // Reset the flag after processing is complete
}
// Event delegation for stream selection buttons
document.body.addEventListener('click', (event) => {
if (event.target.closest('.btn-group-toggle .btn')) {
handleStreamSelection(event);
}
});
// Toggle settings panel
toggleSettingsButton.addEventListener('click', () => {
settingsPanel.style.display = settingsPanel.style.display === 'none' ? 'block' : 'none';
logMessage("");
});
// Save settings
saveSettingsButton.addEventListener('click', () => {
customPrompt = document.getElementById('customPrompt').value || "Analyze this frame";
refreshRate = document.getElementById('refreshRate').value || 15;
apiKey = document.getElementById('apiKey').value || "";
settingsPanel.style.display = 'none';
logMessage("Settings saved.");
});
// Show/Hide API Key
showApiKeyCheckbox.addEventListener('change', () => {
apiKeyInput.type = showApiKeyCheckbox.checked ? 'text' : 'password';
});
// Start Capture
startCaptureButton.addEventListener('click', () => {
if (isProcessing || captureInterval) {
return; // Terminate if processing is in progress or capture is already running
}
isProcessing = true; // Set the flag to indicate processing is in progress
captureFrame(); // Capture the first frame immediately
captureInterval = setInterval(captureFrame, refreshRate * 1000); // Capture every `refreshRate` seconds
logMessage("Capture started.");
isProcessing = false; // Reset the flag after processing is complete
});
// Stop Capture
stopCaptureButton.addEventListener('click', () => {
if (isProcessing || !captureInterval) {
return; // Terminate if processing is in progress or capture is not running
}
isProcessing = true; // Set the flag to indicate processing is in progress
clearInterval(captureInterval);
captureInterval = null;
logMessage("Capture stopped.");
isProcessing = false; // Reset the flag after processing is complete
});
function captureFrame() {
const canvas = document.createElement('canvas');
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
const context = canvas.getContext('2d');
context.drawImage(video, 0, 0, canvas.width, canvas.height);
const dataUrl = canvas.toDataURL('image/jpeg');
logMessage("Frame captured and sent to API.");
fetch('/process_frame', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ image: dataUrl, prompt: customPrompt, api_key: apiKey })
})
.then(response => response.json())
.then(data => {
const formattedMarkdown = marked.parse(data.response); // Fixed function call
formattedMarkdownDiv.innerHTML = formattedMarkdown;
formattedMarkdownDiv.scrollTop = formattedMarkdownDiv.scrollHeight; // Scroll to bottom
})
.catch(error => {
console.error('Error:', error);
logMessage(`Error: ${error.message}`);
});
}
</script>
</body>
</html>