// 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() { console.log('🎨 Creando popup de progreso...'); // Remover popup anterior si existe const existingPopup = document.getElementById('mass-downloader-progress-popup'); if (existingPopup) { console.log('🗑️ Removiendo popup anterior'); existingPopup.remove(); } const popup = document.createElement('div'); popup.id = 'mass-downloader-progress-popup'; // Estilos inline para máxima compatibilidad popup.style.position = 'fixed'; popup.style.top = '20px'; popup.style.right = '20px'; popup.style.width = '320px'; popup.style.background = '#667eea'; popup.style.backgroundImage = 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)'; popup.style.color = 'white'; popup.style.padding = '20px'; popup.style.borderRadius = '12px'; popup.style.boxShadow = '0 8px 32px rgba(0,0,0,0.5)'; popup.style.zIndex = '999999'; // ¡Muy alto! popup.style.fontFamily = 'Arial, sans-serif'; popup.style.display = 'block'; // Visible por defecto popup.style.backdropFilter = 'blur(10px)'; popup.innerHTML = `

📦 Mass Downloader

Seleccionados: 0
`; // Agregar event listeners a los botones setTimeout(() => { const btnDownloadSelected = document.getElementById('btn-download-selected'); const btnDownloadAll = document.getElementById('btn-download-all'); const btnClearSelection = document.getElementById('btn-clear-selection'); if (btnDownloadSelected) { btnDownloadSelected.addEventListener('click', () => { console.log('🎯 Click: Descargar Seleccionados'); // Enviar mensaje al popup.js para ejecutar la descarga chrome.runtime.sendMessage({ action: 'triggerDownloadSelected' }); }); } if (btnDownloadAll) { btnDownloadAll.addEventListener('click', () => { console.log('🎯 Click: Descargar TODOS'); chrome.runtime.sendMessage({ action: 'triggerDownloadAll' }); }); } if (btnClearSelection) { btnClearSelection.addEventListener('click', () => { console.log('🎯 Click: Limpiar Selección'); selectedMangas.clear(); updateSelectedCount(); const countEl = document.getElementById('selected-count'); if (countEl) countEl.textContent = '0'; }); } // Actualizar contador inicialmente const countEl = document.getElementById('selected-count'); if (countEl) countEl.textContent = selectedMangas.size.toString(); }, 100); console.log('✅ Popup creado, agregando al DOM...'); document.body.appendChild(popup); console.log('✅ Popup agregado al DOM:', popup); return popup; } // Actualizar progreso function updateProgress(current, total, title, status) { console.log('📊 updateProgress() llamado:', { current, total, title, status }); let popup = document.getElementById('mass-downloader-progress-popup'); if (!popup) { console.log('⚠️ Popup no existe, creando uno nuevo...'); popup = createFloatingProgressPopup(); } else { console.log('✅ Popup existe, actualizando...'); } const percent = total > 0 ? Math.round((current / total) * 100) : 0; console.log(`📈 Porcentaje calculado: ${percent}%`); // Cambiar a panel de progreso const controlPanel = document.getElementById('control-panel'); const progressPanel = document.getElementById('progress-panel'); if (controlPanel && progressPanel) { controlPanel.style.display = 'none'; progressPanel.style.display = 'block'; console.log('🔄 Cambiado a panel de progreso'); } 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'); console.log('🔍 Elementos encontrados:', { titleEl: !!titleEl, countEl: !!countEl, percentEl: !!percentEl, statusEl: !!statusEl, barEl: !!barEl }); if (titleEl) { titleEl.textContent = title || 'Descargando...'; console.log('📝 Título actualizado:', titleEl.textContent); } if (countEl) { countEl.textContent = `${current} / ${total}`; console.log('🔢 Contador actualizado:', countEl.textContent); } if (percentEl) { percentEl.textContent = `${percent}%`; console.log('📊 Porcentaje actualizado:', percentEl.textContent); } if (statusEl) { statusEl.textContent = status || ''; console.log('💬 Status actualizado:', statusEl.textContent); } if (barEl) { barEl.style.width = `${percent}%`; console.log('📊 Barra actualizada:', `${percent}%`); } popup.style.display = 'block'; popup.style.opacity = '1'; console.log('✅ Popup visible, display:', popup.style.display); console.log('✅ Popup en pantalla:', popup.getBoundingClientRect()); } // Ocultar progreso function hideProgress() { console.log('🙈 hideProgress() llamado'); const popup = document.getElementById('mass-downloader-progress-popup'); if (popup) { // Cambiar de vuelta al panel de control const controlPanel = document.getElementById('control-panel'); const progressPanel = document.getElementById('progress-panel'); if (controlPanel && progressPanel) { progressPanel.style.display = 'none'; controlPanel.style.display = 'block'; console.log('🔄 Cambiado a panel de control'); // Actualizar contador const countEl = document.getElementById('selected-count'); if (countEl) { countEl.textContent = selectedMangas.size.toString(); console.log('🔢 Contador actualizado:', countEl.textContent); } } // NO remover el popup, solo cambiar de panel } } // Escuchar mensajes del popup chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { console.log('📨 MENSAJE RECIBIDO:', request.action, request); 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); } }); console.log('📤 Enviando mangas seleccionados:', selectedObjects.length); sendResponse({ mangas: selectedObjects }); } else if (request.action === 'clearSelection') { selectedMangas.clear(); updateSelectedCount(); sendResponse({ success: true }); } else if (request.action === 'getImageUrls') { console.log('📸 Solicitando URLs de imágenes para:', request.manga.title); getImageUrlsForManga(request.manga) .then(imageUrls => sendResponse({ imageUrls })) .catch(error => sendResponse({ error: error.message })); return true; } else if (request.action === 'extractAllMangas') { const allMangas = extractAllMangasFromPage(); console.log('📤 Enviando todos los mangas:', allMangas.length); sendResponse({ mangas: allMangas }); } else if (request.action === 'showProgress') { console.log('📊 Mostrando progreso:', request); updateProgress(request.current, request.total, request.title, request.status); sendResponse({ success: true }); } else if (request.action === 'hideProgress') { console.log('🙈 Ocultando progreso'); hideProgress(); sendResponse({ success: true }); } else if (request.action === 'updateSelectedCount') { const countEl = document.getElementById('selected-count'); if (countEl) { countEl.textContent = request.count.toString(); console.log('🔢 Contador actualizado:', countEl.textContent); } sendResponse({ success: true }); } else if (request.action === 'triggerDownloadSelected') { console.log('🎯 Trigger: Descargar Seleccionados'); // Enviar mensaje al popup.js para ejecutar la descarga chrome.runtime.sendMessage({ action: 'executeDownloadSelected' }); sendResponse({ success: true }); } else if (request.action === 'triggerDownloadAll') { console.log('🎯 Trigger: Descargar TODOS'); chrome.runtime.sendMessage({ action: 'executeDownloadAll' }); sendResponse({ success: true }); } else { console.log('❓ Acción desconocida:', request.action); } 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() { // Actualizar contador en el control panel const countEl = document.getElementById('selected-count'); if (countEl) { countEl.textContent = selectedMangas.size.toString(); console.log('🔢 Control panel actualizado:', selectedMangas.size); } // También enviar al popup (para compatibilidad) 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(); createFloatingProgressPopup(); // Crear control panel automáticamente }, 1000); } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } })();