Files
manga-mass-downloader/content.js
renato97 829996b41e Initial commit: Manga Mass Downloader Chrome Extension
 Features:
- Multi-selection checkboxes on manga listings
- Batch download selected manga or all manga from page
- Optimized parallel downloading (20ms delays, 5 concurrent)
- Visual progress tracking
- Popup UI for easy control
- Fixed duplicate checkbox issue with deduplication logic

📁 Files:
- manifest.json: Extension configuration
- content.js: Checkbox injection & manga detection
- background.js: Optimized download engine
- popup.html/js: User interface
- README.md: Complete documentation
2025-11-04 04:25:25 +00:00

311 lines
9.5 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;
// Escuchar mensajes del popup
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request.action === 'getSelectedMangas') {
const selected = Array.from(selectedMangas);
sendResponse({ mangas: selected });
} else if (request.action === 'clearSelection') {
selectedMangas.clear();
updateSelectedCount();
sendResponse({ success: true });
} else if (request.action === 'getImageUrls') {
getImageUrlsForManga(request.manga)
.then(imageUrls => sendResponse({ imageUrls }))
.catch(error => sendResponse({ error: error.message }));
return true;
} else if (request.action === 'extractAllMangas') {
const allMangas = extractAllMangasFromPage();
sendResponse({ mangas: allMangas });
}
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
let actualTotalPages = 1;
const pageInfo = doc.querySelector('.gpc, .gt, #gdn + span');
if (pageInfo) {
const pageText = pageInfo.textContent.trim();
const 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);
}
}
// Procesar todas las páginas
for (let page = 1; page <= actualTotalPages; page++) {
const pageUrl = page === 1 ? baseUrl : `${baseUrl}?p=${page}`;
const pageResponse = await fetch(pageUrl, {
credentials: 'include'
});
if (!pageResponse.ok) continue;
const pageHtml = await pageResponse.text();
const pageDoc = parser.parseFromString(pageHtml, 'text/html');
const links = pageDoc.querySelectorAll('a[href*="/s/"]');
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));
}
}
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;
}
console.log(` 📝 [${index}] Creando checkbox para manga ID: ${match ? match[1] : 'N/A'}`);
// 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();
const match = link.href.match(/\/g\/(\d+)\/([a-f0-9]+)/);
if (match) {
if (checkbox.checked) {
selectedMangas.add(match[1]);
} else {
selectedMangas.delete(match[1]);
}
updateSelectedCount();
}
});
container.appendChild(checkbox);
container.appendChild(label);
// Agregar al parent
if (getComputedStyle(parent).position === 'static') {
parent.style.position = 'relative';
}
parent.appendChild(container);
count++;
console.log(` ✅ Checkbox ${count} agregado y appendeado al parent <${parent.tagName}>`);
});
console.log('\n==== RESUMEN FINAL ====');
console.log('🎉 Total checkboxes CREADOS:', count);
console.log('📊 Total checkboxes en DOM:', document.querySelectorAll('.mass-downloader-checkbox').length);
console.log('📊 Set selectedMangas size:', selectedMangas.size);
console.log('==== addCheckboxes() COMPLETADO ====\n');
checkboxesAdded = true;
}
function updateSelectedCount() {
chrome.runtime.sendMessage({
action: 'updateSelectedCount',
count: selectedMangas.size
}).catch(() => {});
}
function addSelectAllButton() {
if (document.querySelector('.mass-downloader-select-all')) return;
const button = document.createElement('button');
button.className = 'mass-downloader-select-all';
button.textContent = 'Select All';
button.style.cssText = `
position: fixed;
bottom: 20px;
left: 20px;
padding: 10px 20px;
background: #667eea;
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
z-index: 10000;
`;
let allSelected = false;
button.addEventListener('click', () => {
allSelected = !allSelected;
const checkboxes = document.querySelectorAll('.mass-downloader-checkbox input');
if (allSelected) {
checkboxes.forEach(cb => {
cb.checked = true;
const match = cb.closest('div').parentElement.querySelector('a[href*="/g/"]').href.match(/\/g\/(\d+)/);
if (match) selectedMangas.add(match[1]);
});
button.textContent = 'Deselect All';
} else {
checkboxes.forEach(cb => cb.checked = false);
selectedMangas.clear();
button.textContent = 'Select All';
}
updateSelectedCount();
});
document.body.appendChild(button);
}
function init() {
if (window.location.href.includes('/g/')) return;
setTimeout(() => {
addCheckboxes();
addSelectAllButton();
}, 1000);
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();