Manga-Gai Downloader v3.5 (Scroll + Memoria Persistente)

Descarga imágenes de manga con scroll infinito y memoria persistente entre páginas.

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Manga-Gai Downloader v3.5 (Scroll + Memoria Persistente)
// @namespace    manga-gai
// @version      3.5
// @description  Descarga imágenes de manga con scroll infinito y memoria persistente entre páginas.
// @author       david
// @license      MIT
// @match        *://manga-gai.net/*
// @match        *://www.manga-gai.net/*
// @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
// ==/UserScript==

(function () {
  'use strict';

  window.addEventListener('load', () => {
    const mangaId = location.pathname.split('/')[2] || 'manga_gai_default';
    const STORAGE_KEY = 'mangagai_images_' + mangaId;

    // ======= BOTONES =======
    const panel = document.createElement('div');
    panel.style.cssText = `
      position: fixed; top: 10px; right: 10px; z-index: 999999;
      display: flex; flex-direction: column; gap: 8px;
    `;

    const btnDownload = document.createElement('button');
    const btnRefresh = document.createElement('button');
    const btnAutoScroll = document.createElement('button');
    const btnClear = document.createElement('button');

    const buttonStyle = `
      font-weight: bold; border: none; padding: 8px 14px; border-radius: 8px; cursor: pointer; color: white;
    `;

    btnDownload.textContent = '📥 Descargar Seleccionadas';
    btnDownload.style.cssText = `background:#22c55e;${buttonStyle}`;
    btnRefresh.textContent = '🔄 Actualizar lista';
    btnRefresh.style.cssText = `background:#3b82f6;${buttonStyle}`;
    btnAutoScroll.textContent = '⬇️ Scroll Automático';
    btnAutoScroll.style.cssText = `background:#a855f7;${buttonStyle}`;
    btnClear.textContent = '🗑️ Limpiar Memoria';
    btnClear.style.cssText = `background:#ef4444;${buttonStyle}`;

    [btnDownload, btnRefresh, btnAutoScroll, btnClear].forEach(b => panel.appendChild(b));
    document.body.appendChild(panel);

    // ======= GALERÍA =======
    const gallery = document.createElement('div');
    gallery.style.cssText = `
      position: fixed; top: 120px; right: 10px; width: 190px; height: 70vh;
      overflow-y: auto; background: rgba(0,0,0,0.85); color: white;
      border-radius: 10px; padding: 10px; z-index: 999999;
      display: flex; flex-direction: column; align-items: center;
      font-size: 12px;
    `;
    document.body.appendChild(gallery);

    // ======= MEMORIA LOCAL =======
    let imageSet = new Set(JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]'));

    function saveToStorage() {
      localStorage.setItem(STORAGE_KEY, JSON.stringify(Array.from(imageSet)));
    }

    function clearStorage() {
      localStorage.removeItem(STORAGE_KEY);
      imageSet.clear();
      renderGallery();
    }

    // ======= DETECTAR IMÁGENES =======
    function findMangaImages() {
      const imgs = Array.from(document.querySelectorAll('img'))
        .map(img => img.src)
        .filter(src =>
          src &&
          !src.startsWith('data:') &&
          src.match(/\.(jpg|jpeg|png|webp|gif)/i) &&
          (/\/manga\//i.test(src) || /\/[0-9]{2,}\.(jpg|jpeg|png|webp|gif)$/i.test(src))
        );

      let added = 0;
      imgs.forEach(src => {
        if (!imageSet.has(src)) {
          imageSet.add(src);
          added++;
        }
      });
      if (added > 0) saveToStorage();
      renderGallery();
    }

    // ======= RENDER GALERÍA =======
    function renderGallery() {
      gallery.innerHTML = '<b>📚 Páginas encontradas:</b>';
      Array.from(imageSet).forEach((src, i) => {
        const box = document.createElement('div');
        box.style.cssText = 'margin:6px;text-align:center;background:#222;padding:5px;border-radius:6px;';
        const img = document.createElement('img');
        img.src = src;
        img.style.cssText = 'width:100%;max-height:140px;object-fit:cover;border-radius:4px;';
        const chk = document.createElement('input');
        chk.type = 'checkbox';
        chk.checked = true;
        chk.dataset.src = src;
        const lbl = document.createElement('div');
        lbl.textContent = String(i + 1).padStart(3, '0');
        box.appendChild(img);
        box.appendChild(lbl);
        box.appendChild(chk);
        gallery.appendChild(box);
      });
    }

    // ======= DESCARGAR SELECCIONADAS =======
    async function downloadSelected() {
      const selected = Array.from(gallery.querySelectorAll('input[type="checkbox"]:checked')).map(cb => cb.dataset.src);
      if (!selected.length) return alert('❌ No seleccionaste ninguna imagen.');

      const zip = new JSZip();
      for (let i = 0; i < selected.length; i++) {
        btnDownload.textContent = `📥 ${i + 1}/${selected.length}`;
        try {
          const res = await fetch(selected[i]);
          const blob = await res.blob();
          zip.file(String(i + 1).padStart(3, '0') + '.jpg', blob);
        } catch (e) {
          console.warn('Error al descargar:', selected[i]);
        }
      }
      btnDownload.textContent = '📦 Generando ZIP...';
      const zipContent = await zip.generateAsync({ type: 'blob' });
      const title = document.title.replace(/[^a-z0-9]/gi, '_').toLowerCase();
      saveAs(zipContent, title + '.zip');
      btnDownload.textContent = '📥 Descargar Seleccionadas';
    }

    // ======= SCROLL INFINITO =======
    let autoScrollActive = false;
    btnAutoScroll.onclick = async () => {
      autoScrollActive = !autoScrollActive;
      btnAutoScroll.textContent = autoScrollActive ? '⏹️ Detener Scroll' : '⬇️ Scroll Automático';
      while (autoScrollActive) {
        window.scrollBy(0, 800);
        findMangaImages();
        await new Promise(r => setTimeout(r, 1000));
        if (window.innerHeight + window.scrollY >= document.body.scrollHeight) break;
      }
    };

    // ======= EVENTOS =======
    btnDownload.onclick = downloadSelected;
    btnRefresh.onclick = findMangaImages;
    btnClear.onclick = clearStorage;

    // ======= CARGA INICIAL =======
    renderGallery();
    findMangaImages();
  });
})();