// ==UserScript==
// @name         Final Minimal Image Downloader with Dynamic JSZip Loading
// @namespace    http://tampermonkey.net/
// @version      1.4
// @description  Download the first image on the page as a ZIP file by dynamically loading JSZip with enhanced debugging
// @author       Your Name
// @match        http://d15.876515.xyz/*
// @match        http://98.987328.xyz/*
// @match        http://97.987327.xyz/*
// @grant        GM_xmlhttpRequest
// @grant        GM_download
// @grant        none
// @connect      cdn.jsdelivr.net
// @connect      d15.876515.xyz
// @connect      98.987328.xyz
// @connect      97.987327.xyz
// ==/UserScript==

(async function() {
    'use strict';

    console.log('Final Minimal Image Downloader script initializing...');

    // Function to load JSZip dynamically via GM_xmlhttpRequest
    async function loadJSZip() {
        return new Promise((resolve, reject) => {
            const jsZipURL = 'https://cdn.jsdelivr.net/npm/jszip@3.10.0/dist/jszip.min.js';
            console.log(`Fetching JSZip from: ${jsZipURL}`);

            GM_xmlhttpRequest({
                method: 'GET',
                url: jsZipURL,
                onload: function(response) {
                    if (response.status === 200) {
                        try {
                            eval(response.responseText);
                            if (typeof JSZip === 'undefined') {
                                console.error('JSZip failed to load after evaluation.');
                                reject(new Error('JSZip is undefined after evaluation.'));
                            } else {
                                console.log(`JSZip version ${JSZip.version} loaded successfully.`);
                                resolve();
                            }
                        } catch (e) {
                            console.error('Error evaluating JSZip:', e);
                            reject(e);
                        }
                    } else {
                        console.error(`Failed to fetch JSZip. Status: ${response.status}`);
                        reject(new Error(`Failed to fetch JSZip. Status: ${response.status}`));
                    }
                },
                onerror: function(error) {
                    console.error('GM_xmlhttpRequest error while fetching JSZip:', error);
                    reject(error);
                }
            });
        });
    }

    // Function to fetch image as blob using GM_xmlhttpRequest to bypass CORS
    async function fetchImage(url) {
        return new Promise((resolve, reject) => {
            console.log(`Fetching image via GM_xmlhttpRequest: ${url}`);
            GM_xmlhttpRequest({
                method: 'GET',
                url: url,
                responseType: 'blob',
                onload: function(response) {
                    if (response.status === 200) {
                        console.log('Image fetched successfully.');
                        resolve(response.response);
                    } else {
                        console.error(`Failed to fetch image. Status: ${response.status}`);
                        reject(new Error(`HTTP error! status: ${response.status}`));
                    }
                },
                onerror: function(error) {
                    console.error(`GM_xmlhttpRequest error while fetching image:`, error);
                    reject(error);
                }
            });
        });
    }

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

    // Helper function to convert Blob to ArrayBuffer
    function blobToArrayBuffer(blob) {
        return new Promise((resolve, reject) => {
            const reader = new FileReader();
            reader.onloadend = () => {
                if (reader.error) {
                    reject(reader.error);
                } else {
                    resolve(reader.result);
                }
            };
            reader.readAsArrayBuffer(blob);
        });
    }

    // Function to download the image Blob directly for verification
    async function downloadImageBlobDirectly(imageBlob, imageName = 'downloaded_image.jpg') {
        try {
            const blobUrl = URL.createObjectURL(imageBlob);
            const a = document.createElement('a');
            a.href = blobUrl;
            a.download = imageName;
            document.body.appendChild(a);
            a.click();
            document.body.removeChild(a);
            URL.revokeObjectURL(blobUrl);
            console.log('Image Blob downloaded directly for verification.');
            alert('Image Blob downloaded directly for verification.');
        } catch (error) {
            console.error('Error downloading image Blob directly:', error);
            alert('Failed to download image Blob directly.');
        }
    }

    // Helper function to create a ZIP and download using standard browser method with enhanced logging and ArrayBuffer
    async function createAndDownloadZip(imageBlob, imageName, zipName = 'image.zip') {
        try {
            console.log('Creating ZIP file...');
            const zip = new JSZip();
            zip.file(imageName, imageBlob);
            console.log(`Added ${imageName} to ZIP.`);

            // Log all files in the ZIP for verification
            console.log('Current ZIP contents:', Object.keys(zip.files));

            console.log('Generating ZIP Blob...');

            // Convert Blob to ArrayBuffer
            console.log('Converting Blob to ArrayBuffer...');
            const arrayBuffer = await blobToArrayBuffer(imageBlob);
            console.log('Blob converted to ArrayBuffer.');

            console.log('Adding ArrayBuffer to ZIP...');
            zip.file(imageName, arrayBuffer);
            console.log(`Re-added ${imageName} to ZIP as ArrayBuffer.`);

            console.log('Generating ZIP Blob with ArrayBuffer...');
            const zipBlob = await Promise.race([
                zip.generateAsync(
                    { type: 'blob', compression: 'STORE' }, // Changed to 'STORE' (no compression)
                    (metadata) => {
                        console.log(`ZIP Generation Progress: ${metadata.percent.toFixed(2)}%`);
                    }
                ),
                new Promise((_, reject) =>
                    setTimeout(() => reject(new Error('ZIP generation timed out.')), 10000) // 10 seconds timeout
                )
            ]);

            console.log('ZIP Blob generated.');
            console.log('ZIP Blob size:', zipBlob.size, 'bytes');

            const blobUrl = URL.createObjectURL(zipBlob);
            console.log('Blob URL created:', blobUrl);

            // Create an <a> element and trigger the download
            const a = document.createElement('a');
            a.href = blobUrl;
            a.download = zipName;
            document.body.appendChild(a);
            console.log('Initiating download...');
            a.click();
            document.body.removeChild(a);
            URL.revokeObjectURL(blobUrl);
            console.log('Download initiated via standard browser method.');
            alert('Download initiated successfully.');
        } catch (error) {
            console.error(`Error creating or downloading ZIP: ${error}`);
            alert(`An error occurred while creating the ZIP file: ${error.message}`);
        }
    }

    // Main function to execute the download process
    async function downloadFirstImageAsZip() {
        const imgSrc = findFirstImage();
        if (!imgSrc) return;

        const absoluteImgUrl = makeAbsoluteURL(imgSrc);
        console.log(`Absolute Image URL: ${absoluteImgUrl}`);

        try {
            const imageBlob = await fetchImage(absoluteImgUrl);
            console.log('Image Blob Details:', {
                size: imageBlob.size,
                type: imageBlob.type
            });

            // Download the image Blob directly for verification
            await downloadImageBlobDirectly(imageBlob, `image_directly_downloaded.${getFileExtension(imgSrc)}`);

            const extension = getFileExtension(imgSrc);
            const imageName = `image.${extension}`;
            const zipName = `image_${Date.now()}.zip`;
            console.log(`Image Name: ${imageName}`);
            console.log(`ZIP Name: ${zipName}`);
            await createAndDownloadZip(imageBlob, imageName, zipName);
        } catch (error) {
            console.error(`Error during download process: ${error}`);
            alert(`Error: ${error.message}`);
        }
    }

    // Main function to execute the download process using a Data URL (for testing)
    async function downloadFirstImageAsZipWithDataURL() {
        const dataURL = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUA' +
                       'AAAFCAYAAACNbyblAAAAHElEQVQI12P4' +
                       '//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==';
        console.log('Using a sample Data URL as the image.');

        try {
            const response = await fetch(dataURL);
            const imageBlob = await response.blob();
            console.log('Sample Image Blob Details:', {
                size: imageBlob.size,
                type: imageBlob.type
            });

            const imageName = 'sample_image.png';
            const zipName = `sample_image_${Date.now()}.zip`;
            console.log(`Image Name: ${imageName}`);
            console.log(`ZIP Name: ${zipName}`);
            await createAndDownloadZip(imageBlob, imageName, zipName);
        } catch (error) {
            console.error(`Error during download process with Data URL: ${error}`);
            alert(`Error: ${error.message}`);
        }
    }

    // Helper function to make absolute URLs
    function makeAbsoluteURL(url) {
        try {
            return new URL(url, window.location.origin).href;
        } catch {
            console.warn(`Invalid URL encountered: ${url}. Returning original URL.`);
            return url;
        }
    }

    // Function to find the first image on the page
    function findFirstImage() {
        console.log('Searching for the first image on the page...');
        // Modify the selector based on the actual structure of your target pages
        // For broader compatibility, we'll try multiple selectors
        const selectors = [
            'div.content img[src]',        // Example selector; modify as needed
            'img[src]',
            'div.item_info img[src]',
            'div.some-other-class img[src]' // Add more selectors as needed
        ];

        let img = null;
        for (const selector of selectors) {
            img = document.querySelector(selector);
            if (img) {
                console.log(`Image found using selector "${selector}".`);
                break;
            }
        }

        if (img) {
            const src = img.getAttribute('src');
            console.log(`First image found with src: ${src}`);
            return src;
        } else {
            console.error('No image found using any of the selectors.');
            alert('No image found on this page.');
            return null;
        }
    }

    // Function to create and inject a DOWNLOAD button
    function addDownloadButton() {
        const existingButton = document.getElementById('final-download-button');
        if (existingButton) {
            console.log('Download button already exists. Skipping addition.');
            return;
        }

        const button = document.createElement('button');
        button.id = 'final-download-button';
        button.innerText = 'DOWNLOAD FIRST IMAGE';
        button.style.position = 'fixed';
        button.style.bottom = '20px';
        button.style.right = '20px';
        button.style.padding = '10px 20px';
        button.style.backgroundColor = '#28a745';
        button.style.color = '#fff';
        button.style.border = 'none';
        button.style.borderRadius = '5px';
        button.style.cursor = 'pointer';
        button.style.fontSize = '14px';
        button.style.boxShadow = '0 2px 6px rgba(0, 0, 0, 0.3)';
        button.style.zIndex = '10000';

        button.addEventListener('click', () => {
            console.log('DOWNLOAD button clicked.');
            button.disabled = true;
            button.innerText = 'Processing...';
            downloadFirstImageAsZip().finally(() => {
                button.innerText = 'DOWNLOAD FIRST IMAGE';
                button.disabled = false;
            });
        });

        document.body.appendChild(button);
        console.log('Download button added to the page.');
    }

    // Function to create and inject a DOWNLOAD button for Data URL test (optional)
    function addTestDownloadButton() {
        const existingButton = document.getElementById('test-download-button');
        if (existingButton) {
            console.log('Test download button already exists. Skipping addition.');
            return;
        }

        const button = document.createElement('button');
        button.id = 'test-download-button';
        button.innerText = 'Test Download with Data URL';
        button.style.position = 'fixed';
        button.style.bottom = '60px';
        button.style.right = '20px';
        button.style.padding = '10px 20px';
        button.style.backgroundColor = '#007bff';
        button.style.color = '#fff';
        button.style.border = 'none';
        button.style.borderRadius = '5px';
        button.style.cursor = 'pointer';
        button.style.fontSize = '14px';
        button.style.boxShadow = '0 2px 6px rgba(0, 0, 0, 0.3)';
        button.style.zIndex = '10000';

        button.addEventListener('click', () => {
            console.log('Test Download button clicked.');
            button.disabled = true;
            button.innerText = 'Processing...';
            downloadFirstImageAsZipWithDataURL().finally(() => {
                button.innerText = 'Test Download with Data URL';
                button.disabled = false;
            });
        });

        document.body.appendChild(button);
        console.log('Test Download button added to the page.');
    }

    // Initialize the script after DOM is fully loaded
    async function initialize() {
        console.log('Initializing Final Minimal Image Downloader script...');
        try {
            await loadJSZip();
        } catch (error) {
            console.error('Failed to load JSZip:', error);
            alert('Failed to load JSZip. Please check the console for details.');
            return;
        }
        addDownloadButton();
        addTestDownloadButton(); // Optional: Comment out if not needed
    }

    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', initialize);
    } else {
        initialize();
    }

})();

