// ==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)';
}
}
})();