import fastMetaTags from 'fast-meta-tags'
import { getFile, getDomain, htmlToText } from './utils.js'
import { fetchOembed } from './oembed.js'
export default async function getLinkPreview(url, opt) {
if (url.startsWith('http://')) url = 'https' + url.slice(4)
const [oem, data] = await Promise.all([fetchOembed(url, opt), fastMetaTags(url, opt)])
if (!oem && !data) {
return null
}
const image = oem?.image || (data ? getImage(data) : null)
const meta = data ? parse({ ...data, image }) : {}
return {
url,
favicon: data?.favicon || null,
title: oem?.title || meta.title || null,
name: oem?.name || meta.name || getDomain(url),
description: oem?.description || meta.description || null,
file: await getFile(image, opt)
}
}
function parse(data) {
return {
url: data.url,
favicon: data.favicon,
title: getTitle(data),
name: getSiteName(data),
description: getDescription(data)
}
}
function getSiteName(data) {
return (
getMetaContent(data.meta, {
name: 'og:site_name',
property: 'og:site_name'
}) || getDomain(data.url)
)
}
function getTitle(data) {
return getMetaContent(data.meta, { name: 'og:title', property: 'og:title' }) || data.title
}
function getDescription(data) {
const description = getMetaContent(data.meta, { name: 'description', property: 'og:description' })
if (description) {
const text = htmlToText(description)
return text && text.length > 500 ? text.slice(0, 500) : text
}
if (!data.image && data.html) {
const paragraphs = data.html.match(/<p(\s[^>]*)?>.*?<\/p>/gis) || []
for (const p of paragraphs) {
const text = htmlToText(p)
if (text && text.length > 100) {
return text.length > 500 ? text.slice(0, 500) : text
}
}
}
return null
}
function getImage(data) {
return getMetaContent(data.meta, { name: 'og:image', property: 'og:image' })
}
function getMetaContent(meta, filter) {
const entries = Object.entries(filter)
return (
meta.find((m) => {
for (const [k, v] of entries) {
if (m[k] === v) return true
}
})?.content || null
)
}