Skip to content

Commit 9722752

Browse files
author
Terrill
committed
Add chapters to automatic transcript
1 parent 2ca0613 commit 9722752

17 files changed

Lines changed: 584 additions & 143 deletions

‎build/ableplayer.dist.js‎

Lines changed: 176 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2761,7 +2761,7 @@
27612761
this.$transcriptToolbar.append($('<label for="autoscroll-transcript-checkbox">' + this.tt.autoScroll + ': </label>'), this.$autoScrollTranscriptCheckbox);
27622762

27632763
// Add field for selecting a transcript language
2764-
// This will be deleted in FUCK if there are no languages
2764+
// This will be deleted in initialize.js > recreatePlayer() if there are no languages
27652765
this.$transcriptLanguageSelect = $('<select id="transcript-language-select">');
27662766
// Add a default "Unknown" option; this will be deleted later if there are any
27672767
// elements with a language.
@@ -2984,7 +2984,16 @@
29842984
});
29852985

29862986
this.$transcriptLanguageSelect.change(function () {
2987+
29872988
var language = thisObj.$transcriptLanguageSelect.val();
2989+
2990+
// set language of all content (chapters, captions & descriptions)
2991+
// to match selection (if languages are avaialable)
2992+
for (var ii in thisObj.chapters) {
2993+
if (thisObj.chapters[ii].language === language) {
2994+
thisObj.transcriptChapters = thisObj.chapters[ii];
2995+
}
2996+
}
29882997
for (var ii in thisObj.captions) {
29892998
if (thisObj.captions[ii].language === language) {
29902999
thisObj.transcriptCaptions = thisObj.captions[ii];
@@ -4136,7 +4145,11 @@
41364145
// called via setupTracks() only if there is track with kind="descriptions"
41374146
// prepares for delivery of text description , in case it's needed
41384147
// whether and how it's delivered is controlled within description.js > initDescription()
4139-
var trackLang = track.getAttribute('srclang');
4148+
4149+
// srcLang should always be included with <track>, but HTML5 spec doesn't require it
4150+
// if not provided, assume track is the same language as the default player language
4151+
var trackLang = track.getAttribute('srclang') || this.lang;
4152+
41404153
this.hasClosedDesc = true;
41414154
this.currentDescription = -1;
41424155
this.descriptions.push({
@@ -4146,11 +4159,29 @@
41464159
};
41474160

41484161
AblePlayer.prototype.setupChapters = function (track, cues) {
4162+
41494163
// NOTE: WebVTT supports nested timestamps (to form an outline)
41504164
// This is not currently supported.
4151-
var i=0;
4165+
4166+
// srcLang should always be included with <track>, but HTML5 spec doesn't require it
4167+
// if not provided, assume track is the same language as the default player language
4168+
var trackLang = track.getAttribute('srclang') || this.lang;
4169+
41524170
this.hasChapters = true;
4171+
4172+
// TODO: Add support for multiple languages of chapters
4173+
// Replace the following line with the commented block that follows
4174+
// Haven't done this because it will have a big effect downstream
4175+
// on all chapter processing
41534176
this.chapters = cues;
4177+
4178+
/* // new
4179+
this.chapters.push({
4180+
cues: cues,
4181+
language: trackLang
4182+
});
4183+
*/
4184+
41544185
if (this.chaptersDivLocation) {
41554186
this.populateChaptersDiv();
41564187
}
@@ -7294,6 +7325,7 @@
72947325

72957326
// Takes a cue and returns the caption text to display for it.
72967327
AblePlayer.prototype.flattenCueForCaption = function (cue) {
7328+
72977329
var result = [];
72987330

72997331
var flattenComponent = function (component) {
@@ -7318,7 +7350,6 @@
73187350
for (var ii in cue.components.children) {
73197351
result.push(flattenComponent(cue.components.children[ii]));
73207352
}
7321-
73227353
return result.join('');
73237354
};
73247355

@@ -7704,9 +7735,12 @@
77047735
return;
77057736
}
77067737
// Update transcript.
7738+
var chapters;
77077739
var captions;
77087740
var descriptions;
77097741
var captionLang;
7742+
7743+
// setup captions
77107744
if (this.transcriptCaptions) {
77117745
// use this independently of this.selectedCaptions
77127746
// user might want captions in one language, transcript in another
@@ -7717,6 +7751,26 @@
77177751
captionLang = this.captionLang;
77187752
captions = this.selectedCaptions.cues;
77197753
}
7754+
7755+
// setup chapters
7756+
if (this.transcriptChapters) {
7757+
chapters = this.transcriptChapters;
7758+
}
7759+
else if (this.chapters.length > 0) {
7760+
// Try and match the caption language.
7761+
if (captionLang) {
7762+
for (var ii in this.chapters) {
7763+
if (this.chapters[ii].language === captionLang) {
7764+
chapters = this.chapters[ii];
7765+
}
7766+
}
7767+
}
7768+
if (typeof chapters === 'undefined') {
7769+
chapters = this.chapters;
7770+
}
7771+
}
7772+
7773+
// setup descriptions
77207774
if (this.transcriptDescriptions) {
77217775
descriptions = this.transcriptDescriptions.cues;
77227776
}
@@ -7733,7 +7787,8 @@
77337787
descriptions = this.descriptions[0].cues;
77347788
}
77357789
}
7736-
var div = this.generateTranscript(captions || [], descriptions || []);
7790+
7791+
var div = this.generateTranscript(chapters || [], captions || [], descriptions || []);
77377792
this.$transcriptDiv.html(div);
77387793

77397794
var thisObj = this;
@@ -7786,39 +7841,99 @@
77867841
}
77877842
};
77887843

7789-
AblePlayer.prototype.generateTranscript = function(captions, descriptions) {
7790-
var thisObj = this;
7844+
AblePlayer.prototype.generateTranscript = function(chapters, captions, descriptions) {
77917845

7792-
var main = $('<div class="able-transcript-container"></div>');
7846+
var thisObj = this;
77937847

7794-
// TODO: Make scrolling optional?
7848+
var $main = $('<div class="able-transcript-container"></div>');
77957849

7796-
var transcriptTitle = 'Transcript';
7850+
var transcriptTitle = this.tt.prefMenuTranscript;
77977851
if (typeof this.transcriptTitle !== 'undefined') {
77987852
transcriptTitle = this.transcriptTitle;
77997853
}
78007854
else if (this.lyricsMode) {
7801-
transcriptTitle = 'Lyrics';
7855+
transcriptTitle = 'Lyrics'; // TODO: Localize this
78027856
}
78037857

78047858
if (typeof this.transcriptDivLocation === 'undefined' && transcriptTitle != '') {
78057859
// only add an HTML heading to internal transcript
78067860
// external transcript is expected to have its own heading
78077861
var headingNumber = this.playerHeadingLevel;
78087862
headingNumber += 1;
7863+
var chapterHeadingNumber = headingNumber + 1;
7864+
7865+
if (headingNumber <= 6) {
7866+
var transcriptHeading = 'h' + headingNumber.toString();
7867+
}
7868+
else {
7869+
var transcriptHeading = 'div';
7870+
}
7871+
// var transcriptHeadingTag = '<' + transcriptHeading + ' class="able-transcript-heading">';
7872+
var $transcriptHeadingTag = $('<' + transcriptHeading + '>');
7873+
$transcriptHeadingTag.addClass('able-transcript-heading');
78097874
if (headingNumber > 6) {
7810-
headingNumber = 6;
7875+
$transcriptHeadingTag.attr({
7876+
'role': 'heading',
7877+
'aria-level': headingNumber
7878+
});
78117879
}
7812-
var transcriptHeading = 'h' + headingNumber.toString();
7813-
var transcriptHeadingTag = '<' + transcriptHeading + ' class="able-transcript-heading">';
7814-
transcriptHeadingTag += transcriptTitle;
7815-
transcriptHeadingTag += '</' + transcriptHeading + '>';
7816-
main.append(transcriptHeadingTag);
7880+
$transcriptHeadingTag.text(transcriptTitle);
7881+
7882+
$main.append($transcriptHeadingTag);
78177883
}
78187884

7885+
var nextChapter = 0;
78197886
var nextCap = 0;
78207887
var nextDesc = 0;
78217888

7889+
var addChapter = function(div, chap) {
7890+
7891+
if (chapterHeadingNumber <= 6) {
7892+
var chapterHeading = 'h' + chapterHeadingNumber.toString();
7893+
}
7894+
else {
7895+
var chapterHeading = 'div';
7896+
}
7897+
7898+
var $chapterHeadingTag = $('<' + chapterHeading + '>',{
7899+
'class': 'able-transcript-chapter-heading'
7900+
});
7901+
if (chapterHeadingNumber > 6) {
7902+
$chapterHeadingTag.attr({
7903+
'role': 'heading',
7904+
'aria-level': chapterHeadingNumber
7905+
});
7906+
}
7907+
7908+
var flattenComponentForChapter = function(comp) {
7909+
var result = [];
7910+
if (comp.type === 'string') {
7911+
result.push(comp.value);
7912+
}
7913+
else {
7914+
for (var ii in comp.children) {
7915+
result = result.concat(flattenComponentForChapter(comp.children[ii]));
7916+
}
7917+
}
7918+
return result;
7919+
}
7920+
7921+
var $chapSpan = $('<span>',{
7922+
'class': 'able-transcript-seekpoint'
7923+
});
7924+
for (var ii in chap.components.children) {
7925+
var results = flattenComponentForChapter(chap.components.children[ii]);
7926+
for (var jj in results) {
7927+
$chapSpan.append(results[jj]);
7928+
}
7929+
}
7930+
$chapSpan.attr('data-start', chap.start.toString());
7931+
$chapSpan.attr('data-end', chap.end.toString());
7932+
$chapterHeadingTag.append($chapSpan);
7933+
7934+
div.append($chapterHeadingTag);
7935+
};
7936+
78227937
var addDescription = function(div, desc) {
78237938
var descDiv = $('<div class="able-desc"><span class="hidden">Description: </span></div>');
78247939

@@ -7922,28 +8037,59 @@
79228037
div.append('\n');
79238038
};
79248039

7925-
while ((nextCap < captions.length) || (nextDesc < descriptions.length)) {
7926-
if ((nextCap < captions.length) && (nextDesc < descriptions.length)) {
7927-
if (descriptions[nextDesc].start <= captions[nextCap].start) {
7928-
addDescription(main, descriptions[nextDesc]);
8040+
// keep looping as long as any one of the three arrays has content
8041+
while ((nextChapter < chapters.length) || (nextDesc < descriptions.length) || (nextCap < captions.length)) {
8042+
8043+
if ((nextChapter < chapters.length) && (nextDesc < descriptions.length) && (nextCap < captions.length)) {
8044+
// they all three have content
8045+
var firstStart = Math.min(chapters[nextChapter].start,descriptions[nextDesc].start,captions[nextCap].start);
8046+
}
8047+
else if ((nextChapter < chapters.length) && (nextDesc < descriptions.length)) {
8048+
// chapters & descriptions have content
8049+
var firstStart = Math.min(chapters[nextChapter].start,descriptions[nextDesc].start);
8050+
}
8051+
else if ((nextChapter < chapters.length) && (nextCap < captions.length)) {
8052+
// chapters & captions have content
8053+
var firstStart = Math.min(chapters[nextChapter].start,captions[nextCap].start);
8054+
}
8055+
else if ((nextDesc < descriptions.length) && (nextCap < captions.length)) {
8056+
// descriptions & captions have content
8057+
var firstStart = Math.min(descriptions[nextDesc].start,captions[nextCap].start);
8058+
}
8059+
else {
8060+
var firstStart = null;
8061+
}
8062+
if (firstStart !== null) {
8063+
if (typeof chapters[nextChapter] !== 'undefined' && chapters[nextChapter].start === firstStart) {
8064+
addChapter($main, chapters[nextChapter]);
8065+
nextChapter += 1;
8066+
}
8067+
else if (typeof descriptions[nextDesc] !== 'undefined' && descriptions[nextDesc].start === firstStart) {
8068+
addDescription($main, descriptions[nextDesc]);
79298069
nextDesc += 1;
79308070
}
79318071
else {
7932-
addCaption(main, captions[nextCap]);
8072+
addCaption($main, captions[nextCap]);
79338073
nextCap += 1;
79348074
}
79358075
}
7936-
else if (nextCap < captions.length) {
7937-
addCaption(main, captions[nextCap]);
7938-
nextCap += 1;
7939-
}
7940-
else if (nextDesc < descriptions.length) {
7941-
addDescription(main, descriptions[nextDesc]);
7942-
nextDesc += 1;
8076+
else {
8077+
if (nextChapter < chapters.length) {
8078+
addCaption($main, chapters[nextChapter]);
8079+
nextChapter += 1;
8080+
}
8081+
else if (nextDesc < descriptions.length) {
8082+
addDescription($main, descriptions[nextDesc]);
8083+
nextDesc += 1;
8084+
}
8085+
else if (nextCap < captions.length) {
8086+
addCaption($main, captions[nextCap]);
8087+
nextCap += 1;
8088+
}
79438089
}
79448090
}
79458091

7946-
return main;
8092+
return $main;
79478093
};
79488094
})(jQuery);
79498095

0 commit comments

Comments
 (0)