import { extractHarPhotos } from "../../web-automation/scripts/har-photos.js"; import { extractZillowPhotos } from "../../web-automation/scripts/zillow-photos.js"; import { withTimeout } from "./async-timeout.js"; export type PhotoSource = "zillow" | "har"; export interface PhotoExtractionResult { source: PhotoSource; requestedUrl: string; finalUrl?: string; expectedPhotoCount?: number | null; complete?: boolean; photoCount: number; imageUrls: string[]; notes: string[]; } export interface PhotoReviewResolution { review: Record; discoveredListingUrls: Array<{ label: string; url: string }>; } interface PhotoReviewDeps { timeoutMs?: number; extractZillowPhotosFn?: typeof extractZillowPhotos; extractHarPhotosFn?: typeof extractHarPhotos; } const DEFAULT_PHOTO_EXTRACTION_TIMEOUT_MS = Number( process.env.PROPERTY_ASSESSOR_PHOTO_TIMEOUT_MS || 25_000 ); export async function extractPhotoData( source: PhotoSource, url: string, deps: PhotoReviewDeps = {} ): Promise { const timeoutMs = deps.timeoutMs ?? DEFAULT_PHOTO_EXTRACTION_TIMEOUT_MS; const extractZillowPhotosFn = deps.extractZillowPhotosFn || extractZillowPhotos; const extractHarPhotosFn = deps.extractHarPhotosFn || extractHarPhotos; if (source === "zillow") { const payload = await withTimeout( () => extractZillowPhotosFn(url), { operationName: "Zillow photo extraction", timeoutMs } ); return { source, requestedUrl: String(payload.requestedUrl || url), finalUrl: typeof payload.finalUrl === "string" ? payload.finalUrl : undefined, expectedPhotoCount: typeof payload.expectedPhotoCount === "number" ? payload.expectedPhotoCount : null, complete: Boolean(payload.complete), photoCount: Number(payload.photoCount || 0), imageUrls: Array.isArray(payload.imageUrls) ? payload.imageUrls.map(String) : [], notes: Array.isArray(payload.notes) ? payload.notes.map(String) : [] }; } const payload = await withTimeout( () => extractHarPhotosFn(url), { operationName: "HAR photo extraction", timeoutMs } ); return { source, requestedUrl: String(payload.requestedUrl || url), finalUrl: typeof payload.finalUrl === "string" ? payload.finalUrl : undefined, expectedPhotoCount: typeof payload.expectedPhotoCount === "number" ? payload.expectedPhotoCount : null, complete: Boolean(payload.complete), photoCount: Number(payload.photoCount || 0), imageUrls: Array.isArray(payload.imageUrls) ? payload.imageUrls.map(String) : [], notes: Array.isArray(payload.notes) ? payload.notes.map(String) : [] }; }