This commit is contained in:
2026-01-06 13:55:04 +01:00
parent 297de69928
commit f991ea6b9b
393 changed files with 41362 additions and 4811 deletions

9
scripts/debug-test.js Normal file
View File

@@ -0,0 +1,9 @@
const { processShortcodes } = require('../lib/html-compat.ts');
const input = '[vc_row bg_image="”10440″" color_overlay="“#000000”"]content[/vc_row]';
console.log('Input:', input);
const result = processShortcodes(input);
console.log('Result:', result);
console.log('Contains bg image?', result.includes('background-image'));
console.log('Style attribute:', result.match(/style="([^"]*)"/)?.[1]);

View File

@@ -0,0 +1,153 @@
#!/usr/bin/env node
/**
* Script to download missing videos and PDFs
* Downloads videos referenced in processed data and PDFs linked in pages
*/
const fs = require('fs');
const path = require('path');
const https = require('https');
const http = require('http');
// Configuration
const MEDIA_DIR = path.join(__dirname, '..', 'public', 'media');
const PROCESSED_DIR = path.join(__dirname, '..', 'data', 'processed');
// Videos to download (from home pages)
const VIDEOS_TO_DOWNLOAD = [
{
url: 'https://klz-cables.com/wp-content/uploads/2025/02/header.mp4',
filename: 'header.mp4'
},
{
url: 'https://klz-cables.com/wp-content/uploads/2025/02/header.webm',
filename: 'header.webm'
}
];
// PDFs to download (from terms pages)
const PDFS_TO_DOWNLOAD = [
{
url: 'https://klz-cables.com/wp-content/uploads/2025/01/agbs.pdf',
filename: 'agbs.pdf'
}
];
// Create media directory if it doesn't exist
if (!fs.existsSync(MEDIA_DIR)) {
fs.mkdirSync(MEDIA_DIR, { recursive: true });
}
// Download file function
function downloadFile(url, filename) {
return new Promise((resolve, reject) => {
const filePath = path.join(MEDIA_DIR, filename);
// Check if file already exists
if (fs.existsSync(filePath)) {
console.log(`✅ Already exists: ${filename}`);
resolve(filePath);
return;
}
console.log(`📥 Downloading: ${filename} from ${url}`);
const protocol = url.startsWith('https') ? https : http;
const file = fs.createWriteStream(filePath);
protocol.get(url, (res) => {
if (res.statusCode === 200) {
res.pipe(file);
file.on('finish', () => {
console.log(`✅ Downloaded: ${filename}`);
resolve(filePath);
});
} else if (res.statusCode === 301 || res.statusCode === 302) {
// Handle redirects
if (res.headers.location) {
console.log(`🔄 Redirected to: ${res.headers.location}`);
downloadFile(res.headers.location, filename).then(resolve).catch(reject);
} else {
reject(new Error(`Redirect without location: ${res.statusCode}`));
}
} else {
reject(new Error(`Failed to download: HTTP ${res.statusCode}`));
}
}).on('error', (err) => {
fs.unlink(filePath, () => {});
reject(err);
});
});
}
// Main function
async function main() {
console.log('🔍 Downloading Missing Assets');
console.log('==============================');
console.log(`Output: ${MEDIA_DIR}`);
console.log('');
const assetMap = {};
const downloaded = [];
// Download videos
console.log('🎬 Videos:');
for (const video of VIDEOS_TO_DOWNLOAD) {
try {
await downloadFile(video.url, video.filename);
assetMap[video.url] = `/media/${video.filename}`;
downloaded.push(video.filename);
} catch (error) {
console.warn(`⚠️ Failed to download video ${video.filename}:`, error.message);
}
}
console.log('');
// Download PDFs
console.log('📄 PDFs:');
for (const pdf of PDFS_TO_DOWNLOAD) {
try {
await downloadFile(pdf.url, pdf.filename);
assetMap[pdf.url] = `/media/${pdf.filename}`;
downloaded.push(pdf.filename);
} catch (error) {
console.warn(`⚠️ Failed to download PDF ${pdf.filename}:`, error.message);
}
}
// Update asset-map.json with new entries
const assetMapPath = path.join(PROCESSED_DIR, 'asset-map.json');
if (fs.existsSync(assetMapPath)) {
const existingMap = JSON.parse(fs.readFileSync(assetMapPath, 'utf8'));
const updatedMap = { ...existingMap, ...assetMap };
fs.writeFileSync(assetMapPath, JSON.stringify(updatedMap, null, 2));
console.log(`\n✅ Updated asset-map.json with ${Object.keys(assetMap).length} new entries`);
}
console.log('\n🎉 Asset Download Complete!');
console.log('==============================');
console.log(`📥 Downloaded: ${downloaded.length} files`);
console.log(`📁 Directory: public/media/`);
console.log('');
console.log('Files downloaded:');
downloaded.forEach(file => {
console.log(` - ${file}`);
});
}
// Run if called directly
if (require.main === module) {
main().catch(error => {
console.error('\n❌ Script failed:', error.message);
process.exit(1);
});
}
module.exports = {
downloadFile,
main
};

