您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
从poipiku下载图片,需要先输入密码
// ==UserScript== // @name poipiku图片下载 // @namespace http://tampermonkey.net/ // @version 0.1 // @description 从poipiku下载图片,需要先输入密码 // @author shadows // @include /^https://poipiku\.com/\d+/\d+\.html/ // @icon http://poipiku.com/favicon.ico // @grant GM_registerMenuCommand // @grant GM.xmlHttpRequest // @grant GM_xmlhttpRequest // @require https://cdn.jsdelivr.net/npm/@zip.js/[email protected]/dist/zip.min.js // @license MIT // ==/UserScript== 'use strict'; GM_registerMenuCommand("下载",clickButton); async function clickButton(event) { event.stopPropagation(); event.preventDefault(); const name = document.querySelector("h2.IllustItemUserName >a").text; const images = document.querySelectorAll('.IllustItemThubExpand img.IllustItemThumbImg'); const targetLength = images.length.toString().length; const blobWriter = new zip.BlobWriter("application/zip"); const zipWriter = new zip.ZipWriter(blobWriter); let imagesData = []; images.forEach((img, idx) =>imagesData.push({url: img.src.replace(/_640\.jpg$/, ''),filename: `${(idx + 1).toString().padStart(targetLength,'0')}.jpg`,zipWriter: zipWriter})); await asyncPool(10, imagesData, imageWorker); const zipFile = await zipWriter.close(); saveBlob(zipFile , `${name}.zip`); } async function imageWorker(item) { let image = await getBlobFromUrl(item.url); console.log(`${item.filename} have downloaded.`); //item.zip.file(item.filename, image); await item.zipWriter.add(item.filename, new zip.BlobReader(image)); } /** * @param poolLimit 并发控制数 (>= 1) * @param array 参数数组 * @param iteratorFn 异步任务,返回 promise 或是 async 方法 * https://www.luanzhuxian.com/post/60c2c548.html */ function asyncPool(poolLimit, array, iteratorFn) { let i = 0 const ret = [] // Promise.all(ret) 的数组 const executing = [] const enqueue = function() { // array 遍历完,进入 Promise.all 流程 if (i === array.length) { return Promise.resolve() } // 每调用一次 enqueue,就初始化一个 promise,并放入 ret 队列 const item = array[i++] const p = Promise.resolve().then(() => iteratorFn(item, array)) ret.push(p) // 插入 executing 队列,即正在执行的 promise 队列,并且 promise 执行完毕后,会从 executing 队列中移除 const e = p.then(() => executing.splice(executing.indexOf(e), 1)) executing.push(e) // 每当 executing 数组中 promise 数量达到 poolLimit 时,就利用 Promise.race 控制并发数,完成的 promise 会从 executing 队列中移除,并触发 Promise.race 也就是 r 的回调,继续递归调用 enqueue,继续 加入新的 promise 任务至 executing 队列 let r = Promise.resolve() if (executing.length >= poolLimit) { r = Promise.race(executing) } // 递归,链式调用,直到遍历完 array return r.then(() => enqueue()) } return enqueue().then(() => Promise.all(ret)) } function saveBlob(content,name) { const fileUrl = window.URL.createObjectURL(content); const anchorElement = document.createElement('a'); anchorElement.href = fileUrl; anchorElement.download = name; anchorElement.style.display = 'none'; document.body.appendChild(anchorElement); anchorElement.click(); anchorElement.remove(); window.URL.revokeObjectURL(fileUrl); } function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } const xmlHttpRequest = (typeof(GM_xmlhttpRequest) === 'undefined') ? GM.xmlHttpRequest : GM_xmlhttpRequest; function getBlobFromUrl(url) { return new Promise((resolve, reject) => { xmlHttpRequest({ method: "GET", url: url, responseType: "blob", // 返回 blob 类型 headers: { "Cookie": document.cookie }, onload: function(response) { resolve(response.response); }, onerror: function(error) { reject(error); } }); }); }