Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add a chapter selection example to the demo #53

Merged
merged 1 commit into from
Jan 24, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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);
amtins marked this conversation as resolved.
Show resolved Hide resolved
});

// 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);
}
Loading