216
scripts/fetch-missing-media.js Executable file
View File

@@ -0,0 +1,216 @@
#!/usr/bin/env node
/**
* Script to fetch specific missing media IDs from WordPress
* Uses the WordPress REST API to get media URLs and download them
*/
const fs = require('fs');
const path = require('path');
const https = require('https');
// Load environment variables
require('dotenv').config();
const BASE_URL = process.env.WOOCOMMERCE_URL;
const APP_PASSWORD = process.env.WORDPRESS_APP_PASSWORD;
// Validate environment
if (!BASE_URL || !APP_PASSWORD) {
console.error('❌ Missing required environment variables');
console.error('Please check .env file for:');
console.error(' - WOOCOMMERCE_URL');
console.error(' - WORDPRESS_APP_PASSWORD');
process.exit(1);
}
// Configuration
const MISSING_MEDIA_IDS = [10432, 10440, 10382, 10616, 10615, 45569, 10638, 5767];
const MEDIA_DIR = path.join(__dirname, '..', 'public', 'media');
const RAW_DATA_DIR = path.join(__dirname, '..', 'data', 'raw', '2025-12-30T15-21-49-331Z');
// Create media directory if it doesn't exist
if (!fs.existsSync(MEDIA_DIR)) {
fs.mkdirSync(MEDIA_DIR, { recursive: true });
}
// WordPress Auth Header
function buildWordPressAuth() {
return {
'Authorization': `Basic ${Buffer.from(`admin:${APP_PASSWORD}`).toString('base64')}`,
'Content-Type': 'application/json'
};
}
// Make HTTPS request
function makeRequest(url, headers = {}) {
return new Promise((resolve, reject) => {
const options = {
headers: {
'User-Agent': 'WordPress-Missing-Media-Fetcher/1.0',
...headers
}
};
https.get(url, options, (res) => {
let data = '';
res.on('data', (chunk) => {
data += chunk;
});
res.on('end', () => {
if (res.statusCode >= 200 && res.statusCode < 300) {
try {
resolve(JSON.parse(data));
} catch (e) {
resolve(data);
}
} else {
reject(new Error(`HTTP ${res.statusCode}: ${data}`));
}
});
}).on('error', reject);
});
}
// Fetch single media item
async function fetchMedia(mediaId) {
const url = `${BASE_URL}/wp-json/wp/v2/media/${mediaId}`;
try {
console.log(`📥 Fetching media ${mediaId}...`);
const media = await makeRequest(url, buildWordPressAuth());
return media;
} catch (error) {
console.error(`❌ Error fetching media ${mediaId}:`, error.message);
return null;
}
}
// Download media file
function downloadMedia(url, filename) {
return new Promise((resolve, reject) => {
const filePath = path.join(MEDIA_DIR, filename);
// Check if file already exists
if (fs.existsSync(filePath)) {
console.log(`✅ Media already exists: ${filename}`);
resolve(filePath);
return;
}
const file = fs.createWriteStream(filePath);
https.get(url, (res) => {
if (res.statusCode === 200) {
res.pipe(file);
file.on('finish', () => {
console.log(`✅ Downloaded: ${filename}`);
resolve(filePath);
});
} else {
reject(new Error(`Failed to download: ${res.statusCode}`));
}
}).on('error', (err) => {
fs.unlink(filePath, () => {});
reject(err);
});
});
}
// Main function
async function main() {
console.log('🔍 Fetching Missing Media IDs');
console.log('==============================');
console.log(`Target: ${BASE_URL}`);
console.log(`Output: ${MEDIA_DIR}`);
console.log(`Missing IDs: ${MISSING_MEDIA_IDS.join(', ')}`);
console.log('');
const mediaManifest = [];
const downloadPromises = [];
for (const mediaId of MISSING_MEDIA_IDS) {
const media = await fetchMedia(mediaId);
if (media && media.source_url) {
const originalFilename = media.source_url.split('/').pop();
const filename = `${mediaId}-${originalFilename}`;
// Add to manifest
mediaManifest.push({
id: mediaId,
url: media.source_url,
filename: filename,
alt: media.alt_text || '',
width: media.media_details?.width,
height: media.media_details?.height,
mime_type: media.mime_type
});
// Download file
downloadPromises.push(
downloadMedia(media.source_url, filename).catch(err => {
console.warn(`⚠️ Failed to download media ${mediaId}:`, err.message);
})
);
} else {
console.warn(`⚠️ Could not fetch media ${mediaId}`);
}
}
// Wait for all downloads
await Promise.all(downloadPromises);
// Update media.json
const mediaJsonPath = path.join(RAW_DATA_DIR, 'media.json');
if (fs.existsSync(mediaJsonPath)) {
const existingMedia = JSON.parse(fs.readFileSync(mediaJsonPath, 'utf8'));
const updatedMedia = [...existingMedia, ...mediaManifest];
fs.writeFileSync(
mediaJsonPath,
JSON.stringify(updatedMedia, null, 2)
);
console.log(`✅ Updated media.json with ${mediaManifest.length} new items`);
} else {
console.warn('⚠️ media.json not found, creating new file');
fs.writeFileSync(
mediaJsonPath,
JSON.stringify(mediaManifest, null, 2)
);
}
// Update assets.json if needed
const assetsJsonPath = path.join(RAW_DATA_DIR, 'assets.json');
if (fs.existsSync(assetsJsonPath)) {
const assets = JSON.parse(fs.readFileSync(assetsJsonPath, 'utf8'));
console.log('✅ Current assets.json:', assets);
}
console.log('\n🎉 Missing Media Fetch Complete!');
console.log('==============================');
console.log(`📥 Fetched: ${mediaManifest.length} items`);
console.log(`📁 Directory: public/media/`);
console.log(`📄 Updated: data/raw/2025-12-30T15-21-49-331Z/media.json`);
console.log('');
console.log('Media items fetched:');
mediaManifest.forEach(item => {
console.log(` - ${item.id}: ${item.filename}`);
});
}
// Run if called directly
if (require.main === module) {
main().catch(error => {
console.error('\n❌ Script failed:', error.message);
process.exit(1);
});
}
module.exports = {
fetchMedia,
downloadMedia,
main
};

