您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Simple media downloader for pixiv.net
当前为
// ==UserScript== // @name Pixiv Media Downloader // @description Simple media downloader for pixiv.net // @version 0.2.2 // @icon https://pixiv.net/favicon.ico // @homepageURL https://github.com/mkm5/pixiv-media-downloader // @namespace https://github.com/mkm5 // @match https://www.pixiv.net/* // @run-at document-idle // @noframes // @require https://cdnjs.cloudflare.com/ajax/libs/jszip/3.6.0/jszip.min.js // @require https://gf.qytechs.cn/scripts/2963-gif-js/code/gifjs.js // @grant GM_xmlhttpRequest // ==/UserScript== const ARTWORK_URL = /https:\/\/www\.pixiv\.net\/([a-z]+\/)?artworks\/[0-9]+/ async function waitFor(f_condition) { return new Promise(resolve => { new MutationObserver((mutation, me) => { if (out = f_condition(mutation)) { resolve(out) me.disconnect() return } }).observe(document, { childList: true, subtree: true }) }) } function createButton(text, onclick) { const node = document.createElement("div") node.style.marginRight = "10px" const button = document.createElement("button") button.type = "button" button.onclick = onclick button.style.display = "inline-block" button.style.height = "32px" button.style.lineHeight = "32px" button.style.border = "none" button.style.background = "none" button.style.color = "inherit" button.style.fontWeight = "700" button.style.cursor = "pointer" const span = document.createElement("span") span.style.verticalAlign = "middle" span.appendChild(document.createTextNode(text)) button.appendChild(span) node.appendChild(button) return node } function saveFile(filename, data) { let link = document.createElement("a") link.href = URL.createObjectURL(data) link.download = filename link.click() URL.revokeObjectURL(link.href) link.remove() } async function requestImage(url) { return new Promise(resolve => { GM_xmlhttpRequest({ method: "GET", url: url, responseType: "blob", headers: { "Referer": "https://www.pixiv.net/" }, onload: (req) => { console.log(`[${req.statusText}:${req.status}] ${req.finalUrl}`) if (req.status == 200) { resolve(req.response) } } }) }) } async function loadImage(src) { return new Promise(resolve => { const img = new Image() img.onload = () => resolve(img) img.src = src }) } async function fetchImages(url_base, illustration_count) { const images = {} return new Promise(fetch_resolve => { const promises = [] for (let i = 0; i < illustration_count; i++) { const url = url_base.replace(/_p\d+/, `_p${i}`) promises.push(requestImage(url).then(data => { images[i] = data })) } Promise.allSettled(promises).then(() => { fetch_resolve(images) }) }) } history.pushState = (function (_super) { return function () { const funcResult = _super.apply(this, arguments) if (window.location.href.match(ARTWORK_URL)) scriptInit() return funcResult } })(history.pushState); (async function scriptInit() { if (!window.location.href.match(ARTWORK_URL)) return; if (typeof GIF === "undefined") { const gif_script = document.createElement("script") gif_script.src = "https://cdnjs.cloudflare.com/ajax/libs/gif.js/0.2.0/gif.js" document.head.appendChild(gif_script) } const image_id = document.URL.split("/").pop() const illust_data_response = await fetch("https://www.pixiv.net/ajax/illust/" + image_id) const illust_data = (await illust_data_response.json()).body console.log("Fetched data:", illust_data) const filename = `${illust_data.illustTitle},${illust_data.illustId}-[${illust_data.userName}]-(${illust_data.createDate.split("T")[0]})` const button_section = await waitFor(() => { let sections = document.querySelectorAll("section") if (sections.length >= 2 && sections[1].childElementCount >= 3 /* NOTE: 3 for guests, 4 for logged users */ ) { return sections[1] } }) if (illust_data.illustType == 0 || illust_data.illustType == 1) /* Picture & Manga */ { const url = illust_data.urls.original const extension = url.split(".").pop() if (illust_data.pageCount == 1) /* Single image mode */ { button_section.appendChild(createButton("Download original", async function () { requestImage(url).then(data => saveFile(filename + '.' + extension, data)) })) return; } button_section.appendChild(createButton("Download separately", async function () { const images = await fetchImages(url, illust_data.pageCount) Object.entries(images).forEach(([idx, data]) => { saveFile(filename + `.p${idx}` + "." + extension, data) }) })) button_section.appendChild(createButton("Download zip", async function () { const images = await fetchImages(url, illust_data.pageCount) const zip = new JSZip() for (const [idx, data] of Object.entries(images)) { zip.file(filename + `.p${idx}` + "." + extension, data, { binary: true }) } zip.generateAsync({ type: "blob" }).then(content => saveFile(filename + ".zip", content)) })) button_section.appendChild(createButton("Download continuous", async function () { const images = await fetchImages(url, illust_data.pageCount) const canvas = document.createElement("canvas") const context = canvas.getContext("2d") canvas.width = 0 canvas.height = 0 const fetched_images = await Promise.all( Object.values(images).map(data => { const object_url = URL.createObjectURL(data) return new Promise(resolve => { loadImage(object_url).then(image => { if (canvas.width < image.width) canvas.width = image.width canvas.height += image.height resolve(image) URL.revokeObjectURL(object_url) }) }) }) ) let current_position = 0; for (const image of fetched_images) { context.drawImage(image, Math.round((canvas.width - image.width) / 2), current_position) current_position += image.height } canvas.toBlob(blob => { saveFile(filename + "." + extension, blob) }) })) } else if (illust_data.illustType == 2) /* Ugoira */ { const ugoira_meta_response = await fetch("https://www.pixiv.net/ajax/illust/" + image_id + "/ugoira_meta") const ugoira_meta_data = (await ugoira_meta_response.json()).body button_section.appendChild(createButton("Download GIF", async function () { const zip_file_response = await fetch(ugoira_meta_data.originalSrc) const zip_blob = await zip_file_response.blob() new JSZip().loadAsync(zip_blob) .then(async zip => { const gif = new GIF({ workers: 6, quality: 10, workerScript: GIF_worker_URL }) gif.on("finished", blob => { saveFile(filename + ".gif", blob) }) await Promise.allSettled( ugoira_meta_data.frames.map(async frame => { const data = await zip.file(frame.file).async("blob") const url = URL.createObjectURL(data) const image = await loadImage(url) gif.addFrame(image, { delay: frame.delay }) URL.revokeObjectURL(url) }) ) gif.render() }) })) } })()
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址