Greasy Fork

Yanmaga Manga Capture & ZIP (v2025 actualizado mejorado)

Captura imágenes completas del manga desde yanmaga.jp, las organiza en una galería y permite descargar como ZIP.

当前为 2025-06-18 提交的版本,查看 最新版本

// ==UserScript==
// @name         Yanmaga Manga Capture & ZIP (v2025 actualizado mejorado)
// @namespace    yanmaga-capture
// @version      2.3
// @description  Captura imágenes completas del manga desde yanmaga.jp, las organiza en una galería y permite descargar como ZIP.
// @author       ChatGPT
// @match        https://yanmaga.jp/viewer/*
// @require      https://cdn.jsdelivr.net/npm/[email protected]/dist/jszip.min.js
// @require      https://cdn.jsdelivr.net/npm/[email protected]/dist/FileSaver.min.js
// @grant        none
// @license      MIT
// ==/UserScript==

(function () {
    'use strict';

    const capturedImages = [];
    const container = document.createElement('div');
    container.style = 'position: fixed; top: 10px; right: 10px; z-index: 9999; display: flex; flex-direction: column; gap: 5px; background: rgba(0,0,0,0.6); padding: 10px; border-radius: 6px;';

    const captureBtn = document.createElement('button');
    captureBtn.textContent = '📸 Capturar';
    const zipBtn = document.createElement('button');
    zipBtn.textContent = '⬇️ Descargar ZIP';
    const clearBtn = document.createElement('button');
    clearBtn.textContent = '🗑️ Limpiar';

    [captureBtn, zipBtn, clearBtn].forEach(btn => {
        btn.style = 'padding: 6px 10px; background: #fff; color: #000; border: none; border-radius: 4px; cursor: pointer; font-weight: bold;';
        container.appendChild(btn);
    });
    document.body.appendChild(container);

    const gallery = document.createElement('div');
    gallery.style = 'position: fixed; bottom: 10px; left: 10px; background: #fff; padding: 10px; max-height: 50vh; overflow-y: auto; z-index: 9998; border: 1px solid #aaa; border-radius: 5px; box-shadow: 0 2px 6px rgba(0,0,0,0.2);';
    document.body.appendChild(gallery);

    function addToGallery(img, index) {
        const wrapper = document.createElement('div');
        wrapper.style = 'margin-bottom: 8px;';
        const label = document.createElement('div');
        label.textContent = `📄 Página ${String(index + 1).padStart(3, '0')} - ${img.naturalWidth}x${img.naturalHeight}`;
        label.style = 'font-size: 12px; margin-bottom: 4px;';
        wrapper.appendChild(label);
        wrapper.appendChild(img);
        img.style.maxWidth = '120px';
        gallery.appendChild(wrapper);
    }

    async function getMangaImages() {
        const allImgs = [...document.querySelectorAll('img')];
        const valid = allImgs.filter(img => {
            const src = img.src || '';
            return (
                src.includes('/viewer/pages/') ||
                src.includes('/viewer/image?') ||
                src.includes('/pages/') ||
                (img.naturalWidth > 800 && img.naturalHeight > 1200)
            );
        });
        return valid;
    }

    captureBtn.onclick = async () => {
        capturedImages.length = 0;
        gallery.innerHTML = '';

        const images = await getMangaImages();
        if (images.length === 0) {
            alert('❌ No se encontraron imágenes grandes. Desplázate por el manga para que cargue.');
            return;
        }

        for (let i = 0; i < images.length; i++) {
            const originalImg = images[i];
            const img = new Image();
            img.crossOrigin = 'anonymous';
            img.src = originalImg.src;
            await new Promise((resolve, reject) => {
                img.onload = () => {
                    const canvas = document.createElement('canvas');
                    canvas.width = img.naturalWidth;
                    canvas.height = img.naturalHeight;
                    const ctx = canvas.getContext('2d');
                    ctx.drawImage(img, 0, 0);
                    canvas.toBlob(blob => {
                        if (blob) {
                            capturedImages.push({
                                name: `pagina_${String(i + 1).padStart(3, '0')}_${img.naturalWidth}x${img.naturalHeight}.jpg`,
                                blob: blob,
                                thumb: img
                            });
                            addToGallery(img, i);
                            resolve();
                        } else reject('❌ No se pudo crear blob');
                    }, 'image/jpeg');
                };
                img.onerror = () => reject('❌ Error al cargar imagen');
            });
        }

        alert(`✅ Se capturaron ${capturedImages.length} páginas.`);
    };

    zipBtn.onclick = async () => {
        if (capturedImages.length === 0) {
            alert('⚠️ No hay imágenes para descargar.');
            return;
        }
        const zip = new JSZip();
        capturedImages.forEach(({ name, blob }) => {
            zip.file(name, blob);
        });
        const content = await zip.generateAsync({ type: 'blob' });
        saveAs(content, 'yanmaga.zip');
    };

    clearBtn.onclick = () => {
        capturedImages.length = 0;
        gallery.innerHTML = '';
        alert('🧹 Galería limpiada.');
    };
})();