Skip to content
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
1 change: 1 addition & 0 deletions docs/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ <h3 class="mt-4">Some code examples:</h3>
<li><a href="./seek-thumbs.html">Seek Thumbnails</a></li>
<li><a href="./share-plugin.html">Share &amp; Download</a></li>
<li><a href="./shoppable.html">Shoppable Videos</a></li>
<li><a href="./source-switcher.html">Source switcher</a></li>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we also add an ESM example page? I'm ok with not having everything doubled, just that this is what we did with other examples

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we dont have yet E2E test for this page (just mocked for now) but I believe ESM should be added only if there is a reason for that (otherwise we are doubling everything), for now I will keep just one page but we need to decide about some rules here (will schedule meeting for us after gathering)

<li><a href="./subtitles-and-captions.html">Subtitles & Captions</a></li>
<li><a href="./transformations.html">Video Transformations</a></li>
<li><a href="./vast-vpaid.html">VAST & VPAID Support</a></li>
Expand Down
143 changes: 143 additions & 0 deletions docs/source-switcher.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Cloudinary Video Player</title>
<link href="https://res.cloudinary.com/cloudinary-marketing/image/upload/f_auto,q_auto/c_scale,w_32/v1597183771/creative_staging/cloudinary_internal/Website/Brand%20Updates/Favicon/cloudinary_web_favicon_192x192.png" rel="icon" type="image/png">

<!-- Bootstrap -->
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">

<!-- highlight.js -->
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/styles/solarized-light.min.css">
<script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/highlight.min.js"></script>
<script>hljs.initHighlightingOnLoad();</script>

<!--
We're loading scripts & style dynamically for development/testing.
Real-world usage would look like this:

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/cloudinary-video-player/dist/cld-video-player.min.css">
<script src="https://cdn.jsdelivr.net/npm/cloudinary-video-player/dist/cld-video-player.min.js"></script>
-->

<script type="text/javascript" src="./scripts.js"></script>

<script type="text/javascript">
window.addEventListener('load', function() {
cloudinary.videoPlayer('player-multiple-sources', {
cloudName: 'demo',
videoSources: [
{
publicId: 'snow_horses'
},
{
publicId: 'dirt_bike',
textTracks: {
captions: {
label: 'Original',
default: true,
},
subtitles: [
{
label: 'English',
language: 'en-US',
},
{
label: 'Polish',
language: 'pl-PL',
},
]
},
},
{
publicId: 'marmots',
label: 'Custom video name',
download: true,
},
],
});
}, false);
</script>

</head>
<body>
<div class="container p-4 col-12 col-md-9 col-xl-6">
<nav class="nav mb-2">
<a href="./index.html">&#60;&#60; Back to examples index</a>
</nav>

<h1>Cloudinary Video Player</h1>

<h3 class="mb-4">Source switcher</h3>

<div class="d-flex flex-column justify-content-start align-items-start">
<video
playsinline
id="player-multiple-sources"
controls
autoplay
class="cld-video-player cld-fluid"
></video>
</div>

<p class="mt-4">
<a href="https://cloudinary.com/documentation/cloudinary_video_player">Full documentation</a>
</p>

<h3 class="mt-4">Example Code:</h3>

<pre>
<code class="language-html">

&lt;video
id="player-multiple-sources"
controls
autoplay
class="cld-video-player"
width="500"&gt;
&lt;/video&gt;

</code>
</pre>

<pre>
<code class="language-javascript">
cloudinary.videoPlayer('player-multiple-sources', {
cloudName: 'demo',
videoSources: [
{
publicId: 'snow_horses'
},
{
publicId: 'dirt_bike',
textTracks: {
captions: {
label: 'Original',
default: true,
},
subtitles: [
{
label: 'English',
language: 'en-US',
},
{
label: 'Polish',
language: 'pl-PL',
},
]
},
},
{
publicId: 'marmots',
label: 'Custom video name',
download: true,
},
],
});
</code>
</pre>
</div>

</body>
</html>
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@
},
{
"path": "./lib/all.js",
"maxSize": "320kb"
"maxSize": "325kb"
}
]
},
Expand Down
4 changes: 4 additions & 0 deletions src/assets/icons/source_switcher_icon_for_black_bg.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions src/assets/icons/source_switcher_icon_for_white_bg.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 3 additions & 1 deletion src/components/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ import JumpBackButton from './jumpButtons/jump-10-minus';
import LogoButton from './logoButton/logo-button';
import ProgressControlEventsBlocker from './progress-control-events-blocker/progress-control-events-blocker';
import TitleBar from './title-bar/title-bar';
import SourceSwitcherButton from './source-switcher-button/source-switcher-button';

