/** * Adapter Mir-Tex24 (mir-tex24.pl) — parser strony produktu nóżki meblowej. * Struktura strony (PrestaShop, stan 2026-09): * h1.product_header_name — nazwa ("Nóżka meblowa stożek 47x130") * .product-specific-codes div — "EAN-13: 5903211093661" * .product-features table.data-sheet tr — th.name / td.value (specyfikacja) * meta[property="og:image"] — pełnowymiarowe zdjęcie (jpg 1280) * img.product-cover__image[data-src] — fallback okładki (webp) * Wyjście: ParsedLegPage — bez sieci, bez zapisu do DB. */ import { load } from "cheerio"; export class LegParseError extends Error { constructor(public readonly code: string, message: string) { super(message); this.name = "LegParseError"; } } export interface ParsedLegPage { name: string; /** wysokość w cm (ze specyfikacji „Wysokość nóżki" albo wymiarów w tytule) */ heightCm: number | null; /** materiał ze specyfikacji („drewniane" → "drewno"), surowy zapis PL */ material: string | null; /** kształt ze specyfikacji (np. stożek, kostka) */ shape: string | null; /** kolorystyka ze specyfikacji (lista wariantów kolorystycznych) */ colors: string | null; ean: string | null; /** pełnowymiarowe zdjęcie produktu */ imageUrl: string; } const num = (s: string) => Number(s.replace(/\s/g, "").replace(",", ".")); /** "130 mm" → 13; "47x130" (tytuł, ostatni wymiar = wysokość) → 13 */ function heightFromSpec(v: string): number | null { const m = v.match(/(\d[\d\s]*(?:[.,]\d+)?)\s*mm/i); return m ? num(m[1]!) / 10 : null; } export function parseMirtexLegPage(html: string, pageUrl: string): ParsedLegPage { const host = new URL(pageUrl).hostname; if (!/(^|\.)mir-tex24\.pl$/i.test(host)) { throw new LegParseError("unsupported_host", `Parser Mir-Tex24 obsługuje wyłącznie mir-tex24.pl (dostano ${host}).`); } const $ = load(html); const name = $("h1.product_header_name").first().text().trim() || $("h1").first().text().trim(); if (!name) throw new LegParseError("no_title", "Strona Mir-Tex24 nie zawiera nazwy produktu (h1.product_header_name)."); const spec: Record = {}; $(".product-features table tr").each((_, tr) => { const k = $(tr).find("th.name").text().trim(); const v = $(tr).find("td.value").text().trim(); if (k && v) spec[k.toLowerCase()] = v; }); const eanRaw = $(".product-specific-codes").text().match(/EAN-?13:\s*(\d{13})/i)?.[1] ?? null; const imageUrl = $('meta[property="og:image"]').first().attr("content")?.trim() || $("img.product-cover__image").first().attr("data-src")?.trim() || $("img.product-cover__image").first().attr("src")?.trim() || ""; if (!imageUrl) throw new LegParseError("no_image", "Strona Mir-Tex24 nie zawiera zdjęcia produktu (og:image / product-cover)."); const heightCm = spec["wysokość nóżki"] ? heightFromSpec(spec["wysokość nóżki"]) : null; // fallback: ostatni wymiar z tytułu ("stożek 47x130" → 13 cm; "H-140" → 14 cm) const titleDim = name.match(/(\d+)\s*[x×]\s*(\d+)|H-?\s*(\d+)/i); const heightFromTitle = titleDim ? num(titleDim[2] ?? titleDim[3] ?? "") / 10 : null; return { name, heightCm: heightCm ?? (heightFromTitle && heightFromTitle > 0 ? heightFromTitle : null), material: spec["materiał nóżek"] ?? spec["materiał"] ?? null, shape: spec["kształt nóżek"] ?? null, colors: spec["kolor nóżek"] ?? null, ean: eanRaw, imageUrl, }; } /** PL → EN dla ai_prompt: kształt i materiał nóżki (mapowanie katalogowe, reszta surowa). */ const SHAPE_EN: Record = { stożek: "tapered cone", stozek: "tapered cone", kostka: "block", trapez: "trapezoid", marchewka: "turned", skośna: "angled", skosna: "angled", prosta: "straight", wałek: "cylindrical", walek: "cylindrical", }; const MATERIAL_EN: Record = { drewniane: "wooden", drewno: "wooden", metalowe: "metal", metal: "metal", stalowe: "steel", plastikowe: "plastic", tworzywo: "plastic", }; /** Buduje domyślny ai_prompt z danych strony — operator może go nadpisać. */ export function legPromptFromParsed(p: ParsedLegPage): string { const shape = SHAPE_EN[(p.shape ?? "").toLowerCase()] ?? p.shape ?? "furniture"; const material = MATERIAL_EN[(p.material ?? "").toLowerCase()] ?? p.material ?? "wooden"; const height = p.heightCm ? `, ${Math.round(p.heightCm)}cm height` : ""; return `${shape} ${material} furniture legs${height}`; }