almeira-web/scripts/extract_wordpress_sql.js

284 lines
8.9 KiB
JavaScript

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);
});