// ==UserScript== // @name Yanmaga Manga Capture (background fix) // @namespace yanmaga-capture // @version 2.2 // @description Captura imágenes completas, incluso si están como background-image. Guarda en galería y ZIP. // @author ChatGPT // @match https://yanmaga.jp/viewer/* // @require https://cdn.jsdelivr.net/npm/jszip@3.10.0/dist/jszip.min.js // @require https://cdn.jsdelivr.net/npm/file-saver@2.0.5/dist/FileSaver.min.js // @grant none // @license MIT // @downloadURL none // ==/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;'; 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: #111; color: #fff; border: none; border-radius: 5px; cursor: pointer; font-size: 14px;'; 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); } function extractBackgroundImageURL(el) { const bg = window.getComputedStyle(el).backgroundImage; if (!bg || bg === 'none') return null; const match = bg.match(/url\("?(.+?)"?\)/); return match ? match[1] : null; } async function getAllImageSources() { const imageURLs = new Set(); // tags document.querySelectorAll('img').forEach(img => { if (img.naturalWidth > 800 && img.naturalHeight > 1000) { imageURLs.add(img.src); } }); // background-image document.querySelectorAll('div, section').forEach(el => { const url = extractBackgroundImageURL(el); if (url) imageURLs.add(url); }); return Array.from(imageURLs); } captureBtn.onclick = async () => { capturedImages.length = 0; gallery.innerHTML = ''; const urls = await getAllImageSources(); if (urls.length === 0) { alert('No se encontraron imágenes válidas.'); return; } for (let i = 0; i < urls.length; i++) { const url = urls[i]; const img = new Image(); img.crossOrigin = 'anonymous'; img.src = url; 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(); }, 'image/jpeg'); }; img.onerror = () => reject(); }); } alert(`Capturadas ${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_manga.zip'); }; clearBtn.onclick = () => { capturedImages.length = 0; gallery.innerHTML = ''; alert('Galería limpiada.'); }; })();