Poipiku Downloader

Download images or text from Poipiku

Versione datata 25/01/2022. Vedi la nuova versione l'ultima versione.

Dovrai installare un'estensione come Tampermonkey, Greasemonkey o Violentmonkey per installare questo script.

You will need to install an extension such as Tampermonkey to install this script.

Dovrai installare un'estensione come Tampermonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Userscripts per installare questo script.

Dovrai installare un'estensione come ad esempio Tampermonkey per installare questo script.

Dovrai installare un gestore di script utente per installare questo script.

(Ho già un gestore di script utente, lasciamelo installare!)

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

(Ho già un gestore di stile utente, lasciamelo installare!)

// ==UserScript==
// @name        Poipiku Downloader
// @name:zh-CN  Poipiku下载器
// @description Download images or text from Poipiku
// @description:zh-cn 从Poipiku下载图片或文字
// @author      calary
// @namespace   http://tampermonkey.net/
// @version     0.3.2
// @license     GPL-3.0
// @include     http*://poipiku.com*
// @match       https://poipiku.com/
// @connect     img.poipiku.com
// @connect     img-org.poipiku.com
// @icon        https://poipiku.com/favicon.ico
// @require     https://cdn.bootcdn.net/ajax/libs/jquery/2.2.4/jquery.min.js
// @require     https://cdn.bootcss.com/jszip/3.1.4/jszip.min.js
// @require     https://cdn.bootcss.com/FileSaver.js/1.3.2/FileSaver.min.js
// @grant       GM.xmlHttpRequest
// @grant       GM_xmlhttpRequest
// @run-at      document-end
// ==/UserScript==

