EhBatchUpload

Upload a large gallery in small batches

Terá de instalar uma extensão como Tampermonkey, Greasemonkey ou Violentmonkey para instalar este script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

Terá de instalar uma extensão como Tampermonkey ou Violentmonkey para instalar este script.

Terá de instalar uma extensão como Tampermonkey ou Userscripts para instalar este script.

Terá de instalar uma extensão como Tampermonkey para instalar este script.

Terá de instalar uma extensão de gestão de scripts de utilizador para instalar este script.

(Já tenho um gestor de scripts de utilizador, deixe-me instalá-lo!)

Terá de instalar uma extensão como Stylus para instalar este estilo.

Terá de instalar uma extensão como Stylus para instalar este estilo.

Terá de instalar uma extensão como Stylus para instalar este estilo.

Terá de instalar uma extensão de gestão de estilos de utilizador para instalar este estilo.

Terá de instalar uma extensão de gestão de estilos de utilizador para instalar este estilo.

Terá de instalar uma extensão de gestão de estilos de utilizador para instalar este estilo.

(Já tenho um gestor de estilos de utilizador, deixe-me instalá-lo!)

// ==UserScript==
// @name         EhBatchUpload
// @namespace    http://tampermonkey.net/
// @version      2025-06-17
// @description  Upload a large gallery in small batches
// @author       4piu
// @license      MIT
// @match        https://upload.e-hentai.org/managegallery*
// @match        https://upld.exhentai.org/upld/managegallery*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=e-hentai.org
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    let batchSize = 5;
    let filesToUpload = [];

    let filesUploaded = 0;
    let totalFiles = 0;
    let currentBatch = 0;
    let totalBatches = 0;
    let batchPercent = 0;
    let uploadInProgress = false;
    let uploadMessage = '';

    // Create a progress display element
    function createProgressDisplay() {
        const progressDiv = document.createElement('div');
        progressDiv.id = 'batchProgress';
        progressDiv.style.cssText = 'padding: 10px; border: 1px solid;';
        progressDiv.innerHTML = '<p id="uploadMessage" style="font-weight: bold;"></p>' +
                              '<p>Status: <span id="batchStatus">Idle</span></p>' +
                              '<p>Files Uploaded: <span id="filesUploaded">0</span>/<span id="totalFiles">0</span> <span id="totalPercent"></span></p>' +
                              '<p>Current Batch: <span id="currentBatch">0</span>/<span id="totalBatches">0</span> <span id="batchPercent"></span></p>';
        document.getElementById('u').appendChild(progressDiv);
    }

    // Create a input box for batch size
    function createBatchSizeInput() {
       const batchSizeInputDiv = document.createElement('div');
       batchSizeInputDiv.innerHTML = '<label for="batchSizeInput">Batch size</label>' +
                             `<input type="number" id="batchSizeInput" name="batchSizeInput" value="${batchSize}" style="width: 40px; line-height: 19px; border: 2px solid; margin: 3px 1px 0; padding: 1px 3px 3px; border-radius: 3px;">`;
       document.getElementById('uploadbutton').parentNode.appendChild(batchSizeInputDiv);
    }

    // Update progress display
    function updateProgressDisplay() {
        document.getElementById('batchStatus').textContent = uploadInProgress ? 'Uploading...' : 'Idle';
        document.getElementById('filesUploaded').textContent = filesUploaded;
        document.getElementById('totalFiles').textContent = totalFiles;
        document.getElementById('totalPercent').textContent = uploadInProgress? `[${(filesUploaded / totalFiles * 100).toFixed(1)}%]` : '';
        document.getElementById('currentBatch').textContent = currentBatch + 1;
        document.getElementById('totalBatches').textContent = totalBatches;
        document.getElementById('batchPercent').textContent = uploadInProgress? `[${batchPercent.toFixed(1)}%]` : '';
        document.getElementById('uploadMessage').textContent = uploadMessage;
    }

    // Upload a single batch of files
    function uploadBatch(batchFiles) {
        if (batchFiles.length === 0) {
            currentBatch++;
            uploadNextBatch();
            return;
        }

        const formData = new FormData();
        batchFiles.forEach(file => formData.append('files[]', file));

        // Copy hidden inputs from original form
        const originalForm = document.getElementById('uploadform');
        const hiddenInputs = originalForm.querySelectorAll('input[type="hidden"]');
        hiddenInputs.forEach(input => {
            formData.append(input.name, input.value);
        });

        uploadInProgress = true;
        updateProgressDisplay();

        const xhr = new XMLHttpRequest();
        xhr.open('POST', originalForm.action, true);

        xhr.upload.onprogress = function(e) {
            if (e.lengthComputable) {
                batchPercent = (e.loaded / e.total) * 100;
                updateProgressDisplay();
            }
        };

        xhr.onload = function() {
            if (xhr.status === 200) {
                currentBatch < totalBatches && currentBatch++;
                filesToUpload = filesToUpload.slice(batchFiles.length);
                filesUploaded = totalFiles - filesToUpload.length;
                uploadInProgress = false;
                updateProgressDisplay();
                uploadNextBatch();
            } else {
                uploadMessage = `Error uploading batch ${currentBatch + 1}: ${xhr.statusText}`;
                uploadInProgress = false;
                updateProgressDisplay();
                document.getElementById('batchSizeInput').disabled = false;
            }
        };

        xhr.onerror = function() {
            uploadMessage = `Error uploading batch ${currentBatch + 1}: Network Error`;
            uploadInProgress = false;
            updateProgressDisplay();
            document.getElementById('batchSizeInput').disabled = false;
        };

        xhr.send(formData);
    }

    // Start uploading the next batch
    function uploadNextBatch() {
        if (currentBatch >= totalBatches || filesToUpload.length === 0) {
            uploadMessage = 'All batches uploaded successfully! Please refresh page to see results. ';
            uploadInProgress = false;
            updateProgressDisplay();
            document.getElementById('batchSizeInput').disabled = false;
            return;
        }

        const start = 0;
        const end = Math.min(batchSize, filesToUpload.length);
        const batchFiles = filesToUpload.slice(start, end);
        uploadBatch(batchFiles);
    }

    // Override the original submit_upload function
    function overrideSubmitUpload() {
        window.submit_upload = function() {
            if (uploadInProgress) {
                alert('Upload in progress. Please wait until the current batch completes.');
                return;
            }

            const fileInput = document.getElementById('uploadfiles');
            if (!fileInput.files.length) {
                alert('Please select files to upload.');
                return;
            }

            if (!disable_submit()) {
                return;
            }

            const batchSizeInput = document.getElementById('batchSizeInput');
            batchSize = parseInt(batchSizeInput.value) || batchSize; // Fallback to current value if invalid
            batchSizeInput.disabled = true;

            filesToUpload = Array.from(fileInput.files);
            totalFiles = filesToUpload.length;
            filesUploaded = 0;
            totalBatches = Math.ceil(filesToUpload.length / batchSize);
            currentBatch = 0;
            document.getElementById('uploadbutton').value = 'Uploading...';
            updateProgressDisplay();
            uploadNextBatch();
        };
    }

    // Initialize the script
    function init() {
        createProgressDisplay();
        createBatchSizeInput();
        overrideSubmitUpload();
        console.info('EhBatchUpload loaded');
    }

    // Run initialization when DOM is fully loaded
    if (document.readyState === 'complete') {
        init();
    } else {
        window.addEventListener('load', init);
    }
})();