144
scripts/fix-video-attrs.js Normal file
View File

@@ -0,0 +1,144 @@
#!/usr/bin/env node
/**
* Script to move video attributes from excerptHtml to contentHtml
* This fixes the issue where video background attributes are in excerptHtml
* but ContentRenderer never sees them because it processes contentHtml
*/
const fs = require('fs');
const path = require('path');
const PROCESSED_DIR = path.join(__dirname, '..', 'data', 'processed');
// Function to extract video attributes from excerptHtml
function extractVideoAttributes(excerptHtml) {
if (!excerptHtml) return null;
// Look for video attributes in vc_row elements
const videoMp4Match = excerptHtml.match(/video_mp4="([^"]*)"/i);
const videoWebmMatch = excerptHtml.match(/video_webm="([^"]*)"/i);
const videoBgMatch = excerptHtml.match(/video_bg="([^"]*)"/i);
// Also check for data attributes
const dataVideoMp4Match = excerptHtml.match(/data-video-mp4="([^"]*)"/i);
const dataVideoWebmMatch = excerptHtml.match(/data-video-webm="([^"]*)"/i);
const dataVideoBgMatch = excerptHtml.match(/data-video-bg="([^"]*)"/i);
const videoMp4 = videoMp4Match?.[1] || dataVideoMp4Match?.[1] || '';
const videoWebm = videoWebmMatch?.[1] || dataVideoWebmMatch?.[1] || '';
const videoBg = videoBgMatch?.[1] || dataVideoBgMatch?.[1] || '';
if (videoMp4 || videoWebm || videoBg) {
return { videoMp4, videoWebm, videoBg };
}
return null;
}
// Function to merge video attributes into contentHtml
function mergeVideoAttributes(contentHtml, videoAttrs) {
if (!contentHtml || !videoAttrs) return contentHtml;
let merged = contentHtml;
// Find the first vc-row element in contentHtml
const vcRowRegex = /<div class="vc-row[^"]*"[^>]*>/i;
const match = merged.match(vcRowRegex);
if (match) {
const existingDiv = match[0];
let newDiv = existingDiv;
// Add video attributes if they don't already exist
if (videoAttrs.videoMp4 && !existingDiv.includes('video_mp4=') && !existingDiv.includes('data-video-mp4=')) {
newDiv = newDiv.replace('>', ` video_mp4="${videoAttrs.videoMp4}">`);
}
if (videoAttrs.videoWebm && !existingDiv.includes('video_webm=') && !existingDiv.includes('data-video-webm=')) {
newDiv = newDiv.replace('>', ` video_webm="${videoAttrs.videoWebm}">`);
}
if (videoAttrs.videoBg && !existingDiv.includes('video_bg=') && !existingDiv.includes('data-video-bg=')) {
newDiv = newDiv.replace('>', ` video_bg="${videoAttrs.videoBg}">`);
}
// Also add data attributes for better compatibility
if (videoAttrs.videoMp4 && !existingDiv.includes('data-video-mp4=')) {
newDiv = newDiv.replace('>', ` data-video-mp4="${videoAttrs.videoMp4}">`);
}
if (videoAttrs.videoWebm && !existingDiv.includes('data-video-webm=')) {
newDiv = newDiv.replace('>', ` data-video-webm="${videoAttrs.videoWebm}">`);
}
if (videoAttrs.videoBg && !existingDiv.includes('data-video-bg=')) {
newDiv = newDiv.replace('>', ` data-video-bg="${videoAttrs.videoBg}">`);
}
merged = merged.replace(existingDiv, newDiv);
}
return merged;
}
// Main function
function main() {
console.log('🎬 Fixing video attributes in processed data...\n');
// Load pages.json
const pagesPath = path.join(PROCESSED_DIR, 'pages.json');
if (!fs.existsSync(pagesPath)) {
console.error('❌ pages.json not found');
process.exit(1);
}
const pages = JSON.parse(fs.readFileSync(pagesPath, 'utf8'));
let fixedCount = 0;
// Process each page
const updatedPages = pages.map(page => {
const videoAttrs = extractVideoAttributes(page.excerptHtml);
if (videoAttrs) {
console.log(`📄 Page: ${page.slug} (${page.locale})`);
console.log(` Found video attrs in excerpt: mp4="${videoAttrs.videoMp4}" webm="${videoAttrs.videoWebm}"`);
// Merge into contentHtml
const originalContent = page.contentHtml;
page.contentHtml = mergeVideoAttributes(page.contentHtml, videoAttrs);
if (page.contentHtml !== originalContent) {
console.log(` ✅ Merged into contentHtml`);
fixedCount++;
} else {
console.log(` ⚠️ Already present or no vc-row found`);
}
console.log('');
}
return page;
});
// Save updated pages
fs.writeFileSync(pagesPath, JSON.stringify(updatedPages, null, 2));
// Also update the main wordpress-data.json if it exists
const wordpressDataPath = path.join(PROCESSED_DIR, 'wordpress-data.json');
if (fs.existsSync(wordpressDataPath)) {
const wordpressData = JSON.parse(fs.readFileSync(wordpressDataPath, 'utf8'));
if (wordpressData.content && wordpressData.content.pages) {
wordpressData.content.pages = updatedPages;
fs.writeFileSync(wordpressDataPath, JSON.stringify(wordpressData, null, 2));
}
}
console.log(`✅ Fixed ${fixedCount} pages with video attributes`);
console.log('📁 Files updated:');
console.log(` ${pagesPath}`);
console.log(` ${wordpressDataPath}`);
}
if (require.main === module) {
main();
}

