97 lines
3.3 KiB
TypeScript
97 lines
3.3 KiB
TypeScript
|
|
import axios from 'axios';
|
|
import { geminiService } from './geminiService.js';
|
|
|
|
interface XRayResult {
|
|
success: boolean;
|
|
data?: {
|
|
metadata: {
|
|
title: string;
|
|
description: string;
|
|
image: string;
|
|
tags: string[];
|
|
};
|
|
analysis: {
|
|
visualDna: string[];
|
|
sentimentGap: string;
|
|
superiorPrompt: string;
|
|
gapAnalysis: string;
|
|
};
|
|
};
|
|
error?: string;
|
|
}
|
|
|
|
export const xrayService = {
|
|
/**
|
|
* Scrapes a product URL and performs deep AI analysis
|
|
*/
|
|
async analyzeProduct(url: string, apiKey?: string): Promise<XRayResult> {
|
|
try {
|
|
console.log(`[X-Ray] Analyzing: ${url}`);
|
|
|
|
// 1. WEB RESEARCH (Google Search Grounding)
|
|
// Replaces legacy Axios scraping which gets blocked (403/422) by Etsy
|
|
const researchResult = await geminiService.performWebResearch(url, apiKey);
|
|
|
|
if (!researchResult.title) {
|
|
throw new Error("AI Retrieval failed to extract product title. The URL may not be accessible or the product page structure is unsupported.");
|
|
}
|
|
|
|
if (!researchResult.image) {
|
|
throw new Error("AI Retrieval could not find a product image URL. Please ensure the URL points to a valid product page.");
|
|
}
|
|
|
|
console.log("[X-Ray] Metadata extracted via Google:", researchResult.title);
|
|
|
|
// 2. Perform AI Analysis (Visual + Text)
|
|
// We pass the image URL directly if public, OR we might need to download it first base64.
|
|
// For stability, let's download the image to base64 buffer
|
|
// Note: Since we have the direct image URL now (likely CDN), downloadImage should work better
|
|
// than scraping the main page.
|
|
const imageBuffer = await this.downloadImage(researchResult.image);
|
|
const imageBase64 = imageBuffer.toString('base64');
|
|
|
|
const metadata = {
|
|
title: researchResult.title,
|
|
description: researchResult.description,
|
|
image: researchResult.image,
|
|
tags: [] // Search might not return tags, optional
|
|
};
|
|
|
|
const analysis = await geminiService.analyzeCompetitorProduct({
|
|
title: researchResult.title,
|
|
description: researchResult.description,
|
|
imageBase64: imageBase64,
|
|
apiKey
|
|
});
|
|
|
|
|
|
return {
|
|
success: true,
|
|
data: {
|
|
metadata,
|
|
analysis
|
|
}
|
|
};
|
|
|
|
} catch (error: any) {
|
|
console.error("[X-Ray] Error:", error.message);
|
|
return {
|
|
success: false,
|
|
error: error.message || "Failed to analyze product"
|
|
};
|
|
}
|
|
},
|
|
|
|
async downloadImage(url: string): Promise<Buffer> {
|
|
const response = await axios.get(url, {
|
|
responseType: 'arraybuffer',
|
|
headers: {
|
|
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36',
|
|
'Referer': 'https://www.etsy.com/'
|
|
}
|
|
});
|
|
return Buffer.from(response.data, 'binary');
|
|
}
|
|
};
|