(() => { let toastHost, toastRoot, tabId = null; const dismissed = new Set(); const toastTimers = new Map(); const knownJobs = new Map(); const stages = { preparing: "Подготовка", downloading: "Скачивание", merging: "Объединение дорожек", remuxing: "Подготовка файла", finalizing: "Финализация файла", encoding: "Перекодирование", audio: "Обработка аудио", saving: "Сохранение файла", installing: "Установка", updating: "Обновление компонентов", complete: "Файл сохранён", error: "Ошибка", cancelled: "Отменено", cancelling: "Останавливаю обработку" }; const isDark = () => document.documentElement.hasAttribute("dark") || document.documentElement.dataset.theme === "dark"; function updateText(node, value) { if (node.textContent === value) return; if (node.childNodes.length === 1 && node.firstChild.nodeType === 3) node.firstChild.data = value; else node.textContent = value; } function downloadSizeText(job) { const megabytes = value => `${(value / 1048576).toLocaleString("ru-RU", { minimumFractionDigits: 1, maximumFractionDigits: 1 })} МБ`; if (job.status === "complete" && Number.isFinite(job.result?.size) && job.result.size >= 0) return `Размер файла: ${megabytes(job.result.size)}`; const value = job.stage === "saving" ? job.copied : job.downloaded; if (!["downloading", "saving"].includes(job.stage) || !Number.isFinite(value) || value < 0) return ""; const downloaded = `${job.stage === "saving" ? "Сохранено" : "Скачано"} ${megabytes(value)}`; return Number.isFinite(job.total) && job.total > 0 ? `${downloaded} из ${megabytes(job.total)}` : downloaded; } function processingText(job) { if (!["merging", "remuxing", "finalizing", "encoding", "audio"].includes(job.stage)) return ""; const duration = value => { const seconds = Math.floor(value); return seconds >= 3600 ? `${Math.floor(seconds / 3600)}:${String(Math.floor(seconds / 60) % 60).padStart(2, "0")}:${String(seconds % 60).padStart(2, "0")}` : `${Math.floor(seconds / 60)}:${String(seconds % 60).padStart(2, "0")}`; }; const parts = []; if (Number.isFinite(job.processedSeconds) && job.processedSeconds >= 0) parts.push(`Обработано ${duration(job.processedSeconds)}${job.durationSeconds > 0 ? ` из ${duration(job.durationSeconds)}` : ""}`); if (Number.isFinite(job.written) && job.written >= 0) parts.push(`Записано ${(job.written / 1048576).toLocaleString("ru-RU", { minimumFractionDigits: 1, maximumFractionDigits: 1 })} МБ`); return parts.join(" · "); } const commonStyle = ` :host{font-family:Roboto,"Segoe UI",sans-serif;color-scheme:light;--bg:#fff;--fg:#152334;--muted:#6a7787;--line:#e1e7ee;--track:#edf2f7;--accent:#0788bc}:host([dark]){color-scheme:dark;--bg:#1b2029;--fg:#eef2fa;--muted:#a1adbd;--line:#333e4e;--track:#2b3543;--accent:#59d0f4}*{box-sizing:border-box}button{font:inherit;cursor:pointer}@media(prefers-reduced-motion:reduce){*,*:before,*:after{animation:none!important;transition:none!important}}`; function applyTheme() { toastHost?.toggleAttribute("dark", isDark()); } function ensureToasts() { if (toastHost?.isConnected) return; toastHost = document.createElement("div"); toastHost.id = "ytdl-notifications"; toastHost.style.cssText = "position:fixed;right:20px;bottom:20px;width:min(365px,calc(100vw - 32px));z-index:2147483647;pointer-events:none"; toastRoot = toastHost.attachShadow({ mode: "open" }); toastRoot.innerHTML = ``; document.documentElement.append(toastHost); applyTheme(); } function dismiss(id) { dismissed.add(id); clearTimeout(toastTimers.get(id)); const toast = toastRoot?.querySelector(`[data-id="${CSS.escape(id)}"]`); toast?.classList.add("exit"); setTimeout(() => toast?.remove(), 180); } function showJob(job) { knownJobs.set(job.id, job); if (dismissed.has(job.id)) return; ensureToasts(); let toast = toastRoot.querySelector(`[data-id="${CSS.escape(job.id)}"]`); if (!toast) { toast = document.createElement("article"); toast.className = "toast"; toast.dataset.id = job.id; toast.setAttribute("role", "status"); toast.setAttribute("aria-live", "polite"); toast.innerHTML = '