@@ -27,17 +27,6 @@ type TextTrackDescriptor = {
2727 id : string ;
2828} ;
2929
30- const captionTypes = new Set < String > ( [ "text/vtt" , "text/srt" ] ) ;
31-
32- // A label in raw annotation JSON may be a plain string or a language map.
33- const captionLabel = ( label : any ) : string | undefined => {
34- if ( ! label || typeof label === "string" ) {
35- return label || undefined ;
36- }
37- const values = label [ Object . keys ( label ) [ 0 ] ] ;
38- return Array . isArray ( values ) ? values [ 0 ] : undefined ;
39- } ;
40-
4130type MediaSourceDescriptor = {
4231 label : string ;
4332 type : string ;
@@ -217,23 +206,7 @@ export class MediaElementCenterPanel extends CenterPanel<
217206 }
218207 }
219208
220- // Captions may also be supplied as supplementing annotations on the
221- // canvas (IIIF cookbook recipe 0219).
222- const supplementing = await this . getSupplementingCaptions ( canvas ) ;
223- for ( const caption of supplementing ) {
224- if ( ! subtitles . some ( ( subtitle ) => subtitle . id === caption . id ) ) {
225- subtitles . push ( caption ) ;
226- }
227- }
228-
229209 if ( subtitles . length > 0 ) {
230- // Resolve caption URLs to ones the player's XHR will be able to read.
231- for ( const subtitle of subtitles ) {
232- if ( subtitle . id ) {
233- subtitle . id = await this . resolveCaptionSource ( subtitle . id ) ;
234- }
235- }
236-
237210 // Show captions options popover for better interface feedback
238211 subtitles . unshift ( { id : "none" } ) ;
239212 }
@@ -419,96 +392,6 @@ export class MediaElementCenterPanel extends CenterPanel<
419392 this . extensionHost . publish ( Events . LOAD ) ;
420393 }
421394
422- // Captions/transcriptions supplied as supplementing annotations in the
423- // canvas' annotations pages (IIIF cookbook recipe 0219). Inline
424- // annotation pages are read directly; pages referenced by id alone are
425- // fetched.
426- async getSupplementingCaptions (
427- canvas : Canvas
428- ) : Promise < TextTrackDescriptor [ ] > {
429- const captions : TextTrackDescriptor [ ] = [ ] ;
430- const pages : any [ ] = canvas . getProperty ( "annotations" ) || [ ] ;
431-
432- for ( const page of pages ) {
433- let items : any [ ] = page . items ;
434-
435- if ( ! items && page . id ) {
436- try {
437- const response = await fetch ( page . id ) ;
438- if ( response . ok ) {
439- items = ( await response . json ( ) ) . items ;
440- }
441- } catch {
442- console . warn (
443- `Annotation page ${ page . id } could not be read (CORS headers are required); any captions it contains will be unavailable.`
444- ) ;
445- }
446- }
447-
448- if ( ! items ) {
449- continue ;
450- }
451-
452- for ( const annotation of items ) {
453- const motivations = Array . isArray ( annotation . motivation )
454- ? annotation . motivation
455- : [ annotation . motivation ] ;
456-
457- if ( ! motivations . includes ( "supplementing" ) ) {
458- continue ;
459- }
460-
461- const bodies = Array . isArray ( annotation . body )
462- ? annotation . body
463- : [ annotation . body ] ;
464-
465- for ( const body of bodies ) {
466- if ( body && body . id && captionTypes . has ( body . format ) ) {
467- captions . push ( {
468- id : body . id ,
469- label : captionLabel ( body . label ) ,
470- language : body . language ,
471- } ) ;
472- }
473- }
474- }
475- }
476-
477- return captions ;
478- }
479-
480- // Captions are fetched with XHR by the player, so a cross-origin URL is
481- // only readable when every response hop carries CORS headers. Follow any
482- // CORS-friendly redirect to its final URL, and retry plain-http sources
483- // over https — an http -> https upgrade redirect whose 301 response lacks
484- // CORS headers is blocked by the browser even though its destination is
485- // readable.
486- async resolveCaptionSource ( src : string ) : Promise < string > {
487- const attempts : string [ ] = [ src ] ;
488-
489- if ( src . startsWith ( "http://" ) ) {
490- attempts . push ( src . replace ( / ^ h t t p : \/ \/ / , "https://" ) ) ;
491- }
492-
493- for ( const attempt of attempts ) {
494- try {
495- const response = await fetch ( attempt ) ;
496- if ( response . ok ) {
497- return response . url ;
498- }
499- } catch {
500- // expected when the attempt is blocked by CORS or mixed content;
501- // fall through to the next candidate
502- }
503- }
504-
505- console . warn (
506- `Captions at ${ src } could not be read (CORS headers are required on every response, including redirects); the player will omit this track.`
507- ) ;
508-
509- return src ;
510- }
511-
512395 appendTextTracks ( subtitles : Array < TextTrackDescriptor > ) {
513396 for ( const subtitle of subtitles ) {
514397 this . $media . append (
@@ -546,14 +429,16 @@ export class MediaElementCenterPanel extends CenterPanel<
546429 return typeGroup === "audio" || typeGroup === "video" ;
547430 }
548431
549- // vtt, srt
432+ // vtt, srt, csv
550433 isTypeCaption ( element : Rendering | AnnotationBody ) {
551434 const type : RenderingFormat | MediaType | null = element . getFormat ( ) ;
552435
553436 if ( type === null ) {
554437 return false ;
555438 }
556439
440+ const captionTypes = new Set < String > ( [ "text/vtt" , "text/srt" ] ) ;
441+
557442 return captionTypes . has ( type . toString ( ) ) ;
558443 }
559444
0 commit comments