| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>Downloadwella to Hugging Face Frame Extractor</title> |
| <style> |
| body { font-family: sans-serif; margin: 20px; background-color: #f4f4f4; color: #333; } |
| .container { max-width: 800px; margin: auto; background: #fff; padding: 20px; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); } |
| h1 { color: #0056b3; } |
| form { margin-top: 20px; display: flex; flex-direction: column; gap: 10px; } |
| input[type="text"] { padding: 10px; border: 1px solid #ddd; border-radius: 4px; font-size: 16px; } |
| button { padding: 10px 15px; background-color: #007bff; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 16px; } |
| button:hover { background-color: #0056b3; } |
| .status-box { background-color: #e9ecef; border: 1px solid #ced4da; padding: 15px; border-radius: 4px; margin-top: 20px; } |
| .log-entry { margin-bottom: 5px; border-bottom: 1px dotted #ccc; padding-bottom: 3px; } |
| .error { color: red; font-weight: bold; } |
| .success { color: green; font-weight: bold; } |
| </style> |
| </head> |
| <body> |
| <div class="container"> |
| <h1>Downloadwella to Hugging Face Frame Extractor</h1> |
| <p>Enter a Downloadwella URL to download the video, extract frames, and upload them as a zip to Hugging Face.</p> |
|
|
| <form id="processForm"> |
| <input type="text" id="videoUrl" name="video_url" placeholder="Enter Downloadwella URL (e.g., https://downloadwella.com/some-video-id)" required> |
| <button type="submit">Start Processing</button> |
| </form> |
|
|
| <div class="status-box"> |
| <h2>Processing Status</h2> |
| <p><strong>Is Running:</strong> <span id="isRunning">{{ status.is_running }}</span></p> |
| <p><strong>Current Action:</strong> <span id="currentAction">{{ status.current_action }}</span></p> |
| <p><strong>Last Processed URL:</strong> <span id="lastProcessedUrl">{{ status.last_processed_url if status.last_processed_url else 'N/A' }}</span></p> |
| <p><strong>Processed Count:</strong> <span id="processedCount">{{ status.processed_count }}</span></p> |
| <h3>Logs:</h3> |
| <div id="logs" style="max-height: 300px; overflow-y: scroll; background-color: #fff; padding: 10px; border-radius: 4px;"> |
| {% for log in status.logs %} |
| <div class="log-entry">{{ log }}</div> |
| {% endfor %} |
| </div> |
| </div> |
| </div> |
|
|
| <script> |
| const processForm = document.getElementById('processForm'); |
| const videoUrlInput = document.getElementById('videoUrl'); |
| const isRunningSpan = document.getElementById('isRunning'); |
| const currentActionSpan = document.getElementById('currentAction'); |
| const lastProcessedUrlSpan = document.getElementById('lastProcessedUrl'); |
| const processedCountSpan = document.getElementById('processedCount'); |
| const logsDiv = document.getElementById('logs'); |
| |
| processForm.addEventListener('submit', async (event) => { |
| event.preventDefault(); |
| const videoUrl = videoUrlInput.value; |
| if (!videoUrl) return; |
| |
| logsDiv.innerHTML = ''; |
| addLog('Initiating processing...'); |
| |
| try { |
| const response = await fetch('/start', { |
| method: 'POST', |
| headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, |
| body: `video_url=${encodeURIComponent(videoUrl)}` |
| }); |
| const data = await response.json(); |
| addLog(data.message); |
| if (data.message === "Processor started") { |
| |
| pollStatus(); |
| } |
| } catch (error) { |
| addLog(`Error starting process: ${error}`, true); |
| } |
| }); |
| |
| async function fetchStatus() { |
| try { |
| const response = await fetch('/stats'); |
| const status = await response.json(); |
| isRunningSpan.textContent = status.is_running; |
| currentActionSpan.textContent = status.current_action; |
| lastProcessedUrlSpan.textContent = status.last_processed_url || 'N/A'; |
| processedCountSpan.textContent = status.processed_count; |
| |
| |
| if (status.logs.length !== logsDiv.children.length) { |
| logsDiv.innerHTML = ''; |
| status.logs.forEach(log => addLog(log)); |
| } |
| |
| return status.is_running; |
| } catch (error) { |
| addLog(`Error fetching status: ${error}`, true); |
| return false; |
| } |
| } |
| |
| function addLog(message, isError = false) { |
| const logEntry = document.createElement('div'); |
| logEntry.classList.add('log-entry'); |
| if (isError) { |
| logEntry.classList.add('error'); |
| } |
| logEntry.textContent = message; |
| logsDiv.appendChild(logEntry); |
| logsDiv.scrollTop = logsDiv.scrollHeight; |
| } |
| |
| let pollingInterval; |
| function pollStatus() { |
| if (pollingInterval) clearInterval(pollingInterval); |
| pollingInterval = setInterval(async () => { |
| const running = await fetchStatus(); |
| if (!running && currentActionSpan.textContent !== "Idle" && currentActionSpan.textContent !== "Error") { |
| addLog("Processing finished or stopped."); |
| clearInterval(pollingInterval); |
| } |
| }, 3000); |
| } |
| |
| |
| fetchStatus(); |
| pollStatus(); |
| |
| </script> |
| </body> |
| </html> |
|
|