// 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'); const btnDebugPages = document.getElementById('btn-debug-pages'); if (btnDownloadSelected) { btnDownloadSelected.addEventListener('click', (e) => { console.log('🎯 BUTTON CLICKED: Descargar Seleccionados'); console.log('📊 selectedMangas Set contents:', selectedMangas); console.log('📊 selectedMangas size:', selectedMangas.size); downloadSelectedMangas(); }); } else { console.error('❌ btnDownloadSelected not found!'); } if (btnDownloadAll) { btnDownloadAll.addEventListener('click', (e) => { console.log('🎯 BUTTON CLICKED: Descargar TODOS'); downloadAllMangas(); }); } else { console.error('❌ btnDownloadAll not found!'); } if (btnClearSelection) { btnClearSelection.addEventListener('click', (e) => { console.log('🎯 BUTTON CLICKED: Limpiar Selección'); selectedMangas.clear(); updateSelectedCount(); const countEl = document.getElementById('selected-count'); if (countEl) countEl.textContent = '0'; }); } else { console.error('❌ btnClearSelection not found!'); } if (btnDebugPages) { btnDebugPages.addEventListener('click', (e) => { console.log('🎯 BUTTON CLICKED: Debug Contar Páginas'); debugCountPagesForAllManga(); }); } else { console.error('❌ btnDebugPages not found!'); } // 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'); downloadSelectedMangas(); sendResponse({ success: true }); } else if (request.action === 'triggerDownloadAll') { console.log('🎯 Trigger: Descargar TODOS'); downloadAllMangas(); 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 - Patrón ESPECÍFICO para galerías let actualTotalPages = 1; console.log('🔍 Iniciando detección de páginas para:', manga.title); // Buscar en TODOS los divs para encontrar el conteo de páginas let foundPageCount = false; const allDivs = doc.querySelectorAll('div'); console.log(`🔍 Buscando en ${allDivs.length} divs...`); const potentialMatches = []; for (let div of allDivs) { const text = div.textContent.trim(); // Patrón específico: número seguido de "pages" o "page" const pageMatch = text.match(/^(\d+)\s+pages?$/i); if (pageMatch) { const pageCount = parseInt(pageMatch[1]); potentialMatches.push({ div: div, text: text, count: pageCount, className: div.className, id: div.id }); console.log(`🔍 Found potential match: "${text}" -> ${pageCount} pages (class: ${div.className}, id: ${div.id})`); // Validar que sea un número razonable (1-100 páginas) if (pageCount > 0 && pageCount <= 100) { actualTotalPages = pageCount; foundPageCount = true; console.log(`✓✅ ACCEPTED: "${text}" -> ${actualTotalPages} páginas (class: ${div.className})`); break; } else { console.log(`❌ REJECTED: ${pageCount} pages (out of range 1-100)`); } } } console.log(`📊 Total potential matches found: ${potentialMatches.length}`); if (potentialMatches.length > 0) { console.log('📋 All matches:', potentialMatches); } // Si no se encontró, usar patrones alternativos if (!foundPageCount) { console.log('⚠️ No se encontró patrón específico, buscando alternativas...'); 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`); foundPageCount = true; } } } // FALLBACK: Solo si no se encontró nada específico if (!foundPageCount) { console.log('⚠️ Usando fallback (puede ser impreciso)'); const allText = doc.body.textContent; // Buscar "Length: X pages" o similar const lengthMatch = allText.match(/Length[:\s]+(\d+)\s*pages?/i); if (lengthMatch) { actualTotalPages = parseInt(lengthMatch[1]); console.log(`✓ Fallback "Length": ${actualTotalPages} páginas`); } else { // Si no hay información, asumir 1 página console.log('⚠️ No se pudo detectar página count, asumiendo 1 página'); actualTotalPages = 1; } } console.log(`📄 Total páginas final 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); } // Descargar manga seleccionados async function downloadSelectedMangas() { console.log('\n========== 🔥 DOWNLOAD SELECTED MANGAS STARTED =========='); console.log('🔍 Function called!'); console.log('📊 selectedMangas Set:', selectedMangas); console.log('📊 selectedMangas size:', selectedMangas.size); console.log('📊 mangaMetadata Map size:', mangaMetadata.size); const selectedObjects = []; selectedMangas.forEach(id => { const mangaObj = mangaMetadata.get(id); console.log(`📝 Checking ID ${id}:`, mangaObj); if (mangaObj) { selectedObjects.push(mangaObj); } }); console.log(`📦 Final selectedObjects array length: ${selectedObjects.length}`); console.log('📦 selectedObjects contents:', selectedObjects); if (selectedObjects.length === 0) { console.log('❌ No manga selected, showing alert'); alert('No hay manga seleccionados. Selecciona algunos manga primero.'); return; } console.log(`✅ Proceeding with download of ${selectedObjects.length} manga...`); // Descargar cada manga console.log(`🔄 Starting download loop for ${selectedObjects.length} manga...`); for (let i = 0; i < selectedObjects.length; i++) { const manga = selectedObjects[i]; const title = manga.title ? manga.title.substring(0, 50) : 'Manga sin título'; console.log(`\n--- 📦 MANGA ${i + 1}/${selectedObjects.length}: ${title} ---`); updateProgress(i, selectedObjects.length, title, `Descargando manga ${i + 1} de ${selectedObjects.length}...`); try { // Obtener URLs de imágenes console.log(`🔍 Getting image URLs for manga ID: ${manga.id}`); const imageUrls = await getImageUrlsForManga(manga); console.log(`✅ ${manga.title}: ${imageUrls.length} imágenes encontradas`); // Enviar al background para descarga console.log(`📨 Sending to background script...`); const response = await chrome.runtime.sendMessage({ action: 'downloadManga', metadata: manga, imageUrls: imageUrls }); console.log(`📨 Background response:`, response); if (!response || !response.success) { console.error(`❌ Error descargando ${manga.title}:`, response ? response.error : 'No response'); } else { console.log(`✅ Descargado exitosamente: ${manga.title}`); } } catch (error) { console.error(`❌❌❌ EXCEPTION in manga ${manga.title}:`, error); console.error(`Stack trace:`, error.stack); // Continue with next manga even if this one fails } console.log(`--- ✅ Finished manga ${i + 1} ---\n`); } console.log('🔄 Download loop completed!'); updateProgress(selectedObjects.length, selectedObjects.length, '¡Completado!', 'Descarga finalizada'); setTimeout(() => { hideProgress(); }, 2000); } // Descargar TODOS los manga de la página async function downloadAllMangas() { console.log('📥 Iniciando descarga de TODOS los manga...'); const allMangas = extractAllMangasFromPage(); if (allMangas.length === 0) { alert('No se encontraron manga en esta página.'); return; } console.log(`📦 Manga encontrados: ${allMangas.length}`); // Descargar cada manga for (let i = 0; i < allMangas.length; i++) { const manga = allMangas[i]; const title = manga.title ? manga.title.substring(0, 50) : 'Manga sin título'; updateProgress(i, allMangas.length, title, `Descargando manga ${i + 1} de ${allMangas.length}...`); try { // Obtener URLs de imágenes const imageUrls = await getImageUrlsForManga(manga); console.log(`✅ ${manga.title}: ${imageUrls.length} imágenes encontradas`); // Enviar al background para descarga const response = await chrome.runtime.sendMessage({ action: 'downloadManga', metadata: manga, imageUrls: imageUrls }); if (!response.success) { console.error(`❌ Error descargando ${manga.title}:`, response.error); } else { console.log(`✅ Descargado: ${manga.title}`); } } catch (error) { console.error(`❌ Error en manga ${manga.title}:`, error.message); } } updateProgress(allMangas.length, allMangas.length, '¡Completado!', 'Descarga finalizada'); setTimeout(() => { hideProgress(); }, 2000); } // Debug: Contar páginas para todos los manga en la página async function debugCountPagesForAllManga() { console.log('\n========== 🔍 DEBUG: PAGE COUNT DETECTION =========='); console.log('🔍 Scanning all manga on page...'); const allMangas = extractAllMangasFromPage(); console.log(`📦 Found ${allMangas.length} manga on page`); const results = []; for (let i = 0; i < allMangas.length; i++) { const manga = allMangas[i]; console.log(`\n--- 🔍 MANGA ${i + 1}/${allMangas.length}: ${manga.title} ---`); console.log(`🔗 URL: ${manga.baseUrl}`); try { const response = await fetch(manga.baseUrl, { credentials: 'include' }); if (!response.ok) { console.error(`❌ Error ${response.status} fetching page`); continue; } const html = await response.text(); const parser = new DOMParser(); const doc = parser.parseFromString(html, 'text/html'); // Usar la misma lógica de detección let detectedPages = 'UNKNOWN'; const allDivs = doc.querySelectorAll('div'); for (let div of allDivs) { const text = div.textContent.trim(); const pageMatch = text.match(/^(\d+)\s+pages?$/i); if (pageMatch) { detectedPages = parseInt(pageMatch[1]); console.log(`✓ Found: "${text}" = ${detectedPages} pages (class: ${div.className})`); break; } } results.push({ title: manga.title, url: manga.baseUrl, pages: detectedPages }); console.log(`📊 Result: ${detectedPages} pages`); } catch (error) { console.error(`❌ Error processing manga:`, error); } } console.log('\n========== 📊 FINAL RESULTS =========='); results.forEach((result, idx) => { console.log(`${idx + 1}. ${result.pages} pages - ${result.title}`); }); console.log('========================================\n'); } 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(); } })();