@@ -46,6 +46,9 @@ class DashViewer extends VideoBaseViewer {
4646 /** @property {Object } - Status of the filmstrip representation */
4747 filmstripStatus ;
4848
49+ /** @property {Object } - Status of the extracted_text (transcription) representation */
50+ transcriptionStatus ;
51+
4952 /** @property {string } - URL for the filmstrip image */
5053 filmstripUrl ;
5154
@@ -164,6 +167,10 @@ class DashViewer extends VideoBaseViewer {
164167 this . filmstripStatus . destroy ( ) ;
165168 }
166169
170+ if ( this . transcriptionStatus ) {
171+ this . transcriptionStatus . destroy ( ) ;
172+ }
173+
167174 clearInterval ( this . statsIntervalId ) ;
168175 if ( this . player ) {
169176 this . player . destroy ( ) ;
@@ -687,6 +694,17 @@ class DashViewer extends VideoBaseViewer {
687694 }
688695 }
689696
697+ /**
698+ * Returns the display-friendly name for a text track's language.
699+ * Maps the undetermined language code 'und' to a localized "Auto-Generated" label.
700+ *
701+ * @param {Object } track - A Shaka text track object
702+ * @return {string } Localized language name or the raw language code
703+ */
704+ getTrackDisplayLanguage ( track ) {
705+ return track . language === 'und' ? __ ( 'auto_generated' ) : getLanguageName ( track . language ) || track . language ;
706+ }
707+
690708 /**
691709 * Loads captions/subtitles into the settings menu
692710 *
@@ -701,7 +719,7 @@ class DashViewer extends VideoBaseViewer {
701719 this . initSubtitles ( ) ;
702720 } else {
703721 this . mediaControls . initSubtitles (
704- this . textTracks . map ( track => getLanguageName ( track . language ) || track . language ) ,
722+ this . textTracks . map ( track => this . getTrackDisplayLanguage ( track ) ) ,
705723 getLanguageName ( this . options . location . locale . substring ( 0 , 2 ) ) ,
706724 ) ;
707725 }
@@ -741,7 +759,7 @@ class DashViewer extends VideoBaseViewer {
741759
742760 this . textTracks = this . textTracks . map ( track => ( {
743761 ...track ,
744- displayLanguage : getLanguageName ( track . language ) || track . language ,
762+ displayLanguage : this . getTrackDisplayLanguage ( track ) ,
745763 } ) ) ;
746764
747765 // Do intelligent selection: Prefer user's language, fallback to English, then first subtitle in list
@@ -895,6 +913,7 @@ class DashViewer extends VideoBaseViewer {
895913 this . startBandwidthTracking ( ) ;
896914 this . loadFilmStrip ( ) ;
897915 this . loadSubtitles ( ) ;
916+ this . loadTranscription ( ) ;
898917 this . loadAlternateAudio ( ) ;
899918 this . showPlayButton ( ) ;
900919
@@ -967,6 +986,69 @@ class DashViewer extends VideoBaseViewer {
967986 }
968987 }
969988
989+ /**
990+ * Loads the extracted_text transcription (.vtt) as a text track when available
991+ *
992+ * @private
993+ * @return {void }
994+ */
995+ async loadTranscription ( ) {
996+ const extractedText = getRepresentation ( this . options . file , 'extracted_text' ) ;
997+ if ( ! extractedText ?. content ?. url_template ) {
998+ return ;
999+ }
1000+
1001+ const transcriptionUrl = this . createContentUrlWithAuthParams ( extractedText . content . url_template ) ;
1002+ this . transcriptionStatus = this . getRepStatus ( extractedText ) ;
1003+
1004+ try {
1005+ await this . transcriptionStatus . getPromise ( ) ;
1006+
1007+ if ( this . isDestroyed ( ) || ! this . player ) {
1008+ return ;
1009+ }
1010+
1011+ await this . player . addTextTrackAsync (
1012+ transcriptionUrl ,
1013+ 'und' ,
1014+ 'subtitles' ,
1015+ 'text/vtt' ,
1016+ undefined ,
1017+ __ ( 'auto_generated' ) ,
1018+ ) ;
1019+
1020+ if ( this . isDestroyed ( ) ) {
1021+ return ;
1022+ }
1023+
1024+ if ( this . textTracks . length > 0 ) {
1025+ // Subtitles were already initialized — find and append only
1026+ // the new track(s) without disturbing the user's selection.
1027+ const prevTracks = this . textTracks ;
1028+ const prevIds = new Set ( prevTracks . map ( t => t . id ) ) ;
1029+ this . textTracks = this . player . getTextTracks ( ) . sort ( ( track1 , track2 ) => track1 . id - track2 . id ) ;
1030+
1031+ if ( this . useReactControls ( ) ) {
1032+ this . textTracks = this . textTracks . map ( track => ( {
1033+ ...track ,
1034+ displayLanguage : this . getTrackDisplayLanguage ( track ) ,
1035+ } ) ) ;
1036+ this . renderUI ( ) ;
1037+ } else {
1038+ this . textTracks . forEach ( ( track , idx ) => {
1039+ if ( ! prevIds . has ( track . id ) ) {
1040+ this . mediaControls . settings . addSubtitle ( __ ( 'auto_generated' ) , idx ) ;
1041+ }
1042+ } ) ;
1043+ }
1044+ } else {
1045+ this . loadSubtitles ( ) ;
1046+ }
1047+ } catch {
1048+ // Transcription is non-critical; allow the viewer to continue without it
1049+ }
1050+ }
1051+
9701052 /**
9711053 * Calculates the video dimension based on representations
9721054 *
0 commit comments