Files
video-edit/templates/index.html
Mike Johnston eab55d2e01
All checks were successful
Build Images and Deploy / Update-PROD-Stack (push) Successful in 1m15s
cleanup and fixes
2026-02-09 18:50:06 -05:00

1053 lines
35 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Simple Video Editor</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
padding: 20px;
}
.container {
max-width: 1200px;
margin: 0 auto;
background: white;
border-radius: 12px;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
overflow: hidden;
}
.header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 30px;
text-align: center;
}
.header h1 {
font-size: 2.5em;
margin-bottom: 10px;
}
.header p {
opacity: 0.9;
font-size: 1.1em;
}
.content {
padding: 40px;
}
.section {
margin-bottom: 30px;
padding: 20px;
background: #f8f9fa;
border-radius: 8px;
border: 2px solid #e9ecef;
}
.section.hidden {
display: none;
}
.section h2 {
color: #667eea;
margin-bottom: 20px;
font-size: 1.5em;
}
.upload-area {
border: 3px dashed #667eea;
border-radius: 8px;
padding: 40px;
text-align: center;
background: white;
cursor: pointer;
transition: all 0.3s;
}
.upload-area:hover {
background: #f8f9fa;
border-color: #764ba2;
}
.upload-area.dragover {
background: #e7f3ff;
border-color: #667eea;
}
.upload-icon {
font-size: 3em;
margin-bottom: 15px;
}
input[type="file"] {
display: none;
}
.btn {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border: none;
padding: 12px 30px;
border-radius: 6px;
font-size: 1em;
cursor: pointer;
transition: transform 0.2s;
font-weight: 600;
}
.btn:hover {
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
}
.btn:disabled {
opacity: 0.5;
cursor: not-allowed;
transform: none;
}
.btn-secondary {
background: #6c757d;
}
.video-preview {
margin: 20px auto;
text-align: center;
position: relative;
display: flex;
justify-content: center;
}
.video-wrapper {
position: relative;
display: inline-block;
}
.video-preview video {
max-width: 100%;
max-height: 500px;
border-radius: 8px;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
display: block;
}
.video-controls {
margin: 15px 0;
padding: 15px;
background: white;
border-radius: 8px;
text-align: center;
}
.crop-canvas {
position: absolute;
top: 0;
left: 0;
cursor: crosshair;
border-radius: 8px;
display: none;
}
.crop-canvas.active {
display: block;
}
.crop-mode-btn {
padding: 10px 20px;
background: #28a745;
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
font-weight: 600;
transition: all 0.3s;
}
.crop-mode-btn:hover {
background: #218838;
}
.crop-mode-btn.active {
background: #dc3545;
}
.crop-hint {
color: #28a745;
font-size: 0.9em;
margin: 5px 0;
font-weight: 600;
}
.trim-buttons {
display: flex;
gap: 10px;
margin: 15px 0;
flex-wrap: wrap;
}
.trim-btn {
flex: 1;
min-width: 200px;
padding: 12px 20px;
background: #007bff;
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
font-weight: 600;
transition: all 0.3s;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
}
.trim-btn:hover {
background: #0056b3;
transform: translateY(-1px);
}
.trim-btn:active {
transform: translateY(0);
}
.quick-actions {
display: flex;
gap: 10px;
margin: 15px 0;
flex-wrap: wrap;
}
.quick-action-btn {
flex: 1;
min-width: 150px;
padding: 10px 16px;
background: white;
color: #667eea;
border: 2px solid #667eea;
border-radius: 6px;
cursor: pointer;
font-weight: 600;
transition: all 0.3s;
display: flex;
align-items: center;
justify-content: center;
gap: 6px;
}
.quick-action-btn:hover {
background: #667eea;
color: white;
transform: translateY(-1px);
}
.quick-action-btn.active {
background: #667eea;
color: white;
}
.current-time-display {
background: white;
padding: 10px 15px;
border-radius: 6px;
text-align: center;
font-size: 1.1em;
color: #667eea;
font-weight: 600;
border: 2px solid #e9ecef;
}
.scale-slider-container {
margin: 15px 0;
}
.scale-slider {
width: 100%;
height: 8px;
border-radius: 4px;
background: #e9ecef;
outline: none;
-webkit-appearance: none;
}
.scale-slider::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 20px;
height: 20px;
border-radius: 50%;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
cursor: pointer;
border: 2px solid white;
box-shadow: 0 2px 5px rgba(0,0,0,0.2);
}
.scale-slider::-moz-range-thumb {
width: 20px;
height: 20px;
border-radius: 50%;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
cursor: pointer;
border: 2px solid white;
box-shadow: 0 2px 5px rgba(0,0,0,0.2);
}
.scale-display {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
}
.scale-value {
font-size: 1.2em;
font-weight: 600;
color: #667eea;
}
.resolution-preview {
background: white;
padding: 10px;
border-radius: 6px;
margin-top: 10px;
font-size: 0.9em;
color: #495057;
}
.video-info {
background: white;
padding: 15px;
border-radius: 8px;
margin: 15px 0;
}
.video-info p {
margin: 5px 0;
color: #495057;
}
.form-group {
margin-bottom: 20px;
}
.form-group label {
display: block;
margin-bottom: 8px;
color: #495057;
font-weight: 600;
}
.form-group input,
.form-group select {
width: 100%;
padding: 10px;
border: 2px solid #e9ecef;
border-radius: 6px;
font-size: 1em;
transition: border-color 0.3s;
}
.form-group input:focus,
.form-group select:focus {
outline: none;
border-color: #667eea;
}
.form-row {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 15px;
}
.progress-container {
margin: 20px 0;
display: none;
}
.progress-bar {
width: 100%;
height: 30px;
background: #e9ecef;
border-radius: 15px;
overflow: hidden;
}
.progress-fill {
height: 100%;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
transition: width 0.3s;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-weight: 600;
}
.alert {
padding: 15px;
border-radius: 6px;
margin: 15px 0;
}
.alert-success {
background: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.alert-error {
background: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
.range-container {
margin: 20px 0;
}
.range-slider {
width: 100%;
}
.crop-controls {
background: white;
padding: 15px;
border-radius: 8px;
margin-top: 15px;
}
.preset-resolutions {
display: flex;
gap: 10px;
flex-wrap: wrap;
margin-bottom: 15px;
}
.preset-btn {
padding: 8px 16px;
background: white;
border: 2px solid #667eea;
color: #667eea;
border-radius: 6px;
cursor: pointer;
transition: all 0.3s;
font-weight: 600;
}
.preset-btn:hover,
.preset-btn.active {
background: #667eea;
color: white;
}
.button-group {
display: flex;
gap: 15px;
margin-top: 20px;
}
.loader {
border: 4px solid #f3f3f3;
border-top: 4px solid #667eea;
border-radius: 50%;
width: 40px;
height: 40px;
animation: spin 1s linear infinite;
margin: 20px auto;
display: none;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>🎬 Simple Video Editor</h1>
<p>Trim, crop, and convert your videos with ease</p>
</div>
<div class="content">
<!-- Step 1: Upload Video -->
<div class="section" id="upload-section">
<h2>1. Select Video</h2>
<div class="upload-area" id="upload-area">
<div class="upload-icon">📹</div>
<h3>Drag and drop your video here</h3>
<p>or click to browse</p>
<p style="margin-top: 10px; color: #6c757d; font-size: 0.9em;">
Supported formats: MP4, AVI, MOV, MKV, WMV, FLV, WebM
</p>
<input type="file" id="video-input" accept="video/*">
</div>
<div class="loader" id="upload-loader"></div>
</div>
<!-- Step 2: Edit Video -->
<div class="section hidden" id="edit-section">
<h2>2. Edit Video</h2>
<div class="video-preview">
<div class="video-wrapper">
<video id="video-preview" controls></video>
<canvas id="crop-canvas" class="crop-canvas"></canvas>
</div>
</div>
<div class="video-info" id="video-info"></div>
<!-- Quick Actions -->
<div class="form-group">
<label>⚡ Quick Actions</label>
<div class="quick-actions">
<button class="quick-action-btn" id="mute-audio-btn" title="Remove audio track from video">
🔇 Mute Audio
</button>
<button class="quick-action-btn" id="rotate-btn" title="Rotate video 90° clockwise">
🔄 Rotate 90°
</button>
</div>
</div>
<!-- Crop Controls -->
<div class="form-group">
<label>✂️ Crop Video</label>
<div class="video-controls">
<button class="crop-mode-btn" id="crop-mode-btn">🎯 Click to Draw Crop Area</button>
<p class="crop-hint" id="crop-hint" style="display: none;">Click and drag on the video to select crop area</p>
</div>
<div class="crop-controls">
<div class="form-row">
<div>
<label style="font-weight: normal;">X Position</label>
<input type="number" id="crop-x" value="0" min="0">
</div>
<div>
<label style="font-weight: normal;">Y Position</label>
<input type="number" id="crop-y" value="0" min="0">
</div>
</div>
<div class="form-row" style="margin-top: 10px;">
<div>
<label style="font-weight: normal;">Crop Width</label>
<input type="number" id="crop-width" placeholder="Full width" min="0">
</div>
<div>
<label style="font-weight: normal;">Crop Height</label>
<input type="number" id="crop-height" placeholder="Full height" min="0">
</div>
</div>
</div>
</div>
<!-- Trim Controls -->
<div class="form-group">
<label>⏱️ Trim Video</label>
<div class="current-time-display">
Video Position: <span id="current-time-display">0:00</span>
</div>
<div class="trim-buttons">
<button class="trim-btn" id="set-start-btn">
⏮️ Set Start Here
</button>
<button class="trim-btn" id="set-end-btn">
⏭️ Set End Here
</button>
</div>
<p style="font-size: 0.9em; color: #6c757d; text-align: center; margin: 10px 0;">
💡 Play the video and click the buttons to set trim points at the current playback position
</p>
<div class="form-row">
<div>
<label style="font-weight: normal;">Start Time (seconds)</label>
<input type="number" id="start-time" value="0" min="0" step="0.1">
</div>
<div>
<label style="font-weight: normal;">End Time (seconds)</label>
<input type="number" id="end-time" min="0" step="0.1">
</div>
</div>
</div>
<!-- Scale Controls -->
<div class="form-group">
<label>📐 Output Scale</label>
<div class="scale-slider-container">
<div class="scale-display">
<span>Scale: <span class="scale-value" id="scale-value-display">100%</span></span>
<span id="resolution-info" style="color: #6c757d;">Output: <strong id="output-resolution">--</strong></span>
</div>
<input type="range" class="scale-slider" id="scale-slider" min="10" max="100" value="100" step="5">
<div class="resolution-preview" id="resolution-preview">
<p style="margin: 0;">💡 <strong>How it works:</strong> Scale is applied to the cropped area (or original if no crop). At 50%, a 200x400 crop becomes 100x200.</p>
</div>
<div class="preset-resolutions" style="margin-top: 15px;">
<button class="preset-btn" data-scale="100">100% (Original)</button>
<button class="preset-btn" data-scale="75">75%</button>
<button class="preset-btn" data-scale="50">50%</button>
<button class="preset-btn" data-scale="25">25%</button>
</div>
</div>
</div>
<div class="button-group">
<button class="btn" id="process-btn">🎬 Process Video</button>
<button class="btn btn-secondary" id="reset-btn">🔄 Start Over</button>
</div>
<div class="loader" id="process-loader"></div>
<div id="alert-container"></div>
</div>
<!-- Step 3: Download -->
<div class="section hidden" id="download-section">
<h2>3. Download Processed Video</h2>
<p style="margin-bottom: 20px;">Your video has been processed successfully!</p>
<div class="button-group">
<button class="btn" id="download-btn">⬇️ Download Video</button>
<button class="btn btn-secondary" id="new-video-btn">🎬 Process Another Video</button>
</div>
</div>
</div>
</div>
<script>
let currentFileId = null;
let videoInfo = null;
let cropMode = false;
let cropStartX = 0;
let cropStartY = 0;
let cropRect = null;
let scalePercentage = 100;
let rotationDegrees = 0;
let muteAudio = false;
// Upload area handling
const uploadArea = document.getElementById('upload-area');
const videoInput = document.getElementById('video-input');
const uploadLoader = document.getElementById('upload-loader');
const processLoader = document.getElementById('process-loader');
const cropCanvas = document.getElementById('crop-canvas');
const cropCtx = cropCanvas.getContext('2d');
const cropModeBtn = document.getElementById('crop-mode-btn');
const cropHint = document.getElementById('crop-hint');
const videoPreview = document.getElementById('video-preview');
const currentTimeDisplay = document.getElementById('current-time-display');
const setStartBtn = document.getElementById('set-start-btn');
const setEndBtn = document.getElementById('set-end-btn');
uploadArea.addEventListener('click', () => videoInput.click());
uploadArea.addEventListener('dragover', (e) => {
e.preventDefault();
uploadArea.classList.add('dragover');
});
uploadArea.addEventListener('dragleave', () => {
uploadArea.classList.remove('dragover');
});
uploadArea.addEventListener('drop', (e) => {
e.preventDefault();
uploadArea.classList.remove('dragover');
const files = e.dataTransfer.files;
if (files.length > 0) {
handleFileUpload(files[0]);
}
});
videoInput.addEventListener('change', (e) => {
if (e.target.files.length > 0) {
handleFileUpload(e.target.files[0]);
}
});
async function handleFileUpload(file) {
const formData = new FormData();
formData.append('video', file);
uploadLoader.style.display = 'block';
try {
const response = await fetch('/upload', {
method: 'POST',
body: formData
});
const data = await response.json();
if (!response.ok) {
throw new Error(data.error || 'Upload failed');
}
currentFileId = data.file_id;
videoInfo = data.info;
// Show video preview
const videoPreview = document.getElementById('video-preview');
videoPreview.src = URL.createObjectURL(file);
// Update video info
const videoInfoDiv = document.getElementById('video-info');
videoInfoDiv.innerHTML = `
<p><strong>File:</strong> ${data.original_name}</p>
<p><strong>Resolution:</strong> ${videoInfo.width}x${videoInfo.height}</p>
<p><strong>Duration:</strong> ${videoInfo.duration.toFixed(2)} seconds</p>
<p><strong>Codec:</strong> ${videoInfo.codec}</p>
`;
// Set default values
document.getElementById('end-time').value = videoInfo.duration.toFixed(2);
scalePercentage = 100;
scaleSlider.value = 100;
updateScaleDisplay();
// Setup canvas for cropping
setupCropCanvas();
// Show edit section
document.getElementById('upload-section').classList.add('hidden');
document.getElementById('edit-section').classList.remove('hidden');
} catch (error) {
alert('Error uploading video: ' + error.message);
} finally {
uploadLoader.style.display = 'none';
}
}
// Setup crop canvas
function setupCropCanvas() {
const video = document.getElementById('video-preview');
// Wait for video to load metadata
video.addEventListener('loadedmetadata', () => {
resizeCropCanvas();
});
// Resize canvas when window resizes
window.addEventListener('resize', resizeCropCanvas);
}
function resizeCropCanvas() {
const video = document.getElementById('video-preview');
cropCanvas.width = video.offsetWidth;
cropCanvas.height = video.offsetHeight;
}
// Crop mode toggle
cropModeBtn.addEventListener('click', () => {
cropMode = !cropMode;
if (cropMode) {
cropCanvas.classList.add('active');
cropModeBtn.classList.add('active');
cropModeBtn.textContent = '❌ Cancel Crop Selection';
cropHint.style.display = 'block';
document.getElementById('video-preview').pause();
} else {
cropCanvas.classList.remove('active');
cropModeBtn.classList.remove('active');
cropModeBtn.textContent = '🎯 Click to Draw Crop Area';
cropHint.style.display = 'none';
clearCropCanvas();
}
});
// Canvas drawing for crop selection
let isDrawing = false;
let startX, startY;
cropCanvas.addEventListener('mousedown', (e) => {
if (!cropMode) return;
isDrawing = true;
const rect = cropCanvas.getBoundingClientRect();
startX = e.clientX - rect.left;
startY = e.clientY - rect.top;
});
cropCanvas.addEventListener('mousemove', (e) => {
if (!cropMode || !isDrawing) return;
const rect = cropCanvas.getBoundingClientRect();
const currentX = e.clientX - rect.left;
const currentY = e.clientY - rect.top;
drawCropRect(startX, startY, currentX - startX, currentY - startY);
});
cropCanvas.addEventListener('mouseup', (e) => {
if (!cropMode || !isDrawing) return;
isDrawing = false;
const rect = cropCanvas.getBoundingClientRect();
const endX = e.clientX - rect.left;
const endY = e.clientY - rect.top;
const width = Math.abs(endX - startX);
const height = Math.abs(endY - startY);
const x = Math.min(startX, endX);
const y = Math.min(startY, endY);
// Calculate actual video coordinates
const video = document.getElementById('video-preview');
const scaleX = videoInfo.width / video.offsetWidth;
const scaleY = videoInfo.height / video.offsetHeight;
const actualX = Math.round(x * scaleX);
const actualY = Math.round(y * scaleY);
const actualWidth = Math.round(width * scaleX);
const actualHeight = Math.round(height * scaleY);
// Update form fields
document.getElementById('crop-x').value = actualX;
document.getElementById('crop-y').value = actualY;
document.getElementById('crop-width').value = actualWidth;
document.getElementById('crop-height').value = actualHeight;
// Exit crop mode
cropMode = false;
cropModeBtn.classList.remove('active');
cropModeBtn.textContent = '🎯 Click to Draw Crop Area';
cropHint.style.display = 'none';
// Keep the rectangle visible for a moment
setTimeout(() => {
cropCanvas.classList.remove('active');
}, 500);
});
function drawCropRect(x, y, width, height) {
clearCropCanvas();
// Draw semi-transparent overlay
cropCtx.fillStyle = 'rgba(0, 0, 0, 0.5)';
cropCtx.fillRect(0, 0, cropCanvas.width, cropCanvas.height);
// Clear the selected area
cropCtx.clearRect(x, y, width, height);
// Draw border around selection
cropCtx.strokeStyle = '#667eea';
cropCtx.lineWidth = 3;
cropCtx.strokeRect(x, y, width, height);
// Draw dimensions text
cropCtx.fillStyle = '#667eea';
cropCtx.font = 'bold 14px sans-serif';
const text = `${Math.abs(Math.round(width))} × ${Math.abs(Math.round(height))}`;
cropCtx.fillText(text, x + 5, y - 5);
}
function clearCropCanvas() {
cropCtx.clearRect(0, 0, cropCanvas.width, cropCanvas.height);
}
// Update current time display
videoPreview.addEventListener('timeupdate', () => {
const currentTime = videoPreview.currentTime;
const minutes = Math.floor(currentTime / 60);
const seconds = Math.floor(currentTime % 60);
const milliseconds = Math.floor((currentTime % 1) * 10);
currentTimeDisplay.textContent = `${minutes}:${seconds.toString().padStart(2, '0')}.${milliseconds}`;
});
// Quick actions
document.getElementById('mute-audio-btn').addEventListener('click', function() {
muteAudio = !muteAudio;
if (muteAudio) {
this.classList.add('active');
this.textContent = '🔇 Audio Muted';
} else {
this.classList.remove('active');
this.textContent = '🔇 Mute Audio';
}
});
document.getElementById('rotate-btn').addEventListener('click', function() {
rotationDegrees = (rotationDegrees + 90) % 360;
if (rotationDegrees === 0) {
this.classList.remove('active');
this.textContent = '🔄 Rotate 90°';
} else {
this.classList.add('active');
this.textContent = `🔄 Rotated ${rotationDegrees}°`;
}
});
// Set start time button
setStartBtn.addEventListener('click', () => {
const currentTime = videoPreview.currentTime;
document.getElementById('start-time').value = currentTime.toFixed(1);
// Flash button feedback
setStartBtn.style.background = '#28a745';
setTimeout(() => {
setStartBtn.style.background = '#007bff';
}, 200);
});
// Set end time button
setEndBtn.addEventListener('click', () => {
const currentTime = videoPreview.currentTime;
document.getElementById('end-time').value = currentTime.toFixed(1);
// Flash button feedback
setEndBtn.style.background = '#28a745';
setTimeout(() => {
setEndBtn.style.background = '#007bff';
}, 200);
});
// Scale slider
const scaleSlider = document.getElementById('scale-slider');
const scaleValueDisplay = document.getElementById('scale-value-display');
const outputResolution = document.getElementById('output-resolution');
function updateScaleDisplay() {
scalePercentage = parseInt(scaleSlider.value);
scaleValueDisplay.textContent = scalePercentage + '%';
// Calculate output resolution
const cropWidth = parseInt(document.getElementById('crop-width').value) || videoInfo.width;
const cropHeight = parseInt(document.getElementById('crop-height').value) || videoInfo.height;
// Round to nearest even number for H.264 compatibility
const outputWidth = Math.round((cropWidth * scalePercentage) / 100 / 2) * 2;
const outputHeight = Math.round((cropHeight * scalePercentage) / 100 / 2) * 2;
outputResolution.textContent = `${outputWidth}x${outputHeight}`;
}
scaleSlider.addEventListener('input', updateScaleDisplay);
// Scale presets
document.querySelectorAll('.preset-btn').forEach(btn => {
btn.addEventListener('click', () => {
const scale = btn.dataset.scale;
if (scale) {
scaleSlider.value = scale;
updateScaleDisplay();
}
});
});
// Update scale display when crop values change
['crop-width', 'crop-height'].forEach(id => {
document.getElementById(id).addEventListener('input', updateScaleDisplay);
});
// Process video
document.getElementById('process-btn').addEventListener('click', async () => {
const startTime = parseFloat(document.getElementById('start-time').value) || 0;
const endTime = parseFloat(document.getElementById('end-time').value) || videoInfo.duration;
const cropX = parseInt(document.getElementById('crop-x').value) || 0;
const cropY = parseInt(document.getElementById('crop-y').value) || 0;
const cropWidth = parseInt(document.getElementById('crop-width').value) || 0;
const cropHeight = parseInt(document.getElementById('crop-height').value) || 0;
const payload = {
file_id: currentFileId,
start_time: startTime,
end_time: endTime,
scale_percentage: scalePercentage,
rotation: rotationDegrees,
mute_audio: muteAudio
};
if (cropWidth > 0 && cropHeight > 0) {
payload.crop = {
x: cropX,
y: cropY,
width: cropWidth,
height: cropHeight
};
}
processLoader.style.display = 'block';
document.getElementById('process-btn').disabled = true;
try {
const response = await fetch('/process', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(payload)
});
const data = await response.json();
if (!response.ok) {
throw new Error(data.error || 'Processing failed');
}
// Show download section
document.getElementById('edit-section').classList.add('hidden');
document.getElementById('download-section').classList.remove('hidden');
} catch (error) {
const alertContainer = document.getElementById('alert-container');
alertContainer.innerHTML = `<div class="alert alert-error">Error: ${error.message}</div>`;
} finally {
processLoader.style.display = 'none';
document.getElementById('process-btn').disabled = false;
}
});
// Download video
document.getElementById('download-btn').addEventListener('click', () => {
window.location.href = `/download/${currentFileId}`;
});
// Reset buttons
document.getElementById('reset-btn').addEventListener('click', resetApp);
document.getElementById('new-video-btn').addEventListener('click', resetApp);
async function resetApp() {
if (currentFileId) {
await fetch(`/cleanup/${currentFileId}`, { method: 'DELETE' });
}
currentFileId = null;
videoInfo = null;
cropMode = false;
cropRect = null;
rotationDegrees = 0;
muteAudio = false;
videoPreview.src = '';
videoInput.value = '';
clearCropCanvas();
cropCanvas.classList.remove('active');
cropModeBtn.classList.remove('active');
cropModeBtn.textContent = '🎯 Click to Draw Crop Area';
cropHint.style.display = 'none';
// Reset quick actions
document.getElementById('mute-audio-btn').classList.remove('active');
document.getElementById('mute-audio-btn').textContent = '🔇 Mute Audio';
document.getElementById('rotate-btn').classList.remove('active');
document.getElementById('rotate-btn').textContent = '🔄 Rotate 90°';
document.getElementById('upload-section').classList.remove('hidden');
document.getElementById('edit-section').classList.add('hidden');
document.getElementById('download-section').classList.add('hidden');
}
</script>
</body>
</html>