generated from polymech/site-template
149 lines
4.3 KiB
TypeScript
149 lines
4.3 KiB
TypeScript
import { IHowto } from './howto-model.js';
|
|
// https://schema.org/HowTo
|
|
export const get = (howto: IHowto, baseUrl: string = 'creava.org', lang: string = 'en') => {
|
|
const jsonLD: any = {
|
|
"@context": "https://schema.org",
|
|
"@type": "HowTo",
|
|
"name": howto.title,
|
|
"description": howto.description,
|
|
"url": `https://${baseUrl}/${lang}/howtos/${howto.slug}`,
|
|
"inLanguage": lang
|
|
};
|
|
|
|
if (howto.cover_image?.downloadUrl) {
|
|
jsonLD.image = howto.cover_image.downloadUrl;
|
|
}
|
|
|
|
// Add user location if available
|
|
if (howto.user?.geo) {
|
|
jsonLD.locationCreated = {
|
|
"@type": "Place",
|
|
"address": {
|
|
"@type": "PostalAddress",
|
|
"addressCountry": howto.user.geo.countryName,
|
|
"addressRegion": howto.user.geo.principalSubdivision,
|
|
"addressLocality": howto.user.geo.city,
|
|
"postalCode": howto.user.geo.postcode
|
|
},
|
|
"geo": {
|
|
"@type": "GeoCoordinates",
|
|
"latitude": howto.user.geo.latitude,
|
|
"longitude": howto.user.geo.longitude
|
|
}
|
|
};
|
|
}
|
|
|
|
// Extract video URL from description or steps
|
|
const videoUrl = extractVideoUrl(howto);
|
|
if (videoUrl) {
|
|
jsonLD.video = {
|
|
"@type": "VideoObject",
|
|
"url": videoUrl
|
|
};
|
|
}
|
|
|
|
if (howto.time) {
|
|
let duration = "";
|
|
const timeMatch = howto.time.match(/(\d+)\s*(hour|minute|second|day|week|month|year)/i);
|
|
if (timeMatch) {
|
|
const value = parseInt(timeMatch[1], 10);
|
|
const unit = timeMatch[2].toLowerCase();
|
|
|
|
switch(unit) {
|
|
case 'hour': duration = `PT${value}H`; break;
|
|
case 'minute': duration = `PT${value}M`; break;
|
|
case 'second': duration = `PT${value}S`; break;
|
|
case 'day': duration = `P${value}D`; break;
|
|
case 'week': duration = `P${value}W`; break;
|
|
case 'month': duration = `P${value}M`; break;
|
|
case 'year': duration = `P${value}Y`; break;
|
|
}
|
|
} else if (howto.time.includes('<')) {
|
|
const lessTimeMatch = howto.time.match(/< (\d+)\s*(hour|minute|second|day|week|month|year)/i);
|
|
if (lessTimeMatch) {
|
|
const value = parseInt(lessTimeMatch[1], 10);
|
|
const unit = lessTimeMatch[2].toLowerCase();
|
|
|
|
switch(unit) {
|
|
case 'hour': duration = `PT${value - 1}H`; break;
|
|
case 'minute': duration = `PT${value - 1}M`; break;
|
|
case 'second': duration = `PT${value - 1}S`; break;
|
|
case 'day': duration = `P${value - 1}D`; break;
|
|
case 'week': duration = `P${value - 1}W`; break;
|
|
case 'month': duration = `P${value - 1}M`; break;
|
|
case 'year': duration = `P${value - 1}Y`; break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (duration) {
|
|
jsonLD.totalTime = duration;
|
|
}
|
|
}
|
|
|
|
if (howto.difficulty_level) {
|
|
jsonLD.difficultyLevel = howto.difficulty_level;
|
|
}
|
|
|
|
if (howto.category?.label) {
|
|
jsonLD.category = howto.category.label;
|
|
}
|
|
|
|
if (howto.tags && howto.tags.length > 0) {
|
|
const keywords = howto.tags.map(tag =>
|
|
typeof tag === 'string' ? tag : tag?.label || ''
|
|
).filter(Boolean).join(', ');
|
|
|
|
if (keywords) {
|
|
jsonLD.keywords = keywords;
|
|
}
|
|
}
|
|
|
|
if (howto.steps && howto.steps.length > 0) {
|
|
jsonLD.step = howto.steps.map((step, index) => {
|
|
const stepObj: any = {
|
|
"@type": "HowToStep",
|
|
"name": step.title,
|
|
"text": step.text,
|
|
"position": index + 1
|
|
};
|
|
|
|
if (step.images && step.images.length > 0) {
|
|
if (step.images.length === 1) {
|
|
stepObj.image = step.images[0].downloadUrl;
|
|
} else {
|
|
stepObj.image = step.images.map(image => image.downloadUrl);
|
|
}
|
|
}
|
|
|
|
return stepObj;
|
|
});
|
|
}
|
|
|
|
return jsonLD;
|
|
};
|
|
|
|
// Helper function to extract video URL from description or steps
|
|
function extractVideoUrl(howto: IHowto): string | null {
|
|
// First try to find video URL in description
|
|
if (howto.description) {
|
|
const videoMatch = howto.description.match(/(https?:\/\/(?:www\.)?(?:youtube\.com|youtu\.be|vimeo\.com)\/[^\s]+)/i);
|
|
if (videoMatch) {
|
|
return videoMatch[1];
|
|
}
|
|
}
|
|
|
|
// Then try to find video URL in steps
|
|
if (howto.steps) {
|
|
for (const step of howto.steps) {
|
|
if (step.text) {
|
|
const videoMatch = step.text.match(/(https?:\/\/(?:www\.)?(?:youtube\.com|youtu\.be|vimeo\.com)\/[^\s]+)/i);
|
|
if (videoMatch) {
|
|
return videoMatch[1];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return null;
|
|
} |