From 75a24fb5e32bd95864d7b30ce3f0b310c9cf1ae2 Mon Sep 17 00:00:00 2001 From: biati-digital Date: Sun, 31 Mar 2024 07:55:47 -0600 Subject: [PATCH 1/8] feat: embed videos using youtube and vimeo API --- packages/video/src/video.ts | 361 ++++++++++++++++++++++++++++-------- 1 file changed, 280 insertions(+), 81 deletions(-) diff --git a/packages/video/src/video.ts b/packages/video/src/video.ts index 1ab46e8..61efaa4 100644 --- a/packages/video/src/video.ts +++ b/packages/video/src/video.ts @@ -1,22 +1,35 @@ import type { BuildParams, PluginAssets, PluginOptions, PluginType } from '@glightbox/plugin-core'; import { GLightboxPlugin } from '@glightbox/plugin-core'; +import { createIframe, mergeObjects } from '@glightbox/utils'; + +declare const Vimeo; +declare const YT; + +export type VideoTypes = 'youtube' | 'vimeo' | 'local'; export interface VideoOptions extends PluginOptions { maxWidth?: string; + aspectRatio?: string; + vertivalAspectRatio?: string; autoPlay?: boolean; injectAssets?: boolean; - customAssets?: { - css?: string[]; - js?: ({ src: string; module?: boolean })[]; + vimeo?: { + api: string; + params?: { [key: string]: string | number | boolean } + }; + youtube?: { + api: string; + params?: { [key: string]: string | number | boolean } }; - vistack?: { [key: string]: unknown }; } export interface VideoPlayer { paused: boolean; duration: number; play(): void; + playVideo(): void; pause(): void; + pauseVideo(): void; stop(): void; addEventListener(event: string, cb: () => void): void; } @@ -26,133 +39,319 @@ export default class VideoSlide extends GLightboxPlugin { type: PluginType = 'slide'; options: VideoOptions = {}; players = new Map(); - playerAssets: PluginAssets; + vimeoPlayers = new Map(); + youtubePlayers = new Map(); defaults: VideoOptions = { maxWidth: '840px', + aspectRatio: '16/9', + vertivalAspectRatio: '9/16', autoPlay: true, injectAssets: true, - vistack: {} + vimeo: { + api: 'https://player.vimeo.com/api/player.js', + params: { + api: 1, + byline: false, + portrait: false, + title: true, + transparent: false + } + }, + youtube: { + api: 'https://www.youtube.com/iframe_api', + params: { + enablejsapi: 1, + rel: 0, + showinfo: 0, + noCookie: true, + iv_load_policy: 3 + } + }, }; constructor(options: Partial = {}) { super(); - this.options = { ...this.defaults, ...options }; - this.playerAssets = { - 'css': [ - 'https://cdn.jsdelivr.net/npm/vidstack@^1.0.0/player/styles/default/theme.min.css', - 'https://cdn.jsdelivr.net/npm/vidstack@^1.0.0/player/styles/default/layouts/video.min.css' - ], - 'js': [{ - 'src': 'https://cdn.jsdelivr.net/npm/vidstack@^1.0.0/cdn/vidstack.js', - 'module': true - }] - }; + this.options = mergeObjects(this.defaults, options); } init(): void { this.instance.on('slide_before_change', () => { - const currentSlide = this.instance.getActiveSlideIndex(); - const player = this.slideHasPlayer(currentSlide); - if (player) { - player?.pause(); - } + const currentSlide = this.instance.getActiveSlide(); + this.playerDoAction(currentSlide, 'pause'); }); if (this.options.autoPlay) { this.instance.on('slide_changed', () => { - const currentSlide = this.instance.getActiveSlideIndex(); - const player = this.slideHasPlayer(currentSlide); - if (player) { - if (player?.paused && player?.duration) { - return player?.play(); - } - player.addEventListener('can-play', () => player?.play()); - } + const currentSlide = this.instance.getActiveSlide(); + this.playerDoAction(currentSlide, 'play'); }); } } public match(url: string): boolean { let matches = false; - if ( - url.match(/vimeo\.com\/([0-9]*)/) || - url.match(/(youtube\.com|youtube-nocookie\.com)\/watch\?v=([a-zA-Z0-9\-_]+)/) || - url.match(/youtu\.be\/([a-zA-Z0-9\-_]+)/) || - url.match(/(youtube\.com|youtube-nocookie\.com)\/embed\/([a-zA-Z0-9\-_]+)/) || - url.match(/(youtube\.com|youtube-nocookie\.com)\/shorts\/([a-zA-Z0-9\-_]+)/) || - url.match(/\.(mpg|avi|webm|mov|ogv|mp4)/) !== null - ) { + if (this.isVimeo(url) || this.isYoutube(url) || this.isRegularVideo(url)) { matches = true; } return matches; } - async build({ index, slide, config }: BuildParams): Promise { + public async build({ index, slide, config }: BuildParams): Promise { const randID = Math.floor(Math.random() * Date.now()) + index; - const playerAttr: { [key: string]: string | boolean | null } = { - 'id': `gl-player-${randID}`, - 'class': 'gl-video-player', - 'viewType': 'video', - 'controls': true, - 'aspectRatio': '16/9', - 'src': config.url, - 'crossorigin': '' - }; - const attrs = Object.entries(playerAttr).reduce((attrs, [key, value]) => { - const val = !value ? key : `${key}="${String(value)}"`; - return `${attrs} ${val}`; - }, ''); - - const html = ` - - - - - `; - + const id = `gl-player-${randID}`; const videoWidth = config?.width || this.options.maxWidth; + const aspectRatio = config?.aspectRatio || this.options.aspectRatio; + + slide?.style.setProperty('--gl-video-max-width', videoWidth); + slide?.style.setProperty('--gl-video-aspect-ratio', aspectRatio as string); - slide?.insertAdjacentHTML('beforeend', html); - if (videoWidth) { - slide?.style.setProperty('--gl-video-max-width', videoWidth); + if (this.isRegularVideo(config.url)) { + await this.buildVideo(slide, { ...config, id }); + } else if (this.isVimeo(config.url)) { + await this.buildVimeo(slide, { ...config, id }); + } else if (this.isYoutube(config.url)) { + await this.buildYoutube(slide, { ...config, id }); } - const player = document.getElementById(`gl-player-${randID}`); - this.players.set(`player-${index}`, player); return true; } - private slideHasPlayer(slideIndex): false | VideoPlayer { - if (this.players.has(`player-${slideIndex}`)) { - return this.players.get(`player-${slideIndex}`) as VideoPlayer; + private buildVideo(slide, config) { + return new Promise((resolve) => { + const video = document.createElement('video'); + const playerAttr: { [key: string]: string | boolean | null } = { + 'id': config.id, + 'src': config.url, + 'class': 'gl-video-player', + 'controls': '', + 'playsinline': '', + 'data-type': 'local', + 'preload': 'auto' + }; + for (const [key, value] of Object.entries(playerAttr)) { + video.setAttribute(key, value as string); + } + + video.addEventListener("canplay", (event) => { + this.instance.trigger('video_api_ready', { id: config.id, api: video }); + resolve(true); + }); + this.players.set(config.id, video); + slide.appendChild(video); + }); + } + + + public playerDoAction(slide, action: string): void { + const player = this.slideHasPlayer(slide); + if (!player) { + return; + } + // API has not finished loading, listen for it + if (!player?.api) { + this.instance.once('video_api_ready', (data) => { + if (data?.id === player?.id) { + this.playerDoAction(slide, action); + } + }); + return; + } + if (player?.type === 'local') { + action === 'play' ? player.api.play() : player.api.pause(); + } + if (player?.type === 'vimeo') { + action === 'play' ? player.api.play() : player.api.pause(); + } + if (player?.type === 'youtube') { + action === 'play' ? player.api.playVideo() : player.api.pauseVideo(); + } + } + + + private buildVimeo(slide, config) { + return new Promise((resolve) => { + const vimeoID = /vimeo.*\/(\d+)/i.exec(config.url); + const params = this.buildURLParams(this.options?.vimeo?.params ?? {}); + const videoUrl = `https://player.vimeo.com/video/${vimeoID[1]}?${params}` + + const iframe = createIframe({ + url: videoUrl, + appendTo: slide, + attrs: { + 'id': config.id, + 'allow': `autoplay; fullscreen; picture-in-picture`, + 'class': 'gl-video', + 'frameborder': '0', + 'data-type': 'vimeo', + }, + }) as HTMLIFrameElement; + + iframe.onload = () => { + iframe.onload = null; + + const playerAPI = new Vimeo.Player(iframe); + this.vimeoPlayers.set(config.id, playerAPI); + playerAPI.ready().then(() => { + this.instance.trigger('video_api_ready', { id: config.id, api: playerAPI }); + resolve(true); + }); + }; + }); + } + + private buildYoutube(slide, config) { + return new Promise((resolve) => { + const youtubeID = this.getYoutubeID(config.url); + const params = this.buildURLParams(this.options?.youtube?.params ?? {}); + const videoUrl = `https://www.youtube.com/embed/${youtubeID}?${params}` + + if (config.url.includes('shorts')) { + slide?.style.setProperty('--gl-video-max-width', '460px'); + slide?.style.setProperty('--gl-video-aspect-ratio', this.options.vertivalAspectRatio); + } + + const iframe = createIframe({ + url: videoUrl, + appendTo: slide, + attrs: { + 'id': config.id, + 'allow': `autoplay; fullscreen; picture-in-picture`, + 'class': 'gl-video', + 'frameborder': '0', + 'data-type': 'youtube', + }, + }) as HTMLIFrameElement; + + iframe.onload = () => { + iframe.onload = null; + + const playerAPI = new YT.Player(iframe, { + events: { + 'onReady': (e) => { + this.instance.trigger('video_api_ready', { id: config.id, api: playerAPI }); + resolve(true); + } + } + }); + this.youtubePlayers.set(config.id, playerAPI); + }; + }); + } + + + private slideHasPlayer(slideNode): false | { type: VideoTypes; node: HTMLIFrameElement; id: string; api: VideoPlayer } { + const iframe = slideNode.querySelector('iframe.gl-video'); + const video = slideNode.querySelector('video.gl-video-player'); + if (!iframe && !video) { + return false; + } + + const node = iframe || video; + const playerID = node.id; + const type = node.dataset.type; + const reponse = { + type, + node, + id: playerID, + api: undefined + }; + + if (type === 'local') { + reponse.api = node as VideoPlayer; + return reponse; + } + if (type === 'vimeo') { + reponse.api = this.vimeoPlayers.get(playerID) as VideoPlayer; + return reponse; + } + if (type === 'youtube') { + reponse.api = this.youtubePlayers.get(playerID) as VideoPlayer; + return reponse; + } + + return false; + } + + private isVimeo(url: string): boolean { + if (url.match(/vimeo\.com\/([0-9]*)/)) { + return true; + } + return false; + } + + private isYoutube(url: string): boolean { + if (url.match(/(youtube\.com|youtube-nocookie\.com)\/watch\?v=([a-zA-Z0-9\-_]+)/) || + url.match(/youtu\.be\/([a-zA-Z0-9\-_]+)/) || + url.match(/(youtube\.com|youtube-nocookie\.com)\/embed\/([a-zA-Z0-9\-_]+)/) || + url.match(/(youtube\.com|youtube-nocookie\.com)\/shorts\/([a-zA-Z0-9\-_]+)/)) { + return true; + } + return false; + } + + private getYoutubeID(url: string): string { + const [a, , b] = url.replace(/(>|<)/gi, '').split(/^.*(?:(?:youtu\.?be(\.com)?\/|v\/|vi\/|u\/\w\/|embed\/|shorts\/)|(?:(?:watch)?\?v(?:i)?=|&v(?:i)?=))([^#&?]*).*/) + if (b !== undefined) { + return b.split(/[^0-9a-z_-]/i)[0] + } else { + return a + } + } + + private buildURLParams(params: Record): string { + return Object.keys(params).map(key => { + let val = params[key]; + if (val === false) { val = '0' } + if (val === true) { val = '1' } + return `${key}=${val}`; + }).join('&'); + } + + private isRegularVideo(url: string): boolean { + if (url.match(/\.(mpg|avi|webm|mov|ogv|mp4)/)) { + return true; } return false; } + destroy(): void { + this.players = new Map(); + this.vimeoPlayers = new Map(); + this.youtubePlayers = new Map(); + } + assets(): false | PluginAssets { - if (!this.options.injectAssets) { + if (!this.options?.injectAssets) { return false; } - if (this.options?.customAssets) { - return this.options.customAssets; + + const apiAssets = []; + if (this.options?.vimeo?.api) { + apiAssets.push({ src: this.options.vimeo.api }); + } + if (this.options?.youtube?.api) { + apiAssets.push({ src: this.options.youtube.api }); } - return this.playerAssets; + + return { + js: apiAssets + }; } cssStyle(): string { return ` - .gl-type-video iframe.vds-youtube[data-no-controls] { - height: 100%; - } - .gl-type-video .vds-blocker { - display: none; - } .gl-type-video { - width: 100%; - max-width: var(--gl-video-max-width, 768px); - aspect-ratio: 16/9; + width: var(--gl-video-max-width, 768px); + max-width: calc(85vmin + (var(--gl-video-max-width) / 2)); + max-height: 100vmin; + aspect-ratio: var(--gl-video-aspect-ratio, 16/9); background-color: var(--gl-video-background-color, #000000); } + + .gl-video-player { + width: 100%; + height: 100%; + } `; } } From c199ba47f111a8cfa7b49087a7e2f528007e5284 Mon Sep 17 00:00:00 2001 From: biati-digital Date: Sun, 31 Mar 2024 07:56:06 -0600 Subject: [PATCH 2/8] feat: add function to create iframes --- packages/utils/src/index.ts | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index 34e8b1d..77dc4bb 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -136,6 +136,27 @@ export function windowSize() { }; } + +export function createIframe(config) { + const { url, attrs, appendTo } = config; + const iframe = document.createElement('iframe'); + iframe.className = 'gl-iframe'; + iframe.src = url; + iframe.style.width = '100%'; + iframe.style.height = '100%'; + + if (attrs) { + for (const [key, value] of Object.entries(attrs)) { + iframe.setAttribute(key, value as string); + } + } + if (appendTo) { + appendTo.appendChild(iframe); + } + return iframe +} + + /** * Inject videos api * used for video player From 40ae98d79c0bb0f38711a6ab0221b8de52a5746c Mon Sep 17 00:00:00 2001 From: biati-digital Date: Sun, 31 Mar 2024 07:57:01 -0600 Subject: [PATCH 3/8] fix: make sure it scrolls into view before content loading --- packages/glightbox/src/glightbox.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/glightbox/src/glightbox.ts b/packages/glightbox/src/glightbox.ts index ea4f59a..3f5fb31 100644 --- a/packages/glightbox/src/glightbox.ts +++ b/packages/glightbox/src/glightbox.ts @@ -116,13 +116,14 @@ export default class GLightbox { this.trigger('slide_before_change', { current: this.state.get('prevActiveSlideIndex'), next: index }); } - await this.preloadSlide(index, !first); const effect = this.options.appearance?.slideEffect; const openEffect = this.options.appearance?.openEffect; const scrollAnim = effect !== 'slide' || first ? 'instant' : 'smooth'; slideNode.scrollIntoView({ behavior: scrollAnim, block: 'start', inline: 'start' }); + await this.preloadSlide(index, !first); + removeClass(media, 'gl-animation-ended'); if (first && openEffect) { From 710ed37b18064d1d1066e9f6fb2e47bd09056651 Mon Sep 17 00:00:00 2001 From: biati-digital Date: Sun, 31 Mar 2024 11:16:44 -0600 Subject: [PATCH 4/8] chore: added utils dependency --- packages/video/package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/video/package.json b/packages/video/package.json index 3ce1a88..145ccac 100644 --- a/packages/video/package.json +++ b/packages/video/package.json @@ -28,7 +28,8 @@ "glightbox" ], "dependencies": { - "@glightbox/plugin-core": "1.0.0-beta.3" + "@glightbox/plugin-core": "1.0.0-beta.3", + "@glightbox/utils": "1.0.0-beta.1" }, "license": "GPLV3" } \ No newline at end of file From 5539626290a50a4ab01e80839df2c8c025b1778b Mon Sep 17 00:00:00 2001 From: biati-digital Date: Mon, 1 Apr 2024 07:07:35 -0600 Subject: [PATCH 5/8] refactor: renamed method --- packages/video/src/video.ts | 54 ++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 28 deletions(-) diff --git a/packages/video/src/video.ts b/packages/video/src/video.ts index 61efaa4..4bb29fa 100644 --- a/packages/video/src/video.ts +++ b/packages/video/src/video.ts @@ -141,33 +141,6 @@ export default class VideoSlide extends GLightboxPlugin { }); } - - public playerDoAction(slide, action: string): void { - const player = this.slideHasPlayer(slide); - if (!player) { - return; - } - // API has not finished loading, listen for it - if (!player?.api) { - this.instance.once('video_api_ready', (data) => { - if (data?.id === player?.id) { - this.playerDoAction(slide, action); - } - }); - return; - } - if (player?.type === 'local') { - action === 'play' ? player.api.play() : player.api.pause(); - } - if (player?.type === 'vimeo') { - action === 'play' ? player.api.play() : player.api.pause(); - } - if (player?.type === 'youtube') { - action === 'play' ? player.api.playVideo() : player.api.pauseVideo(); - } - } - - private buildVimeo(slide, config) { return new Promise((resolve) => { const vimeoID = /vimeo.*\/(\d+)/i.exec(config.url); @@ -239,7 +212,7 @@ export default class VideoSlide extends GLightboxPlugin { } - private slideHasPlayer(slideNode): false | { type: VideoTypes; node: HTMLIFrameElement; id: string; api: VideoPlayer } { + private getSlidePlayer(slideNode): false | { type: VideoTypes; node: HTMLIFrameElement; id: string; api: VideoPlayer } { const iframe = slideNode.querySelector('iframe.gl-video'); const video = slideNode.querySelector('video.gl-video-player'); if (!iframe && !video) { @@ -272,6 +245,31 @@ export default class VideoSlide extends GLightboxPlugin { return false; } + public playerDoAction(slide: HTMLElement, action: string): void { + const player = this.getSlidePlayer(slide); + if (!player) { + return; + } + // API has not finished loading, listen for it + if (!player?.api) { + this.instance.once('video_api_ready', (data) => { + if (data?.id === player?.id) { + this.playerDoAction(slide, action); + } + }); + return; + } + if (player?.type === 'local') { + action === 'play' ? player.api.play() : player.api.pause(); + } + if (player?.type === 'vimeo') { + action === 'play' ? player.api.play() : player.api.pause(); + } + if (player?.type === 'youtube') { + action === 'play' ? player.api.playVideo() : player.api.pauseVideo(); + } + } + private isVimeo(url: string): boolean { if (url.match(/vimeo\.com\/([0-9]*)/)) { return true; From eb4f2310c91f2932fafbeb9f08d070389407d4e5 Mon Sep 17 00:00:00 2001 From: biati-digital Date: Mon, 1 Apr 2024 08:03:46 -0600 Subject: [PATCH 6/8] refactor: minimal code changes --- packages/video/src/video.ts | 38 ++++++++++++++++++------------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/packages/video/src/video.ts b/packages/video/src/video.ts index 4bb29fa..1c38202 100644 --- a/packages/video/src/video.ts +++ b/packages/video/src/video.ts @@ -68,6 +68,12 @@ export default class VideoSlide extends GLightboxPlugin { } }, }; + private iframeAttrs: { [key: string]: string } = { + 'allow': `autoplay; fullscreen; picture-in-picture`, + 'class': 'gl-video', + 'frameborder': '0', + 'data-type': '', + }; constructor(options: Partial = {}) { super(); @@ -151,12 +157,10 @@ export default class VideoSlide extends GLightboxPlugin { url: videoUrl, appendTo: slide, attrs: { + ...this.iframeAttrs, 'id': config.id, - 'allow': `autoplay; fullscreen; picture-in-picture`, - 'class': 'gl-video', - 'frameborder': '0', - 'data-type': 'vimeo', - }, + 'data-type': 'vimeo' + } }) as HTMLIFrameElement; iframe.onload = () => { @@ -174,7 +178,12 @@ export default class VideoSlide extends GLightboxPlugin { private buildYoutube(slide, config) { return new Promise((resolve) => { - const youtubeID = this.getYoutubeID(config.url); + const [a, , b] = config.url.replace(/(>|<)/gi, '').split(/^.*(?:(?:youtu\.?be(\.com)?\/|v\/|vi\/|u\/\w\/|embed\/|shorts\/)|(?:(?:watch)?\?v(?:i)?=|&v(?:i)?=))([^#&?]*).*/) + const youtubeID = b !== undefined ? b.split(/[^0-9a-z_-]/i)[0] : a + if (!youtubeID) { + throw new Error('Unable to get Youtube video ID'); + } + const params = this.buildURLParams(this.options?.youtube?.params ?? {}); const videoUrl = `https://www.youtube.com/embed/${youtubeID}?${params}` @@ -187,12 +196,10 @@ export default class VideoSlide extends GLightboxPlugin { url: videoUrl, appendTo: slide, attrs: { + ...this.iframeAttrs, 'id': config.id, - 'allow': `autoplay; fullscreen; picture-in-picture`, - 'class': 'gl-video', - 'frameborder': '0', - 'data-type': 'youtube', - }, + 'data-type': 'youtube' + } }) as HTMLIFrameElement; iframe.onload = () => { @@ -287,15 +294,6 @@ export default class VideoSlide extends GLightboxPlugin { return false; } - private getYoutubeID(url: string): string { - const [a, , b] = url.replace(/(>|<)/gi, '').split(/^.*(?:(?:youtu\.?be(\.com)?\/|v\/|vi\/|u\/\w\/|embed\/|shorts\/)|(?:(?:watch)?\?v(?:i)?=|&v(?:i)?=))([^#&?]*).*/) - if (b !== undefined) { - return b.split(/[^0-9a-z_-]/i)[0] - } else { - return a - } - } - private buildURLParams(params: Record): string { return Object.keys(params).map(key => { let val = params[key]; From 255c7b59bb26b4dca664069a8c93af012d65dcd0 Mon Sep 17 00:00:00 2001 From: biati-digital Date: Mon, 1 Apr 2024 08:12:03 -0600 Subject: [PATCH 7/8] fix: wrong variable name --- packages/video/src/video.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/video/src/video.ts b/packages/video/src/video.ts index 1c38202..3d771e5 100644 --- a/packages/video/src/video.ts +++ b/packages/video/src/video.ts @@ -10,7 +10,7 @@ export type VideoTypes = 'youtube' | 'vimeo' | 'local'; export interface VideoOptions extends PluginOptions { maxWidth?: string; aspectRatio?: string; - vertivalAspectRatio?: string; + verticalAspectRatio?: string; autoPlay?: boolean; injectAssets?: boolean; vimeo?: { @@ -44,7 +44,7 @@ export default class VideoSlide extends GLightboxPlugin { defaults: VideoOptions = { maxWidth: '840px', aspectRatio: '16/9', - vertivalAspectRatio: '9/16', + verticalAspectRatio: '9/16', autoPlay: true, injectAssets: true, vimeo: { @@ -189,7 +189,7 @@ export default class VideoSlide extends GLightboxPlugin { if (config.url.includes('shorts')) { slide?.style.setProperty('--gl-video-max-width', '460px'); - slide?.style.setProperty('--gl-video-aspect-ratio', this.options.vertivalAspectRatio); + slide?.style.setProperty('--gl-video-aspect-ratio', this.options.verticalAspectRatio); } const iframe = createIframe({ From 7ae45bfa750e78250675b291bcf1e03559a5ec68 Mon Sep 17 00:00:00 2001 From: biati-digital Date: Fri, 6 Sep 2024 21:17:53 -0600 Subject: [PATCH 8/8] feat: youtube api methods general --- packages/video/src/video.ts | 37 +++++++++++++++++-------------------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/packages/video/src/video.ts b/packages/video/src/video.ts index 3d771e5..8ded283 100644 --- a/packages/video/src/video.ts +++ b/packages/video/src/video.ts @@ -213,6 +213,8 @@ export default class VideoSlide extends GLightboxPlugin { } } }); + playerAPI.play = () => { playerAPI.playVideo() }; + playerAPI.pause = () => { playerAPI.pauseVideo() }; this.youtubePlayers.set(config.id, playerAPI); }; }); @@ -266,29 +268,31 @@ export default class VideoSlide extends GLightboxPlugin { }); return; } - if (player?.type === 'local') { - action === 'play' ? player.api.play() : player.api.pause(); + const actions = { + 'play': player.api.play, + 'pause': player.api.pause } - if (player?.type === 'vimeo') { - action === 'play' ? player.api.play() : player.api.pause(); - } - if (player?.type === 'youtube') { - action === 'play' ? player.api.playVideo() : player.api.pauseVideo(); + if (action in actions) { + actions[action].apply(player.api); } } private isVimeo(url: string): boolean { - if (url.match(/vimeo\.com\/([0-9]*)/)) { + return url.includes('vimeo.com'); + } + + private isYoutube(url: string): boolean { + if (url.includes('youtube.com') || + url.includes('youtu.be') || + url.includes('youtube-nocookie.com') + ) { return true; } return false; } - private isYoutube(url: string): boolean { - if (url.match(/(youtube\.com|youtube-nocookie\.com)\/watch\?v=([a-zA-Z0-9\-_]+)/) || - url.match(/youtu\.be\/([a-zA-Z0-9\-_]+)/) || - url.match(/(youtube\.com|youtube-nocookie\.com)\/embed\/([a-zA-Z0-9\-_]+)/) || - url.match(/(youtube\.com|youtube-nocookie\.com)\/shorts\/([a-zA-Z0-9\-_]+)/)) { + private isRegularVideo(url: string): boolean { + if (url.match(/\.(mpg|avi|webm|mov|ogv|mp4)/)) { return true; } return false; @@ -303,13 +307,6 @@ export default class VideoSlide extends GLightboxPlugin { }).join('&'); } - private isRegularVideo(url: string): boolean { - if (url.match(/\.(mpg|avi|webm|mov|ogv|mp4)/)) { - return true; - } - return false; - } - destroy(): void { this.players = new Map(); this.vimeoPlayers = new Map();