78 lines
2.6 KiB
TypeScript
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 };
|
|
}
|