Files
2026-08-16 14:58:47 +08:00

78 lines
2.6 KiB
TypeScript

/** Pure pass that promotes prepared interactive HTML to a first-class scene base. */
import type { CompilerScene, InteractiveHtmlMeta, InteractiveHtmlSource } from '../deps';
import { INTERACTIVE_READY_TIMEOUT_MS, INTERACTIVE_SETTLE_MS } from '../interactive-static';
import type { Diagnostic, DiagnosticCode, VideoTimelineScene } from '../ir';
export interface InteractiveResult {
scenes: VideoTimelineScene[];
diagnostics: Diagnostic[];
}
function failureCode(meta: InteractiveHtmlMeta | null): DiagnosticCode {
if (!meta || meta.failure === 'missing-html') return 'missing-interactive-html';
if (meta.failure === 'unresolved-resource') return 'unresolved-interactive-resource';
return 'interactive-html-packaging';
}
function failureReason(meta: InteractiveHtmlMeta | null): string {
if (!meta || meta.failure === 'missing-html') {
return 'missing-interactive-html';
}
return meta.message || 'interactive-html-packaging';
}
export function applyInteractiveHtml(
timelineScenes: readonly VideoTimelineScene[],
sourceScenes: readonly CompilerScene[],
source?: InteractiveHtmlSource,
): InteractiveResult {
const diagnostics: Diagnostic[] = [];
const scenes = timelineScenes.map((timeline, index): VideoTimelineScene => {
const scene = sourceScenes[index];
if (!scene || scene.type !== 'interactive') return timeline;
const hasEmbeddedHtml =
typeof scene.content?.html === 'string' && scene.content.html.trim() !== '';
const meta = hasEmbeddedHtml ? (source?.html(scene) ?? null) : null;
if (!hasEmbeddedHtml || !meta?.present || !meta.contentHash) {
const reason = failureReason(
hasEmbeddedHtml
? (meta ?? {
id: `interactive:${scene.id}`,
present: false,
failure: 'packaging-failed',
message: 'interactive-html-packaging',
})
: null,
);
diagnostics.push({
severity: 'warn',
code: hasEmbeddedHtml ? failureCode(meta) : 'missing-interactive-html',
sceneId: timeline.id,
message: reason,
});
return { ...timeline, base: { kind: 'placeholder', reason } };
}
diagnostics.push({
severity: 'info',
code: 'interactive-static-html',
sceneId: timeline.id,
message: 'interactive-static-html',
});
return {
...timeline,
supported: true,
base: {
kind: 'interactive-html',
assetId: meta.id,
contentHash: meta.contentHash,
readyTimeoutMs: INTERACTIVE_READY_TIMEOUT_MS,
settleMs: INTERACTIVE_SETTLE_MS,
},
};
});
return { scenes, diagnostics };
}