diff --git a/frontend/index.html b/frontend/index.html
index adea8db..1426e35 100644
--- a/frontend/index.html
+++ b/frontend/index.html
@@ -222,6 +222,19 @@
+
+
+
Обновить все метаданные
+
Запускает браузер для каждой скачанной манги: обновляет обложку, синопсис, жанры и метаданные в файлах CBZ/PDF/EPUB.
+
+
+
+
+
Сменить пароль
@@ -703,6 +716,18 @@ function handleEvent(msg) {
_updateMetaBtn(msg.url, msg.failed === -1 ? 'error' : 'done');
break;
+ case 'refresh_all_started':
+ _handleRefreshAllStarted(msg);
+ break;
+
+ case 'refresh_all_progress':
+ _handleRefreshAllProgress(msg);
+ break;
+
+ case 'refresh_all_done':
+ _handleRefreshAllDone(msg);
+ break;
+
case 'manga_meta_updated':
if(state.mangas[msg.url]) {
state.mangas[msg.url].title = msg.title;
@@ -812,7 +837,7 @@ function switchTab(tab) {
document.getElementById('manga-filters').classList.toggle('hidden', tab !== 'mangas');
if(tab === 'history') loadHistory();
if(tab === 'news') { newsUnreadCount = 0; updateNewsBadge(); loadNews(); }
- if(tab === 'settings') { loadSources(); showUsersSection(); }
+ if(tab === 'settings') { loadSources(); showUsersSection(); showRefreshAllSection(); }
}
function updateNewsBadge() {
@@ -1132,6 +1157,11 @@ function showUsersSection() {
}
}
+function showRefreshAllSection() {
+ const el = document.getElementById('refresh-all-section');
+ if(el) el.classList.toggle('hidden', !isAdmin());
+}
+
async function loadUsers() {
if(!isAdmin()) return;
try {
@@ -1654,7 +1684,7 @@ function _updateMetaBtn(url, result) {
btn.style.color = '#4ade80';
btn.style.borderColor = '#166534';
setTimeout(() => {
- btn.innerHTML = '🏷 Обновить метатеги';
+ btn.innerHTML = '🏷 Обновить метаданные';
btn.style.color = '#a78bfa';
btn.style.borderColor = '#312e81';
}, 2500);
@@ -1664,12 +1694,12 @@ function _updateMetaBtn(url, result) {
btn.style.color = '#f87171';
btn.style.borderColor = '#7f1d1d';
setTimeout(() => {
- btn.innerHTML = '🏷 Обновить метатеги';
+ btn.innerHTML = '🏷 Обновить метаданные';
btn.style.color = '#a78bfa';
btn.style.borderColor = '#312e81';
}, 3000);
} else {
- btn.innerHTML = '🏷 Обновить метатеги';
+ btn.innerHTML = '🏷 Обновить метаданные';
btn.disabled = false;
btn.style.color = '#a78bfa';
btn.style.borderColor = '#312e81';
@@ -1691,6 +1721,46 @@ async function refreshMetaModal(url) {
// Спиннер появится через WS meta_refresh_started, исчезнет через meta_refreshed
}
+async function refreshAllMeta() {
+ const btn = document.getElementById('refresh-all-btn');
+ const status = document.getElementById('refresh-all-status');
+ const r = await fetch('/api/mangas/refresh_all_meta', {method:'POST'});
+ if(!r.ok) {
+ const err = await r.json().catch(() => ({}));
+ if(status) { status.textContent = err.detail || 'Ошибка запуска'; status.classList.remove('hidden'); status.style.color = '#f87171'; }
+ return;
+ }
+ if(btn) { btn.disabled = true; btn.textContent = '⏳ Запускаем...'; }
+ if(status) { status.textContent = 'Инициализация...'; status.classList.remove('hidden'); status.style.color = '#94a3b8'; }
+}
+
+function _handleRefreshAllStarted(data) {
+ const btn = document.getElementById('refresh-all-btn');
+ const status = document.getElementById('refresh-all-status');
+ if(btn) { btn.disabled = true; btn.textContent = '⏳ Обновляем...'; }
+ if(status) { status.textContent = `0 / ${data.total}`; status.classList.remove('hidden'); status.style.color = '#94a3b8'; }
+}
+
+function _handleRefreshAllProgress(data) {
+ const status = document.getElementById('refresh-all-status');
+ if(status) {
+ const title = data.title ? ` — ${data.title}` : '';
+ status.textContent = `${data.done + 1} / ${data.total}${title}`;
+ status.style.color = '#94a3b8';
+ }
+}
+
+function _handleRefreshAllDone(data) {
+ const btn = document.getElementById('refresh-all-btn');
+ const status = document.getElementById('refresh-all-status');
+ if(btn) { btn.disabled = false; btn.textContent = '🔄 Обновить все метаданные'; }
+ if(status) {
+ status.textContent = `Готово: ${data.total} манг, обновлено файлов: ${data.total_updated}`;
+ status.style.color = '#4ade80';
+ status.classList.remove('hidden');
+ }
+}
+
async function forceRedownload(url, closeModalAfter = false) {
if(!confirm('Скачать заново ВСЕ главы? Уже скачанные файлы будут перезаписаны.')) return;
const r = await fetch('/api/mangas/force_redownload?url='+encodeURIComponent(url), {method:'POST'});
@@ -2236,7 +2306,7 @@ function renderModalBody(data) {
` : ''}
${data.status !== 'downloading' && data.status !== 'queued' && isAdmin() ? `