Created
February 11, 2026 07:08
-
-
Save nickalexej/30bed76bb33856fc55ec2cf923f51719 to your computer and use it in GitHub Desktop.
Entfernt Automatisch erstellte Workouts aus Garmin Connect Web --- 2026-02-11: An Anpassungen im HTML von Garmin angepasst. Verbesserte Log Ausgabe im Terminal
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| function deleteWorkouts() { | |
| // Suche alle Zeilen in der Workout-Tabelle | |
| const workoutRows = document.querySelectorAll('tr.SortableTableLayout_tableRow__iHLrR'); | |
| if (workoutRows.length === 0) { | |
| console.log('Keine Workout-Zeilen gefunden.'); | |
| return; | |
| } | |
| // Finde das erste Workout mit "Bei Predictive Fitness bearbeiten" | |
| let targetRow = null; | |
| for (const row of workoutRows) { | |
| // Prüfe die "Bearbeitet"-Spalte | |
| const editedCell = row.querySelector('td[data-title="Bearbeitet"]'); | |
| if (editedCell) { | |
| const text = editedCell.textContent.trim(); | |
| if (text === 'Bei Predictive Fitness bearbeiten') { | |
| const workoutName = row.querySelector('td[data-title="Name"]')?.textContent; | |
| targetRow = row; | |
| console.log('✓ Predictive Fitness Workout gefunden:', workoutName); | |
| break; | |
| } | |
| } | |
| } | |
| // Wenn kein passendes Training gefunden wurde → fertig | |
| if (!targetRow) { | |
| console.log('✓✓✓ Keine Predictive Fitness Workouts mehr gefunden. Fertig!'); | |
| return; | |
| } | |
| // Finde den Drei-Punkte-Button (Ellipsis) | |
| const ellipsisButton = targetRow.querySelector('button[aria-label="Optionen"] i.icon-ellipsis-vertical'); | |
| if (!ellipsisButton) { | |
| console.log('⚠ Drei-Punkte-Menü nicht gefunden. Überspringe...'); | |
| setTimeout(deleteWorkouts, 1000); | |
| return; | |
| } | |
| // Klicke auf den Button (parent element) | |
| ellipsisButton.closest('button').click(); | |
| console.log('→ Menü geöffnet...'); | |
| setTimeout(() => { | |
| findAndClickDelete(targetRow); | |
| }, 500); | |
| } | |
| function findAndClickDelete(row) { | |
| // Suche das Löschen-Element im Menü dieser Zeile | |
| const menuItems = row.querySelectorAll('.Menu_menuItems__eNgH5'); | |
| console.log('→ Gefundene Menü-Items:', menuItems.length); | |
| let deleteItem = null; | |
| for (const item of menuItems) { | |
| const text = item.textContent.toLowerCase().trim(); | |
| if (text.includes('löschen') || text.includes('delete')) { | |
| deleteItem = item; | |
| console.log('✓ Löschen-Item gefunden!'); | |
| break; | |
| } | |
| } | |
| if (!deleteItem) { | |
| console.log('→ Löschen-Item nicht in Zeile gefunden. Versuche globale Suche...'); | |
| // Alternative: Suche überall nach sichtbaren Menü-Items | |
| const allMenuItems = document.querySelectorAll('.Menu_menuItems__eNgH5'); | |
| for (const item of allMenuItems) { | |
| const text = item.textContent.toLowerCase().trim(); | |
| if (text.includes('löschen') || text.includes('delete')) { | |
| deleteItem = item; | |
| console.log('✓ Löschen-Item global gefunden!'); | |
| break; | |
| } | |
| } | |
| } | |
| if (!deleteItem) { | |
| console.log('⚠ Kein Löschen-Item gefunden. Schließe Menü und versuche nächstes Workout...'); | |
| document.body.click(); // Menü schließen | |
| setTimeout(deleteWorkouts, 1500); | |
| return; | |
| } | |
| deleteItem.click(); | |
| console.log('→ Löschen geklickt, warte auf Bestätigungs-Dialog...'); | |
| setTimeout(() => confirmDelete(), 1200); | |
| } | |
| function confirmDelete() { | |
| // Suche nach ALLEN Dialogen | |
| const allDialogs = document.querySelectorAll('.Dialog_dialog__fiyk-'); | |
| console.log('→ Gefundene Dialoge:', allDialogs.length); | |
| // Finde den Dialog mit "Training löschen" im Header | |
| let deleteDialog = null; | |
| for (const dialog of allDialogs) { | |
| const header = dialog.querySelector('.Dialog_dialogHeader__Guxgt h3'); | |
| if (header) { | |
| const headerText = header.textContent.trim(); | |
| console.log(' → Dialog-Titel: "' + headerText + '"'); | |
| if (headerText === 'Training löschen' || headerText.toLowerCase().includes('löschen')) { | |
| deleteDialog = dialog; | |
| console.log('✓ "Training löschen"-Dialog gefunden!'); | |
| break; | |
| } | |
| } | |
| } | |
| if (!deleteDialog) { | |
| console.log('⚠ "Training löschen"-Dialog nicht gefunden! Warte länger...'); | |
| // Zähle Wiederholungen | |
| if (!confirmDelete.retryCount) confirmDelete.retryCount = 0; | |
| confirmDelete.retryCount++; | |
| if (confirmDelete.retryCount < 5) { | |
| setTimeout(confirmDelete, 500); | |
| } else { | |
| console.log('⚠ Dialog erscheint nicht. Schließe alle Dialoge und versuche nächstes Workout...'); | |
| confirmDelete.retryCount = 0; | |
| // Schließe alle Dialoge | |
| const closeBtns = document.querySelectorAll('.Dialog_dialogCloseBtn__EtPd-'); | |
| closeBtns.forEach(btn => btn.click()); | |
| setTimeout(deleteWorkouts, 2000); | |
| } | |
| return; | |
| } | |
| // Reset retry counter | |
| confirmDelete.retryCount = 0; | |
| // Suche den Footer in diesem spezifischen Dialog | |
| const dialogFooter = deleteDialog.querySelector('.Dialog_dialogFooter__W9xGT'); | |
| if (!dialogFooter) { | |
| console.log('⚠ Dialog-Footer nicht gefunden in diesem Dialog!'); | |
| setTimeout(confirmDelete, 500); | |
| return; | |
| } | |
| console.log('✓ Dialog-Footer im richtigen Dialog gefunden!'); | |
| // Suche alle Buttons im Footer dieses spezifischen Dialogs | |
| const footerButtons = dialogFooter.querySelectorAll('button'); | |
| console.log('→ Gefundene Footer-Buttons im Lösch-Dialog:', footerButtons.length); | |
| let confirmButton = null; | |
| for (const btn of footerButtons) { | |
| const text = btn.textContent.toLowerCase().trim(); | |
| const classes = btn.className; | |
| console.log(' → Button-Text: "' + text + '", Klassen:', classes); | |
| // Suche nach dem "Löschen" Button | |
| if (text === 'löschen' || text === 'delete') { | |
| confirmButton = btn; | |
| console.log('✓✓ Löschen-Button gefunden!'); | |
| break; | |
| } | |
| } | |
| if (!confirmButton) { | |
| // Fallback: Suche nach danger-Button | |
| confirmButton = dialogFooter.querySelector('button.Button_danger__hwLq8, button[class*="danger"]'); | |
| if (confirmButton) { | |
| console.log('✓✓ Löschen-Button via Danger-Klasse gefunden!'); | |
| } | |
| } | |
| if (!confirmButton) { | |
| console.log('⚠ Kein Löschen-Button gefunden. Schließe Dialog...'); | |
| const closeBtn = deleteDialog.querySelector('.Dialog_dialogCloseBtn__EtPd-'); | |
| if (closeBtn) { | |
| closeBtn.click(); | |
| console.log('→ Dialog geschlossen, versuche nächstes Workout...'); | |
| } | |
| setTimeout(deleteWorkouts, 2000); | |
| return; | |
| } | |
| console.log('→ Klicke Löschen-Button...'); | |
| confirmButton.click(); | |
| console.log('✓✓✓ Workout gelöscht!\n'); | |
| // Nächstes Workout nach kurzer Pause | |
| setTimeout(deleteWorkouts, 2000); | |
| } | |
| // Start | |
| console.log('╔════════════════════════════════════════════════════╗'); | |
| console.log('║ Predictive Fitness Workout Bulk-Löscher ║'); | |
| console.log('╚════════════════════════════════════════════════════╝'); | |
| console.log('Kriterium: "Bei Predictive Fitness bearbeiten"\n'); | |
| deleteWorkouts(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment