// ==UserScript==
// @name         WebURL Image Downloader
// @namespace    http://tampermonkey.net/
// @version      1.1
// @description  Download images from specified WebURLs as a ZIP file.
// @author       Your Name
// @match        http://d15.876515.xyz/*
// @match        http://98.987328.xyz/*
// @match        http://97.987327.xyz/*
// @grant        GM_download
// @require      https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.0/jszip.min.js
// @connect      *
// ==/UserScript==

(async function() {
    'use strict';

    // Utility function to fetch and parse HTML
    async function fetchHTML(url) {
        try {
            const response = await fetch(url);
            if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
            const text = await response.text();
            const parser = new DOMParser();
            return parser.parseFromString(text, 'text/html');
        } catch (error) {
            console.error(`Error fetching HTML from ${url}: ${error}`);
            throw error;
        }
    }

    // Utility function to fetch image as blob
    async function fetchImage(url) {
        try {
            const response = await fetch(url);
            if (!response.ok) throw new Error(`Failed to fetch image: ${url} - Status: ${response.status}`);
            return await response.blob();
        } catch (error) {
            console.warn(`Error fetching image ${url}: ${error}`);
            throw error;
        }
    }

    // Function to add DOWNLOAD button
    function addDownloadButton(spanElement, pageUrl) {
        // Create the button
        const button = document.createElement('button');
        button.innerText = 'DOWNLOAD';
        button.style.marginLeft = '10px';
        button.style.padding = '5px 10px';
        button.style.cursor = 'pointer';
        button.style.backgroundColor = '#28a745';
        button.style.color = '#fff';
        button.style.border = 'none';
        button.style.borderRadius = '4px';
        button.style.fontSize = '14px';
        button.style.transition = 'background-color 0.3s';

        // Hover effect
        button.addEventListener('mouseenter', () => {
            button.style.backgroundColor = '#218838';
        });
        button.addEventListener('mouseleave', () => {
            button.style.backgroundColor = '#28a745';
        });

        // Add click event listener
        button.addEventListener('click', async () => {
            button.disabled = true;
            const originalText = button.innerText;
            button.innerText = 'Processing...';
            try {
                await downloadImages(pageUrl);
                button.innerText = 'DOWNLOAD';
                button.disabled = false;
                alert('Download completed successfully.');
            } catch (error) {
                console.error(error);
                alert('An error occurred during the download process.');
                button.innerText = originalText;
                button.disabled = false;
            }
        });

        // Append the button to the span element
        spanElement.appendChild(button);
    }

    // Main function to handle the download process
    async function downloadImages(initialPageUrl) {
        // Fetch the initial page
        const initialDoc = await fetchHTML(initialPageUrl);

        // Extract TITLE from div.jianjie
        const jianjieDiv = initialDoc.querySelector('div.jianjie');
        if (!jianjieDiv) throw new Error('div.jianjie not found.');
        const jianjieText = jianjieDiv.textContent;
        const titleMatch = jianjieText.match(/\[ (.*?)P\]/);
        if (!titleMatch || !titleMatch[1]) throw new Error('TITLE not found.');
        const TITLE = titleMatch[1].trim().replace(/[\\/:*?"<>|]/g, '_'); // Replace illegal filename characters

        // Initialize JSZip
        const zip = new JSZip();

        // Extract all related PageURLs from div.page
        const pageDiv = initialDoc.querySelector('div.page');
        if (!pageDiv) throw new Error('div.page not found.');
        const pageLinks = pageDiv.querySelectorAll('a[href]');
        const pageUrls = Array.from(pageLinks).map(link => {
            let href = link.getAttribute('href');
            // Ensure the href is absolute
            if (!href.startsWith('http')) {
                // Assuming relative links, prepend the origin
                href = new URL(href, initialPageUrl).href;
            }
            return href;
        });

        if (pageUrls.length === 0) throw new Error('No page URLs found.');

        // Limit concurrent fetches to avoid overloading
        const CONCURRENT_LIMIT = 5;
        let index = 0;

        // Collect all image URLs
        const images = [];
        const workers = [];
        for (let i = 0; i < CONCURRENT_LIMIT; i++) {
            workers.push(processNext());
        }
        await Promise.all(workers);

        if (images.length === 0) throw new Error('No images found.');

        // Fetch all images and add to zip
        let counter = 0;
        const total = images.length;
        for (const imgUrl of images) {
            try {
                const absoluteImgUrl = makeAbsoluteURL(imgUrl, initialPageUrl);
                const blob = await fetchImage(absoluteImgUrl);
                const extension = getFileExtension(imgUrl) || 'jpg';
                const filename = `${String(counter + 1).padStart(3, '0')}.${extension}`;
                zip.file(filename, blob);
                counter++;
                // Optional: Update progress here (can be enhanced)
                console.log(`Added ${filename} to ZIP (${counter}/${total})`);
            } catch (e) {
                console.warn(`Failed to fetch image ${imgUrl}: ${e.message}`);
            }
        }

        if (counter === 0) throw new Error('No images were successfully downloaded.');

        // Generate the zip file
        const zipBlob = await zip.generateAsync({ type: 'blob' });

        // Trigger the download using GM_download
        const blobUrl = URL.createObjectURL(zipBlob);
        GM_download({
            url: blobUrl,
            name: `${TITLE}.zip`,
            saveAs: true,
            onload: () => {
                URL.revokeObjectURL(blobUrl);
                console.log('Download completed.');
            },
            onerror: (error) => {
                console.error('Download failed:', error);
                alert('Failed to download ZIP file.');
            }
        });
    }

    // Helper function to process each page URL
    async function processNext() {
        while (index < pageUrls.length) {
            const currentIndex = index++;
            const pageUrl = pageUrls[currentIndex];
            try {
                const pageDoc = await fetchHTML(pageUrl);
                const contentDiv = pageDoc.querySelector('div.content');
                if (contentDiv) {
                    const paragraphs = contentDiv.querySelectorAll('p');
                    paragraphs.forEach(p => {
                        const imgs = p.querySelectorAll('img[src]');
                        imgs.forEach(img => {
                            const src = img.getAttribute('src');
                            if (src) images.push(src);
                        });
                    });
                }
            } catch (e) {
                console.warn(`Failed to process ${pageUrl}: ${e.message}`);
            }
        }
    }

    // Helper function to get file extension
    function getFileExtension(url) {
        const match = url.match(/\.([a-zA-Z0-9]+)(?:\?.*)?$/);
        return match ? match[1].toLowerCase() : null;
    }

    // Helper function to make absolute URLs
    function makeAbsoluteURL(url, base) {
        try {
            return new URL(url, base).href;
        } catch {
            return url; // Return original if invalid
        }
    }

    // Locate all target span elements and add DOWNLOAD buttons
    function initialize() {
        const spanElements = document.querySelectorAll('div.title h2 a span');
        spanElements.forEach(span => {
            // Avoid adding multiple buttons
            if (span.querySelector('button') === null) {
                const aTag = span.parentElement;
                if (aTag && aTag.href) {
                    // Construct absolute PageURL
                    const pageUrl = aTag.href.startsWith('http') ? aTag.href : new URL(aTag.getAttribute('href'), window.location.origin).href;
                    addDownloadButton(span, pageUrl);
                }
            }
        });
    }

    // Wait for the DOM to load before initializing
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', initialize);
    } else {
        initialize();
    }

})();

Can you explain this script to me in detail?