Created
April 2, 2026 03:18
-
-
Save rezarajan/77b66de37d708158569f4fbdf4ef6bbc to your computer and use it in GitHub Desktop.
A script which automated the deletion of all photos in Google Photos. Simply copy-and-paste into the developer console.
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
| (async function () { | |
| "use strict"; | |
| const CONFIG = { | |
| maxRuntimeMinutes: 30, // safety cutoff | |
| maxPerBatch: 30, | |
| stallLimit: 5, | |
| scrollDelay: 1200, | |
| actionDelay: 1200, | |
| dryRun: false, | |
| }; | |
| const startTime = Date.now(); | |
| const sleep = (ms) => new Promise((r) => setTimeout(r, ms)); | |
| function log(msg, type = "info") { | |
| const styles = { | |
| info: "color:#2196F3", | |
| success: "color:#4CAF50", | |
| warn: "color:#FFC107", | |
| error: "color:#F44336", | |
| }; | |
| console.log(`%c[AUTO-DELETE] ${msg}`, styles[type]); | |
| } | |
| function getCheckboxes() { | |
| return Array.from( | |
| document.querySelectorAll(".ckGgle[aria-checked='false']") | |
| ).filter((el) => el.offsetParent !== null); | |
| } | |
| function getDeleteButton() { | |
| return document.querySelector('button[aria-label="Move to trash"]'); | |
| } | |
| function getConfirmButton() { | |
| return Array.from(document.querySelectorAll("button")).find( | |
| (b) => | |
| b.textContent.trim() === "Move to trash" && | |
| b.offsetParent !== null | |
| ); | |
| } | |
| async function autoScroll() { | |
| const before = document.body.scrollHeight; | |
| window.scrollTo(0, before); | |
| await sleep(CONFIG.scrollDelay); | |
| const after = document.body.scrollHeight; | |
| return after > before; // did new content load? | |
| } | |
| async function deleteBatch() { | |
| const boxes = getCheckboxes().slice(0, CONFIG.maxPerBatch); | |
| if (!boxes.length) return 0; | |
| log(`Selecting ${boxes.length} items...`); | |
| boxes.forEach((b) => { | |
| try { b.click(); } catch {} | |
| }); | |
| await sleep(400); | |
| if (CONFIG.dryRun) { | |
| log("DRY RUN: skipping delete", "warn"); | |
| return boxes.length; | |
| } | |
| const deleteBtn = getDeleteButton(); | |
| if (!deleteBtn) { | |
| log("Delete button not found", "error"); | |
| return 0; | |
| } | |
| deleteBtn.click(); | |
| await sleep(500); | |
| const confirmBtn = getConfirmButton(); | |
| if (!confirmBtn) { | |
| log("Confirm button not found", "error"); | |
| return 0; | |
| } | |
| confirmBtn.click(); | |
| await sleep(CONFIG.actionDelay); | |
| return boxes.length; | |
| } | |
| async function run() { | |
| let totalDeleted = 0; | |
| let stallCounter = 0; | |
| let noNewScrolls = 0; | |
| log("=== FULL AUTO MODE STARTED ===", "success"); | |
| while (true) { | |
| // Safety timeout | |
| if ((Date.now() - startTime) > CONFIG.maxRuntimeMinutes * 60000) { | |
| log("Max runtime reached. Stopping.", "warn"); | |
| break; | |
| } | |
| // Step 1: Scroll to load more | |
| const newLoaded = await autoScroll(); | |
| if (!newLoaded) { | |
| noNewScrolls++; | |
| log(`No new content loaded (${noNewScrolls})`, "warn"); | |
| } else { | |
| noNewScrolls = 0; | |
| } | |
| // Step 2: Try deleting visible batch | |
| const deleted = await deleteBatch(); | |
| if (deleted === 0) { | |
| stallCounter++; | |
| log(`No deletions this cycle (${stallCounter})`, "warn"); | |
| } else { | |
| stallCounter = 0; | |
| totalDeleted += deleted; | |
| log(`Deleted ${deleted}. Total: ${totalDeleted}`, "success"); | |
| } | |
| // Step 3: Exit conditions | |
| if (stallCounter >= CONFIG.stallLimit && noNewScrolls >= 3) { | |
| log("No progress + no new content. Likely finished.", "success"); | |
| break; | |
| } | |
| // Step 4: Reset view occasionally (prevents UI bugs) | |
| window.scrollTo(0, 0); | |
| await sleep(800); | |
| } | |
| log(`DONE. Total deleted: ~${totalDeleted}`, "success"); | |
| } | |
| await run(); | |
| })(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment