From a645493c320ba75bced772bb64eaac811c82c84c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CNaeel=E2=80=9D?= Date: Mon, 24 Aug 2026 15:23:38 +0300 Subject: [PATCH] =?UTF-8?q?v0.0.64:=20=D1=82=D0=B8=D0=BA=D0=B5=D1=80=20?= =?UTF-8?q?=D1=82=D0=B5=D0=BA=D1=83=D1=89=D0=B5=D0=B3=D0=BE=20=D1=84=D0=B0?= =?UTF-8?q?=D0=B9=D0=BB=D0=B0=20=D0=B8=20=D0=BE=D1=86=D0=B5=D0=BD=D0=BA?= =?UTF-8?q?=D0=B8=20=D0=B2=D1=80=D0=B5=D0=BC=D0=B5=D0=BD=D0=B8=20=D0=BD?= =?UTF-8?q?=D0=B0=20=D0=B2=D1=81=D0=B5=D1=85=20=D1=8D=D1=82=D0=B0=D0=BF?= =?UTF-8?q?=D0=B0=D1=85=20=D0=BE=D0=B1=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=BA?= =?UTF-8?q?=D0=B8=20(=D0=B2=20=D1=82.=D1=87.=20=D0=B8=D0=B7=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D1=87=D0=B5=D0=BD=D0=B8=D0=B5),=20=D1=81=D0=B0=D0=BC?= =?UTF-8?q?=D0=BE=D0=BA=D0=B0=D0=BB=D0=B8=D0=B1=D1=80=D0=BE=D0=B2=D0=BA?= =?UTF-8?q?=D0=B0=20=D1=81=D0=BA=D0=BE=D1=80=D0=BE=D1=81=D1=82=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- site/app.py | 2 +- site/templates/index.html | 28 ++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/site/app.py b/site/app.py index 2a6558a..1256a79 100644 --- a/site/app.py +++ b/site/app.py @@ -21,7 +21,7 @@ if _sys_path_root not in sys.path: sys.path.insert(0, _sys_path_root) # Версия приложения (меняется при изменениях) -VERSION = "0.0.63" +VERSION = "0.0.64" def setup_logging(): diff --git a/site/templates/index.html b/site/templates/index.html index 88ddedf..60570df 100644 --- a/site/templates/index.html +++ b/site/templates/index.html @@ -210,6 +210,7 @@ let procPhase = 'idle'; // 'idle' | 'processing' let procState = {}; // sfIdx -> {st, elapsed, eta, est, chars, t0} let procNameIdx = {}; // имя (с бэка) -> sfIdx let procExtractDone = false; // true после extract_done (далее done = реальная готовность) +let procExtractRate = null; // измеренная скорость извлечения (сек/МБ) для оценок ожидающих let procRefresh = null; // setInterval перерисовки таблицы let ptimer = null, liveRefresh = null; // таймеры статуса и live-блока const MAX_FILE_BYTES = 50 * 1024 * 1024; // 50 МБ на один файл @@ -235,6 +236,7 @@ function resetAll() { procState = {}; procNameIdx = {}; procExtractDone = false; + procExtractRate = null; document.getElementById('cancelBtn').style.display = 'none'; fi.value = ''; rr(); @@ -323,10 +325,17 @@ function renderProcTable() { rows.push('○ Ожидают обработки (' + groups.pending.length + ')'); for (const i of groups.pending) { const p = procState[i] || {}; + // Оценка: LLM (chars x rate) если известна; иначе грубая по размеру/скорости извлечения if (rate && !p.est && p.chars > 0) p.est = p.chars * rate; + if (!p.est) { + const szMB = (sf[i] ? sf[i].size : 0) / 1048576; + const k = procExtractRate || 3; // сек/МБ: замеренная или запасная + p.est = Math.max(1, Math.round(szMB * k)); + } let txt; if (overNames.has(sf[i].name)) txt = '🔥 не учитывается'; else if (p.st === 'analyzed') txt = 'анализ ✓'; + else if (p.st === 'current') txt = ''; else if (p.est != null) txt = '~' + fmtSec(p.est) + ''; else txt = ''; rows.push(procRow(i, txt)); @@ -644,6 +653,7 @@ async function uploadFiles() { procState = {}; procNameIdx = {}; procExtractDone = false; + procExtractRate = null; for (let k = 0; k < total; k++) { procState[sendIdx[k]] = { st: 'pending', elapsed: 0, eta: null, est: null, chars: 0, t0: 0 }; } @@ -688,6 +698,20 @@ async function uploadFiles() { procNameIdx[d.name] = idx; const p = procState[idx]; if (p) { p.st = 'pending'; p.elapsed = 0; } + // Этап извлечения: текущий файл = обрабатываемый сейчас (тикер в таблице). + // Предыдущий «текущий» демотируем и замеряем его скорость (сек/МБ) для оценок. + if (!procExtractDone) { + let prevIdx = null; + for (const i in procState) { + if (procState[i].st === 'current') { prevIdx = Number(i); procState[i].st = 'pending'; } + } + if (prevIdx != null && sf[prevIdx] && procState[prevIdx].t0) { + const el = (performance.now() - procState[prevIdx].t0) / 1000; + const szMB = sf[prevIdx].size / 1048576; + if (el > 0 && szMB > 0) procExtractRate = el / szMB; + } + if (p) { p.st = 'current'; p.t0 = performance.now(); p.eta = null; } + } const f = sf[idx]; const sz = f ? fs(f.size) : ''; currentLiveFile = 'Файл ' + (d.idx + 1) + '/' + total + ': ' + d.name + (sz ? ' (' + sz + ')' : ''); @@ -696,6 +720,10 @@ async function uploadFiles() { activeES.addEventListener('extract_done', function(e) { const d = JSON.parse(e.data); procExtractDone = true; + // Этап извлечения завершён — снимаем подсветку «текущего» извлечения + for (const i in procState) { + if (procState[i].st === 'current') procState[i].st = 'pending'; + } // per-file символы -> оценка времени для ожидающих файлов if (d.per_file) { for (const nm in d.per_file) {