Skip to content

Instantly share code, notes, and snippets.

@StellarStoic
Created October 26, 2025 22:40
Show Gist options
  • Select an option

  • Save StellarStoic/cb3a412306ee4eb57ebeec697adae6af to your computer and use it in GitHub Desktop.

Select an option

Save StellarStoic/cb3a412306ee4eb57ebeec697adae6af to your computer and use it in GitHub Desktop.
Delete Google photos one by one programmatically
// Infinite slow google photos Delete Loop which does the job.
// Visit photos.google.com and open the first image full screen.
// Paste this script in a DEV console.
// When you hit enter, the script will start deleting photos one by one.
// You can stop it with stopDeleteLoop() or simply refresh the page.
// To slow down or speed up the deletion process, play with timing. (Current settings provided me good balance without errors)
// Images are only deleted from Google photos and any duplicates on other devices should not be affected.
// Use at your own risk and create a backup of your data first by visiting https://takeout.google.com/
(function() {
let totalDeleted = 0;
let isRunning = false;
let currentState = 'LOOKING_FOR_DELETE'; // States: LOOKING_FOR_DELETE, SELECTING_DELETE_OPTION, COMPLETED_CYCLE
const wait = (ms) => new Promise(resolve => setTimeout(resolve, ms));
function getElementByXPath(xpath) {
return document.evaluate(xpath, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
}
function safeClick(element) {
if (!element) return false;
try {
element.click();
return true;
} catch (e) {
return false;
}
}
// Alternative method to find "Current photo only" by text content
function findCurrentPhotoOnlyOption() {
// Method 1: Try by XPath first (your provided XPath)
const xpathElement = getElementByXPath('/html/body/div[1]/div/c-wiz/div[4]/c-wiz/div[1]/div[2]/div/span/div/div[9]/div[31]/div/ul/li[2]');
if (xpathElement) return xpathElement;
// Method 2: Search by text content
const menuItems = document.querySelectorAll('li[role="menuitem"]');
for (let item of menuItems) {
if (item.textContent && item.textContent.includes('Current photo only')) {
return item;
}
}
// Method 3: Search by class names from your HTML
const elements = document.querySelectorAll('.aqdrmf-rymPhb-ibnC6b');
for (let element of elements) {
if (element.textContent && element.textContent.includes('Current photo only')) {
return element;
}
}
return null;
}
// Function to check for Move to Bin button
function findMoveToBinButton() {
// Method 1: Your XPath
const xpathElement = getElementByXPath('/html/body/div[2]/div/div[2]/div[2]/div/div[1]/div/div[2]/button[2]/span[4]');
if (xpathElement) return xpathElement;
// Method 2: Search by text content
const buttons = document.querySelectorAll('button');
for (let button of buttons) {
if (button.textContent && button.textContent.includes('Move to bin')) {
return button;
}
}
return null;
}
async function deleteLoop() {
if (isRunning) {
console.log('Delete loop already running');
return;
}
isRunning = true;
console.log('Starting enhanced delete loop with "Move to Bin" priority...');
console.log('Make sure the first photo is already open in the viewer!');
while (isRunning) {
try {
console.log(`\n--- Deleting Photo ${totalDeleted + 1} (State: ${currentState}) ---`);
// ALWAYS CHECK FOR MOVE TO BIN FIRST (HIGHEST PRIORITY)
const moveToBin = findMoveToBinButton();
if (moveToBin) {
console.log('Clicking move to bin (HIGH PRIORITY)...');
safeClick(moveToBin);
totalDeleted++;
console.log(`SUCCESS! Deleted photo ${totalDeleted}`);
currentState = 'LOOKING_FOR_DELETE';
await wait(3000); // Wait for deletion and next image to load
continue; // Skip the rest of the loop and start fresh
}
switch (currentState) {
case 'LOOKING_FOR_DELETE':
// STEP 1: Look for Delete button (only if Move to Bin is not available)
const deleteBtn = getElementByXPath('/html/body/div[1]/div/c-wiz/div[4]/c-wiz/div[1]/div[2]/div/span/div/div[9]/span/button/div');
if (deleteBtn) {
console.log('Clicking delete button...');
safeClick(deleteBtn);
currentState = 'SELECTING_DELETE_OPTION';
await wait(2000); // Wait for menu to appear
} else {
console.log('Delete button not found. Checking if delete options are available...');
// Check if delete options are already visible
const currentPhotoOption = findCurrentPhotoOnlyOption();
if (currentPhotoOption) {
console.log('Delete menu already open! Moving to option selection...');
currentState = 'SELECTING_DELETE_OPTION';
} else {
console.log('Waiting for delete button...');
await wait(2000);
}
}
break;
case 'SELECTING_DELETE_OPTION':
// STEP 2: Look for and click "Current photo only" option (only if Move to Bin is not available)
const currentPhotoOption = findCurrentPhotoOnlyOption();
if (currentPhotoOption) {
console.log('Clicking "Current photo only"...');
safeClick(currentPhotoOption);
totalDeleted++;
console.log(`SUCCESS! Deleted photo ${totalDeleted}`);
currentState = 'LOOKING_FOR_DELETE';
await wait(3000); // Wait for next image to load
} else {
console.log('"Current photo only" option not found. Checking state...');
// If option not found, check if we're back to delete button (cycle completed)
const deleteBtn = getElementByXPath('/html/body/div[1]/div/c-wiz/div[4]/c-wiz/div[1]/div[2]/div/span/div/div[9]/span/button/div');
if (deleteBtn) {
console.log('Cycle completed! Photo was deleted. Moving to next...');
totalDeleted++;
console.log(`SUCCESS! Deleted photo ${totalDeleted}`);
currentState = 'LOOKING_FOR_DELETE';
} else {
console.log('Waiting for "Current photo only" option...');
await wait(2000);
}
}
break;
}
} catch (error) {
console.log('Error in delete loop:', error);
// Reset state on error to prevent infinite stuck state
currentState = 'LOOKING_FOR_DELETE';
await wait(2000);
}
}
}
function stopDeleteLoop() {
isRunning = false;
console.log('Stopped delete loop');
console.log(`Total deleted: ${totalDeleted}`);
}
// Manual start function for testing
function startDeleteLoop() {
if (!isRunning) {
deleteLoop();
}
}
// Auto-start the delete loop
console.log('AUTO-STARTING ENHANCED DELETE LOOP IN 3 SECONDS...');
console.log('Make sure a photo is open in the viewer!');
setTimeout(() => {
deleteLoop();
}, 3000);
console.log(`
To stop: Run stopDeleteLoop() or refresh the page
Commands:
- stopDeleteLoop() - Stop the loop
- startDeleteLoop() - Start manually
- totalDeleted - See count: ${totalDeleted}
`);
// Make functions globally available
window.stopDeleteLoop = stopDeleteLoop;
window.startDeleteLoop = startDeleteLoop;
window.totalDeleted = totalDeleted;
})();
@StellarStoic
Copy link
Author

Forget this, because this is so much better... https://github.com/mrishab/google-photos-delete-tool

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment