Skip to content

Commit

Permalink
feat: add a chapter selection example to the demo
Browse files Browse the repository at this point in the history
Resolves #52 by providing a code snippet to inspire a developer to create their
own chapter selection UI.

- add chapter selection to showcase page
- add html page and css containing chapter selection
  • Loading branch information
amtins committed Jan 24, 2025
1 parent 0e79cfc commit 9bd2bae
Show file tree
Hide file tree
Showing 3 changed files with 203 additions and 0 deletions.
21 changes: 21 additions & 0 deletions src/layout/content/showcase/showcase-page.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import rawPlaylistExample from '../../../../static/showcases/playlist.html?raw';
import rawqualityMenuExample from '../../../../static/showcases/quality-menu.html?raw';
import rawCountdown from '../../../../static/showcases/countdown.html?raw';
import rawPlaybackRate from '../../../../static/showcases/playback-rate.html?raw';
import rawChapterSelection from '../../../../static/showcases/chapter-selection.html?raw';
import { getTextFromHTML } from './example-parser.js';

const startTimeExampleTxt = getTextFromHTML(rawStartTimeExample);
Expand All @@ -26,6 +27,7 @@ const playlistExampleTxt = getTextFromHTML(rawPlaylistExample);
const qualityMenuExampleTxt = getTextFromHTML(rawqualityMenuExample);
const countdownExampleTxt = getTextFromHTML(rawCountdown);
const playbackRateExampleTxt = getTextFromHTML(rawPlaybackRate);
const chapterSelectionExampleTxt = getTextFromHTML(rawChapterSelection);

export class ShowCasePage extends LitElement {
static styles = [theme, animations, unsafeCSS(showcasePageCss)];
Expand All @@ -41,6 +43,7 @@ export class ShowCasePage extends LitElement {
${this.#renderQualityMenu()}
${this.#renderCountdown()}
${this.#renderPlaybackRate()}
${this.#renderChapterSelection()}
`;
}

Expand Down Expand Up @@ -223,6 +226,24 @@ export class ShowCasePage extends LitElement {
</div>
`;
}

#renderChapterSelection() {
return html`
<div class="fade-in"
@animationend="${e => e.target.classList.remove('fade-in')}">
<showcase-component href="chapter-selection.html">
<h2 slot="title">Chapter Selection</h2>
<p slot="description">
In this showcase, we'll demonstrate how to display a chapter selector.
</p>
<code-block slot="code" language="javascript">${chapterSelectionExampleTxt}</code-block>
</showcase-component>
<a part="showcase-link" href="./static/showcases/chapter-selection.html" target="_blank">
Open this showcase
</a>
</div>
`;
}
}

customElements.define('showcase-page', ShowCasePage);
Expand Down
129 changes: 129 additions & 0 deletions static/showcases/chapter-selection.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>Pillarbox Demo - Chapter Selection</title>
<link rel="icon" type="image/x-icon" href="../../img/favicon.ico">
<link rel="stylesheet" href="./chapter-selection.scss"/>
</head>

<body>
<core-demo-header></core-demo-header>
<div class="showcase-content">
<h2>Chapter Selection</h2>
<div class="video-container">
<video-js id="video-element-id" class="pillarbox-js" controls></video-js>
</div>

<button class="showcase-btn" id="close-btn">Close this window</button>
</div>

<script type="module" data-implementation>
// Import the pillarbox library
import pillarbox from '@srgssr/pillarbox-web';

// Create a pillarbox player instance with the currentChapter plugin
window.player = pillarbox(
'video-element-id',
{muted: true}
);

// Listen for a chapter change
player.on('srgssr/chapter', ({ data = { text : null} }) => {
const currentChapter = JSON.parse(data.text);

// Select the current chapter
document.querySelectorAll('.chapter').forEach(chapter => {
const isChapterSelected = currentChapter && currentChapter.urn === chapter.dataset.urn;
chapter.classList.toggle('chapter-selected', isChapterSelected);
});
});

// Listens for the loadeddata event to build the chapter selector
player.on('loadeddata', () => {
// Remove the chapter selector element each time a new media is loaded
if(document.getElementById('chapter-selector')){
document.getElementById('chapter-selector').remove();
}

// Get each cue from the srgssr-chapters text track
const chapters = Array.from(player.textTracks().getTrackById('srgssr-chapters').cues);

if(!chapters) return;

// Create the chapter selector element
const chapterSelector = document.createElement('div');

chapterSelector.id ="chapter-selector";

// Create the chapter element
chapters.forEach(({startTime, text}, i) => {
// Extract the data used to create the chapter UI
const {
duration,
imageUrl,
imageTitle,
mediaType,
title,
urn,
vendor
} = JSON.parse(text);
const chapterId = `${'chapter-' + i}`;
const chapter = document.createElement('a');

chapter.dataset.urn = urn;
chapter.className = 'chapter';
chapter.setAttribute('aria-labelledby', chapterId);
// Create the Play URL for the chapter
chapter.href = `https://www.${vendor.toLowerCase()}.ch/play/tv/-/${mediaType.toLowerCase()}/-?urn=${urn}`;
// The UI to display the chapter
chapter.innerHTML = /*html*/`
<figure>
<img
src="${imageUrl}"
alt="${imageTitle}"
loading="lazy"
/>
<figcaption id="${chapterId}">
<p class="title">${title}</p>
<span aria-hidden="true" class="duration">${pillarbox.time.formatTime(duration/1_000, 600)}</span>
</figcaption>
</figure>
`;

// Handle the click on a chapter
chapter.addEventListener('click', (e) => {
e.preventDefault();

// Add a small tolerance to avoid selecting the previous chapter when
// endTime matches the selected chapter’s startTime
player.currentTime(startTime + 0.1);
});

// Append the chapter to the chapter selector element
chapterSelector.append(chapter);
});

// Append the chapter selector element to the document body
document.body.append(chapterSelector);
});

// Set the video source for the player
player.src({ src: 'urn:rts:video:10894383', type: 'srgssr/urn' });
</script>

<script type="module">
import pillarbox from '@srgssr/pillarbox-web';
import '../../src/layout/header/core-demo-header-component.js';

document.querySelector('#close-btn').addEventListener('click', () => {
window.close();
});

window.pillarbox = pillarbox;
</script>

</body>
</html>
53 changes: 53 additions & 0 deletions static/showcases/chapter-selection.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
@import './static-showcase';

#chapter-selector {
display: grid;
gap: var(--size-2);
}

.chapter {
display: inline-block;
padding: var(--size-3);
color: var(--color-4);
text-decoration: none;
background: var(--color-10);
border-radius: var(--size-2);
}

.chapter-selected {
background: var(--red-11);
}

.chapter:hover:not(.chapter-selected) {
background: var(--color-9);
}

.chapter figure {
display: grid;
grid-template-columns: var(--size-12) 1fr;
gap: var(--size-5);
margin: 0;
}

.chapter figcaption {
display: grid;
}

.chapter img {
width: 100%;
border-radius: var(--size-2);
}

.chapter p {
margin: 0;
}

.chapter span {
justify-self: end;
float: right;
margin-top: var(--size-4);
padding: var(--size-1);
background-color: var(--color-8);
border-color: var(--color-9);
border-radius: var(--radius-2);
}

0 comments on commit 9bd2bae

Please sign in to comment.