Greasy Fork

YNJN Full-Page Downloader (Final)

Descarga imágenes grandes de capítulos en Young Jump Web Comics (YNJN)

目前为 2025-02-19 提交的版本。查看 最新版本

// ==UserScript==
// @name         YNJN Full-Page Downloader (Final)
// @namespace    ynjn-downloader
// @version      0.5
// @description  Descarga imágenes grandes de capítulos en Young Jump Web Comics (YNJN)
// @match        https://ynjn.jp/*
// @require      https://cdn.jsdelivr.net/npm/jszip@3/dist/jszip.min.js
// @require      https://cdn.jsdelivr.net/npm/file-saver@2/dist/FileSaver.min.js
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    // Esperar a que la página cargue por completo
    function waitForPageLoad(callback) {
        if (document.readyState === 'complete') {
            callback();
        } else {
            window.addEventListener('load', callback);
        }
    }

    waitForPageLoad(() => {
        console.log("📥 [YNJN Downloader] Script activo.");

        // Crear botón flotante
        const downloadBtn = document.createElement('button');
        downloadBtn.textContent = 'Descargar Manga (YNJN)';
        downloadBtn.style.cssText = `
            position: fixed;
            top: 10px;
            right: 10px;
            z-index: 9999;
            background: #e63946;
            color: #fff;
            padding: 8px 12px;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            font-size: 14px;
        `;
        document.body.appendChild(downloadBtn);

        downloadBtn.addEventListener('click', async () => {
            downloadBtn.disabled = true;
            downloadBtn.textContent = 'Cargando imágenes...';

            // 1. Hacer auto-scroll para cargar imágenes lazy
            await autoScroll();

            // 2. Recolectar URLs de imágenes
            const allUrls = gatherImageUrls();

            if (!allUrls.length) {
                alert('⚠️ No se encontraron imágenes. Intenta hacer scroll hasta el final del capítulo o desactivar otros scripts.');
                resetButton();
                return;
            }

            // 3. Descargar y crear ZIP
            try {
                await downloadAsZip(allUrls);
            } catch (err) {
                console.error('❌ Error al descargar imágenes:', err);
                alert('⚠️ Error al descargar. Revisa la consola (F12) para más detalles.');
            }

            resetButton();
        });
    });

    // Desplazamiento automático para forzar carga de imágenes
    async function autoScroll() {
        return new Promise(resolve => {
            let totalHeight = 0;
            const distance = 500;
            const timer = setInterval(() => {
                const scrollTopBefore = document.documentElement.scrollTop;
                window.scrollBy(0, distance);
                totalHeight += distance;

                // Si no avanzó más o llegamos al final, paramos
                if (document.documentElement.scrollTop === scrollTopBefore ||
                    (window.innerHeight + window.scrollY) >= document.body.offsetHeight) {
                    clearInterval(timer);
                    // Esperar 1 segundo extra por si hay lazy load
                    setTimeout(resolve, 1000);
                }
            }, 400);
        });
    }

    // Recolectar imágenes en <img> y <link rel="preload">
    function gatherImageUrls() {
        // 1. Imágenes en <img> (src / data-src)
        const imgTags = Array.from(document.querySelectorAll('img'));
        const imgSrcs = imgTags.map(img => img.src || img.dataset.src || '').filter(Boolean);

        // 2. Imágenes en <link rel="preload"> (href)
        const preloadLinks = Array.from(document.querySelectorAll('link[rel="preload"]'));
        const linkSrcs = preloadLinks.map(link => link.href || '').filter(Boolean);

        // Filtrar para quedarnos con URLs que tengan pinta de ser páginas
        // Por ejemplo, que incluyan '/public/' o extensión .jpg, .png, etc.
        const possibleImgs = [...imgSrcs, ...linkSrcs].filter(url => {
            // Ajusta este filtro según tu necesidad
            return (
                url.includes('/public/') ||
                url.match(/\.(jpe?g|png|webp)$/i)
            );
        });

        // Eliminar duplicados
        return Array.from(new Set(possibleImgs));
    }

    // Descargar todas las imágenes y guardarlas en un ZIP
    async function downloadAsZip(urls) {
        const zip = new JSZip();

        for (let i = 0; i < urls.length; i++) {
            const url = urls[i];
            console.log(`📄 Descargando (${i+1}/${urls.length}):`, url);
            try {
                const resp = await fetch(url);
                const blob = await resp.blob();
                // Nombre con 3 dígitos
                zip.file(`${String(i+1).padStart(3, '0')}.jpg`, blob);
            } catch (err) {
                console.error(`Error al descargar ${url}:`, err);
            }
        }

        const zipContent = await zip.generateAsync({ type: 'blob' });
        saveAs(zipContent, 'ynjn_manga.zip');
        alert(`✅ Descarga completa: ${urls.length} imágenes.`);
    }

    function resetButton() {
        const btn = document.querySelector('button');
        if (btn) {
            btn.disabled = false;
            btn.textContent = 'Descargar Manga (YNJN)';
        }
    }
})();