From d1f09495b40ad7d044d854235ff523865c36930f Mon Sep 17 00:00:00 2001 From: Ivo Branco Date: Fri, 8 Dec 2023 23:02:15 +0000 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8(video=5Fplayer)=20lazy=20load=20embed?= =?UTF-8?q?=20video=20player?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of loading the external video player, it only loads the video player if the user clicks on the big ▶ icon. --- src/frontend/scss/components/_subheader.scss | 35 ++++++++++ .../djangocms_video/default/video_player.html | 53 ++++++++++++-- .../apps/core/templates/richie/icons.html | 7 +- tests/apps/core/test_videoplayer.py | 69 +++++++++++++++++++ .../test_templates_course_detail_rendering.py | 4 +- 5 files changed, 160 insertions(+), 8 deletions(-) create mode 100644 tests/apps/core/test_videoplayer.py diff --git a/src/frontend/scss/components/_subheader.scss b/src/frontend/scss/components/_subheader.scss index 228aaa76f6..e414bc4bd1 100644 --- a/src/frontend/scss/components/_subheader.scss +++ b/src/frontend/scss/components/_subheader.scss @@ -170,6 +170,41 @@ $r-subheader-search-title-width: 19rem !default; // aligned on computed search r position: relative; padding-bottom: 56.25%; // Aspect ratio 16/9 + .video-player-image { + img { + filter: brightness(0.85); + object-fit: cover; + } + img, + span { + position: absolute; + width: 100%; + top: 0; + bottom: 0; + margin: auto; + } + span { + text-align: center; + font: 48px/1.5 sans-serif; + fill: white; + display: flex; + justify-content: center; + align-items: center; + } + span svg { + transition: 0.5s; + width: 85px; + height: 85px; + } + img:hover, + span:hover svg { + fill-opacity: 1; + filter: drop-shadow(3px 3px 30px rgb(0 0 0 / 0.65)); + } + span svg { + filter: drop-shadow(3px 3px 12px rgb(0 0 0 / 0.25)); + } + } iframe { height: 100%; position: absolute; diff --git a/src/richie/apps/core/templates/djangocms_video/default/video_player.html b/src/richie/apps/core/templates/djangocms_video/default/video_player.html index e13d7b05fb..d5f1d07a90 100644 --- a/src/richie/apps/core/templates/djangocms_video/default/video_player.html +++ b/src/richie/apps/core/templates/djangocms_video/default/video_player.html @@ -1,17 +1,60 @@ -{% load i18n cms_tags %} +{% load i18n cms_tags extra_tags thumbnail static %} {% comment %} This is a copy of original template from plugin just to clean {% with disabled=instance.embed_link %} @@ -32,9 +75,9 @@ {% comment %} # Available variables: - {{ instance.template }} + {{ instance.template }} {{ instance.label }} {{ instance.embed_link }} {{ instance.poster }} - {{ instance.attributes_str }} -{% endcomment %} + {{ instance.attributes_str }} +{% endcomment %} \ No newline at end of file diff --git a/src/richie/apps/core/templates/richie/icons.html b/src/richie/apps/core/templates/richie/icons.html index bf725bbb2f..ae28cb8e67 100644 --- a/src/richie/apps/core/templates/richie/icons.html +++ b/src/richie/apps/core/templates/richie/icons.html @@ -192,6 +192,11 @@ - + + + + + + diff --git a/tests/apps/core/test_videoplayer.py b/tests/apps/core/test_videoplayer.py new file mode 100644 index 0000000000..0b8b12fecf --- /dev/null +++ b/tests/apps/core/test_videoplayer.py @@ -0,0 +1,69 @@ +""" +Test the custom video player with a performance improvement. +""" + +import lxml.html # nosec +from cms.test_utils.testcases import CMSTestCase + +from richie.apps.courses.factories import CourseFactory, VideoSample + + +class CoursesTemplatesCourseDetailRenderingCMSTestCase(CMSTestCase): + """ + Test the custom video player with a performance improvement. + """ + + video_sample_without_image = VideoSample( + "Anant Agarwal: Why massively open online courses (still) matter", + None, + "//www.youtube.com/embed/rYwTA5RA9eU", + ) + + def test_templates_course_detail_teaser_video_cover_empty(self): + """ + When the `course_teaser` placeholder is filled with a VideoPlayerPlugin. + The course page should return an empty video cover image if: + - the video poster image is empty; + - the course page hasn't any `course_cover` placeholder. + """ + video_sample = self.video_sample_without_image + course = CourseFactory(fill_teaser=video_sample, should_publish=True) + + response = self.client.get(course.extended_object.get_absolute_url()) + self.assertEqual(response.status_code, 200) + html = lxml.html.fromstring(response.content) + iframe = html.cssselect(".subheader__teaser .aspect-ratio iframe")[0] + self.assertEqual(iframe.get("data-src"), video_sample.url + "?&autoplay=1") + self.assertEqual(iframe.get("title"), video_sample.label) + self.assertEqual(iframe.get("style"), "display: none;") + self.assertIn("allowfullscreen", iframe.keys()) + # no video cover image + self.assertEqual( + len(html.cssselect(".subheader__teaser .aspect-ratio a img")), 0 + ) + + def test_templates_course_detail_teaser_video_cover_from_course_cover(self): + """ + When the `course_teaser` placeholder is filled with a VideoPlayerPlugin. + The course page show the course cover image if: + - the video poster image is empty; + - the course page has a `course_cover` placeholder. + """ + cover_file_name = cover_file_name = "cover.jpg" + video_sample = self.video_sample_without_image + course = CourseFactory( + fill_teaser=video_sample, + fill_cover={"original_filename": cover_file_name}, + should_publish=True, + ) + + response = self.client.get(course.extended_object.get_absolute_url()) + self.assertEqual(response.status_code, 200) + html = lxml.html.fromstring(response.content) + iframe = html.cssselect(".subheader__teaser .aspect-ratio iframe")[0] + self.assertEqual(iframe.get("data-src"), video_sample.url + "?&autoplay=1") + self.assertEqual(iframe.get("title"), video_sample.label) + self.assertEqual(iframe.get("style"), "display: none;") + self.assertIn("allowfullscreen", iframe.keys()) + img = html.cssselect(".subheader__teaser .aspect-ratio a img")[0] + self.assertIn(cover_file_name, img.get("src")) diff --git a/tests/apps/courses/test_templates_course_detail_rendering.py b/tests/apps/courses/test_templates_course_detail_rendering.py index 17122b8c56..234dbcc282 100644 --- a/tests/apps/courses/test_templates_course_detail_rendering.py +++ b/tests/apps/courses/test_templates_course_detail_rendering.py @@ -595,7 +595,7 @@ def test_templates_course_detail_teaser_video_cover_empty(self): iframe = html.cssselect(".subheader__teaser .aspect-ratio iframe")[0] self.assertIn("allowfullscreen", iframe.keys()) self.assertEqual(iframe.get("title"), video_sample.label) - self.assertEqual(iframe.get("src"), video_sample.url) + self.assertEqual(iframe.get("data-src"), video_sample.url + "?&autoplay=1") def test_templates_course_detail_teaser_empty_cover_image(self): """ @@ -634,7 +634,7 @@ def test_templates_course_detail_teaser_video_cover_image(self): iframe = html.cssselect(".subheader__teaser .aspect-ratio iframe")[0] self.assertIn("allowfullscreen", iframe.keys()) self.assertEqual(iframe.get("title"), video_sample.label) - self.assertEqual(iframe.get("src"), video_sample.url) + self.assertEqual(iframe.get("data-src"), video_sample.url + "?&autoplay=1") # pylint: disable=too-many-public-methods