mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-01 19:42:03 +00:00
Merge pull request #2681 from heygen-com/07-21-feat_engine_warn_on_live_map_viewports_at_capture_init
feat(engine): warn when a live map viewport is detected at capture init
This commit is contained in:
@@ -0,0 +1,24 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { buildLiveMapWarning } from "./frameCapture.js";
|
||||
|
||||
describe("buildLiveMapWarning", () => {
|
||||
it("names every detected map library in the message and details", () => {
|
||||
const warning = buildLiveMapWarning(["Leaflet", "MapLibre GL"]);
|
||||
expect(warning.code).toBe("live_map_detected");
|
||||
expect(warning.message).toContain("Leaflet, MapLibre GL");
|
||||
expect(warning.details?.sources).toEqual(["Leaflet", "MapLibre GL"]);
|
||||
});
|
||||
|
||||
it("points the author at the bake pipeline instead of just describing the failure", () => {
|
||||
const warning = buildLiveMapWarning(["Leaflet"]);
|
||||
expect(warning.message).toContain("bake-basemap.mjs");
|
||||
expect(warning.message).toContain("deterministic-render");
|
||||
});
|
||||
|
||||
it("copies the libraries array so later mutation cannot alter the recorded warning", () => {
|
||||
const libraries = ["Leaflet"];
|
||||
const warning = buildLiveMapWarning(libraries);
|
||||
libraries.push("Mapbox GL");
|
||||
expect(warning.details?.sources).toEqual(["Leaflet"]);
|
||||
});
|
||||
});
|
||||
@@ -1716,6 +1716,53 @@ function recordSubTimelineWarning(session: CaptureSession, timeoutMs: number): v
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runtime-mounted map-viewport markers, keyed by the CSS selector each map
|
||||
* library stamps on its live container. Selector presence means an actual map
|
||||
* INSTANCE is rendering in the page — not merely that a map library script
|
||||
* loaded — which keeps false positives low (a baked map video carries none of
|
||||
* these).
|
||||
*/
|
||||
const LIVE_MAP_MARKERS: ReadonlyArray<{ selector: string; library: string }> = [
|
||||
{ selector: ".leaflet-container", library: "Leaflet" },
|
||||
{ selector: ".maplibregl-map", library: "MapLibre GL" },
|
||||
{ selector: ".mapboxgl-map", library: "Mapbox GL" },
|
||||
{ selector: ".gm-style", library: "Google Maps" },
|
||||
{ selector: ".ol-viewport", library: "OpenLayers" },
|
||||
];
|
||||
|
||||
/**
|
||||
* Build the `live_map_detected` warning for the detected map libraries.
|
||||
* A live tile map violates the deterministic-render contract (tiles are
|
||||
* render-time network fetches): none of the readiness polls cover late-added
|
||||
* tile images or map canvases, so frames captured before tiles arrive ship a
|
||||
* blank/partial map with no error — the render exits success. Wild signature:
|
||||
* PRINFRA-300 (blank hook scene, zero diagnostics, "fixed" by re-render).
|
||||
*/
|
||||
export function buildLiveMapWarning(libraries: readonly string[]): CaptureWarning {
|
||||
return {
|
||||
code: "live_map_detected",
|
||||
message:
|
||||
`Live map viewport(s) detected in the composition (${libraries.join(", ")}). ` +
|
||||
`Map tiles load over the network at render time, which the deterministic-render ` +
|
||||
`contract forbids — frames captured before tiles arrive ship a blank or partial map ` +
|
||||
`with no error. Bake the map to a video first (see the motion-graphics maps skill / ` +
|
||||
`bake-basemap.mjs) and use the baked file as the imagery layer.`,
|
||||
details: { sources: [...libraries] },
|
||||
};
|
||||
}
|
||||
|
||||
/** Detect live map viewports in the page and record the warning. */
|
||||
async function recordLiveMapWarning(session: CaptureSession, page: Page): Promise<void> {
|
||||
const libraries = (await page.evaluate(
|
||||
(markers: ReadonlyArray<{ selector: string; library: string }>) =>
|
||||
markers.filter((m) => document.querySelector(m.selector) !== null).map((m) => m.library),
|
||||
LIVE_MAP_MARKERS,
|
||||
)) as string[];
|
||||
if (libraries.length === 0) return;
|
||||
recordCaptureWarnings(session, [buildLiveMapWarning(libraries)]);
|
||||
}
|
||||
|
||||
// Force every successfully-loaded `<img>` to be GPU-uploaded before the first
|
||||
// frame capture. `naturalWidth > 0` means the bitmap has been decoded into
|
||||
// CPU memory, but compositor-side GPU upload can still happen lazily on first
|
||||
@@ -2002,6 +2049,7 @@ export async function initializeSession(session: CaptureSession): Promise<void>
|
||||
session,
|
||||
await collectMediaReadinessWarnings(page, skipVideoIds, pageReadyTimeout),
|
||||
);
|
||||
await recordLiveMapWarning(session, page);
|
||||
|
||||
await recordSessionInitTelemetry(session, initStart);
|
||||
|
||||
@@ -2164,6 +2212,7 @@ export async function initializeSession(session: CaptureSession): Promise<void>
|
||||
session,
|
||||
await collectMediaReadinessWarnings(page, bfSkipVideoIds, pageReadyTimeout),
|
||||
);
|
||||
await recordLiveMapWarning(session, page);
|
||||
|
||||
await recordSessionInitTelemetry(session, initStart);
|
||||
|
||||
|
||||
@@ -18,7 +18,8 @@ export type CaptureWarningCode =
|
||||
| "media_load_failed"
|
||||
| "audio_processing_failed"
|
||||
| "sub_timeline_readiness_timeout"
|
||||
| "sub_timeline_script_failure";
|
||||
| "sub_timeline_script_failure"
|
||||
| "live_map_detected";
|
||||
|
||||
/** Structured correctness warning produced while preparing a capture session. */
|
||||
export interface CaptureWarning {
|
||||
|
||||
Reference in New Issue
Block a user