284 lines
8.9 KiB
JavaScript
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);
|
|
});
|
|
|