diff --git a/addons/website_event_track_live/__init__.py b/addons/website_event_track_live/__init__.py index 67dee8c60db..7d34c7c054a 100644 --- a/addons/website_event_track_live/__init__.py +++ b/addons/website_event_track_live/__init__.py @@ -1,2 +1,5 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. + +from . import controllers +from . import models diff --git a/addons/website_event_track_live/__manifest__.py b/addons/website_event_track_live/__manifest__.py index 98e2c69bc9a..8f137e270e2 100644 --- a/addons/website_event_track_live/__manifest__.py +++ b/addons/website_event_track_live/__manifest__.py @@ -15,8 +15,16 @@ 'website_event_track_session', ], 'data': [ + 'views/assets.xml', + 'views/event_track_templates_list.xml', + 'views/event_track_templates_page.xml', + 'views/event_track_views.xml', ], 'demo': [ + 'data/event_track_demo.xml' + ], + 'qweb': [ + 'static/src/xml/website_event_track_live_templates.xml' ], 'application': False, 'installable': True, diff --git a/addons/website_event_track_live/controllers/__init__.py b/addons/website_event_track_live/controllers/__init__.py new file mode 100644 index 00000000000..df58fe9572a --- /dev/null +++ b/addons/website_event_track_live/controllers/__init__.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from . import track_live +from . import session diff --git a/addons/website_event_track_live/controllers/session.py b/addons/website_event_track_live/controllers/session.py new file mode 100644 index 00000000000..9d6381e6300 --- /dev/null +++ b/addons/website_event_track_live/controllers/session.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +import re + +from odoo.addons.website_event_track_session.controllers.session import WebsiteEventSessionController +from odoo.http import request + + +class WebsiteEventSessionLiveController(WebsiteEventSessionController): + + def _event_track_page_get_values(self, event, track, **options): + if 'widescreen' not in options: + options['widescreen'] = bool(track.youtube_video_url) + values = super(WebsiteEventSessionLiveController, self)._event_track_page_get_values(event, track, **options) + # Youtube disables the chat embed on all mobile devices + # This regex is a naive attempt at matching their behavior (should work for most cases) + values['is_mobile_chat_disabled'] = bool(re.match( + r'^.*(Android|iPad|iPhone).*', + request.httprequest.headers.get('User-Agent', request.httprequest.headers.get('user-agent', '')))) + return values diff --git a/addons/website_event_track_live/controllers/track_live.py b/addons/website_event_track_live/controllers/track_live.py new file mode 100644 index 00000000000..d85d3d13c79 --- /dev/null +++ b/addons/website_event_track_live/controllers/track_live.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo import http + +from odoo.addons.website_event_track_online.controllers.event_track import EventTrackOnlineController + + +class EventTrackLiveController(EventTrackOnlineController): + + @http.route('/event_track/get_track_suggestion', type='json', auth='public', website=True) + def get_next_track_suggestion(self, track_id): + track = self._fetch_track(track_id) + track_suggestion = track._get_track_suggestions( + restrict_domain=[('youtube_video_url', '!=', False), ('is_published', '=', True)], + limit=1) + if not track_suggestion: + return False + track_suggestion_sudo = track_suggestion.sudo() + track_sudo = track.sudo() + return self._prepare_track_suggestion_values(track_sudo, track_suggestion_sudo) + + def _prepare_track_suggestion_values(self, track, track_suggestion): + return { + 'current_track': { + 'name': track.name, + 'website_image_url': track.website_image_url, + }, + 'suggestion': { + 'id': track_suggestion.id, + 'name': track_suggestion.name, + 'speaker_name': track_suggestion.partner_name, + 'website_url': track_suggestion.website_url + } + } diff --git a/addons/website_event_track_live/data/event_track_demo.xml b/addons/website_event_track_live/data/event_track_demo.xml new file mode 100644 index 00000000000..79e01e8c5f0 --- /dev/null +++ b/addons/website_event_track_live/data/event_track_demo.xml @@ -0,0 +1,46 @@ + + + + + https://www.youtube.com/watch?v=66AIsiezq5g + + + + https://www.youtube.com/watch?v=IvUU8joBb1Q + + + + https://www.youtube.com/watch?v=qRrA8al77a0 + + + + https://www.youtube.com/watch?v=bZ-queeu-lk + + + + https://www.youtube.com/watch?v=gV3g5FzmQiM + + + + https://www.youtube.com/watch?v=g3IoL9hxABg + + + + + https://www.youtube.com/watch?v=AVZtyXJB-rI + + + + https://www.youtube.com/watch?v=LHztthKy4RE + + + + https://www.youtube.com/watch?v=B-nEYsyRlYo + + + + https://www.youtube.com/watch?v=7X8xuipr4_A + + + + diff --git a/addons/website_event_track_live/models/__init__.py b/addons/website_event_track_live/models/__init__.py new file mode 100644 index 00000000000..e3798014924 --- /dev/null +++ b/addons/website_event_track_live/models/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from . import event_track diff --git a/addons/website_event_track_live/models/event_track.py b/addons/website_event_track_live/models/event_track.py new file mode 100644 index 00000000000..7765bfa455b --- /dev/null +++ b/addons/website_event_track_live/models/event_track.py @@ -0,0 +1,42 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +import re + +from odoo import api, fields, models + + +class Track(models.Model): + _inherit = 'event.track' + + youtube_video_url = fields.Char('Youtube Video URL', + help="Configure this URL so that event attendees can see your Track in video!") + youtube_video_id = fields.Char('Youtube video ID', compute='_compute_youtube_video_id', + help="Extracted from the video URL and used to infer various links (embed/thumbnail/...)") + is_youtube_replay = fields.Boolean('Is Youtube Replay', + help="Check this option if the video is already available on Youtube to avoid showing 'Direct' options (Chat, ...)") + is_youtube_chat_available = fields.Boolean('Is Chat Available', compute='_compute_is_youtube_chat_available') + + @api.depends('youtube_video_url') + def _compute_youtube_video_id(self): + for track in self: + if track.youtube_video_url: + regex = r'^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|&v=)([^#&?]*).*' + match = re.match(regex, track.youtube_video_url) + if match and len(match.groups()) == 2 and len(match.group(2)) == 11: + track.youtube_video_id = match.group(2) + + if not track.youtube_video_id: + track.youtube_video_id = False + + @api.depends('youtube_video_id', 'is_youtube_replay', 'date_end', 'is_track_done') + def _compute_website_image_url(self): + youtube_thumbnail_tracks = self.filtered(lambda track: not track.website_image and track.youtube_video_id) + super(Track, self - youtube_thumbnail_tracks)._compute_website_image_url() + for track in youtube_thumbnail_tracks: + track.website_image_url = f'https://img.youtube.com/vi/{track.youtube_video_id}/maxresdefault.jpg' + + @api.depends('youtube_video_url', 'is_youtube_replay', 'date', 'date_end', 'is_track_upcoming', 'is_track_live') + def _compute_is_youtube_chat_available(self): + for track in self: + track.is_youtube_chat_available = track.youtube_video_url and not track.is_youtube_replay and (track.is_track_soon or track.is_track_live) diff --git a/addons/website_event_track_live/static/src/js/website_event_track_live.js b/addons/website_event_track_live/static/src/js/website_event_track_live.js new file mode 100644 index 00000000000..a330308de63 --- /dev/null +++ b/addons/website_event_track_live/static/src/js/website_event_track_live.js @@ -0,0 +1,121 @@ +var onYouTubeIframeAPIReady; + +odoo.define('website_event_track_live.website_event_youtube_embed', function (require) { +'use strict'; + +var publicWidget = require('web.public.widget'); +var TrackSuggestionWidget = require('website_event_track_live.website_event_track_suggestion'); + +var YOUTUBE_VIDEO_ENDED = 0; +var YOUTUBE_VIDEO_PLAYING = 1; +var YOUTUBE_VIDEO_PAUSED = 2; + +publicWidget.registry.websiteEventTrackLive = publicWidget.Widget.extend({ + selector: '.o_wevent_event_track_live', + custom_events: _.extend({}, publicWidget.Widget.prototype.custom_events, { + 'video-ended': '_onVideoEnded' + }), + + start: function () { + var self = this; + return this._super(...arguments).then(function () { + self._setupYoutubePlayer(); + self.isFullScreen = !!document.fullscreenElement; + }); + }, + + //-------------------------------------------------------------------------- + // Handlers + //-------------------------------------------------------------------------- + + _onPlayerReady: function () { + $(window).on('resize', this._onResize.bind(this)); + this.$('.o_wevent_event_track_live_loading').remove(); + }, + + _onPlayerStateChange: function (event) { + if (event.data === YOUTUBE_VIDEO_ENDED) { + this.trigger('video-ended'); + } else if (event.data === YOUTUBE_VIDEO_PLAYING) { + this.trigger('video-playing'); + } else if (event.data === YOUTUBE_VIDEO_PAUSED) { + this.trigger('video-paused'); + } + }, + + _onVideoEnded: function () { + var self = this; + this._rpc({ + route: '/event_track/get_track_suggestion', + params: { + track_id: this.$el.data('trackId'), + } + }).then(function (suggestion) { + self.nextSuggestion = suggestion; + self._showSuggestion(); + }); + }, + + _onReplay: function () { + this.youtubePlayer.seekTo(0); + this.youtubePlayer.playVideo(); + if (this.trackSuggestion) { + delete this.trackSuggestion; + } + }, + + /** + * The 'fullscreenchange' event is probably a better fit but it is unfortunately + * not triggered when Youtube enters fullscreen mode. + * However, the global window 'resize' is. + */ + _onResize: function () { + this.isFullScreen = !!document.fullscreenElement; + this._showSuggestion(); + }, + + //-------------------------------------------------------------------------- + // Private + //-------------------------------------------------------------------------- + + _setupYoutubePlayer: function () { + var self = this; + + var youtubeId = self.$el.data('youtubeVideoId'); + var $youtubeElement = $(' + + + + + diff --git a/addons/website_event_track_live/views/event_track_templates_list.xml b/addons/website_event_track_live/views/event_track_templates_list.xml new file mode 100644 index 00000000000..b2fc5243af7 --- /dev/null +++ b/addons/website_event_track_live/views/event_track_templates_list.xml @@ -0,0 +1,22 @@ + + + + + + diff --git a/addons/website_event_track_live/views/event_track_templates_page.xml b/addons/website_event_track_live/views/event_track_templates_page.xml new file mode 100644 index 00000000000..f72152f6e71 --- /dev/null +++ b/addons/website_event_track_live/views/event_track_templates_page.xml @@ -0,0 +1,53 @@ + + + + + + + + + + +