const VOLATILE_IMAGE_HOSTS = new Set([ 'finder.video.qq.com', ]); const VOLATILE_IMAGE_PATH_PARTS = [ '/stodownload', ]; const IMAGE_FIELD_KEYS = new Set([ 'avatar', 'avatarUrl', 'avatar_url', 'accountAvatar', 'authorAvatar', 'cover', 'coverUrl', 'cover_url', 'thumbnail', 'thumbnailUrl', 'thumbnail_url', ]); function isVolatileRemoteImageUrl(value: string): boolean { const trimmed = value.trim(); if (!trimmed || trimmed.startsWith('data:') || trimmed.startsWith('blob:')) { return false; } try { const url = new URL(trimmed); const host = url.hostname.toLowerCase(); const path = url.pathname.toLowerCase(); if (VOLATILE_IMAGE_HOSTS.has(host) && VOLATILE_IMAGE_PATH_PARTS.some((part) => path.includes(part))) { return true; } return host.endsWith('.video.qq.com') && url.searchParams.has('encfilekey'); } catch { return false; } } export function getSafeImageSrc(value: string | null | undefined): string | undefined { if (!value) return undefined; const trimmed = value.trim(); if (!trimmed || isVolatileRemoteImageUrl(trimmed)) return undefined; return trimmed; } export function sanitizeApiImageFields(payload: T): T { const seen = new WeakSet(); function visit(value: unknown): unknown { if (!value || typeof value !== 'object') return value; if (seen.has(value)) return value; seen.add(value); if (Array.isArray(value)) { value.forEach((item) => visit(item)); return value; } const record = value as Record; Object.keys(record).forEach((key) => { const fieldValue = record[key]; if (typeof fieldValue === 'string' && IMAGE_FIELD_KEYS.has(key) && isVolatileRemoteImageUrl(fieldValue)) { record[key] = ''; return; } visit(fieldValue); }); return value; } return visit(payload) as T; }