您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Descarga capítulos completos de Viz Manga y Shonen Jump, detectando dinámicamente todas las páginas de manga cargadas en el lector propio y excluyendo imágenes inválidas.
// ==UserScript== // @name Viz Manga Universal Downloader v3.0.0 // @namespace shadows // @version 3.1.0 // @description Descarga capítulos completos de Viz Manga y Shonen Jump, detectando dinámicamente todas las páginas de manga cargadas en el lector propio y excluyendo imágenes inválidas. // @author // @license MIT // @match https://www.viz.com/vizmanga/* // @match https://www.viz.com/shonenjump/* // @grant GM_download // @connect viz.com // ==/UserScript== "use strict"; (function () { const MIN_DIMENSION = 500; // Dimensiones mínimas de las imágenes (ancho/alto) const MAX_MONOCHROME_RATIO = 0.95; // Porcentaje máximo permitido de negro o blanco puro en una imagen // Función para hacer scroll y cargar todas las páginas const scrollToLoadAllPages = async () => { let previousHeight; while (true) { window.scrollBy(0, window.innerHeight); await new Promise(resolve => setTimeout(resolve, 1000)); // Esperar un segundo const currentHeight = document.body.scrollHeight; if (currentHeight === previousHeight) break; // Si no hay más contenido, salir del bucle previousHeight = currentHeight; } }; const createDownloadButton = () => { if (document.querySelector("#download-button")) return; const button = document.createElement("button"); button.id = "download-button"; button.textContent = "Descargar capítulo completo"; button.style = ` position: fixed; top: 10px; right: 10px; z-index: 10000; background-color: #0078ff; color: white; border: none; padding: 10px 20px; font-size: 14px; border-radius: 5px; cursor: pointer; `; document.body.appendChild(button); button.addEventListener("click", async () => { button.disabled = true; button.textContent = "Descargando..."; try { // Hacer scroll para cargar todas las páginas await scrollToLoadAllPages(); // Esperar a que todas las páginas estén cargadas await waitForPagesToLoad(); // Descargar todas las páginas await downloadAllPages(); } catch (err) { console.error("Error al descargar:", err); alert("Hubo un problema al descargar el capítulo."); } finally { button.disabled = false; button.textContent = "Descargar capítulo completo"; } }); }; const waitForPagesToLoad = async () => { return new Promise((resolve, reject) => { let attempts = 0; const maxAttempts = 30; // Tiempo máximo de espera (30 intentos de 500ms = 15s) const interval = setInterval(() => { const canvases = document.querySelectorAll("canvas.reader_page_canvas"); if (canvases.length > 0) { clearInterval(interval); resolve(); } else if (attempts >= maxAttempts) { clearInterval(interval); reject(new Error("No se pudieron cargar todas las páginas en el tiempo esperado.")); } attempts++; }, 500); }); }; const downloadAllPages = async () => { const canvases = document.querySelectorAll("canvas.reader_page_canvas"); if (canvases.length === 0) { alert("No se encontraron páginas para descargar."); return; } let downloadedCount = 0; let errors = []; for (let i = 0; i < canvases.length; i++) { const canvas = canvases[i]; const fileName = `page-${String(i + 1).padStart(3, "0")}.png`; try { if (!isCanvasValid(canvas)) { console.log(`Página ${i + 1} descartada: no cumple los criterios.`); continue; } const imageURL = canvas.toDataURL("image/png"); await downloadImage(imageURL, fileName); downloadedCount++; console.log(`Descargada: ${fileName}`); } catch (err) { console.error(`Error al descargar ${fileName}:`, err); errors.push(fileName); } } alert(`Se descargaron ${downloadedCount} imágenes correctamente.${errors.length > 0 ? `\nErrores: ${errors.join(", ")}` : ""}`); }; const isCanvasValid = (canvas) => { // Verificar dimensiones mínimas if (canvas.width < MIN_DIMENSION || canvas.height < MIN_DIMENSION) { console.log("Canvas descartado: dimensiones demasiado pequeñas."); return false; } const context = canvas.getContext("2d"); const imageData = context.getImageData(0, 0, canvas.width, canvas.height); const pixels = imageData.data; let totalPixels = 0; let blackPixels = 0; let whitePixels = 0; for (let i = 0; i < pixels.length; i += 4) { const r = pixels[i]; const g = pixels[i + 1]; const b = pixels[i + 2]; totalPixels++; if (r === 0 && g === 0 && b === 0) blackPixels++; if (r === 255 && g === 255 && b === 255) whitePixels++; } const blackRatio = blackPixels / totalPixels; const whiteRatio = whitePixels / totalPixels; if (blackRatio > MAX_MONOCHROME_RATIO || whiteRatio > MAX_MONOCHROME_RATIO) { console.log("Canvas descartado: demasiados píxeles monocromáticos."); return false; } return true; }; const downloadImage = (url, fileName) => { return new Promise((resolve, reject) => { const link = document.createElement("a"); link.href = url; link.download = fileName; document.body.appendChild(link); link.click(); document.body.removeChild(link); resolve(); }); }; const initObserver = () => { const observer = new MutationObserver(() => { if (document.querySelector("canvas.reader_page_canvas")) { createDownloadButton(); } }); observer.observe(document.body, { childList: true, subtree: true }); }; window.addEventListener("load", () => { createDownloadButton(); initObserver(); }); })();