File diff suppressed because it is too large Load Diff

68
scripts/update-asset-map.js Executable file
View File

@@ -0,0 +1,68 @@
#!/usr/bin/env node
/**
* Script to update asset-map.json with new media entries
*/
const fs = require('fs');
const path = require('path');
// Configuration
const RAW_DATA_DIR = path.join(__dirname, '..', 'data', 'raw', '2025-12-30T15-21-49-331Z');
const PROCESSED_DATA_DIR = path.join(__dirname, '..', 'data', 'processed');
// New media IDs to add
const NEW_MEDIA_IDS = [10432, 10440, 10382, 10616, 10615, 45569, 10638];
function updateAssetMap() {
console.log('🔄 Updating asset-map.json with new media entries');
// Load current media.json
const mediaJsonPath = path.join(RAW_DATA_DIR, 'media.json');
const mediaData = JSON.parse(fs.readFileSync(mediaJsonPath, 'utf8'));
// Load current asset-map.json
const assetMapPath = path.join(PROCESSED_DATA_DIR, 'asset-map.json');
let assetMap = {};
if (fs.existsSync(assetMapPath)) {
assetMap = JSON.parse(fs.readFileSync(assetMapPath, 'utf8'));
}
// Add new entries
let addedCount = 0;
NEW_MEDIA_IDS.forEach(id => {
const mediaEntry = mediaData.find(m => m.id === id);
if (mediaEntry) {
const localPath = `/media/${mediaEntry.filename}`;
assetMap[mediaEntry.url] = localPath;
console.log(`✅ Added: ${id}${localPath}`);
addedCount++;
} else {
console.warn(`⚠️ Media ID ${id} not found in media.json`);
}
});
// Save updated asset-map.json
fs.writeFileSync(
assetMapPath,
JSON.stringify(assetMap, null, 2)
);
console.log(`\n🎉 Asset map updated! Added ${addedCount} new entries`);
console.log(`Total entries in asset-map.json: ${Object.keys(assetMap).length}`);
return assetMap;
}
// Run if called directly
if (require.main === module) {
try {
updateAssetMap();
} catch (error) {
console.error('❌ Failed to update asset map:', error.message);
process.exit(1);
}
}
module.exports = { updateAssetMap };

