Files
manga-mass-downloader/content.js
renato97 acaea82b77 Fix page detection and download loop issues
Fixed:
1. Page detection now looks for exact 'X pages' pattern in divs
2. Validates page count is reasonable (1-100)
3. Download loop continues even if one manga fails
4. Added comprehensive error handling and logging
5. Logs background responses to verify downloads
2025-11-04 05:29:05 +00:00

762 lines
27 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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>
</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');
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!');
}
// 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...`);
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]);
// Validar que sea un número razonable (1-100 páginas)
if (pageCount > 0 && pageCount <= 100) {
actualTotalPages = pageCount;
foundPageCount = true;
console.log(`✓ Patrón específico "X pages": ${actualTotalPages} páginas (div: ${div.className})`);
break;
}
}
}
// 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);
}
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();
}
})();