Fix für Bulk-löschen
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
// ==UserScript==
|
// ==UserScript==
|
||||||
// @name Anime-Loads: Verlauf-Massenlöschung
|
// @name Anime-Loads: Verlauf-Massenlöschung
|
||||||
// @namespace https://git.ponywave.de/Akamaru/Userscripts
|
// @namespace https://git.ponywave.de/Akamaru/Userscripts
|
||||||
// @version 1.1
|
// @version 1.2
|
||||||
// @description Fügt Buttons hinzu, um Verlaufseinträge auf anime-loads.org schneller zu löschen
|
// @description Fügt Buttons hinzu, um Verlaufseinträge auf anime-loads.org schneller zu löschen
|
||||||
// @author Akamaru
|
// @author Akamaru
|
||||||
// @match https://www.anime-loads.org/history*
|
// @match https://www.anime-loads.org/history*
|
||||||
@@ -21,6 +21,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function init() {
|
function init() {
|
||||||
|
// Prüfe ob Bulk-Delete läuft
|
||||||
|
checkAndContinueBulkDelete();
|
||||||
|
|
||||||
const historyDiv = document.getElementById('history');
|
const historyDiv = document.getElementById('history');
|
||||||
if (!historyDiv) return;
|
if (!historyDiv) return;
|
||||||
|
|
||||||
@@ -55,13 +58,6 @@
|
|||||||
updateCounter();
|
updateCounter();
|
||||||
};
|
};
|
||||||
|
|
||||||
// Button: Alle Seiten laden
|
|
||||||
const loadAllBtn = document.createElement('button');
|
|
||||||
loadAllBtn.textContent = 'Alle Seiten laden';
|
|
||||||
loadAllBtn.className = 'btn btn-info';
|
|
||||||
loadAllBtn.id = 'load-all-pages-btn';
|
|
||||||
loadAllBtn.onclick = loadAllPages;
|
|
||||||
|
|
||||||
// Zähler für ausgewählte Einträge
|
// Zähler für ausgewählte Einträge
|
||||||
const counterSpan = document.createElement('span');
|
const counterSpan = document.createElement('span');
|
||||||
counterSpan.id = 'selection-counter';
|
counterSpan.id = 'selection-counter';
|
||||||
@@ -71,7 +67,6 @@
|
|||||||
buttonContainer.appendChild(deleteAllBtn);
|
buttonContainer.appendChild(deleteAllBtn);
|
||||||
buttonContainer.appendChild(deleteSelectedBtn);
|
buttonContainer.appendChild(deleteSelectedBtn);
|
||||||
buttonContainer.appendChild(toggleAllBtn);
|
buttonContainer.appendChild(toggleAllBtn);
|
||||||
buttonContainer.appendChild(loadAllBtn);
|
|
||||||
buttonContainer.appendChild(counterSpan);
|
buttonContainer.appendChild(counterSpan);
|
||||||
|
|
||||||
// Füge Button-Container vor dem History-Div ein
|
// Füge Button-Container vor dem History-Div ein
|
||||||
@@ -158,12 +153,194 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function deleteAll() {
|
function deleteAll() {
|
||||||
|
showDeleteOptionsModal();
|
||||||
|
}
|
||||||
|
|
||||||
|
function showDeleteOptionsModal() {
|
||||||
|
// Erstelle Modal-Overlay
|
||||||
|
const overlay = document.createElement('div');
|
||||||
|
overlay.style.cssText = 'position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.5); z-index: 9999; display: flex; align-items: center; justify-content: center;';
|
||||||
|
|
||||||
|
// Erstelle Modal-Content
|
||||||
|
const modal = document.createElement('div');
|
||||||
|
modal.className = 'modal-content';
|
||||||
|
modal.style.cssText = 'background: white; padding: 25px; border-radius: 8px; max-width: 500px; box-shadow: 0 4px 6px rgba(0,0,0,0.1);';
|
||||||
|
|
||||||
|
const title = document.createElement('h3');
|
||||||
|
title.textContent = 'Verlauf löschen';
|
||||||
|
title.style.cssText = 'margin: 0 0 20px 0;';
|
||||||
|
|
||||||
|
const optionsDiv = document.createElement('div');
|
||||||
|
optionsDiv.style.cssText = 'margin-bottom: 20px;';
|
||||||
|
|
||||||
|
// Option 1: Aktuelle Seite
|
||||||
|
const currentPageBtn = document.createElement('button');
|
||||||
|
currentPageBtn.textContent = 'Aktuelle Seite löschen';
|
||||||
|
currentPageBtn.className = 'btn btn-warning';
|
||||||
|
currentPageBtn.style.cssText = 'width: 100%; margin-bottom: 10px;';
|
||||||
|
currentPageBtn.onclick = () => {
|
||||||
|
overlay.remove();
|
||||||
|
deleteCurrentPage();
|
||||||
|
};
|
||||||
|
|
||||||
|
// Option 2: Gesamter Verlauf
|
||||||
|
const allPagesBtn = document.createElement('button');
|
||||||
|
allPagesBtn.textContent = 'Gesamten Verlauf löschen';
|
||||||
|
allPagesBtn.className = 'btn btn-danger';
|
||||||
|
allPagesBtn.style.cssText = 'width: 100%; margin-bottom: 10px;';
|
||||||
|
allPagesBtn.onclick = () => {
|
||||||
|
overlay.remove();
|
||||||
|
deleteAllPages();
|
||||||
|
};
|
||||||
|
|
||||||
|
// Option 3: Seite x bis x
|
||||||
|
const rangeDiv = document.createElement('div');
|
||||||
|
rangeDiv.style.cssText = 'display: flex; gap: 10px; align-items: center; margin-bottom: 10px;';
|
||||||
|
|
||||||
|
const fromInput = document.createElement('input');
|
||||||
|
fromInput.type = 'number';
|
||||||
|
fromInput.placeholder = 'Von Seite';
|
||||||
|
fromInput.className = 'form-control';
|
||||||
|
fromInput.style.cssText = 'flex: 1;';
|
||||||
|
fromInput.min = '1';
|
||||||
|
|
||||||
|
const toInput = document.createElement('input');
|
||||||
|
toInput.type = 'number';
|
||||||
|
toInput.placeholder = 'Bis Seite';
|
||||||
|
toInput.className = 'form-control';
|
||||||
|
toInput.style.cssText = 'flex: 1;';
|
||||||
|
toInput.min = '1';
|
||||||
|
|
||||||
|
const rangeBtn = document.createElement('button');
|
||||||
|
rangeBtn.textContent = 'Löschen';
|
||||||
|
rangeBtn.className = 'btn btn-primary';
|
||||||
|
rangeBtn.onclick = () => {
|
||||||
|
const from = parseInt(fromInput.value);
|
||||||
|
const to = parseInt(toInput.value);
|
||||||
|
if (from && to && from <= to) {
|
||||||
|
overlay.remove();
|
||||||
|
deletePageRange(from, to);
|
||||||
|
} else {
|
||||||
|
alert('Bitte gültige Seitenzahlen eingeben.');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
rangeDiv.appendChild(fromInput);
|
||||||
|
rangeDiv.appendChild(toInput);
|
||||||
|
rangeDiv.appendChild(rangeBtn);
|
||||||
|
|
||||||
|
// Abbrechen Button
|
||||||
|
const cancelBtn = document.createElement('button');
|
||||||
|
cancelBtn.textContent = 'Abbrechen';
|
||||||
|
cancelBtn.className = 'btn btn-default';
|
||||||
|
cancelBtn.style.cssText = 'width: 100%;';
|
||||||
|
cancelBtn.onclick = () => overlay.remove();
|
||||||
|
|
||||||
|
optionsDiv.appendChild(currentPageBtn);
|
||||||
|
optionsDiv.appendChild(allPagesBtn);
|
||||||
|
optionsDiv.appendChild(rangeDiv);
|
||||||
|
optionsDiv.appendChild(cancelBtn);
|
||||||
|
|
||||||
|
modal.appendChild(title);
|
||||||
|
modal.appendChild(optionsDiv);
|
||||||
|
overlay.appendChild(modal);
|
||||||
|
document.body.appendChild(overlay);
|
||||||
|
|
||||||
|
// Schließe Modal bei Klick auf Overlay
|
||||||
|
overlay.addEventListener('click', (e) => {
|
||||||
|
if (e.target === overlay) overlay.remove();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteCurrentPage() {
|
||||||
const removeButtons = Array.from(document.querySelectorAll('.remove-history'));
|
const removeButtons = Array.from(document.querySelectorAll('.remove-history'));
|
||||||
showModal(`Möchtest du wirklich ALLE ${removeButtons.length} Einträge aus dem Verlauf löschen?`, () => {
|
showModal(`Möchtest du wirklich alle ${removeButtons.length} Einträge auf dieser Seite löschen?`, () => {
|
||||||
deleteEntries(removeButtons);
|
deleteEntries(removeButtons);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function deleteAllPages() {
|
||||||
|
const pagination = document.querySelector('.pagination');
|
||||||
|
if (!pagination) {
|
||||||
|
showModal('Keine Pagination gefunden.', () => {});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const pageLinks = Array.from(pagination.querySelectorAll('li a'));
|
||||||
|
const pageNumbers = pageLinks
|
||||||
|
.map(link => {
|
||||||
|
const match = link.href.match(/\/page\/(\d+)/);
|
||||||
|
return match ? parseInt(match[1]) : 0;
|
||||||
|
})
|
||||||
|
.filter(num => num > 0);
|
||||||
|
|
||||||
|
const lastPage = pageNumbers.length > 0 ? Math.max(...pageNumbers) : 1;
|
||||||
|
|
||||||
|
showModal(`Möchtest du wirklich den GESAMTEN Verlauf löschen (${lastPage} Seiten)?`, async () => {
|
||||||
|
await deletePageRange(1, lastPage);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function deletePageRange(fromPage, toPage) {
|
||||||
|
// Speichere Lösch-Status in localStorage
|
||||||
|
localStorage.setItem('bulkDeleteFrom', fromPage);
|
||||||
|
localStorage.setItem('bulkDeleteTo', toPage);
|
||||||
|
localStorage.setItem('bulkDeleteCurrent', fromPage);
|
||||||
|
|
||||||
|
// Starte Löschvorgang
|
||||||
|
const url = fromPage === 1 ? 'https://www.anime-loads.org/history' : `https://www.anime-loads.org/history/page/${fromPage}`;
|
||||||
|
window.location.href = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkAndContinueBulkDelete() {
|
||||||
|
const current = parseInt(localStorage.getItem('bulkDeleteCurrent'));
|
||||||
|
const to = parseInt(localStorage.getItem('bulkDeleteTo'));
|
||||||
|
|
||||||
|
if (!current || !to) return;
|
||||||
|
|
||||||
|
// Zeige Fortschritt mit Abbrechen-Button
|
||||||
|
const progress = document.createElement('div');
|
||||||
|
progress.style.cssText = 'position: fixed; top: 20px; right: 20px; background: #f0ad4e; color: white; padding: 15px; border-radius: 5px; z-index: 10000; font-weight: bold;';
|
||||||
|
|
||||||
|
const progressText = document.createElement('div');
|
||||||
|
progressText.textContent = `Lösche Seite ${current} von ${to}...`;
|
||||||
|
progressText.style.marginBottom = '10px';
|
||||||
|
|
||||||
|
const cancelBtn = document.createElement('button');
|
||||||
|
cancelBtn.textContent = 'Abbrechen';
|
||||||
|
cancelBtn.className = 'btn btn-sm btn-default';
|
||||||
|
cancelBtn.onclick = () => {
|
||||||
|
localStorage.removeItem('bulkDeleteFrom');
|
||||||
|
localStorage.removeItem('bulkDeleteTo');
|
||||||
|
localStorage.removeItem('bulkDeleteCurrent');
|
||||||
|
window.location.href = 'https://www.anime-loads.org/history';
|
||||||
|
};
|
||||||
|
|
||||||
|
progress.appendChild(progressText);
|
||||||
|
progress.appendChild(cancelBtn);
|
||||||
|
document.body.appendChild(progress);
|
||||||
|
|
||||||
|
// Lösche alle Einträge auf der aktuellen Seite
|
||||||
|
const removeButtons = Array.from(document.querySelectorAll('.remove-history'));
|
||||||
|
removeButtons.forEach(btn => btn.click());
|
||||||
|
|
||||||
|
// Warte, dann gehe zur nächsten Seite
|
||||||
|
setTimeout(() => {
|
||||||
|
if (current < to) {
|
||||||
|
const nextPage = current + 1;
|
||||||
|
localStorage.setItem('bulkDeleteCurrent', nextPage);
|
||||||
|
const url = nextPage === 1 ? 'https://www.anime-loads.org/history' : `https://www.anime-loads.org/history/page/${nextPage}`;
|
||||||
|
window.location.href = url;
|
||||||
|
} else {
|
||||||
|
// Fertig - lösche Status
|
||||||
|
localStorage.removeItem('bulkDeleteFrom');
|
||||||
|
localStorage.removeItem('bulkDeleteTo');
|
||||||
|
localStorage.removeItem('bulkDeleteCurrent');
|
||||||
|
window.location.href = 'https://www.anime-loads.org/history';
|
||||||
|
}
|
||||||
|
}, 2000);
|
||||||
|
}
|
||||||
|
|
||||||
function deleteSelected() {
|
function deleteSelected() {
|
||||||
const checkboxes = Array.from(document.querySelectorAll('.bulk-delete-checkbox:checked'));
|
const checkboxes = Array.from(document.querySelectorAll('.bulk-delete-checkbox:checked'));
|
||||||
if (checkboxes.length === 0) {
|
if (checkboxes.length === 0) {
|
||||||
@@ -178,109 +355,24 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function deleteEntries(buttons) {
|
function deleteEntries(buttons) {
|
||||||
let deleted = 0;
|
let processed = 0;
|
||||||
const total = buttons.length;
|
const total = buttons.length;
|
||||||
|
|
||||||
buttons.forEach((btn, index) => {
|
buttons.forEach((btn, index) => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
// Prüfe ob Button noch im DOM existiert
|
||||||
|
if (document.body.contains(btn)) {
|
||||||
btn.click();
|
btn.click();
|
||||||
deleted++;
|
}
|
||||||
|
processed++;
|
||||||
|
|
||||||
if (deleted === total) {
|
if (processed === total) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
// Seite neu laden oder Checkboxen neu hinzufügen
|
// Seite neu laden
|
||||||
location.reload();
|
location.reload();
|
||||||
}, 500);
|
}, 1000); // Längere Wartezeit für alle Lösch-Requests
|
||||||
}
|
}
|
||||||
}, index * 200); // 200ms Verzögerung zwischen jedem Klick
|
}, index * 300); // Längere Verzögerung zwischen Klicks
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async function loadAllPages() {
|
|
||||||
const btn = document.getElementById('load-all-pages-btn');
|
|
||||||
const pagination = document.querySelector('.pagination');
|
|
||||||
|
|
||||||
if (!pagination) {
|
|
||||||
showModal('Keine Pagination gefunden.', () => {});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Finde die letzte Seitenzahl
|
|
||||||
const pageLinks = Array.from(pagination.querySelectorAll('li a'));
|
|
||||||
const pageNumbers = pageLinks
|
|
||||||
.map(link => {
|
|
||||||
const match = link.href.match(/\/page\/(\d+)/);
|
|
||||||
return match ? parseInt(match[1]) : 0;
|
|
||||||
})
|
|
||||||
.filter(num => num > 0);
|
|
||||||
|
|
||||||
if (pageNumbers.length === 0) {
|
|
||||||
showModal('Konnte letzte Seite nicht ermitteln.', () => {});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const lastPage = Math.max(...pageNumbers);
|
|
||||||
const currentPage = parseInt(window.location.pathname.match(/\/page\/(\d+)/)?.[1] || 1);
|
|
||||||
|
|
||||||
if (currentPage !== 1) {
|
|
||||||
showModal('Bitte gehe zuerst zur ersten Seite.', () => {});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
showModal(`Möchtest du ${lastPage - 1} weitere Seiten laden? Dies kann eine Weile dauern.`, async () => {
|
|
||||||
btn.disabled = true;
|
|
||||||
btn.textContent = 'Lade Seiten...';
|
|
||||||
|
|
||||||
const historyDiv = document.getElementById('history');
|
|
||||||
const table = historyDiv.querySelector('table tbody');
|
|
||||||
|
|
||||||
for (let page = 2; page <= lastPage; page++) {
|
|
||||||
try {
|
|
||||||
btn.textContent = `Lade Seite ${page}/${lastPage}...`;
|
|
||||||
|
|
||||||
const response = await fetch(`https://www.anime-loads.org/history/page/${page}`);
|
|
||||||
const html = await response.text();
|
|
||||||
|
|
||||||
const parser = new DOMParser();
|
|
||||||
const doc = parser.parseFromString(html, 'text/html');
|
|
||||||
const newRows = doc.querySelectorAll('#history table tbody tr');
|
|
||||||
|
|
||||||
newRows.forEach(row => {
|
|
||||||
const clonedRow = row.cloneNode(true);
|
|
||||||
table.appendChild(clonedRow);
|
|
||||||
|
|
||||||
// Checkbox sofort zum geklonten Row hinzufügen
|
|
||||||
const removeBtn = clonedRow.querySelector('.remove-history');
|
|
||||||
if (removeBtn && !clonedRow.querySelector('.bulk-delete-checkbox')) {
|
|
||||||
const checkbox = document.createElement('input');
|
|
||||||
checkbox.type = 'checkbox';
|
|
||||||
checkbox.className = 'bulk-delete-checkbox';
|
|
||||||
checkbox.style.cssText = 'margin-right: 10px; transform: scale(1.3);';
|
|
||||||
checkbox.addEventListener('change', updateCounter);
|
|
||||||
|
|
||||||
const td = removeBtn.closest('td');
|
|
||||||
td.insertBefore(checkbox, td.firstChild);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Counter nach jeder Seite aktualisieren
|
|
||||||
updateCounter();
|
|
||||||
|
|
||||||
// Kleine Verzögerung, um Server nicht zu überlasten
|
|
||||||
await new Promise(resolve => setTimeout(resolve, 500));
|
|
||||||
} catch (error) {
|
|
||||||
console.error(`Fehler beim Laden von Seite ${page}:`, error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pagination verstecken
|
|
||||||
pagination.style.display = 'none';
|
|
||||||
|
|
||||||
btn.textContent = 'Alle Seiten geladen!';
|
|
||||||
setTimeout(() => {
|
|
||||||
btn.textContent = 'Alle Seiten laden';
|
|
||||||
btn.disabled = false;
|
|
||||||
}, 3000);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|||||||
Reference in New Issue
Block a user