Lcandy / 云展网PDF下载 YunZhanDownloader

// ==UserScript==
// @name         云展网PDF下载 YunZhanDownloader
// @namespace    https://github.com/lcandy2/YunZhanDownloader
// @version      1.1
// @author       lcandy2
// @description  从云展网下载PDF书籍
// @license      MIT
// @icon         https://book.yunzhan365.com/favicon.ico
// @match        *://book.yunzhan365.com/*/*/mobile/*
// @require      https://unpkg.com/jspdf@2.5.1/dist/jspdf.umd.min.js
// @require      https://cdn.jsdelivr.net/npm/jquery@3.7.1/dist/jquery.min.js
// @run-at       document-end
// ==/UserScript==

(function ($, jspdf) {
  'use strict';

  const getFliphtml5Pages = async () => {
    try {
      const scriptTag = document.querySelector('script[src*="javascript/config.js"]');
      if (!scriptTag) {
        throw new Error("Script tag not found");
      }
      const src = scriptTag.getAttribute("src");
      const response = await fetch(src);
      const scriptContent = await response.text();
      const configMatch = scriptContent.match(/var htmlConfig = ({.*});/);
      if (!configMatch || !configMatch[1]) {
        throw new Error("htmlConfig not found in the script");
      }
      const configObject = JSON.parse(configMatch[1]);
      const fliphtml5_pages = configObject.fliphtml5_pages;
      return fliphtml5_pages;
    } catch (error) {
      console.error("Error:", error.message);
    }
  };
  const getImageData = async (url) => {
    const response = await fetch(url);
    const blob = await response.blob();
    return new Promise((resolve, reject) => {
      const reader = new FileReader();
      reader.onload = () => resolve(reader.result);
      reader.onerror = reject;
      reader.readAsDataURL(blob);
    });
  };
  const addDownloadBtn = (listener) => {
    let observer;
    const checkAndProcessButtonBar = () => {
      const $buttonBar = $("div.fbTopBar div.buttonBar");
      const buttonBarAvailable = $buttonBar.length;
      const $shareTitle = $("div.share_form p.form_title");
      const $shareCloseBtn = $("div.share_form img.close");
      const $shareContent = $("div.share_form div.share_content");
      const shareFormAvailable = $shareTitle.length && $shareCloseBtn.length && $shareContent.length;
      if (buttonBarAvailable && shareFormAvailable) {
        observer.disconnect();
        console.log("Found buttonBar:", $buttonBar);
        const shareElement = $buttonBar.children().eq(8);
        const $element = $(`<p id="dl-dialog-btn" style="pointer-events: none; font-size: 12px; color: rgb(255, 255, 255);">下载</p>`);
        shareElement.html($element);
        $shareTitle.html("下载");
        $shareCloseBtn.remove();
        const downloadHtml = $(`<h1>下载《${$("title").text()}》</h1
            <br><br>
            <h2 id="dl-progress">未开始下载</h1>
            <br><br>
            <button id="dl-btn" style="width: 100%; height: 50px; font-size: 20px; background-color: #4CAF50; color: white; border: none; border-radius: 5px; cursor: pointer;">下载</button>`);
        $shareContent.html(downloadHtml);
        $("#dl-btn").on("click", listener);
        return shareElement;
      }
    };
    const observeDOM = (execute) => {
      observer = new MutationObserver((mutations) => {
        for (let mutation of mutations) {
          if (mutation.addedNodes.length) {
            execute();
            break;
          }
        }
      });
      observer.observe(document.body, { childList: true, subtree: true });
    };
    observeDOM(checkAndProcessButtonBar);
  };
  const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
  const pdf = new jspdf.jsPDF();
  const ConvertToPDF = async (imagePromises) => {
    const $dlprogress = $("#dl-progress");
    const $dlbtn = $("#dl-btn");
    $dlbtn.html("下载中...");
    $dlbtn.prop("disabled", true);
    $dlbtn.css("background-color", "#808080");
    $dlbtn.css("cursor", "not-allowed");
    $dlprogress.html("准备下载...");
    const title2 = $("title").text();
    try {
      const images = await Promise.all(imagePromises);
      for (let index = 0; index < images.length; index++) {
        const progress = `下载中:${index + 1} / ${images.length} 页`;
        $dlprogress.html(progress);
        console.log(`Downloading: ${progress}`);
        if (index > 0)
          pdf.addPage();
        pdf.addImage(images[index], "WEBP", 0, 0, pdf.internal.pageSize.getWidth(), pdf.internal.pageSize.getHeight());
        await delay(1e3);
      }
    } catch (error) {
      console.error("Error processing images:", error);
    }
    $dlbtn.html("下载 PDF");
    $dlbtn.prop("disabled", false);
    $dlbtn.css("background-color", "#007bff");
    $dlbtn.css("cursor", "pointer");
    $dlprogress.html("下载完成");
    console.log(`Downloading: ${title2}.pdf`);
    DownloadConvertedPDF();
    $dlbtn.off("click").on("click", DownloadConvertedPDF);
  };
  const DownloadConvertedPDF = async () => {
    pdf.save(`${title}.pdf`);
  };
  $(async () => {
    const fliphtml5Pages = await getFliphtml5Pages();
    if (!fliphtml5Pages) {
      console.error("fliphtml5Pages not found");
      return;
    }
    const imagePromises = fliphtml5Pages.map((page) => getImageData(page.n[0]));
    const convertToPDF = () => ConvertToPDF(imagePromises);
    addDownloadBtn(convertToPDF);
  });

})(jQuery, jspdf);