Fix image gallery: scrape real product image URLs from JV website
This commit is contained in:
@@ -52,6 +52,28 @@ async function serveStatic(pathname: string): Promise<Response | null> {
|
||||
return null
|
||||
}
|
||||
|
||||
// In-memory cache for scraped image URLs per SKU
|
||||
const imageCache: Record<string, string[]> = {}
|
||||
|
||||
async function scrapeProductImages(sku: string): Promise<string[]> {
|
||||
if (imageCache[sku]) return imageCache[sku]
|
||||
const images = await readJson(join(PIPELINE_ROOT, 'product-images.json')) as Record<string, any>
|
||||
const productPath = images?.[sku]?.url
|
||||
if (!productPath) return []
|
||||
try {
|
||||
const res = await fetch(`https://www.justvitamins.co.uk${productPath}`, {
|
||||
headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36' }
|
||||
})
|
||||
const html = await res.text()
|
||||
// Extract filenames like {uuid}-{n}.jpg from anywhere in the page
|
||||
const matches = [...html.matchAll(/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}-\d+)\.jpg/g)]
|
||||
const filenames = [...new Set(matches.map(m => m[1]))]
|
||||
const urls = filenames.map(f => `https://images.justvitamins.co.uk/product-images/Normal/${f}.jpg`)
|
||||
imageCache[sku] = urls
|
||||
return urls
|
||||
} catch { return [] }
|
||||
}
|
||||
|
||||
const server = Bun.serve({
|
||||
port: PORT,
|
||||
async fetch(req) {
|
||||
@@ -71,6 +93,13 @@ const server = Bun.serve({
|
||||
return new Response(JSON.stringify(resolved), { headers: cors })
|
||||
}
|
||||
|
||||
// GET /api/product-images/:sku
|
||||
const imgMatch = path.match(/^\/api\/product-images\/([^/]+)$/)
|
||||
if (imgMatch) {
|
||||
const urls = await scrapeProductImages(imgMatch[1])
|
||||
return new Response(JSON.stringify({ urls }), { headers: cors })
|
||||
}
|
||||
|
||||
// GET /api/intelligence/:sku/:view
|
||||
const intelMatch = path.match(/^\/api\/intelligence\/([^/]+)\/([^/]+)$/)
|
||||
if (intelMatch) {
|
||||
|
||||
@@ -86,6 +86,7 @@ import { useRoute } from 'vue-router'
|
||||
const route = useRoute()
|
||||
const data = ref(null)
|
||||
const loading = ref(false)
|
||||
const galleryUrls = ref([])
|
||||
|
||||
const axisRows = computed(() => {
|
||||
const axes = data.value?.axes || {}
|
||||
@@ -97,16 +98,6 @@ const axisRows = computed(() => {
|
||||
]
|
||||
})
|
||||
|
||||
// Extract base UUID from imageUrl and generate all variant URLs
|
||||
const galleryUrls = computed(() => {
|
||||
const url = data.value?.imageUrl
|
||||
if (!url) return []
|
||||
// URL pattern: .../Normal/{uuid}-1.jpg → strip the -1.jpg suffix
|
||||
const base = url.replace(/-\d+\.jpg$/, '')
|
||||
// Generate 7 URLs (confirmed via CDN probing)
|
||||
return Array.from({ length: 7 }, (_, i) => `${base}-${i + 1}.jpg`)
|
||||
})
|
||||
|
||||
function scoreClass(v) {
|
||||
if (!v) return 'fair'
|
||||
if (v >= 4) return 'good'
|
||||
@@ -122,8 +113,13 @@ async function load(sku) {
|
||||
if (!sku) return
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await fetch(`/api/intelligence/${sku}/image-audit`)
|
||||
data.value = res.ok ? await res.json() : null
|
||||
const [auditRes, imgRes] = await Promise.all([
|
||||
fetch(`/api/intelligence/${sku}/image-audit`),
|
||||
fetch(`/api/product-images/${sku}`)
|
||||
])
|
||||
data.value = auditRes.ok ? await auditRes.json() : null
|
||||
const imgData = imgRes.ok ? await imgRes.json() : null
|
||||
galleryUrls.value = imgData?.urls || []
|
||||
} finally { loading.value = false }
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user