View File

@@ -0,0 +1,145 @@
#!/usr/bin/env node
const fs = require('fs');
const path = require('path');
const filePath = path.join(__dirname, 'process-data-with-bg-images.js');
let content = fs.readFileSync(filePath, 'utf8');
// 1. Update processPages to async
content = content.replace(
'function processPages(pagesEN, pagesDE, translationMapping, mediaMapping, assetMap) {',
'async function processPages(pagesEN, pagesDE, translationMapping, mediaMapping, assetMap) {'
);
// 2. Update processPosts to async
content = content.replace(
'function processPosts(postsEN, postsDE, translationMapping, mediaMapping, assetMap) {',
'async function processPosts(postsEN, postsDE, translationMapping, mediaMapping, assetMap) {'
);
// 3. Update main to async
content = content.replace(
'function main() {',
'async function main() {'
);
// 4. Update main() call
content = content.replace(
'if (require.main === module) {\n main();\n}',
'if (require.main === module) {\n main().catch(console.error);\n}'
);
// 5. Update processPages English loop
content = content.replace(
'pagesEN.forEach(page => {',
'for (const page of pagesEN) {'
);
// 6. Update processPages German loop
content = content.replace(
'pagesDE.forEach(page => {',
'for (const page of pagesDE) {'
);
// 7. Add video processing in processPages English
content = content.replace(
'contentHtml = replaceUrlsWithLocalPaths(contentHtml, assetMap);\n \n let excerptHtml = decodeContent(page.excerptHtml);',
'contentHtml = replaceUrlsWithLocalPaths(contentHtml, assetMap);\n \n // Process video attributes and download videos\n const videoResult = await processVideoAttributes(contentHtml);\n contentHtml = videoResult.html;\n \n let excerptHtml = decodeContent(page.excerptHtml);'
);
// 8. Add video processing in processPages German
const germanPattern = /contentHtml = replaceUrlsWithLocalPaths\(contentHtml, assetMap\);\n \n let excerptHtml = decodeContent\(page\.excerptHtml\);\n excerptHtml = replaceBgImageIds\(excerptHtml, mediaMapping\);\n excerptHtml = replaceUrlsWithLocalPaths\(excerptHtml, assetMap\);\n \n processed\.push\(\{/;
content = content.replace(
germanPattern,
`contentHtml = replaceUrlsWithLocalPaths(contentHtml, assetMap);
// Process video attributes and download videos
const videoResult = await processVideoAttributes(contentHtml);
contentHtml = videoResult.html;
let excerptHtml = decodeContent(page.excerptHtml);
excerptHtml = replaceBgImageIds(excerptHtml, mediaMapping);
excerptHtml = replaceUrlsWithLocalPaths(excerptHtml, assetMap);
processed.push({`
);
// 9. Update processPosts English loop
content = content.replace(
'postsEN.forEach(post => {',
'for (const post of postsEN) {'
);
// 10. Update processPosts German loop
content = content.replace(
'postsDE.forEach(post => {',
'for (const post of postsDE) {'
);
// 11. Add video processing in processPosts English
const postsEnglishPattern = /contentHtml = replaceUrlsWithLocalPaths\(contentHtml, assetMap\);\n \n let excerptHtml = decodeContent\(post\.excerptHtml\);\n excerptHtml = replaceBgImageIds\(excerptHtml, mediaMapping\);\n excerptHtml = replaceUrlsWithLocalPaths\(excerptHtml, assetMap\);\n \n processed\.push\(\{/;
content = content.replace(
postsEnglishPattern,
`contentHtml = replaceUrlsWithLocalPaths(contentHtml, assetMap);
// Process video attributes and download videos
const videoResult = await processVideoAttributes(contentHtml);
contentHtml = videoResult.html;
let excerptHtml = decodeContent(post.excerptHtml);
excerptHtml = replaceBgImageIds(excerptHtml, mediaMapping);
excerptHtml = replaceUrlsWithLocalPaths(excerptHtml, assetMap);
processed.push({`
);
// 12. Add video processing in processPosts German
const postsGermanPattern = /contentHtml = replaceUrlsWithLocalPaths\(contentHtml, assetMap\);\n \n let excerptHtml = decodeContent\(post\.excerptHtml\);\n excerptHtml = replaceBgImageIds\(excerptHtml, mediaMapping\);\n excerptHtml = replaceUrlsWithLocalPaths\(excerptHtml, assetMap\);\n \n processed\.push\(\{[\s\S]*?translation: enMatch \? \{ locale: 'en', id: enMatch\.en \} : null\n \}\);\n \}\n \n return processed;\n\}/;
content = content.replace(
postsGermanPattern,
`contentHtml = replaceUrlsWithLocalPaths(contentHtml, assetMap);
// Process video attributes and download videos
const videoResult = await processVideoAttributes(contentHtml);
contentHtml = videoResult.html;
let excerptHtml = decodeContent(post.excerptHtml);
excerptHtml = replaceBgImageIds(excerptHtml, mediaMapping);
excerptHtml = replaceUrlsWithLocalPaths(excerptHtml, assetMap);
processed.push({
id: post.id,
translationKey: translationKey,
locale: 'de',
slug: post.slug,
path: \`/de/blog/\${post.slug}\`,
title: post.titleHtml.replace(/<[^>]*>/g, ''),
titleHtml: post.titleHtml,
contentHtml: sanitizeHTML(contentHtml),
excerptHtml: processExcerptShortcodes(excerptHtml) || generateExcerpt(contentHtml),
featuredImage: post.featuredImage,
datePublished: post.datePublished,
updatedAt: post.updatedAt,
translation: enMatch ? { locale: 'en', id: enMatch.en } : null
});
}
return processed;
}`
);
// 13. Update main() to await processPages and processPosts
content = content.replace(
'const pages = processPages(pagesEN, pagesDE, translationMapping, mediaMapping, assetMap);\n const posts = processPosts(postsEN, postsDE, translationMapping, mediaMapping, assetMap);',
'const pages = await processPages(pagesEN, pagesDE, translationMapping, mediaMapping, assetMap);\n const posts = await processPosts(postsEN, postsDE, translationMapping, mediaMapping, assetMap);'
);
// 14. Update module.exports
content = content.replace(
'module.exports = {\n processPages,\n processPosts,\n processProducts,\n processProductCategories,\n processMedia,\n generateAssetMap,\n replaceBgImageIds,\n replaceUrlsWithLocalPaths\n};',
'module.exports = {\n processPages,\n processPosts,\n processProducts,\n processProductCategories,\n processMedia,\n generateAssetMap,\n replaceBgImageIds,\n replaceUrlsWithLocalPaths,\n processVideoAttributes\n};'
);
fs.writeFileSync(filePath, content);
console.log('✅ Updated process-data-with-bg-images.js to be async');

View File

@@ -581,8 +581,10 @@ async function exportLogoAndFavicon() {
const assets = {
logo: null,
logoSvg: null,
favicon: null,
appleTouchIcon: null
appleTouchIcon: null,
siteIconId: null
};
try {
@@ -594,16 +596,24 @@ async function exportLogoAndFavicon() {
console.log(`📥 Found custom_logo ID: ${settings.custom_logo}`);
const logoMedia = await fetchMedia(settings.custom_logo);
if (logoMedia && logoMedia.source_url) {
const logoFilename = 'logo.webp';
const ext = path.extname(logoMedia.source_url);
const logoFilename = `logo${ext}`;
await downloadMedia(logoMedia.source_url, logoFilename);
assets.logo = `/media/${logoFilename}`;
console.log(`✅ Logo downloaded: ${logoFilename}`);
// Check if it's SVG
if (logoMedia.mime_type === 'image/svg+xml' || ext === '.svg') {
assets.logoSvg = `/media/${logoFilename}`;
console.log(`✅ SVG logo detected: ${logoFilename}`);
}
}
}
// Try to get site_icon
if (settings.site_icon) {
console.log(`📥 Found site_icon ID: ${settings.site_icon}`);
assets.siteIconId = settings.site_icon;
const iconMedia = await fetchMedia(settings.site_icon);
if (iconMedia && iconMedia.source_url) {
// Download as favicon.ico
@@ -620,26 +630,108 @@ async function exportLogoAndFavicon() {
}
}
// If no logo found in settings, try to find it in media
// WP CLI Equivalent: wp media list --search=logo --format=json
console.log('🔍 WP CLI Equivalent: Searching for logo media...');
if (!assets.logo) {
console.log('⚠️ No logo found in settings, searching media...');
// Try to find logo by filename pattern
const allMedia = await fetchWithPagination('media', { per_page: 100 });
const logoCandidates = allMedia.filter(m =>
m.title?.rendered?.toLowerCase().includes('logo') ||
m.slug?.toLowerCase().includes('logo') ||
m.source_url?.toLowerCase().includes('logo')
);
const logoCandidates = allMedia.filter(m => {
const title = m.title?.rendered?.toLowerCase() || '';
const slug = m.slug?.toLowerCase() || '';
const url = m.source_url?.toLowerCase() || '';
return title.includes('logo') || slug.includes('logo') || url.includes('logo');
});
if (logoCandidates.length > 0) {
const logoMedia = logoCandidates[0];
const logoFilename = 'logo.webp';
const ext = path.extname(logoMedia.source_url);
const logoFilename = `logo${ext}`;
await downloadMedia(logoMedia.source_url, logoFilename);
assets.logo = `/media/${logoFilename}`;
console.log(`✅ Logo found and downloaded: ${logoFilename}`);
if (logoMedia.mime_type === 'image/svg+xml' || ext === '.svg') {
assets.logoSvg = `/media/${logoFilename}`;
console.log(`✅ SVG logo found and downloaded: ${logoFilename}`);
} else {
console.log(`✅ Logo found and downloaded: ${logoFilename}`);
}
}
}
// WP CLI Equivalent: wp media list --mime=image/svg+xml --format=json
console.log('🔍 WP CLI Equivalent: Searching for SVG images...');
const allMedia = await fetchWithPagination('media', { per_page: 200 });
const svgImages = allMedia.filter(m => m.mime_type === 'image/svg+xml');
if (svgImages.length > 0) {
console.log(`📥 Found ${svgImages.length} SVG images`);
for (const svg of svgImages) {
const filename = `svg-${svg.id}-${path.basename(svg.source_url)}`;
await downloadMedia(svg.source_url, filename);
console.log(`✅ SVG downloaded: ${filename}`);
}
}
// WP CLI Equivalent: wp postmeta list --post_type=any --meta_key~=_vc --format=json
console.log('🔍 WP CLI Equivalent: Searching for Salient/VC images...');
const salientImages = new Set();
// Search pages and posts for Visual Composer meta
const searchEndpoints = ['pages', 'posts'];
for (const endpoint of searchEndpoints) {
const items = await fetchWithPagination(endpoint, { per_page: 100 });
items.forEach(item => {
// Look for VC-related meta
if (item.meta) {
Object.keys(item.meta).forEach(key => {
if (key.includes('_vc') || key.includes('vc_') || key.includes('salient')) {
const metaValue = item.meta[key];
if (typeof metaValue === 'string') {
// Extract URLs from meta value
const urlMatches = metaValue.match(/https?:\/\/[^\s"']+/g);
if (urlMatches) {
urlMatches.forEach(url => salientImages.add(url));
}
}
}
});
}
// Also check content for images
const content = item.content?.rendered || '';
const contentUrls = content.match(/https?:\/\/[^\s"']+\.(jpg|jpeg|png|webp|svg)/gi);
if (contentUrls) {
contentUrls.forEach(url => salientImages.add(url));
}
});
}
// Download Salient/VC images
if (salientImages.size > 0) {
console.log(`📥 Found ${salientImages.size} Salient/VC images`);
const salientManifest = [];
for (const url of salientImages) {
try {
const filename = `salient-${Date.now()}-${path.basename(url)}`;
await downloadMedia(url, filename);
salientManifest.push({
originalUrl: url,
localPath: `/media/${filename}`,
filename: filename
});
console.log(`✅ Salient image downloaded: ${filename}`);
} catch (err) {
console.warn(`⚠️ Failed to download Salient image ${url}:`, err.message);
}
}
// Save Salient images manifest
fs.writeFileSync(
path.join(OUTPUT_DIR, 'salient-images.json'),
JSON.stringify(salientManifest, null, 2)
);
}
// If no favicon found, try to download from common locations
if (!assets.favicon) {
console.log('⚠️ No favicon found in settings, trying common locations...');
@@ -744,6 +836,57 @@ async function generateTranslationMapping() {
return mapping;
}
async function exportWPCliPostmeta() {
console.log('\n📊 EXPORTING WP CLI POSTMETA (VC/Salient)');
const vcMeta = [];
try {
// Get all pages and posts
const pages = await fetchWithPagination('pages', { status: 'publish', per_page: 100 });
const posts = await fetchWithPagination('posts', { status: 'publish', per_page: 100 });
const allItems = [...pages, ...posts];
console.log(`🔍 Scanning ${allItems.length} items for VC/Salient meta...`);
allItems.forEach(item => {
if (item.meta) {
const vcKeys = Object.keys(item.meta).filter(key =>
key.includes('_vc') || key.includes('vc_') || key.includes('salient') || key.includes('wpb_')
);
if (vcKeys.length > 0) {
vcKeys.forEach(key => {
const value = item.meta[key];
vcMeta.push({
post_id: item.id,
post_type: item.type || 'page',
post_slug: item.slug,
meta_key: key,
meta_value: typeof value === 'string' ? value.substring(0, 200) : JSON.stringify(value),
full_value: value
});
});
}
}
});
// Save VC postmeta
fs.writeFileSync(
path.join(OUTPUT_DIR, 'vc-postmeta.json'),
JSON.stringify(vcMeta, null, 2)
);
console.log(`✅ VC/Salient postmeta: ${vcMeta.length} entries found`);
} catch (error) {
console.error('❌ Error exporting postmeta:', error.message);
}
return vcMeta;
}
async function generateRedirects() {
console.log('\n📊 GENERATING REDIRECT RULES');
@@ -785,7 +928,7 @@ async function generateRedirects() {
// Main Execution
async function main() {
console.log('🚀 WordPress → Next.js Data Export');
console.log('🚀 WordPress → Next.js Data Export (WP CLI Enhanced)');
console.log('=====================================');
console.log(`Target: ${BASE_URL}`);
console.log(`Output: ${OUTPUT_DIR}`);
@@ -799,10 +942,13 @@ async function main() {
await exportProducts();
await exportProductCategories();
await exportMenus();
// Step 2: WP CLI Enhanced exports
await exportWPCliPostmeta();
await exportMedia();
await exportLogoAndFavicon();
// Step 2: Generate mappings and redirects
// Step 3: Generate mappings and redirects
await generateTranslationMapping();
await generateRedirects();
@@ -812,6 +958,12 @@ async function main() {
console.log(`🖼️ Media directory: public/media/`);
console.log(`🎨 Logo/Favicon: public/`);
console.log('');
console.log('WP CLI Features:');
console.log('✓ SVG logo detection and download');
console.log('✓ All SVG images exported');
console.log('✓ Salient/VC postmeta extracted');
console.log('✓ All media downloaded locally');
console.log('');
console.log('Next steps:');
console.log('1. Review exported data for completeness');
console.log('2. Check for any missing translations');
@@ -838,6 +990,7 @@ module.exports = {
exportMedia,
exportSiteInfo,
exportLogoAndFavicon,
exportWPCliPostmeta,
generateTranslationMapping,
generateRedirects
};