diff --git a/app/plugins/_registry.js b/app/plugins/_registry.js index 6caed334..c281970b 100644 --- a/app/plugins/_registry.js +++ b/app/plugins/_registry.js @@ -11,6 +11,7 @@ import { commands as tota11y_commands, default as Tota11yPlugin } from './tota11 import { commands as shuffle_commands, default as ShufflePlugin } from './shuffle' import { commands as colorblind_commands, default as ColorblindPlugin } from './colorblind' import { commands as zindex_commands, default as ZIndexPlugin } from './zindex' +import { commands as dl_images_commands, default as DlImagesPlugin } from './download-images' const commandsToHash = (plugin_commands, plugin_fn) => plugin_commands.reduce((commands, command) => @@ -31,6 +32,7 @@ export const PluginRegistry = new Map(Object.entries({ ...commandsToHash(shuffle_commands, ShufflePlugin), ...commandsToHash(colorblind_commands, ColorblindPlugin), ...commandsToHash(zindex_commands, ZIndexPlugin), + ...commandsToHash(dl_images_commands, DlImagesPlugin), })) export const PluginHints = [ @@ -46,5 +48,6 @@ export const PluginHints = [ tota11y_commands[0], shuffle_commands[0], zindex_commands[0], + dl_images_commands[0], ...colorblind_commands, ].map(command => `/${command}`) diff --git a/app/plugins/download-images.js b/app/plugins/download-images.js new file mode 100644 index 00000000..05ae2605 --- /dev/null +++ b/app/plugins/download-images.js @@ -0,0 +1,87 @@ +export const commands = [ + 'download-images', + 'download-all-images', +] + +const fetchAndWrite = async ({url, filename}, dirHandle) => { + try { + const response = await fetch(url) + const file = await dirHandle.getFileHandle( + filename.length > 40 + ? filename.substr(0, 40) + : filename, + { create: true } + ) + const writable = await file.createWritable() + + return await response.body.pipeTo(writable) + } catch (err) { + console.error(err) + throw new Error(err) + } +} + +export default async function () { + if (window.showDirectoryPicker === undefined) { + alert('missing the Directory Picker api 🙁') + return + } + + const imgs = [...document.querySelectorAll("img")] + .filter(img => img.src) + .map(img => ({ + url: img.src, + filename: img.src.substr(img.src.lastIndexOf('/') + 1), + })) + + const css_urls = [...document.styleSheets] + .filter(sheet => { + try { return sheet.cssRules } + catch { } + }) + .flatMap(sheet => Array.from(sheet.cssRules)) + .filter(rule => rule.style) + .filter(rule => rule.style.backgroundImage !== '') + .filter(rule => rule.style.backgroundImage !== 'initial') + .filter(rule => rule.style.backgroundImage.includes("url")) + .reduce((urls, { style }) => { + let filename = '' + let url = '' + + let cots = false + let start = style.backgroundImage.indexOf('(') + if (style.backgroundImage.charAt(start + 1) == '"') cots = true + let end = style.backgroundImage.lastIndexOf(')') + if (cots) { + start += 2 + end -= 6 + } + url = style.backgroundImage.substr(start, end) + + const hasParams = url.indexOf("?") + if (hasParams > 0) + url = url.substr(0, hasParams) + + filename = url.substr(url.lastIndexOf('/') + 1) + + urls.push({ + url, + filename: filename + ? filename + : url, + }) + return urls + }, []) + + if (!confirm(`Download around ${imgs.length + css_urls.length} images?`)) return + const dirHandle = await window.showDirectoryPicker() + + const downloads = [...imgs, ...css_urls] + .map(image => + fetchAndWrite(image, dirHandle)) + + const results = await Promise.allSettled(downloads) + const successes = results.filter(res => res.status == 'fulfilled') + + confirm(`Successfully downloaded ${successes.length} images 👍`) +}