@@ -349,6 +349,29 @@ async function pollPageExpression(
349349 return Boolean ( await page . evaluate ( expression ) ) ;
350350}
351351
352+ async function pollVideosReady (
353+ page : Page ,
354+ skipIds : readonly string [ ] ,
355+ timeoutMs : number ,
356+ intervalMs : number = 100 ,
357+ ) : Promise < boolean > {
358+ const check = async ( ) : Promise < boolean > => {
359+ return Boolean (
360+ await page . evaluate ( ( skipIdList : readonly string [ ] ) => {
361+ const skip = new Set ( skipIdList ) ;
362+ const vids = Array . from ( document . querySelectorAll ( "video" ) ) . filter ( ( v ) => ! skip . has ( v . id ) ) ;
363+ return vids . length === 0 || vids . every ( ( v ) => ( v as HTMLVideoElement ) . readyState >= 2 ) ;
364+ } , skipIds ) ,
365+ ) ;
366+ } ;
367+ const deadline = Date . now ( ) + timeoutMs ;
368+ while ( Date . now ( ) < deadline ) {
369+ if ( await check ( ) ) return true ;
370+ await new Promise ( ( resolve ) => setTimeout ( resolve , intervalMs ) ) ;
371+ }
372+ return check ( ) ;
373+ }
374+
352375async function applyVideoMetadataHints (
353376 page : Page ,
354377 hints : readonly CaptureVideoMetadataHint [ ] | undefined ,
@@ -490,10 +513,9 @@ export async function initializeSession(session: CaptureSession): Promise<void>
490513 // sources) whose frames come from ffmpeg out-of-band. videoMetadataHints
491514 // supply intrinsic dimensions for skipped videos whose layout depends on
492515 // aspect ratio, while Chromium may still fail to decode/load metadata.
493- const skipIdsLiteral = JSON . stringify ( session . options . skipReadinessVideoIds ?? [ ] ) ;
494- const videosReady = await pollPageExpression (
516+ const videosReady = await pollVideosReady (
495517 page ,
496- `(() => { const skip = new Set( ${ skipIdsLiteral } ); const vids = Array.from(document.querySelectorAll("video")).filter(v => !skip.has(v.id)); return vids.length === 0 || vids.every(v => v.readyState >= 2); })()` ,
518+ session . options . skipReadinessVideoIds ?? [ ] ,
497519 pageReadyTimeout ,
498520 ) ;
499521 if ( ! videosReady ) {
@@ -596,16 +618,11 @@ export async function initializeSession(session: CaptureSession): Promise<void>
596618 await applyVideoMetadataHints ( page , session . options . videoMetadataHints ) ;
597619
598620 // Same readyState contract as the screenshot path above (>= 2 / HAVE_CURRENT_DATA).
599- const beginframeSkipIdsLiteral = JSON . stringify ( session . options . skipReadinessVideoIds ?? [ ] ) ;
600- const videoDeadline =
601- Date . now ( ) + ( session . config ?. playerReadyTimeout ?? DEFAULT_CONFIG . playerReadyTimeout ) ;
602- while ( Date . now ( ) < videoDeadline ) {
603- const videosReady = await page . evaluate (
604- `(() => { const skip = new Set(${ beginframeSkipIdsLiteral } ); const vids = Array.from(document.querySelectorAll("video")).filter(v => !skip.has(v.id)); return vids.length === 0 || vids.every(v => v.readyState >= 2); })()` ,
605- ) ;
606- if ( videosReady ) break ;
607- await new Promise ( ( r ) => setTimeout ( r , 100 ) ) ;
608- }
621+ await pollVideosReady (
622+ page ,
623+ session . options . skipReadinessVideoIds ?? [ ] ,
624+ session . config ?. playerReadyTimeout ?? DEFAULT_CONFIG . playerReadyTimeout ,
625+ ) ;
609626
610627 // Font check (no rAF dependency — uses fonts.ready API directly)
611628 await page . evaluate ( `document.fonts?.ready` ) ;
0 commit comments