Pixiv Downloader Userscript

Pixiv Downloader Userscript

Overview

This userscript enhances the Pixiv website by adding a floating Download button to artwork pages.
It allows users to quickly download all related resources of a Pixiv illustration with one click.

Main Features

  • Automatically detects Pixiv artwork pages (/artworks/{id})
  • Injects a fixed-position Download button on the page
  • Downloads the following files:
    • Original full-resolution images (all pages)
    • Thumbnail image
    • info.json metadata file
// ==UserScript==
// @name         Pixiv Downloader
// @namespace    pixiv-downloader
// @version      0.3
// @match        https://www.pixiv.net/*
// @grant        GM_download
// @run-at       document-idle
// ==/UserScript==

(function () {
  'use strict';

  let lastUrl = location.href;

  // URL 变化检测(Pixiv SPA 必备)
  setInterval(() => {
    if (location.href !== lastUrl) {
      lastUrl = location.href;
      onPageChange();
    }
  }, 500);

  // 初次执行
  onPageChange();

  function onPageChange() {
    if (!location.pathname.startsWith('/artworks/')) return;

    // 防重复注入
    if (document.getElementById('pixiv-download-btn')) return;

    waitForBody(injectButton);
  }

  function waitForBody(cb) {
    if (document.body) {
      cb();
      return;
    }
    const obs = new MutationObserver(() => {
      if (document.body) {
        obs.disconnect();
        cb();
      }
    });
    obs.observe(document.documentElement, { childList: true, subtree: true });
  }

  function injectButton() {
    const btn = document.createElement("button");
    btn.textContent = "Download";
    btn.id = "pixiv-download-btn";

    btn.style.cssText = `
      position: fixed;
      right: 20px;
      bottom: 20px;
      z-index: 99999;
      padding: 10px 14px;
      font-size: 14px;
      background: #0096fa;
      color: white;
      border: none;
      border-radius: 6px;
      cursor: pointer;
    `;

    btn.onclick = onClick;
    document.body.appendChild(btn);
  }

  async function onClick() {
    try {
      const id = location.pathname.match(/artworks\/(\d+)/)?.[1];
      if (!id) {
        alert("Not an artwork page");
        return;
      }

      const api = `https://www.pixiv.net/ajax/illust/${id}`;
      const res = await fetch(api, { credentials: "include" });
      const json = await res.json();
      const body = json.body;

      const base = `${id}/`;

      // info.json
      const info = {
        artworkId: body.id,
        title: body.title,
        description: body.description,
        tags: body.tags.tags.map(t => t.tag),
        author: {
          id: body.userId,
          name: body.userName
        },
        pageCount: body.pageCount,
        sourceUrl: location.href
      };

      downloadText(JSON.stringify(info, null, 2), base + "info.json");

      // thumbnail
      downloadFile(body.urls.small, base + "thumbnail.jpg");

      // originals
      for (let i = 0; i < body.pageCount; i++) {
        const url = body.urls.original.replace("_p0", `_p${i}`);
        const ext = url.includes(".png") ? ".png" : ".jpg";
        downloadFile(url, base + `original_${i}${ext}`);
      }
    } catch (e) {
      console.error(e);
      alert("Download failed, check console");
    }
  }

  function downloadText(text, filename) {
    const blob = new Blob([text], { type: "application/json" });
    const url = URL.createObjectURL(blob);
    GM_download({ url, name: filename });
  }

  function downloadFile(url, filename) {
    GM_download({
      url,
      name: filename,
      headers: { Referer: "https://www.pixiv.net/" }
    });
  }
})();

Requirements

  • Tampermonkey or compatible userscript manager
  • Logged-in Pixiv session