11import React , { useRef } from 'react' ;
22import type { EditorController } from '../useEditor' ;
3- import type { ContentData , EditorContext } from '../types' ;
3+ import type { ContentData , EditorContext , RawTranscript } from '../types' ;
44import { getMimeTypeLabel , t } from '../i18n/i18n' ;
55import { FileIcon } from '../icons' ;
6+ import { isVideoMimeType } from '../constants' ;
67
7- /**
8- * Preview via the legacy ekstep content renderer — the same mechanism the old
9- * generic editor used (org.ekstep.genericeditorpreview). One renderer handles all
10- * mimeTypes (video/pdf/epub/ecml/html/scorm/h5p/youtube/url) through its coreplugins.
11- *
12- * Loads `content/preview/preview.html?webview=true` in an iframe (same-origin via the
13- * host proxy), then calls its global `initializePreview()` with the content id +
14- * metadata. No player-v2 dependency, no client-side unzip.
15- */
8+ interface PlayerTranscript {
9+ language : string ;
10+ identifier : string ;
11+ languageCode : string ;
12+ artifactUrl : string ;
13+ wordByWordUrl ?: string ;
14+ sourceLanguage ?: boolean ;
15+ }
16+
17+ /** Maps raw enrichment.transcripts into the shape sunbird-video-player expects, mirroring
18+ * the portal's ContentService mapping (artifactUrl here is the VTT, not transcript.json).
19+ * Requires an explicit 'Live' status - a missing status is treated the same safe way
20+ * TranscriptsDrawer.statusTone does (not-yet-approved), never served as a live caption. */
21+ function mapRawTranscripts ( raw : RawTranscript [ ] ) : PlayerTranscript [ ] {
22+ return raw
23+ . filter ( ( e ) : e is RawTranscript & { captionsUrl : string } =>
24+ ! ! e . captionsUrl && e . status === 'Live' )
25+ . map ( ( e ) => ( {
26+ language : e . language || ( e . languageCode || 'Unknown' ) . toUpperCase ( ) ,
27+ identifier : e . code ?? e . identifier ?? '' ,
28+ languageCode : e . languageCode || '' ,
29+ artifactUrl : e . captionsUrl ,
30+ wordByWordUrl : e . captionsUrl ,
31+ sourceLanguage : ! ! e . sourceLanguage ,
32+ } ) ) ;
33+ }
34+
35+ /** Preview via the legacy ekstep content renderer (all mimeTypes through its coreplugins), loaded in an iframe and driven via its global `initializePreview()`. */
1636const RendererPreview : React . FC < {
1737 content : ContentData ; context : EditorContext ; previewUrl : string ; previewConfig : Record < string , unknown > ;
1838} > = ( { content, context, previewUrl, previewConfig } ) => {
@@ -55,9 +75,17 @@ const RendererPreview: React.FC<{
5575} ;
5676
5777const EditorPreview : React . FC < { ed : EditorController ; context : EditorContext } > = ( { ed, context } ) => {
58- const { content, lang, previewUrl, previewConfig } = ed ;
78+ const { content, lang, previewUrl, previewConfig, transcripts : rawTranscripts , transcriptsChecked } = ed ;
79+
5980 if ( ! content ) return null ;
6081
82+ const isVideo = isVideoMimeType ( content . mimeType ) ;
83+ const transcripts = mapRawTranscripts ( rawTranscripts ) ;
84+ // The iframe key changes only when the actual live-caption set changes (not just its
85+ // count), so a same-count swap (one language leaves Live as another joins) still remounts.
86+ const transcriptsKey = transcripts . map ( ( tr ) => tr . identifier ) . sort ( ) . join ( ',' ) ;
87+ const previewMetadata = transcripts . length ? { ...content , transcripts } : content ;
88+
6189 return (
6290 < div className = "ce-preview-card" >
6391 < div className = "ce-preview-bar" >
@@ -67,13 +95,22 @@ const EditorPreview: React.FC<{ ed: EditorController; context: EditorContext }>
6795 < span className = "ce-preview-chip" > { t ( lang , 'PREVIEW_MODE' ) } </ span >
6896 </ div >
6997 < div className = "ce-preview-frame" >
70- < RendererPreview
71- key = { `${ content . identifier } -${ content . artifactUrl ?? '' } ` }
72- content = { content }
73- context = { context }
74- previewUrl = { previewUrl }
75- previewConfig = { previewConfig }
76- />
98+ { isVideo && ! transcriptsChecked ? (
99+ // Wait for the first transcripts check before ever mounting the renderer, so a
100+ // slow initial read can't yank the iframe out (and restart playback) right after
101+ // the user presses play - by the time it first mounts, this is already settled.
102+ < div className = "ce-center" style = { { height : '100%' } } >
103+ < div className = "ce-spinner ce-spinner--sm" />
104+ </ div >
105+ ) : (
106+ < RendererPreview
107+ key = { `${ content . identifier } -${ content . artifactUrl ?? '' } -${ transcriptsKey } ` }
108+ content = { previewMetadata }
109+ context = { context }
110+ previewUrl = { previewUrl }
111+ previewConfig = { previewConfig }
112+ />
113+ ) }
77114 </ div >
78115 </ div >
79116 ) ;
0 commit comments