jQuery(function ($) {
  const lang = (
    window.navigator.language ||
    window.navigator.browserLanguage ||
    "en-us"
  ).toLowerCase();

  const i18nMap = {
    "en-us": {
      ui_logined: "Logined",
      ui_password: "Password",
      ui_qualitytrue: "You can download high quality images.",
      ui_qualityfalse: "You cannot download high quality images.",
      ui_mode: "Rename image with page id",
      btn_downloadimages: "Save images (.zip)",
      btn_downloadimageseperately: "Save images Seperately",
      btn_downloadtext: "Save text (.txt)",
      error_default: "Something went wrong",
      error_fetch: "Fetch content error. Entered wrong password?",
      error_noimage: "No Images",
      txt_title: "Title: ",
      txt_author: "Author: ",
      txt_twitter: "Twitter: ",
      txt_link: "Link: ",
    },
    "zh-cn": {
      ui_logined: "登录状态",
      ui_password: "密码",
      ui_qualitytrue: "可以下载高质量图片。",
      ui_qualityfalse: "不能下载高质量图片。",
      ui_mode: "图片命名包含当页ID",
      btn_downloadimages: "图片打包为(.zip)",
      btn_downloadimageseperately: "独立保存图片",
      btn_downloadtext: "保存文字为(.txt)",
      error_default: "出错了",
      error_fetch: "请求失败。是否输入密码有误?",
      error_noimage: "没有图片",
      txt_title: "标题:",
      txt_author: "作者:",
      txt_twitter: "推特:",
      txt_link: "地址:",
    },
  };
  const i18n = (key) =>
    (i18nMap[lang] && i18nMap[lang][key]) || i18nMap["en-us"][key];

  const website = "poipiku";
  const url = window.location.href;
  const execResult = /\/(\d+)\/(\d+)/.exec(url);
  const authorId = execResult && execResult[1];
  const workId = execResult && execResult[2];
  const logined = $(".LoginButton").length === 0;
  const isText = $(".IllustItem").hasClass("Text");
  const hasPassword = $(".IllustItem").hasClass("Password");
  const fontFamily = "Arial, 'Microsoft Yahei', Helvetica, sans-serif";

  if (!workId) {
    return;
  }

  const $panel = $(`<div>
    <div>${i18n("ui_logined")}: <b style="color:red">${logined}</b>.</div>
    <div class="line-qualitytip" >${
      logined ? i18n("ui_qualitytrue") : i18n("ui_qualityfalse")
    }</div>
    <div class="line-password">${i18n(
      "ui_password"
    )} <input type='text' class="password"></div>
    <div class="line-mode" >${i18n(
      "ui_mode"
    )} <input type='checkbox' class="saveFileMode"></div>
    <div class="line-images">
      <button class="btn-downloadImagesSeperately" style="font-size:20px">${i18n(
        "btn_downloadimageseperately"
      )} <b class='status'></b></button></button><br>
      <button class="btn-downloadImages" style="font-size:20px">${i18n(
        "btn_downloadimages"
      )} <b class='status'></b></button>
    </div>
    <div class="line-text"><button class="btn-downloadText" style="font-size:20px">${i18n(
      "btn_downloadtext"
    )}</button></div>
  </div>`)
    .css({
      position: "fixed",
      left: 0,
      top: 50,
      zIndex: 999999,
      background: "#fff",
      color: "#333",
      fontSize: 18,
      fontFamily: fontFamily,
      padding: 10,
    })
    .appendTo($("body"));

  const $password = $panel.find(".password");
  const $saveFileMode = $panel.find(".saveFileMode");
  $panel.find("button").css({
    fontFamily: fontFamily,
  });

  if (!hasPassword) {
    $panel.find(".line-password").hide();
  }
  if (isText) {
    $panel.find(".line-images").hide();
    $panel.find(".line-qualitytip").hide();
    $panel.find(".line-mode").hide();
  } else {
    $panel.find(".line-text").hide();
  }
  $panel.find(".btn-downloadImages").on("click", downloadImagesAsZip);
  $panel
    .find(".btn-downloadImagesSeperately")
    .on("click", downloadImagesSeperately);
  $panel.find(".btn-downloadText").on("click", downloadText);

  function request(config) {
    return new Promise((resolve, reject) => {
      $.ajax({
        ...config,
        success: (response) => {
          resolve(response);
        },
        error: () => {
          reject(new Error(i18n("error_default")));
        },
      });
    });
  }

  function getBlob(url) {
    // return fetch(url).then((response) => response.blob());

    return new Promise((resolve, reject) => {
      GM.xmlHttpRequest({
        method: "GET",
        url: url,
        responseType: "blob",
        headers: { referer: window.location.href },
        onload: (payload) => {
          resolve(payload.response);
        },
        onerror: () => {
          reject(new Error(i18n("error_default")));
        },
      });
    });
  }

  // 过滤文件名非法字符
  function filterFilename(filename) {
    return filename.replace(/\?|\*|\:|\"|\<|\>|\\|\/|\|/g, "");
  }

  // 生成保存文件名
  function getSaveFilename() {
    const twitter = $(".UserInfoProgile a").html();
    const username = $(".UserInfoUserName a").html();
    const username2 = twitter ? twitter.substring(1) : username;
    const desc = $(".IllustItemDesc").text().substring(0, 20);

    return filterFilename(
      `[${username2}][${website}][${authorId}_${workId}]${desc}`
    );
  }

  // 生成保存图片文件名
  // 默认:序号.后缀名
  // 选中:网站_作品id_序号.后缀名
  function getSaveImageFilename(src, index) {
    let suffix = src.split(".").splice(-1);
    const mode = $saveFileMode.is(":checked");

    if (mode) {
      return `${website}_${workId}_${index + 1}.${suffix}`;
    }

    return `${index + 1}.${suffix}`;
  }

  // 批量下载图片的默认方法
  function saveImages(list, saveAsZip, $status) {
    let zip = new JSZip();
    let finishehCount = 0;
    let folder = zip.folder(getSaveFilename());

    $status = $status || $("<div></div>");
    $status.text(`0/${list.length}`);

    let promises = list.map((src, index) => {
      return getBlob(src).then((blob) => {
        finishehCount++;
        $status.text(`${finishehCount}/${list.length}`);

        if (saveAsZip) {
          folder.file(getSaveImageFilename(src, index), blob, { binary: true });
        } else {
          let suffix = src.split(".").splice(-1);
          saveAs(
            new Blob([blob]),
            `${getSaveFilename()}_${index + 1}.${suffix}`
          );

          console.log("save image", index);
        }
      });
    });

    Promise.all(promises).then(() => {
      if (saveAsZip) {
        zip
          .generateAsync({ type: "blob", base64: true })
          .then((content) => saveAs(content, getSaveFilename()));
      }
    });
  }

  // 保存文字的默认方法
  function saveText(option) {
    let str = "";

    if (option.title) {
      str += `${i18n("txt_title")}${option.title}\n`;
    }
    if (option.author) {
      str += `${i18n("txt_author")}${option.author}\n`;
    }
    if (option.twitter) {
      str += `${i18n("txt_twitter")}${option.twitter}\n`;
    }
    str += `${i18n("txt_link")}${window.location.href}\n`;
    str += `\n\n`;
    str += option.content;

    saveAs(
      new Blob([str], { type: "text/plain;charset=UTF-8" }),
      getSaveFilename() + ".txt"
    );
  }

  // 下载图片
  function downloadImages(saveAsZip, $status) {
    const promise = logined
      ? request({
          url: "/f/ShowIllustDetailF.jsp",
          type: "POST",
          data: {
            ID: authorId,
            TD: workId,
            AD: "-1",
            PAS: $password.val(),
          },
          dataType: "json",
        }).then((payload) => {
          if (!payload.html) {
            throw new Error(i18n("error_fetch"));
          }
          return payload.html;
        })
      : request({
          url: "/f/ShowAppendFileF.jsp",
          type: "POST",
          data: {
            UID: authorId,
            IID: workId,
            PAS: $password.val(),
            MD: 0,
            TWF: -1,
          },
          dataType: "json",
        }).then((payload) => {
          if (payload.result_num < 0) {
            throw new Error(payload.html);
          }
          return $(".IllustItemThumb").eq(0).prop("outerHTML") + payload.html;
        });

    promise
      .then((html) => {
        let $page = $(html);
        let list = [];

        $page
          .find(logined ? ".DetailIllustItemImage" : ".IllustItemThumbImg")
          .each(function () {
            const src = $(this).attr("src");

            if (src && !/warning\.png/.test(src)) {
              list.push(window.location.protocol + src);
            }
          });

        if (list.length) {
          saveImages(list, saveAsZip, $status);
        } else {
          throw new Error(i18n("error_noimage"));
        }
      })
      .catch((e) => {
        alert(e.message || i18n("error_default"));
      });
  }

  // 打包图片
  function downloadImagesAsZip() {
    downloadImages(true, $(this).find(".status"));
  }

  // 独立下载图片
  function downloadImagesSeperately() {
    downloadImages(false, $(this).find(".status"));
  }

  // 下载文字
  function downloadText() {
    request({
      url: "/f/ShowAppendFileF.jsp",
      type: "POST",
      data: {
        UID: authorId,
        IID: workId,
        PAS: $password.val(),
        MD: 0,
        TWF: -1,
      },
      dataType: "json",
    })
      .then((payload) => {
        if (payload.result_num < 0) {
          throw new Error(payload.html);
        }

        let $page = $(payload.html);

        saveText({
          title: $(".IllustItemDesc").text(),
          author: $(".UserInfoUserName a").html(),
          twitter: $(".UserInfoProgile a").prop("href"),
          content: $page.find(".NovelSection").text(),
        });
      })
      .catch((e) => {
        alert(e.message || i18n("error_default"));
      });
  }
});