These are the logs...
chext_loader.js:71 Initialized chextloader at: 1731163528678
Final Minimal Image Downloader with Dynamic JSZip Loading.user.js:22 Final Minimal Image Downloader script initializing...
Final Minimal Image Downloader with Dynamic JSZip Loading.user.js:360 Initializing Final Minimal Image Downloader script...
Final Minimal Image Downloader with Dynamic JSZip Loading.user.js:28 Fetching JSZip from: https://cdn.jsdelivr.net/npm/jszip@3.10.0/dist/jszip.min.js
Final Minimal Image Downloader with Dynamic JSZip Loading.user.js:41 JSZip version 3.10.0 loaded successfully.
Final Minimal Image Downloader with Dynamic JSZip Loading.user.js:317 Download button added to the page.
Final Minimal Image Downloader with Dynamic JSZip Loading.user.js:355 Test Download button added to the page.
chext_driver.js:505 Initialized driver at: Sat Nov 09 2024 22:45:29 GMT+0800 (Hong Kong Standard Time)
chext_loader.js:71 Initialized chextloader at: 1731163529889
Final Minimal Image Downloader with Dynamic JSZip Loading.user.js:345 Test Download button clicked.
Final Minimal Image Downloader with Dynamic JSZip Loading.user.js:219 Using a sample Data URL as the image.
Final Minimal Image Downloader with Dynamic JSZip Loading.user.js:224 Sample Image Blob Details: {size: 85, type: 'image/png'}
Final Minimal Image Downloader with Dynamic JSZip Loading.user.js:231 Image Name: sample_image.png
Final Minimal Image Downloader with Dynamic JSZip Loading.user.js:232 ZIP Name: sample_image_1731163539609.zip
Final Minimal Image Downloader with Dynamic JSZip Loading.user.js:129 Creating ZIP file...
Final Minimal Image Downloader with Dynamic JSZip Loading.user.js:132 Added sample_image.png to ZIP.
Final Minimal Image Downloader with Dynamic JSZip Loading.user.js:135 Current ZIP contents: ['sample_image.png']
Final Minimal Image Downloader with Dynamic JSZip Loading.user.js:137 Generating ZIP Blob...
Final Minimal Image Downloader with Dynamic JSZip Loading.user.js:140 Converting Blob to ArrayBuffer...
Final Minimal Image Downloader with Dynamic JSZip Loading.user.js:142 Blob converted to ArrayBuffer.
Final Minimal Image Downloader with Dynamic JSZip Loading.user.js:144 Adding ArrayBuffer to ZIP...
Final Minimal Image Downloader with Dynamic JSZip Loading.user.js:146 Re-added sample_image.png to ZIP as ArrayBuffer.
Final Minimal Image Downloader with Dynamic JSZip Loading.user.js:148 Generating ZIP Blob with ArrayBuffer...
Final Minimal Image Downloader with Dynamic JSZip Loading.user.js:179 Error creating or downloading ZIP: Error: ZIP generation timed out.
createAndDownloadZip @ Final Minimal Image Downloader with Dynamic JSZip Loading.user.js:179
await in createAndDownloadZip
downloadFirstImageAsZipWithDataURL @ Final Minimal Image Downloader with Dynamic JSZip Loading.user.js:233
await in downloadFirstImageAsZipWithDataURL
(anonymous) @ Final Minimal Image Downloader with Dynamic JSZip Loading.user.js:348

