// ==UserScript==
// @name         Sesemanhua Image Downloader
// @namespace    https://www.sesemanhua.com/
// @version      0.1
// @description  Download images from sesemanhua chapter pages and rename them based on chapter number or title.
// @match        https://www.sesemanhua.com/*
// @grant        GM_xmlhttpRequest
// @require      https://cdn.jsdelivr.net/npm/file-saver@2.0.5/dist/FileSaver.min.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/jszip/3.7.1/jszip.min.js
// @require      https://cdn.jsdelivr.net/npm/@cloudflare/workers/client@latest/dist/workers-client.min.js
// ==/UserScript==

(async () => {
    'use strict';

    const chapterLinkElements = document.querySelectorAll('.j-chapter-link');
    const chapterNumbers = {};
    let maxChapterNumber = 0;
    const downloadingFiles = new Map();

    // Function to sanitize the filename
    function sanitizeFilename(title) {
        // Replace non-alphanumeric characters with underscores
        let sanitizedTitle = title.replace(/[^\w\s]/gi, '_');
        // Remove leading and trailing whitespace, then replace spaces with underscores
        sanitizedTitle = sanitizedTitle.trim().replace(/\s+/g, '_');
        // Truncate the title to prevent overly long filenames
        sanitizedTitle = sanitizedTitle.substring(0, 30); // Adjust the length as needed
        // Ensure the sanitized title is not empty or consists only of underscores
        return sanitizedTitle || 'Untitled_Chapter'; // Default value if sanitized title is empty
    }

    // Function to create the status bar container
    function createStatusBarContainer() {
        const statusBarContainer = document.createElement('div');
        statusBarContainer.id = 'status-bar-container';
        statusBarContainer.style.position = 'fixed';
        statusBarContainer.style.bottom = '10px';
        statusBarContainer.style.right = '10px';
        statusBarContainer.style.backgroundColor = 'rgba(0, 0, 0, 0.7)';
        statusBarContainer.style.color = 'white';
        statusBarContainer.style.padding = '5px';
        statusBarContainer.style.borderRadius = '5px';
        document.body.appendChild(statusBarContainer);
        return statusBarContainer;
    }

    // Function to add a status bar entry with a progress bar
    function addStatusBarEntry(filename) {
        const statusBarContainer = document.getElementById('status-bar-container');
        const statusBarItem = document.createElement('div');
        statusBarItem.className = 'status-bar-item';
        statusBarItem.innerHTML = `
            <span class="filename">${filename}</span>
            <progress class="progress-bar" value="0" max="100"></progress>
        `;
        statusBarContainer.appendChild(statusBarItem);
        return statusBarItem.querySelector('.progress-bar');
    }

    // Function to remove a status bar entry
    function removeStatusBarEntry(filename) {
        const statusBarContainer = document.getElementById('status-bar-container');
        const statusItems = statusBarContainer.children;
        for (let i = 0; i < statusItems.length; i++) {
            if (statusItems[i].querySelector('.filename').textContent === filename) {
                statusBarContainer.removeChild(statusItems[i]);
                break;
            }
        }
    }

    // First pass: Calculate maxChapterNumber
    chapterLinkElements.forEach(linkElement => {
        const chapterTitle = linkElement.textContent.trim();
        console.log(`Processing chapter title: ${chapterTitle}`); // Log the chapter title
        const chapterNumberMatch = chapterTitle.match(/第(\d+)话/);
        if (chapterNumberMatch) {
            const chapterNumber = parseInt(chapterNumberMatch[1]);
            maxChapterNumber = Math.max(maxChapterNumber, chapterNumber);
        }
    });

    // Second pass: Create buttons and handle duplicates
    chapterLinkElements.forEach(linkElement => {
        const chapterTitle = linkElement.textContent.trim();
        const chapterUrl = linkElement.getAttribute('href');
        let chapterNumber = null;
        let chapterIdentifier = null;

        // Assign chapter number or identifier
        const chapterNumberMatch = chapterTitle.match(/第(\d+)话/);
        if (chapterNumberMatch) {
            chapterNumber = parseInt(chapterNumberMatch[1]);
            chapterIdentifier = chapterNumber.toString(); // Keep chapter number as is
        } else if (chapterTitle.includes('最终话')) {
            chapterNumber = maxChapterNumber + 1;
            chapterIdentifier = (maxChapterNumber + 1).toString(); // Keep chapter number as is
        } else {
            // Use the chapter title directly in the filename, replacing spaces with underscores
            chapterIdentifier = chapterTitle.replace(/ /g, '_');
        }

        // Determine if the chapter title did not contain a number
        const isNoNumber = !chapterNumberMatch && !chapterTitle.includes('最终话');

        const isDuplicate = chapterNumbers[chapterIdentifier];
        if (!isNoNumber && !isDuplicate) {
            chapterNumbers[chapterIdentifier] = true;
        }

        // Style the button based on whether a chapter number was found
        const downloadButton = document.createElement('button');
        downloadButton.className = 'download-button';
        downloadButton.innerText = 'Download';
        downloadButton.style.cssText = `
            font-size: 8px;
            position: absolute;
            padding: 2px;
            color: white;
            z-index: 10;
            cursor: pointer;
            opacity: 0.9;
            border-radius: 5px;
            border: 1px solid ${isNoNumber ? '#bbbbbb' : (isDuplicate ? '#336699' : '#f92672')};
            background-color: ${isNoNumber ? '#bbbbbb' : (isDuplicate ? '#ea465a' : '#336699')};
        `;

        downloadButton.addEventListener('click', async function() {
            try {
                if (downloadingFiles.has(`${chapterIdentifier}.zip`)) {
                    addStatusBarEntry(`${chapterIdentifier}.zip`, 'Already Downloading...');
                    return;
                }

                if (chapterIdentifier) {
                    addStatusBarEntry(`${chapterIdentifier}.zip`, 'Starting Download...');
                    const progressBar = addStatusBarEntry(`${chapterIdentifier}.zip`).querySelector('.progress-bar');
                    downloadingFiles.set(`${chapterIdentifier}.zip`, { progressBar, totalBytes: 0, receivedBytes: 0 });

                    await fetchChapterImages(chapterUrl, chapterIdentifier, chapterTitle, isNoNumber, progressBar);

                    downloadingFiles.delete(`${chapterIdentifier}.zip`);
                    removeStatusBarEntry(`${chapterIdentifier}.zip`);
                } else {
                    console.error("Chapter identifier is missing. Cannot proceed.");
                }
            } catch (error) {
                console.error(`Error fetching images: ${error}`);
                downloadingFiles.delete(`${chapterIdentifier}.zip`);
                removeStatusBarEntry(`${chapterIdentifier}.zip`);
            }
        });
        linkElement.parentNode.appendChild(downloadButton);
    });

    async function fetchChapterImages(chapterUrl, chapterIdentifier, chapterTitle, isNoNumber, progressBar) {
        const response = await fetch(chapterUrl);
        if (!response.ok) {
            throw new Error(`Could not fetch chapter page: ${response.statusText}`);
        }
        const html = await response.text();
        const parser = new DOMParser();
        const doc = parser.parseFromString(html, "text/html");
        const images = doc.querySelectorAll('.rd-article__pic .lazy-read');

        const zip = new JSZip();
        let totalBytes = 0;
        let receivedBytes = 0;

        images.forEach(img => {
            const originalSrc = img.getAttribute('data-original');
            const dataIndex = img.parentElement.getAttribute('data-index');
            totalBytes += parseInt(img.dataset.size, 10) || 0; // Assuming data-size attribute contains the size of each image
        });

        for (const [index, img] of images.entries()) {
            const originalSrc = img.getAttribute('data-original');
            const dataIndex = img.parentElement.getAttribute('data-index');

            try {
                const proxyUrl = `https://addroute.maestro1four1.workers.dev/?url=${encodeURIComponent(originalSrc)}`;
                const imageResponse = await fetch(proxyUrl, {
                    method: 'GET',
                    responseType: 'blob',
                    onloadstart: function(e) {
                        receivedBytes += e.loaded;
                        updatingProgress(progressBar, receivedBytes, totalBytes, chapterTitle);
                    },
                    onprogress: function(e) {
                        if (e.lengthComputable) {
                            receivedBytes += e.loaded;
                            updatingProgress(progressBar, receivedBytes, totalBytes, chapterTitle);
                        }
                    }
                });

                const imageData = await imageResponse.blob();
                zip.file(`${chapterIdentifier}_${dataIndex}.jpg`, imageData);
            } catch (error) {
                console.error(`Failed to download ${originalSrc}: ${error}`);
            }
        }

        const content = await zip.generateAsync({ type: "blob" });
        saveAs(content, isNoNumber ? chapterTitle : `${chapterIdentifier}.zip`);
    }

    function updatingProgress(progressBar, receivedBytes, totalBytes, chapterTitle) {
        const percentage = Math.round((receivedBytes / totalBytes) * 100);
        progressBar.value = percentage;
        progressBar.nextElementSibling.textContent = `${chapterTitle}: ${percentage}%`;
    }

    // Initialize the status bar container
    createStatusBarContainer();
})();
The script is showing two download progress bar and it it is also not updating.  Would you help me fix it?