From 9ee71dab5891c32fede3a724f74a420f3ae0235e Mon Sep 17 00:00:00 2001 From: saasfreelancer Date: Thu, 1 Dec 2022 15:19:34 +0500 Subject: [PATCH 01/25] feat(cxl-ui): cxl-jw-player rename jw player component Co-authored-by: Andrew Noblet feat(cxl-ui): tweak jw player component Co-authored-by: Andrew Noblet feat(cxl-ui): rename caption mixin to transcript mixin Co-authored-by: Andrew Noblet feat(cxl-ui): tweak transcript mixin Co-authored-by: Andrew Noblet feat(cxl-ui): rename chapter mixin to chapter navigation mixin Co-authored-by: Andrew Noblet feat(cxl-ui): tweak chapter navigation mixin Co-authored-by: Andrew Noblet feat(cxl-ui): add cxl-jw-player-transcript element Co-authored-by: Andrew Noblet feat: cleanup, refactor & improve the code for PHP `cxl/jw-player` * jw-player height tweaks * [jw-player] add support for sources cherry-pick: #236 * cxl-jw-player set up api endpoint cherry-pick: #234 --- .../cxl-jw-player-chapter-navigation.scss} | 0 .../cxl-jw-player-shadow.scss} | 4 -- .../cxl-jw-player-transcript-shadow.scss | 3 + .../cxl-jw-player-transcript.scss | 3 + .../scss/cxl-jw-player/cxl-jw-player.scss | 8 +++ .../{jw-player => cxl-jw-player}/README.md | 9 +-- .../cxl-jw-player-transcript/index.js | 21 ++++++ .../index.html.js | 2 +- .../{jw-player => cxl-jw-player}/index.js | 23 +++--- .../mixins/BaseMixin.js | 71 +++++++++++++++---- .../mixins/SavePositionMixin.js | 8 ++- .../mixins/TranscriptMixin.js} | 27 +++++-- .../mixins/chapter-navigation}/index.html.js | 3 +- .../mixins/chapter-navigation}/index.js | 18 ++--- .../components/cxl-jw-player/mixins/index.js | 4 ++ .../{jw-player => cxl-jw-player}/utility.js | 0 .../src/components/jw-player/mixins/index.js | 4 -- packages/cxl-ui/src/index-jwplayer.js | 2 +- packages/cxl-ui/src/index-storybook.js | 2 +- .../cxl-ui/src/styles/jw-player/.gitignore | 1 - .../index.stories.js | 23 +++--- .../playlist.stories.js | 14 ++-- 22 files changed, 178 insertions(+), 72 deletions(-) rename packages/cxl-ui/scss/{jw-player/chapter.scss => cxl-jw-player/cxl-jw-player-chapter-navigation.scss} (100%) rename packages/cxl-ui/scss/{jw-player/jw-player.scss => cxl-jw-player/cxl-jw-player-shadow.scss} (94%) create mode 100644 packages/cxl-ui/scss/cxl-jw-player/cxl-jw-player-transcript-shadow.scss create mode 100644 packages/cxl-ui/scss/cxl-jw-player/cxl-jw-player-transcript.scss create mode 100644 packages/cxl-ui/scss/cxl-jw-player/cxl-jw-player.scss rename packages/cxl-ui/src/components/{jw-player => cxl-jw-player}/README.md (96%) create mode 100644 packages/cxl-ui/src/components/cxl-jw-player/cxl-jw-player-transcript/index.js rename packages/cxl-ui/src/components/{jw-player => cxl-jw-player}/index.html.js (96%) rename packages/cxl-ui/src/components/{jw-player => cxl-jw-player}/index.js (50%) rename packages/cxl-ui/src/components/{jw-player => cxl-jw-player}/mixins/BaseMixin.js (54%) rename packages/cxl-ui/src/components/{jw-player => cxl-jw-player}/mixins/SavePositionMixin.js (81%) rename packages/cxl-ui/src/components/{jw-player/mixins/CaptionMixin.js => cxl-jw-player/mixins/TranscriptMixin.js} (78%) rename packages/cxl-ui/src/components/{jw-player/mixins/chapter => cxl-jw-player/mixins/chapter-navigation}/index.html.js (89%) rename packages/cxl-ui/src/components/{jw-player/mixins/chapter => cxl-jw-player/mixins/chapter-navigation}/index.js (53%) create mode 100644 packages/cxl-ui/src/components/cxl-jw-player/mixins/index.js rename packages/cxl-ui/src/components/{jw-player => cxl-jw-player}/utility.js (100%) delete mode 100644 packages/cxl-ui/src/components/jw-player/mixins/index.js delete mode 100644 packages/cxl-ui/src/styles/jw-player/.gitignore rename packages/storybook/cxl-ui/{jw-player => cxl-jw-player}/index.stories.js (55%) rename packages/storybook/cxl-ui/{jw-player => cxl-jw-player}/playlist.stories.js (78%) diff --git a/packages/cxl-ui/scss/jw-player/chapter.scss b/packages/cxl-ui/scss/cxl-jw-player/cxl-jw-player-chapter-navigation.scss similarity index 100% rename from packages/cxl-ui/scss/jw-player/chapter.scss rename to packages/cxl-ui/scss/cxl-jw-player/cxl-jw-player-chapter-navigation.scss diff --git a/packages/cxl-ui/scss/jw-player/jw-player.scss b/packages/cxl-ui/scss/cxl-jw-player/cxl-jw-player-shadow.scss similarity index 94% rename from packages/cxl-ui/scss/jw-player/jw-player.scss rename to packages/cxl-ui/scss/cxl-jw-player/cxl-jw-player-shadow.scss index 5868a2c64..54f4f6c4e 100644 --- a/packages/cxl-ui/scss/jw-player/jw-player.scss +++ b/packages/cxl-ui/scss/cxl-jw-player/cxl-jw-player-shadow.scss @@ -8,10 +8,6 @@ } } -:host([captions]) #container { - grid-template-rows: 1fr max-content 1fr; -} - .captions { h2, span { diff --git a/packages/cxl-ui/scss/cxl-jw-player/cxl-jw-player-transcript-shadow.scss b/packages/cxl-ui/scss/cxl-jw-player/cxl-jw-player-transcript-shadow.scss new file mode 100644 index 000000000..769714c22 --- /dev/null +++ b/packages/cxl-ui/scss/cxl-jw-player/cxl-jw-player-transcript-shadow.scss @@ -0,0 +1,3 @@ +:host(:not([hidden])) { + display: block; +} diff --git a/packages/cxl-ui/scss/cxl-jw-player/cxl-jw-player-transcript.scss b/packages/cxl-ui/scss/cxl-jw-player/cxl-jw-player-transcript.scss new file mode 100644 index 000000000..769714c22 --- /dev/null +++ b/packages/cxl-ui/scss/cxl-jw-player/cxl-jw-player-transcript.scss @@ -0,0 +1,3 @@ +:host(:not([hidden])) { + display: block; +} diff --git a/packages/cxl-ui/scss/cxl-jw-player/cxl-jw-player.scss b/packages/cxl-ui/scss/cxl-jw-player/cxl-jw-player.scss new file mode 100644 index 000000000..3a387c1c1 --- /dev/null +++ b/packages/cxl-ui/scss/cxl-jw-player/cxl-jw-player.scss @@ -0,0 +1,8 @@ +.jw-player-button { + width: 32px; + fill: rgba(255, 255, 255, 0.8); + + &:hover { + fill: rgba(255, 255, 255, 1); + } +} diff --git a/packages/cxl-ui/src/components/jw-player/README.md b/packages/cxl-ui/src/components/cxl-jw-player/README.md similarity index 96% rename from packages/cxl-ui/src/components/jw-player/README.md rename to packages/cxl-ui/src/components/cxl-jw-player/README.md index d8d40eb94..96d3b7815 100644 --- a/packages/cxl-ui/src/components/jw-player/README.md +++ b/packages/cxl-ui/src/components/cxl-jw-player/README.md @@ -1,15 +1,16 @@ -# JW Player +# CXL JW Player ## Usage ``` - + > ``` ## Features: diff --git a/packages/cxl-ui/src/components/cxl-jw-player/cxl-jw-player-transcript/index.js b/packages/cxl-ui/src/components/cxl-jw-player/cxl-jw-player-transcript/index.js new file mode 100644 index 000000000..5bbd17f44 --- /dev/null +++ b/packages/cxl-ui/src/components/cxl-jw-player/cxl-jw-player-transcript/index.js @@ -0,0 +1,21 @@ +import { html, LitElement } from 'lit'; +import { customElement } from 'lit/decorators.js'; +import style from '../../../styles/cxl-jw-player/cxl-jw-player-transcript-css'; +import shadowStyle from '../../../styles/cxl-jw-player/cxl-jw-player-transcript-shadow-css'; + +@customElement('cxl-jw-player-transcript') +export class CXLJWPlayerTranscriptElement extends LitElement { + static get styles() { + return [shadowStyle]; + } + + render() { + return html``; + } + + async __setup() { + await super.__setup(); + + this.__addStyle(style); + } +} diff --git a/packages/cxl-ui/src/components/jw-player/index.html.js b/packages/cxl-ui/src/components/cxl-jw-player/index.html.js similarity index 96% rename from packages/cxl-ui/src/components/jw-player/index.html.js rename to packages/cxl-ui/src/components/cxl-jw-player/index.html.js index fd37a2de1..0199e1eb3 100644 --- a/packages/cxl-ui/src/components/jw-player/index.html.js +++ b/packages/cxl-ui/src/components/cxl-jw-player/index.html.js @@ -5,7 +5,7 @@ import '@vaadin/text-field'; // eslint-disable-next-line func-names export const template = function () { return html` -
+
${this.captions ? html` diff --git a/packages/cxl-ui/src/components/jw-player/index.js b/packages/cxl-ui/src/components/cxl-jw-player/index.js similarity index 50% rename from packages/cxl-ui/src/components/jw-player/index.js rename to packages/cxl-ui/src/components/cxl-jw-player/index.js index c0a80cb51..fd42ca9d0 100644 --- a/packages/cxl-ui/src/components/jw-player/index.js +++ b/packages/cxl-ui/src/components/cxl-jw-player/index.js @@ -1,20 +1,20 @@ import { LitElement } from 'lit'; import { customElement } from 'lit/decorators.js'; -import { BaseMixin, CaptionMixin, ChapterMixin, SavePositionMixin } from './mixins'; -import style from '../../styles/jw-player/jw-player-css'; +import { BaseMixin, TranscriptMixin, ChapterNavigationMixin, SavePositionMixin } from './mixins'; +import style from '../../styles/cxl-jw-player/cxl-jw-player-css'; +import shadowStyle from '../../styles/cxl-jw-player/cxl-jw-player-shadow-css'; import { mixin } from './utility'; import { template } from './index.html'; -@customElement('jw-player') -export class JWPlayerElement extends mixin(LitElement, [ +@customElement('cxl-jw-player') +export class CXLJWPlayerElement extends mixin(LitElement, [ BaseMixin, - CaptionMixin, - ChapterMixin, + TranscriptMixin, + ChapterNavigationMixin, SavePositionMixin, ]) { config = { width: '100%', - height: '100%', playbackRateControls: [0.25, 0.5, 0.75, 1, 1.25, 1.5, 1.75, 2], plugins: { // 'http://192.168.0.101:8080/telemetry-8.20.0.js': {}, @@ -22,13 +22,20 @@ export class JWPlayerElement extends mixin(LitElement, [ skin: { name: 'cxl-institute', }, + stretching: 'uniform', }; static get styles() { - return [style]; + return [shadowStyle]; } render() { return template.bind(this)(); } + + async __setup() { + await super.__setup(); + + this.__addStyle(style); + } } diff --git a/packages/cxl-ui/src/components/jw-player/mixins/BaseMixin.js b/packages/cxl-ui/src/components/cxl-jw-player/mixins/BaseMixin.js similarity index 54% rename from packages/cxl-ui/src/components/jw-player/mixins/BaseMixin.js rename to packages/cxl-ui/src/components/cxl-jw-player/mixins/BaseMixin.js index 5f49c5e83..531233f97 100644 --- a/packages/cxl-ui/src/components/jw-player/mixins/BaseMixin.js +++ b/packages/cxl-ui/src/components/cxl-jw-player/mixins/BaseMixin.js @@ -1,3 +1,4 @@ +import { render } from 'lit'; import { property } from 'lit/decorators.js'; import { throttle } from 'lodash-es'; import { parseSync } from 'subtitle'; @@ -16,10 +17,18 @@ export function BaseMixin(BaseClass) { @property({ attribute: 'media-id', type: String }) mediaId; - @property({ attribute: 'player-id', type: String }) playerId; + @property({ attribute: 'media-source', type: String }) mediaSource; + + @property({ attribute: 'is-public', type: String }) isPublic; + + @property({ attribute: 'library-id', type: String }) libraryId; + + @property({ attribute: 'library-source', type: String }) librarySource; @property({ attribute: 'playlist-id', type: String }) playlistId; + @property({ attribute: 'playlist-source', type: String }) playlistSource; + firstUpdated(_changedProperties) { super.firstUpdated(_changedProperties); @@ -34,33 +43,60 @@ export function BaseMixin(BaseClass) { } get __scriptUrl() { - return `https://content.jwplatform.com/libraries/${this.playerId}.js`; + let scriptUrl; + + if (this.libraryId && this.isPublic) { + scriptUrl = `https://content.jwplatform.com/libraries/${this.libraryId}.js`; + } else if (this.librarySource) { + scriptUrl = this.librarySource; + } else { + return false; + } + + return scriptUrl; + } + + __addStyle(style) { + const el = document.createElement('style'); + render(style, el); + this.appendChild(el); } async __getChapters() { const playlistItem = this.__jwPlayer.getPlaylistItem(); - const { file } = playlistItem.tracks.filter((track) => track.kind === 'chapters')[0]; + const chapters = playlistItem.tracks.filter((track) => track.kind === 'chapters'); + const { file } = chapters.length > 0 ? chapters[0] : ''; const response = await (await fetch(file)).text(); return parseSync(response); } async __getMedia() { - if (!this.mediaId) return false; - - const response = await fetch(`https://cdn.jwplayer.com/v2/media/${this.mediaId}`); - const result = await response.json(); + let response; + + if (this.mediaId && this.isPublic) { + response = await fetch(`https://cdn.jwplayer.com/v2/media/${this.mediaId}`); + } else if (this.mediaSource) { + response = await fetch(this.mediaSource); + } else { + return false; + } - return result; + return response.json(); } async __getPlaylist() { - if (!this.playlistId) return false; - - const response = await fetch(`https://cdn.jwplayer.com/v2/playlists/${this.playlistId}`); - const result = await response.json(); + let response; + + if (this.playlistId) { + response = await fetch(`https://cdn.jwplayer.com/v2/playlists/${this.playlistId}`); + } else if (this.playlistSource) { + response = await fetch(`https://cdn.jwplayer.com/v2/playlists/${this.playlistId}`); + } else { + return false; + } - return result; + return response.json(); } async __loadScript() { @@ -90,6 +126,15 @@ export function BaseMixin(BaseClass) { * Each mixin has the ability to hook onto this method. */ async __setup() { + + // Merge configs from `cxlJWPlayerData`. + if (typeof window.cxlJWPlayerData !== 'undefined') { + // eslint-disable-next-line camelcase + const { media_config } = window.cxlJWPlayerData[this.mediaId]; + // eslint-disable-next-line camelcase + this.config = { ...this.config, ...media_config }; + } + const jwPlayer = await this.__loadScript(); const el = document.createElement('div'); diff --git a/packages/cxl-ui/src/components/jw-player/mixins/SavePositionMixin.js b/packages/cxl-ui/src/components/cxl-jw-player/mixins/SavePositionMixin.js similarity index 81% rename from packages/cxl-ui/src/components/jw-player/mixins/SavePositionMixin.js rename to packages/cxl-ui/src/components/cxl-jw-player/mixins/SavePositionMixin.js index 6ae10b0b6..9ea091ba6 100644 --- a/packages/cxl-ui/src/components/jw-player/mixins/SavePositionMixin.js +++ b/packages/cxl-ui/src/components/cxl-jw-player/mixins/SavePositionMixin.js @@ -1,6 +1,6 @@ export function SavePositionMixin(BaseClass) { class Mixin extends BaseClass { - __endpoint = ''; + __endpoint; __nonce; @@ -9,6 +9,12 @@ export function SavePositionMixin(BaseClass) { async __setup() { await super.__setup(); + this.__endpoint = `${window.ajaxurl}?action=jwplayer_save_position`; + + if ( typeof window.cxl_pum_vars !== 'undefined' ) { + this.__nonce = window.cxl_pum_vars.nonce; + } + this.__loadPosition(); } diff --git a/packages/cxl-ui/src/components/jw-player/mixins/CaptionMixin.js b/packages/cxl-ui/src/components/cxl-jw-player/mixins/TranscriptMixin.js similarity index 78% rename from packages/cxl-ui/src/components/jw-player/mixins/CaptionMixin.js rename to packages/cxl-ui/src/components/cxl-jw-player/mixins/TranscriptMixin.js index fbeb3f765..9ee8d35f2 100644 --- a/packages/cxl-ui/src/components/jw-player/mixins/CaptionMixin.js +++ b/packages/cxl-ui/src/components/cxl-jw-player/mixins/TranscriptMixin.js @@ -3,13 +3,13 @@ import { debounce } from 'lodash-es'; import Mark from 'mark.js'; import { parseSync } from 'subtitle'; -export function CaptionMixin(BaseClass) { +export function TranscriptMixin(BaseClass) { class Mixin extends BaseClass { __debouncedSearch; __mark; - @property({ type: Boolean }) captions = false; + @property({ reflect: true, type: Boolean }) captions = false; @state() __currentCue = 0; @@ -117,12 +117,19 @@ export function CaptionMixin(BaseClass) { async __setup() { await super.__setup(); - this.__setupCaptions(); + this.__setupTranscript(); } - async __setupCaptions() { + async __setupTranscript() { if (!this.__jwPlayer) return; + this.__jwPlayer.addButton( + ``, + 'Transcript', + this.__toggleTranscript.bind(this), + 'toggle-transcript' + ); + if (this.captions) { this.__tracks = await this.__getTracks(); @@ -138,16 +145,26 @@ export function CaptionMixin(BaseClass) { if (changedProperties.has('captions')) { if (this.captions) { - this.__setupCaptions(); + this.__setupTranscript(); } else if (this.mark) { this.__mark.unmark(); } } } + __attachListeners() { + super.__attachListeners(); + } + __toggleShouldScroll() { this.shouldScroll = !this.shouldScroll; } + + __toggleTranscript() { + // this.dispatchEvent(new CustomEvent('toggle-transcript')); + + this.captions = !this.captions; + } } return Mixin; diff --git a/packages/cxl-ui/src/components/jw-player/mixins/chapter/index.html.js b/packages/cxl-ui/src/components/cxl-jw-player/mixins/chapter-navigation/index.html.js similarity index 89% rename from packages/cxl-ui/src/components/jw-player/mixins/chapter/index.html.js rename to packages/cxl-ui/src/components/cxl-jw-player/mixins/chapter-navigation/index.html.js index 88b64537d..6c1cb1f4d 100644 --- a/packages/cxl-ui/src/components/jw-player/mixins/chapter/index.html.js +++ b/packages/cxl-ui/src/components/cxl-jw-player/mixins/chapter-navigation/index.html.js @@ -13,8 +13,7 @@ export const chapterNavigationTemplate = function (chapters) { theme="icon small primary" aria-label="Close" > - - ✕ +
    diff --git a/packages/cxl-ui/src/components/jw-player/mixins/chapter/index.js b/packages/cxl-ui/src/components/cxl-jw-player/mixins/chapter-navigation/index.js similarity index 53% rename from packages/cxl-ui/src/components/jw-player/mixins/chapter/index.js rename to packages/cxl-ui/src/components/cxl-jw-player/mixins/chapter-navigation/index.js index aa386cd9e..2ab998723 100644 --- a/packages/cxl-ui/src/components/jw-player/mixins/chapter/index.js +++ b/packages/cxl-ui/src/components/cxl-jw-player/mixins/chapter-navigation/index.js @@ -1,13 +1,13 @@ import { render } from 'lit'; import { property } from 'lit/decorators.js'; -import style from '../../../../styles/jw-player/chapter-css'; +import style from '../../../../styles/cxl-jw-player/cxl-jw-player-chapter-navigation-css'; import { chapterNavigationTemplate } from './index.html'; -export function ChapterMixin(BaseClass) { +export function ChapterNavigationMixin(BaseClass) { class Mixin extends BaseClass { @property({ attribute: 'plugin-path', type: String }) pluginPath; - async __createChapterNavigation() { + async __setupChapterNavigation() { const chapters = await this.__getChapters(); this.__chapterNavigation = document.createElement('div'); @@ -19,19 +19,13 @@ export function ChapterMixin(BaseClass) { this.__jwPlayerContainer.appendChild(this.__chapterNavigation); this.__jwPlayer.addButton( - `${this.pluginPath}images/chapter-bookmark-icon.svg`, + ``, 'Show Chapters', this.__toggleChapterNavigation.bind(this), 'toggle-chapters' ); } - __addStyle() { - const el = document.createElement('style'); - render(style, el); - this.appendChild(el); - } - __chapterSeek(e) { const index = Number(e.currentTarget.dataset.index); this.__jwPlayer.seek(this.__chapters[index].data.start / 1000); @@ -40,8 +34,8 @@ export function ChapterMixin(BaseClass) { async __setup() { await super.__setup(); - this.__addStyle(); - this.__createChapterNavigation(); + this.__addStyle(style); + this.__setupChapterNavigation(); } __toggleChapterNavigation() { diff --git a/packages/cxl-ui/src/components/cxl-jw-player/mixins/index.js b/packages/cxl-ui/src/components/cxl-jw-player/mixins/index.js new file mode 100644 index 000000000..280a40e71 --- /dev/null +++ b/packages/cxl-ui/src/components/cxl-jw-player/mixins/index.js @@ -0,0 +1,4 @@ +export { BaseMixin } from './BaseMixin'; +export { TranscriptMixin } from './TranscriptMixin'; +export { ChapterNavigationMixin } from './chapter-navigation'; +export { SavePositionMixin } from './SavePositionMixin'; diff --git a/packages/cxl-ui/src/components/jw-player/utility.js b/packages/cxl-ui/src/components/cxl-jw-player/utility.js similarity index 100% rename from packages/cxl-ui/src/components/jw-player/utility.js rename to packages/cxl-ui/src/components/cxl-jw-player/utility.js diff --git a/packages/cxl-ui/src/components/jw-player/mixins/index.js b/packages/cxl-ui/src/components/jw-player/mixins/index.js deleted file mode 100644 index ca56fdb7c..000000000 --- a/packages/cxl-ui/src/components/jw-player/mixins/index.js +++ /dev/null @@ -1,4 +0,0 @@ -export { BaseMixin } from './BaseMixin'; -export { CaptionMixin } from './CaptionMixin'; -export { ChapterMixin } from './chapter'; -export { SavePositionMixin } from './SavePositionMixin'; diff --git a/packages/cxl-ui/src/index-jwplayer.js b/packages/cxl-ui/src/index-jwplayer.js index 6106a8c1f..557a5aaa7 100644 --- a/packages/cxl-ui/src/index-jwplayer.js +++ b/packages/cxl-ui/src/index-jwplayer.js @@ -1 +1 @@ -export { JWPlayerElement } from './components/jw-player/index.js'; +export { CXLJWPlayerElement } from './components/cxl-jw-player/index.js'; diff --git a/packages/cxl-ui/src/index-storybook.js b/packages/cxl-ui/src/index-storybook.js index 0e071ca35..826de3c28 100644 --- a/packages/cxl-ui/src/index-storybook.js +++ b/packages/cxl-ui/src/index-storybook.js @@ -19,7 +19,7 @@ export { CXLSaveFavoriteElement } from './components/cxl-save-favorite.js'; export { CXLStarRatingElement } from './components/cxl-star-rating.js'; export { CXLLikeOrDislikeElement } from './components/cxl-like-or-dislike.js'; export { CXLPaywallElement } from './components/cxl-paywall.js'; -export { JWPlayerElement } from './components/jw-player/index.js'; +export { CXLJWPlayerElement } from './components/cxl-jw-player/index.js'; // @todo maybe https://github.com/tc39/proposal-export-default-from export { Headroom }; diff --git a/packages/cxl-ui/src/styles/jw-player/.gitignore b/packages/cxl-ui/src/styles/jw-player/.gitignore deleted file mode 100644 index f935021a8..000000000 --- a/packages/cxl-ui/src/styles/jw-player/.gitignore +++ /dev/null @@ -1 +0,0 @@ -!.gitignore diff --git a/packages/storybook/cxl-ui/jw-player/index.stories.js b/packages/storybook/cxl-ui/cxl-jw-player/index.stories.js similarity index 55% rename from packages/storybook/cxl-ui/jw-player/index.stories.js rename to packages/storybook/cxl-ui/cxl-jw-player/index.stories.js index 17bf14379..c9adc7c12 100644 --- a/packages/storybook/cxl-ui/jw-player/index.stories.js +++ b/packages/storybook/cxl-ui/cxl-jw-player/index.stories.js @@ -1,34 +1,38 @@ import { html } from 'lit'; -import '@conversionxl/cxl-ui/src/components/jw-player/index.js'; +import '@conversionxl/cxl-ui/src/components/cxl-jw-player/index.js'; export default { - title: 'JW Player/JW Player', + title: 'CXL UI/cxl-jw-player', }; -const Template = ({ captions, mediaId, minimumSearchLength, playerId, playlistId, pluginPath }) => +const Template = ({ captions, mediaId, mediaSource, minimumSearchLength, libraryId, librarySource, playlistId, playlistSource, pluginPath }) => html` - + > `; export const Default = Template.bind({}); @@ -37,9 +41,12 @@ Object.assign(Default, { args: { captions: true, mediaId: 'fZ0XiGdb', + mediaSource: '', minimumSearchLength: 3, playerId: '5CFJNXKb', + playerSource: '', playlistId: '', + playlistSource: '', pluginPath: 'https://cxl.com/institute/wp-content/plugins/cxl-jwplayer/', }, }); diff --git a/packages/storybook/cxl-ui/jw-player/playlist.stories.js b/packages/storybook/cxl-ui/cxl-jw-player/playlist.stories.js similarity index 78% rename from packages/storybook/cxl-ui/jw-player/playlist.stories.js rename to packages/storybook/cxl-ui/cxl-jw-player/playlist.stories.js index 752c77387..3a55354aa 100644 --- a/packages/storybook/cxl-ui/jw-player/playlist.stories.js +++ b/packages/storybook/cxl-ui/cxl-jw-player/playlist.stories.js @@ -1,8 +1,8 @@ import { html } from 'lit'; -import '@conversionxl/cxl-ui/src/components/jw-player/index.js'; +import '@conversionxl/cxl-ui/src/components/cxl-jw-player/index.js'; export default { - title: 'JW Player/JW Player', + title: 'CXL UI/cxl-jw-player', }; const Template = ({ captions, mediaId, playerId, playlistId, pluginPath }) => @@ -12,16 +12,16 @@ const Template = ({ captions, mediaId, playerId, playlistId, pluginPath }) => height: 100vh; } - jw-player { + cxl-jw-player { display: block; height: 100%; } - jw-player * { + cxl-jw-player * { box-sizing: border-box; } - playlist-id=${playlistId} plugin-path="${pluginPath}" type="playlist" - > + > `; export const Playlist = Template.bind({}); Object.assign(Playlist, { args: { - captions: true, + captions: false, mediaId: '', playerId: '5CFJNXKb', playlistId: 'tAxwbNsA', From c83003c953399fcbceede149d33434db2a6b8e0f Mon Sep 17 00:00:00 2001 From: Andrew Noblet Date: Mon, 20 Feb 2023 12:16:42 -0500 Subject: [PATCH 02/25] feat(cxl-ui): cxl-jw-player add styles directory --- packages/cxl-ui/src/styles/cxl-jw-player/.gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 packages/cxl-ui/src/styles/cxl-jw-player/.gitignore diff --git a/packages/cxl-ui/src/styles/cxl-jw-player/.gitignore b/packages/cxl-ui/src/styles/cxl-jw-player/.gitignore new file mode 100644 index 000000000..f935021a8 --- /dev/null +++ b/packages/cxl-ui/src/styles/cxl-jw-player/.gitignore @@ -0,0 +1 @@ +!.gitignore From b14bfcd68a101fa8468327ca6f3dd87459032675 Mon Sep 17 00:00:00 2001 From: Andrew Noblet Date: Mon, 20 Feb 2023 12:24:24 -0500 Subject: [PATCH 03/25] feat(cxl-ui): cxl-jw-player rename playerId, playerSource to libraryId, librarySource feat(cxl-ui): cxl-jw-player add hidden property style feat(cxl-ui): cxl-jw-player set container display and height feat(cxl-ui): cxl-jw-player fix playlist story feat(cxl-ui): cxl-jw-player fix playlist styles feat(cxl-ui): cxl-jw-player add next up CTA feat(cxl-ui): cxl-jw-player hide default next up overlay feat(cxl-ui): cxl-jw-player add pointer events to the link not the container feat(cxl-ui): cxl-jw-player move next up styles to the global directory feat(cxl-ui): cxl-jw-player refactor private methods and properties to protected feat(cxl-ui): cxl-jw-player refactor for playlists feat(cxl-ui): cxl-jw-player move styles to global directory feat(cxl-ui): cxl-jw-player remove save position endpoint request feat(cxl-ui): cxl-jw-player add the ability to save the playback rate feat(cxl-ui): cxl-jw-player add the ability to play protected content feat(cxl-ui): cxl-jw-player fix media and playlist source url feat(cxl-ui): cxl-jw-player fix and improve next up feature feat(cxl-ui): cxl-jw-player remove api secret default value feat(cxl-ui): cxl-jw-player fix save position when using a playlist --- packages/cxl-ui/package.json | 2 + .../cxl-jw-player/cxl-jw-player-shadow.scss | 12 +- .../scss/cxl-jw-player/cxl-jw-player.scss | 8 - .../cxl-jw-player-chapter-navigation.scss | 4 + .../cxl-jw-player/cxl-jw-player-nextup.scss | 20 +++ .../cxl-jw-player-transcript.scss | 0 .../global/cxl-jw-player/cxl-jw-player.scss | 25 +++ .../src/components/cxl-jw-player/README.md | 8 +- .../cxl-jw-player-transcript/index.js | 8 +- .../components/cxl-jw-player/index.html.js | 18 +-- .../src/components/cxl-jw-player/index.js | 22 ++- .../cxl-jw-player/mixins/BaseMixin.js | 144 ++++++++++++------ .../cxl-jw-player/mixins/NextUpMixin.js | 41 +++++ .../cxl-jw-player/mixins/SavePositionMixin.js | 50 ------ .../cxl-jw-player/mixins/StateMixin.js | 63 ++++++++ .../cxl-jw-player/mixins/TranscriptMixin.js | 128 +++++++++------- .../mixins/chapter-navigation/index.html.js | 4 +- .../mixins/chapter-navigation/index.js | 42 ++--- .../components/cxl-jw-player/mixins/index.js | 5 +- .../styles/global/cxl-jw-player/.gitignore | 1 + .../cxl-ui/cxl-jw-player/index.stories.js | 28 +++- .../cxl-ui/cxl-jw-player/playlist.stories.js | 35 ++++- yarn.lock | 10 ++ 23 files changed, 455 insertions(+), 223 deletions(-) delete mode 100644 packages/cxl-ui/scss/cxl-jw-player/cxl-jw-player.scss rename packages/cxl-ui/scss/{ => global}/cxl-jw-player/cxl-jw-player-chapter-navigation.scss (97%) create mode 100644 packages/cxl-ui/scss/global/cxl-jw-player/cxl-jw-player-nextup.scss rename packages/cxl-ui/scss/{ => global}/cxl-jw-player/cxl-jw-player-transcript.scss (100%) create mode 100644 packages/cxl-ui/scss/global/cxl-jw-player/cxl-jw-player.scss create mode 100644 packages/cxl-ui/src/components/cxl-jw-player/mixins/NextUpMixin.js delete mode 100644 packages/cxl-ui/src/components/cxl-jw-player/mixins/SavePositionMixin.js create mode 100644 packages/cxl-ui/src/components/cxl-jw-player/mixins/StateMixin.js create mode 100644 packages/cxl-ui/src/styles/global/cxl-jw-player/.gitignore diff --git a/packages/cxl-ui/package.json b/packages/cxl-ui/package.json index 493fbe6a8..9e9eb7855 100644 --- a/packages/cxl-ui/package.json +++ b/packages/cxl-ui/package.json @@ -32,8 +32,10 @@ "@vaadin/tooltip": "^23.3.7", "@vaadin/vaadin-themable-mixin": "^23.3.7", "cross-env": "~7.0.2", + "crypto-js": "^4.1.1", "headroom.js": "^0.12.0", "imports-loader": "^2.0.0", + "jose": "^4.13.1", "laravel-mix": "^6.0.39", "lit": "^2.2.5", "lodash-es": "^4.17.21", diff --git a/packages/cxl-ui/scss/cxl-jw-player/cxl-jw-player-shadow.scss b/packages/cxl-ui/scss/cxl-jw-player/cxl-jw-player-shadow.scss index 54f4f6c4e..22d930f3e 100644 --- a/packages/cxl-ui/scss/cxl-jw-player/cxl-jw-player-shadow.scss +++ b/packages/cxl-ui/scss/cxl-jw-player/cxl-jw-player-shadow.scss @@ -1,10 +1,12 @@ -@use "~@conversionxl/cxl-lumo-styles/scss/mixins"; +@use '~@conversionxl/cxl-lumo-styles/scss/mixins'; :host { box-sizing: border-box; +} - [active] { - background-color: var(--lumo-shade-10pct); +:host([has-captions]) { + .cxl-jw-player-container { + display: grid; } } @@ -35,6 +37,10 @@ justify-content: center; } +.cxl-jw-player-container { + height: 100%; +} + .flex { display: flex; height: 100%; diff --git a/packages/cxl-ui/scss/cxl-jw-player/cxl-jw-player.scss b/packages/cxl-ui/scss/cxl-jw-player/cxl-jw-player.scss deleted file mode 100644 index 3a387c1c1..000000000 --- a/packages/cxl-ui/scss/cxl-jw-player/cxl-jw-player.scss +++ /dev/null @@ -1,8 +0,0 @@ -.jw-player-button { - width: 32px; - fill: rgba(255, 255, 255, 0.8); - - &:hover { - fill: rgba(255, 255, 255, 1); - } -} diff --git a/packages/cxl-ui/scss/cxl-jw-player/cxl-jw-player-chapter-navigation.scss b/packages/cxl-ui/scss/global/cxl-jw-player/cxl-jw-player-chapter-navigation.scss similarity index 97% rename from packages/cxl-ui/scss/cxl-jw-player/cxl-jw-player-chapter-navigation.scss rename to packages/cxl-ui/scss/global/cxl-jw-player/cxl-jw-player-chapter-navigation.scss index 83f43539d..54de81403 100644 --- a/packages/cxl-ui/scss/cxl-jw-player/cxl-jw-player-chapter-navigation.scss +++ b/packages/cxl-ui/scss/global/cxl-jw-player/cxl-jw-player-chapter-navigation.scss @@ -14,6 +14,10 @@ background: var(--lumo-shade); gap: var(--lumo-space-s); + &[hidden] { + display: none; + } + .close { font-size: var(--lumo-font-size-xs); cursor: pointer; diff --git a/packages/cxl-ui/scss/global/cxl-jw-player/cxl-jw-player-nextup.scss b/packages/cxl-ui/scss/global/cxl-jw-player/cxl-jw-player-nextup.scss new file mode 100644 index 000000000..1587b92c9 --- /dev/null +++ b/packages/cxl-ui/scss/global/cxl-jw-player/cxl-jw-player-nextup.scss @@ -0,0 +1,20 @@ +cxl-jw-player { + .jw-nextup-container { + display: flex; + flex-direction: column; + align-items: flex-end; + + .jw-nextup-cta { + max-width: 400px; + width: 64%; + + a { + pointer-events: all; + + vaadin-button { + width: 100%; + } + } + } + } +} diff --git a/packages/cxl-ui/scss/cxl-jw-player/cxl-jw-player-transcript.scss b/packages/cxl-ui/scss/global/cxl-jw-player/cxl-jw-player-transcript.scss similarity index 100% rename from packages/cxl-ui/scss/cxl-jw-player/cxl-jw-player-transcript.scss rename to packages/cxl-ui/scss/global/cxl-jw-player/cxl-jw-player-transcript.scss diff --git a/packages/cxl-ui/scss/global/cxl-jw-player/cxl-jw-player.scss b/packages/cxl-ui/scss/global/cxl-jw-player/cxl-jw-player.scss new file mode 100644 index 000000000..29e15a17d --- /dev/null +++ b/packages/cxl-ui/scss/global/cxl-jw-player/cxl-jw-player.scss @@ -0,0 +1,25 @@ +cxl-jw-player { + .jw-player-button { + width: 32px; + fill: rgba(255, 255, 255, 0.8); + + &:hover { + fill: rgba(255, 255, 255, 1); + } + } + + .jw-related-item { + height: 100% !important; /* stylelint-disable-line declaration-no-important */ + } + + .jwplayer:not(.jw-flag-small-player) .jw-related-item-next-up { + .jw-related-item-poster { + height: 100%; + } + + .jw-related-item-title { + position: absolute; + bottom: 0; + } + } +} diff --git a/packages/cxl-ui/src/components/cxl-jw-player/README.md b/packages/cxl-ui/src/components/cxl-jw-player/README.md index 96d3b7815..0baa00e76 100644 --- a/packages/cxl-ui/src/components/cxl-jw-player/README.md +++ b/packages/cxl-ui/src/components/cxl-jw-player/README.md @@ -62,13 +62,13 @@ Order is important as each mixin extends the previous one. In this case, `MixinT There are currently two methods which are important to the lifecycle of the component: -`__setup()` +`_setup()` -This method is async and called when the component is first created. You must call `await super.__setup()` at the beginning of this method to make sure each parent class's setup method is called. +This method is async and called when the component is first created. You must call `await super._setup()` at the beginning of this method to make sure each parent class's setup method is called. -`__onTimeListener()` +`_onTimeListener()` -This method is async and called when the player's time changes. As with `__setup()`, you must call `await super.__onTimeListener()` at the beginning of this method. +This method is async and called when the player's time changes. As with `_setup()`, you must call `await super._onTimeListener()` at the beginning of this method. Current mixins available for use: diff --git a/packages/cxl-ui/src/components/cxl-jw-player/cxl-jw-player-transcript/index.js b/packages/cxl-ui/src/components/cxl-jw-player/cxl-jw-player-transcript/index.js index 5bbd17f44..35a3a47c2 100644 --- a/packages/cxl-ui/src/components/cxl-jw-player/cxl-jw-player-transcript/index.js +++ b/packages/cxl-ui/src/components/cxl-jw-player/cxl-jw-player-transcript/index.js @@ -1,6 +1,6 @@ import { html, LitElement } from 'lit'; import { customElement } from 'lit/decorators.js'; -import style from '../../../styles/cxl-jw-player/cxl-jw-player-transcript-css'; +import style from '../../../styles/global/cxl-jw-player/cxl-jw-player-transcript-css'; import shadowStyle from '../../../styles/cxl-jw-player/cxl-jw-player-transcript-shadow-css'; @customElement('cxl-jw-player-transcript') @@ -13,9 +13,9 @@ export class CXLJWPlayerTranscriptElement extends LitElement { return html``; } - async __setup() { - await super.__setup(); + async _setup() { + await super._setup(); - this.__addStyle(style); + this._addStyle(style); } } diff --git a/packages/cxl-ui/src/components/cxl-jw-player/index.html.js b/packages/cxl-ui/src/components/cxl-jw-player/index.html.js index 0199e1eb3..57f840bfa 100644 --- a/packages/cxl-ui/src/components/cxl-jw-player/index.html.js +++ b/packages/cxl-ui/src/components/cxl-jw-player/index.html.js @@ -11,7 +11,7 @@ export const template = function () { ? html`
    - ${this.__tracks.map( + ${this._tracks.map( (track, index) => html`${track.isChapter - ? html`

    + ? html`

    ${track.data.text}

    ` : html` ${track.data.text} diff --git a/packages/cxl-ui/src/components/cxl-jw-player/index.js b/packages/cxl-ui/src/components/cxl-jw-player/index.js index fd42ca9d0..903c10535 100644 --- a/packages/cxl-ui/src/components/cxl-jw-player/index.js +++ b/packages/cxl-ui/src/components/cxl-jw-player/index.js @@ -1,20 +1,28 @@ import { LitElement } from 'lit'; import { customElement } from 'lit/decorators.js'; -import { BaseMixin, TranscriptMixin, ChapterNavigationMixin, SavePositionMixin } from './mixins'; -import style from '../../styles/cxl-jw-player/cxl-jw-player-css'; +import style from '../../styles/global/cxl-jw-player/cxl-jw-player-css'; import shadowStyle from '../../styles/cxl-jw-player/cxl-jw-player-shadow-css'; -import { mixin } from './utility'; import { template } from './index.html'; +import { + BaseMixin, + ChapterNavigationMixin, + NextUpMixin, + StateMixin, + TranscriptMixin, +} from './mixins'; +import { mixin } from './utility'; @customElement('cxl-jw-player') export class CXLJWPlayerElement extends mixin(LitElement, [ BaseMixin, TranscriptMixin, ChapterNavigationMixin, - SavePositionMixin, + NextUpMixin, + StateMixin, ]) { config = { width: '100%', + nextupoffset: '-60', playbackRateControls: [0.25, 0.5, 0.75, 1, 1.25, 1.5, 1.75, 2], plugins: { // 'http://192.168.0.101:8080/telemetry-8.20.0.js': {}, @@ -33,9 +41,9 @@ export class CXLJWPlayerElement extends mixin(LitElement, [ return template.bind(this)(); } - async __setup() { - await super.__setup(); + async _setup() { + await super._setup(); - this.__addStyle(style); + this._addStyle(style); } } diff --git a/packages/cxl-ui/src/components/cxl-jw-player/mixins/BaseMixin.js b/packages/cxl-ui/src/components/cxl-jw-player/mixins/BaseMixin.js index 531233f97..db0ac098d 100644 --- a/packages/cxl-ui/src/components/cxl-jw-player/mixins/BaseMixin.js +++ b/packages/cxl-ui/src/components/cxl-jw-player/mixins/BaseMixin.js @@ -1,30 +1,32 @@ +import * as jose from 'jose'; import { render } from 'lit'; import { property } from 'lit/decorators.js'; import { throttle } from 'lodash-es'; import { parseSync } from 'subtitle'; +import { MD5 } from 'crypto-js'; export function BaseMixin(BaseClass) { class Mixin extends BaseClass { - __boundOnTimeListener; + _boundOnTimeListener; - __chapters; + _chapters; - __jwPlayer; + _jwPlayer; - __jwPlayerContainer; + _jwPlayerContainer; - __position; + @property({ attribute: 'api-secret', type: String }) apiSecret = ''; - @property({ attribute: 'media-id', type: String }) mediaId; - - @property({ attribute: 'media-source', type: String }) mediaSource; - - @property({ attribute: 'is-public', type: String }) isPublic; + @property({ attribute: 'is-public', type: Boolean }) isPublic; @property({ attribute: 'library-id', type: String }) libraryId; @property({ attribute: 'library-source', type: String }) librarySource; + @property({ attribute: 'media-id', type: String }) mediaId; + + @property({ attribute: 'media-source', type: String }) mediaSource; + @property({ attribute: 'playlist-id', type: String }) playlistId; @property({ attribute: 'playlist-source', type: String }) playlistSource; @@ -32,77 +34,100 @@ export function BaseMixin(BaseClass) { firstUpdated(_changedProperties) { super.firstUpdated(_changedProperties); - this.__setup(); + this._setup(); } updated(_changedProperties) { super.updated(_changedProperties); if (_changedProperties.has('captions') || _changedProperties.has('mediaId')) { - // this.__setup(); + // this._setup(); } } - get __scriptUrl() { + get _scriptUrl() { + if (!this.libraryId && !this.librarySource) return false; + let scriptUrl; - if (this.libraryId && this.isPublic) { - scriptUrl = `https://content.jwplatform.com/libraries/${this.libraryId}.js`; - } else if (this.librarySource) { + if (this.libraryId) { + if (this.isPublic) { + scriptUrl = `https://content.jwplatform.com/libraries/${this.libraryId}.js`; + } else { + scriptUrl = this.__signedURL(`libraries/${this.libraryId}.js`); + } + } + + if (this.librarySource) { scriptUrl = this.librarySource; - } else { - return false; } return scriptUrl; } - __addStyle(style) { + _addStyle(style) { const el = document.createElement('style'); render(style, el); this.appendChild(el); } - async __getChapters() { - const playlistItem = this.__jwPlayer.getPlaylistItem(); + async _getChapters() { + const playlistItem = this._jwPlayer.getPlaylistItem(); const chapters = playlistItem.tracks.filter((track) => track.kind === 'chapters'); + + if (chapters.length === 0) { + return []; + } + const { file } = chapters.length > 0 ? chapters[0] : ''; const response = await (await fetch(file)).text(); return parseSync(response); } - async __getMedia() { + async _getMedia() { + if (!this.mediaId && !this.mediaSource) return false; + let response; - if (this.mediaId && this.isPublic) { - response = await fetch(`https://cdn.jwplayer.com/v2/media/${this.mediaId}`); - } else if (this.mediaSource) { + if (this.mediaId) { + if (this.isPublic) { + response = await fetch(`https://cdn.jwplayer.com/v2/media/${this.mediaId}`); + } else { + response = await fetch(await this._signedJWTURL(`/v2/media/${this.mediaId}`)); + } + } + + if (this.mediaSource) { response = await fetch(this.mediaSource); - } else { - return false; } return response.json(); } - async __getPlaylist() { + async _getPlaylist() { + if (!this.playlistId && !this.playlistSource) return false; + let response; if (this.playlistId) { - response = await fetch(`https://cdn.jwplayer.com/v2/playlists/${this.playlistId}`); - } else if (this.playlistSource) { - response = await fetch(`https://cdn.jwplayer.com/v2/playlists/${this.playlistId}`); - } else { - return false; + if (this.isPublic) { + response = await fetch(`https://cdn.jwplayer.com/v2/playlists/${this.playlistId}`); + } else { + response = await fetch(await this._signedJWTURL(`/v2/playlists/${this.playlistId}`)); + } + } + + if (this.playlistSource) { + response = await fetch(this.playlistSource); } return response.json(); } - async __loadScript() { - return new Promise((resolve) => { + async _loadScript() { + return new Promise(async (resolve) => { const el = document.createElement('script'); - el.src = this.__scriptUrl; + el.src = this._scriptUrl; el.onload = () => { resolve(self.jwplayer); }; @@ -115,18 +140,17 @@ export function BaseMixin(BaseClass) { */ // eslint-disable-next-line class-methods-use-this, no-unused-vars, no-empty-function - async __onTimeListener(event) {} + async _onTimeListener(event) {} - __registerListeners() { - this.__boundOnTimeListener = throttle(this.__onTimeListener.bind(this), 1000); - this.__jwPlayer.on('time', this.__boundOnTimeListener); + _registerListeners() { + this._boundOnTimeListener = throttle(this._onTimeListener.bind(this), 1000); + this._jwPlayer.on('time', this._boundOnTimeListener); } /** * Each mixin has the ability to hook onto this method. */ - async __setup() { - + async _setup() { // Merge configs from `cxlJWPlayerData`. if (typeof window.cxlJWPlayerData !== 'undefined') { // eslint-disable-next-line camelcase @@ -135,26 +159,46 @@ export function BaseMixin(BaseClass) { this.config = { ...this.config, ...media_config }; } - const jwPlayer = await this.__loadScript(); + const jwPlayer = await this._loadScript(); const el = document.createElement('div'); this.appendChild(el); - this.__jwPlayer = jwPlayer(el).setup({ + this._jwPlayer = jwPlayer(el).setup({ ...this.config, - ...(await this.__getMedia()), - ...(await this.__getPlaylist()), + ...(await this._getMedia()), + ...(await this._getPlaylist()), }); await new Promise((resolve) => { - this.__jwPlayer.on('ready', resolve); + this._jwPlayer.on('ready', resolve); }); - this.__jwPlayerContainer = this.__jwPlayer.getContainer(); + this._jwPlayerContainer = this._jwPlayer.getContainer(); + + this._registerListeners(); + + this._chapters = await this._getChapters(); + } + + async _signedJWTURL(path) { + const secret = new TextEncoder().encode(this.apiSecret); + const alg = 'HS256'; + const typ = 'JWT'; + + const token = await new jose.SignJWT({ resource: path }) + .setProtectedHeader({ alg, typ }) + .setExpirationTime('2h') + .sign(secret); + + return `https://cdn.jwplayer.com${path}?token=${token}`; + } - this.__registerListeners(); + __signedURL(path) { + const expires = Math.ceil((new Date().getTime() + 3600) / 300) * 300; + const signature = MD5(`${path}:${expires}:${this.apiSecret}`); - this.__chapters = await this.__getChapters(); + return `https://cdn.jwplayer.com/${path}?exp=${expires}&sig=${signature}`; } } diff --git a/packages/cxl-ui/src/components/cxl-jw-player/mixins/NextUpMixin.js b/packages/cxl-ui/src/components/cxl-jw-player/mixins/NextUpMixin.js new file mode 100644 index 000000000..3b8797d51 --- /dev/null +++ b/packages/cxl-ui/src/components/cxl-jw-player/mixins/NextUpMixin.js @@ -0,0 +1,41 @@ +import { html, render } from 'lit'; +import style from '../../../styles/global/cxl-jw-player/cxl-jw-player-nextup-css'; +export function NextUpMixin(BaseClass) { + class Mixin extends BaseClass { + _nextUpCTA; + + async _setup() { + await super._setup(); + + this._addStyle(style); + + this._nextUpCTA = document.createElement('div'); + this._nextUpCTA.classList.add('jw-nextup-cta'); + + const container = this.querySelector('.jw-nextup-container'); + container.insertBefore(this._nextUpCTA, container.firstChild); + + this._updateNextUp(); + this._jwPlayer.on('playlistItem', this._updateNextUp.bind(this)); + } + + _updateNextUp() { + const playlistItem = this._jwPlayer.getPlaylistItem(); + + if (playlistItem && playlistItem.coursePage) { + render(this._getTemplate(playlistItem), this._nextUpCTA); + } + } + + // eslint-disable-next-line class-methods-use-this + _getTemplate(playlistItem) { + return html` + + Click here to continue this course + + `; + } + } + + return Mixin; +} diff --git a/packages/cxl-ui/src/components/cxl-jw-player/mixins/SavePositionMixin.js b/packages/cxl-ui/src/components/cxl-jw-player/mixins/SavePositionMixin.js deleted file mode 100644 index 9ea091ba6..000000000 --- a/packages/cxl-ui/src/components/cxl-jw-player/mixins/SavePositionMixin.js +++ /dev/null @@ -1,50 +0,0 @@ -export function SavePositionMixin(BaseClass) { - class Mixin extends BaseClass { - __endpoint; - - __nonce; - - __userId; - - async __setup() { - await super.__setup(); - - this.__endpoint = `${window.ajaxurl}?action=jwplayer_save_position`; - - if ( typeof window.cxl_pum_vars !== 'undefined' ) { - this.__nonce = window.cxl_pum_vars.nonce; - } - - this.__loadPosition(); - } - - async __loadPosition() { - this.__jwPlayer.seek(Number(localStorage.getItem(`jw-player-${this.mediaId}-position`))); - this.__jwPlayer.pause(); - } - - /** - * The listener is registered in the base class (../index.js). - */ - __onTimeListener(event) { - super.__onTimeListener(event); - - this.__savePosition(event); - } - - __savePosition({ position }) { - localStorage.setItem(`jw-player-${this.mediaId}-position`, position); - - fetch(this.__endpoint, { - _ajax_nonce: this.__nonce, - body: JSON.stringify({ mediaId: this.mediaId, position }), - headers: { - 'Content-Type': 'application/json', - }, - method: 'POST', - }); - } - } - - return Mixin; -} diff --git a/packages/cxl-ui/src/components/cxl-jw-player/mixins/StateMixin.js b/packages/cxl-ui/src/components/cxl-jw-player/mixins/StateMixin.js new file mode 100644 index 000000000..cb28364ed --- /dev/null +++ b/packages/cxl-ui/src/components/cxl-jw-player/mixins/StateMixin.js @@ -0,0 +1,63 @@ +export function StateMixin(BaseClass) { + class Mixin extends BaseClass { + _endpoint; + + _nonce; + + _userId; + + async _setup() { + await super._setup(); + + this._endpoint = `${window.ajaxurl}?action=jwplayer_save_position`; + + if (typeof window.cxl_pum_vars !== 'undefined') { + this._nonce = window.cxl_pum_vars.nonce; + } + + this._playbackRate(); + this._position(); + } + + _playbackRate() { + const playbackRate = localStorage.getItem(`jw-player-playback-rate`); + + if (playbackRate) { + this._jwPlayer.setPlaybackRate(Number(playbackRate)); + } + + this._jwPlayer.on('playbackRateChanged', ({ playbackRate }) => { + localStorage.setItem(`jw-player-playback-rate`, playbackRate); + }); + } + + _position() { + const mediaId = this.mediaId || this._jwPlayer.getPlaylistItem().mediaId; + + const position = localStorage.getItem(`jw-player-${mediaId}-position`); + + if (position) { + if (this.mediaId) { + this._setPosition(position); + } else { + this._jwPlayer.on('playlistItem', () => { + this._setPosition(position); + }); + + this._jwPlayer.playlistItem(this._jwPlayer.getPlaylistIndex()); + } + } + + this._jwPlayer.on('seek time', ({ position }) => { + localStorage.setItem(`jw-player-${mediaId}-position`, position); + }); + } + + _setPosition(position) { + this._jwPlayer.seek(Number(position)); + this._jwPlayer.pause(); + } + } + + return Mixin; +} diff --git a/packages/cxl-ui/src/components/cxl-jw-player/mixins/TranscriptMixin.js b/packages/cxl-ui/src/components/cxl-jw-player/mixins/TranscriptMixin.js index 9ee8d35f2..49b03c624 100644 --- a/packages/cxl-ui/src/components/cxl-jw-player/mixins/TranscriptMixin.js +++ b/packages/cxl-ui/src/components/cxl-jw-player/mixins/TranscriptMixin.js @@ -5,46 +5,52 @@ import { parseSync } from 'subtitle'; export function TranscriptMixin(BaseClass) { class Mixin extends BaseClass { - __debouncedSearch; + _debouncedSearch; - __mark; + _mark; @property({ reflect: true, type: Boolean }) captions = false; + @property({ attribute: 'has-captions', reflect: true, type: Boolean }) hasCaptions = false; - @state() __currentCue = 0; + @state() _currentCue = 0; - @state() __currentTrack = 0; + @state() _currentTrack = 0; - @state() __isSearchMinimumLength = false; + @state() _isSearchMinimumLength = false; - @state() __matches = 0; + @state() _matches = 0; @property({ attribute: 'minimum-search-length', type: Number }) minimumSearchLength = 3; @property({ type: Boolean }) shouldScroll = true; - @query('#search') __searchElement; + @query('#search') _searchElement; - @state() __searchValue; + @state() _searchValue; - @state() __tracks = []; + @state() _tracks = []; constructor() { super(); - this.__debouncedSearch = debounce(this.__search, 300); + this._debouncedSearch = debounce(this._search, 300); } - async __getCaptions() { - const playlistItem = this.__jwPlayer.getPlaylistItem(); - const { file } = playlistItem.tracks.filter((track) => track.kind === 'captions')[0]; - const response = await (await fetch(file)).text(); + async _getCaptions() { + const playlistItem = this._jwPlayer.getPlaylistItem(); + const track = playlistItem.tracks.filter((track) => track.kind === 'captions')[0]; + + if (!track) { + return []; + } + + const response = await (await fetch(track.file)).text(); return parseSync(response); } /* eslint-disable array-callback-return, class-methods-use-this, consistent-return, no-return-assign */ - __getCaptionsInChapter(chapters, captions, index) { + _getCaptionsInChapter(chapters, captions, index) { return captions.filter((caption) => { if (caption.data.start >= chapters[index].data.start) { if (chapters[index + 1]) { @@ -59,31 +65,34 @@ export function TranscriptMixin(BaseClass) { } /* eslint-enable array-callback-return, class-methods-use-this, consistent-return, no-return-assign */ - async __getTracks() { + async _getTracks() { const tracks = []; - const captions = await this.__getCaptions(); - const chapters = [...[{ data: { start: 0, text: '' } }], ...(await this.__getChapters())]; + const captions = await this._getCaptions(); - chapters.forEach((chapter, index) => { - tracks.push({ ...chapter, ...{ isChapter: true } }); - tracks.push(...this.__getCaptionsInChapter(chapters, captions, index)); - }); + if (captions.length) { + const chapters = [...[{ data: { start: 0, text: '' } }], ...(await this._getChapters())]; + + chapters.forEach((chapter, index) => { + tracks.push({ ...chapter, ...{ isChapter: true } }); + tracks.push(...this._getCaptionsInChapter(chapters, captions, index)); + }); + } return tracks; } - __onCaptionClick(e) { + _onCaptionClick(e) { const index = Number(e.currentTarget.dataset.index); - this.__jwPlayer.seek(this.__tracks[index].data.start / 1000); + this._jwPlayer.seek(this._tracks[index].data.start / 1000); } - __onTimeListener(event) { - super.__onTimeListener(event); + _onTimeListener(event) { + super._onTimeListener(event); const position = event.position * 1000; // Convert to milliseconds - this.__tracks.forEach(({ data: { end, start } }, index) => { + this._tracks.forEach(({ data: { end, start } }, index) => { if (start <= position && end >= position) { if (this.shouldScroll) { const el = this.renderRoot.querySelector(`[data-index="${index}"]`); @@ -92,51 +101,62 @@ export function TranscriptMixin(BaseClass) { } } - this.__currentTrack = index; + this._currentTrack = index; } }); } - __search() { - this.__mark.unmark(); + _search() { + this._mark.unmark(); - if (this.__searchElement.value.length >= this.minimumSearchLength) { - this.__isSearchMinimumLength = true; + if (this._searchElement.value.length >= this.minimumSearchLength) { + this._isSearchMinimumLength = true; - this.__mark.mark(this.__searchElement.value, { + this._mark.mark(this._searchElement.value, { done: (total) => { - this.__matches = total; + this._matches = total; }, separateWordSearch: false, }); } else { - this.__isSearchMinimumLength = false; + this._isSearchMinimumLength = false; } } - async __setup() { - await super.__setup(); + async _setup() { + await super._setup(); + + this._setupTranscript(); - this.__setupTranscript(); + this._jwPlayer.on('playlistItem', this._setupTranscript.bind(this)); } - async __setupTranscript() { - if (!this.__jwPlayer) return; + async _setupTranscript() { + if (!this._jwPlayer) return; - this.__jwPlayer.addButton( - ``, - 'Transcript', - this.__toggleTranscript.bind(this), - 'toggle-transcript' - ); + this._tracks = []; if (this.captions) { - this.__tracks = await this.__getTracks(); + this._tracks = await this._getTracks(); + } + + if (this._tracks.length) { + this.hasCaptions = true; // Make sure the DOM is up to date await this.updateComplete; - this.__mark = new Mark(this.renderRoot.querySelectorAll('.captions h2, .captions span')); + this._mark = new Mark(this.renderRoot.querySelectorAll('.captions h2, .captions span')); + + this._jwPlayer.addButton( + ``, + 'Transcript', + this._toggleTranscript.bind(this), + 'toggle-transcript' + ); + } else { + this.hasCaptions = false; + this._jwPlayer.removeButton('toggle-transcript'); } } @@ -145,22 +165,22 @@ export function TranscriptMixin(BaseClass) { if (changedProperties.has('captions')) { if (this.captions) { - this.__setupTranscript(); + this._setupTranscript(); } else if (this.mark) { - this.__mark.unmark(); + this._mark.unmark(); } } } - __attachListeners() { - super.__attachListeners(); + _attachListeners() { + super._attachListeners(); } - __toggleShouldScroll() { + _toggleShouldScroll() { this.shouldScroll = !this.shouldScroll; } - __toggleTranscript() { + _toggleTranscript() { // this.dispatchEvent(new CustomEvent('toggle-transcript')); this.captions = !this.captions; diff --git a/packages/cxl-ui/src/components/cxl-jw-player/mixins/chapter-navigation/index.html.js b/packages/cxl-ui/src/components/cxl-jw-player/mixins/chapter-navigation/index.html.js index 6c1cb1f4d..cb89afd14 100644 --- a/packages/cxl-ui/src/components/cxl-jw-player/mixins/chapter-navigation/index.html.js +++ b/packages/cxl-ui/src/components/cxl-jw-player/mixins/chapter-navigation/index.html.js @@ -8,7 +8,7 @@ export const chapterNavigationTemplate = function (chapters) {
    Chapters html` -
  • +
  • ${chapter.data.text}
  • ` diff --git a/packages/cxl-ui/src/components/cxl-jw-player/mixins/chapter-navigation/index.js b/packages/cxl-ui/src/components/cxl-jw-player/mixins/chapter-navigation/index.js index 2ab998723..4e9df2514 100644 --- a/packages/cxl-ui/src/components/cxl-jw-player/mixins/chapter-navigation/index.js +++ b/packages/cxl-ui/src/components/cxl-jw-player/mixins/chapter-navigation/index.js @@ -1,45 +1,51 @@ import { render } from 'lit'; import { property } from 'lit/decorators.js'; -import style from '../../../../styles/cxl-jw-player/cxl-jw-player-chapter-navigation-css'; +import style from '../../../../styles/global/cxl-jw-player/cxl-jw-player-chapter-navigation-css'; import { chapterNavigationTemplate } from './index.html'; export function ChapterNavigationMixin(BaseClass) { class Mixin extends BaseClass { @property({ attribute: 'plugin-path', type: String }) pluginPath; - async __setupChapterNavigation() { - const chapters = await this.__getChapters(); + async _setupChapterNavigation() { + const chapters = await this._getChapters(); - this.__chapterNavigation = document.createElement('div'); - this.__chapterNavigation.classList.add('chapter-navigation'); - this.__chapterNavigation.hidden = true; + if (!chapters.length) { + this._jwPlayer.removeButton('chapter-navigation'); - render(chapterNavigationTemplate.bind(this)(chapters), this.__chapterNavigation); + return; + } - this.__jwPlayerContainer.appendChild(this.__chapterNavigation); + this._chapterNavigation = document.createElement('div'); + this._chapterNavigation.classList.add('chapter-navigation'); + this._chapterNavigation.hidden = true; - this.__jwPlayer.addButton( + render(chapterNavigationTemplate.bind(this)(chapters), this._chapterNavigation); + + this._jwPlayerContainer.appendChild(this._chapterNavigation); + + this._jwPlayer.addButton( ``, 'Show Chapters', - this.__toggleChapterNavigation.bind(this), + this._toggleChapterNavigation.bind(this), 'toggle-chapters' ); } - __chapterSeek(e) { + _chapterSeek(e) { const index = Number(e.currentTarget.dataset.index); - this.__jwPlayer.seek(this.__chapters[index].data.start / 1000); + this._jwPlayer.seek(this._chapters[index].data.start / 1000); } - async __setup() { - await super.__setup(); + async _setup() { + await super._setup(); - this.__addStyle(style); - this.__setupChapterNavigation(); + this._addStyle(style); + this._setupChapterNavigation(); } - __toggleChapterNavigation() { - this.__chapterNavigation.hidden = !this.__chapterNavigation.hidden; + _toggleChapterNavigation() { + this._chapterNavigation.hidden = !this._chapterNavigation.hidden; } } diff --git a/packages/cxl-ui/src/components/cxl-jw-player/mixins/index.js b/packages/cxl-ui/src/components/cxl-jw-player/mixins/index.js index 280a40e71..ba2613c5a 100644 --- a/packages/cxl-ui/src/components/cxl-jw-player/mixins/index.js +++ b/packages/cxl-ui/src/components/cxl-jw-player/mixins/index.js @@ -1,4 +1,5 @@ export { BaseMixin } from './BaseMixin'; +export { ChapterNavigationMixin } from './chapter-navigation/index.js'; +export { NextUpMixin } from './NextUpMixin'; +export { StateMixin } from './StateMixin'; export { TranscriptMixin } from './TranscriptMixin'; -export { ChapterNavigationMixin } from './chapter-navigation'; -export { SavePositionMixin } from './SavePositionMixin'; diff --git a/packages/cxl-ui/src/styles/global/cxl-jw-player/.gitignore b/packages/cxl-ui/src/styles/global/cxl-jw-player/.gitignore new file mode 100644 index 000000000..f935021a8 --- /dev/null +++ b/packages/cxl-ui/src/styles/global/cxl-jw-player/.gitignore @@ -0,0 +1 @@ +!.gitignore diff --git a/packages/storybook/cxl-ui/cxl-jw-player/index.stories.js b/packages/storybook/cxl-ui/cxl-jw-player/index.stories.js index c9adc7c12..60b129bc8 100644 --- a/packages/storybook/cxl-ui/cxl-jw-player/index.stories.js +++ b/packages/storybook/cxl-ui/cxl-jw-player/index.stories.js @@ -5,7 +5,19 @@ export default { title: 'CXL UI/cxl-jw-player', }; -const Template = ({ captions, mediaId, mediaSource, minimumSearchLength, libraryId, librarySource, playlistId, playlistSource, pluginPath }) => +const Template = ({ + apiSecret, + captions, + isPublic, + libraryId, + librarySource, + mediaId, + mediaSource, + minimumSearchLength, + playlistId, + playlistSource, + pluginPath, +}) => html` + +const Template = ({ + apiSecret, + captions, + isPublic, + libraryId, + librarySource, + mediaId, + mediaSource, + minimumSearchLength, + playlistId, + playlistSource, + pluginPath, +}) => html` + `; @@ -36,10 +53,16 @@ export const Playlist = Template.bind({}); Object.assign(Playlist, { args: { - captions: false, + apiSecret: '', + captions: true, + isPublic: true, + libraryId: '5CFJNXKb', + librarySource: '', mediaId: '', - playerId: '5CFJNXKb', + mediaSource: '', + minimumSearchLength: 3, playlistId: 'tAxwbNsA', + playlistSource: '', pluginPath: 'https://cxl.com/institute/wp-content/plugins/cxl-jwplayer/', }, }); diff --git a/yarn.lock b/yarn.lock index b3ac10475..08b0f8282 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7127,6 +7127,11 @@ crypto-browserify@^3.11.0: randombytes "^2.0.0" randomfill "^1.0.3" +crypto-js@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-4.1.1.tgz#9e485bcf03521041bd85844786b83fb7619736cf" + integrity sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw== + crypto-random-string@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-1.0.0.tgz#a230f64f568310e1498009940790ec99545bca7e" @@ -11213,6 +11218,11 @@ jest-worker@^27.4.5: merge-stream "^2.0.0" supports-color "^8.0.0" +jose@^4.13.1: + version "4.13.1" + resolved "https://registry.yarnpkg.com/jose/-/jose-4.13.1.tgz#449111bb5ab171db85c03f1bd2cb1647ca06db1c" + integrity sha512-MSJQC5vXco5Br38mzaQKiq9mwt7lwj2eXpgpRyQYNHYt2lq1PjkWa7DLXX0WVcQLE9HhMh3jPiufS7fhJf+CLQ== + js-string-escape@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/js-string-escape/-/js-string-escape-1.0.1.tgz#e2625badbc0d67c7533e9edc1068c587ae4137ef" From 27b4a2292fb5f42f848ee45b6a63cf32289b1c07 Mon Sep 17 00:00:00 2001 From: Andrew Noblet Date: Tue, 7 Mar 2023 08:45:36 -0500 Subject: [PATCH 04/25] feat(cxl-ui): cxl-jw-player add ability to save playlist index --- .../cxl-jw-player/mixins/StateMixin.js | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/packages/cxl-ui/src/components/cxl-jw-player/mixins/StateMixin.js b/packages/cxl-ui/src/components/cxl-jw-player/mixins/StateMixin.js index cb28364ed..14eef3ca9 100644 --- a/packages/cxl-ui/src/components/cxl-jw-player/mixins/StateMixin.js +++ b/packages/cxl-ui/src/components/cxl-jw-player/mixins/StateMixin.js @@ -20,36 +20,45 @@ export function StateMixin(BaseClass) { } _playbackRate() { - const playbackRate = localStorage.getItem(`jw-player-playback-rate`); + const playbackRate = localStorage.getItem(`cxl-jw-player-playback-rate`); if (playbackRate) { this._jwPlayer.setPlaybackRate(Number(playbackRate)); } this._jwPlayer.on('playbackRateChanged', ({ playbackRate }) => { - localStorage.setItem(`jw-player-playback-rate`, playbackRate); + localStorage.setItem(`cxl-jw-player-playback-rate`, playbackRate); }); } _position() { const mediaId = this.mediaId || this._jwPlayer.getPlaylistItem().mediaId; - const position = localStorage.getItem(`jw-player-${mediaId}-position`); + const position = localStorage.getItem(`cxl-jw-player-${mediaId}-position`); if (position) { if (this.mediaId) { this._setPosition(position); } else { - this._jwPlayer.on('playlistItem', () => { - this._setPosition(position); + this._jwPlayer.on('playlistItem', ({ index }) => { + localStorage.setItem(`cxl-jw-player-${this.playlistId}-index`, index); + + // Wait for the player to load the new playlist item + setTimeout(() => { + this._setPosition(position); + }, 1000); }); - this._jwPlayer.playlistItem(this._jwPlayer.getPlaylistIndex()); + const index = + localStorage.getItem(`cxl-jw-player-${this.playlistId}-index`) || + this._jwPlayer.getPlaylistIndex(); + + this._jwPlayer.playlistItem(index); } } this._jwPlayer.on('seek time', ({ position }) => { - localStorage.setItem(`jw-player-${mediaId}-position`, position); + localStorage.setItem(`cxl-jw-player-${mediaId}-position`, position); }); } From ab8abe3cfadea4704fe5b702fc64d2d744d38a0d Mon Sep 17 00:00:00 2001 From: Andrew Noblet Date: Tue, 7 Mar 2023 09:50:05 -0500 Subject: [PATCH 05/25] feat(cxl-ui): cxl-jw-player add nextupoffset attribute --- .../cxl-ui/src/components/cxl-jw-player/index.js | 1 - .../components/cxl-jw-player/mixins/BaseMixin.js | 8 ++++++-- .../components/cxl-jw-player/mixins/NextUpMixin.js | 13 ++++++++++++- .../cxl-ui/cxl-jw-player/playlist.stories.js | 3 +++ 4 files changed, 21 insertions(+), 4 deletions(-) diff --git a/packages/cxl-ui/src/components/cxl-jw-player/index.js b/packages/cxl-ui/src/components/cxl-jw-player/index.js index 903c10535..5504602a4 100644 --- a/packages/cxl-ui/src/components/cxl-jw-player/index.js +++ b/packages/cxl-ui/src/components/cxl-jw-player/index.js @@ -22,7 +22,6 @@ export class CXLJWPlayerElement extends mixin(LitElement, [ ]) { config = { width: '100%', - nextupoffset: '-60', playbackRateControls: [0.25, 0.5, 0.75, 1, 1.25, 1.5, 1.75, 2], plugins: { // 'http://192.168.0.101:8080/telemetry-8.20.0.js': {}, diff --git a/packages/cxl-ui/src/components/cxl-jw-player/mixins/BaseMixin.js b/packages/cxl-ui/src/components/cxl-jw-player/mixins/BaseMixin.js index db0ac098d..3fc218bcd 100644 --- a/packages/cxl-ui/src/components/cxl-jw-player/mixins/BaseMixin.js +++ b/packages/cxl-ui/src/components/cxl-jw-player/mixins/BaseMixin.js @@ -31,9 +31,10 @@ export function BaseMixin(BaseClass) { @property({ attribute: 'playlist-source', type: String }) playlistSource; - firstUpdated(_changedProperties) { - super.firstUpdated(_changedProperties); + async firstUpdated(_changedProperties) { + await super.firstUpdated(_changedProperties); + await this._beforeSetup(); this._setup(); } @@ -70,6 +71,9 @@ export function BaseMixin(BaseClass) { this.appendChild(el); } + // eslint-disable-next-line class-methods-use-this, no-empty-function + _beforeSetup() {} + async _getChapters() { const playlistItem = this._jwPlayer.getPlaylistItem(); const chapters = playlistItem.tracks.filter((track) => track.kind === 'chapters'); diff --git a/packages/cxl-ui/src/components/cxl-jw-player/mixins/NextUpMixin.js b/packages/cxl-ui/src/components/cxl-jw-player/mixins/NextUpMixin.js index 3b8797d51..011264f7d 100644 --- a/packages/cxl-ui/src/components/cxl-jw-player/mixins/NextUpMixin.js +++ b/packages/cxl-ui/src/components/cxl-jw-player/mixins/NextUpMixin.js @@ -1,9 +1,18 @@ import { html, render } from 'lit'; +import { property } from 'lit/decorators.js'; import style from '../../../styles/global/cxl-jw-player/cxl-jw-player-nextup-css'; export function NextUpMixin(BaseClass) { class Mixin extends BaseClass { _nextUpCTA; + @property({ attribute: 'nextupoffset', type: String }) nextupoffset = '-100%`'; + + async _beforeSetup() { + await super._beforeSetup(); + + this.config.nextupoffset = this.nextupoffset; + } + async _setup() { await super._setup(); @@ -31,7 +40,9 @@ export function NextUpMixin(BaseClass) { _getTemplate(playlistItem) { return html` - Click here to continue this course + Click here to continue this course `; } diff --git a/packages/storybook/cxl-ui/cxl-jw-player/playlist.stories.js b/packages/storybook/cxl-ui/cxl-jw-player/playlist.stories.js index 1db9e33ae..7e32092de 100644 --- a/packages/storybook/cxl-ui/cxl-jw-player/playlist.stories.js +++ b/packages/storybook/cxl-ui/cxl-jw-player/playlist.stories.js @@ -14,6 +14,7 @@ const Template = ({ mediaId, mediaSource, minimumSearchLength, + nextupoffset, playlistId, playlistSource, pluginPath, @@ -43,6 +44,7 @@ const Template = ({ media-id=${mediaId} media-source=${mediaSource} minimum-search-length=${minimumSearchLength} + nextupoffset=${nextupoffset} playlist-id=${playlistId} playlist-source=${playlistSource} plugin-path="${pluginPath}" @@ -61,6 +63,7 @@ Object.assign(Playlist, { mediaId: '', mediaSource: '', minimumSearchLength: 3, + nextupoffset: '-100%', playlistId: 'tAxwbNsA', playlistSource: '', pluginPath: 'https://cxl.com/institute/wp-content/plugins/cxl-jwplayer/', From 702065a93eafaf9076dafa935a7c83b485e8da83 Mon Sep 17 00:00:00 2001 From: Andrew Noblet Date: Tue, 7 Mar 2023 10:26:52 -0500 Subject: [PATCH 06/25] feat(cxl-ui): cxl-jw-player add separate mobile CTA feat(cxl-ui): cxl-jw-player fix chapter navigation when using playlists feat(cxl-ui): cxl-jw-player fix CTA pointer events feat(cxl-ui): cxl-jw-player fix saving position when using a playlist feat(cxl-ui): cxl-jw-player update component references feat(cxl-ui): cxl-jw-player fix poor refactoring --- .../cxl-jw-player/cxl-jw-player-shadow.scss | 9 ++- .../cxl-jw-player/cxl-jw-player-nextup.scss | 27 +++++++-- .../src/components/cxl-course-dialog.js | 6 +- .../components/cxl-jw-player/index.html.js | 2 +- .../src/components/cxl-jw-player/index.js | 1 + .../cxl-jw-player/mixins/BaseMixin.js | 27 ++++++++- .../cxl-jw-player/mixins/NextUpMixin.js | 29 ++++++--- .../cxl-jw-player/mixins/StateMixin.js | 60 ++++++++++--------- .../cxl-jw-player/mixins/TranscriptMixin.js | 4 +- .../mixins/chapter-navigation/index.js | 28 ++++++--- 10 files changed, 134 insertions(+), 59 deletions(-) diff --git a/packages/cxl-ui/scss/cxl-jw-player/cxl-jw-player-shadow.scss b/packages/cxl-ui/scss/cxl-jw-player/cxl-jw-player-shadow.scss index 22d930f3e..47d442925 100644 --- a/packages/cxl-ui/scss/cxl-jw-player/cxl-jw-player-shadow.scss +++ b/packages/cxl-ui/scss/cxl-jw-player/cxl-jw-player-shadow.scss @@ -2,11 +2,16 @@ :host { box-sizing: border-box; + + [active] { + background-color: var(--lumo-shade-10pct); + } } -:host([has-captions]) { - .cxl-jw-player-container { +:host([captions]) { + #container { display: grid; + grid-template-rows: 1fr max-content 1fr; } } diff --git a/packages/cxl-ui/scss/global/cxl-jw-player/cxl-jw-player-nextup.scss b/packages/cxl-ui/scss/global/cxl-jw-player/cxl-jw-player-nextup.scss index 1587b92c9..3a1eb4ef2 100644 --- a/packages/cxl-ui/scss/global/cxl-jw-player/cxl-jw-player-nextup.scss +++ b/packages/cxl-ui/scss/global/cxl-jw-player/cxl-jw-player-nextup.scss @@ -1,19 +1,34 @@ cxl-jw-player { + &[wide] { + .jw-nextup-cta-mobile { + display: none; + } + } + + &:not([wide]) { + .jw-nextup-cta { + display: none; + } + } + .jw-nextup-container { display: flex; flex-direction: column; align-items: flex-end; + .jw-nextup-cta, + .jw-nextup-cta-mobile { + a { + pointer-events: all; + } + } + .jw-nextup-cta { max-width: 400px; width: 64%; - a { - pointer-events: all; - - vaadin-button { - width: 100%; - } + vaadin-button { + width: 100%; } } } diff --git a/packages/cxl-ui/src/components/cxl-course-dialog.js b/packages/cxl-ui/src/components/cxl-course-dialog.js index 45e8b063c..6f0cc5386 100644 --- a/packages/cxl-ui/src/components/cxl-course-dialog.js +++ b/packages/cxl-ui/src/components/cxl-course-dialog.js @@ -5,7 +5,7 @@ import { registerGlobalStyles } from '@conversionxl/cxl-lumo-styles/src/utils'; import '@vaadin/button'; import '@vaadin/dialog'; import './cxl-time.js'; -import './jw-player/index.js'; +import './cxl-jw-player/index.js'; import { dialogFooterRenderer, dialogRenderer } from '@vaadin/dialog/lit.js'; import cxlCourseDialogGlobalStyles from '../styles/global/cxl-course-dialog-css.js'; @@ -64,14 +64,14 @@ export class CXLCourseDialogElement extends LitElement {
    ${this.video - ? html` ` + >` : ''}

    ${this.course.description}

    diff --git a/packages/cxl-ui/src/components/cxl-jw-player/index.html.js b/packages/cxl-ui/src/components/cxl-jw-player/index.html.js index 57f840bfa..c248e1a3e 100644 --- a/packages/cxl-ui/src/components/cxl-jw-player/index.html.js +++ b/packages/cxl-ui/src/components/cxl-jw-player/index.html.js @@ -5,7 +5,7 @@ import '@vaadin/text-field'; // eslint-disable-next-line func-names export const template = function () { return html` -
    +
    ${this.captions ? html` diff --git a/packages/cxl-ui/src/components/cxl-jw-player/index.js b/packages/cxl-ui/src/components/cxl-jw-player/index.js index 5504602a4..1feb2ec0b 100644 --- a/packages/cxl-ui/src/components/cxl-jw-player/index.js +++ b/packages/cxl-ui/src/components/cxl-jw-player/index.js @@ -21,6 +21,7 @@ export class CXLJWPlayerElement extends mixin(LitElement, [ StateMixin, ]) { config = { + height: '100%', width: '100%', playbackRateControls: [0.25, 0.5, 0.75, 1, 1.25, 1.5, 1.75, 2], plugins: { diff --git a/packages/cxl-ui/src/components/cxl-jw-player/mixins/BaseMixin.js b/packages/cxl-ui/src/components/cxl-jw-player/mixins/BaseMixin.js index 3fc218bcd..6068c042a 100644 --- a/packages/cxl-ui/src/components/cxl-jw-player/mixins/BaseMixin.js +++ b/packages/cxl-ui/src/components/cxl-jw-player/mixins/BaseMixin.js @@ -4,6 +4,7 @@ import { property } from 'lit/decorators.js'; import { throttle } from 'lodash-es'; import { parseSync } from 'subtitle'; import { MD5 } from 'crypto-js'; +import { MediaQueryController } from '@vaadin/component-base/src/media-query-controller.js'; export function BaseMixin(BaseClass) { class Mixin extends BaseClass { @@ -15,6 +16,9 @@ export function BaseMixin(BaseClass) { _jwPlayerContainer; + // Device Detector media query. + _wideMediaQuery = '(min-width: 750px)'; + @property({ attribute: 'api-secret', type: String }) apiSecret = ''; @property({ attribute: 'is-public', type: Boolean }) isPublic; @@ -31,6 +35,20 @@ export function BaseMixin(BaseClass) { @property({ attribute: 'playlist-source', type: String }) playlistSource; + // MediaQueryController. + @property({ type: Boolean, reflect: true }) + wide; + + constructor() { + super(); + + this.addController( + new MediaQueryController(this._wideMediaQuery, (matches) => { + this.wide = matches; + }) + ); + } + async firstUpdated(_changedProperties) { await super.firstUpdated(_changedProperties); @@ -143,6 +161,9 @@ export function BaseMixin(BaseClass) { * Each mixin has the ability to hook onto this method. */ + // eslint-disable-next-line class-methods-use-this, no-unused-vars, no-empty-function + async _onReadyListener() {} + // eslint-disable-next-line class-methods-use-this, no-unused-vars, no-empty-function async _onTimeListener(event) {} @@ -175,7 +196,11 @@ export function BaseMixin(BaseClass) { }); await new Promise((resolve) => { - this._jwPlayer.on('ready', resolve); + this._jwPlayer.on('ready', async () => { + await this._onReadyListener(); + + resolve(); + }); }); this._jwPlayerContainer = this._jwPlayer.getContainer(); diff --git a/packages/cxl-ui/src/components/cxl-jw-player/mixins/NextUpMixin.js b/packages/cxl-ui/src/components/cxl-jw-player/mixins/NextUpMixin.js index 011264f7d..b654ccc58 100644 --- a/packages/cxl-ui/src/components/cxl-jw-player/mixins/NextUpMixin.js +++ b/packages/cxl-ui/src/components/cxl-jw-player/mixins/NextUpMixin.js @@ -3,7 +3,8 @@ import { property } from 'lit/decorators.js'; import style from '../../../styles/global/cxl-jw-player/cxl-jw-player-nextup-css'; export function NextUpMixin(BaseClass) { class Mixin extends BaseClass { - _nextUpCTA; + _nextupCTA; + _nextupCTAMobile; @property({ attribute: 'nextupoffset', type: String }) nextupoffset = '-100%`'; @@ -18,11 +19,15 @@ export function NextUpMixin(BaseClass) { this._addStyle(style); - this._nextUpCTA = document.createElement('div'); - this._nextUpCTA.classList.add('jw-nextup-cta'); + this._nextupCTA = document.createElement('div'); + this._nextupCTA.classList.add('jw-nextup-cta'); + + this._nextupCTAMobile = document.createElement('div'); + this._nextupCTAMobile.classList.add('jw-nextup-cta-mobile'); const container = this.querySelector('.jw-nextup-container'); - container.insertBefore(this._nextUpCTA, container.firstChild); + container.insertBefore(this._nextupCTA, container.firstChild); + container.insertBefore(this._nextupCTAMobile, container.firstChild); this._updateNextUp(); this._jwPlayer.on('playlistItem', this._updateNextUp.bind(this)); @@ -32,17 +37,25 @@ export function NextUpMixin(BaseClass) { const playlistItem = this._jwPlayer.getPlaylistItem(); if (playlistItem && playlistItem.coursePage) { - render(this._getTemplate(playlistItem), this._nextUpCTA); + render(this._getTemplate(playlistItem), this._nextupCTA); + render(this._getMobileTemplate(playlistItem), this._nextupCTAMobile); } } + // eslint-disable-next-line class-methods-use-this + _getMobileTemplate(playlistItem) { + return html` + + Go to course + + `; + } + // eslint-disable-next-line class-methods-use-this _getTemplate(playlistItem) { return html` - Click here to continue this course + Go to course `; } diff --git a/packages/cxl-ui/src/components/cxl-jw-player/mixins/StateMixin.js b/packages/cxl-ui/src/components/cxl-jw-player/mixins/StateMixin.js index 14eef3ca9..1046210a7 100644 --- a/packages/cxl-ui/src/components/cxl-jw-player/mixins/StateMixin.js +++ b/packages/cxl-ui/src/components/cxl-jw-player/mixins/StateMixin.js @@ -6,6 +6,26 @@ export function StateMixin(BaseClass) { _userId; + async _index() { + if (this.playlistId) { + const index = + localStorage.getItem(`cxl-jw-player-${this.playlistId}-index`) || + this._jwPlayer.getPlaylistIndex(); + + this._jwPlayer.playlistItem(index); + + this._jwPlayer.on('playlistItem', async ({ index }) => { + localStorage.setItem(`cxl-jw-player-${this.playlistId}-index`, index); + }); + } + } + + async _onReadyListener() { + await this._index(); + this._position(); + this._playbackRate(); + } + async _setup() { await super._setup(); @@ -14,9 +34,6 @@ export function StateMixin(BaseClass) { if (typeof window.cxl_pum_vars !== 'undefined') { this._nonce = window.cxl_pum_vars.nonce; } - - this._playbackRate(); - this._position(); } _playbackRate() { @@ -32,39 +49,28 @@ export function StateMixin(BaseClass) { } _position() { - const mediaId = this.mediaId || this._jwPlayer.getPlaylistItem().mediaId; - - const position = localStorage.getItem(`cxl-jw-player-${mediaId}-position`); + if (this.mediaId) { + this._setPosition(); + } - if (position) { - if (this.mediaId) { - this._setPosition(position); - } else { - this._jwPlayer.on('playlistItem', ({ index }) => { - localStorage.setItem(`cxl-jw-player-${this.playlistId}-index`, index); - - // Wait for the player to load the new playlist item - setTimeout(() => { - this._setPosition(position); - }, 1000); - }); - - const index = - localStorage.getItem(`cxl-jw-player-${this.playlistId}-index`) || - this._jwPlayer.getPlaylistIndex(); - - this._jwPlayer.playlistItem(index); - } + if (this.playlistId) { + this._jwPlayer.on('playlistItem', async ({ index }) => { + await jwplayer().getPlaylistItemPromise(index); + this._setPosition(); + }); } this._jwPlayer.on('seek time', ({ position }) => { + const mediaId = this.mediaId || this._jwPlayer.getPlaylistItem().mediaid; localStorage.setItem(`cxl-jw-player-${mediaId}-position`, position); }); } - _setPosition(position) { + _setPosition() { + const mediaId = this.mediaId || this._jwPlayer.getPlaylistItem().mediaid; + const position = localStorage.getItem(`cxl-jw-player-${mediaId}-position`); + this._jwPlayer.seek(Number(position)); - this._jwPlayer.pause(); } } diff --git a/packages/cxl-ui/src/components/cxl-jw-player/mixins/TranscriptMixin.js b/packages/cxl-ui/src/components/cxl-jw-player/mixins/TranscriptMixin.js index 49b03c624..9f72c6f90 100644 --- a/packages/cxl-ui/src/components/cxl-jw-player/mixins/TranscriptMixin.js +++ b/packages/cxl-ui/src/components/cxl-jw-player/mixins/TranscriptMixin.js @@ -141,7 +141,7 @@ export function TranscriptMixin(BaseClass) { } if (this._tracks.length) { - this.hasCaptions = true; + this.captions = true; // Make sure the DOM is up to date await this.updateComplete; @@ -155,7 +155,7 @@ export function TranscriptMixin(BaseClass) { 'toggle-transcript' ); } else { - this.hasCaptions = false; + this.captions = false; this._jwPlayer.removeButton('toggle-transcript'); } } diff --git a/packages/cxl-ui/src/components/cxl-jw-player/mixins/chapter-navigation/index.js b/packages/cxl-ui/src/components/cxl-jw-player/mixins/chapter-navigation/index.js index 4e9df2514..009a4c0aa 100644 --- a/packages/cxl-ui/src/components/cxl-jw-player/mixins/chapter-navigation/index.js +++ b/packages/cxl-ui/src/components/cxl-jw-player/mixins/chapter-navigation/index.js @@ -5,25 +5,21 @@ import { chapterNavigationTemplate } from './index.html'; export function ChapterNavigationMixin(BaseClass) { class Mixin extends BaseClass { + _chapterNavigation; + @property({ attribute: 'plugin-path', type: String }) pluginPath; async _setupChapterNavigation() { const chapters = await this._getChapters(); if (!chapters.length) { - this._jwPlayer.removeButton('chapter-navigation'); + this._jwPlayer.removeButton('toggle-chapters'); return; } - this._chapterNavigation = document.createElement('div'); - this._chapterNavigation.classList.add('chapter-navigation'); - this._chapterNavigation.hidden = true; - render(chapterNavigationTemplate.bind(this)(chapters), this._chapterNavigation); - this._jwPlayerContainer.appendChild(this._chapterNavigation); - this._jwPlayer.addButton( ``, 'Show Chapters', @@ -39,9 +35,23 @@ export function ChapterNavigationMixin(BaseClass) { async _setup() { await super._setup(); - + this._addStyle(style); - this._setupChapterNavigation(); + + this._chapterNavigation = document.createElement('div'); + this._chapterNavigation.classList.add('chapter-navigation'); + this._chapterNavigation.hidden = true; + this._jwPlayerContainer.appendChild(this._chapterNavigation); + + if (this.mediaId) { + this._setupChapterNavigation(); + } + + if (this.playlistId) { + this._jwPlayer.on('playlistItem', () => { + this._setupChapterNavigation(); + }); + } } _toggleChapterNavigation() { From 2a873b429ed5c27c6c4145af8499466e76cda785 Mon Sep 17 00:00:00 2001 From: Andrew Noblet Date: Mon, 23 Sep 2024 14:31:01 +0000 Subject: [PATCH 07/25] feat(cxl-ui): cxl-jw-player refactor base url --- .../components/cxl-jw-player/mixins/BaseMixin.js | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/packages/cxl-ui/src/components/cxl-jw-player/mixins/BaseMixin.js b/packages/cxl-ui/src/components/cxl-jw-player/mixins/BaseMixin.js index 6068c042a..11fc4ae33 100644 --- a/packages/cxl-ui/src/components/cxl-jw-player/mixins/BaseMixin.js +++ b/packages/cxl-ui/src/components/cxl-jw-player/mixins/BaseMixin.js @@ -8,6 +8,8 @@ import { MediaQueryController } from '@vaadin/component-base/src/media-query-con export function BaseMixin(BaseClass) { class Mixin extends BaseClass { + _baseUrl = 'https://cdn.jwplayer.com/'; + _boundOnTimeListener; _chapters; @@ -70,7 +72,7 @@ export function BaseMixin(BaseClass) { if (this.libraryId) { if (this.isPublic) { - scriptUrl = `https://content.jwplatform.com/libraries/${this.libraryId}.js`; + scriptUrl = `${this._baseUrl}libraries/${this.libraryId}.js`; } else { scriptUrl = this.__signedURL(`libraries/${this.libraryId}.js`); } @@ -113,9 +115,9 @@ export function BaseMixin(BaseClass) { if (this.mediaId) { if (this.isPublic) { - response = await fetch(`https://cdn.jwplayer.com/v2/media/${this.mediaId}`); + response = await fetch(`${this._baseUrl}v2/media/${this.mediaId}`); } else { - response = await fetch(await this._signedJWTURL(`/v2/media/${this.mediaId}`)); + response = await fetch(await this._signedJWTURL(`v2/media/${this.mediaId}`)); } } @@ -133,9 +135,9 @@ export function BaseMixin(BaseClass) { if (this.playlistId) { if (this.isPublic) { - response = await fetch(`https://cdn.jwplayer.com/v2/playlists/${this.playlistId}`); + response = await fetch(`${this._baseUrl}v2/playlists/${this.playlistId}`); } else { - response = await fetch(await this._signedJWTURL(`/v2/playlists/${this.playlistId}`)); + response = await fetch(await this._signedJWTURL(`v2/playlists/${this.playlistId}`)); } } @@ -220,14 +222,14 @@ export function BaseMixin(BaseClass) { .setExpirationTime('2h') .sign(secret); - return `https://cdn.jwplayer.com${path}?token=${token}`; + return `${this._baseUrl}${path}?token=${token}`; } __signedURL(path) { const expires = Math.ceil((new Date().getTime() + 3600) / 300) * 300; const signature = MD5(`${path}:${expires}:${this.apiSecret}`); - return `https://cdn.jwplayer.com/${path}?exp=${expires}&sig=${signature}`; + return `${this._baseUrl}${path}?exp=${expires}&sig=${signature}`; } } From b72df6280d2971a48ec0551eaf7a997a23f630b0 Mon Sep 17 00:00:00 2001 From: Andrew Noblet Date: Wed, 25 Sep 2024 15:37:44 +0000 Subject: [PATCH 08/25] feat(cxl-ui): cxl-jw-player remove jw player link https://app.clickup.com/t/apru1v?comment=90140063544642 --- packages/cxl-ui/scss/global/cxl-jw-player/cxl-jw-player.scss | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/cxl-ui/scss/global/cxl-jw-player/cxl-jw-player.scss b/packages/cxl-ui/scss/global/cxl-jw-player/cxl-jw-player.scss index 29e15a17d..eca744089 100644 --- a/packages/cxl-ui/scss/global/cxl-jw-player/cxl-jw-player.scss +++ b/packages/cxl-ui/scss/global/cxl-jw-player/cxl-jw-player.scss @@ -23,3 +23,7 @@ cxl-jw-player { } } } + +.jw-rightclick-list li:last-of-type { + display: none; +} From 9e0a30c8380a2726455538c9c3e1f945364cd631 Mon Sep 17 00:00:00 2001 From: Andrew Noblet Date: Sat, 28 Sep 2024 14:00:23 +0000 Subject: [PATCH 09/25] feat(cxl-ui): cxl-jw-player update player styles --- .../cxl-jw-player/cxl-jw-player-shadow.scss | 33 ++++++++++++++++--- .../src/components/cxl-jw-player/index.js | 2 +- .../cxl-jw-player/mixins/TranscriptMixin.js | 3 +- 3 files changed, 32 insertions(+), 6 deletions(-) diff --git a/packages/cxl-ui/scss/cxl-jw-player/cxl-jw-player-shadow.scss b/packages/cxl-ui/scss/cxl-jw-player/cxl-jw-player-shadow.scss index 47d442925..3645c7f0f 100644 --- a/packages/cxl-ui/scss/cxl-jw-player/cxl-jw-player-shadow.scss +++ b/packages/cxl-ui/scss/cxl-jw-player/cxl-jw-player-shadow.scss @@ -1,7 +1,14 @@ @use '~@conversionxl/cxl-lumo-styles/scss/mixins'; +@use "~@conversionxl/cxl-lumo-styles/scss/mq"; :host { + background-color: var(--wp--preset--color--white); box-sizing: border-box; + display: block; + + * { + box-sizing: border-box; + } [active] { background-color: var(--lumo-shade-10pct); @@ -9,13 +16,24 @@ } :host([captions]) { + height: 512px; + + @media #{mq.$medium} { + height: 1024px; + } + #container { + background-color: var(--lumo-shade-5pct); display: grid; - grid-template-rows: 1fr max-content 1fr; + grid-template-rows: max-content max-content auto; } } .captions { + line-height: calc(var(--lumo-line-height-m) * 1.25); + margin: 0 auto; + max-width: var(--cxl-content-max-width); + h2, span { &:hover { @@ -86,15 +104,21 @@ .scroll { @include mixins.better-webkit-scrollbars(); - overflow: auto; + overflow: hidden; + + &:hover { + overflow: auto; + } } .search { color: #fff; - background-color: #000; + max-width: 100vw; vaadin-checkbox { --lumo-body-text-color: #fff; + + display: none; } vaadin-checkbox:not([checked])::part(checkbox) { @@ -102,7 +126,8 @@ } vaadin-text-field { - width: 50%; + box-shadow: var(--lumo-box-shadow-xs); + width: var(--cxl-content-max-width); padding: 0; background-color: #fff; border-radius: var(--lumo-space-xs); diff --git a/packages/cxl-ui/src/components/cxl-jw-player/index.js b/packages/cxl-ui/src/components/cxl-jw-player/index.js index 1feb2ec0b..8879d5429 100644 --- a/packages/cxl-ui/src/components/cxl-jw-player/index.js +++ b/packages/cxl-ui/src/components/cxl-jw-player/index.js @@ -30,7 +30,7 @@ export class CXLJWPlayerElement extends mixin(LitElement, [ skin: { name: 'cxl-institute', }, - stretching: 'uniform', + // stretching: 'uniform', }; static get styles() { diff --git a/packages/cxl-ui/src/components/cxl-jw-player/mixins/TranscriptMixin.js b/packages/cxl-ui/src/components/cxl-jw-player/mixins/TranscriptMixin.js index 9f72c6f90..061d0e4e1 100644 --- a/packages/cxl-ui/src/components/cxl-jw-player/mixins/TranscriptMixin.js +++ b/packages/cxl-ui/src/components/cxl-jw-player/mixins/TranscriptMixin.js @@ -10,6 +10,7 @@ export function TranscriptMixin(BaseClass) { _mark; @property({ reflect: true, type: Boolean }) captions = false; + @property({ attribute: 'has-captions', reflect: true, type: Boolean }) hasCaptions = false; @state() _currentCue = 0; @@ -22,7 +23,7 @@ export function TranscriptMixin(BaseClass) { @property({ attribute: 'minimum-search-length', type: Number }) minimumSearchLength = 3; - @property({ type: Boolean }) shouldScroll = true; + @property({ type: Boolean }) shouldScroll = false; @query('#search') _searchElement; From 69f57789fe3a3efb0c6ced047aba537bf1adf01e Mon Sep 17 00:00:00 2001 From: Andrew Noblet Date: Sat, 28 Sep 2024 15:34:53 +0000 Subject: [PATCH 10/25] feat(cxl-ui): cxl-jw-player improve support for source url --- .../cxl-jw-player/mixins/BaseMixin.js | 24 ++++++++++++++++++- .../cxl-jw-player/mixins/StateMixin.js | 20 +++++++++------- 2 files changed, 34 insertions(+), 10 deletions(-) diff --git a/packages/cxl-ui/src/components/cxl-jw-player/mixins/BaseMixin.js b/packages/cxl-ui/src/components/cxl-jw-player/mixins/BaseMixin.js index 11fc4ae33..225f748d5 100644 --- a/packages/cxl-ui/src/components/cxl-jw-player/mixins/BaseMixin.js +++ b/packages/cxl-ui/src/components/cxl-jw-player/mixins/BaseMixin.js @@ -18,6 +18,10 @@ export function BaseMixin(BaseClass) { _jwPlayerContainer; + _mediaId + + _playlistId; + // Device Detector media query. _wideMediaQuery = '(min-width: 750px)'; @@ -92,7 +96,25 @@ export function BaseMixin(BaseClass) { } // eslint-disable-next-line class-methods-use-this, no-empty-function - _beforeSetup() {} + _beforeSetup() { + if(this.mediaId) { + this._mediaId = this.mediaId; + } + + if(this.mediaSource) { + const url = new URL(this.mediaSource); + this._mediaId = url.pathname.split('/').pop(); + } + + if(this.playlistId) { + this._playlistId = this.playlistId; + } + + if(this.playlistSource) { + const url = new URL(this.playlistSource); + this._playlistId = url.pathname.split('/').pop(); + } + } async _getChapters() { const playlistItem = this._jwPlayer.getPlaylistItem(); diff --git a/packages/cxl-ui/src/components/cxl-jw-player/mixins/StateMixin.js b/packages/cxl-ui/src/components/cxl-jw-player/mixins/StateMixin.js index 1046210a7..c0ea4016e 100644 --- a/packages/cxl-ui/src/components/cxl-jw-player/mixins/StateMixin.js +++ b/packages/cxl-ui/src/components/cxl-jw-player/mixins/StateMixin.js @@ -7,15 +7,15 @@ export function StateMixin(BaseClass) { _userId; async _index() { - if (this.playlistId) { + if (this._playlistId) { const index = - localStorage.getItem(`cxl-jw-player-${this.playlistId}-index`) || + localStorage.getItem(`cxl-jw-player-${this._playlistId}-index`) || this._jwPlayer.getPlaylistIndex(); this._jwPlayer.playlistItem(index); this._jwPlayer.on('playlistItem', async ({ index }) => { - localStorage.setItem(`cxl-jw-player-${this.playlistId}-index`, index); + localStorage.setItem(`cxl-jw-player-${this._playlistId}-index`, index); }); } } @@ -49,28 +49,30 @@ export function StateMixin(BaseClass) { } _position() { - if (this.mediaId) { + if (this._mediaId) { this._setPosition(); } - if (this.playlistId) { + if (this._playlistId) { this._jwPlayer.on('playlistItem', async ({ index }) => { - await jwplayer().getPlaylistItemPromise(index); + await this._jwPlayer.getPlaylistItemPromise(index); this._setPosition(); }); } this._jwPlayer.on('seek time', ({ position }) => { - const mediaId = this.mediaId || this._jwPlayer.getPlaylistItem().mediaid; + const mediaId = this._mediaId || this._jwPlayer.getPlaylistItem().mediaid; localStorage.setItem(`cxl-jw-player-${mediaId}-position`, position); }); } _setPosition() { - const mediaId = this.mediaId || this._jwPlayer.getPlaylistItem().mediaid; + const mediaId = this._mediaId || this._jwPlayer.getPlaylistItem().mediaid; const position = localStorage.getItem(`cxl-jw-player-${mediaId}-position`); - this._jwPlayer.seek(Number(position)); + this._jwPlayer.on('firstFrame', () => { + this._jwPlayer.seek(Number(position)); + }); } } From 88913efb1431b83cf94be2ccdd1954aaa0b59e92 Mon Sep 17 00:00:00 2001 From: Andrew Noblet Date: Sat, 28 Sep 2024 15:37:44 +0000 Subject: [PATCH 11/25] feat(cxl-ui): cxl-jw-player remove support for plugins --- packages/cxl-ui/src/components/cxl-jw-player/index.js | 3 --- .../cxl-jw-player/mixins/chapter-navigation/index.js | 3 --- 2 files changed, 6 deletions(-) diff --git a/packages/cxl-ui/src/components/cxl-jw-player/index.js b/packages/cxl-ui/src/components/cxl-jw-player/index.js index 8879d5429..dc9143c20 100644 --- a/packages/cxl-ui/src/components/cxl-jw-player/index.js +++ b/packages/cxl-ui/src/components/cxl-jw-player/index.js @@ -24,9 +24,6 @@ export class CXLJWPlayerElement extends mixin(LitElement, [ height: '100%', width: '100%', playbackRateControls: [0.25, 0.5, 0.75, 1, 1.25, 1.5, 1.75, 2], - plugins: { - // 'http://192.168.0.101:8080/telemetry-8.20.0.js': {}, - }, skin: { name: 'cxl-institute', }, diff --git a/packages/cxl-ui/src/components/cxl-jw-player/mixins/chapter-navigation/index.js b/packages/cxl-ui/src/components/cxl-jw-player/mixins/chapter-navigation/index.js index 009a4c0aa..6bf4562a1 100644 --- a/packages/cxl-ui/src/components/cxl-jw-player/mixins/chapter-navigation/index.js +++ b/packages/cxl-ui/src/components/cxl-jw-player/mixins/chapter-navigation/index.js @@ -1,5 +1,4 @@ import { render } from 'lit'; -import { property } from 'lit/decorators.js'; import style from '../../../../styles/global/cxl-jw-player/cxl-jw-player-chapter-navigation-css'; import { chapterNavigationTemplate } from './index.html'; @@ -7,8 +6,6 @@ export function ChapterNavigationMixin(BaseClass) { class Mixin extends BaseClass { _chapterNavigation; - @property({ attribute: 'plugin-path', type: String }) pluginPath; - async _setupChapterNavigation() { const chapters = await this._getChapters(); From 1ab4e129ad55ce127841046a8005a0edbf9c71a1 Mon Sep 17 00:00:00 2001 From: Andrew Noblet Date: Sat, 28 Sep 2024 16:17:38 +0000 Subject: [PATCH 12/25] feat(cxl-ui): cxl-jw-player update player styles --- .../scss/cxl-jw-player/cxl-jw-player-shadow.scss | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/packages/cxl-ui/scss/cxl-jw-player/cxl-jw-player-shadow.scss b/packages/cxl-ui/scss/cxl-jw-player/cxl-jw-player-shadow.scss index 3645c7f0f..6ac903dd0 100644 --- a/packages/cxl-ui/scss/cxl-jw-player/cxl-jw-player-shadow.scss +++ b/packages/cxl-ui/scss/cxl-jw-player/cxl-jw-player-shadow.scss @@ -16,12 +16,6 @@ } :host([captions]) { - height: 512px; - - @media #{mq.$medium} { - height: 1024px; - } - #container { background-color: var(--lumo-shade-5pct); display: grid; @@ -32,6 +26,7 @@ .captions { line-height: calc(var(--lumo-line-height-m) * 1.25); margin: 0 auto; + max-height: 16rem; max-width: var(--cxl-content-max-width); h2, @@ -113,7 +108,6 @@ .search { color: #fff; - max-width: 100vw; vaadin-checkbox { --lumo-body-text-color: #fff; @@ -127,10 +121,10 @@ vaadin-text-field { box-shadow: var(--lumo-box-shadow-xs); - width: var(--cxl-content-max-width); - padding: 0; background-color: #fff; border-radius: var(--lumo-space-xs); + max-width: var(--cxl-content-max-width); + width: 100%; &::part(input-field) { background-color: initial; From 5bc46c298279925c4892ff7b32e937bd242682ee Mon Sep 17 00:00:00 2001 From: Andrew Noblet Date: Mon, 30 Sep 2024 14:02:17 +0000 Subject: [PATCH 13/25] feat(cxl-ui): cxl-jw-player update search result count styles --- .../scss/cxl-jw-player/cxl-jw-player-shadow.scss | 6 ++++-- .../cxl-ui/src/components/cxl-jw-player/index.html.js | 10 ++++++---- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/packages/cxl-ui/scss/cxl-jw-player/cxl-jw-player-shadow.scss b/packages/cxl-ui/scss/cxl-jw-player/cxl-jw-player-shadow.scss index 6ac903dd0..d2d33468e 100644 --- a/packages/cxl-ui/scss/cxl-jw-player/cxl-jw-player-shadow.scss +++ b/packages/cxl-ui/scss/cxl-jw-player/cxl-jw-player-shadow.scss @@ -107,7 +107,10 @@ } .search { - color: #fff; + flex-direction: column; + margin: 0 auto; + max-width: var(--cxl-content-max-width); + width: 100%; vaadin-checkbox { --lumo-body-text-color: #fff; @@ -123,7 +126,6 @@ box-shadow: var(--lumo-box-shadow-xs); background-color: #fff; border-radius: var(--lumo-space-xs); - max-width: var(--cxl-content-max-width); width: 100%; &::part(input-field) { diff --git a/packages/cxl-ui/src/components/cxl-jw-player/index.html.js b/packages/cxl-ui/src/components/cxl-jw-player/index.html.js index c248e1a3e..7655dc19c 100644 --- a/packages/cxl-ui/src/components/cxl-jw-player/index.html.js +++ b/packages/cxl-ui/src/components/cxl-jw-player/index.html.js @@ -9,7 +9,7 @@ export const template = function () { ${this.captions ? html` -