// Content Script para Manga Mass Downloader // Basado en addon original - SIMPLIFICADO (function() { 'use strict'; let selectedMangas = new Set(); let checkboxesAdded = false; // Map para almacenar metadatos completos de manga por ID let mangaMetadata = new Map(); // Crear popup flotante de progreso function createFloatingProgressPopup() { // Remover popup anterior si existe const existingPopup = document.getElementById('mass-downloader-progress-popup'); if (existingPopup) { existingPopup.remove(); } const popup = document.createElement('div'); popup.id = 'mass-downloader-progress-popup'; popup.style.cssText = ` position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 400px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 25px; border-radius: 12px; box-shadow: 0 10px 40px rgba(0,0,0,0.5); z-index: 10001; font-family: Arial, sans-serif; display: none; `; popup.innerHTML = `

📦 Descargando Manga

Preparando descarga...
0 / 0 0%
`; document.body.appendChild(popup); return popup; } // Actualizar progreso function updateProgress(current, total, title, status) { const popup = document.getElementById('mass-downloader-progress-popup') || createFloatingProgressPopup(); const percent = total > 0 ? Math.round((current / total) * 100) : 0; const titleEl = document.getElementById('progress-title'); const countEl = document.getElementById('progress-count'); const percentEl = document.getElementById('progress-percent'); const statusEl = document.getElementById('progress-status'); const barEl = document.getElementById('progress-bar-fill'); if (titleEl) titleEl.textContent = title || 'Descargando...'; if (countEl) countEl.textContent = `${current} / ${total}`; if (percentEl) percentEl.textContent = `${percent}%`; if (statusEl) statusEl.textContent = status || ''; if (barEl) barEl.style.width = `${percent}%`; popup.style.display = 'block'; } // Ocultar progreso function hideProgress() { const popup = document.getElementById('mass-downloader-progress-popup'); if (popup) { setTimeout(() => { popup.style.opacity = '0'; popup.style.transition = 'opacity 0.5s ease'; setTimeout(() => { popup.remove(); }, 500); }, 1000); } } // Escuchar mensajes del popup chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { if (request.action === 'getSelectedMangas') { // Devolver objetos completos en lugar de solo IDs const selectedObjects = []; selectedMangas.forEach(id => { const mangaObj = mangaMetadata.get(id); if (mangaObj) { selectedObjects.push(mangaObj); } }); sendResponse({ mangas: selectedObjects }); } else if (request.action === 'clearSelection') { selectedMangas.clear(); updateSelectedCount(); sendResponse({ success: true }); } else if (request.action === 'getImageUrls') { getImageUrlsForManga(request.manga) .then(imageUrls => sendResponse({ imageUrls })) .catch(error => sendResponse({ error: error.message })); return true; } else if (request.action === 'extractAllMangas') { const allMangas = extractAllMangasFromPage(); sendResponse({ mangas: allMangas }); } else if (request.action === 'showProgress') { updateProgress(request.current, request.total, request.title, request.status); sendResponse({ success: true }); } else if (request.action === 'hideProgress') { hideProgress(); sendResponse({ success: true }); } return true; }); // Extraer TODOS los manga de la página function extractAllMangasFromPage() { const mangas = []; const links = document.querySelectorAll('a[href*="/g/"]'); links.forEach(link => { const href = link.href; const match = href.match(/\/g\/(\d+)\/([a-f0-9]+)/); if (match) { const title = link.textContent.trim() || 'Untitled'; mangas.push({ id: match[1], token: match[2], title: title, url: href, baseUrl: `https://e-hentai.org/g/${match[1]}/${match[2]}/` }); } }); return mangas; } // Obtener URLs de imágenes para un manga async function getImageUrlsForManga(manga) { const imageUrls = []; const baseUrl = manga.baseUrl; try { const response = await fetch(baseUrl, { credentials: 'include' }); if (!response.ok) { throw new Error(`Error ${response.status}`); } const html = await response.text(); const parser = new DOMParser(); const doc = parser.parseFromString(html, 'text/html'); // Detectar páginas - Múltiples patrones let actualTotalPages = 1; console.log('🔍 Iniciando detección de páginas para:', manga.title); const pageInfo = doc.querySelector('.gpc, .gt, #gdn + span'); if (pageInfo) { const pageText = pageInfo.textContent.trim(); console.log('🔍 Page info encontrado:', pageText); // Patrón 1: "Showing 1 - 20 of 220 images" let pageMatch = pageText.match(/Showing\s+1\s*-\s*\d+\s+of\s+(\d+)\s+images/i); if (pageMatch) { const totalImages = parseInt(pageMatch[1]); actualTotalPages = Math.ceil(totalImages / 20); console.log(`✓ Patrón "Showing": ${totalImages} imágenes = ${actualTotalPages} páginas`); } else { // Patrón 2: "5 pages" o "X pages" pageMatch = pageText.match(/(\d+)\s+pages?/i); if (pageMatch) { actualTotalPages = parseInt(pageMatch[1]); console.log(`✓ Patrón "pages": ${actualTotalPages} páginas`); } } } // Fallback: Buscar en todo el documento if (actualTotalPages === 1) { const allText = doc.body.textContent; const pageMatch = allText.match(/(\d+)\s+pages?/i); if (pageMatch) { actualTotalPages = parseInt(pageMatch[1]); console.log(`✓ Fallback: Encontradas ${actualTotalPages} páginas en el documento`); } else { console.log('⚠️ No se detectaron múltiples páginas, asumiendo 1 página'); } } console.log(`📄 Total páginas a procesar: ${actualTotalPages}`); // Procesar todas las páginas for (let page = 1; page <= actualTotalPages; page++) { const pageUrl = page === 1 ? baseUrl : `${baseUrl}?p=${page}`; console.log(`📄 Procesando página ${page}/${actualTotalPages}: ${pageUrl}`); const pageResponse = await fetch(pageUrl, { credentials: 'include' }); if (!pageResponse.ok) { console.error(`❌ Error ${pageResponse.status} en página ${page}`); continue; } const pageHtml = await pageResponse.text(); const pageDoc = parser.parseFromString(pageHtml, 'text/html'); const links = pageDoc.querySelectorAll('a[href*="/s/"]'); console.log(`✓ Página ${page}: Encontrados ${links.length} enlaces de imagen`); links.forEach(link => { const href = link.href; if (href && href.includes('/s/')) { imageUrls.push({ url: href, index: imageUrls.length }); } }); if (page < actualTotalPages) { await new Promise(resolve => setTimeout(resolve, 20)); } } console.log(`✅ Total de imágenes encontradas: ${imageUrls.length}`); return imageUrls; } catch (error) { console.error('Error:', error); throw error; } } // Agregar checkbox a enlaces de galerías function addCheckboxes() { console.log('\n==== addCheckboxes() INICIADO ===='); console.log('Timestamp:', new Date().toLocaleTimeString()); const existingCheckboxes = document.querySelectorAll('.mass-downloader-checkbox').length; console.log('📊 Checkboxes existentes antes de limpiar:', existingCheckboxes); // SOLUCIÓN DEFINITIVA: Limpiar TODOS los checkboxes existentes document.querySelectorAll('.mass-downloader-checkbox').forEach(cb => cb.remove()); selectedMangas.clear(); console.log('🧹 Checkboxes limpiados'); // Obtener TODOS los enlaces const allLinks = document.querySelectorAll('a[href*="/g/"]'); console.log('🔍 Total enlaces encontrados:', allLinks.length); // Log detallado de todos los enlaces const allLinkData = []; allLinks.forEach((link, index) => { const match = link.href.match(/\/g\/(\d+)\/([a-f0-9]+)/); if (match) { allLinkData.push({ index: index, id: match[1], href: link.href.substring(0, 60) + '...', parentTag: link.parentElement.tagName, parentClass: link.parentElement.className }); } }); console.log('📋 Detalle de enlaces (primeros 5):', allLinkData.slice(0, 5)); // DEDUPLICACIÓN ROBUSTA: Trackear IDs únicos durante la iteración const seenIds = new Set(); const uniqueLinks = []; allLinks.forEach(link => { const match = link.href.match(/\/g\/(\d+)/); if (match) { const galleryId = match[1]; if (!seenIds.has(galleryId)) { seenIds.add(galleryId); uniqueLinks.push(link); console.log(` ➕ ID único encontrado: ${galleryId} (href: ${link.href.substring(0, 50)}...)`); } else { console.log(` ⏭️ ID duplicado omitido: ${galleryId}`); } } }); console.log('✅ Total IDs únicos (sin duplicados):', uniqueLinks.length); console.log('✅ Set "seenIds" size:', seenIds.size); // Crear checkboxes SOLO para enlaces únicos let count = 0; console.log('\n🎯 CREANDO CHECKBOXES:'); uniqueLinks.forEach((link, index) => { const match = link.href.match(/\/g\/(\d+)\/([a-f0-9]+)/); const parent = link.parentElement; // Verificar que este parent no tenga ya un checkbox if (parent.querySelector('.mass-downloader-checkbox')) { console.log(` ⚠️ [${index}] Parent ya tiene checkbox, saltando...`); return; } const mangaId = match ? match[1] : null; const token = match ? match[2] : null; const title = link.textContent.trim() || 'Untitled'; console.log(` 📝 [${index}] Creando checkbox para manga ID: ${mangaId}`); // Guardar metadatos completos en el Map if (mangaId && token) { const metadata = { id: mangaId, token: token, title: title, url: link.href, baseUrl: `https://e-hentai.org/g/${mangaId}/${token}/` }; mangaMetadata.set(mangaId, metadata); console.log(` 💾 Metadatos guardados para ID ${mangaId}: ${title.substring(0, 30)}...`); } // Crear checkbox const container = document.createElement('div'); container.className = 'mass-downloader-checkbox'; container.style.cssText = ` position: absolute; top: 5px; left: 5px; z-index: 9999; background: rgba(0,0,0,0.8); padding: 4px 8px; border-radius: 4px; `; const checkbox = document.createElement('input'); checkbox.type = 'checkbox'; checkbox.style.cssText = 'width: 16px; height: 16px;'; const label = document.createElement('label'); label.textContent = 'Select'; label.style.cssText = 'color: white; font-size: 11px; margin-left: 4px;'; checkbox.addEventListener('change', (e) => { e.stopPropagation(); if (mangaId) { if (checkbox.checked) { selectedMangas.add(mangaId); console.log(` ☑️ Manga ${mangaId} seleccionado`); } else { selectedMangas.delete(mangaId); console.log(` ☐ Manga ${mangaId} deseleccionado`); } updateSelectedCount(); } }); container.appendChild(checkbox); container.appendChild(label); // Agregar al parent if (getComputedStyle(parent).position === 'static') { parent.style.position = 'relative'; } parent.appendChild(container); count++; console.log(` ✅ Checkbox ${count} agregado y appendeado al parent <${parent.tagName}>`); }); console.log('\n==== RESUMEN FINAL ===='); console.log('🎉 Total checkboxes CREADOS:', count); console.log('📊 Total checkboxes en DOM:', document.querySelectorAll('.mass-downloader-checkbox').length); console.log('📊 Set selectedMangas size:', selectedMangas.size); console.log('==== addCheckboxes() COMPLETADO ====\n'); checkboxesAdded = true; } function updateSelectedCount() { chrome.runtime.sendMessage({ action: 'updateSelectedCount', count: selectedMangas.size }).catch(() => {}); } function addSelectAllButton() { if (document.querySelector('.mass-downloader-select-all')) return; const button = document.createElement('button'); button.className = 'mass-downloader-select-all'; button.textContent = 'Select All'; button.style.cssText = ` position: fixed; bottom: 20px; left: 20px; padding: 10px 20px; background: #667eea; color: white; border: none; border-radius: 6px; cursor: pointer; z-index: 10000; `; let allSelected = false; button.addEventListener('click', () => { allSelected = !allSelected; const checkboxes = document.querySelectorAll('.mass-downloader-checkbox input'); if (allSelected) { checkboxes.forEach(cb => { cb.checked = true; const match = cb.closest('div').parentElement.querySelector('a[href*="/g/"]').href.match(/\/g\/(\d+)/); if (match) selectedMangas.add(match[1]); }); button.textContent = 'Deselect All'; } else { checkboxes.forEach(cb => cb.checked = false); selectedMangas.clear(); button.textContent = 'Select All'; } updateSelectedCount(); }); document.body.appendChild(button); } function init() { if (window.location.href.includes('/g/')) return; setTimeout(() => { addCheckboxes(); addSelectAllButton(); }, 1000); } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } })();