Last active
October 16, 2025 16:10
-
-
Save jcisio/b173edab440ef636aebc9df3b7b8d93f to your computer and use it in GitHub Desktop.
Download all links in selected text
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 () => { | |
| // Defaults that usually work, but change to fit your need. | |
| const delayMs = 1000; // delay between links (milliseconds), it should be at least 1/3 the time to download a file | |
| const allow = /\.(pdf|zip|mp3)(\?|#|$)/i; // Restrict to certain file types | |
| // === Nothing to change after this === | |
| const sel = window.getSelection(); | |
| if (!sel || sel.rangeCount === 0) { | |
| alert('Select some content on the page first.'); | |
| return; | |
| } | |
| // Clone the selection to a detached fragment so we can safely query it | |
| const frag = sel.getRangeAt(0).cloneContents(); | |
| // 1) Links from <a. | |
| const anchorUrls = Array.from(frag.querySelectorAll('a[href]')) | |
| .map(a => a.href); | |
| // 2) Plain-text URLs inside the selection (e.g., “https://example.com/file.pdf”) | |
| const text = (frag.textContent || ''); | |
| const plainUrls = Array.from(text.matchAll(/\bhttps?:\/\/[^\s"'<>]+/gi)).map(m => m[0]); | |
| // Merge & de-duplicate | |
| const urls = Array.from(new Set([...anchorUrls, ...plainUrls])) | |
| .filter(u => /^https?:\/\//i.test(u)); | |
| if (urls.length === 0) { | |
| alert('No links found in the selection.'); | |
| return; | |
| } | |
| const urlsFiltered = urls.filter(u => allow.test(u)); | |
| const targets = urlsFiltered.length ? urlsFiltered : urls; | |
| const inferName = (url, i) => { | |
| const raw = url.split('/').pop().split('#')[0].split('?')[0]; | |
| const name = decodeURIComponent(raw || ''); | |
| return name || `download-${i + 1}`; | |
| }; | |
| // Try to trigger download via <a download>. This generally works best for | |
| // same-origin or when the server allows it; some cross-origin links may open in a tab. | |
| const sleep = (ms) => new Promise(res => setTimeout(res, ms)); | |
| let ok = 0; | |
| for (let i = 0; i < urls.length; i++) { | |
| const url = urls[i]; | |
| const a = document.createElement('a'); | |
| a.href = url; | |
| a.download = inferName(url, i); // browsers may ignore for cross-origin, but it's a hint | |
| a.rel = 'noopener'; | |
| a.style.display = 'none'; | |
| document.body.appendChild(a); | |
| // Trigger the download | |
| a.click(); | |
| a.remove(); | |
| ok++; | |
| // Wait between downloads | |
| await sleep(delayMs); | |
| } | |
| console.log(`Attempted downloads: ${count}, but sometimes you can only download about 20 files each time.\n`, targets); | |
| })(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment