const fs = require('fs'); const zlib = require('zlib'); const path = require('path'); const SQL_GZ_PATH = 'C:/Users/Carlos/Downloads/u858407019_Lq8sf.almeiraproducciones-com.20260607074508.sql.gz'; const OUT_JSON_PATH = path.join(__dirname, 'wp_extracted_data.json'); // Parse SQL tuple lines/blocks for a given table function createTableTupleParser(onRow) { let inTuple = false; let inString = false; let escapeNext = false; let currentField = ''; let currentRow = []; let tokenWasString = false; return function feedChunk(str) { for (let i = 0; i < str.length; i++) { const ch = str[i]; if (!inTuple) { if (ch === '(') { // Make sure this '(' is start of a tuple (not CREATE TABLE or INSERT INTO col list) inTuple = true; inString = false; escapeNext = false; currentField = ''; currentRow = []; tokenWasString = false; } continue; } if (inString) { if (escapeNext) { if (ch === 'n') currentField += '\n'; else if (ch === 'r') currentField += '\r'; else if (ch === 't') currentField += '\t'; else if (ch === '0') currentField += '\0'; else currentField += ch; escapeNext = false; } else if (ch === '\\') { escapeNext = true; } else if (ch === "'") { inString = false; } else { currentField += ch; } } else { if (ch === "'") { inString = true; tokenWasString = true; } else if (ch === ',') { const val = tokenWasString ? currentField : (currentField.trim() === 'NULL' ? null : currentField.trim()); currentRow.push(val); currentField = ''; tokenWasString = false; } else if (ch === ')') { const val = tokenWasString ? currentField : (currentField.trim() === 'NULL' ? null : currentField.trim()); currentRow.push(val); onRow(currentRow); inTuple = false; currentField = ''; currentRow = []; tokenWasString = false; } else { currentField += ch; } } } }; } async function run() { console.log('Extrayendo datos históricos de WordPress desde:', SQL_GZ_PATH); const terms = {}; // term_id -> { id, name, slug } const taxonomies = {}; // term_taxonomy_id -> { term_id, taxonomy } const postCategories = {}; // post_id -> [term_taxonomy_id] const posts = {}; // post_id -> post object const attachments = {}; // attachment_id -> { id, title, guid, mime } const postThumbnails = {}; // post_id -> attachment_id const attachedFiles = {}; // attachment_id -> relative path in wp-content/uploads const attachmentAlts = {}; // attachment_id -> alt text const targetTables = new Set([ 'wp_terms', 'wp_term_taxonomy', 'wp_term_relationships', 'wp_posts', 'wp_postmeta', ]); let currentTable = null; let insideValues = false; const parsers = { wp_terms: createTableTupleParser((row) => { if (row.length >= 3) { terms[row[0]] = { id: row[0], name: row[1], slug: row[2] }; } }), wp_term_taxonomy: createTableTupleParser((row) => { if (row.length >= 3 && row[2] === 'category') { taxonomies[row[0]] = { term_taxonomy_id: row[0], term_id: row[1], taxonomy: row[2] }; } }), wp_term_relationships: createTableTupleParser((row) => { if (row.length >= 2) { const objId = row[0]; const taxId = row[1]; if (!postCategories[objId]) postCategories[objId] = []; postCategories[objId].push(taxId); } }), wp_posts: createTableTupleParser((row) => { if (row.length < 22) return; const id = row[0]; const postDate = row[2]; const postDateGmt = row[3]; const postContent = row[4]; const postTitle = row[5]; const postExcerpt = row[6]; const postStatus = row[7]; const postName = row[11]; const postParent = row[17]; const guid = row[18]; const postType = row[20]; const postMimeType = row[21]; if (postType === 'post' && postStatus === 'publish') { posts[id] = { id, date: postDateGmt && postDateGmt !== '0000-00-00 00:00:00' ? postDateGmt : postDate, title: postTitle, content: postContent, excerpt: postExcerpt, slug: postName, guid, }; } else if (postType === 'attachment') { attachments[id] = { id, title: postTitle, guid, mime: postMimeType, parent: postParent, }; } }), wp_postmeta: createTableTupleParser((row) => { if (row.length < 4) return; const postId = row[1]; const metaKey = row[2]; const metaValue = row[3]; if (metaKey === '_thumbnail_id' && metaValue) { postThumbnails[postId] = String(metaValue).trim(); } else if (metaKey === '_wp_attached_file' && metaValue) { attachedFiles[postId] = String(metaValue).trim(); } else if (metaKey === '_wp_attachment_image_alt' && metaValue) { attachmentAlts[postId] = String(metaValue).trim(); } }), }; const stream = fs.createReadStream(SQL_GZ_PATH).pipe(zlib.createGunzip()); let leftover = ''; for await (const chunk of stream) { const text = leftover + chunk.toString('utf8'); const lines = text.split('\n'); leftover = lines.pop(); for (const line of lines) { if (line.startsWith('-- Dumping data for table `')) { const m = line.match(/^-- Dumping data for table `([^`]+)`/); if (m && targetTables.has(m[1])) { currentTable = m[1]; insideValues = false; console.log(`-> Leyendo tabla ${currentTable}...`); } else { currentTable = null; insideValues = false; } continue; } if (!currentTable) continue; if (line.startsWith('/*!40000 ALTER TABLE') && line.includes('ENABLE KEYS')) { currentTable = null; insideValues = false; continue; } if (line.startsWith('INSERT INTO `')) { insideValues = true; const idx = line.indexOf('VALUES'); if (idx !== -1) { const rest = line.slice(idx + 6); if (rest.trim()) parsers[currentTable](rest + '\n'); } continue; } if (insideValues) { parsers[currentTable](line + '\n'); } } } // Build categories list const categoriesList = []; const taxIdToCategorySlug = {}; for (const taxId of Object.keys(taxonomies)) { const tax = taxonomies[taxId]; const term = terms[tax.term_id]; if (term) { categoriesList.push({ term_id: term.id, term_taxonomy_id: taxId, nombre: term.name, slug: term.slug, }); taxIdToCategorySlug[taxId] = term.slug; } } // Enrich posts with featured image info and category slugs const postsList = Object.values(posts).map((p) => { let thumbId = postThumbnails[p.id] || null; let imageRelPath = thumbId ? attachedFiles[thumbId] || null : null; let imageGuid = thumbId && attachments[thumbId] ? attachments[thumbId].guid : null; let imageTitle = thumbId && attachments[thumbId] ? attachments[thumbId].title : p.title; let imageAlt = thumbId ? attachmentAlts[thumbId] || p.title : p.title; // Fallback: if no _thumbnail_id, check if an attachment has post_parent == p.id or extract first wp-content/uploads img from content if (!imageRelPath && p.content) { const m = p.content.match(/wp-content\/uploads\/([^"'\s>]+)/i); if (m) { imageRelPath = m[1]; } } const catTaxIds = postCategories[p.id] || []; const catSlugs = catTaxIds.map((tid) => taxIdToCategorySlug[tid]).filter(Boolean); return { ...p, thumbId, imageRelPath, imageGuid, imageTitle, imageAlt, categories: catSlugs, }; }); // Sort posts chronologically postsList.sort((a, b) => new Date(a.date) - new Date(b.date)); const withImageCount = postsList.filter((p) => p.imageRelPath).length; console.log(`\n======================================================`); console.log(`✅ Extracción completada:`); console.log(` - Categorías de WordPress: ${categoriesList.length}`); console.log(` - Publicaciones históricas (publish): ${postsList.length}`); console.log(` - Publicaciones con imagen destacada identificada: ${withImageCount}`); console.log(` - Total adjuntos (attachments) en WP: ${Object.keys(attachments).length}`); console.log(`======================================================`); fs.writeFileSync( OUT_JSON_PATH, JSON.stringify({ categories: categoriesList, posts: postsList }, null, 2), 'utf8' ); console.log(`Guardado en: ${OUT_JSON_PATH}`); } run().catch((e) => { console.error('Error:', e); process.exit(1); });