export {
JumpForwardButton,
JumpBackButton,
LogoButton,
ProgressControlEventsBlocker,
TitleBar
TitleBar,
SourceSwitcherButton
};
125 changes: 125 additions & 0 deletions src/components/source-switcher-button/source-switcher-button.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import videojs from 'video.js';
import './source-switcher-button.scss';

const MenuButton = videojs.getComponent('MenuButton');
const MenuItem = videojs.getComponent('MenuItem');

class SourceSwitcherButton extends MenuButton {
constructor(player, options = {}) {
super(player, options);

this.controlText(options.tooltip || 'Sources');
this._emptyLabel = options.emptyLabel || 'No sources';

this._items = options.items || [];
this._selectedIndex = Number.isInteger(options.defaultIndex) ? options.defaultIndex : undefined;

const onSelected = typeof options.onSelected === 'function' ? options.onSelected : null;

this._onSelected = onSelected || null;
this._setEnabled(Array.isArray(this._items) && this._items.length > 0);
}

buildCSSClass() {
const empty = !Array.isArray(this._items) || this._items.length === 0;
return `vjs-source-switcher-button${empty ? ' vjs-source-switcher-disabled' : ''} ${super.buildCSSClass()}`;
}

createItems() {
if (!Array.isArray(this._items) || this._items.length === 0) {
const empty = new MenuItem(this.player_, {
label: this._emptyLabel || 'No sources',
selectable: false
});
empty.addClass('vjs-source-switcher-empty');
empty.disable();
return [empty];
Comment on lines +30 to +36
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we have an "empty" state?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

initially this component is mounted without any source, just to avoid complex implementation and just after that it gets list of sources provided as player params (so its the part of player, it should never be visible but just in case empty state is added rather than nothing)

}

return this._items.map(({ label, value }, index) => {
const item = new MenuItem(this.player_, {
label,
selectable: true,
selected: index === this._selectedIndex
});
item.value = value;
item._ssIndex = index;

item.on('click', () => {
this.menu.children().forEach((child) => {
if (child instanceof MenuItem) {
child.selected(child._ssIndex === index);
}
});

this._selectedIndex = index;
const payload = { index, value, label };
if (this._onSelected) this._onSelected(payload, this.player_);
this.player_.trigger('sourceswitcher:change', payload);
});

return item;
});
}

setItems(items) {
this._items = items;
this._selectedIndex = this._items.length ? 0 : undefined;

this._setEnabled(this._items.length > 0);
this._rebuildMenu();
}

setSelected(index) {
if (
!Array.isArray(this._items) ||
index == null ||
index < 0 ||
index >= this._items.length
) return;

this._selectedIndex = index;

// reflect in UI if menu exists
if (this.menu) {
this.menu.children().forEach((child) => {
if (child instanceof MenuItem) {
child.selected(child._ssIndex === index);
}
});
}

const { label, value } = this._items[index];
const payload = { index, value, label };
if (this._onSelected) this._onSelected(payload, this.player_);
this.player_.trigger('sourceswitcher:change', payload);
}

setOnSelected(fn) {
this._onSelected = typeof fn === 'function' ? fn : null;
}

_rebuildMenu() {
if (!this.menu) return;
this.menu.children().slice().forEach((c) => this.menu.removeChild(c));
this.createItems().forEach((i) => this.menu.addItem(i));

// Toggle disabled class based on emptiness
const el = this.el();
if (el) {
const empty = this._items.length === 0;
el.classList.toggle('vjs-source-switcher-disabled', empty);
el.setAttribute('aria-disabled', String(empty));
}
}

_setEnabled(enabled) {
const el = this.el();
if (!el) return;
el.classList.toggle('vjs-source-switcher-disabled', !enabled);
el.setAttribute('aria-disabled', String(!enabled));
}
}

videojs.registerComponent('sourceSwitcherButton', SourceSwitcherButton);
export default SourceSwitcherButton;
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
.vjs-control-bar .vjs-menu-button.vjs-source-switcher-button {
background-image: url("../../assets/icons/source_switcher_icon_for_black_bg.svg");
background-size: 25px;
background-position: center;
background-repeat: no-repeat;
color: inherit;
opacity: 0.9;

.cld-video-player-skin-light & {
background-image: url("../../assets/icons/source_switcher_icon_for_white_bg.svg");
}

&:hover {
cursor: pointer;
opacity: 1;
}
}
Loading