@@ -37,6 +37,10 @@ function withHTMLSubtitles(Video) {
3737 var assRenderer = createASSRenderer ( {
3838 containerElement : containerElement ,
3939 onError : function ( error , track ) {
40+ if ( track && track . id === selectedEmbeddedASSTrackId ) {
41+ fallbackFromEmbeddedASS ( error , track ) ;
42+ return ;
43+ }
4044 fallbackFromASS ( error , track , selectedSubtitleText )
4145 . catch ( function ( fallbackError ) {
4246 reportSubtitleError ( fallbackError , track ) ;
@@ -141,6 +145,10 @@ function withHTMLSubtitles(Video) {
141145 var usingASSRenderer = false ;
142146 var selectedSubtitleText = null ;
143147 var selectionRequestId = 0 ;
148+ var embeddedASSSources = [ ] ;
149+ var videoSubtitleTracks = [ ] ;
150+ var selectedEmbeddedASSTrackId = null ;
151+ var embeddedSubtitlesOpacity = 1 ;
144152 var nativeAssSubtitlesSupported = options . shellTransport &&
145153 options . shellTransport . capabilities &&
146154 options . shellTransport . capabilities . nativeAssSubtitles === true ;
@@ -277,6 +285,7 @@ function withHTMLSubtitles(Video) {
277285 break ;
278286 }
279287 case 'subtitlesTracks' : {
288+ videoSubtitleTracks = Array . isArray ( propValue ) ? propValue : [ ] ;
280289 updateNativeASSTrack ( propValue ) ;
281290 break ;
282291 }
@@ -371,11 +380,33 @@ function withHTMLSubtitles(Video) {
371380 }
372381 function getPublicSubtitleTrack ( track ) {
373382 var publicTrack = Object . assign ( { } , track ) ;
383+ if ( getEmbeddedASSSource ( track ) !== null ) {
384+ publicTrack . ass = true ;
385+ }
374386 delete publicTrack . nativeExternal ;
375387 delete publicTrack . nativeExternalFilename ;
376388 delete publicTrack . nativeExternalTitle ;
389+ delete publicTrack . _hlsSubtitlePlaylistURL ;
377390 return publicTrack ;
378391 }
392+ function getEmbeddedSubtitleId ( track ) {
393+ if ( ! track || typeof track . _hlsSubtitlePlaylistURL !== 'string' ) {
394+ return null ;
395+ }
396+
397+ var match = track . _hlsSubtitlePlaylistURL . match ( / (?: ^ | \/ ) s u b t i t l e ( \d + ) \. m 3 u 8 (?: $ | [ ? # ] ) / i) ;
398+ return match === null ? null : match [ 1 ] ;
399+ }
400+ function getEmbeddedASSSource ( track ) {
401+ var subtitleId = getEmbeddedSubtitleId ( track ) ;
402+ if ( subtitleId === null ) {
403+ return null ;
404+ }
405+
406+ return embeddedASSSources . find ( function ( source ) {
407+ return String ( source . id ) === subtitleId ;
408+ } ) || null ;
409+ }
379410 function isManagedNativeASSTrack ( track ) {
380411 return track . id === nativeAssTrackId ||
381412 removingNativeAssTrackIds . includes ( track . id ) ||
@@ -503,6 +534,78 @@ function withHTMLSubtitles(Video) {
503534 loadPlainSubtitles ( track , text , true , currentRequestId ) :
504535 Promise . resolve ( null ) ;
505536 }
537+ function clearEmbeddedASS ( ) {
538+ if ( selectedEmbeddedASSTrackId === null ) {
539+ return ;
540+ }
541+
542+ selectedEmbeddedASSTrackId = null ;
543+ usingASSRenderer = false ;
544+ setASSSubtitlesStylingActive ( false ) ;
545+ assRenderer . destroy ( ) ;
546+ subtitlesElement . style . display = '' ;
547+ if ( selectedTrackId === null ) {
548+ stopRenderLoop ( ) ;
549+ }
550+ }
551+ function fallbackFromEmbeddedASS ( error , track , currentRequestId ) {
552+ currentRequestId = currentRequestId || selectionRequestId ;
553+ if ( ! track || currentRequestId !== selectionRequestId || selectedEmbeddedASSTrackId !== track . id ) {
554+ return ;
555+ }
556+
557+ var trackId = selectedEmbeddedASSTrackId ;
558+ reportSubtitleError ( error , getPublicSubtitleTrack ( track ) ) ;
559+ clearEmbeddedASS ( ) ;
560+ video . dispatch ( {
561+ type : 'setProp' ,
562+ propName : 'selectedSubtitlesTrackId' ,
563+ propValue : trackId ,
564+ } ) ;
565+ }
566+ function loadEmbeddedASS ( track , source , currentRequestId ) {
567+ var assTrack = Object . assign ( { } , getPublicSubtitleTrack ( track ) , {
568+ ass : true ,
569+ codec : source . codec ,
570+ fonts : Array . isArray ( source . fonts ) ? source . fonts : [ ] ,
571+ url : source . url ,
572+ } ) ;
573+
574+ cuesByTime = null ;
575+ usingASSRenderer = true ;
576+ setASSSubtitlesStylingActive ( false ) ;
577+ subtitlesElement . style . display = 'none' ;
578+ while ( subtitlesElement . hasChildNodes ( ) ) {
579+ subtitlesElement . removeChild ( subtitlesElement . lastChild ) ;
580+ }
581+ lastTimeIndex = null ;
582+ assRenderer . setDelay ( 0 ) ;
583+ assRenderer . setOpacity ( embeddedSubtitlesOpacity ) ;
584+ startRenderLoop ( ) ;
585+
586+ return getSubtitlesData ( assTrack , false )
587+ . then ( function ( text ) {
588+ if ( currentRequestId !== selectionRequestId || selectedEmbeddedASSTrackId !== track . id ) {
589+ return null ;
590+ }
591+
592+ return assRenderer . load ( assTrack , text ) ;
593+ } )
594+ . then ( function ( loadedTrack ) {
595+ if ( currentRequestId !== selectionRequestId || selectedEmbeddedASSTrackId !== track . id ) {
596+ return null ;
597+ }
598+ if ( loadedTrack !== null ) {
599+ setASSSubtitlesStylingActive ( true ) ;
600+ events . emit ( 'subtitlesTrackLoaded' , getPublicSubtitleTrack ( track ) ) ;
601+ }
602+ return loadedTrack ;
603+ } )
604+ . catch ( function ( error ) {
605+ fallbackFromEmbeddedASS ( error , assTrack , currentRequestId ) ;
606+ return null ;
607+ } ) ;
608+ }
506609 function loadASSSubtitles ( track , text , currentRequestId ) {
507610 cuesByTime = null ;
508611 usingASSRenderer = true ;
@@ -692,6 +795,9 @@ function withHTMLSubtitles(Video) {
692795 return videoPropValue ;
693796 }
694797 case 'selectedSubtitlesTrackId' : {
798+ if ( selectedEmbeddedASSTrackId !== null ) {
799+ return selectedEmbeddedASSTrackId ;
800+ }
695801 if ( isNativeASSSelection ( videoPropValue ) ) {
696802 return null ;
697803 }
@@ -739,10 +845,44 @@ function withHTMLSubtitles(Video) {
739845 }
740846 function setProp ( propName , propValue ) {
741847 switch ( propName ) {
848+ case 'selectedSubtitlesTrackId' : {
849+ var selectedVideoTrack = videoSubtitleTracks . find ( function ( track ) {
850+ return track . id === propValue ;
851+ } ) ;
852+ var embeddedASSSource = getEmbeddedASSSource ( selectedVideoTrack ) ;
853+ if ( assSubtitlesStylingEnabled && selectedVideoTrack && embeddedASSSource !== null ) {
854+ if ( selectedEmbeddedASSTrackId === propValue ) {
855+ return true ;
856+ }
857+ if ( selectedTrackId !== null ) {
858+ setProp ( 'selectedExtraSubtitlesTrackId' , null ) ;
859+ }
860+ var embeddedRequestId = ++ selectionRequestId ;
861+ clearEmbeddedASS ( ) ;
862+ selectedEmbeddedASSTrackId = propValue ;
863+ video . dispatch ( {
864+ type : 'setProp' ,
865+ propName : 'selectedSubtitlesTrackId' ,
866+ propValue : null ,
867+ } ) ;
868+ loadEmbeddedASS ( selectedVideoTrack , embeddedASSSource , embeddedRequestId ) ;
869+ return true ;
870+ }
871+ if ( selectedEmbeddedASSTrackId !== null ) {
872+ selectionRequestId = selectionRequestId + 1 ;
873+ clearEmbeddedASS ( ) ;
874+ }
875+
876+ return false ;
877+ }
742878 case 'selectedExtraSubtitlesTrackId' : {
743879 if ( propValue !== null && selectedTrackId === propValue ) {
744880 return true ;
745881 }
882+ if ( propValue !== null && selectedEmbeddedASSTrackId !== null ) {
883+ selectionRequestId = selectionRequestId + 1 ;
884+ clearEmbeddedASS ( ) ;
885+ }
746886 clearNativeASS ( ) ;
747887 if ( propValue !== null ) {
748888 video . dispatch ( {
@@ -888,6 +1028,16 @@ function withHTMLSubtitles(Video) {
8881028
8891029 return true ;
8901030 }
1031+ case 'subtitlesOpacity' : {
1032+ if ( typeof propValue === 'number' ) {
1033+ embeddedSubtitlesOpacity = Math . min ( Math . max ( propValue / 100 , 0 ) , 1 ) ;
1034+ if ( selectedEmbeddedASSTrackId !== null ) {
1035+ assRenderer . setOpacity ( embeddedSubtitlesOpacity ) ;
1036+ }
1037+ }
1038+
1039+ return false ;
1040+ }
8911041 default : {
8921042 return false ;
8931043 }
@@ -947,6 +1097,10 @@ function withHTMLSubtitles(Video) {
9471097 case 'load' : {
9481098 command ( 'unload' ) ;
9491099 assSubtitlesStylingEnabled = commandArgs . assSubtitlesStyling === true ;
1100+ embeddedASSSources = commandArgs . stream && Array . isArray ( commandArgs . stream . _embeddedASSSources ) ?
1101+ commandArgs . stream . _embeddedASSSources
1102+ :
1103+ [ ] ;
9501104 if ( commandArgs . stream && Array . isArray ( commandArgs . stream . subtitles ) ) {
9511105 command ( 'addExtraSubtitlesTracks' , {
9521106 tracks : commandArgs . stream . subtitles . map ( function ( track ) {
@@ -973,6 +1127,10 @@ function withHTMLSubtitles(Video) {
9731127 tracks = [ ] ;
9741128 selectedTrackId = null ;
9751129 selectedSubtitleText = null ;
1130+ embeddedASSSources = [ ] ;
1131+ videoSubtitleTracks = [ ] ;
1132+ selectedEmbeddedASSTrackId = null ;
1133+ embeddedSubtitlesOpacity = 1 ;
9761134 delay = null ;
9771135 usingASSRenderer = false ;
9781136 setASSSubtitlesStylingActive ( false ) ;
0 commit comments