import type { AiCoursewareOutline } from "@/api/aiCourseware" import type { Slide } from "@/types/slides" import type { AiPptQualityIssue } from "@/utils/aiPptQuality" // 仅在浏览器检查实际可加载性,不以 URL 格式代替媒体可用性。 export async function inspectAiPptResources(outline: AiCoursewareOutline, slides: Slide[]): Promise { const pending = new Map>() const issues: AiPptQualityIssue[] = [] const probe = (url: string, kind: "image" | "audio" | "video") => { const key = `${kind}:${url}` if (!pending.has(key)) pending.set(key, new Promise((resolve, reject) => { const media = kind === "image" ? new Image() : document.createElement(kind) const finish = (error?: string) => { clearTimeout(timer) media.onload = media.onerror = null if (media instanceof HTMLMediaElement) { media.onloadedmetadata = null media.removeAttribute("src") media.load() } if (error) reject(new Error(error)) else resolve(media instanceof HTMLImageElement ? { width: media.naturalWidth, height: media.naturalHeight } : { width: 0, height: 0 }) } const timer = window.setTimeout(() => finish("加载超时"), 15000) media.onerror = () => finish("无法加载或格式不受支持") if (media instanceof HTMLMediaElement) { media.preload = "metadata" media.onloadedmetadata = () => finish() } else media.onload = () => finish() media.src = url })) return pending.get(key)! } await Promise.all(slides.map(async (slide, index) => { const scoreUrls = new Set((outline.pages[index]?.resourceBindings || []).filter(item => item.resourceType === "MUSIC").map(item => item.scoreImageUrl)) await Promise.all(slide.elements.map(async element => { const kind = element.type === "image" ? "image" : element.type === "elf" && element.subtype === "elf-audio" ? "audio" : element.type === "elf" && element.subtype === "elf-video" ? "video" : undefined if (!kind || !("src" in element)) return try { const size = await probe(element.src, kind) // 谱图完整等比放进原槽位,禁止拉伸音符或裁掉小节。 if (element.type === "image" && scoreUrls.has(element.src) && size.width && size.height) { const scale = Math.min(element.width / size.width, element.height / size.height) const width = size.width * scale const height = size.height * scale element.left += (element.width - width) / 2 element.top += (element.height - height) / 2 element.width = width element.height = height } } catch (error) { issues.push({ code: "resource_unavailable", severity: "error", pageNo: index + 1, message: `${kind === "image" ? "图片" : kind === "audio" ? "音频" : "视频"}${error instanceof Error ? error.message : "不可用"},请更换资源后重试` }) } })) })) return issues }