Changed from: /^(\d+)\s+pages?$/i (exact match only) To: /(\d+)\s+pages?/i (matches anywhere in text) Added: - Shows ALL text matches found - Logs which div contains the match - Searches in body text as fallback - Better visibility into what text exists This will reveal why page detection is failing!
890 lines
32 KiB
JavaScript
890 lines
32 KiB
JavaScript
// 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 = `
|
||
<div style="text-align: center;">
|
||
<h3 style="margin: 0 0 15px 0; font-size: 18px; font-weight: bold;">📦 Mass Downloader</h3>
|
||
|
||
<!-- Panel de control (cuando no está descargando) -->
|
||
<div id="control-panel" style="display: block;">
|
||
<div style="font-size: 12px; margin-bottom: 10px; opacity: 0.9;">
|
||
Seleccionados: <span id="selected-count">0</span>
|
||
</div>
|
||
<button id="btn-download-selected" style="width: 100%; padding: 10px; margin: 5px 0; background: #4CAF50; color: white; border: none; border-radius: 6px; cursor: pointer; font-weight: bold;">⬇️ Descargar Seleccionados</button>
|
||
<button id="btn-download-all" style="width: 100%; padding: 10px; margin: 5px 0; background: #2196F3; color: white; border: none; border-radius: 6px; cursor: pointer; font-weight: bold;">📚 Descargar TODOS</button>
|
||
<button id="btn-clear-selection" style="width: 100%; padding: 10px; margin: 5px 0; background: #ff9800; color: white; border: none; border-radius: 6px; cursor: pointer; font-weight: bold;">🗑️ Limpiar Selección</button>
|
||
<button id="btn-debug-pages" style="width: 100%; padding: 10px; margin: 5px 0; background: #9C27B0; color: white; border: none; border-radius: 6px; cursor: pointer; font-weight: bold; font-size: 11px;">🔍 Debug: Contar Páginas</button>
|
||
</div>
|
||
|
||
<!-- Panel de progreso (cuando está descargando) -->
|
||
<div id="progress-panel" style="display: none;">
|
||
<div id="progress-title" style="font-size: 14px; font-weight: bold; margin: 10px 0;">Preparando...</div>
|
||
<div style="background: rgba(255,255,255,0.4); height: 12px; border-radius: 6px; overflow: hidden;">
|
||
<div id="progress-bar-fill" style="height: 100%; width: 0%; background: white; transition: width 0.3s ease;"></div>
|
||
</div>
|
||
<div style="display: flex; justify-content: space-between; margin-top: 8px; font-size: 12px; font-weight: bold;">
|
||
<span id="progress-count">0 / 0</span>
|
||
<span id="progress-percent">0%</span>
|
||
</div>
|
||
<div id="progress-status" style="margin-top: 10px; font-size: 11px; opacity: 0.9;"></div>
|
||
</div>
|
||
</div>
|
||
`;
|
||
|
||
// 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');
|
||
console.log('📝 Button element:', btnDebugPages);
|
||
try {
|
||
debugCountPagesForAllManga();
|
||
} catch (error) {
|
||
console.error('❌❌❌ Error in debugCountPagesForAllManga:', error);
|
||
console.error('Stack:', error.stack);
|
||
}
|
||
});
|
||
} 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');
|
||
const allText = doc.body.textContent;
|
||
|
||
console.log('🔍 Searching in all divs...');
|
||
const textMatches = [];
|
||
|
||
for (let div of allDivs) {
|
||
const text = div.textContent.trim();
|
||
// Buscar CUALQUIER número seguido de "pages" o "page"
|
||
const pageMatch = text.match(/(\d+)\s+pages?/i);
|
||
if (pageMatch) {
|
||
const count = parseInt(pageMatch[1]);
|
||
textMatches.push({
|
||
text: text,
|
||
count: count,
|
||
className: div.className
|
||
});
|
||
console.log(`🔍 Found text "${text}" = ${count} pages (class: ${div.className})`);
|
||
}
|
||
}
|
||
|
||
console.log(`📊 Total text matches found: ${textMatches.length}`);
|
||
if (textMatches.length > 0) {
|
||
textMatches.forEach((match, idx) => {
|
||
console.log(` ${idx + 1}. "${match.text}" -> ${match.count} pages (class: ${match.className})`);
|
||
});
|
||
// Usar el primer match
|
||
detectedPages = textMatches[0].count;
|
||
console.log(`✅ Using: ${detectedPages} pages from "${textMatches[0].text}"`);
|
||
} else {
|
||
console.log('❌ No matches found in divs');
|
||
// Buscar en todo el texto
|
||
const bodyMatch = allText.match(/(\d+)\s+pages?/i);
|
||
if (bodyMatch) {
|
||
const count = parseInt(bodyMatch[1]);
|
||
console.log(`⚠️ Found in body text: ${count} pages`);
|
||
detectedPages = count;
|
||
} else {
|
||
console.log('❌ No page count found anywhere!');
|
||
}
|
||
}
|
||
|
||
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();
|
||
}
|
||
|
||
})();
|