Skip to content

Commit

Permalink
feat(UI): Add chapter titles and dividers on the seek bar (#5863)
Browse files Browse the repository at this point in the history
Closes #3597
  • Loading branch information
avelad committed Nov 8, 2023
1 parent f7448b7 commit c1198df
Show file tree
Hide file tree
Showing 9 changed files with 304 additions and 6 deletions.
11 changes: 10 additions & 1 deletion demo/common/assets.js
Expand Up @@ -149,6 +149,9 @@ shakaAssets.Feature = {
// Set if the asset has at least one image stream.
THUMBNAILS: 'Thumbnails',

// Set if the asset has at least one chapter stream.
CHAPTERS: 'Chapters',

// Set if the asset has LCEVC.
LCEVC: 'LCEVC',

Expand Down Expand Up @@ -401,7 +404,13 @@ shakaAssets.testAssets = [
.addFeature(shakaAssets.Feature.MP4)
.addFeature(shakaAssets.Feature.SUBTITLES)
.addFeature(shakaAssets.Feature.WEBM)
.addFeature(shakaAssets.Feature.OFFLINE),
.addFeature(shakaAssets.Feature.OFFLINE)
.addFeature(shakaAssets.Feature.CHAPTERS)
.addExtraChapter({
uri: 'https://storage.googleapis.com/shaka-demo-assets/sintel-chapters.vtt',
language: 'en',
mime: 'text/vtt',
}),
new ShakaDemoAssetInfo(
/* name= */ 'Sintel w/ trick mode (MP4 only, 720p)',
/* iconUri= */ 'https://storage.googleapis.com/shaka-asset-icons/sintel.png',
Expand Down
2 changes: 2 additions & 0 deletions demo/search.js
Expand Up @@ -391,6 +391,8 @@ shakaDemo.Search = class {
'Filters for assets that do not have video streams.');
this.makeBooleanInput_(specialContainer, Feature.THUMBNAILS, FEATURE,
'Filters for assets that have a thumbnail track.');
this.makeBooleanInput_(specialContainer, Feature.CHAPTERS, FEATURE,
'Filters for assets that have a chapters track.');
this.makeBooleanInput_(specialContainer, Feature.LCEVC, FEATURE,
'Filters for assets that have an LCEVC enhancement layer.');

Expand Down
13 changes: 13 additions & 0 deletions docs/tutorials/ui-customization.md
Expand Up @@ -188,6 +188,19 @@ const config = {
ui.configure(config);
```

If you've chosen to display chapters, you can specify the color for the chapter markers on
the timeline and the text color of the chapter titles that popup on hover:
```js
const config = {
displayChapters: true,
seekBarColors: {
chapterMarks: 'rgb(27, 27, 27)',
chapterLabels: 'rgb(255, 255, 255)'
}
}
ui.configure(config);
```

#### Configuring playback, fast forward and rewind rates
The rate in which the player can play, fast forward and rewind content can be configured using the `playbackRates`, `fastForwardRates` and `rewindRates` options.

Expand Down
15 changes: 11 additions & 4 deletions lib/player.js
Expand Up @@ -40,6 +40,7 @@ goog.require('shaka.text.WebVttGenerator');
goog.require('shaka.util.BufferUtils');
goog.require('shaka.util.CmcdManager');
goog.require('shaka.util.ConfigUtils');
goog.require('shaka.util.Dom');
goog.require('shaka.util.Error');
goog.require('shaka.util.EventManager');
goog.require('shaka.util.FakeEvent');
Expand Down Expand Up @@ -1216,6 +1217,11 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
this.adManager_.onAssetUnload();
}

if (this.video_) {
// Remove all track nodes
shaka.util.Dom.removeAllChildren(this.video_);
}

// In order to unload a media element, we need to remove the src attribute
// and then load again. When we destroy media source engine, this will be
// done for us, but for src=, we need to do it here.
Expand All @@ -1231,10 +1237,6 @@ shaka.Player = class extends shaka.util.FakeEventTarget {

this.video_.removeAttribute('src');
this.video_.load();
// Remove all track nodes
while (this.video_.lastChild) {
this.video_.removeChild(this.video_.firstChild);
}
}

if (this.drmEngine_) {
Expand Down Expand Up @@ -4183,6 +4185,9 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
* @export
*/
getChapters(language) {
if (!this.video_ || !this.video_.src || !this.video_.textTracks) {
return [];
}
const LanguageUtils = shaka.util.LanguageUtils;
const inputlanguage = LanguageUtils.normalize(language);
const chaptersTracks = this.getChaptersTracks_();
Expand Down Expand Up @@ -4871,6 +4876,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
});
});

this.onTracksChanged_();

return chaptersTrack;
}

Expand Down
11 changes: 10 additions & 1 deletion ui/externs/ui.js
Expand Up @@ -21,7 +21,9 @@ shaka.extern = {};
* base: string,
* buffered: string,
* played: string,
* adBreaks: string
* adBreaks: string,
* chapterMarks: string,
* chapterLabels: string
* }}
*
* @property {string} base
Expand All @@ -36,6 +38,13 @@ shaka.extern = {};
* @property {string} adBreaks
* The CSS background color applied to the portion of the seek bar showing
* when the ad breaks are scheduled to occur on the timeline.
* @property {string} chapterMarks
* The CSS border color applied to sections of the seek bar showing
* when the chapters start and end on the timeline.
* Defaults to dark grey rgb(27, 27, 27).
* @property {string} chapterLabels
* The CSS text color applied to the chapter labels that appear above the
* seek bar on hover. Defaults to white rgb(255, 255, 255).
* @exportDoc
*/
shaka.extern.UISeekBarColors;
Expand Down
33 changes: 33 additions & 0 deletions ui/less/range_elements.less
Expand Up @@ -154,3 +154,36 @@
.shaka-ad-markers {
.overlay-child();
}

#shaka-player-ui-chapters-container {
width: 100%;
height: 100%;
display: flex;
flex-direction: row;
z-index: 0;
}

.shaka-chapter {
height: 100%;
position: relative;
}

.shaka-chapter-label {
position: relative;
top: -2.5em;
width: 100%;
text-align: center;
text-shadow: 1px 1px gray;
line-height: normal;
}

.shaka-chapter > div:first-child {
width: 100%;
height: 100%;
border-right: 1px solid;
position: absolute;
}

.shaka-chapter:last-child > div:first-child {
border-right: none;
}
10 changes: 10 additions & 0 deletions ui/localization.js
Expand Up @@ -230,6 +230,16 @@ shaka.ui.Localization = class extends shaka.util.FakeEventTarget {
return '';
}

/**
* The locales currently used. An empty set means "no preference".
*
* @return {!Set.<string>}
* @export
*/
getCurrentLocales() {
return this.currentLocales_;
}

/**
* @private
*/
Expand Down

0 comments on commit c1198df

Please sign in to comment.