MangaPlus Blob Downloader

Descarga imágenes grandes del manga desde MangaPlus, capturando solo imágenes únicas de tipo Blob.

// ==UserScript==
// @name         MangaPlus Blob Downloader
// @namespace    shadows
// @version      1.7.3
// @description  Descarga imágenes grandes del manga desde MangaPlus, capturando solo imágenes únicas de tipo Blob.
// @author       shadows
// @license      MIT
// @match        https://mangaplus.shueisha.co.jp/*
// @grant        GM_download
// ==/UserScript==

(function() {
    'use strict';

    let blobs = [];
    let imageHashes = new Set();  // Para hacer seguimiento de imágenes únicas por hash
    let downloadedCount = 0;

    // Función para calcular hash a partir de un blob
    async function hashBlob(blob) {
        const arrayBuffer = await blob.arrayBuffer();
        const hashBuffer = await crypto.subtle.digest('SHA-256', arrayBuffer);
        const hashArray = Array.from(new Uint8Array(hashBuffer));
        const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
        return hashHex;
    }

    // Función para guardar blobs como archivos PNG
    function saveBlob(blob, filename) {
        const blobUrl = URL.createObjectURL(blob);
        GM_download({
            url: blobUrl,
            name: filename,
            saveAs: false
        });
    }

    // Función para descargar las imágenes una por una
    function downloadSequentially(index) {
        if (index >= blobs.length) {
            alert(`Descargadas todas las ${blobs.length} imágenes.`);
            return;
        }

        const filename = `pagina-${index + 1}.png`;
        const blob = blobs[index];
        saveBlob(blob, filename);
        downloadedCount++;

        console.log(`Descargada ${filename}`);

        setTimeout(() => {
            downloadSequentially(index + 1);
        }, 1000);  // 1 segundo de retraso entre descargas
    }

    // Función para descargar todas las imágenes almacenadas en blobs
    function downloadImages() {
        if (blobs.length === 0) {
            alert("No se encontraron imágenes. Intenta desplazarte manualmente y vuelve a intentarlo.");
            return;
        }

        downloadedCount = 0;
        downloadSequentially(0);
    }

    // Interceptar la creación de URL para Blob y almacenar blobs únicos
    const originalCreateObjectURL = URL.createObjectURL;
    URL.createObjectURL = function(blob) {
        const url = originalCreateObjectURL(blob);
        if (blob.type.startsWith('image/')) {
            hashBlob(blob).then(hash => {
                if (!imageHashes.has(hash)) {  // Verificar si el hash es único
                    blobs.push(blob);  // Guardar el blob único
                    imageHashes.add(hash);  // Añadir hash al conjunto
                    console.log(`Capturado blob de imagen único: ${url} (hash: ${hash})`);
                } else {
                    console.log(`Blob de imagen duplicado omitido: ${url} (hash: ${hash})`);
                }
            });
        }
        return url;
    };

    // Crear un botón para descargar
    function createDownloadButton() {
        const button = document.createElement('button');
        button.innerText = "Descargar todas las imágenes";
        button.style.position = 'fixed';
        button.style.top = '10px';
        button.style.right = '10px';
        button.style.zIndex = '9999';
        button.style.padding = '10px 20px';
        button.style.backgroundColor = '#28a745';
        button.style.color = 'white';
        button.style.border = 'none';
        button.style.borderRadius = '5px';
        button.style.fontSize = '16px';
        button.style.fontWeight = 'bold';
        button.style.cursor = 'pointer';
        button.style.boxShadow = '0 4px 6px rgba(0,0,0,0.1)';
        document.body.appendChild(button);

        button.addEventListener('click', () => {
            console.log("Botón de descarga pulsado");
            downloadImages();
        });
    }

    // Comprobar si la página está lista y crear el botón
    const interval = setInterval(() => {
        if (document.readyState === "complete") {
            clearInterval(interval);
            console.log("Página completamente cargada, inicializando script.");
            createDownloadButton();
        }
    }, 1000);
})();