Skip to content

Commit 28c3a2d

Browse files
committed
fix(opencv): Handle delayed cv.ready and add timeout
This fixes an issue where opencv was not being able to load. This likely was because `cv.ready` would not resolve.
1 parent 46cbfc9 commit 28c3a2d

File tree

1 file changed

+67
-4
lines changed

1 file changed

+67
-4
lines changed

web_ui/packages/smart-tools/src/utils/opencv-loader.ts

Lines changed: 67 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,79 @@
33

44
import type { OpenCVTypes } from '../opencv/interfaces';
55

6+
const READY_CHECK_INTERVAL_MS = 100;
7+
const OPENCV_LOAD_TIMEOUT_MS = 30_000;
8+
69
let opencv: OpenCVTypes | null = null;
10+
let loadingPromise: Promise<OpenCVTypes> | null = null;
11+
12+
function delay(ms: number): Promise<void> {
13+
return new Promise((resolve) => setTimeout(resolve, ms));
14+
}
15+
16+
/**
17+
* Wait for cv.ready to be available with polling
18+
* Some OpenCV builds delay initialization of cv.ready
19+
*/
20+
const waitForOpenCVReady = async (cv: OpenCVTypes): Promise<void> => {
21+
const startTime = Date.now();
22+
23+
while (Date.now() - startTime < OPENCV_LOAD_TIMEOUT_MS) {
24+
// Check if cv.ready exists and is a Promise-like object
25+
if (cv && typeof cv.ready === 'object' && 'then' in cv.ready) {
26+
try {
27+
await cv.ready;
28+
return; // Success
29+
} catch (error) {
30+
console.error('Error waiting for cv.ready:', error);
31+
throw error;
32+
}
33+
}
34+
35+
// Check if cv.ready is already resolved (some builds may have it pre-resolved)
36+
if (cv && cv.onload && typeof cv.onload === 'function') {
37+
return;
38+
}
39+
40+
// cv.ready not available yet, wait and retry
41+
await delay(READY_CHECK_INTERVAL_MS);
42+
}
43+
44+
throw new Error(
45+
`Timeout waiting for cv.ready (${OPENCV_LOAD_TIMEOUT_MS}ms). ` +
46+
'OpenCV may not be properly built or the file is corrupted.'
47+
);
48+
};
749

850
export const OpenCVLoader = async (): Promise<OpenCVTypes> => {
951
if (opencv) return opencv;
52+
if (loadingPromise) return loadingPromise;
1053

11-
const cv: OpenCVTypes = await import('../opencv/4.9.0/opencv.js');
54+
loadingPromise = Promise.race([
55+
(async () => {
56+
try {
57+
const cv: OpenCVTypes = await import('../opencv/4.9.0/opencv.js');
1258

13-
if ('ready' in cv) await cv.ready;
59+
// Wait for cv.ready with polling and timeout
60+
await waitForOpenCVReady(cv);
1461

15-
opencv = cv;
62+
if (!cv.Mat) {
63+
throw new Error('OpenCV missing essential methods');
64+
}
65+
opencv = cv;
66+
return opencv;
67+
} catch (error) {
68+
loadingPromise = null;
69+
throw error;
70+
}
71+
})(),
72+
new Promise<never>((_, reject) =>
73+
setTimeout(
74+
() => reject(new Error(`OpenCV loading timeout (${OPENCV_LOAD_TIMEOUT_MS}ms)`)),
75+
OPENCV_LOAD_TIMEOUT_MS
76+
)
77+
),
78+
]);
1679

17-
return opencv;
80+
return loadingPromise;
1881
};

0 commit comments

Comments
 (0)