Viewer for XiuRen22

秀人相册查看器:列表页快速预览,详情页用 Viewer.js 浏览全部图片

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

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

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

// ==UserScript==
// @name         Viewer for XiuRen22
// @namespace    http://tampermonkey.net/XiuRen22
// @version      1.0.0
// @description  秀人相册查看器:列表页快速预览,详情页用 Viewer.js 浏览全部图片
// @author       You
// @include      *://*.xx05.my/*
// @include      *://*xiuren*
// @include      *://*xr*
// @include      *://*.*/*XiuRen*
// @icon         https://www.google.com/s2/favicons?domain=xiurenb.net
// @grant        GM_xmlhttpRequest
// @grant        GM_addStyle
// @grant        GM_getResourceText
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_listValues
// @grant        GM_deleteValue
// @require      https://cdnjs.cloudflare.com/ajax/libs/viewerjs/1.11.8/viewer.min.js
// @resource     viewerCSS   https://cdnjs.cloudflare.com/ajax/libs/viewerjs/1.11.8/viewer.min.css
// ==/UserScript==

(() => {
  // src/shared/constants.js
  var API_BASE_URL = "http://de.ik5.cc/06/";
  var DEBUG_INFO = true;
  var DEBUG_VERBOSE = true;
  var CACHE_TTL = 7 * 24 * 60 * 60 * 1e3;
  var VIEWER_OPTIONS = {
    fullscreen: false,
    interval: 1200,
    loop: false,
    transition: false
  };

  // src/shared/debug.js
  function debugInfo(...data) {
    if (DEBUG_INFO) console.log("[INFO]", ...data);
  }
  function debugVerbose(...data) {
    if (DEBUG_VERBOSE) console.log("[VERBOSE]", ...data);
  }

  // src/shared/img.js
  function fixImgSrc(src) {
    if (!src || src.indexOf("/uploadfile/") === -1) return src;
    const parts = src.split("/uploadfile/");
    return parts.length > 1 ? API_BASE_URL + "Uploadfile/" + parts[1] : src;
  }
  function fixImgNode(img) {
    const raw = img.getAttribute("src");
    const fixed = fixImgSrc(raw);
    if (raw === fixed) return;
    img.setAttribute("src", fixed);
    const origin = img.getAttribute("data-original");
    if (origin) img.setAttribute("data-original", fixImgSrc(origin));
  }
  function fixPageImages(root) {
    for (const img of root.getElementsByTagName("img")) fixImgNode(img);
  }
  function imageParagraphNode(rootDoc) {
    for (const img of rootDoc.getElementsByTagName("img")) {
      const src = img.getAttribute("src");
      if (!src) continue;
      if (src.indexOf("/uploadfile/2") !== -1 || src.indexOf("Uploadfile/2") !== -1) {
        return img.parentNode;
      }
    }
    return null;
  }

  // src/shared/cache.js
  function createCache(prefix, ttl) {
    const memCache = {};
    function get(url) {
      const key = prefix + url;
      if (memCache[key]) return memCache[key];
      let entry = null;
      try {
        const raw = GM_getValue(key, null);
        if (Array.isArray(raw)) {
          entry = { urls: raw, pageCount: 1, time: Date.now() };
          try {
            GM_setValue(key, entry);
          } catch (e) {
          }
        } else if (raw && Array.isArray(raw.urls)) {
          entry = raw;
        }
      } catch (e) {
        return null;
      }
      if (!entry || Date.now() - entry.time > ttl) {
        delete memCache[key];
        try {
          GM_deleteValue(key);
        } catch (e) {
        }
        return null;
      }
      memCache[key] = entry;
      return entry;
    }
    function set(url, urls, pageCount) {
      const key = prefix + url;
      const entry = { urls, pageCount: pageCount || 1, time: Date.now() };
      memCache[key] = entry;
      try {
        GM_setValue(key, entry);
      } catch (e) {
        console.warn("\u6301\u4E45\u7F13\u5B58\u5199\u5165\u5931\u8D25\uFF0C\u6E05\u7406\u8FC7\u671F\u7F13\u5B58\u540E\u91CD\u8BD5", e);
        purge();
        try {
          GM_setValue(key, entry);
        } catch (e2) {
        }
      }
    }
    function purge() {
      const now = Date.now();
      try {
        GM_listValues().forEach((key) => {
          if (key.indexOf(prefix) !== 0) return;
          const raw = GM_getValue(key, null);
          if (!raw || Array.isArray(raw)) return;
          if (!raw.time || now - raw.time > ttl) {
            GM_deleteValue(key);
          }
        });
      } catch (e) {
      }
    }
    return { get, set, purge };
  }

  // src/shared/page.js
  function fetchPageHtml(url) {
    return new Promise((resolve, reject) => {
      GM_xmlhttpRequest({
        url,
        method: "GET",
        onload: (xhr) => resolve(xhr.response),
        onerror: reject
      });
    });
  }
  function parseDetailPage(html) {
    const doc = new DOMParser().parseFromString(html, "text/html");
    const node = imageParagraphNode(doc);
    const urls = node ? Array.from(node.getElementsByTagName("img")).map((img) => img.getAttribute("src")).filter(Boolean).map(fixImgSrc) : [];
    const current = doc.getElementsByClassName("current")[0];
    const indicator = current ? current.parentNode : null;
    let pageCount = 1;
    if (indicator) {
      indicator.querySelectorAll("a").forEach((a) => {
        const m = a.textContent.trim().match(/^(\d+)$/);
        if (m) pageCount = Math.max(pageCount, parseInt(m[1], 10));
        const h = (a.getAttribute("href") || "").match(/_(\d+)\.html$/);
        if (h) pageCount = Math.max(pageCount, parseInt(h[1], 10) + 1);
      });
    }
    return { urls, pageCount };
  }
  function parseImageUrlsFromHtml(html) {
    return parseDetailPage(html).urls;
  }
  function createImageLoader(cache2) {
    async function fetchPageImageUrls(url) {
      const cached = cache2.get(url);
      if (cached) {
        debugVerbose(`\u7F13\u5B58\u547D\u4E2D: ${url} (${cached.urls.length} \u5F20)`);
        return cached.urls;
      }
      debugInfo("\u91C7\u96C6\u56FE\u7247\u81EA", url);
      const html = await fetchPageHtml(url);
      const urls = parseImageUrlsFromHtml(html);
      debugVerbose(`\u89E3\u6790\u5230 ${urls.length} \u5F20\u56FE\u7247: ${url}`);
      if (urls.length > 0) cache2.set(url, urls, 1);
      return urls;
    }
    async function loadSubPageUrls(baseUrl, pageCount) {
      if (pageCount <= 1) {
        const all = [];
        let i = 1;
        while (true) {
          const urls = await fetchPageImageUrls(baseUrl + "_" + i + ".html");
          if (urls.length === 0) break;
          all.push(...urls);
          i++;
        }
        return all;
      }
      const tasks = [];
      for (let i = 1; i < pageCount; i++) {
        tasks.push(fetchPageImageUrls(baseUrl + "_" + i + ".html"));
      }
      const results = await Promise.allSettled(tasks);
      return results.filter((r) => r.status === "fulfilled").map((r) => r.value).flat();
    }
    return { fetchPageImageUrls, loadSubPageUrls };
  }

  // src/shared/preview.js
  async function openGalleryPreview(btn, galleryUrl, { cache: cache2, loader: loader2 }) {
    const originalText = btn.innerText;
    btn.disabled = true;
    btn.innerText = "...";
    debugInfo("\u5F00\u59CB\u5FEB\u901F\u9884\u89C8:", galleryUrl);
    try {
      let firstUrls;
      let pageCount;
      const cached = cache2.get(galleryUrl);
      if (cached && cached.urls.length > 0) {
        firstUrls = cached.urls;
        pageCount = cached.pageCount;
        debugVerbose(`\u7F13\u5B58\u547D\u4E2D: ${galleryUrl} (${firstUrls.length} \u5F20)`);
      } else {
        const html = await fetchPageHtml(galleryUrl);
        const data = parseDetailPage(html);
        firstUrls = data.urls;
        pageCount = data.pageCount;
        if (firstUrls.length === 0) {
          alert("\u672A\u627E\u5230\u56FE\u7247");
          return;
        }
        cache2.set(galleryUrl, firstUrls, pageCount);
      }
      const ul = document.createElement("ul");
      ul.style.display = "none";
      document.body.appendChild(ul);
      const addImgs = (urls) => {
        for (const url of urls) {
          const img = document.createElement("img");
          img.src = url;
          img.style.maxHeight = "1080px";
          ul.appendChild(img);
        }
      };
      addImgs(firstUrls);
      let closed = false;
      let playerObserver = null;
      const viewer = new Viewer(ul, {
        ...VIEWER_OPTIONS,
        title: false,
        hidden() {
          closed = true;
          if (playerObserver) playerObserver.disconnect();
          viewer.destroy();
          ul.remove();
          btn.innerText = originalText;
        }
      });
      viewer.show();
      if (viewer.player) {
        playerObserver = new MutationObserver(() => {
          if (!viewer.played) return;
          const imgs = viewer.player.querySelectorAll("img");
          const last = imgs[imgs.length - 1];
          if (last && last.classList.contains("viewer-in")) {
            setTimeout(() => {
              if (viewer.played) viewer.stop();
            }, Math.max(50, viewer.options.interval - 100));
          }
        });
        playerObserver.observe(viewer.player, {
          attributes: true,
          subtree: true,
          attributeFilter: ["class"]
        });
      }
      const baseUrl = galleryUrl.replace(".html", "");
      const extraUrls = await loader2.loadSubPageUrls(baseUrl, pageCount);
      if (!closed && extraUrls.length > 0) {
        addImgs(extraUrls);
        viewer.update();
      }
      debugInfo("\u5FEB\u901F\u9884\u89C8\u5171\u52A0\u8F7D", ul.children.length, "\u5F20\u56FE\u7247");
    } catch (e) {
      console.error("Quick Preview Error:", e);
      btn.innerText = "Err";
      setTimeout(() => {
        btn.innerText = originalText;
      }, 2e3);
    } finally {
      btn.disabled = false;
    }
  }

  // src/shared/random.js
  function createRandomPlayButton({ getUrls, cache: cache2, loader: loader2 }) {
    const btn = document.createElement("button");
    btn.className = "xr-random-play";
    btn.title = "\u968F\u673A\u64AD\u653E\u5F53\u524D\u7011\u5E03\u6D41\u4E2D\u7684\u4E00\u4E2A\u76F8\u518C";
    btn.textContent = "\u968F\u673A\u64AD\u653E";
    btn.addEventListener("click", () => {
      const urls = getUrls();
      if (urls.length === 0) {
        alert("\u6682\u65E0\u53EF\u64AD\u653E\u7684\u76F8\u518C");
        return;
      }
      const url = urls[Math.floor(Math.random() * urls.length)];
      debugInfo("\u968F\u673A\u64AD\u653E:", url);
      openGalleryPreview(btn, url, { cache: cache2, loader: loader2 });
    });
    document.body.appendChild(btn);
    return btn;
  }

  // src/shared/styles.js
  function addSharedStyles() {
    GM_addStyle(GM_getResourceText("viewerCSS"));
    GM_addStyle(`
    .xiuren-preview-btn {
      position: absolute;
      top: 5px;
      right: 5px;
      z-index: 1000;
      padding: 4px 8px;
      font-size: 12px;
      opacity: 0.9;
      display: inline-block;
      background-color: #e91e63;
      color: #fff;
      border-radius: 4px;
      cursor: pointer;
      font-weight: bold;
      border: none;
      transition: background-color 0.3s;
      line-height: normal;
    }
    .xiuren-preview-btn:hover {
      background-color: #c2185b;
    }
    .xiuren-preview-btn:disabled {
      background-color: #ccc;
      cursor: not-allowed;
    }
    /* \u7F29\u7565\u56FE\u5DE6\u4E0B\u89D2\u65E5\u671F\u5FBD\u6807 */
    .xr-s-date {
      position: absolute;
      left: 5px;
      bottom: 5px;
      z-index: 1;
      padding: 2px 6px;
      font-size: 11px;
      line-height: 1.2;
      color: #fff;
      background: rgba(0, 0, 0, 0.55);
      border-radius: 3px;
      pointer-events: none;
    }
    /* \u53F3\u4E0B\u89D2\u60AC\u6D6E\u7684\u968F\u673A\u64AD\u653E\u6309\u94AE */
    .xr-random-play {
      position: fixed;
      right: 20px;
      bottom: 20px;
      z-index: 9999;
      padding: 10px 16px;
      font-size: 14px;
      font-weight: bold;
      color: #fff;
      background: #e91e63;
      border: none;
      border-radius: 20px;
      cursor: pointer;
      box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
    }
    .xr-random-play:hover {
      background: #c2185b;
    }
    .xr-random-play:disabled {
      background: #ccc;
      cursor: not-allowed;
    }
  `);
  }

  // src/shared/infinite.js
  function setupInfiniteScroll({ loadPage }) {
    let loading = false;
    let stopped = false;
    async function maybeLoad() {
      if (loading || stopped) return;
      if (window.innerHeight + window.scrollY < document.documentElement.scrollHeight - 600) {
        return;
      }
      loading = true;
      let more = false;
      try {
        more = await loadPage();
      } catch (e) {
        more = false;
      }
      loading = false;
      if (!more) {
        stopped = true;
        return;
      }
      maybeLoad();
    }
    let ticking = false;
    const onScroll = () => {
      if (ticking) return;
      ticking = true;
      requestAnimationFrame(() => {
        ticking = false;
        maybeLoad();
      });
    };
    window.addEventListener("scroll", onScroll);
    window.addEventListener("resize", onScroll);
    maybeLoad();
  }

  // src/shared/util.js
  function extractPostId(url) {
    const m = url.match(/(\d+)\.html/);
    return m ? m[1] : null;
  }

  // src/main.js
  var CACHE_PREFIX = "xr_cache_";
  var LIST_ITEM_SELECTOR = ".update_area_lists .i_list";
  var LIST_CONTAINER_SELECTOR = "ul.update_area_lists";
  var cache = createCache(CACHE_PREFIX, CACHE_TTL);
  var loader = createImageLoader(cache);
  addSharedStyles();
  GM_addStyle(`
  /* \u5217\u8868\u9875\u5361\u7247\u6837\u5F0F\u7EDF\u4E00\u4E3A\u641C\u7D22\u9875\u98CE\u683C\uFF1A\u54CD\u5E94\u5F0F\u7EA6 5 \u5217\u5706\u89D2\u5361\u7247 */
  ul.update_area_lists {
    display: grid !important;
    grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
    gap: 12px;
    padding: 12px;
    margin: 0;
    list-style: none;
  }
  ul.update_area_lists > .i_list {
    position: relative;
    overflow: hidden;
    border-radius: 6px;
    background: #fff;
    box-shadow: 0 1px 4px rgba(0, 0, 0, 0.12);
    margin: 0 !important;
    width: auto !important;
  }
  ul.update_area_lists .i_list > a {
    display: block;
    position: relative;
  }
  ul.update_area_lists .i_list img {
    width: 100%;
    aspect-ratio: 3 / 4;
    object-fit: cover;
    display: block !important;
  }
  ul.update_area_lists .case_info {
    padding: 0;
  }
  ul.update_area_lists .case_info .meta-title {
    padding: 6px 8px;
    font-size: 12px;
    line-height: 17px;
    color: #333;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
  }
  ul.update_area_lists .case_info .meta-post {
    display: none;
  }
  /* \u6A21\u7279\u540D\u505A\u6210\u5C0F\u5FBD\u6807\uFF0C\u653E\u5728\u7F29\u7565\u56FE\u53F3\u4E0B\u89D2\uFF08\u4E0E\u5DE6\u4E0B\u89D2\u65E5\u671F\u5FBD\u6807\u5BF9\u79F0\uFF09 */
  ul.update_area_lists .postlist-imagenum {
    position: absolute;
    right: 5px;
    bottom: 5px;
    left: auto;
    top: auto;
    z-index: 1;
    width: auto !important;
    max-width: calc(100% - 10px);
    padding: 2px 6px;
    background: rgba(0, 0, 0, 0.55);
    border-radius: 3px;
  }
  .postlist-imagenum span {
    color: #fff !important;
    cursor: pointer;
  }
  .postlist-imagenum span:hover {
    text-decoration: underline;
  }
`);
  function scanListItems() {
    document.querySelectorAll(LIST_ITEM_SELECTOR).forEach((item) => {
      if (item.dataset.xiurenPreviewDone) return;
      item.dataset.xiurenPreviewDone = "true";
      const link = item.querySelector('a[href*=".html"]');
      if (!link) return;
      const href = link.getAttribute("href");
      if (!extractPostId(href)) return;
      item.style.position = "relative";
      const meta = item.querySelector(".case_info .meta-post");
      const dateMatch = meta ? meta.textContent.match(/(\d{4})\.(\d{1,2})\.(\d{1,2})/) : null;
      if (dateMatch) {
        const badge = document.createElement("span");
        badge.className = "xr-s-date";
        badge.textContent = `${dateMatch[1].slice(-2)}.${parseInt(dateMatch[2], 10)}.${parseInt(dateMatch[3], 10)}`;
        link.appendChild(badge);
      }
      const btn = document.createElement("button");
      btn.className = "xiuren-preview-btn";
      btn.innerText = "\u25B6";
      btn.title = "Quick Preview";
      btn.addEventListener("click", (e) => {
        e.preventDefault();
        e.stopPropagation();
        openGalleryPreview(btn, href, { cache, loader });
      });
      item.appendChild(btn);
      link.addEventListener("click", (e) => {
        e.preventDefault();
        e.stopPropagation();
        openGalleryPreview(btn, href, { cache, loader });
      });
      const modelSpan = item.querySelector(".postlist-imagenum span");
      if (modelSpan) {
        modelSpan.addEventListener("click", (e) => {
          e.preventDefault();
          e.stopPropagation();
          const name = modelSpan.textContent.trim();
          if (!name) return;
          window.open(
            location.origin + "/plus/search/index.asp?keyword=" + encodeURIComponent(name) + "&searchtype=titlekeywords",
            "_blank"
          );
        });
      }
    });
  }
  var infiniteScroll = {
    current: 1,
    // 已加载到的页码(首页为 1)
    total: 1,
    // 总页数
    finished: false
  };
  function detectPagination() {
    const m = location.pathname.match(/index(\d+)\.html$/);
    infiniteScroll.current = m ? parseInt(m[1], 10) : 1;
    let max = 1;
    document.querySelectorAll('.page a[href*="index"]').forEach((a) => {
      const n = a.getAttribute("href").match(/index(\d+)\.html/);
      if (n) max = Math.max(max, parseInt(n[1], 10));
    });
    infiniteScroll.total = max;
    infiniteScroll.finished = infiniteScroll.current >= max;
  }
  function nextPageUrl() {
    const base = location.pathname.replace(/index\d+\.html$/, "").replace(/\/+$/, "") + "/";
    return location.origin + base + `index${infiniteScroll.current + 1}.html`;
  }
  function parseListItems(html) {
    const doc = new DOMParser().parseFromString(html, "text/html");
    return doc.querySelectorAll(LIST_ITEM_SELECTOR);
  }
  function appendListPage(items) {
    const container = document.querySelector(LIST_CONTAINER_SELECTOR);
    if (!container || items.length === 0) return false;
    items.forEach((li) => {
      const node = document.importNode(li, true);
      fixPageImages(node);
      container.appendChild(node);
    });
    return true;
  }
  function loadNextListPage() {
    return new Promise((resolve) => {
      if (infiniteScroll.finished) {
        resolve(false);
        return;
      }
      const url = nextPageUrl();
      debugInfo("\u7011\u5E03\u6D41\u52A0\u8F7D\u7B2C", infiniteScroll.current + 1, "\u9875:", url);
      GM_xmlhttpRequest({
        url,
        method: "GET",
        onload: (xhr) => {
          const items = parseListItems(xhr.response);
          let more = false;
          if (appendListPage(items)) {
            infiniteScroll.current++;
            debugInfo("\u5DF2\u8FFD\u52A0\u7B2C", infiniteScroll.current, "\u9875\uFF0C\u5171", items.length, "\u4E2A\u76F8\u518C");
            scanListItems();
            more = true;
          } else {
            infiniteScroll.finished = true;
            debugInfo("\u7011\u5E03\u6D41\u7ED3\u675F\uFF1A\u8BE5\u9875\u65E0\u66F4\u591A\u76F8\u518C");
          }
          if (infiniteScroll.current >= infiniteScroll.total) {
            infiniteScroll.finished = true;
            debugInfo("\u7011\u5E03\u6D41\u7ED3\u675F\uFF1A\u5DF2\u52A0\u8F7D\u5168\u90E8\u9875\u9762");
          }
          resolve(more);
        },
        onerror: () => {
          console.error("XiuRen: \u7011\u5E03\u6D41\u52A0\u8F7D\u5931\u8D25", url);
          infiniteScroll.finished = true;
          resolve(false);
        }
      });
    });
  }
  function initListPage() {
    scanListItems();
    new MutationObserver(scanListItems).observe(document.body, {
      childList: true,
      subtree: true
    });
    createRandomPlayButton({
      getUrls() {
        const urls = [];
        document.querySelectorAll(LIST_ITEM_SELECTOR).forEach((item) => {
          const link = item.querySelector('a[href*=".html"]');
          const href = link ? link.getAttribute("href") : null;
          if (href && extractPostId(href)) urls.push(href);
        });
        return urls;
      },
      cache,
      loader
    });
    detectPagination();
    if (infiniteScroll.finished) return;
    setupInfiniteScroll({ loadPage: loadNextListPage });
  }
  function getPageImages(i, htmlDoc) {
    const node = imageParagraphNode(htmlDoc);
    if (!node) return [];
    const template = document.createElement("div");
    template.innerHTML = `<div class='child${i}'>${node.outerHTML}</div>`;
    return Array.from(template.firstChild.getElementsByTagName("img"));
  }
  function getPageNumbers() {
    const current = document.getElementsByClassName("current")[0];
    const indicator = current ? current.parentNode : null;
    if (!indicator) return [];
    const nums = [];
    indicator.querySelectorAll("a").forEach((a) => {
      const m = a.textContent.trim().match(/^(\d+)$/);
      if (m) nums.push(parseInt(m[1], 10));
    });
    return nums;
  }
  function buildPageUrls(href, pageNumbers) {
    const urls = [href];
    for (const num of pageNumbers) {
      const page = parseInt(num, 10);
      if (!page || page - 1 === 0) continue;
      urls.push(href.slice(0, href.lastIndexOf(".")) + `_${page - 1}.html`);
    }
    return urls;
  }
  function initGalleryPage(href) {
    const paragraph = imageParagraphNode(document);
    if (!paragraph) return;
    const container = paragraph.parentNode;
    const viewerUl = document.createElement("ul");
    viewerUl.id = "imagesViewer";
    container.appendChild(viewerUl);
    const gallery = new Viewer(viewerUl, VIEWER_OPTIONS);
    paragraph.remove();
    const pageUrls = buildPageUrls(href, getPageNumbers());
    const pagesMap = {};
    function updateImgSet(page, imgSet) {
      pagesMap[page] = imgSet;
      debugVerbose(
        `update_img_set: \u52A0\u8F7D\u9875\u9762 ${page + 1} / ${pageUrls.length}, \u5171\u8BA1 ${imgSet.length} \u5F20\u56FE\u7247`
      );
      if (Object.keys(pagesMap).length === pageUrls.length) {
        debugInfo("\u6240\u6709\u9875\u9762\u7684\u4FE1\u606F\u5DF2\u7ECF\u52A0\u8F7D\u5230 pages_map \u4E2D");
        let total = 0;
        for (let i = 0; i < pageUrls.length; i++) {
          const imgs = pagesMap[i];
          if (!imgs) continue;
          let pageCount = 0;
          for (const img of imgs) {
            img.style.maxHeight = "1080px";
            fixImgNode(img);
            viewerUl.appendChild(img);
            total++;
            pageCount++;
          }
          debugInfo(i, "\u9875", pageCount, "\u5F20");
        }
        debugInfo("\u5171\u8BA1", total, "\u5F20");
      }
      gallery.update();
    }
    pageUrls.forEach((url, i) => {
      debugInfo("\u91C7\u96C6\u56FE\u7247\u81EA", url);
      GM_xmlhttpRequest({
        url,
        method: "GET",
        onload: (xhr) => {
          const doc = new DOMParser().parseFromString(xhr.response, "text/html");
          updateImgSet(i, getPageImages(i, doc));
        }
      });
    });
  }
  function init() {
    cache.purge();
    fixPageImages(document);
    const href = window.location.href;
    debugInfo("handle: " + href);
    if (href.indexOf("_") !== -1) return;
    if (document.querySelector(".update_area_lists")) {
      debugInfo("\u68C0\u6D4B\u5230\u5217\u8868\u9875\uFF0C\u542F\u7528\u9884\u89C8\u6309\u94AE");
      initListPage();
      return;
    }
    initGalleryPage(href);
  }
  init();
})();