From ca60af9dadc25adbc9eb159870ce1233a2886492 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=20De=20Caluw=C3=A9?= Date: Fri, 1 Apr 2022 11:35:49 +0000 Subject: [PATCH] [FIX] web_editor, website: allow autoplay of youtube videos on mobile The autoplay option for youtube videos in a .media_iframe_video snippet does not currently work on mobile devices. This happens because the autoplay param in the url is only considered for desktop devices. Mobile autoplay can be forced by using the youtube js api, in the same way as it is already done for background videos. Therefore, the common code is extracted into a mixin, extended by both widgets. opw-2607308 closes odoo/odoo#89439 X-original-commit: 83972aa28957414d53c69bb41ce36646891a829e Signed-off-by: Quentin Smetz (qsm) Co-authored-by: qsm-odoo --- addons/web_editor/tools.py | 6 + .../src/js/content/snippets.animation.js | 136 ++++++++++++------ 2 files changed, 97 insertions(+), 45 deletions(-) diff --git a/addons/web_editor/tools.py b/addons/web_editor/tools.py index 51023bf4894..2f3b1a63b4a 100644 --- a/addons/web_editor/tools.py +++ b/addons/web_editor/tools.py @@ -68,6 +68,12 @@ def get_video_url_data(video_url, autoplay=False, loop=False, hide_controls=Fals params['autoplay'] = autoplay and 1 or 0 if autoplay: params['mute'] = 1 + # The youtube js api is needed for autoplay on mobile. Note: this + # was added as a fix, old customers may have autoplay videos + # without this, which will make their video autoplay on desktop but + # not in mobile (so no behavior change was done in stable, this + # should not be migrated). + params['enablejsapi'] = 1 if hide_controls: params['controls'] = 0 if loop: diff --git a/addons/website/static/src/js/content/snippets.animation.js b/addons/website/static/src/js/content/snippets.animation.js index 20d667e3f66..35e77682f1c 100644 --- a/addons/website/static/src/js/content/snippets.animation.js +++ b/addons/website/static/src/js/content/snippets.animation.js @@ -5,6 +5,7 @@ odoo.define('website.content.snippets.animation', function (require) { * Provides a way to start JS code for snippets' initialization and animations. */ +const ajax = require('web.ajax'); var Class = require('web.Class'); var config = require('web.config'); var core = require('web.core'); @@ -596,7 +597,55 @@ registry.Parallax = Animation.extend({ }, }); -registry.mediaVideo = publicWidget.Widget.extend({ +const MobileYoutubeAutoplayMixin = { + /** + * Takes care of any necessary setup for autoplaying video. In practice, + * this method will load the youtube iframe API for mobile environments + * because mobile environments don't support the youtube autoplay param + * passed in the url. + * + * @private + * @param {string} src - The source url of the video + */ + _setupAutoplay: function (src) { + let promise = Promise.resolve(); + + this.isYoutubeVideo = src.indexOf('youtube') >= 0; + this.isMobileEnv = config.device.size_class <= config.device.SIZES.LG && config.device.touch; + + if (this.isYoutubeVideo && this.isMobileEnv && !window.YT) { + const oldOnYoutubeIframeAPIReady = window.onYouTubeIframeAPIReady; + promise = new Promise(resolve => { + window.onYouTubeIframeAPIReady = () => { + if (oldOnYoutubeIframeAPIReady) { + oldOnYoutubeIframeAPIReady(); + } + return resolve(); + }; + }); + ajax.loadJS('https://www.youtube.com/iframe_api'); + } + + return promise; + }, + /** + * @private + * @param {DOMElement} iframeEl - the iframe containing the video player + */ + _triggerAutoplay: function (iframeEl) { + // YouTube does not allow to auto-play video in mobile devices, so we + // have to play the video manually. + if (this.isMobileEnv && this.isYoutubeVideo) { + new window.YT.Player(iframeEl, { + events: { + onReady: ev => ev.target.playVideo(), + } + }); + } + }, +}; + +registry.mediaVideo = publicWidget.Widget.extend(MobileYoutubeAutoplayMixin, { selector: '.media_iframe_video', /** @@ -606,15 +655,36 @@ registry.mediaVideo = publicWidget.Widget.extend({ // TODO: this code should be refactored to make more sense and be better // integrated with Odoo (this refactoring should be done in master). - var def = this._super.apply(this, arguments); - if (this.$target.children('iframe').length) { - // There already is an