Download screenshots from all pages from a Doc88 file in one PDF
// ==UserScript==
// @name Doc88Downloader
// @namespace https://github.com/lomexicano/Doc88Downloader
// @version 2.1
// @description Download screenshots from all pages from a Doc88 file in one PDF
// @author lomexicano
// @match https://www.doc88.com/*
// @require https://cdnjs.cloudflare.com/ajax/libs/pdf-lib/1.11.1/pdf-lib.js
// @license MIT
// ==/UserScript==
// Function to extract the page title from the HTML
function extractPageTitle() {
const pageTitleElement = document.querySelector('h1[title]');
if (pageTitleElement) {
return pageTitleElement.getAttribute('title').trim();
}
return 'merged_pages'; // Default title if not found
}
// Function to download the current page's "page_i" elements in a PDF file;
async function downloadPagesAsPDF(from, to) {
'use strict';
const pageTitle = extractPageTitle();
console.log('Page title: '+pageTitle);
const pdfDoc = await window.PDFLib.PDFDocument.create();
for (let i = from; i <= to; i++) {
ongoingProcess = true;
if (cancelProcess) {
break;
}
const pageCanvas = document.getElementById('page_' + i);
if (pageCanvas === null) {
console.log('OK! All pages were added to the PDF...');
break;
}
console.log('Adding page '+i+' to the PDF...');
const blob = await new Promise((resolve) => {
pageCanvas.toBlob((blob) => resolve(blob));
});
// Convert the blob to a Uint8Array
const arrayBuffer = await blob.arrayBuffer();
const uint8Array = new Uint8Array(arrayBuffer);
const image = await pdfDoc.embedPng(uint8Array);
// Add a new page with the same dimensions as the image
const page = pdfDoc.addPage([image.width, image.height]);
// Draw the image onto the page
const pageWidth = page.getWidth();
const pageHeight = page.getHeight();
const imageWidth = image.width;
const imageHeight = image.height;
const scaleFactor = Math.min(pageWidth / imageWidth, pageHeight / imageHeight);
page.drawImage(image, {
x: 0,
y: 0,
width: imageWidth * scaleFactor,
height: imageHeight * scaleFactor,
});
}
if (!cancelProcess) {
const pdfBytes = await pdfDoc.save();
const anchor = document.createElement('a');
anchor.download = pageTitle + '.pdf';
anchor.href = URL.createObjectURL(new Blob([pdfBytes], { type: 'application/pdf' }));
anchor.click();
URL.revokeObjectURL(anchor.href);
//cancelButton.textContent = "Cancel";
//cancelButton.disabled = true;
}
ongoingProcess = false;
}
function displayAllPages() {
// Check if the 'continue_page' element exists
const continuePage = document.getElementById('continue_page');
if (continuePage) {
// Find the button with class 'iconfont more' inside 'continue_page'
const moreButton = continuePage.querySelector('.iconfont.more');
if (moreButton) {
// Trigger a click event on the button
moreButton.click();
console.log('Clicking the button to display all pages...');
// Wait for 2 seconds (2000 milliseconds) after clicking, so it loads the other pages HTML, at least;
setTimeout(() => {
}, 2000);
}
}
}
async function scrollToPagesWithPercentage() {
const waitFor = (ms) => new Promise(resolve => setTimeout(resolve, ms));
while (true) {
ongoingProcess = true;
let pagesWithPercentage = document.querySelectorAll('div.page_pb[id^="pagepb_"]');
let allPagesLoaded = true; // Track if all pages have been loaded successfully
for (let i = 0; i < pagesWithPercentage.length; i++) {
if (cancelProcess) {
break;
}
const page = pagesWithPercentage[i];
const updatedText = page.textContent.trim();
if (updatedText.endsWith('%')) {
console.log('Loading page '+(i+1)+'/'+pagesWithPercentage.length+'...');
// Scroll to the page
page.scrollIntoView({ behavior: 'smooth' });
// Wait for a moment (you can adjust the delay as needed)
await waitFor(100);
// Check if the percentage is still there after each 100ms increment
let percentageRemoved = false;
for (let j = 0; j < 12; j++) {
await waitFor(100);
// Update the page.textContent value in each iteration
const updatedText = page.textContent.trim();
if (!updatedText.endsWith('%')) {
// If the percentage is no longer there, set flag for this page
percentageRemoved = true;
break;
}
}
if (!percentageRemoved) {
// If the percentage is still there after 20 attempts, set flag for all pages
allPagesLoaded = false;
console.log('This page could not be loaded yet. Coming back for it later...');
}
}
}
// If all pages have had their percentage removed, exit the loop
if (allPagesLoaded) {
console.log('All pages loaded successfully!');
break;
}
if (cancelProcess) {
break;
}
}
ongoingProcess = false;
}
let cancelProcess = false; // Flag to indicate if the processes should be canceled
let ongoingProcess = false; // Flag to indicate if there is an ongoing process
function createDownloadButton() {
const container = document.createElement('div'); // Container for the buttons
container.style.position = 'fixed';
container.style.top = '20px';
container.style.right = '20px';
container.style.zIndex = '9999';
container.style.width = '160px'; // Adjust the width as needed
// Create the white rectangle with black outline
container.style.backgroundColor = 'white';
container.style.border = '2px solid black';
container.style.padding = '10px';
const checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.id = 'loadAllPagesCheckbox'; // Set the checkbox ID
checkbox.checked = true; // Default value is true
const checkboxLabel = document.createElement('label');
checkboxLabel.textContent = 'Load all pages';
checkboxLabel.htmlFor = 'loadAllPagesCheckbox'; // Associate the label with the checkbox
const button = document.createElement('button');
button.textContent = 'Download PDF';
button.style.marginTop = '5px';
const cancelButton = document.createElement('button'); // Cancel button
cancelButton.textContent = 'Cancel';
cancelButton.style.marginTop = '7px'; // Add margin to separate from other buttons
cancelButton.disabled = true; // Initially disable the Cancel button
// Function to update cancel button text and enable/disable it
function updateCancelButton(text, isEnabled) {
cancelButton.textContent = text;
cancelButton.disabled = !isEnabled;
}
// Add a click event listener to the cancel button
cancelButton.addEventListener('click', async () => {
cancelProcess = true; // Set the cancel flag to true
updateCancelButton('OK. Canceled.', false); // Disable the Cancel button
button.disabled = false;
setTimeout(() => {
updateCancelButton('Cancel', false);
cancelProcess = true;
}, 1500);
console.log('Canceled ongoing processes.');
cancelProcess = false; // Set the cancel flag to true
});
// Add a click event listener to the download button
button.addEventListener('click', async () => {
const shouldLoadAllPages = document.getElementById('loadAllPagesCheckbox').checked;
updateCancelButton('Cancel', true); // Enable the Cancel button
button.disabled = true;
if (shouldLoadAllPages) {
ongoingProcess = true; // There is an ongoing process
await displayAllPages();
if (cancelProcess) {
console.log('Download canceled.');
ongoingProcess = false; // No ongoing process
cancelProcess = false; // Reset the cancel flag
return; // Exit the function if canceled
}
await scrollToPagesWithPercentage();
if (cancelProcess) {
console.log('Download canceled.');
ongoingProcess = false; // No ongoing process
cancelProcess = false; // Reset the cancel flag
return; // Exit the function if canceled
}
}
if (cancelProcess) {
console.log('Download canceled.');
ongoingProcess = false; // No ongoing process
cancelProcess = false; // Reset the cancel flag
return; // Exit the function if canceled
}
await downloadPagesAsPDF(1, 9999);
ongoingProcess = false; // No ongoing process
button.disabled = false;
});
const buttonContainer = document.createElement('div'); // Container for the buttons
buttonContainer.style.display = 'flex'; // Use flex to align checkbox and button horizontally
buttonContainer.appendChild(checkbox); // Append checkbox to container
buttonContainer.appendChild(checkboxLabel); // Append label to container
container.appendChild(buttonContainer); // Append checkbox container to main container
container.appendChild(button); // Append button to main container
container.appendChild(cancelButton); // Append cancel button to main container
document.body.appendChild(container); // Append the main container to the page
}
// Call the function to create the buttons when the page loads
window.addEventListener('load', createDownloadButton);