// ==UserScript== // @name Anime-Loads: Verlauf-Massenlöschung // @namespace https://git.ponywave.de/Akamaru/Userscripts // @version 1.2 // @description Fügt Buttons hinzu, um Verlaufseinträge auf anime-loads.org schneller zu löschen // @author Akamaru // @match https://www.anime-loads.org/history* // @grant none // @updateURL https://git.ponywave.de/Akamaru/Userscripts/raw/branch/master/anime-loads-bulk-delete-history.user.js // @downloadURL https://git.ponywave.de/Akamaru/Userscripts/raw/branch/master/anime-loads-bulk-delete-history.user.js // ==/UserScript== (function() { 'use strict'; // Warte bis die Seite geladen ist if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } function init() { // Prüfe ob Bulk-Delete läuft checkAndContinueBulkDelete(); const historyDiv = document.getElementById('history'); if (!historyDiv) return; // Erstelle Button-Container const buttonContainer = document.createElement('div'); buttonContainer.style.cssText = 'margin-bottom: 15px; padding: 10px; background-color: #f5f5f5; border-radius: 5px;'; // Button: Alle löschen const deleteAllBtn = document.createElement('button'); deleteAllBtn.textContent = 'Alle löschen'; deleteAllBtn.className = 'btn btn-danger'; deleteAllBtn.style.marginRight = '10px'; deleteAllBtn.onclick = deleteAll; // Button: Ausgewählte löschen const deleteSelectedBtn = document.createElement('button'); deleteSelectedBtn.textContent = 'Ausgewählte löschen'; deleteSelectedBtn.className = 'btn btn-warning'; deleteSelectedBtn.style.marginRight = '10px'; deleteSelectedBtn.onclick = deleteSelected; // Button: Alle auswählen/abwählen const toggleAllBtn = document.createElement('button'); toggleAllBtn.textContent = 'Alle auswählen'; toggleAllBtn.className = 'btn btn-primary'; toggleAllBtn.style.marginRight = '10px'; toggleAllBtn.onclick = function() { const checkboxes = historyDiv.querySelectorAll('.bulk-delete-checkbox'); const allChecked = Array.from(checkboxes).every(cb => cb.checked); checkboxes.forEach(cb => cb.checked = !allChecked); toggleAllBtn.textContent = allChecked ? 'Alle auswählen' : 'Alle abwählen'; updateCounter(); }; // Zähler für ausgewählte Einträge const counterSpan = document.createElement('span'); counterSpan.id = 'selection-counter'; counterSpan.style.cssText = 'margin-left: 15px; font-weight: bold; font-size: 16px;'; counterSpan.textContent = 'Ausgewählt: 0'; buttonContainer.appendChild(deleteAllBtn); buttonContainer.appendChild(deleteSelectedBtn); buttonContainer.appendChild(toggleAllBtn); buttonContainer.appendChild(counterSpan); // Füge Button-Container vor dem History-Div ein historyDiv.parentNode.insertBefore(buttonContainer, historyDiv); // Füge Checkboxen zu jedem Eintrag hinzu addCheckboxes(); } function updateCounter() { const counter = document.getElementById('selection-counter'); if (!counter) return; const checked = document.querySelectorAll('.bulk-delete-checkbox:checked').length; const total = document.querySelectorAll('.bulk-delete-checkbox').length; counter.textContent = `Ausgewählt: ${checked} / ${total}`; } function addCheckboxes() { const historyDiv = document.getElementById('history'); if (!historyDiv) return; const rows = historyDiv.querySelectorAll('table tbody tr'); rows.forEach(row => { if (row.querySelector('.bulk-delete-checkbox')) return; const removeBtn = row.querySelector('.remove-history'); if (!removeBtn) return; 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); }); updateCounter(); } function showModal(message, onConfirm) { // 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 messageEl = document.createElement('p'); messageEl.textContent = message; messageEl.style.cssText = 'margin: 0 0 20px 0; font-size: 16px;'; const buttonContainer = document.createElement('div'); buttonContainer.style.cssText = 'display: flex; justify-content: flex-end; gap: 10px;'; const cancelBtn = document.createElement('button'); cancelBtn.textContent = 'Abbrechen'; cancelBtn.className = 'btn btn-default'; cancelBtn.onclick = () => overlay.remove(); const confirmBtn = document.createElement('button'); confirmBtn.textContent = 'Bestätigen'; confirmBtn.className = 'btn btn-danger'; confirmBtn.onclick = () => { overlay.remove(); onConfirm(); }; buttonContainer.appendChild(cancelBtn); buttonContainer.appendChild(confirmBtn); modal.appendChild(messageEl); modal.appendChild(buttonContainer); 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 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')); showModal(`Möchtest du wirklich alle ${removeButtons.length} Einträge auf dieser Seite löschen?`, () => { 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() { const checkboxes = Array.from(document.querySelectorAll('.bulk-delete-checkbox:checked')); if (checkboxes.length === 0) { showModal('Bitte wähle mindestens einen Eintrag aus.', () => {}); return; } showModal(`Möchtest du ${checkboxes.length} Einträge aus dem Verlauf löschen?`, () => { const removeButtons = checkboxes.map(cb => cb.closest('td').querySelector('.remove-history')); deleteEntries(removeButtons); }); } function deleteEntries(buttons) { let processed = 0; const total = buttons.length; buttons.forEach((btn, index) => { setTimeout(() => { // Prüfe ob Button noch im DOM existiert if (document.body.contains(btn)) { btn.click(); } processed++; if (processed === total) { setTimeout(() => { // Seite neu laden location.reload(); }, 1000); // Längere Wartezeit für alle Lösch-Requests } }, index * 300); // Längere Verzögerung zwischen Klicks }); } })();