ffmpeg / templates /index.html
devusman's picture
ready for deployment
c4278f8
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vimeo Downloader</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
Helvetica, Arial, sans-serif;
background-color: #f0f2f5;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
margin: 0;
padding: 1rem;
box-sizing: border-box;
}
.container {
background: white;
padding: 2rem 2.5rem;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
width: 100%;
max-width: 500px;
}
h1 {
font-size: 1.75rem;
color: #1c1e21;
margin: 0 0 1.5rem 0;
text-align: center;
}
.form-group {
margin-bottom: 1rem;
}
label {
display: block;
margin-bottom: 0.5rem;
font-weight: 600;
color: #606770;
}
input,
select,
button {
width: 100%;
padding: 0.75rem;
font-size: 1rem;
border-radius: 6px;
border: 1px solid #dddfe2;
box-sizing: border-box;
}
button {
background-color: #007bff;
color: white;
border: none;
cursor: pointer;
font-weight: bold;
transition: background-color 0.2s;
}
button:disabled {
background-color: #a0c9f8;
cursor: not-allowed;
}
#progress-container {
display: none;
margin-top: 1.5rem;
}
#progress-bar-wrapper {
width: 100%;
background-color: #e9ecef;
border-radius: 0.25rem;
overflow: hidden;
height: 30px;
border: 1px solid #ddd;
}
#progress-bar {
height: 100%;
background-color: #007bff;
color: white;
text-align: center;
line-height: 30px;
font-weight: bold;
transition: width 0.3s ease;
width: 0%;
min-width: 60px;
}
#status-text {
text-align: center;
margin-top: 0.75rem;
font-weight: 500;
color: #495057;
min-height: 20px;
}
#download-link-container {
text-align: center;
margin-top: 1rem;
}
#download-link-container a {
display: inline-block;
padding: 0.75rem 1.5rem;
background-color: #28a745;
color: white;
text-decoration: none;
border-radius: 6px;
font-weight: bold;
}
.debug {
font-size: 12px;
color: #666;
margin-top: 5px;
text-align: center;
}
</style>
</head>
<body>
<div class="container">
<h1>Vimeo Downloader</h1>
<form id="download-form">
<div class="form-group">
<label for="url">URL</label>
<input
type="text"
id="url"
name="url"
placeholder="Paste Vimeo URL here"
required
/>
</div>
<div class="form-group">
<label for="format">Format</label>
<select id="format" name="format">
<optgroup label="Audio">
<option value="mp3" selected>MP3</option>
<option value="m4a">M4A</option>
<option value="flac">FLAC</option>
<option value="opus">OPUS</option>
<option value="wav">WAV</option>
</optgroup>
<optgroup label="Video">
<option value="1080">MP4 (1080p)</option>
<option value="720">MP4 (720p)</option>
<option value="480">MP4 (480p)</option>
<option value="360">MP4 (360p)</option>
<option value="1440">MP4 (1440p)</option>
</optgroup>
</select>
</div>
<button type="submit" id="download-button">Download</button>
</form>
<div id="progress-container">
<div id="progress-bar-wrapper">
<div id="progress-bar">0%</div>
</div>
<div id="status-text">Starting...</div>
<div id="debug-info" class="debug"></div>
<div id="download-link-container"></div>
</div>
</div>
<script>
const form = document.getElementById("download-form");
const button = document.getElementById("download-button");
const progressContainer = document.getElementById("progress-container");
const progressBar = document.getElementById("progress-bar");
const statusText = document.getElementById("status-text");
const debugInfo = document.getElementById("debug-info");
const downloadContainer = document.getElementById(
"download-link-container"
);
let eventSource;
form.addEventListener("submit", function (e) {
e.preventDefault();
// Reset UI
button.disabled = true;
button.textContent = "Downloading...";
progressContainer.style.display = "block";
downloadContainer.innerHTML = "";
// Reset progress bar
progressBar.style.width = "0%";
progressBar.textContent = "0%";
progressBar.style.backgroundColor = "#007bff";
statusText.textContent = "Connecting...";
debugInfo.textContent = "";
// Close existing connection
if (eventSource) {
eventSource.close();
}
// Build URL
const formData = new FormData(form);
const params = new URLSearchParams(formData);
const streamUrl = `/stream-download?${params}`;
console.log("Starting EventSource:", streamUrl);
eventSource = new EventSource(streamUrl);
eventSource.onopen = function (event) {
console.log("EventSource opened");
statusText.textContent = "Connected to server...";
};
eventSource.onmessage = function (event) {
try {
const data = JSON.parse(event.data);
console.log("Received:", data);
const progress = parseFloat(data.progress || 0);
const status = data.status || "unknown";
// Update debug info
debugInfo.textContent = `Status: ${status} | Progress: ${progress.toFixed(
1
)}%`;
// Update progress bar
progressBar.style.width = `${progress}%`;
progressBar.textContent = `${progress.toFixed(1)}%`;
// Update status and colors
switch (status) {
case "waiting":
statusText.textContent = "Waiting for server...";
progressBar.style.backgroundColor = "#6c757d";
break;
case "initializing":
statusText.textContent = "Initializing download...";
progressBar.style.backgroundColor = "#007bff";
break;
case "fetching_info":
statusText.textContent = "Fetching video information...";
progressBar.style.backgroundColor = "#007bff";
break;
case "preparing":
statusText.textContent = "Preparing download...";
progressBar.style.backgroundColor = "#007bff";
break;
case "starting_download":
statusText.textContent = "Starting download...";
progressBar.style.backgroundColor = "#007bff";
break;
case "downloading":
const eta = data.eta || 0;
const speed = data.speed || "0 MB/s";
statusText.textContent = `Downloading... ETA: ${eta}s at ${speed}`;
progressBar.style.backgroundColor = "#007bff";
break;
case "processing":
statusText.textContent = "Processing file...";
progressBar.style.backgroundColor = "#ffc107";
progressBar.textContent = "Processing...";
break;
case "complete":
statusText.textContent = "Download Complete!";
progressBar.style.backgroundColor = "#28a745";
progressBar.textContent = "Done!";
if (data.filename) {
downloadContainer.innerHTML = `<a href="/download-file/${data.filename}" download>Click to Save File</a>`;
}
eventSource.close();
resetButton();
break;
case "error":
const errorMsg = data.message || "Download failed";
statusText.textContent = `Error: ${errorMsg}`;
progressBar.style.backgroundColor = "#dc3545";
progressBar.textContent = "Error!";
eventSource.close();
resetButton();
break;
}
} catch (err) {
console.error("Error parsing message:", err, event.data);
statusText.textContent = "Error processing server response";
resetButton();
}
};
eventSource.onerror = function (event) {
console.error("EventSource error:", event);
statusText.textContent = "Connection error. Please try again.";
eventSource.close();
resetButton();
};
});
function resetButton() {
button.disabled = false;
button.textContent = "Download";
}
// Cleanup on page unload
window.addEventListener("beforeunload", function () {
if (eventSource) {
eventSource.close();
}
});
</script>
</body>
</html>