/**
* URL에서 Open Graph 메타데이터 직접 추출
* Nitter가 카드를 비워서 줄 때(주로 YouTube) fallback으로 사용.
*/
const OG_TIMEOUT = 8000;
/**
* HTML 엔티티 디코딩 (이미지 URL의 & 등 처리)
*/
function decodeEntities(str) {
if (!str) return str;
return str
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/?39;/g, "'")
.replace(/'/gi, "'")
.replace(///gi, '/')
.replace(/(\d+);/g, (_, d) => String.fromCodePoint(parseInt(d, 10)))
.replace(/([0-9a-f]+);/gi, (_, h) => String.fromCodePoint(parseInt(h, 16)));
}
/**
* HTML에서 og:/twitter: 메타 태그 값 추출 (속성 순서 무관)
*/
function pickMeta(html, prop) {
const patterns = [
new RegExp(`]+(?:property|name)="${prop}"[^>]+content="([^"]*)"`, 'i'),
new RegExp(`]+content="([^"]*)"[^>]+(?:property|name)="${prop}"`, 'i'),
];
for (const re of patterns) {
const m = html.match(re);
if (m) return decodeEntities(m[1]);
}
return null;
}
/**
* URL의 OG 메타데이터를 가져와 카드 형식으로 반환
* @param {string} url - 대상 URL
* @returns {Promise