From 672e199d3a0a9c43107bbcbad1c951dd69bda121 Mon Sep 17 00:00:00 2001 From: StenFredd Date: Sun, 3 May 2026 14:37:57 +0300 Subject: [PATCH] validation --- frontend/index.html | 92 ++++++++++++++++++ src/api.py | 40 +++++++- src/sources/mangalib.py | 53 +++++++++++ src/sources/readmanga.py | 13 +++ src/worker.py | 201 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 398 insertions(+), 1 deletion(-) diff --git a/frontend/index.html b/frontend/index.html index adea8db..7e7f5c3 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -393,6 +393,7 @@ const state = { currentUser: null, // {id, username, role} authWarnings: {}, // source_slug → {source_slug, source_name} metaUpdating: new Set(), // urls where meta refresh is in progress + validating: {}, // url → {checked, total} for in-progress validations }; // ── Auth ───────────────────────────────────── @@ -703,6 +704,31 @@ function handleEvent(msg) { _updateMetaBtn(msg.url, msg.failed === -1 ? 'error' : 'done'); break; + case 'validate_started': + state.validating[msg.url] = {checked: 0, total: 0}; + _updateValidateBtn(msg.url); + break; + + case 'validate_progress': + if(state.validating[msg.url]) { + state.validating[msg.url].checked = msg.checked; + state.validating[msg.url].total = msg.total; + } + _updateValidateBtn(msg.url); + break; + + case 'validate_done': { + delete state.validating[msg.url]; + const result = msg.total_to_redownload > 0 || msg.new_chapters > 0 ? 'issues' : 'ok'; + _updateValidateBtn(msg.url, result, msg); + break; + } + + case 'validate_error': + delete state.validating[msg.url]; + _updateValidateBtn(msg.url, 'error'); + break; + case 'manga_meta_updated': if(state.mangas[msg.url]) { state.mangas[msg.url].title = msg.title; @@ -1691,6 +1717,63 @@ async function refreshMetaModal(url) { // Спиннер появится через WS meta_refresh_started, исчезнет через meta_refreshed } +function _updateValidateBtn(url, result, data) { + const modal = document.getElementById('modal'); + if(!modal || modal.classList.contains('hidden') || modal.dataset.currentUrl !== url) return; + const btn = document.getElementById('modal-validate-btn'); + if(!btn) return; + const v = state.validating[url]; + if(v !== undefined) { + const prog = v.total > 0 ? ` ${v.checked}/${v.total}` : '...'; + btn.innerHTML = ` Проверка${prog}`; + btn.disabled = true; + btn.style.color = '#94a3b8'; + btn.style.borderColor = '#334155'; + } else if(result === 'ok') { + btn.innerHTML = '✅ Всё в порядке'; + btn.disabled = false; + btn.style.color = '#4ade80'; + btn.style.borderColor = '#166534'; + setTimeout(() => { btn.innerHTML = '🔍 Проверить целостность'; btn.style.color='#67e8f9'; btn.style.borderColor='#164e63'; btn.disabled=false; }, 3000); + } else if(result === 'issues') { + const n = data ? (data.total_to_redownload + data.new_chapters) : '?'; + btn.innerHTML = `⚡ Найдено проблем: ${n} — поставлено в очередь`; + btn.disabled = true; + btn.style.color = '#fbbf24'; + btn.style.borderColor = '#78350f'; + setTimeout(() => { btn.innerHTML = '🔍 Проверить целостность'; btn.style.color='#67e8f9'; btn.style.borderColor='#164e63'; btn.disabled=false; }, 5000); + } else if(result === 'error') { + btn.innerHTML = '❌ Ошибка валидации'; + btn.disabled = false; + btn.style.color = '#f87171'; + btn.style.borderColor = '#7f1d1d'; + setTimeout(() => { btn.innerHTML = '🔍 Проверить целостность'; btn.style.color='#67e8f9'; btn.style.borderColor='#164e63'; btn.disabled=false; }, 3000); + } else { + btn.innerHTML = '🔍 Проверить целостность'; + btn.disabled = false; + btn.style.color = '#67e8f9'; + btn.style.borderColor = '#164e63'; + } +} + +async function validateManga(url) { + const btn = document.getElementById('modal-validate-btn'); + if(btn) { + btn.innerHTML = ' Запуск...'; + btn.disabled = true; + } + const r = await fetch('/api/mangas/validate?url='+encodeURIComponent(url), {method:'POST'}); + if(!r.ok) { + const err = await r.json().catch(() => ({})); + if(btn) { + btn.innerHTML = '❌ ' + (err.detail || 'Ошибка'); + btn.style.color = '#f87171'; + btn.style.borderColor = '#7f1d1d'; + setTimeout(() => { btn.innerHTML = '🔍 Проверить целостность'; btn.style.color='#67e8f9'; btn.style.borderColor='#164e63'; btn.disabled=false; }, 3000); + } + } +} + async function forceRedownload(url, closeModalAfter = false) { if(!confirm('Скачать заново ВСЕ главы? Уже скачанные файлы будут перезаписаны.')) return; const r = await fetch('/api/mangas/force_redownload?url='+encodeURIComponent(url), {method:'POST'}); @@ -2233,6 +2316,15 @@ function renderModalBody(data) { 📁 Переименовать папку ` : ''} ${data.status === 'done' && canManage(data) ? ` + ` : ''} + ${data.status === 'done' && canManage(data) ? `