This commit is contained in:
2026-04-30 17:14:21 +03:00
parent 0aa057c991
commit 7c5ce807b8
4 changed files with 345 additions and 11 deletions

View File

@@ -192,6 +192,58 @@
</div>
</div>
<!-- Модалка редактирования метаданных -->
<div id="edit-meta-modal" class="fixed inset-0 z-[60] hidden items-center justify-center" style="background:rgba(0,0,0,0.75)">
<div class="card rounded-2xl w-full max-w-md mx-4 p-6 flex flex-col gap-4">
<h3 class="font-semibold text-white text-base">✏️ Редактировать название</h3>
<div class="flex flex-col gap-3">
<div>
<label class="text-xs text-gray-400 mb-1 block">Название (ru)</label>
<input id="edit-meta-title-ru" type="text"
class="w-full px-3 py-2 rounded-lg text-sm text-white border border-gray-700 focus:border-indigo-500 outline-none"
style="background:#0f1117" placeholder="Название на русском">
</div>
<div>
<label class="text-xs text-gray-400 mb-1 block">Полное название</label>
<input id="edit-meta-title-full" type="text"
class="w-full px-3 py-2 rounded-lg text-sm text-white border border-gray-700 focus:border-indigo-500 outline-none"
style="background:#0f1117" placeholder="Полное название">
</div>
<p class="text-xs text-gray-500">⚠️ Папка не переименуется. Метаданные файлов обновятся автоматически.</p>
</div>
<div class="flex gap-3 justify-end mt-2">
<button onclick="closeEditMeta()"
class="px-4 py-2 rounded-lg text-sm text-gray-400 hover:text-white"
style="background:#1e293b">Отмена</button>
<button onclick="saveEditMeta()" id="edit-meta-save-btn"
class="px-4 py-2 rounded-lg text-sm font-semibold text-white"
style="background:#4f46e5">Сохранить</button>
</div>
</div>
</div>
<!-- Модалка переименования папки -->
<div id="rename-folder-modal" class="fixed inset-0 z-[60] hidden items-center justify-center" style="background:rgba(0,0,0,0.75)">
<div class="card rounded-2xl w-full max-w-md mx-4 p-6 flex flex-col gap-4">
<h3 class="font-semibold text-white text-base">📁 Переименовать папку</h3>
<div>
<label class="text-xs text-gray-400 mb-1 block">Новое имя папки</label>
<input id="rename-folder-input" type="text"
class="w-full px-3 py-2 rounded-lg text-sm text-white border border-gray-700 focus:border-indigo-500 outline-none"
style="background:#0f1117" placeholder="Название папки">
<p class="text-xs text-gray-500 mt-2">Специальные символы будут удалены. Пробелы заменятся на «_».</p>
</div>
<div class="flex gap-3 justify-end mt-2">
<button onclick="closeRenameFolder()"
class="px-4 py-2 rounded-lg text-sm text-gray-400 hover:text-white"
style="background:#1e293b">Отмена</button>
<button onclick="saveRenameFolder()" id="rename-folder-save-btn"
class="px-4 py-2 rounded-lg text-sm font-semibold text-white"
style="background:#4f46e5">Переименовать</button>
</div>
</div>
</div>
<script>
// ── State ────────────────────────────────────
const state = {
@@ -377,6 +429,34 @@ function handleEvent(msg) {
// Ничего не делаем визуально — файлы обновлены на диске
break;
case 'manga_meta_updated':
if(state.mangas[msg.url]) {
state.mangas[msg.url].title = msg.title;
state.mangas[msg.url].title_ru = msg.title_ru;
state.mangas[msg.url].title_full = msg.title_full;
updateMangaRow(msg.url);
}
break;
case 'manga_folder_renamed':
if(state.mangas[msg.url]) {
state.mangas[msg.url].folder_name = msg.folder_name;
}
break;
case 'queue_positions': {
// Обновляем queue_position для всех манг
const pos = msg.positions || {};
Object.values(state.mangas).forEach(m => {
const newPos = pos[m.url] ?? null;
if(m.queue_position !== newPos) {
m.queue_position = newPos;
updateMangaRow(m.url);
}
});
break;
}
case 'manga_done':
if(state.mangas[msg.url]) {
state.mangas[msg.url].status = 'done';
@@ -948,7 +1028,7 @@ function _rowButtons(m) {
${m.status === 'stopped' || m.status === 'failed'
? `<button onclick="resumeManga('${u}')" title="Возобновить" style="background:#14532d;color:#86efac;padding:4px 12px;border-radius:6px;font-size:0.75rem;cursor:pointer">▶</button>`
: ''}
${!isActive
${m.status === 'queued'
? `<button onclick="prioritizeManga('${u}')" title="Загрузить первой" style="background:#312e81;color:#a5b4fc;padding:4px 8px;border-radius:6px;font-size:0.75rem;cursor:pointer">🚀</button>`
: ''}
<button onclick="deleteManga('${u}')" class="btn-danger" title="Удалить">✕</button>
@@ -1185,6 +1265,17 @@ function renderModalBody(data) {
</details>` : '<div class="text-xs text-gray-500 mb-3">Файлов на диске нет</div>'}
<div class="flex flex-wrap gap-2 mt-4 pt-4 border-t border-gray-800">
<button onclick="openEditMeta('${escHtml(data.url)}')"
class="flex items-center gap-2 px-4 py-2 rounded-lg text-sm font-semibold transition-colors"
style="background:#1e293b;color:#94a3b8;border:1px solid #334155">
✏️ Редактировать название
</button>
${data.status !== 'downloading' ? `
<button onclick="openRenameFolder('${escHtml(data.url)}', '${escHtml(data.folder_name || '')}')"
class="flex items-center gap-2 px-4 py-2 rounded-lg text-sm font-semibold transition-colors"
style="background:#1e293b;color:#94a3b8;border:1px solid #334155">
📁 Переименовать папку
</button>` : ''}
${data.status === 'done' ? `
<button id="modal-refresh-meta-btn" onclick="refreshMetaModal('${escHtml(data.url)}')"
class="flex items-center gap-2 px-4 py-2 rounded-lg text-sm font-semibold transition-colors"
@@ -1323,6 +1414,98 @@ function escHtml(s) {
return String(s||'').replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;');
}
// ── Edit Meta ────────────────────────────────
let _editMetaUrl = null;
function openEditMeta(url) {
_editMetaUrl = url;
const m = state.mangas[url] || {};
document.getElementById('edit-meta-title-ru').value = m.title_ru || m.title || '';
document.getElementById('edit-meta-title-full').value = m.title_full || '';
const modal = document.getElementById('edit-meta-modal');
modal.classList.remove('hidden');
modal.classList.add('flex');
}
function closeEditMeta() {
const modal = document.getElementById('edit-meta-modal');
modal.classList.add('hidden');
modal.classList.remove('flex');
_editMetaUrl = null;
}
async function saveEditMeta() {
if(!_editMetaUrl) return;
const btn = document.getElementById('edit-meta-save-btn');
btn.disabled = true; btn.textContent = '⏳ Сохраняем...';
const title_ru = document.getElementById('edit-meta-title-ru').value.trim();
const title_full = document.getElementById('edit-meta-title-full').value.trim();
try {
const r = await fetch('/api/mangas/update_meta', {
method: 'POST',
headers: {'Content-Type':'application/json'},
body: JSON.stringify({url: _editMetaUrl, title_ru, title_full}),
});
if(!r.ok) throw new Error(await r.text());
// Update local state
if(state.mangas[_editMetaUrl]) {
state.mangas[_editMetaUrl].title = title_ru;
state.mangas[_editMetaUrl].title_ru = title_ru;
state.mangas[_editMetaUrl].title_full = title_full;
}
renderList();
closeEditMeta();
} catch(e) {
alert('Ошибка: ' + e.message);
} finally {
btn.disabled = false; btn.textContent = 'Сохранить';
}
}
// ── Rename Folder ────────────────────────────
let _renameFolderUrl = null;
function openRenameFolder(url, currentFolder) {
_renameFolderUrl = url;
const m = state.mangas[url] || {};
const cur = currentFolder || m.folder_name || (m.title_ru || m.title || '').replace(/[^\w\s\-]/g,'').trim().replace(/ /g,'_');
document.getElementById('rename-folder-input').value = cur;
const modal = document.getElementById('rename-folder-modal');
modal.classList.remove('hidden');
modal.classList.add('flex');
}
function closeRenameFolder() {
const modal = document.getElementById('rename-folder-modal');
modal.classList.add('hidden');
modal.classList.remove('flex');
_renameFolderUrl = null;
}
async function saveRenameFolder() {
if(!_renameFolderUrl) return;
const btn = document.getElementById('rename-folder-save-btn');
btn.disabled = true; btn.textContent = '⏳ Переименовываем...';
const folder_name = document.getElementById('rename-folder-input').value.trim();
try {
const r = await fetch('/api/mangas/rename_folder', {
method: 'POST',
headers: {'Content-Type':'application/json'},
body: JSON.stringify({url: _renameFolderUrl, folder_name}),
});
if(!r.ok) throw new Error((await r.json()).detail || await r.text());
const data = await r.json();
if(state.mangas[_renameFolderUrl]) {
state.mangas[_renameFolderUrl].folder_name = data.folder_name;
}
closeRenameFolder();
} catch(e) {
alert('Ошибка: ' + e.message);
} finally {
btn.disabled = false; btn.textContent = 'Переименовать';
}
}
// ── Init ─────────────────────────────────────
async function init() {
_initDeleteModal();
@@ -1344,6 +1527,12 @@ document.addEventListener('DOMContentLoaded', init);
document.getElementById('modal').addEventListener('click', function(e) {
if(e.target === this) closeModal();
});
document.getElementById('edit-meta-modal').addEventListener('click', function(e) {
if(e.target === this) closeEditMeta();
});
document.getElementById('rename-folder-modal').addEventListener('click', function(e) {
if(e.target === this) closeRenameFolder();
});
</script>
</body>
</html>