// ==UserScript==
// @name         WebURL Image Downloader with Multiple Buttons and Fixes
// @namespace    http://tampermonkey.net/
// @version      1.6
// @description  Download images from specified WebURLs as a ZIP file, log image URLs, and add DOWNLOAD buttons in multiple locations correctly.
// @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      d15.876515.xyz 98.987328.xyz 97.987327.xyz
// ==/UserScript==

(async function() {
    'use strict';

    // Utility function to fetch and parse HTML
    async function fetchHTML(url) {
        try {
            console.log(`Fetching HTML from: ${url}`);
            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();
            const doc = parser.parseFromString(text, 'text/html');
            return doc;
        } catch (error) {
            console.error(`Error fetching HTML from ${url}: ${error}`);
            throw error;
        }
    }

    // Utility function to fetch image as blob
    async function fetchImage(url) {
        try {
            console.log(`Fetching image: ${url}`);
            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 create a styled DOWNLOAD button
    function createDownloadButton() {
        const button = document.createElement('button');
        button.innerText = 'DOWNLOAD';
        button.classList.add('download-button'); // Unique class to prevent duplication
        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';
        });

        return button;
    }

    // Function to add DOWNLOAD button to a target element
    function addDownloadButton(targetElement, useCurrentPage = false) {
        // Create the button
        const button = createDownloadButton();

        // Add click event listener
        button.addEventListener('click', async () => {
            button.disabled = true;
            const originalText = button.innerText;
            button.innerText = 'Processing...';
            console.log(`DOWNLOAD button clicked. Initiating download. Use current page: ${useCurrentPage}`);

            try {
                const pageUrl = useCurrentPage ? window.location.href : undefined;
                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 target element
        targetElement.appendChild(button);
    }

    // Main function to handle the download process
    async function downloadImages(initialPageUrl = null) {
        let initialDoc;

        if (initialPageUrl && initialPageUrl !== window.location.href) {
            // Fetch another page's DOM
            initialDoc = await fetchHTML(initialPageUrl);
        } else {
            // Use the current page's DOM
            initialDoc = document;
            console.log('Using current page DOM.');
        }

        // Extract TITLE from div.jianjie
        // Updated selector to be more specific
        const jianjieDiv = initialDoc.querySelector('div.item_title div.jianjie') || initialDoc.querySelector('div.jianjie');
        if (!jianjieDiv) {
            console.error('div.jianjie not found.');
            if (initialPageUrl && initialPageUrl !== window.location.href) {
                console.log('Fetched HTML body:', initialDoc.body.innerHTML);
            }
            throw new Error('div.jianjie not found.');
        }
        const jianjieText = jianjieDiv.textContent.trim();
        console.log(`Extracted jianjie text: ${jianjieText}`);

        // Corrected Regex to capture the entire title including [73P]
        const titleMatch = jianjieText.match(/(\[XiuRen秀人网\]\s*.*?\[\d+P\])/);
        if (!titleMatch || !titleMatch[1]) {
            console.error('TITLE not found in jianjie text.');
            throw new Error('TITLE not found.');
        }
        const TITLE = titleMatch[1].trim().replace(/[\\/:*?"<>|]/g, '_'); // Replace illegal filename characters
        console.log(`Extracted TITLE for ZIP: ${TITLE}`);

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

        // Extract all related PageURLs from div.page
        const pageDiv = initialDoc.querySelector('div.page');
        if (!pageDiv) {
            console.error('div.page not found.');
            throw new Error('div.page not found.');
        }
        const pageLinks = pageDiv.querySelectorAll('a[href]');
        const pageUrls = Array.from(pageLinks)
                              .filter(link => /^\d+$/.test(link.textContent.trim()))
                              .map(link => {
                                  let href = link.getAttribute('href');
                                  // Ensure the href is absolute
                                  if (!href.startsWith('http')) {
                                      href = new URL(href, initialPageUrl || window.location.origin).href;
                                  }
                                  return href;
                              });

        console.log(`Total related pages found: ${pageUrls.length}`);

        if (pageUrls.length === 0) {
            console.error('No page URLs found.');
            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 = new Set(); // Use Set to avoid duplicates

        // Define processNext inside downloadImages to access 'index'
        async function processNext() {
            while (index < pageUrls.length) {
                const currentIndex = index++;
                const pageUrl = pageUrls[currentIndex];
                try {
                    console.log(`Processing page: ${pageUrl}`);
                    const pageDoc = await fetchHTML(pageUrl);
                    const contentDivs = pageDoc.querySelectorAll('div.content');
                    if (contentDivs.length > 0) {
                        let imagesInPage = [];
                        contentDivs.forEach(div => {
                            const imgs = div.querySelectorAll('img[src]');
                            imagesInPage.push(...imgs);
                        });
                        console.log(`Found ${imagesInPage.length} images in ${pageUrl}`);
                        if (imagesInPage.length === 0) {
                            console.warn(`No images found in any div.content of ${pageUrl}. Inspecting div.content HTML:`);
                            contentDivs.forEach(div => {
                                console.log(div.innerHTML);
                            });
                        }
                        imagesInPage.forEach(img => {
                            const src = img.getAttribute('src');
                            if (src) {
                                images.add(src);
                                // Log each image URL as it's collected
                                console.log(`Collected Image URL: ${makeAbsoluteURL(src, pageUrl)}`);
                            }
                        });
                    } else {
                        console.warn(`No div.content found in ${pageUrl}`);
                    }
                } catch (e) {
                    console.warn(`Failed to process ${pageUrl}: ${e.message}`);
                }
            }
        }

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

        if (images.size === 0) {
            console.error('No images found.');
            throw new Error('No images found.');
        }

        // Convert Set to Array for processing
        const imageArray = Array.from(images);
        console.log(`Total images collected: ${imageArray.length}`);

        // Log all collected image URLs to the console
        imageArray.forEach((imgUrl, idx) => {
            console.log(`Image ${idx + 1}: ${makeAbsoluteURL(imgUrl, initialPageUrl)}`);
        });

        // Fetch all images and add to zip
        let counter = 0;
        const totalImages = imageArray.length;
        for (const imgUrl of imageArray) {
            try {
                const absoluteImgUrl = makeAbsoluteURL(imgUrl, initialPageUrl);
                const blob = await fetchImage(absoluteImgUrl);
                const extension = getFileExtension(imgUrl) || 'jpg';
                // Ensure the extension is valid
                const validExtensions = ['jpg', 'jpeg', 'png', 'gif', 'webp'];
                const finalExtension = validExtensions.includes(extension) ? extension : 'jpg';
                const filename = `${String(counter + 1).padStart(3, '0')}.${finalExtension}`;
                zip.file(filename, blob);
                counter++;
                // Log progress
                console.log(`Added ${filename} to ZIP (${counter}/${totalImages})`);
            } catch (e) {
                console.warn(`Failed to fetch image ${imgUrl}: ${e.message}`);
            }
        }

        if (counter === 0) {
            console.error('No images were successfully downloaded.');
            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 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 || window.location.origin).href;
        } catch {
            return url; // Return original if invalid
        }
    }

    // Locate all target elements and add DOWNLOAD buttons
    function addButtons() {
        // Existing buttons in div.title > h2 > a > span
        const titleSpanElements = document.querySelectorAll('div.title h2 a span');
        titleSpanElements.forEach(span => {
            // Avoid adding multiple buttons
            if (!span.querySelector('button.download-button')) {
                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;
                    console.log(`Adding DOWNLOAD button to title span with pageUrl: ${pageUrl}`);
                    addDownloadButton(span, false); // useCurrentPage=false
                }
            }
        });

        // New buttons in div.item_info
        const itemInfoDivs = document.querySelectorAll('div.item_info');
        itemInfoDivs.forEach(div => {
            // Avoid adding multiple buttons
            if (!div.querySelector('button.download-button')) {
                console.log(`Adding DOWNLOAD button to item_info div using current page.`);
                addDownloadButton(div, true); // useCurrentPage=true
            }
        });
    }

    // Initialize function to add buttons in both locations
    function initialize() {
        addButtons();
    }

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

})();

It fails to generate zip file.  Does it require FileSaver.min.js?