fix: make property-assessor safer for whatsapp runs
This commit is contained in:
@@ -10,6 +10,7 @@ import {
|
||||
gotoListing,
|
||||
normalizeImageCandidates,
|
||||
parseTarget,
|
||||
runWithOperationTimeout,
|
||||
scrollUntilSettled,
|
||||
sleep,
|
||||
waitForPhotoExperience,
|
||||
@@ -107,77 +108,82 @@ async function collectZillowStructuredPhotoCandidates(page) {
|
||||
export async function extractZillowPhotos(rawUrl) {
|
||||
const requestedUrl = parseTarget(rawUrl);
|
||||
const { context, page } = await createPageSession({ headless: process.env.HEADLESS !== "false" });
|
||||
const closeContext = async () => {
|
||||
await context.close().catch(() => {});
|
||||
};
|
||||
|
||||
try {
|
||||
await gotoListing(page, requestedUrl);
|
||||
await dismissCommonOverlays(page);
|
||||
return await runWithOperationTimeout(
|
||||
"Zillow photo extraction",
|
||||
async () => {
|
||||
await gotoListing(page, requestedUrl);
|
||||
await dismissCommonOverlays(page);
|
||||
|
||||
const expectedPhotoCount = await getAnnouncedPhotoCount(page);
|
||||
const beforeUrl = page.url();
|
||||
let clickedLabel = null;
|
||||
let clickError = null;
|
||||
const expectedPhotoCount = await getAnnouncedPhotoCount(page);
|
||||
const beforeUrl = page.url();
|
||||
let clickedLabel = null;
|
||||
let clickError = null;
|
||||
|
||||
try {
|
||||
clickedLabel = await clickPhotoEntryPoint(page, ZILLOW_LABELS);
|
||||
await waitForPhotoExperience(page, beforeUrl);
|
||||
await scrollUntilSettled(page);
|
||||
await sleep(1200);
|
||||
} catch (error) {
|
||||
clickError = error instanceof Error ? error.message : String(error);
|
||||
}
|
||||
try {
|
||||
clickedLabel = await clickPhotoEntryPoint(page, ZILLOW_LABELS);
|
||||
await waitForPhotoExperience(page, beforeUrl);
|
||||
await scrollUntilSettled(page);
|
||||
await sleep(1200);
|
||||
} catch (error) {
|
||||
clickError = error instanceof Error ? error.message : String(error);
|
||||
}
|
||||
|
||||
const [structuredCandidates, renderedCandidates] = await Promise.all([
|
||||
collectZillowStructuredPhotoCandidates(page),
|
||||
collectZillowPhotoCandidates(page),
|
||||
]);
|
||||
const candidates = [...structuredCandidates, ...renderedCandidates];
|
||||
const normalized = normalizeImageCandidates(candidates, {
|
||||
hostIncludes: ["photos.zillowstatic.com"],
|
||||
minWidth: 240,
|
||||
minHeight: 180,
|
||||
});
|
||||
const photos = collapseZillowPhotos(normalized);
|
||||
const [structuredCandidates, renderedCandidates] = await Promise.all([
|
||||
collectZillowStructuredPhotoCandidates(page),
|
||||
collectZillowPhotoCandidates(page),
|
||||
]);
|
||||
const candidates = [...structuredCandidates, ...renderedCandidates];
|
||||
const normalized = normalizeImageCandidates(candidates, {
|
||||
hostIncludes: ["photos.zillowstatic.com"],
|
||||
minWidth: 240,
|
||||
minHeight: 180,
|
||||
});
|
||||
const photos = collapseZillowPhotos(normalized);
|
||||
|
||||
if (!photos.length) {
|
||||
fail(
|
||||
"Zillow photo extraction failed.",
|
||||
clickError || "No Zillow image URLs were found on the rendered listing page."
|
||||
);
|
||||
}
|
||||
if (!photos.length) {
|
||||
fail(
|
||||
"Zillow photo extraction failed.",
|
||||
clickError || "No Zillow image URLs were found on the rendered listing page."
|
||||
);
|
||||
}
|
||||
|
||||
const complete = expectedPhotoCount ? photos.length >= expectedPhotoCount : true;
|
||||
const notes = [];
|
||||
if (clickedLabel) {
|
||||
notes.push("Opened Zillow all-photos flow and extracted direct Zillow image URLs.");
|
||||
} else {
|
||||
notes.push("The rendered Zillow listing shell already exposed the Zillow photo stream, so extraction completed without relying on the all-photos click path.");
|
||||
}
|
||||
if (clickError) {
|
||||
notes.push(`All-photos click path was not required: ${clickError}`);
|
||||
}
|
||||
const complete = expectedPhotoCount ? photos.length >= expectedPhotoCount : true;
|
||||
const notes = [];
|
||||
if (clickedLabel) {
|
||||
notes.push("Opened Zillow all-photos flow and extracted direct Zillow image URLs.");
|
||||
} else {
|
||||
notes.push("The rendered Zillow listing shell already exposed the Zillow photo stream, so extraction completed without relying on the all-photos click path.");
|
||||
}
|
||||
if (clickError) {
|
||||
notes.push(`All-photos click path was not required: ${clickError}`);
|
||||
}
|
||||
|
||||
const result = {
|
||||
source: "zillow",
|
||||
requestedUrl,
|
||||
finalUrl: page.url(),
|
||||
title: await page.title(),
|
||||
clickedLabel,
|
||||
expectedPhotoCount,
|
||||
complete,
|
||||
photoCount: photos.length,
|
||||
imageUrls: photos.map((photo) => photo.url),
|
||||
notes,
|
||||
};
|
||||
|
||||
await context.close();
|
||||
return result;
|
||||
return {
|
||||
source: "zillow",
|
||||
requestedUrl,
|
||||
finalUrl: page.url(),
|
||||
title: await page.title(),
|
||||
clickedLabel,
|
||||
expectedPhotoCount,
|
||||
complete,
|
||||
photoCount: photos.length,
|
||||
imageUrls: photos.map((photo) => photo.url),
|
||||
notes,
|
||||
};
|
||||
},
|
||||
{
|
||||
onTimeout: closeContext
|
||||
}
|
||||
);
|
||||
} catch (error) {
|
||||
try {
|
||||
await context.close();
|
||||
} catch {
|
||||
// Ignore close errors after the primary failure.
|
||||
}
|
||||
throw new Error(error instanceof Error ? error.message : String(error));
|
||||
} finally {
|
||||
await closeContext();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user