Final Minimal Image Downloader with Dynamic JSZip Loading.user.js:22 Final Minimal Image Downloader script initializing...
Final Minimal Image Downloader with Dynamic JSZip Loading.user.js:360 Initializing Final Minimal Image Downloader script...
Final Minimal Image Downloader with Dynamic JSZip Loading.user.js:28 Fetching JSZip from: https://cdn.jsdelivr.net/npm/jszip@3.10.0/dist/jszip.min.js
Final Minimal Image Downloader with Dynamic JSZip Loading.user.js:41 JSZip version 3.10.0 loaded successfully.
Final Minimal Image Downloader with Dynamic JSZip Loading.user.js:317 Download button added to the page.
Final Minimal Image Downloader with Dynamic JSZip Loading.user.js:355 Test Download button added to the page.
chext_driver.js:505 Initialized driver at: Sat Nov 09 2024 22:46:10 GMT+0800 (Hong Kong Standard Time)
chext_loader.js:71 Initialized chextloader at: 1731163570677
chext_loader.js:71 Initialized chextloader at: 1731163571448
Final Minimal Image Downloader with Dynamic JSZip Loading.user.js:307 DOWNLOAD button clicked.
Final Minimal Image Downloader with Dynamic JSZip Loading.user.js:252 Searching for the first image on the page...
Final Minimal Image Downloader with Dynamic JSZip Loading.user.js:266 Image found using selector "div.content img[src]".
Final Minimal Image Downloader with Dynamic JSZip Loading.user.js:273 First image found with src: /uploadfile/202411/8/94163212205.jpg
Final Minimal Image Downloader with Dynamic JSZip Loading.user.js:190 Absolute Image URL: http://d15.876515.xyz/uploadfile/202411/8/94163212205.jpg
Final Minimal Image Downloader with Dynamic JSZip Loading.user.js:64 Fetching image via GM_xmlhttpRequest: http://d15.876515.xyz/uploadfile/202411/8/94163212205.jpg
Final Minimal Image Downloader with Dynamic JSZip Loading.user.js:71 Image fetched successfully.
Final Minimal Image Downloader with Dynamic JSZip Loading.user.js:194 Image Blob Details: {size: 271182, type: 'image/jpeg'}
Final Minimal Image Downloader with Dynamic JSZip Loading.user.js:118 Image Blob downloaded directly for verification.
Final Minimal Image Downloader with Dynamic JSZip Loading.user.js:205 Image Name: image.jpg
Final Minimal Image Downloader with Dynamic JSZip Loading.user.js:206 ZIP Name: image_1731163589941.zip
Final Minimal Image Downloader with Dynamic JSZip Loading.user.js:129 Creating ZIP file...
Final Minimal Image Downloader with Dynamic JSZip Loading.user.js:132 Added image.jpg to ZIP.
Final Minimal Image Downloader with Dynamic JSZip Loading.user.js:135 Current ZIP contents: ['image.jpg']
Final Minimal Image Downloader with Dynamic JSZip Loading.user.js:137 Generating ZIP Blob...
Final Minimal Image Downloader with Dynamic JSZip Loading.user.js:140 Converting Blob to ArrayBuffer...
Final Minimal Image Downloader with Dynamic JSZip Loading.user.js:142 Blob converted to ArrayBuffer.
Final Minimal Image Downloader with Dynamic JSZip Loading.user.js:144 Adding ArrayBuffer to ZIP...
Final Minimal Image Downloader with Dynamic JSZip Loading.user.js:146 Re-added image.jpg to ZIP as ArrayBuffer.
Final Minimal Image Downloader with Dynamic JSZip Loading.user.js:148 Generating ZIP Blob with ArrayBuffer...
Final Minimal Image Downloader with Dynamic JSZip Loading.user.js:179 Error creating or downloading ZIP: Error: ZIP generation timed out.
createAndDownloadZip @ Final Minimal Image Downloader with Dynamic JSZip Loading.user.js:179
await in createAndDownloadZip
downloadFirstImageAsZip @ Final Minimal Image Downloader with Dynamic JSZip Loading.user.js:207
await in downloadFirstImageAsZip
(anonymous) @ Final Minimal Image Downloader with Dynamic JSZip Loading.user.js:310