diff --git a/CHANGELOG.md b/CHANGELOG.md index 323478f42c..c146294caf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## Draft +- Fixed YouTube video playback in Safari by adding widget_referrer and origin parameters [#2598](https://github.com/bigcommerce/cornerstone/pull/2598) ## 6.18.2 (01-08-2026) - Fixed validation removal on form fields [#2593](https://github.com/bigcommerce/cornerstone/pull/2593) diff --git a/assets/js/theme/product/video-gallery.js b/assets/js/theme/product/video-gallery.js index 2351ef28da..e20fa6fc6a 100644 --- a/assets/js/theme/product/video-gallery.js +++ b/assets/js/theme/product/video-gallery.js @@ -3,7 +3,78 @@ export class VideoGallery { this.$player = $element.find('[data-video-player]'); this.$videos = $element.find('[data-video-item]'); this.currentVideo = {}; + this.referrer = window.location.origin; + this.isSafari = this.detectSafari(); this.bindEvents(); + this.initializePlayer(); + } + + detectSafari() { + const userAgent = navigator.userAgent.toLowerCase(); + + return /safari/.test(userAgent) && !/chrome|chromium|crios|fxios/.test(userAgent); + } + + /** + * Build YouTube embed URL with widget_referrer and origin parameters + * @param {string} videoId - YouTube video ID + * @param {string} existingParams - Existing URL parameters (e.g., "rel=0") + * @returns {string} Complete YouTube embed URL + */ + buildEmbedUrl(videoId, existingParams = '') { + const params = new URLSearchParams(existingParams); + + // helps with Safari compatibility + params.set('widget_referrer', this.referrer); + params.set('origin', this.referrer); + + return `//www.youtube.com/embed/${videoId}?${params.toString()}`; + } + + /** + * Set iframe permissions for enhanced YouTube player functionality + */ + setIframePermissions() { + const allowPermissions = [ + 'autoplay', + 'clipboard-write', + // allows the iframe to use Encrypted Media Extensions and play protected content + 'encrypted-media', + 'picture-in-picture', + 'web-share', + ]; + const currentPermissions = (this.$player.attr('allow') || '').split(';').map(s => s.trim()).filter(Boolean); + const allowSet = new Set([...currentPermissions, ...allowPermissions]); + this.$player.attr('allow', Array.from(allowSet).join('; ')); + } + + initializePlayer() { + // Set iframe referrer policy to ensure Referer header is sent + this.$player.attr('referrerpolicy', 'strict-origin-when-cross-origin'); + + // Set extra permissions for better YouTube functionality + // this.setIframePermissions(); + + const currentSrc = this.$player.attr('src') || this.$player.attr('data-src'); + if (currentSrc) { + // Extract video ID and existing parameters from the URL + const match = currentSrc.match(/\/embed\/([^?]+)(?:\?(.+))?/); + if (match) { + const videoId = match[1]; + const existingParams = match[2] || ''; + // Rebuild URL to ensure widget_referrer and origin are present for Safari compatibility + const newSrc = this.buildEmbedUrl(videoId, existingParams); + + if (this.isSafari) { + // Removing lazyload and setting src directly ensures referrerpolicy is respected before the first request in Safari + this.$player.removeClass('lazyload'); + this.$player.attr('src', newSrc); + } else { + // Keep lazyload class and only update data-src for rest browsers + this.$player.attr('data-src', newSrc); + } + } + } } selectNewVideo(e) { @@ -21,7 +92,8 @@ export class VideoGallery { } setMainVideo() { - this.$player.attr('src', `//www.youtube.com/embed/${this.currentVideo.id}`); + const embedUrl = this.buildEmbedUrl(this.currentVideo.id); + this.$player.attr('src', embedUrl); } setActiveThumb() {