Created
May 21, 2026 23:55
-
-
Save brunolm/7c1a4a6d2a69edc247a7e1a0fd737045 to your computer and use it in GitHub Desktop.
Google Photos bulk-select via DevTools
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
| // Google Photos — bulk-select all photos by scrolling and clicking checkboxes. | |
| // | |
| // Usage: | |
| // 1. Open https://photos.google.com in your browser. | |
| // 2. Open DevTools (F12) and switch to the Console tab. | |
| // 3. Paste this script and press Enter. | |
| // 4. Wait for it to finish; verify the selection count in the toolbar. | |
| // 5. Click the trash icon to delete (items go to Trash for 30 days). | |
| // | |
| // How it works: | |
| // - Finds the inner scrollable container (Photos virtualizes its list). | |
| // - Clicks day-header "Select all" checkboxes (efficient for multi-photo days). | |
| // - Clicks individual photo checkboxes for single-photo days. | |
| // - Scrolls and repeats until the bottom is reached. | |
| // | |
| // Caveats: | |
| // - Destructive. Test on a small range first. | |
| // - Google may cap very large selections; if Delete fails, do it in batches. | |
| // - If selectors stop matching, inspect a checkbox and update R4HkWb / ckGgle. | |
| (async () => { | |
| const __my_sleep = __my_ms => new Promise(__my_r => setTimeout(__my_r, __my_ms)); | |
| const __my_scroller = (() => { | |
| for (const __my_el of document.querySelectorAll('*')) { | |
| if (__my_el.scrollHeight > __my_el.clientHeight + 100) { | |
| const __my_s = getComputedStyle(__my_el); | |
| if (__my_s.overflowY === 'auto' || __my_s.overflowY === 'scroll') return __my_el; | |
| } | |
| } | |
| return document.scrollingElement; | |
| })(); | |
| console.log('Scroller:', __my_scroller); | |
| let __my_stable = 0, __my_clicked = 0, __my_lastScroll = -1; | |
| while (__my_stable < 8) { | |
| document.querySelectorAll('div.QcpS9c.R4HkWb[aria-checked="false"]').forEach(__my_cb => { | |
| __my_cb.click(); __my_clicked++; | |
| }); | |
| await __my_sleep(150); | |
| document.querySelectorAll('div.QcpS9c.ckGgle[aria-checked="false"]').forEach(__my_cb => { | |
| __my_cb.click(); __my_clicked++; | |
| }); | |
| await __my_sleep(100); | |
| const __my_before = __my_scroller.scrollTop; | |
| __my_scroller.scrollBy({ top: __my_scroller.clientHeight * 0.7, behavior: 'instant' }); | |
| await __my_sleep(700); | |
| const __my_after = __my_scroller.scrollTop; | |
| if (__my_after === __my_lastScroll || __my_after === __my_before) __my_stable++; else __my_stable = 0; | |
| __my_lastScroll = __my_after; | |
| console.log(`clicked=${__my_clicked} scroll=${__my_after}/${__my_scroller.scrollHeight} stable=${__my_stable}`); | |
| } | |
| console.log(`Done. Clicked ${__my_clicked} checkboxes.`); | |
| })(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment