feat(edit): 编辑上报细节优化

This commit is contained in:
2026-10-06 18:30:05 +08:00
parent e20cd7547f
commit 79b310efe7
92 changed files with 2764 additions and 368 deletions
+55 -2
View File
@@ -29,6 +29,14 @@ type ImageGroup = {
*/
const DOM_IMAGE_PROFILES: Record<DomMediaPlatform, ImageGroup[]> = {
'1688': [
{
role: 'source_main',
relation: 'inherited_product_default',
// 画廊缩略项虽小,但背景地址指向商品原图;位置与控件类型比尺寸更可靠。
selectors: ['.od-picture-gallery-list > .v-image-cover'],
sourceProps: ['backgroundImage'],
excludeWithin: ['.video-image-cover', '.v-image-cover:has(.cpv-icon, .cpv-tag-name)'],
},
{
role: 'source_main',
relation: 'inherited_product_default',
@@ -43,6 +51,7 @@ const DOM_IMAGE_PROFILES: Record<DomMediaPlatform, ImageGroup[]> = {
'.detail-gallery-turn img.detail-gallery-img',
'.img-list-wrapper img.od-gallery-img',
],
excludeWithin: ['.video-image-cover', '.v-image-cover:has(.cpv-icon, .cpv-tag-name)'],
minWidth: 200,
minHeight: 200,
},
@@ -189,15 +198,58 @@ export function collectPlatformDomAssets(
}
}
}
if (platform === '1688') add1688GalleryVideos(root, pageUrl, assets);
return assets;
}
/** 只读取商品画廊内播放器的公开直链,不扫描全页视频或根据封面猜测地址。 */
function add1688GalleryVideos(root: Document, pageUrl: string, assets: Asset[]) {
const selectors = [
'[data-module="od_picture_gallery"] video',
'.module-od-picture-gallery video',
'.od-picture-gallery video',
'.od-picture-gallery-list video',
'.detail-gallery-turn video',
'.od-gallery-turn-item-wrapper video',
];
const seen = new Set<string>();
for (const selector of selectors) {
for (const video of Array.from(root.querySelectorAll<HTMLVideoElement>(selector))) {
const candidates = [video.currentSrc, video.getAttribute('src'),
...Array.from(video.querySelectorAll('source')).map((source) => source.getAttribute('src'))];
const url = candidates.map((candidate) => normaliseVideoUrl(candidate ?? '', pageUrl)).find(Boolean);
if (!url || seen.has(url) || assets.length >= 500) continue;
seen.add(url);
const cover = root.querySelector<HTMLElement>('.od-picture-gallery-list > .video-image-cover');
const poster = video.poster || video.getAttribute('poster');
const posterUrl = (poster ? normaliseImageUrl(poster, pageUrl, '1688') : undefined) ??
(cover ? imageUrlFromElement(cover, ['backgroundImage'], pageUrl, '1688') : undefined);
assets.push({
url, mediaType: 'video', role: 'source_main', relation: 'product_candidate',
...(posterUrl?.startsWith('https://') ? { posterUrl } : {}),
sortOrder: assets.length, evidenceRef: '1688:dom:source_video',
});
}
}
}
export function normaliseVideoUrl(raw: string, pageUrl: string): string | undefined {
if (!raw.trim()) return undefined;
try {
const url = new URL(raw, pageUrl);
return url.protocol === 'https:' && /\.(mp4|webm)$/i.test(url.pathname) ? url.href : undefined;
} catch {
return undefined;
}
}
/** Restores the original CDN URL before payload storage and de-duplication. */
export function normaliseImageUrl(
raw: string,
pageUrl: string,
platform: SupportedMediaPlatform,
): string | undefined {
if (!raw.trim() || /^(?:undefined|null|none)$/i.test(raw.trim())) return undefined;
let url: URL;
try {
url = new URL(raw, pageUrl);
@@ -266,9 +318,10 @@ function imageUrlFromElement(
else if (prop === 'currentSrc')
raw = (target as HTMLImageElement).currentSrc ?? '';
else if (prop === 'backgroundImage') {
// 优先使用内联地址,避免占位值 undefined 被计算样式解析成一个页面相对 URL。
const style =
target.ownerDocument.defaultView?.getComputedStyle(target)
.backgroundImage ?? target.style.backgroundImage;
target.style.backgroundImage ||
target.ownerDocument.defaultView?.getComputedStyle(target).backgroundImage || '';
raw = style.match(/url\(["']?(.*?)["']?\)/i)?.[1] ?? '';
// Some 1688 SKU selectors directly match an <img> despite the
// group being background-image-first.