fix/detail-page-image (#25)

* fix: fix can`t get imgurls on the page

* fix: 优化详情页图片下载逻辑,添加加载等待重试机制

* perf: 为打开see full view添加兜底机制

* fix(amazon): 使用 ul 类名 XPath 抓取五点描述

chore: 将扩展 displayName 更新为 v0.7.1.10-beta
Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
PetrichorFun 2026-05-08 17:14:13 +08:00 committed by GitHub
parent 2c503a9ab6
commit b062d96742
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 64 additions and 8 deletions

1
.gitignore vendored
View File

@ -8,6 +8,7 @@
*.pem
*.xpi
*.zip
*.7z
dist
dist-ssr
extension/manifest.json

View File

@ -1,6 +1,6 @@
{
"name": "azon-seeker",
"displayName": "Azon Seeker v0.7.1.7-beta",
"displayName": "Azon Seeker v0.7.1.10-beta",
"version": "0.7.2",
"private": true,
"description": "Starter modify by honestfox101 and PetrichorFun",

View File

@ -16,6 +16,7 @@ export const isFirefox = navigator.userAgent.includes('Firefox');
// export const remoteHost = __DEV__ ? '127.0.0.1:8000' : '47.251.4.191:8000';
// export const remoteHost = __DEV__ ? '127.0.0.1:18000' : 'vm8nc3zr-18000.usw2.devtunnels.ms';
export const remoteHost = __DEV__ ? 'ogsm-dev.rekeymed.com:8888' : 'amzplugin.rekeymed.com';
// export const remoteHost = __DEV__ ? 'ogsm-dev.rekeymed.com:8888' : 'amzplugin.rekeymed.com';
export const remoteHost = __DEV__ ? '127.0.0.1:18000' : 'amzplugin.rekeymed.com';
// export const remoteHost = __DEV__ ? '127.0.0.1:18000' : 'amzplugin.rekeymed.com';
// export const remoteHost = '47.251.4.191:8000';

View File

@ -22,7 +22,16 @@ const httpClient = createAlova({
method.config.headers['x-public-key'] = publicKeyBase64Data;
},
responded: {
onSuccess: (response) => response.json(),
// 使用 text() 而不是 json() 来避免 Response body 被重复读取的问题
// 参考: https://github.com/alovajs/alova/issues/XXX
onSuccess: async (response) => {
const text = await response.text();
// 检查空响应
if (!text || text.trim() === '') {
return null;
}
return JSON.parse(text);
},
onError: (response: Response) => {
const message = useMessage();
message.error(`HTTP Error: ${response.status}`);

View File

@ -490,11 +490,17 @@ export class AmazonDetailPageInjector extends BaseInjector {
public async getImageUrls() {
return this.run(async () => {
const overlay = document.querySelector<HTMLElement>('.overlayRestOfImages');
if (overlay) {
if (document.querySelector<HTMLElement>('#ivThumbs')!.getClientRects().length === 0) {
overlay.click();
// Click to see full view
document.querySelector<HTMLElement>('#canvasCaption > a')?.click();
await new Promise((resolve) => setTimeout(resolve, 1000));
}
} else {
// finally click to see full view if not clicked before
document.querySelector<HTMLElement>('#canvasCaption > a')?.click();
await new Promise((resolve) => setTimeout(resolve, 1000));
}
let script = document.evaluate(
`//script[starts-with(text(), "\nP.when(\'A\').register") or contains(text(), "\nP.when('A').register")]`,
@ -512,7 +518,46 @@ export class AmazonDetailPageInjector extends BaseInjector {
/(?<="large":")https:\/\/m.media-amazon.com\/images\/I\/[\w\d\.\-+]+(?=")/g,
);
}
document.querySelector<HTMLElement>('header > [data-action="a-popover-close"]')?.click();
if (urls.length === 0) {
const divs: NodeListOf<HTMLDivElement> =
document.querySelectorAll('.ivRow div.ivThumbImage');
await new Promise((resolve) => setTimeout(resolve, 500 + ~~(500 * Math.random())));
for (const div of divs) {
// click div com
div.click();
// wait for image to load
await new Promise((resolve) => setTimeout(resolve, 500 + ~~(500 * Math.random())));
// if img src is not loading gif, then get url and break, otherwise continue to wait, with a max retry count to avoid infinite loop
let retryCount = 0;
const maxRetries = 5;
while (true) {
const loadingGifUrl =
'https://m.media-amazon.com/images/G/01/ui/loadIndicators/loading-large_labeled._CB485921664_.gif';
// get img dom
const imgDom = document.querySelector('#ivLargeImage > img');
// get url from img dom src attribute
if (imgDom && 'src' in imgDom && imgDom.src !== loadingGifUrl) {
urls.push(imgDom.src as string);
break;
} else {
// wait for 500ms and try again
await new Promise((resolve) => setTimeout(resolve, 500 + ~~(500 * Math.random())));
retryCount++;
if (retryCount >= maxRetries) {
console.error('在detail页面下载图片已达最大重试次数:');
break;
}
}
}
}
}
document.querySelector<HTMLElement>('body > div.a-modal-scroller.a-declarative')?.click();
return urls;
});
}
@ -621,9 +666,9 @@ export class AmazonDetailPageInjector extends BaseInjector {
}
return nodes.length > 0 ? nodes : undefined;
};
const abouts = $x(
`//*[normalize-space(text())='About this item']/following-sibling::ul[1]/li`,
)?.map((el) => el.innerText);
const abouts = $x<HTMLElement>(
`(//ul[contains(concat(' ', normalize-space(@class), ' '), ' a-unordered-list ') and contains(concat(' ', normalize-space(@class), ' '), ' a-vertical ') and contains(concat(' ', normalize-space(@class), ' '), ' a-spacing-mini ')])[1]/li`,
)?.map((el) => el.innerText.trim());
const brand = $x(`//*[./span[normalize-space(text())='Brand']]/following-sibling::*[1]`)?.[0]
.innerText;
const flavor = $x(