/* URL-tool flow: paste link → instant info → job → poll → download. */ (function () { 'use strict'; var panel = document.getElementById('tool-panel'); if (!panel) return; var slug = panel.dataset.tool; var form = document.getElementById('tool-form'); var urlInput = document.getElementById('url-input'); var infoPanel = document.getElementById('info-panel'); var statusFlow = document.getElementById('status-flow'); var progressFill = document.getElementById('progress-fill'); var resultPanel = document.getElementById('result-panel'); var errorBox = document.getElementById('error-box'); var pollTimer = null, currentJobId = null; function track(name) { try { navigator.sendBeacon && navigator.sendBeacon('/api/events', new Blob([JSON.stringify({ name: name, tool: slug })], { type: 'application/json' })); } catch (e) {} } function showError(msg) { errorBox.textContent = msg; errorBox.hidden = false; track('tool_failure'); } function setStage(stage, progress) { statusFlow.hidden = false; document.querySelectorAll('.stages li').forEach(function (li) { li.classList.toggle('done', li.dataset.stage !== stage); li.classList.toggle('active', li.dataset.stage === stage); }); if (typeof progress === 'number') progressFill.style.width = Math.max(6, progress) + '%'; } form.addEventListener('submit', function (e) { e.preventDefault(); hideError(); resultPanel.classList.remove('show'); fetch('/api/jobs', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ tool: slug, url: urlInput.value }) }) .then(function (r) { return r.json().then(function (j) { return { status: r.status, body: j }; }); }) .then(function (res) { if (!res.ok) throw new Error(res.body.message || res.body.error || 'Request failed.'); currentJobId = res.body.job_id; setStage('preparing', 8); poll(); }) .catch(function (err) { showError(err.message || 'Something went wrong. Please try again.'); }); }); function hideError() { errorBox.hidden = true; } function poll() { if (!currentJobId) return; fetch('/api/jobs/' + currentJobId) .then(function (r) { return r.json(); }) .then(function (data) { if (!data.ok) throw new Error('lost'); if (data.status === 'completed') { finish(data); return; } if (data.status === 'failed') { showError(data.message || 'This video could not be processed.'); return; } var stageMap = { preparing: 'preparing', fetching: 'fetching', processing: 'processing', converting: 'converting' }; setStage(stageMap[data.stage] || 'processing', data.progress); pollTimer = setTimeout(poll, 1600); }) .catch(function () { pollTimer = setTimeout(poll, 3200); }); } function finish(data) { clearTimeout(pollTimer); setStage('complete', 100); resultPanel.innerHTML = '
'; resultPanel.classList.add('show'); } function humanSize(bytes) { var u = ['B', 'KB', 'MB', 'GB'], i = 0; while (bytes >= 1024 && i < u.length - 1) { bytes /= 1024; i++; } return (Math.round(bytes * 10) / 10) + ' ' + u[i]; } function escapeHtml(s) { var d = document.createElement('div'); d.textContent = s; return d.innerHTML; } })();