// ==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 // @downloadURL 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 y function gatherImageUrls() { // 1. Imágenes en (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 (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)'; } } })();