Pixiv Downloader

一键下载Pixiv各页面原图。支持多图下载,动图下载,按作品标签下载,画师作品批量下载。动图支持格式转换:Gif | Apng | Webp | Webm。下载的图片将保存到以画师名命名的单独文件夹(需要调整tampermonkey“下载”设置为“浏览器API”)。保留已下载图片的记录。

À partir de 2023-05-07. Voir la dernière version.

Vous devrez installer une extension telle que Tampermonkey, Greasemonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Userscripts pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey pour installer ce script.

Vous devrez installer une extension de gestionnaire de script utilisateur pour installer ce script.

(J'ai déjà un gestionnaire de scripts utilisateur, laissez-moi l'installer !)

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

(J'ai déjà un gestionnaire de style utilisateur, laissez-moi l'installer!)

// ==UserScript==
// @name         Pixiv Downloader
// @namespace    https://greasyfork.org/zh-CN/scripts/432150
// @version      0.8.1
// @description:en  Download the original images of Pixiv pages with one click. Supports:multiple illustrations, ugoira(animation), and batch downloads of artists' work. Ugoira support format conversion: Gif | Apng | Webp | Webm. The downloaded images will be saved in a separate folder named after the artist (you need to adjust the tampermonkey "Download" setting to "Browser API"). A record of downloaded images is kept.
// @description  一键下载Pixiv各页面原图。支持多图下载,动图下载,按作品标签下载,画师作品批量下载。动图支持格式转换:Gif | Apng | Webp | Webm。下载的图片将保存到以画师名命名的单独文件夹(需要调整tampermonkey“下载”设置为“浏览器API”)。保留已下载图片的记录。
// @description:zh-TW  一鍵下載Pixiv各頁面原圖。支持多圖下載,動圖下載,按作品標籤下載,畫師作品批次下載。動圖支持格式轉換:Gif | Apng | Webp | Webm。下載的圖片將保存到以畫師名命名的單獨文件夾(需要調整tampermonkey“下載”設置為“瀏覽器API”)。保留已下載圖片的紀錄。
// @author       ruaruarua
// @match        https://www.pixiv.net/*
// @icon         https://www.pixiv.net/favicon.ico
// @noframes
// @grant        GM_xmlhttpRequest
// @grant        GM_download
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_info
// @grant        GM_registerMenuCommand
// @connect      i.pximg.net
// @require      https://cdnjs.cloudflare.com/ajax/libs/jszip/3.7.1/jszip.min.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/gif.js/0.2.0/gif.js
// @require      https://greasyfork.org/scripts/455256-toanimatedwebp/code/toAnimatedWebp.js?version=1120088
// ==/UserScript==
(function (JSZip, GIF, workerChunk) {
  'use strict';

  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }

  var JSZip__default = /*#__PURE__*/_interopDefaultLegacy(JSZip);
  var GIF__default = /*#__PURE__*/_interopDefaultLegacy(GIF);
  var workerChunk__default = /*#__PURE__*/_interopDefaultLegacy(workerChunk);

  var e=[],t=[];function n(n,r){if(n&&"undefined"!=typeof document){var a,s=!0===r.prepend?"prepend":"append",d=!0===r.singleTag,i="string"==typeof r.container?document.querySelector(r.container):document.getElementsByTagName("head")[0];if(d){var u=e.indexOf(i);-1===u&&(u=e.push(i)-1,t[u]={}),a=t[u]&&t[u][s]?t[u][s]:t[u][s]=c();}else a=c();65279===n.charCodeAt(0)&&(n=n.substring(1)),a.styleSheet?a.styleSheet.cssText+=n:a.appendChild(document.createTextNode(n));}function c(){var e=document.createElement("style");if(e.setAttribute("type","text/css"),r.attributes)for(var t=Object.keys(r.attributes),n=0;n<t.length;n++)e.setAttribute(t[n],r.attributes[t[n]]);var a="prepend"===s?"afterbegin":"beforeend";return i.insertAdjacentElement(a,e),e}}

  var css$3 = "@property --pdl-progress {\n  syntax: '<percentage>';\n  inherits: true;\n  initial-value: 0%;\n}\n@keyframes pdl_loading {\n  100% {\n    transform: translate(-50%, -50%) rotate(360deg);\n  }\n}\n.pdl-btn {\n  position: relative;\n  border-top-right-radius: 8px;\n  background: no-repeat center/85%;\n  background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E %3Cpath fill='%233C3C3C' d='M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zm0 448c-110.5 0-200-89.5-200-200S145.5 56 256 56s200 89.5 200 200-89.5 200-200 200zm-32-316v116h-67c-10.7 0-16 12.9-8.5 20.5l99 99c4.7 4.7 12.3 4.7 17 0l99-99c7.6-7.6 2.2-20.5-8.5-20.5h-67V140c0-6.6-5.4-12-12-12h-40c-6.6 0-12 5.4-12 12z'%3E%3C/path%3E %3C/svg%3E\");\n  color: #01b468;\n  display: inline-block;\n  font-size: 13px;\n  font-weight: bold;\n  height: 32px;\n  line-height: 32px;\n  margin: 0;\n  overflow: hidden;\n  padding: 0;\n  border: none;\n  text-decoration: none !important;\n  text-align: center;\n  text-overflow: ellipsis;\n  user-select: none;\n  white-space: nowrap;\n  width: 32px;\n  z-index: 1;\n  cursor: pointer;\n}\n.pdl-btn-main {\n  margin: 0 0 0 10px;\n}\n.pdl-btn-sub {\n  bottom: 0;\n  background-color: rgba(255, 255, 255, 0.5);\n  left: 0;\n  position: absolute;\n}\n.pdl-btn-sub.artworks {\n  position: sticky;\n  top: 40px;\n  border-radius: 4px;\n}\n.pdl-btn-sub.presentation {\n  position: fixed;\n  top: 50px;\n  right: 16px;\n  border-radius: 8px;\n  left: auto;\n}\n.pdl-btn-sub-bookmark.pdl-btn-sub-bookmark {\n  left: auto;\n  right: 0;\n  bottom: 34px;\n  border-radius: 8px;\n  border-top-right-radius: 0px;\n  border-bottom-right-radius: 0px;\n}\n.pdl-error {\n  background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E %3Cpath fill='%23EA0000' d='M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zm0 448c-110.5 0-200-89.5-200-200S145.5 56 256 56s200 89.5 200 200-89.5 200-200 200zm101.8-262.2L295.6 256l62.2 62.2c4.7 4.7 4.7 12.3 0 17l-22.6 22.6c-4.7 4.7-12.3 4.7-17 0L256 295.6l-62.2 62.2c-4.7 4.7-12.3 4.7-17 0l-22.6-22.6c-4.7-4.7-4.7-12.3 0-17l62.2-62.2-62.2-62.2c-4.7-4.7-4.7-12.3 0-17l22.6-22.6c4.7-4.7 12.3-4.7 17 0l62.2 62.2 62.2-62.2c4.7-4.7 12.3-4.7 17 0l22.6 22.6c4.7 4.7 4.7 12.3 0 17z'%3E%3C/path%3E %3C/svg%3E\") !important;\n}\n.pdl-complete {\n  background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E %3Cpath fill='%2301B468' d='M256 8C119.033 8 8 119.033 8 256s111.033 248 248 248 248-111.033 248-248S392.967 8 256 8zm0 48c110.532 0 200 89.451 200 200 0 110.532-89.451 200-200 200-110.532 0-200-89.451-200-200 0-110.532 89.451-200 200-200m140.204 130.267l-22.536-22.718c-4.667-4.705-12.265-4.736-16.97-.068L215.346 303.697l-59.792-60.277c-4.667-4.705-12.265-4.736-16.97-.069l-22.719 22.536c-4.705 4.667-4.736 12.265-.068 16.971l90.781 91.516c4.667 4.705 12.265 4.736 16.97.068l172.589-171.204c4.704-4.668 4.734-12.266.067-16.971z'%3E%3C/path%3E %3C/svg%3E\") !important;\n}\n.pdl-progress {\n  background-image: none !important;\n  cursor: default !important;\n}\n.pdl-progress:after {\n  content: '';\n  display: inline-block;\n  position: absolute;\n  top: 50%;\n  left: 50%;\n  width: 27px;\n  height: 27px;\n  transform: translate(-50%, -50%);\n  -webkit-mask: radial-gradient(transparent, transparent 54%, #000 57%, #000);\n  mask: radial-gradient(transparent, transparent 54%, #000 57%, #000);\n  border-radius: 50%;\n}\n.pdl-progress:not(:empty):after {\n  background: conic-gradient(\n    #01b468 0,\n    #01b468 var(--pdl-progress),\n    transparent var(--pdl-progress),\n    transparent\n  );\n  transition: --pdl-progress 0.2s ease;\n}\n.pdl-progress:empty:after {\n  background: conic-gradient(#01b468 0, #01b468 25%, #01b46833 25%, #01b46833);\n  animation: 1.5s infinite linear pdl_loading;\n}\n\n.pdl-btn.pdl-tag {\n  height: auto;\n  border-top-right-radius: 4px;\n  border-bottom-right-radius: 4px;\n  left: -1px;\n  background-color: rgba(0, 0, 0, 0.12);\n  transition: background-image 0.5s;\n}\n\n.pdl-btn.pdl-tag.pdl-tag-hide,\n.pdl-btn.pdl-modal-tag.pdl-tag-hide {\n  background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E  %3C/svg%3E\");\n  pointer-events: none;\n}\n\n.pdl-btn.pdl-modal-tag {\n  position: absolute;\n  right: 65px;\n  top: 6px;\n  background-origin: content-box;\n  border-radius: 4px;\n  padding: 5px;\n  width: 42px;\n  height: 50px;\n  background-color: rgba(0, 0, 0, 0.04);\n  transition: 0.25s background-color;\n}\n\n.pdl-btn.pdl-modal-tag:not(.pdl-tag-hide):hover {\n  background-color: rgba(0, 0, 0, 0.12);\n}\n\n.pdl-wrap-artworks {\n  position: absolute;\n  right: 8px;\n  top: 0px;\n  bottom: 0px;\n  margin-top: 40px;\n}\n";
  n(css$3,{});

  var css$2 = ".pdl-modal * {\n  font-family: 'win-bug-omega, system-ui, -apple-system, \"Segoe UI\", Roboto, Ubuntu, Cantarell, \"Noto Sans\", \"Hiragino Kaku Gothic ProN\", Meiryo, sans-serif';\n  line-height: 1.15;\n}\n\n.pdl-modal {\n  position: fixed;\n  top: 0;\n  left: 0;\n  width: 100%;\n  height: 100%;\n  display: flex;\n  z-index: 99;\n  background-color: rgba(0, 0, 0, 0.32);\n  user-select: none;\n}\n\n.pdl-dialog {\n  position: relative;\n  background-color: #fff;\n  border-radius: 24px;\n  margin: auto;\n  padding: 20px 40px 30px 40px;\n  width: 600px;\n  font-size: 16px;\n}\n\n.pdl-dialog-header > h3 {\n  font-weight: bold;\n  font-size: 1.17em;\n  margin: 1em 0;\n}\n\n.pdl-dialog-close {\n  position: absolute;\n  top: 10px;\n  right: 10px;\n  margin: 0;\n  padding: 0;\n  width: 25px;\n  height: 25px;\n  border: none;\n  cursor: pointer;\n  border-radius: 50%;\n  background-color: transparent;\n  transform: rotate(45deg);\n  transition: 0.25s background-color;\n  background: linear-gradient(rgb(125, 125, 125) 0%, rgb(125, 125, 125) 100%)\n      center/18px 2px no-repeat,\n    linear-gradient(rgb(125, 125, 125) 0%, rgb(125, 125, 125) 100%) center/2px\n      18px no-repeat;\n}\n\n.pdl-dialog-close:hover {\n  background-color: rgba(0, 0, 0, 0.05);\n}\n\n.pdl-dialog-content hr {\n  height: 0 !important;\n  margin: 0;\n  border: none;\n  border-top: 1px solid rgba(0, 0, 0, 0.1);\n}\n\n/* button */\n.pdl-dialog-button {\n  font-size: 16px;\n  background-color: #fff;\n  border: 1px solid rgb(125, 125, 125);\n  border-radius: 5px;\n  padding: 0.5em 1.5em;\n  cursor: pointer;\n  transition: 0.2s opacity;\n  line-height: 1.15;\n}\n.pdl-dialog-button:hover {\n  opacity: 0.7;\n}\n\n.pdl-dialog-button.primary {\n  color: #fff;\n  background-color: #0096fa;\n  border-color: #0096fa;\n}\n\n.pdl-dialog-button.icon {\n  padding: 0.5em 0.8em;\n}\n\n.pdl-dialog-button[disabled] {\n  cursor: not-allowed !important;\n  color: #c0c4cc;\n  background-color: #fff;\n  border-color: #e4e7ed;\n  opacity: 1 !important;\n}\n\n.pdl-dialog-button[disabled]:hover {\n  opacity: 1 !important;\n}\n\n.pdl-dialog-button.primary[disabled] {\n  color: #fff;\n  background-color: #a0cfff;\n  border-color: #a0cfff;\n}\n\n/* tabs */\n.pdl-tabs-nav {\n  display: flex;\n  align-items: center;\n  border-bottom: 1px solid #dcdfe6;\n  position: relative;\n}\n\n.pdl-tabs-nav .pdl-tab-item {\n  padding: 0px 16px;\n  line-height: 2.5;\n  cursor: pointer;\n  transition: 0.2s color;\n}\n\n.pdl-tabs-nav .pdl-tab-item:nth-child(2) {\n  padding-left: 0px;\n}\n\n.pdl-tabs-nav .pdl-tab-item:last-child {\n  padding-right: 0px;\n}\n\n.pdl-tab-item:hover,\n.pdl-tab-item.active {\n  color: #0096fa;\n}\n\n.pdl-tab-item.active {\n  font-weight: 700;\n}\n\n.pdl-tabs-content {\n  padding: 16px;\n  min-height: 200px;\n}\n\n.pdl-tabs-nav .pdl-tabs__active-bar {\n  position: absolute;\n  bottom: 0;\n  left: 0;\n  height: 2px;\n  background-color: #0096fa;\n  z-index: 1;\n  transition: width 0.2s, transform 0.2s;\n}\n\n/* filename */\n\n#pdl-setting-filename a {\n  color: #0096fa;\n  text-decoration: underline;\n}\n#pdl-setting-filename .tags-option label,\n#pdl-setting-filename .tags-option input {\n  cursor: pointer;\n}\n\n#pdl-setting-filename .tags-content {\n  flex: 1;\n  display: flex;\n  gap: 20px;\n}\n\n#pdl-setting-filename .pdl-input-wrap,\n#pdl-setting-filename .tags-option {\n  display: flex;\n  align-items: center;\n  margin: 12px 0;\n  gap: 12px;\n}\n\n#pdl-setting-filename .pdl-input-wrap label,\n#pdl-setting-filename .tags-option .tags-title {\n  cursor: default;\n  font-weight: 700;\n  width: 6em;\n}\n\n#pdl-setting-filename .pdl-input-wrap input[type='text'] {\n  height: auto;\n  padding: 0.5em;\n  font-size: 16px;\n  line-height: 1.5;\n  flex: 1;\n  border: 1px solid #333;\n}\n\n#pdl-setting-filename .pdl-input-wrap input[type='text']:focus {\n  background-color: #fff !important;\n}\n\n#pdl-setting-filename .pdl-input-wrap button {\n  line-height: 1.5;\n}\n\n/* ugoria */\n#pdl-setting-ugoria p.option-header {\n  font-weight: 700;\n}\n\n#pdl-setting-ugoria #pdl-ugoria-format-wrap {\n  display: flex;\n  justify-content: space-between;\n  flex-wrap: nowrap;\n  margin: 1.5em 1em;\n}\n\n#pdl-ugoria-format-wrap .pdl-ugoria-format-item,\n#pdl-ugoria-format-wrap .pdl-ugoria-format-item label,\n#pdl-ugoria-format-wrap .pdl-ugoria-format-item input {\n  cursor: pointer;\n}\n\n#pdl-ugoria-format-wrap .pdl-ugoria-format-item label {\n  padding-left: 4px;\n}\n\n/* history */\n#pdl-setting-history div {\n  text-align: center;\n  margin: 1em 0;\n}\n\n#pdl-setting-history .btn-history {\n  width: 80%;\n}\n\n/* others setting */\n#pdl-setting-others .pdl-options {\n  display: flex;\n  align-items: center;\n  gap: 20px;\n  cursor: pointer;\n  transition: 0.2s background-color;\n  padding: 1em 0.5em;\n  border-radius: 4px;\n}\n\n#pdl-setting-others .pdl-options:hover {\n  background-color: rgba(0, 0, 0, 0.05);\n}\n\n/* donate */\n#pdl-setting-donate {\n  text-align: center;\n}\n\n#pdl-setting-donate p {\n  margin: 0.5em 0;\n}\n\n/* upgrade msg */\n.pdl-changelog h4 {\n  margin: 1em 0;\n  font-weight: bold !important;\n}\n\n.pdl-changelog ul {\n  padding-inline-start: 40px !important;\n}\n\n.pdl-changelog li {\n  line-height: 2;\n  list-style-type: disc !important;\n}\n";
  n(css$2,{});

  var css$1 = ".pdl-nav-placeholder {\n  flex-grow: 1;\n  height: 42px;\n  line-height: 42px;\n  text-align: right;\n  font-weight: bold;\n  font-size: 16px;\n  color: rgb(133, 133, 133);\n  border-top: 4px solid transparent;\n  cursor: default;\n  white-space: nowrap;\n}\n\n.pdl-btn-all.pdl-btn-all,\n.pdl-stop.pdl-stop {\n  background-color: transparent;\n  border: none;\n  padding: 0 10px;\n}\n\n.pdl-btn-all.pdl-btn-all:hover,\n.pdl-stop.pdl-stop:hover {\n  color: rgb(31, 31, 31);\n}\n\n.pdl-btn-all::before,\n.pdl-stop::before {\n  content: '';\n  height: 24px;\n  width: 24px;\n  transition: background-image 0.2s ease 0s;\n  background: no-repeat center/85%;\n}\n\n.pdl-btn-all::before {\n  background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E %3Cpath fill='%23858585' d='M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zm0 448c-110.5 0-200-89.5-200-200S145.5 56 256 56s200 89.5 200 200-89.5 200-200 200zm-32-316v116h-67c-10.7 0-16 12.9-8.5 20.5l99 99c4.7 4.7 12.3 4.7 17 0l99-99c7.6-7.6 2.2-20.5-8.5-20.5h-67V140c0-6.6-5.4-12-12-12h-40c-6.6 0-12 5.4-12 12z'%3E%3C/path%3E %3C/svg%3E\");\n}\n\n.pdl-stop::before {\n  background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E %3Cpath fill='%23858585' d='M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zm0 448c-110.5 0-200-89.5-200-200S145.5 56 256 56s200 89.5 200 200-89.5 200-200 200zm101.8-262.2L295.6 256l62.2 62.2c4.7 4.7 4.7 12.3 0 17l-22.6 22.6c-4.7 4.7-12.3 4.7-17 0L256 295.6l-62.2 62.2c-4.7 4.7-12.3 4.7-17 0l-22.6-22.6c-4.7-4.7-4.7-12.3 0-17l62.2-62.2-62.2-62.2c-4.7-4.7-4.7-12.3 0-17l22.6-22.6c4.7-4.7 12.3-4.7 17 0l62.2 62.2 62.2-62.2c4.7-4.7 12.3-4.7 17 0l22.6 22.6c4.7 4.7 4.7 12.3 0 17z'%3E%3C/path%3E %3C/svg%3E\");\n}\n\n.pdl-btn-all:hover::before {\n  background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E %3Cpath fill='%231F1F1F' d='M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zm0 448c-110.5 0-200-89.5-200-200S145.5 56 256 56s200 89.5 200 200-89.5 200-200 200zm-32-316v116h-67c-10.7 0-16 12.9-8.5 20.5l99 99c4.7 4.7 12.3 4.7 17 0l99-99c7.6-7.6 2.2-20.5-8.5-20.5h-67V140c0-6.6-5.4-12-12-12h-40c-6.6 0-12 5.4-12 12z'%3E%3C/path%3E %3C/svg%3E\");\n}\n\n.pdl-stop:hover::before {\n  background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E %3Cpath fill='%231F1F1F' d='M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zm0 448c-110.5 0-200-89.5-200-200S145.5 56 256 56s200 89.5 200 200-89.5 200-200 200zm101.8-262.2L295.6 256l62.2 62.2c4.7 4.7 4.7 12.3 0 17l-22.6 22.6c-4.7 4.7-12.3 4.7-17 0L256 295.6l-62.2 62.2c-4.7 4.7-12.3 4.7-17 0l-22.6-22.6c-4.7-4.7-4.7-12.3 0-17l62.2-62.2-62.2-62.2c-4.7-4.7-4.7-12.3 0-17l22.6-22.6c4.7-4.7 12.3-4.7 17 0l62.2 62.2 62.2-62.2c4.7-4.7 12.3-4.7 17 0l22.6 22.6c4.7 4.7 4.7 12.3 0 17z'%3E%3C/path%3E %3C/svg%3E\");\n}\n\n.pdl-hide {\n  display: none !important;\n}\n\n.pdl-wrap {\n  display: flex;\n  justify-content: flex-end;\n  gap: 12px;\n  font-weight: bold;\n  font-size: 14px;\n  line-height: 14px;\n  color: rgb(133, 133, 133);\n  transition: color 0.2s ease 0s;\n}\n\n.pdl-wrap.unavailable {\n  pointer-events: none !important;\n  opacity: 0.5 !important;\n}\n\n.pdl-wrap .pdl-filter:hover {\n  color: rgb(31, 31, 31);\n}\n\n.pdl-wrap label {\n  padding-left: 8px;\n  cursor: pointer;\n}\n\n.pdl-checkbox.pdl-checkbox {\n  vertical-align: top;\n  appearance: none;\n  position: relative;\n  box-sizing: border-box;\n  width: 28px;\n  border: 2px solid transparent;\n  cursor: pointer;\n  border-radius: 14px;\n  height: 14px;\n  background-color: rgba(133, 133, 133);\n  transition: background-color 0.2s ease 0s, box-shadow 0.2s ease 0s;\n}\n\n.pdl-checkbox:hover {\n  background-color: rgba(31, 31, 31);\n}\n\n.pdl-checkbox::after {\n  content: '';\n  position: absolute;\n  display: block;\n  top: 0px;\n  left: 0px;\n  width: 10px;\n  height: 10px;\n  transform: translateX(0px);\n  background-color: rgb(255, 255, 255);\n  border-radius: 10px;\n  transition: transform 0.2s ease 0s;\n}\n\n.pdl-checkbox:checked {\n  background-color: rgb(0, 150, 250);\n}\n\n.pdl-checkbox:checked::after {\n  transform: translateX(14px);\n}\n";
  n(css$1,{});

  var css = "[data-theme='dark'] .pdl-btn-all::before {\n  background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E %3Cpath fill='%23858585' d='M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zm0 448c-110.5 0-200-89.5-200-200S145.5 56 256 56s200 89.5 200 200-89.5 200-200 200zm-32-316v116h-67c-10.7 0-16 12.9-8.5 20.5l99 99c4.7 4.7 12.3 4.7 17 0l99-99c7.6-7.6 2.2-20.5-8.5-20.5h-67V140c0-6.6-5.4-12-12-12h-40c-6.6 0-12 5.4-12 12z'%3E%3C/path%3E %3C/svg%3E\");\n}\n\n[data-theme='dark'] .pdl-btn-main,\n[data-theme='dark'] .pdl-btn-all:hover::before {\n  background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E %3Cpath fill='%23D6D6D6' d='M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zm0 448c-110.5 0-200-89.5-200-200S145.5 56 256 56s200 89.5 200 200-89.5 200-200 200zm-32-316v116h-67c-10.7 0-16 12.9-8.5 20.5l99 99c4.7 4.7 12.3 4.7 17 0l99-99c7.6-7.6 2.2-20.5-8.5-20.5h-67V140c0-6.6-5.4-12-12-12h-40c-6.6 0-12 5.4-12 12z'%3E%3C/path%3E %3C/svg%3E\");\n}\n\n[data-theme='dark'] .pdl-stop::before {\n  background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E %3Cpath fill='%23858585' d='M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zm0 448c-110.5 0-200-89.5-200-200S145.5 56 256 56s200 89.5 200 200-89.5 200-200 200zm101.8-262.2L295.6 256l62.2 62.2c4.7 4.7 4.7 12.3 0 17l-22.6 22.6c-4.7 4.7-12.3 4.7-17 0L256 295.6l-62.2 62.2c-4.7 4.7-12.3 4.7-17 0l-22.6-22.6c-4.7-4.7-4.7-12.3 0-17l62.2-62.2-62.2-62.2c-4.7-4.7-4.7-12.3 0-17l22.6-22.6c4.7-4.7 12.3-4.7 17 0l62.2 62.2 62.2-62.2c4.7-4.7 12.3-4.7 17 0l22.6 22.6c4.7 4.7 4.7 12.3 0 17z'%3E%3C/path%3E %3C/svg%3E\");\n}\n\n[data-theme='dark'] .pdl-stop:hover::before {\n  background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E %3Cpath fill='%23D6D6D6' d='M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zm0 448c-110.5 0-200-89.5-200-200S145.5 56 256 56s200 89.5 200 200-89.5 200-200 200zm101.8-262.2L295.6 256l62.2 62.2c4.7 4.7 4.7 12.3 0 17l-22.6 22.6c-4.7 4.7-12.3 4.7-17 0L256 295.6l-62.2 62.2c-4.7 4.7-12.3 4.7-17 0l-22.6-22.6c-4.7-4.7-4.7-12.3 0-17l62.2-62.2-62.2-62.2c-4.7-4.7-4.7-12.3 0-17l22.6-22.6c4.7-4.7 12.3-4.7 17 0l62.2 62.2 62.2-62.2c4.7-4.7 12.3-4.7 17 0l22.6 22.6c4.7 4.7 4.7 12.3 0 17z'%3E%3C/path%3E %3C/svg%3E\");\n}\n\n[data-theme='dark'] .pdl-checkbox:not(:checked):hover {\n  background-color: rgba(155, 155, 155);\n}\n\n[data-theme='dark'] .pdl-btn.pdl-tag {\n  background-color: rgba(255, 255, 255, 0.4);\n}\n\n[data-theme='dark'] .pdl-btn.pdl-modal-tag {\n  background-color: rgba(255, 255, 255, 0.4);\n}\n\n[data-theme='dark'] .pdl-btn.pdl-modal-tag:hover {\n  background-color: rgba(255, 255, 255, 0.6);\n}\n\n[data-theme='dark'] .pdl-wrap .pdl-filter:hover,\n[data-theme='dark'] .pdl-stop.pdl-stop:hover,\n[data-theme='dark'] .pdl-btn-all.pdl-btn-all:hover {\n  color: rgb(214, 214, 214);\n}\n\n/* modal */\n[data-theme='dark'] .pdl-dialog {\n  background-color: rgb(31, 31, 31);\n}\n\n[data-theme='dark'] .pdl-dialog-footer button {\n  background-color: rgb(245, 245, 245);\n}\n\n[data-theme='dark'] .pdl-dialog-content hr {\n  border-top: 1px solid rgba(255, 255, 255, 0.3);\n}\n\n/* others setting */\n[data-theme='dark'] #pdl-setting-others .pdl-options:hover {\n  background-color: rgba(255, 255, 255, 0.1);\n}\n";
  n(css,{});

  function debugLog(...msgs) {
  }

  var IllustType;
  (function (IllustType) {
      IllustType[IllustType["illusts"] = 0] = "illusts";
      IllustType[IllustType["manga"] = 1] = "manga";
      IllustType[IllustType["ugoira"] = 2] = "ugoira";
  })(IllustType || (IllustType = {}));

  const defaultSettings = Object.freeze({
      version: '0.8.1',
      ugoriaFormat: 'zip',
      folderPattern: 'pixiv/{artist}',
      filenamePattern: '{artist}_{title}_{id}_p{page}',
      tagLang: 'ja',
      showMsg: true,
      log: false,
      filter: {
          exclude_downloaded: false,
          [IllustType.illusts]: true,
          [IllustType.manga]: true,
          [IllustType.ugoira]: true
      },
      bundleIllusts: false,
      bundleManga: false
  });
  const regexp = {
      preloadData: /"meta-preload-data" content='(.*)'>/,
      artworksPage: /artworks\/(\d+)$/,
      userPage: /users\/(\d+)/,
      bookmarkPage: /users\/\d+\/bookmarks\/artworks/,
      userPageTags: /users\/\d+\/(artworks|illustrations|manga|bookmarks(?!artworks))/,
      ppSearchPage: /\/tags\/.*\/(artworks|illustrations|manga)/,
      suscribePage: /bookmark_new_illust/,
      activityHref: /illust_id=(\d+)/,
      originSrcPageNum: /(?<=_p)\d+/
  };
  const depsUrls = {
      gifWorker: 'https://cdnjs.cloudflare.com/ajax/libs/gif.js/0.2.0/gif.worker.js',
      pako: 'https://cdnjs.cloudflare.com/ajax/libs/pako/2.0.4/pako.min.js',
      upng: 'https://cdnjs.cloudflare.com/ajax/libs/upng-js/2.1.0/UPNG.min.js'
  };
  const creditCode = `<img style="display: block; margin: 1em auto; width: 200px"
src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAJYAAACWCAYAAAA8AXHiAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAACxMAAAsTAQCanBgAADzWSURBVHhe7Z0HnBRF9sdrMyxZoiCegKCCOZ3xzKcCgp56CoqKohj+6pmznBGMGM+cQDALBgwneuZ4nieGU5KKIoqAIrCEDf1/3zddQ29vd09P2oD++BQzW93TXV316uWqLvhZUCowv8P8+OOP5uOPPzY9evQwvXr1MoWFhe6RYDiOY2bPnm2+/vprs+WWW5p27dq5R37bWCEwNTU1K6WDfvNYvny5M2LECKd58+bOjjvu6Hz77bfukXB88803zuabb+60aNHCueqqq5xVq1a5R+JB+t79tmZBnms5U7IqQWdrHhYvXmymTZvm/hWNlStXmpkzZxohMOVCcK9UKCkpMT/88INZtmyZqa6uNgUFBe6RaHzxxRdm6dKl7l9rJKqieX0Txi+//GKGDh1qhKOYs846y1RVRc+fsrIy0759eyWOyspK2Ll7JBxrr722ef31182ECRPMyJEjTXFxsXskHOeee67p27ev+b//+79Y92iqWGMJCy4yb9481YNeeOGFlBwINbNbt26qV8G94g567969lYDXWmsttyYatIM2iWqLGuLWrnlYYwmrdevWyoHATz/9ZL777jv9HgYIqmPHjqaoqEg5FmIUAsg1xo4dax588EFz/fXXm/Lycrd2zUOTIiy4CDqNKMluTThatmxpNt54YxVPiMXvv/8+JaF07dpVCYvrw1nywVHatm1rDjvsMLU64+hk6G60Jc4zNyY0GcJCPI0ePdoMGjTIiAWmXCUKDNr+++9vNthgA7PeeuupmEs1kB06dFDCgqDgcg0tqrj/zTffbAYPHmzGjRuXUk9sVJDGL5OZ3OghHMfZZpttYDmO6DXOl19+6R4JhwyEI7PdmTNnjn5PhX/9619Oq1at9B4nn3yyuiDiQvrR/ZY7VFRUOMJFtT177723s2TJEvdI44b0xZImw7FQjvv166cc5auvvjJTp05VMREFzu3UqZPp3r27fk8FxJQ9LxXH4t633Xab2XrrrVVfSsVBM0GzZs3MlVdeaf70pz+Z4cOHm+bNm7tHmgCaCscC77//vtOmTRudwZtuuqkjg+8eiQYz/7PPPnNk8N2aYMyePdvp3LmzXn+PPfZwfv31V/dIXcyfP9/Zbrvt9Ny+ffsqV4xC3DYEAU4lhOz+1fjRpDiWtNdstdVWZt9999W/P/30UzNlyhT9ngrXXHON2XHHHdWHFAUh2mRYZtGiRZEcCwcnliOAy6XyYV133XXahlGjRrk18dGiRYuU4aVGh8bAscTaiz0jZ8yYoSEUabrqWmLxuUfCIaJEzxcCcObNm+fW1gXtsHrcH/7wh0iO+MUXXzg9evTQc//4xz86QojukbrgulZXEmPCrQ0HoaFrr73WkYngiBUcW3+jD9PRC/OFRsGx3nvvPXPUUUeZ++67L5aesv7665tjjjlGvwuRmXvuuUe/R0EGXj/Ri15++WX9HgS87zvttJN+h3NFcSFcHzhhgYjPSP1HjAJ1kwARsfoZhaefflq565gxY8xTTz0Vyy1BKOqGG25QVwb6Z4OjoTnWgQce6EjHafD3zTffdGuj8c477ziiaMfiFkBEpiNEo+eLEhzKHeEMIt6cCy+8UC3EKC767rvvOq1bt9ZrCqG7tcE47rjj9DzKs88+69YGg/tvttlmei5t5j5xMHPmTGejjTbSvtxnn30y0uVyhUbBsbB40FGYccOGDdMwTCrgm8IaYyb/73//M//+97/dI8HgfOl09XSjR4VxAOrx2F922WVm1113jeQUePJFIdfv1sMfBvxjOGy32GILs+2227q1wXj44YfNl19+qd+HDBlixEjR76lAG0jd6dmzp+qhcazgvKKhORbWkrDv5Iw++OCDI60xi0cffVR1LazEl156ya0NBj6s//znP8ot0tFZoiDErHqTEIxytygsXLjQefHFF9UqTKVL3nXXXcq9hRidV155Ja22LliwQP176HQNCThWAYQlM7NBg1b//e9/zX777adcoFWrVkaIxuy99956LIxr4IV+5plnVNfBw17fPh70tY8++kitVThFrjgEz/PEE08ohxswYECsjInGBumTpQ3OsZiRzOKLLrrIKSkpUa5Fot3PP//snpFbYOkxo9PhBL8jPcCxGlzHgiPhozn99NONKPJah2ed2GBcEGT+8MMPk7E0eTb99IPcKeJuf//732OnxdQnPvnkkzUnAbChOZYX0qnO3/72N+eBBx5QX04criImv3PEEUeolYjfB+4X9jtSj+WRndLSUmfatGlubV288MILzvHHH+98+umnbk1d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"
/>`;

  const handleWorker = `
let webpApi = {};
Module.onRuntimeInitialized = () => {
  webpApi = {
    init: Module.cwrap('init', '', ['number', 'number', 'number']),
    createBuffer: Module.cwrap('createBuffer', 'number', ['number']),
    addFrame: Module.cwrap('addFrame', 'number', ['number', 'number', 'number']),
    generate: Module.cwrap('generate', 'number', []),
    freeResult: Module.cwrap('freeResult', '', []),
    getResultPointer: Module.cwrap('getResultPointer', 'number', []),
    getResultSize: Module.cwrap('getResultSize', 'number', []),
  };

  postMessage('ok');
};

onmessage = (evt) => {
  const { dataURLs, delays, lossless = 1, quality = 75, method = 4} = evt.data;
  
  webpApi.init(lossless, quality, method);
  dataURLs.forEach((dataURL, idx) => {
    const base64 = dataURL.split(',')[1];
    const binStr = atob(base64);
    const u8a = new Uint8Array(binStr.length);
    let p = binStr.length;
    while (p) {
      p--;
      u8a[p] = binStr.codePointAt(p);
    }

    const pointer = webpApi.createBuffer(u8a.length);
    Module.HEAPU8.set(u8a, pointer);
    webpApi.addFrame(pointer, u8a.length, delays[idx]);
    postMessage(idx);
  });

  webpApi.generate();
  const resultPointer = webpApi.getResultPointer();
  const resultSize = webpApi.getResultSize();
  const result = new Uint8Array(Module.HEAP8.buffer, resultPointer, resultSize);
  postMessage(result);
  webpApi.freeResult();
};`;

  function initialDeps(urls) {
      return Promise.all([
          _getGifWS(urls.gifWorker),
          _getApngWS(urls.pako, urls.upng),
          _getWebpWS()
      ]).then(([gif, apng, webp]) => {
          this._deps.gif = URL.createObjectURL(new Blob([gif], { type: 'text/javascript' }));
          this._deps.apng = URL.createObjectURL(new Blob([apng], { type: 'text/javascript' }));
          this._deps.webp = URL.createObjectURL(new Blob([webp], { type: 'text/javascript' }));
          return this;
      });
  }
  function _fetchDeps(url) {
      return fetch(url)
          .then((res) => {
          if (res.ok)
              return res.text();
          throw new Error(res.status + res.statusText);
      })
          .catch((err) => {
          console.log('[Pixiv Downloader]Fetch dependency failed.', url, err);
          return '';
      });
  }
  async function _getGifWS(url) {
      let gifWS;
      if (!(gifWS = await GM_getValue('gifWS'))) {
          gifWS = await _fetchDeps(url);
          if (!gifWS)
              throw new Error('[Pixiv Downloader]Can not fetch gif worker script.');
          GM_setValue('gifWS', gifWS);
      }
      return gifWS;
  }
  async function _getApngWS(pakoUrl, upngUrl) {
      let apngWS;
      if (!(apngWS = await GM_getValue('apngWS'))) {
          const pako = _fetchDeps(pakoUrl);
          const upng = _fetchDeps(upngUrl);
          const pakoScript = await pako;
          let upngScript = await upng;
          if (!pakoScript || !upngScript)
              throw new Error('[Pixiv Downloader]Can not fetch apng script.');
          upngScript = upngScript
              .replace('window.UPNG', 'UPNG')
              .replace('window.pako', 'pako');
          const workerEvt = `onmessage = (evt) => {
      const {data, width, height, delay } = evt.data;
      const png = UPNG.encode(data, width, height, 0, delay, {loop: 0});
      if (!png) console.log('Convert Apng failed.');
      postMessage(png);
    };`;
          apngWS = workerEvt + pako + upng;
          GM_setValue('apngWS', apngWS);
      }
      return apngWS;
  }
  function _getWebpWS() {
      return workerChunk__default["default"] + handleWorker;
  }
  function _createImgElements(zip) {
      const eles = [];
      zip.forEach((relativePath, file) => {
          eles.push(new Promise((resolve) => {
              const image = new Image();
              image.onload = () => {
                  resolve(image);
              };
              file
                  .async('blob')
                  .then((blob) => void (image.src = URL.createObjectURL(blob)));
          }));
      });
      return Promise.all(eles);
  }
  function createInstance() {
      const zip = new JSZip__default["default"]();
      const freeApngWorkers = [];
      const freeWebpWorkers = [];
      const MAX_CONVERT = 2;
      let queue = [];
      let active = [];
      let isStop = false;
      const convertTo = {
          webp: (frames, convertMeta) => {
              return new Promise((resolve, reject) => {
                  let worker;
                  let reuse = false;
                  if (freeWebpWorkers.length) {
                      worker = freeWebpWorkers.shift();
                      reuse = true;
                  }
                  else {
                      worker = new Worker(createConverter._deps.webp);
                  }
                  convertMeta.abort = convertMeta._baseAbort.bind(convertMeta, () => {
                      reject('[Info]Convert stop manually, reject when convert webp. ' +
                          convertMeta.id);
                      worker.terminate();
                  });
                  const workerLoad = new Promise((resolve) => {
                      if (reuse)
                          return resolve();
                      worker.onmessage = (evt) => {
                          if (evt.data === 'ok') {
                              resolve();
                          }
                      };
                  });
                  let dataURLs = [];
                  const canvas = document.createElement('canvas');
                  const width = (canvas.width = frames[0].naturalWidth);
                  const height = (canvas.height = frames[0].naturalHeight);
                  const context = canvas.getContext('2d', { willReadFrequently: true });
                  if (!context)
                      throw '[Error]Can not get canvas context';
                  const delays = convertMeta.framesInfo.map((frameInfo) => {
                      return Number(frameInfo.delay);
                  });
                  dataURLs = frames.map((frame, idx) => {
                      if (convertMeta.isAborted)
                          throw ('[Info]Convert stop manually when converting image to webp. ' +
                              convertMeta.id);
                      context.clearRect(0, 0, width, height);
                      context.drawImage(frame, 0, 0, width, height);
                      const dataURL = canvas.toDataURL('image/webp', 1);
                      if (typeof convertMeta.onProgress === 'function') {
                          debugLog('[Info]Webp convert phrase 1:', convertMeta.id);
                          convertMeta.onProgress((idx / frames.length) * 0.5, 'webp');
                      }
                      return dataURL;
                  });
                  workerLoad.then(() => {
                      worker.onmessage = (evt) => {
                          if (typeof evt.data !== 'object') {
                              if (typeof convertMeta.onProgress === 'function') {
                                  debugLog('[Info]Webp convert phrase 2:', convertMeta.id, evt.data);
                                  convertMeta.onProgress(0.5 + (evt.data / frames.length) * 0.5, 'webp');
                              }
                          }
                          else {
                              freeWebpWorkers.push(worker);
                              resolve(new Blob([evt.data], { type: 'image/webp' }));
                          }
                      };
                      worker.postMessage({ dataURLs, delays });
                  });
              });
          },
          gif: (frames, convertMeta) => {
              return new Promise((resolve, reject) => {
                  const gif = new GIF__default["default"]({
                      workers: 2,
                      quality: 10,
                      workerScript: createConverter._deps.gif
                  });
                  convertMeta.abort = convertMeta._baseAbort.bind(null, gif.abort.bind(gif));
                  debugLog('[Info]Start convert:', convertMeta.id);
                  frames.forEach((frame, i) => {
                      gif.addFrame(frame, { delay: convertMeta.framesInfo[i].delay });
                  });
                  gif.on('progress', (progress) => {
                      debugLog('[Info]Convert progress:', convertMeta.id);
                      if (typeof convertMeta.onProgress === 'function')
                          convertMeta.onProgress(progress, 'gif');
                  });
                  gif.on('finished', (gifBlob) => {
                      resolve(gifBlob);
                  });
                  gif.on('abort', () => {
                      reject('[Info]Convert stop: abort. ' + convertMeta.id);
                  });
                  gif.render();
              });
          },
          png: (frames, convertMeta) => {
              return new Promise((resolve, reject) => {
                  const canvas = document.createElement('canvas');
                  const width = (canvas.width = frames[0].naturalWidth);
                  const height = (canvas.height = frames[0].naturalHeight);
                  const context = canvas.getContext('2d', { willReadFrequently: true });
                  if (!context)
                      return reject('[Error]Can not get canvas context');
                  const data = [];
                  const delay = convertMeta.framesInfo.map((frameInfo) => {
                      return Number(frameInfo.delay);
                  });
                  frames.forEach((frame) => {
                      if (convertMeta.isAborted)
                          throw ('[Info]Convert stop manually, reject when drawImage. ' +
                              convertMeta.id);
                      context.clearRect(0, 0, width, height);
                      context.drawImage(frame, 0, 0, width, height);
                      data.push(context.getImageData(0, 0, width, height).data);
                  });
                  debugLog('[Info]Start convert:', convertMeta.id);
                  let worker;
                  if (freeApngWorkers.length) {
                      worker = freeApngWorkers.shift();
                  }
                  else {
                      worker = new Worker(createConverter._deps.apng);
                  }
                  convertMeta.abort = convertMeta._baseAbort.bind(null, () => {
                      reject('[Info]Convert stop manually, reject when convert apng. ' +
                          convertMeta.id);
                      worker.terminate();
                  });
                  worker.onmessage = function (e) {
                      freeApngWorkers.push(worker);
                      if (!e.data) {
                          return reject('[Error]apng data is null. ' + convertMeta.id);
                      }
                      const pngBlob = new Blob([e.data], { type: 'image/png' });
                      resolve(pngBlob);
                  };
                  const cfg = { data, width, height, delay };
                  worker.postMessage(cfg);
              });
          },
          webm: (frames, convertMeta) => {
              return new Promise((resolve, reject) => {
                  const canvas = document.createElement('canvas');
                  const width = (canvas.width = frames[0].naturalWidth);
                  const height = (canvas.height = frames[0].naturalHeight);
                  const context = canvas.getContext('2d');
                  if (!context)
                      return reject('[Error]Can not get canvas context');
                  const stream = canvas.captureStream();
                  const recorder = new MediaRecorder(stream, {
                      mimeType: 'video/webm',
                      videoBitsPerSecond: 80000000
                  });
                  const delay = convertMeta.framesInfo.map((frame) => {
                      return Number(frame.delay);
                  });
                  const data = [];
                  let frame = 0;
                  const displayFrame = () => {
                      context.clearRect(0, 0, width, height);
                      context.drawImage(frames[frame], 0, 0);
                      if (convertMeta.isAborted) {
                          return recorder.stop();
                      }
                      setTimeout(() => {
                          if (typeof convertMeta.onProgress === 'function')
                              convertMeta.onProgress((frame + 1) / frames.length, 'webm');
                          if (frame === frames.length - 1) {
                              return recorder.stop();
                          }
                          else {
                              frame++;
                          }
                          displayFrame();
                      }, delay[frame]);
                  };
                  recorder.ondataavailable = (event) => {
                      if (event.data && event.data.size) {
                          data.push(event.data);
                      }
                  };
                  recorder.onstop = () => {
                      if (convertMeta.isAborted) {
                          return reject('[info]Convert stop manually, reject when convert webm.' +
                              convertMeta.id);
                      }
                      resolve(new Blob(data, { type: 'video/webm' }));
                  };
                  displayFrame();
                  recorder.start();
              });
          }
      };
      const convert = (convertMeta) => {
          const { id, data, convertResolve, convertReject } = convertMeta;
          let frames;
          active.push(convertMeta);
          if (typeof convertMeta.onProgress === 'function')
              convertMeta.onProgress(0, 'zip');
          const newFolder = zip.folder(id);
          if (!newFolder)
              throw '[Error]Can not get new root folder';
          newFolder
              .loadAsync(data)
              .then(_createImgElements)
              .then((imgEles) => {
              zip.remove(id);
              frames = imgEles;
              if (convertMeta.isAborted)
                  throw '[Info]Convert stop manually, reject when unzip. ' + id;
              return convertTo[convertMeta.format](frames, convertMeta);
          })
              .then(convertResolve)
              .catch(convertReject)
              .finally(() => {
              frames.forEach((frame) => URL.revokeObjectURL(frame.src));
              active.splice(active.indexOf(convertMeta), 1);
              if (queue.length)
                  convert(queue.shift());
          });
      };
      return {
          add: (convertMeta) => {
              debugLog('[Info]Converter add', convertMeta.id);
              return new Promise((convertResolve, convertReject) => {
                  convertMeta.isAborted = false;
                  convertMeta.convertResolve = convertResolve;
                  convertMeta.convertReject = convertReject;
                  convertMeta._baseAbort = (callBack) => {
                      if (typeof callBack === 'function')
                          callBack();
                      convertMeta.isAborted = true;
                  };
                  convertMeta.abort = convertMeta._baseAbort;
                  queue.push(convertMeta);
                  while (active.length < MAX_CONVERT && queue.length && !isStop) {
                      convert(queue.shift());
                  }
              });
          },
          del: (metas) => {
              if (!metas.length)
                  return;
              isStop = true;
              active = active.filter((convertMeta) => {
                  if (metas.find((meta) => meta.id === convertMeta.id)) {
                      convertMeta.abort && convertMeta.abort();
                  }
                  else {
                      return true;
                  }
              });
              queue = queue.filter((convertMeta) => !metas.find((meta) => meta.id === convertMeta.id));
              isStop = false;
              while (active.length < MAX_CONVERT && queue.length) {
                  convert(queue.shift());
              }
          }
      };
  }
  const createConverter = {
      _deps: {
          gif: '',
          apng: '',
          webp: ''
      },
      initialDeps,
      createInstance
  };

  function getSettings() {
      let settings;
      if (!localStorage.pdlSetting) {
          settings = { ...defaultSettings };
          saveSettings(settings);
      }
      else {
          settings = JSON.parse(localStorage.pdlSetting);
          if (settings.version !== defaultSettings.version) {
              settings.version = defaultSettings.version;
              settings.showMsg = true;
              settings = { ...defaultSettings, ...settings };
              saveSettings(settings);
          }
      }
      return settings;
  }
  function saveSettings(settingObj) {
      settingObj = settingObj || settings;
      localStorage.pdlSetting = JSON.stringify(settingObj);
  }
  function upgradeSettings(key, value) {
      if (key in settings && settings[key] !== value) {
          settings[key] = value;
          saveSettings();
      }
  }
  const settings = getSettings();

  function sleep(delay) {
      return new Promise((resolve) => {
          setTimeout(resolve, delay);
      });
  }
  function wakeableSleep(delay) {
      let wake = () => void {};
      const sleep = new Promise((r) => {
          setTimeout(r, delay);
          wake = r;
      });
      return {
          wake,
          sleep
      };
  }
  function getSelfId() {
      return document.querySelector('#qualtrics_user-id')
          ?.textContent;
  }
  const env = {
      isViolentmonkey: GM_info.scriptHandler === 'Violentmonkey',
      isBlobDlAvaliable: !(navigator.userAgent.includes('Firefox') &&
          GM_info.scriptHandler === 'Tampermonkey' &&
          parseFloat(GM_info.version ?? '') > 4.17),
      isSupportSubpath: GM_info.downloadMode && GM_info.downloadMode === 'browser'
  };

  const zip = new JSZip__default["default"]();
  function add$1(id, name, data) {
      zip.folder(id)?.file(name, data);
  }
  function bundle(id) {
      const folder = zip.folder(id);
      if (!folder)
          throw new Error('[Error]no folder:' + id);
      return folder.generateAsync({ type: 'blob' });
  }
  function remove(ids) {
      if (typeof ids === 'string') {
          zip.remove(ids);
      }
      else {
          const dirs = zip.filter((_, file) => file.dir).map((dir) => dir.name);
          const dirsToDel = ids.filter((id) => dirs.some((dir) => dir.includes(id)));
          dirsToDel.forEach((dir) => zip.remove(dir));
      }
  }
  function fileCount(id) {
      let count = 0;
      zip.folder(id)?.forEach(() => count++);
      return count;
  }
  const compressor = {
      add: add$1,
      bundle,
      remove,
      fileCount
  };

  const _saveWithoutSubpath = (blob, meta) => {
      const dlEle = document.createElement('a');
      dlEle.href = URL.createObjectURL(blob);
      dlEle.download = meta.path;
      dlEle.click();
      URL.revokeObjectURL(dlEle.href);
      meta.resolve && meta.resolve(meta);
  };
  const _saveWithSubpath = (blob, meta) => {
      const imgUrl = URL.createObjectURL(blob);
      const request = {
          url: imgUrl,
          name: meta.path,
          onerror: (error) => {
              console.log('[pixiv downloader]Error when saving', meta.path);
              URL.revokeObjectURL(imgUrl);
              meta.reject && meta.reject(error);
          },
          onload: () => {
              if (typeof meta.onLoad === 'function')
                  meta.onLoad();
              URL.revokeObjectURL(imgUrl);
              meta.resolve && meta.resolve(meta);
          }
      };
      meta.abort = GM_download(request).abort;
  };
  function createDownloader(converter) {
      const MAX_DOWNLOAD = 5;
      const MAX_RETRY = 3;
      const INTERVAL = 500;
      const TIMEOUT = 20000;
      let isStop = false;
      let queue = [];
      let active = [];
      let save;
      if (env.isBlobDlAvaliable && env.isSupportSubpath) {
          save = _saveWithSubpath;
      }
      else {
          debugLog('[Info]scriptHandler:', GM_info.scriptHandler, GM_info.version);
          save = _saveWithoutSubpath;
      }
      const download = (meta) => {
          debugLog('[Info]Start download:', meta.path);
          active.push(meta);
          let abortObj;
          if ((!env.isBlobDlAvaliable || env.isViolentmonkey) &&
              !meta.needConvert &&
              !meta.needBundle) {
              abortObj = GM_download({
                  url: meta.src,
                  name: meta.path,
                  headers: {
                      referer: 'https://www.pixiv.net'
                  },
                  ontimeout: errHandler.bind(null, meta),
                  onerror: errHandler.bind(null, meta),
                  onload: () => {
                      debugLog('[Info]Download complete', meta.path);
                      if (typeof meta.onLoad === 'function')
                          meta.onLoad();
                      active.splice(active.indexOf(meta), 1);
                      if (queue.length && !isStop)
                          download(queue.shift());
                      meta.resolve && meta.resolve(meta);
                  }
              });
          }
          else {
              abortObj = GM_xmlhttpRequest({
                  url: meta.src,
                  timeout: TIMEOUT,
                  method: 'GET',
                  headers: {
                      referer: 'https://www.pixiv.net'
                  },
                  responseType: 'blob',
                  ontimeout: errHandler.bind(null, meta),
                  onprogress: (e) => {
                      if (e.lengthComputable && typeof meta.onProgress === 'function') {
                          meta.onProgress(e.loaded / e.total);
                      }
                  },
                  onload: async (e) => {
                      debugLog('[Info]Download complete', meta.id);
                      if (!meta.state)
                          return debugLog('[Warning]But download was canceled.', meta.id);
                      if (meta.needConvert) {
                          const convertMeta = {
                              id: meta.id,
                              data: e.response,
                              format: settings.ugoriaFormat,
                              framesInfo: meta.ugoiraMeta?.frames,
                              onProgress: meta.onProgress
                          };
                          converter.add(convertMeta).then((blob) => {
                              save(blob, meta);
                          }, meta.reject);
                      }
                      else if (meta.needBundle) {
                          compressor.add(meta.id, meta.path, e.response);
                          if (compressor.fileCount(meta.id) === meta.pageCount) {
                              compressor.bundle(meta.id).then((blob) => {
                                  meta.path = meta.bundlePath ?? meta.path;
                                  save(blob, meta);
                                  compressor.remove(meta.id);
                              });
                          }
                          else {
                              meta.resolve && meta.resolve(meta);
                              if (typeof meta.onLoad === 'function')
                                  meta.onLoad();
                          }
                      }
                      else {
                          save(e.response, meta);
                      }
                      await sleep(INTERVAL);
                      active.splice(active.indexOf(meta), 1);
                      if (queue.length && !isStop)
                          download(queue.shift());
                  },
                  onerror: errHandler.bind(null, meta)
              });
          }
          meta.abort = () => {
              meta.state = 0;
              abortObj.abort();
              meta.reject && meta.reject('[Warning]xhr abort manually. ' + meta.id);
          };
      };
      const add = (metas) => {
          if (metas.length < 1)
              return Promise.resolve(metas);
          const promises = [];
          metas.forEach((meta) => {
              promises.push(new Promise((resolve, reject) => {
                  meta.state = 1;
                  meta.resolve = resolve;
                  meta.reject = reject;
              }));
          });
          queue = queue.concat(metas);
          while (active.length < MAX_DOWNLOAD && queue.length && !isStop) {
              download(queue.shift());
          }
          return Promise.all(promises);
      };
      const del = (metas) => {
          if (!metas.length)
              return;
          isStop = true;
          active = active.filter((meta) => {
              if (metas.includes(meta)) {
                  meta.abort && meta.abort();
              }
              else {
                  return true;
              }
          });
          queue = queue.filter((meta) => !metas.includes(meta));
          isStop = false;
          while (active.length < MAX_DOWNLOAD && queue.length) {
              download(queue.shift());
          }
      };
      const errHandler = (meta) => {
          debugLog('[Error]xmlhttpRequest timeout:', meta.src);
          if (!meta.retries) {
              meta.retries = 1;
          }
          else {
              meta.retries++;
          }
          if (meta.retries > MAX_RETRY) {
              meta.reject && meta.reject('[Error]xmlhttpRequest failed: ' + meta.src);
              console.log('[pixiv downloader]Network error:', meta.path, meta.src);
              active.splice(active.indexOf(meta), 1);
              if (queue.length && !isStop)
                  download(queue.shift());
          }
          else {
              debugLog('[Warning]retry xhr:', meta.retries, meta.src);
              download(meta);
          }
      };
      return {
          add: add,
          del: del
      };
  }

  const lang = document.documentElement.getAttribute('lang')?.toLowerCase() ||
      'en';
  const i18nLib = {
      en: {
          bookmarks: 'Bookmarks',
          bookmarks_public: 'Public',
          bookmarks_private: 'Private',
          stop: 'Stop',
          dlbar_category_works: 'Works',
          dlbar_filter_illusts: 'Illustrations',
          dlbar_filter_manga: 'Manga',
          dlbar_filter_ugoria: 'Ugoria',
          dlbar_filter_exclude_downloaded: 'Exclude downloaded',
          feedback: 'Feedback',
          gm_menu_setting: 'Settings',
          tabs_nav_filename: 'Filename',
          tags_lang: 'Tags language: ',
          tags_tips: '{artist}, {artistID}, {title}, {id}, {page}, {tags}',
          tags_tips2: 'Note: Tags language may not be the language you selected, <a href="https://crowdin.com/project/pixiv-tags" target="_blank">some tags without translations</a> may still be in other languages.',
          folder: 'Folder:',
          folder_tips: "I don't need subfolder",
          folder_tips2: "If you don't need a subfolder, just leave the folder name blank",
          folder_vm_tips: "VM doesn't support",
          folder_api_tips: 'Need Browser Api',
          filename: 'FileName:',
          filename_tips: 'Your Name?',
          tabs_nav_history: 'History',
          'modal_history_content.import_btn': 'Import',
          'modal_history_content.export_btn': 'Export',
          'modal_history_content.clear_btn': 'Clear',
          clear_history_tips: 'Do you really want to clear history?',
          tabs_nav_ugoria: 'Ugoria',
          modal_ugoria_format_title: 'Ugoria Format:',
          tabs_nav_others: 'Others',
          'modal_others_content.bundle_illusts': 'Pack multi-page illustrations into a .zip archive',
          'modal_others_content.bundle_manga': 'Pack manga into a .zip archive',
          tabs_nav_donate: 'Feedback / Donate'
      },
      'zh-cn': {
          bookmarks: '收藏',
          bookmarks_public: '公开',
          bookmarks_private: '不公开',
          stop: '停止',
          dlbar_category_works: '作品',
          dlbar_filter_illusts: '插画',
          dlbar_filter_manga: '漫画',
          dlbar_filter_ugoria: '动图',
          dlbar_filter_exclude_downloaded: '排除已下载图片',
          feedback: '有问题or想建议?这里反馈',
          gm_menu_setting: '设置',
          tabs_nav_filename: '文件名',
          tags_lang: '标签语言:',
          tags_tips: '{artist}:作者, {artistID}:作者ID, {title}:作品标题, {id}:作品pixiv ID, {page}:页码, {tags}:作品标签。',
          tags_tips2: '请注意:标签翻译不一定是你选择的语言,部分<a href="https://crowdin.com/project/pixiv-tags" target="_blank">无对应语言翻译的标签</a>仍可能是其他语言。',
          folder: '文件夹名:',
          folder_tips: '我不想保存到子文件夹',
          folder_tips2: '如果不想保存到画师目录,文件夹名留空即可。',
          folder_vm_tips: 'Violentmonkey不支持',
          folder_api_tips: '需要Browser Api',
          filename: '文件名:',
          filename_tips: '你的名字?',
          tabs_nav_history: '历史记录',
          'modal_history_content.import_btn': '导入记录',
          'modal_history_content.export_btn': '导出记录',
          'modal_history_content.clear_btn': '清除记录',
          clear_history_tips: '真的要清除历史记录吗?',
          tabs_nav_ugoria: '动图',
          modal_ugoria_format_title: '动图格式:',
          tabs_nav_others: '其它',
          'modal_others_content.bundle_illusts': '将多页插图打包为.zip压缩包',
          'modal_others_content.bundle_manga': '将多页漫画作品打包为.zip压缩包',
          tabs_nav_donate: '反馈 / 赞赏'
      },
      zh: {},
      ja: {},
      ko: {},
      'zh-tw': {}
  };
  i18nLib.en = Object.create(i18nLib['zh-cn'], Object.getOwnPropertyDescriptors(i18nLib.en));
  i18nLib.ja = Object.create(i18nLib.en);
  i18nLib.ko = Object.create(i18nLib.en);
  i18nLib['zh-tw'] = Object.create(i18nLib['zh-cn']);
  i18nLib.zh = i18nLib['zh-cn'];
  const i18n = (key) => i18nLib[lang]?.[key] || `i18n[${lang}][${key}] not found`;
  const modalHtml = {
      upgradeMsgTitle: `<h3>Pixiv Downloader ${defaultSettings.version}</h3>`,
      upgradeMsgContent: `      <div class="pdl-changelog">
  <ul>
    <li>修正了关注页面卡死的bug。</li>
  </ul>
</div>`,
      modalCreditFooter: `<style>.pdl-dialog-footer {
    position: relative;
    font-size: 12px;
  }</style><details style="margin-top: 1.5em;">
  <summary style="display: inline-block; list-style: none; cursor: pointer; color: #0096fa; text-decoration: underline">脚本还行?请我喝杯可乐吧!</summary>
  ${creditCode}
  <p style="text-align: center">愿你每天都能找到对的色图,就像我每天都能喝到香草味可乐</p>
</details>`,
      modalFeedback: `<a target="_blank" style="position: absolute; right: 0px; top: 0px; color: #0096fa; text-decoration: underline" href="https://greasyfork.org/zh-CN/scripts/432150-pixiv-downloader/feedback">${i18n('feedback')}</a>`,
      modalSettingsContent: `      <div>
  <div class="pdl-tabs-nav">
  <div class="pdl-tabs__active-bar"></div>
  </div>
  <div class="pdl-tabs-content">
  </div>
</div>`,
      modalSettingFilename: `            <div id="pdl-setting-filename">
<div>
  <div class="pdl-input-wrap">
    <label for="pdlfolder">${i18n('folder')}</label>
    <input type="text" id="pdlfolder" maxlength="100" />
    <button id="pdl-filename-folder-reset" class="pdl-dialog-button icon" disabled>↺</button>
    <button id="pdl-filename-folder-confirm" class="pdl-dialog-button icon primary" disabled>✓</button>
  </div>
  <div class="pdl-input-wrap">
    <label for="pdlfilename">${i18n('filename')}</label>
    <input
      type="text"
      id="pdlfilename"
      placeholder="${i18n('filename_tips')}"
      required
      maxlength="100"
    />
    <button id="pdl-filename-filename-reset" class="pdl-dialog-button icon" disabled>↺</button>
    <button id="pdl-filename-filename-confirm" class="pdl-dialog-button icon primary" disabled>✓</button>
  </div>
</div>
<div class="tags-option">
<span class="tags-title">${i18n('tags_lang')}</span>
<div class="tags-content">
  <div class="tags-item">
    <input type="radio" name="lang" id="lang_ja" value="ja" />
    <label for="lang_ja">日本語(default)</label>
  </div>
  <div class="tags-item">
    <input type="radio" name="lang" id="lang_zh" value="zh" />
    <label for="lang_zh">简中</label>
  </div>
  <div class="tags-item">
    <input type="radio" name="lang" id="lang_zh_tw" value="zh_tw" />
    <label for="lang_zh_tw">繁中</label>
  </div>
  <div class="tags-item">
    <input type="radio" name="lang" id="lang_en" value="en" />
    <label for="lang_en">English</label>
  </div>
</div>
</div>
<p style="font-size: 14px; margin: 0.5em 0">
  ${i18n('tags_tips')}
</p>
<p style="font-size: 14px; margin: 0.5em 0">
  ${i18n('folder_tips2')}
</p>
<p style="font-size: 14px; margin: 0.5em 0">
  ${i18n('tags_tips2')}
</p>
</div>`,
      modalSettingUgoria: `            <div id="pdl-setting-ugoria">
<p class="option-header">${i18n('modal_ugoria_format_title')}</p>
<div id="pdl-ugoria-format-wrap">
  <div class="pdl-ugoria-format-item">
    <input
      type="radio"
      id="pdl-ugoria-zip"
      value="zip"
      name="format"
    /><label for="pdl-ugoria-zip">Zip</label>
  </div>
  <div class="pdl-ugoria-format-item">
    <input
      type="radio"
      id="pdl-ugoria-gif"
      value="gif"
      name="format"
    /><label for="pdl-ugoria-gif">Gif</label>
  </div>
  <div class="pdl-ugoria-format-item">
    <input
      type="radio"
      id="pdl-ugoria-apng"
      value="apng"
      name="format"
    /><label for="pdl-ugoria-apng">Png</label>
  </div>
  <div class="pdl-ugoria-format-item">
    <input
      type="radio"
      id="pdl-ugoria-webm"
      value="webm"
      name="format"
    /><label for="pdl-ugoria-webm">Webm</label>
  </div>
  <div class="pdl-ugoria-format-item">
    <input
      type="radio"
      id="pdl-ugoria-webp"
      value="webp"
      name="format"
    /><label for="pdl-ugoria-webp">Webp</label>
  </div>
</div>
</div>`,
      modalSettingHistory: `            <div id="pdl-setting-history">
<div>
  <button id="pdl-export" class="btn-history pdl-dialog-button primary">
    ${i18n('modal_history_content.export_btn')}
  </button>
</div>
<div>
  <input
    type="file"
    id="pdl-import"
    accept=".txt"
    style="display: none"
  /><button id="pdl-import-btn" class="btn-history pdl-dialog-button primary">
    ${i18n('modal_history_content.import_btn')}
  </button>
</div>
<div>
  <button id="pdl-clear-history" class="btn-history pdl-dialog-button primary">
    ${i18n('modal_history_content.clear_btn')}
  </button>
</div>
</div>`,
      modalSettingOthers: `            <div id="pdl-setting-others">
<div>
  <label class="pdl-options"
    ><input
      id="pdl-options-bundle-illusts"
      type="checkbox"
      class="pdl-checkbox"
    /><span
      >${i18n('modal_others_content.bundle_illusts')}</span
    ></label
  >
</div>
<hr />
<div>
  <label class="pdl-options"
    ><input
      id="pdl-options-bundle-manga"
      type="checkbox"
      class="pdl-checkbox"
    /><span
      >${i18n('modal_others_content.bundle_manga')}</span
    ></label
  >
</div>
</div>`,
      modalSettingDonate: `            <div id="pdl-setting-donate">
${creditCode}
<p>如果脚本有帮助到你,欢迎扫码请我喝杯可乐 ^_^</p>
<p><a target="_blank" style="color: #0096fa; text-decoration: underline" href="https://greasyfork.org/zh-CN/scripts/432150-pixiv-downloader/feedback">${i18n('feedback')}</a></p>
</div>`
  };

  function add(pixivId) {
      this._records.add(pixivId);
      localStorage.setItem(`pdlTemp-${pixivId}`, '');
  }
  function has(pixivId) {
      return this._records.has(pixivId);
  }
  function getHistory() {
      const storage = localStorage.pixivDownloader || '[]';
      return new Set(JSON.parse(storage));
  }
  function updateHistory() {
      Object.keys(localStorage).forEach((key) => {
          const matchResult = /pdlTemp-(\d+)/.exec(key);
          if (matchResult) {
              this._records.add(matchResult[1]);
              localStorage.removeItem(matchResult[0]);
          }
      });
      this.saveHistory();
  }
  function clearHistory() {
      const isConfirm = confirm(i18n('clear_history_tips'));
      if (!isConfirm)
          return;
      this.updateHistory();
      this._records = new Set();
      localStorage.pixivDownloader = '[]';
      location.reload();
  }
  function saveHistory(historyArr) {
      if (historyArr) {
          localStorage.pixivDownloader = JSON.stringify(historyArr);
      }
      else {
          localStorage.pixivDownloader = JSON.stringify([...this._records]);
      }
  }
  function getAll() {
      return [...this._records];
  }
  const pixivHistory = {
      _records: getHistory(),
      add,
      has,
      getAll,
      updateHistory,
      saveHistory,
      clearHistory
  };

  const needBundle = (type) => {
      return ((type === IllustType.manga && settings.bundleManga) ||
          (type === IllustType.illusts && settings.bundleIllusts));
  };
  const replaceInvalidChar = (string) => {
      if (!string)
          return '';
      const temp = document.createElement('div');
      temp.innerHTML = string;
      if (!temp.textContent)
          return '';
      return temp.textContent
          .trim()
          .replace(/^\.|\.$/g, '')
          .replace(/[\u200b-\u200f\uFEFF\u202a-\u202e\\/:*?|]/g, '')
          .replace(/"/g, "'")
          .replace(/</g, '﹤')
          .replace(/>/g, '﹥');
  };
  const getFilePath = ({ user, userId, title, tags, illustId, page, ext }, option = { needBundle: false, needConvert: false }) => {
      let pathPattern;
      if (settings.folderPattern &&
          env.isSupportSubpath &&
          (!option.needConvert || env.isBlobDlAvaliable) &&
          !option.needBundle) {
          pathPattern = settings.folderPattern + '/' + settings.filenamePattern;
      }
      else {
          pathPattern = settings.filenamePattern;
      }
      if (option.needBundle && !settings.filenamePattern.includes('{page}')) {
          pathPattern += '_{page}';
      }
      return (pathPattern
          .replaceAll('{artist}', user)
          .replaceAll('{artistID}', userId)
          .replaceAll('{title}', title)
          .replaceAll('{tags}', tags)
          .replaceAll('{page}', String(page))
          .replaceAll('{id}', illustId) + ext);
  };
  const makeTagsStr = (prev, cur, index, tagsArr) => {
      const tag = settings.tagLang === 'ja' ? cur.tag : cur.translation?.['en'] || cur.tag;
      if (index < tagsArr.length - 1) {
          return prev + tag + '_';
      }
      else {
          return prev + tag;
      }
  };
  const getData = async (url) => {
      const res = await fetch(url);
      if (!res.ok)
          throw new Error('[Error]fail to fetch:' + url + ', code:' + res.status);
      const data = await res.json();
      if (data.error)
          throw new Error('[Error]json return error.' + data.message);
      return data;
  };
  const fetchJson = async (url) => {
      let json;
      let retry = 0;
      do {
          try {
              debugLog('[Info]fetch url:', url);
              json = await getData(url);
          }
          catch (error) {
              retry++;
              if (retry === 3)
                  throw error;
              sleep(3000);
          }
      } while (!json);
      return json;
  };
  const parseByIllust = async (illustId, seletedPage) => {
      let params = '';
      if (settings.tagLang !== 'ja')
          params = '?lang=' + settings.tagLang;
      const res = await fetch('https://www.pixiv.net/artworks/' + illustId + params);
      if (!res.ok)
          throw new Error(String(res.status));
      const htmlText = await res.text();
      const preloadDataText = htmlText.match(regexp.preloadData);
      if (!preloadDataText)
          throw new Error('[Error]Fail to parse preload data.');
      const preloadData = JSON.parse(preloadDataText[1]);
      const illustInfo = preloadData.illust[illustId];
      const user = replaceInvalidChar(illustInfo.userName) || 'userId-' + illustInfo.userId;
      const title = replaceInvalidChar(illustInfo.illustTitle) ||
          'illustId-' + illustInfo.illustId;
      const tags = replaceInvalidChar(illustInfo.tags.tags.reduce(makeTagsStr, ''));
      const illustType = illustInfo.illustType;
      const metas = [];
      const pathInfo = {
          user,
          title,
          tags,
          illustId,
          userId: illustInfo.userId,
          ext: '',
          page: 0
      };
      const baseMeta = {
          id: illustId,
          illustType: illustType
      };
      if (illustType === IllustType.illusts || illustType === IllustType.manga) {
          const firstImgSrc = illustInfo.urls.original;
          const srcPrefix = firstImgSrc.slice(0, firstImgSrc.indexOf('_') + 2);
          const extendName = firstImgSrc.slice(-4);
          pathInfo.ext = extendName;
          if (illustInfo.pageCount > 1 && seletedPage === undefined) {
              const requireBundle = needBundle(illustType);
              let option;
              if (requireBundle) {
                  option = {
                      needBundle: true
                  };
                  pathInfo.ext = '.zip';
                  baseMeta.needBundle = true;
                  baseMeta.pageCount = illustInfo.pageCount;
                  pathInfo.page = illustInfo.pageCount;
                  baseMeta.bundlePath = getFilePath(pathInfo);
              }
              pathInfo.ext = extendName;
              for (let i = 0; i < illustInfo.pageCount; i++) {
                  pathInfo.page = i;
                  metas.push({
                      ...baseMeta,
                      page: i,
                      src: srcPrefix + i + extendName,
                      path: getFilePath(pathInfo, option)
                  });
              }
          }
          else {
              let src = firstImgSrc;
              if (seletedPage !== undefined) {
                  src = srcPrefix + seletedPage + extendName;
                  pathInfo.page = seletedPage;
              }
              pathInfo.ext = illustInfo.urls.original.slice(-4);
              metas.push({
                  ...baseMeta,
                  src,
                  path: getFilePath(pathInfo)
              });
          }
      }
      else if (illustType === IllustType.ugoira) {
          const ugoira = await fetchJson('https://www.pixiv.net/ajax/illust/' + illustId + '/ugoira_meta');
          let option;
          if (settings.ugoriaFormat !== 'zip') {
              option = {
                  needConvert: true
              };
              baseMeta.needConvert = true;
              baseMeta.ugoiraMeta = ugoira.body;
          }
          pathInfo.ext = '.' + settings.ugoriaFormat;
          metas.push({
              ...baseMeta,
              src: ugoira.body.originalSrc,
              path: getFilePath(pathInfo, option)
          });
      }
      return metas;
  };
  function filterWorks(works, option) {
      const obj = {
          unavaliable: [],
          avaliable: [],
          invalid: []
      };
      works.forEach((work) => {
          if (!work.isBookmarkable) {
              obj.unavaliable.push(work.id);
          }
          else if (option.exclude_downloaded && pixivHistory.has(work.id)) {
              obj.invalid.push(work.id);
          }
          else if (!option[work.illustType]) {
              obj.invalid.push(work.id);
          }
          else {
              obj.avaliable.push(work.id);
          }
      });
      return obj;
  }
  async function getChunksGenerator(userId, category, tag, rest, filterOption) {
      const OFFSET = 48;
      let requestUrl;
      if (category === 'bookmarks') {
          requestUrl = `https://www.pixiv.net/ajax/user/${userId}/illusts/bookmarks?tag=${tag}&offset=0&limit=${OFFSET}&rest=${rest}&lang=ja`;
      }
      else {
          requestUrl = `https://www.pixiv.net/ajax/user/${userId}/${category}/tag?tag=${tag}&offset=0&limit=${OFFSET}&lang=ja`;
      }
      let head = 0;
      const firstPageData = await fetchJson(requestUrl);
      const total = firstPageData.body.total;
      async function* generateIds() {
          yield filterWorks(firstPageData.body.works, filterOption);
          head += OFFSET;
          while (head < total) {
              const data = await fetchJson(requestUrl.replace('offset=0', 'offset=' + head));
              head += OFFSET;
              await sleep(3000);
              yield filterWorks(data.body.works, filterOption);
          }
      }
      return {
          total,
          generator: generateIds()
      };
  }
  async function getAllWorksGenerator(userId, filterOption) {
      const requestUrl = 'https://www.pixiv.net/ajax/user/' + userId + '/profile/all';
      const profile = await fetchJson(requestUrl);
      const data = profile.body;
      let illustIds = [];
      let mangaIds = [];
      if (filterOption[IllustType.illusts] && typeof data.illusts === 'object') {
          illustIds.push(...Object.keys(data.illusts).reverse());
      }
      if (filterOption[IllustType.manga] && typeof data.manga === 'object') {
          mangaIds.push(...Object.keys(data.manga).reverse());
      }
      if (filterOption.exclude_downloaded) {
          illustIds = illustIds.filter((id) => !pixivHistory.has(id));
          mangaIds = mangaIds.filter((id) => !pixivHistory.has(id));
      }
      async function* generateIds() {
          const OFFSET = 48;
          const baseUrl = 'https://www.pixiv.net/ajax/user/' + userId + '/profile/illusts';
          let workCategory = 'illust';
          while (illustIds.length > 0) {
              let searchStr = '?';
              const chunk = illustIds.splice(0, OFFSET);
              searchStr +=
                  chunk.map((id) => 'ids[]=' + id).join('&') +
                      `&work_category=${workCategory}&is_first_page=0&lang=ja`;
              const data = await fetchJson(baseUrl + searchStr);
              await sleep(3000);
              yield filterWorks(Object.values(data.body.works), filterOption);
          }
          workCategory = 'manga';
          while (mangaIds.length > 0) {
              let searchStr = '?';
              const chunk = mangaIds.splice(0, OFFSET);
              searchStr +=
                  chunk.map((id) => 'ids[]=' + id).join('&') +
                      `&work_category=${workCategory}&is_first_page=0&lang=ja`;
              const data = await fetchJson(baseUrl + searchStr);
              await sleep(3000);
              yield filterWorks(Object.values(data.body.works), filterOption);
          }
      }
      return {
          total: illustIds.length + mangaIds.length,
          generator: generateIds()
      };
  }
  const parser = {
      id: parseByIllust,
      getChunksGenerator,
      getAllWorksGenerator
  };

  let converter;
  let downloader;
  async function initial() {
      converter = await createConverter
          .initialDeps(depsUrls)
          .then((createConverter) => createConverter.createInstance());
      downloader = createDownloader(converter);
  }

  function changeDlbarDisplay() {
      document.querySelectorAll('nav [pdl-userid]').forEach((ele) => {
          ele.classList.toggle('pdl-hide');
      });
      document.querySelectorAll('section [pdl-userid]').forEach((ele) => {
          ele.classList.toggle('pdl-tag-hide');
      });
      document.querySelector('.pdl-wrap')?.classList.toggle('unavailable');
  }
  let isDownloading = false;
  const dlBarRef = {
      filter: {
          exclude_downloaded: undefined,
          [IllustType.illusts]: undefined,
          [IllustType.manga]: undefined,
          [IllustType.ugoira]: undefined
      },
      statusBar: undefined,
      abortBtn: undefined
  };
  function getFilterOption() {
      return {
          exclude_downloaded: dlBarRef.filter.exclude_downloaded?.checked ??
              defaultSettings.filter.exclude_downloaded,
          [IllustType.illusts]: dlBarRef.filter[IllustType.illusts]?.checked ??
              defaultSettings.filter[IllustType.illusts],
          [IllustType.manga]: dlBarRef.filter[IllustType.manga]?.checked ??
              defaultSettings.filter[IllustType.manga],
          [IllustType.ugoira]: dlBarRef.filter[IllustType.ugoira]?.checked ??
              defaultSettings.filter[IllustType.ugoira]
      };
  }
  function updataStatus(str) {
      dlBarRef.statusBar && (dlBarRef.statusBar.textContent = str);
  }
  function onProgressCB({ illustId, total, completed }) {
      updataStatus(`Downloading: ${completed} / ${total}`);
  }
  async function useDownload(chunksGenerators) {
      if (!dlBarRef.abortBtn)
          return;
      isDownloading = true;
      changeDlbarDisplay();
      let total = 0;
      let failedResult;
      const idsGenerators = [];
      try {
          if (chunksGenerators instanceof Array) {
              await Promise.all(chunksGenerators).then((chunksGenerator) => {
                  chunksGenerator.forEach((val) => {
                      total += val.total;
                      idsGenerators.push(val.generator);
                  });
              });
          }
          else {
              await chunksGenerators.then((val) => {
                  total = val.total;
                  idsGenerators.push(val.generator);
              });
          }
      }
      catch (error) {
          updataStatus('Error, see console.');
          changeDlbarDisplay();
          isDownloading = false;
          throw error;
      }
      try {
          if (total === 0) {
              throw 'No works.';
          }
          debugLog('[Info]Total:', total);
          const { failed, unavaliable } = await downloadByIds(total, idsGenerators, dlBarRef.abortBtn, updataStatus, onProgressCB);
          if (failed.length || unavaliable.length) {
              updataStatus(`Failed: ${failed.length + unavaliable.length}. See console.`);
              console.log('[Pixiv Downloader]Failed: ', failed.join(', '));
              console.log('[Pixiv Downloader]Unavaliable: ', unavaliable.join(', '));
              if (failed.length)
                  failedResult = failed;
          }
          else {
              updataStatus('Complete');
          }
      }
      catch (error) {
          updataStatus(error);
      }
      changeDlbarDisplay();
      isDownloading = false;
      return failedResult;
  }
  async function downloadByIds(total, idsGenerators, abortBtn, updataStatus, onProgressCB) {
      let resolve;
      let reject;
      const done = new Promise((r, j) => {
          resolve = r;
          reject = j;
      });
      let wakeFn;
      let completed = 0;
      const failed = [], unavaliable = [], invalid = [];
      let isCanceled = false;
      let metasRecord = [];
      let tooManyRequests = false;
      abortBtn.onclick = () => {
          isCanceled = true;
          abortBtn.onclick = null;
          reject(`Stopped. ${completed} / ${total}`);
          wakeFn && wakeFn();
          if (metasRecord.length) {
              downloader.del(metasRecord);
              converter.del(metasRecord);
              compressor.remove(metasRecord.map((meta) => meta.id));
              metasRecord = [];
          }
      };
      const afterEach = (illustId) => {
          onProgressCB({
              illustId,
              total,
              completed
          });
          if (completed === total - failed.length) {
              resolve({ failed, unavaliable });
          }
      };
      updataStatus('Downloading...');
      try {
          for (const idsGenerator of idsGenerators) {
              if (isCanceled)
                  return done;
              for await (const ids of idsGenerator) {
                  debugLog('[Info]ids:', ids);
                  if (isCanceled)
                      return done;
                  if (ids.unavaliable.length) {
                      unavaliable.push(...ids.unavaliable);
                      total -= ids.unavaliable.length;
                  }
                  if (ids.invalid.length) {
                      invalid.push(...ids.invalid);
                      total -= ids.invalid.length;
                  }
                  if (ids.avaliable.length) {
                      for (const id of ids.avaliable) {
                          if (isCanceled)
                              return done;
                          if (tooManyRequests) {
                              updataStatus('Too many requests, wait 30s');
                              console.log('[Pixiv Downloader]Too many requests, wait 30s');
                              const { wake, sleep } = wakeableSleep(30000);
                              wakeFn = wake;
                              await sleep;
                              tooManyRequests = false;
                              if (isCanceled)
                                  return done;
                              updataStatus('Downloading...');
                          }
                          parser
                              .id(id)
                              .then((metas) => {
                              if (isCanceled) {
                                  throw '[Warning]Download stop manually: ' + metas[0].id;
                              }
                              metasRecord = metasRecord.concat(metas);
                              return downloader.add(metas);
                          })
                              .then((metas) => {
                              pixivHistory.add(id);
                              if (!isCanceled) {
                                  metasRecord = metasRecord.filter((meta) => !metas.find((downloadMeta) => downloadMeta.id === meta.id));
                                  completed++;
                                  afterEach(id);
                              }
                          }, (reason) => {
                              if (!isCanceled) {
                                  if (reason.message && reason.message === '429')
                                      tooManyRequests = true;
                                  if (reason.message &&
                                      reason.message === '[Error]Fail to parse preload data.') {
                                      unavaliable.push(id);
                                  }
                                  else {
                                      failed.push(id);
                                  }
                                  afterEach(id);
                              }
                          });
                          await sleep(1000);
                      }
                  }
                  else {
                      afterEach();
                  }
              }
          }
      }
      catch (error) {
          console.error(error);
          return Promise.reject('Error, see console.');
      }
      return done;
  }
  function downloadWorks(evt) {
      evt.preventDefault();
      evt.stopPropagation();
      if (isDownloading)
          return;
      const btn = evt.target;
      const userId = btn.getAttribute('pdl-userid');
      const filterOption = getFilterOption();
      const ids = parser.getAllWorksGenerator(userId, filterOption);
      useDownload(ids)
          .then((failed) => {
          if (failed instanceof Array && failed.length) {
              const gen = async function* () {
                  yield {
                      avaliable: failed,
                      unavaliable: [],
                      invalid: []
                  };
              };
              useDownload(Promise.resolve({ total: failed.length, generator: gen() })).catch(console.error);
          }
      })
          .catch(console.error);
  }
  async function downloadBookmarksOrTags(evt) {
      evt.preventDefault();
      evt.stopPropagation();
      if (isDownloading)
          return;
      const btn = evt.target;
      const userId = btn.getAttribute('pdl-userid');
      const category = btn.getAttribute('category');
      const tag = btn.getAttribute('tag') || '';
      const rest = (btn.getAttribute('rest') || 'show');
      const filterOption = getFilterOption();
      let idsGenerators;
      if (rest === 'all') {
          const idsShowPromise = parser.getChunksGenerator(userId, 'bookmarks', '', 'show', filterOption);
          const idsHidePromise = parser.getChunksGenerator(userId, 'bookmarks', '', 'hide', filterOption);
          idsGenerators = [idsShowPromise, idsHidePromise];
      }
      else {
          idsGenerators = parser.getChunksGenerator(userId, category, tag, rest, filterOption);
      }
      useDownload(idsGenerators)
          .then((failed) => {
          if (failed instanceof Array && failed.length) {
              const gen = async function* () {
                  yield {
                      avaliable: failed,
                      unavaliable: [],
                      invalid: []
                  };
              };
              useDownload(Promise.resolve({ total: failed.length, generator: gen() })).catch(console.error);
          }
      })
          .catch(console.error);
  }

  function handleDownload(pdlBtn, illustId) {
      let pageCount, pageComplete = 0, shouldDownloadPage;
      const pageAttr = pdlBtn.getAttribute('should-download');
      if (pageAttr) {
          shouldDownloadPage = Number(pageAttr);
      }
      const onProgress = (progress = 0, type = null) => {
          if (pageCount > 1)
              return;
          progress = Math.floor(progress * 100);
          switch (type) {
              case null:
                  pdlBtn.style.setProperty('--pdl-progress', progress + '%');
              case 'gif':
              case 'webm':
              case 'webp':
                  pdlBtn.textContent = String(progress);
                  break;
              case 'zip':
                  pdlBtn.textContent = '';
                  break;
          }
      };
      const onLoad = function () {
          if (pageCount < 2)
              return;
          const progress = Math.floor((++pageComplete / pageCount) * 100);
          pdlBtn.textContent = String(progress);
          pdlBtn.style.setProperty('--pdl-progress', progress + '%');
      };
      pdlBtn.classList.add('pdl-progress');
      parser
          .id(illustId, shouldDownloadPage)
          .then((metas) => {
          pageCount = metas.length;
          metas.forEach((meta) => {
              meta.onProgress = onProgress;
              meta.onLoad = onLoad;
          });
          return downloader.add(metas);
      })
          .then(() => {
          pixivHistory.add(illustId);
          pdlBtn.classList.remove('pdl-error');
          pdlBtn.classList.add('pdl-complete');
      })
          .catch((err) => {
          if (err)
              console.log(err);
          pdlBtn.classList.remove('pdl-complete');
          pdlBtn.classList.add('pdl-error');
      })
          .finally(() => {
          pdlBtn.innerHTML = '';
          pdlBtn.style.removeProperty('--pdl-progress');
          pdlBtn.classList.remove('pdl-progress');
      });
  }

  function createModal({ header = '', content, footer = '' }, option) {
      const modal = document.createElement('div');
      const dialog = document.createElement('div');
      modal.classList.add('pdl-modal');
      dialog.classList.add('pdl-dialog');
      if (option.closeOnClickModal) {
          dialog.onclick = (e) => {
              e.stopPropagation();
          };
          modal.onclick = () => {
              modal.remove();
          };
      }
      dialog.innerHTML = `  <header class="pdl-dialog-header">${header}</header>
  <div class="pdl-dialog-content">${content}</div>
  <footer class="pdl-dialog-footer">${footer}</footer>`;
      const closeBtn = document.createElement('button');
      closeBtn.classList.add('pdl-dialog-close');
      closeBtn.onclick = () => {
          modal.remove();
      };
      dialog.insertBefore(closeBtn, dialog.firstChild);
      modal.appendChild(dialog);
      return modal;
  }
  function showUpgradeMsg() {
      document.body.appendChild(createModal({
          header: modalHtml.upgradeMsgTitle,
          content: modalHtml.upgradeMsgContent,
          footer: modalHtml.modalCreditFooter + modalHtml.modalFeedback
      }, { closeOnClickModal: false }));
  }
  function createTabsPart({ tabName, paneHtml }) {
      const tab = document.createElement('div');
      tab.classList.add('pdl-tab-item');
      tab.textContent = tabName;
      const pane = document.createElement('div');
      pane.classList.add('pdl-tab-pane');
      pane.innerHTML = paneHtml;
      return {
          tab,
          pane
      };
  }
  function createTabFilename({ tabName, paneHtml }) {
      const { tab, pane } = createTabsPart({ tabName, paneHtml });
      const folder = pane.querySelector('#pdlfolder');
      const folderReset = pane.querySelector('#pdl-filename-folder-reset');
      const folderUpdate = pane.querySelector('#pdl-filename-folder-confirm');
      const filename = pane.querySelector('#pdlfilename');
      const filenameReset = pane.querySelector('#pdl-filename-filename-reset');
      const filenameUpdate = pane.querySelector('#pdl-filename-filename-confirm');
      if (!folder || !filename)
          throw new Error('[Error]Can not create modal.');
      filename.value = settings.filenamePattern;
      if (!env.isSupportSubpath) {
          folder.setAttribute('disabled', '');
          folder.value = '';
      }
      else {
          folder.value = settings.folderPattern;
      }
      folder.placeholder = env.isViolentmonkey
          ? i18n('folder_vm_tips')
          : !env.isSupportSubpath
              ? i18n('folder_api_tips')
              : i18n('folder_tips');
      folder.addEventListener('input', () => {
          folderReset?.removeAttribute('disabled');
          folderUpdate?.removeAttribute('disabled');
      });
      folderReset?.addEventListener('click', () => {
          folder.value = settings.folderPattern;
          folderReset?.setAttribute('disabled', '');
          folderUpdate?.setAttribute('disabled', '');
      });
      folderUpdate?.addEventListener('click', () => {
          const folderPattern = folder.value
              .split('/')
              .map((path) => path
              .trim()
              .replace(/^\.+|\.+$|[\u200b-\u200f\uFEFF\u202a-\u202e\\:*?"|<>]/g, ''))
              .filter((path) => !!path)
              .join('/') ?? '';
          upgradeSettings('folderPattern', folderPattern);
          folder.value = folderPattern;
          folderReset?.setAttribute('disabled', '');
          folderUpdate?.setAttribute('disabled', '');
      });
      filename.addEventListener('input', () => {
          filenameReset?.removeAttribute('disabled');
          filenameUpdate?.removeAttribute('disabled');
      });
      filenameReset?.addEventListener('click', () => {
          filename.value = settings.filenamePattern;
          filenameReset?.setAttribute('disabled', '');
          filenameUpdate?.setAttribute('disabled', '');
      });
      filenameUpdate?.addEventListener('click', () => {
          const filenamePattern = filename.value
              .trim()
              .replace(/^\.+|[\u200b-\u200f\uFEFF\u202a-\u202e\\/:*?"|<>]/g, '') ??
              '';
          if (filenamePattern === '')
              return filenameReset?.click();
          upgradeSettings('filenamePattern', filenamePattern);
          filename.value = filenamePattern;
          filenameReset?.setAttribute('disabled', '');
          filenameUpdate?.setAttribute('disabled', '');
      });
      pane
          .querySelectorAll('.tags-content .tags-item input')
          .forEach((input) => {
          if (settings.tagLang === input.value)
              input.checked = true;
          input.addEventListener('change', (ev) => {
              upgradeSettings('tagLang', ev.currentTarget.value);
          });
      });
      return {
          tab,
          pane
      };
  }
  function createTabUgoria({ tabName, paneHtml }) {
      const { tab, pane } = createTabsPart({ tabName, paneHtml });
      pane
          .querySelectorAll('.pdl-ugoria-format-item input[type="radio"]')
          .forEach((el) => {
          if (settings.ugoriaFormat === el.value)
              el.checked = true;
          el.addEventListener('change', (ev) => {
              upgradeSettings('ugoriaFormat', ev.currentTarget.value);
          });
      });
      return {
          tab,
          pane
      };
  }
  function createTabHistory({ tabName, paneHtml }) {
      const { tab, pane } = createTabsPart({ tabName, paneHtml });
      const file = pane.querySelector('#pdl-import');
      file?.addEventListener('change', (evt) => {
          const file = evt.currentTarget.files?.[0];
          if (!file)
              return;
          if (file.type === 'text/plain') {
              const reader = new FileReader();
              reader.readAsText(file);
              reader.onload = (readEvt) => {
                  const text = readEvt.target?.result;
                  try {
                      if (typeof text !== 'string')
                          throw new Error('Invalid file');
                      const history = JSON.parse(text);
                      if (!(history instanceof Array))
                          throw new Error('Invalid file');
                      if (history.length &&
                          !history.every((id) => typeof id === 'string')) {
                          throw new Error('Invalid id type');
                      }
                      pixivHistory.saveHistory(history);
                      location.reload();
                  }
                  catch (error) {
                      alert(error.message);
                  }
              };
          }
          else {
              alert('Invalid file');
          }
      });
      const importBtn = pane.querySelector('#pdl-import-btn');
      importBtn?.addEventListener('click', () => file?.click());
      const exportBtn = pane.querySelector('#pdl-export');
      exportBtn?.addEventListener('click', () => {
          const dlEle = document.createElement('a');
          const history = JSON.stringify(pixivHistory.getAll());
          dlEle.href = URL.createObjectURL(new Blob([history], { type: 'text/plain' }));
          dlEle.download = 'Pixiv Downloader ' + new Date().toLocaleString();
          dlEle.click();
          URL.revokeObjectURL(dlEle.href);
      });
      const clearBtn = pane.querySelector('#pdl-clear-history');
      clearBtn?.addEventListener('click', () => pixivHistory.clearHistory());
      return {
          tab,
          pane
      };
  }
  function createTabOthers({ tabName, paneHtml }) {
      const { tab, pane } = createTabsPart({ tabName, paneHtml });
      const bundleIllust = pane.querySelector('#pdl-options-bundle-illusts');
      bundleIllust && (bundleIllust.checked = settings.bundleIllusts);
      bundleIllust?.addEventListener('change', (ev) => {
          upgradeSettings('bundleIllusts', ev.currentTarget.checked);
      });
      const bundleManga = pane.querySelector('#pdl-options-bundle-manga');
      bundleManga && (bundleManga.checked = settings.bundleManga);
      bundleManga?.addEventListener('change', (ev) => {
          upgradeSettings('bundleManga', ev.currentTarget.checked);
      });
      return {
          tab,
          pane
      };
  }
  function showSettings() {
      if (document.querySelector('.pdl-modal'))
          return;
      const modal = createModal({
          content: modalHtml.modalSettingsContent
      }, { closeOnClickModal: false });
      const tabsNav = modal.querySelector('.pdl-tabs-nav');
      const tabContent = modal.querySelector('.pdl-tabs-content');
      [
          createTabFilename({
              tabName: i18n('tabs_nav_filename'),
              paneHtml: modalHtml.modalSettingFilename
          }),
          createTabUgoria({
              tabName: i18n('tabs_nav_ugoria'),
              paneHtml: modalHtml.modalSettingUgoria
          }),
          createTabHistory({
              tabName: i18n('tabs_nav_history'),
              paneHtml: modalHtml.modalSettingHistory
          }),
          createTabOthers({
              tabName: i18n('tabs_nav_others'),
              paneHtml: modalHtml.modalSettingOthers
          }),
          createTabsPart({
              tabName: i18n('tabs_nav_donate'),
              paneHtml: modalHtml.modalSettingDonate
          })
      ].forEach(({ tab, pane }) => {
          tabsNav.appendChild(tab);
          tabContent.appendChild(pane);
      });
      const panes = Array.from(tabContent.querySelectorAll('.pdl-tab-pane'));
      panes.forEach((el) => {
          el.style.setProperty('display', 'none');
      });
      const activeBar = tabsNav.querySelector('.pdl-tabs__active-bar');
      const tabs = Array.from(modal.querySelectorAll('.pdl-tabs-nav .pdl-tab-item'));
      tabs.forEach((el) => {
          el.addEventListener('click', (ev) => {
              const tab = ev.currentTarget;
              if (!tab)
                  return;
              tabs.forEach((tab) => tab.classList.remove('active'));
              tab.classList.add('active');
              activeBar.style.width = getComputedStyle(tab).width;
              activeBar.style.transform = `translateX(${tab.offsetLeft + parseFloat(getComputedStyle(tab).paddingLeft)}px)`;
              panes.forEach((pane) => pane.style.setProperty('display', 'none'));
              panes[tabs.indexOf(tab)].style.removeProperty('display');
          });
      });
      tabs[0].classList.add('active');
      panes[0].style.removeProperty('display');
      document.body.appendChild(modal);
      activeBar.style.width = getComputedStyle(tabs[0]).width;
      activeBar.style.transform = `translateX(${tabs[0].offsetLeft + parseFloat(getComputedStyle(tabs[0]).paddingLeft)}px)`;
  }
  function getIllustId(node) {
      const isLinkToArtworksPage = regexp.artworksPage.exec(node.href);
      if (isLinkToArtworksPage) {
          if (node.getAttribute('data-gtm-value') ||
              node.classList.contains('gtm-illust-recommend-node-node') ||
              node.classList.contains('gtm-discover-user-recommend-node') ||
              node.classList.contains('work')) {
              return isLinkToArtworksPage[1];
          }
      }
      else {
          const isActivityThumb = regexp.activityHref.exec(node.href);
          if (isActivityThumb && node.classList.contains('work')) {
              return isActivityThumb[1];
          }
      }
      return '';
  }
  function createPdlBtn(attributes, textContent = '', { addEvent } = { addEvent: true }) {
      const ele = document.createElement('button');
      ele.textContent = textContent;
      if (!attributes)
          return ele;
      const { attrs, classList } = attributes;
      if (classList && classList.length > 0) {
          for (const cla of classList) {
              ele.classList.add(cla);
          }
      }
      if (attrs) {
          for (const key in attrs) {
              ele.setAttribute(key, attrs[key]);
          }
      }
      if (addEvent) {
          ele.addEventListener('click', (evt) => {
              evt.preventDefault();
              evt.stopPropagation();
              const ele = evt.currentTarget;
              if (!ele.classList.contains('pdl-progress')) {
                  handleDownload(ele, ele.getAttribute('pdl-id'));
              }
          });
      }
      return ele;
  }
  function createMainBtn(id) {
      if (document.querySelector('.pdl-btn-main'))
          return;
      const handleBar = document.querySelector('main section section');
      if (handleBar) {
          const pdlBtnWrap = handleBar.lastElementChild.cloneNode();
          const attrs = {
              attrs: { 'pdl-id': id },
              classList: ['pdl-btn', 'pdl-btn-main']
          };
          if (pixivHistory.has(id))
              attrs.classList.push('pdl-complete');
          pdlBtnWrap.appendChild(createPdlBtn(attrs));
          handleBar.appendChild(pdlBtnWrap);
      }
  }
  function createFilterEl(id, filterType, text) {
      const checkbox = document.createElement('input');
      const label = document.createElement('label');
      checkbox.id = id;
      checkbox.type = 'checkbox';
      checkbox.classList.add('pdl-checkbox');
      checkbox.setAttribute('category', String(filterType));
      checkbox.checked = settings.filter[filterType];
      label.setAttribute('for', id);
      label.setAttribute('category', String(filterType));
      label.textContent = text;
      checkbox.addEventListener('change', (evt) => {
          const checkbox = evt.currentTarget;
          const category = checkbox.getAttribute('category');
          upgradeSettings('filter', {
              ...settings.filter,
              [category]: checkbox.checked
          });
      });
      dlBarRef.filter[filterType] = checkbox;
      const wrap = document.createElement('div');
      wrap.classList.add('pdl-filter');
      wrap.appendChild(checkbox);
      wrap.appendChild(label);
      return wrap;
  }
  function createDownloadBar(userId) {
      const nav = document.querySelector('nav[class~="sc-192ftwf-0"]');
      if (!nav)
          return;
      const dlBtn = nav.querySelector('.pdl-btn-all');
      if (dlBtn) {
          if (dlBtn.getAttribute('pdl-userid') === userId)
              return;
          nav.querySelector('.pdl-nav-placeholder')?.remove();
          nav.querySelectorAll('button').forEach((btn) => btn.remove());
          document.querySelector('.pdl-wrap')?.remove();
      }
      const fragment = document.createDocumentFragment();
      const placeholder = document.createElement('div');
      placeholder.classList.add('pdl-nav-placeholder');
      dlBarRef.statusBar = fragment.appendChild(placeholder);
      const baseClasses = nav.querySelector('a:not([aria-current])').classList;
      dlBarRef.abortBtn = fragment.appendChild(createPdlBtn({
          attrs: { 'pdl-userId': userId },
          classList: [...baseClasses, 'pdl-stop', 'pdl-hide']
      }, i18n('stop'), { addEvent: false }));
      if (userId !== getSelfId()) {
          const hasWorks = ["a[href$='illustrations']", "a[href$='manga']"].some((selector) => !!nav.querySelector(selector));
          if (hasWorks) {
              const el = createPdlBtn({
                  attrs: { 'pdl-userid': userId },
                  classList: [...baseClasses, 'pdl-btn-all']
              }, i18n('dlbar_category_works'), { addEvent: false });
              el.addEventListener('click', downloadWorks);
              fragment.appendChild(el);
          }
          if (nav.querySelector("a[href*='bookmarks']")) {
              const el = createPdlBtn({
                  attrs: { 'pdl-userid': userId, category: 'bookmarks' },
                  classList: [...baseClasses, 'pdl-btn-all']
              }, i18n('bookmarks'), { addEvent: false });
              el.addEventListener('click', downloadBookmarksOrTags);
              fragment.appendChild(el);
          }
      }
      else {
          if (nav.querySelector("a[href*='bookmarks']")) {
              fragment.appendChild(createPdlBtn({
                  attrs: { 'pdl-userid': userId, category: 'bookmarks', rest: 'all' },
                  classList: [...baseClasses, 'pdl-btn-all']
              }, i18n('bookmarks'), { addEvent: false }));
              fragment.appendChild(createPdlBtn({
                  attrs: {
                      'pdl-userid': userId,
                      category: 'bookmarks',
                      rest: 'show'
                  },
                  classList: [...baseClasses, 'pdl-btn-all']
              }, i18n('bookmarks_public'), { addEvent: false }));
              fragment.appendChild(createPdlBtn({
                  attrs: {
                      'pdl-userid': userId,
                      category: 'bookmarks',
                      rest: 'hide'
                  },
                  classList: [...baseClasses, 'pdl-btn-all']
              }, i18n('bookmarks_private'), { addEvent: false }));
              fragment
                  .querySelectorAll('.pdl-btn-all')
                  .forEach((node) => {
                  node.addEventListener('click', downloadBookmarksOrTags);
              });
          }
      }
      const wrapper = document.createElement('div');
      wrapper.classList.add('pdl-wrap');
      wrapper.appendChild(createFilterEl('pdl-filter-exclude_downloaded', 'exclude_downloaded', i18n('dlbar_filter_exclude_downloaded')));
      wrapper.appendChild(createFilterEl('pdl-filter-illusts', IllustType.illusts, i18n('dlbar_filter_illusts')));
      wrapper.appendChild(createFilterEl('pdl-filter-manga', IllustType.manga, i18n('dlbar_filter_manga')));
      wrapper.appendChild(createFilterEl('pdl-filter-ugoria', IllustType.ugoira, i18n('dlbar_filter_ugoria')));
      nav.parentElement.insertBefore(wrapper, nav);
      nav.appendChild(fragment);
  }
  function createSubBtn(nodes) {
      const isBookmarkPage = regexp.bookmarkPage.test(location.pathname);
      nodes.forEach((e) => {
          if (e.childElementCount !== 0 &&
              !e.querySelector('.pdl-btn-sub')) {
              const illustId = getIllustId(e);
              if (illustId) {
                  const attrs = {
                      attrs: { 'pdl-id': illustId },
                      classList: ['pdl-btn', 'pdl-btn-sub']
                  };
                  if (pixivHistory.has(illustId))
                      attrs.classList.push('pdl-complete');
                  if (isBookmarkPage)
                      attrs.classList.push('pdl-btn-sub-bookmark');
                  e.appendChild(createPdlBtn(attrs));
              }
          }
      });
  }
  function createMultyWorksBtn(id) {
      const works = document.querySelectorAll("[role='presentation'] > a");
      if (works.length < 2)
          return;
      const containers = Array.from(works).map((node) => node.parentElement.parentElement);
      if (containers[0].querySelector('.pdl-btn'))
          return;
      containers.forEach((node, idx) => {
          const wrapper = document.createElement('div');
          wrapper.classList.add('pdl-wrap-artworks');
          const attrs = {
              attrs: { 'pdl-id': id, 'should-download': String(idx) },
              classList: ['pdl-btn', 'pdl-btn-sub', 'artworks']
          };
          wrapper.appendChild(createPdlBtn(attrs));
          node.appendChild(wrapper);
      });
  }
  const createPresentationBtn = (() => {
      let observer, btn;
      function cb(mutationList) {
          const newImg = mutationList[1]['addedNodes'][0];
          const [pageNum] = regexp.originSrcPageNum.exec(newImg.src) ?? [];
          if (!pageNum)
              throw new Error('[Error]Invalid Image Element.');
          btn?.setAttribute('should-download', String(pageNum));
      }
      return (id) => {
          const containers = document.querySelector("body > [role='presentation'] > div");
          if (!containers) {
              if (observer) {
                  observer.disconnect();
                  observer = null;
                  btn = null;
              }
              return;
          }
          if (containers.querySelector('.pdl-btn'))
              return;
          const img = containers.querySelector('img');
          if (!img)
              return;
          const isOriginImg = regexp.originSrcPageNum.exec(img.src);
          if (!isOriginImg)
              return;
          const [pageNum] = isOriginImg;
          const attrs = {
              attrs: { 'pdl-id': id, 'should-download': pageNum },
              classList: ['pdl-btn', 'pdl-btn-sub', 'presentation']
          };
          btn = createPdlBtn(attrs);
          containers.appendChild(btn);
          if (!img.parentElement)
              return;
          observer = new MutationObserver(cb);
          observer.observe(img.parentElement, { childList: true, subtree: true });
      };
  })();
  function createPreviewModalBtn() {
      const illustModalBtn = document.querySelectorAll('.gtm-manga-viewer-preview-modal-open');
      const mangaModalBtn = document.querySelectorAll('.gtm-manga-viewer-open-preview');
      const mangaViewerModalBtn = document.querySelectorAll('.gtm-manga-viewer-close-icon')?.[1];
      if (!illustModalBtn.length && !mangaModalBtn.length)
          return;
      const btns = [...illustModalBtn, ...mangaModalBtn];
      if (mangaViewerModalBtn)
          btns.push(mangaViewerModalBtn);
      btns.forEach((node) => {
          node.addEventListener('click', handleModalClick);
      });
  }
  function handleModalClick() {
      const timer = setInterval(() => {
          const ulList = document.querySelectorAll('ul');
          const previewList = ulList[ulList.length - 1];
          if (getComputedStyle(previewList).display !== 'grid')
              return;
          clearInterval(timer);
          const [, id] = regexp.artworksPage.exec(location.pathname) ?? [];
          previewList.childNodes.forEach((node, idx) => {
              node.style.position = 'relative';
              const attrs = {
                  attrs: { 'pdl-id': id, 'should-download': String(idx) },
                  classList: ['pdl-btn', 'pdl-btn-sub']
              };
              node.appendChild(createPdlBtn(attrs));
          });
      }, 300);
  }
  function createTagsBtn(userId, category) {
      const tagsEles = document.querySelectorAll('section> div:nth-child(2) > div > div');
      if (!tagsEles.length)
          return;
      let cate;
      if (category === 'illustrations' || category === 'artworks') {
          cate = 'illusts';
      }
      else {
          cate = category;
      }
      let rest = 'show';
      if (userId === getSelfId() &&
          category === 'bookmarks' &&
          location.search.includes('rest=hide'))
          rest = 'hide';
      tagsEles.forEach((ele) => {
          const tagBtn = ele.querySelector('.pdl-btn');
          if (tagBtn) {
              const btnRest = tagBtn.getAttribute('rest');
              if (rest !== btnRest)
                  tagBtn.setAttribute('rest', rest);
              return;
          }
          let tag;
          const tagLink = ele.querySelector('a');
          if (!tagLink)
              return;
          if (tagLink.getAttribute('status') !== 'active') {
              if (rest === 'hide') {
                  tag = tagLink.href.slice(tagLink.href.lastIndexOf('/') + 1, tagLink.href.lastIndexOf('?'));
              }
              else {
                  tag = tagLink.href.slice(tagLink.href.lastIndexOf('/') + 1);
              }
          }
          else {
              const tagTextEles = ele.querySelectorAll('div[title]');
              if (!tagTextEles.length)
                  return console.log('[Info]No Tags Element found.');
              tag = tagTextEles[tagTextEles.length - 1].getAttribute('title').slice(1);
          }
          const attrs = {
              attrs: { 'pdl-userId': userId, category, tag, rest },
              classList: ['pdl-btn', 'pdl-tag']
          };
          if (isDownloading)
              attrs.classList.push('pdl-tag-hide');
          const dlBtn = createPdlBtn(attrs, '', { addEvent: false });
          if (!(tagLink.href.includes('bookmarks') &&
              tagLink.getAttribute('status') !== 'active')) {
              dlBtn.style.backgroundColor = tagLink.getAttribute('color') + '80';
          }
          dlBtn.addEventListener('click', downloadBookmarksOrTags);
          ele.appendChild(dlBtn);
      });
      let modalTagsEles;
      let modal;
      if (category === 'bookmarks') {
          modal = document.querySelector('div[role="presentation"]');
          if (!modal)
              return;
          modalTagsEles = modal.querySelectorAll('a');
      }
      else {
          const charcoalTokens = document.querySelectorAll('.charcoal-token');
          modal = charcoalTokens[charcoalTokens.length - 1];
          if (!modal)
              return;
          modalTagsEles = modal.querySelectorAll('a');
      }
      if (!regexp.userPageTags.exec(modalTagsEles[0]?.href))
          return;
      modalTagsEles.forEach((ele) => {
          if (ele.querySelector('.pdl-btn'))
              return;
          let tag;
          if (rest === 'hide') {
              tag = ele.href.slice(ele.href.lastIndexOf('/') + 1, ele.href.lastIndexOf('?'));
          }
          else {
              tag = ele.href.slice(ele.href.lastIndexOf('/') + 1);
          }
          const attrs = {
              attrs: { 'pdl-userId': userId, category: cate, tag, rest },
              classList: ['pdl-btn', 'pdl-modal-tag']
          };
          if (isDownloading)
              attrs.classList.push('pdl-tag-hide');
          const dlBtn = createPdlBtn(attrs, '', { addEvent: false });
          dlBtn.addEventListener('click', (evt) => {
              modal.querySelector('svg').parentElement.click();
              downloadBookmarksOrTags(evt);
          });
          ele.appendChild(dlBtn);
      });
  }
  function compatPixivPreviewer(nodes) {
      const isPpSearchPage = regexp.ppSearchPage.test(location.pathname);
      if (!isPpSearchPage)
          return;
      nodes.forEach((node) => {
          const pdlEle = node.querySelector('.pdl-btn');
          if (!pdlEle)
              return false;
          pdlEle.remove();
      });
  }
  let firstRun = true;
  function observerCallback(records) {
      const addedNodes = [];
      records.forEach((record) => {
          if (!record.addedNodes.length)
              return;
          record.addedNodes.forEach((node) => {
              if (node.nodeType === Node.ELEMENT_NODE &&
                  node.tagName !== 'BUTTON' &&
                  node.tagName !== 'IMG') {
                  addedNodes.push(node);
              }
          });
      });
      if (!addedNodes.length) {
          return;
      }
      if (firstRun) {
          createSubBtn(document.querySelectorAll('a'));
          firstRun = false;
      }
      else {
          compatPixivPreviewer(addedNodes);
          const thunmnails = addedNodes.reduce((prev, current) => {
              return prev.concat(Array.from(current.querySelectorAll('a')));
          }, []);
          createSubBtn(thunmnails);
      }
      const isArtworksPage = regexp.artworksPage.exec(location.pathname);
      const isUserPage = regexp.userPage.exec(location.pathname);
      const isTagsPage = regexp.userPageTags.exec(location.pathname);
      if (isArtworksPage) {
          const id = isArtworksPage[1];
          createMainBtn(id);
          createMultyWorksBtn(id);
          createPresentationBtn(id);
          createPreviewModalBtn();
      }
      else if (isUserPage) {
          createDownloadBar(isUserPage[1]);
          if (isTagsPage) {
              createTagsBtn(isUserPage[1], isTagsPage[1]);
          }
      }
  }

  pixivHistory.updateHistory();
  GM_registerMenuCommand(i18n('gm_menu_setting'), showSettings, 's');
  initial()
      .then(() => {
      if (settings.showMsg) {
          showUpgradeMsg();
          upgradeSettings('showMsg', false);
      }
      new MutationObserver(observerCallback).observe(document.body, {
          childList: true,
          subtree: true
      });
      document.addEventListener('keydown', (e) => {
          if (e.ctrlKey && e.key === 'q') {
              const pdlMainBtn = document.querySelector('.pdl-btn-main');
              if (pdlMainBtn) {
                  e.preventDefault();
                  if (!e.repeat) {
                      pdlMainBtn.dispatchEvent(new MouseEvent('click'));
                  }
              }
          }
      });
  })
      .catch(console.error);

})(JSZip, GIF, workerChunk);