diff --git a/package-lock.json b/package-lock.json index 4d91e3d..8447f1e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,8 @@ "@fortawesome/fontawesome-free": "^6.1.1", "bulma": "^0.9.4", "darkreader": "^4.9.51", - "elevator.js": "^1.0.1" + "elevator.js": "^1.0.1", + "lite-youtube-embed": "^0.2.0" }, "devDependencies": { "esbuild": "^0.14.43", @@ -1070,6 +1071,11 @@ "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", "dev": true }, + "node_modules/lite-youtube-embed": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/lite-youtube-embed/-/lite-youtube-embed-0.2.0.tgz", + "integrity": "sha512-XXXAk5sbvtjjwbie3XG+6HppgTm1HTGL/Uk9z9NkJH53o7puZLur434heHzAjkS60hZB3vT4ls25zl5rMiX4EA==" + }, "node_modules/load-json-file": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", @@ -2267,6 +2273,11 @@ "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", "dev": true }, + "lite-youtube-embed": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/lite-youtube-embed/-/lite-youtube-embed-0.2.0.tgz", + "integrity": "sha512-XXXAk5sbvtjjwbie3XG+6HppgTm1HTGL/Uk9z9NkJH53o7puZLur434heHzAjkS60hZB3vT4ls25zl5rMiX4EA==" + }, "load-json-file": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", diff --git a/package.json b/package.json index aaa0bf7..a022b23 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "@fortawesome/fontawesome-free": "^6.1.1", "bulma": "^0.9.4", "darkreader": "^4.9.51", - "elevator.js": "^1.0.1" + "elevator.js": "^1.0.1", + "lite-youtube-embed": "^0.2.0" } } diff --git a/static/src/js/lite-youtube-embed.js b/static/src/js/lite-youtube-embed.js new file mode 100644 index 0000000..685f5c0 --- /dev/null +++ b/static/src/js/lite-youtube-embed.js @@ -0,0 +1 @@ +require("lite-youtube-embed"); diff --git a/static/src/scss/_blocks.scss b/static/src/scss/_blocks.scss index 3f016c2..611737b 100644 --- a/static/src/scss/_blocks.scss +++ b/static/src/scss/_blocks.scss @@ -7,3 +7,11 @@ div.block-image { .content > div:not(:last-child) { margin-bottom: $content-block-margin-bottom; } + +div.block-embed { + lite-youtube { + max-width: $embed_width; + min-width: $embed_width; + margin: 0 auto; + } +} diff --git a/static/src/scss/_variables.scss b/static/src/scss/_variables.scss index e69de29..667c787 100644 --- a/static/src/scss/_variables.scss +++ b/static/src/scss/_variables.scss @@ -0,0 +1 @@ +$embed_width: 85%; diff --git a/static/src/scss/lite-youtube-embed.scss b/static/src/scss/lite-youtube-embed.scss new file mode 100644 index 0000000..62c1ebc --- /dev/null +++ b/static/src/scss/lite-youtube-embed.scss @@ -0,0 +1 @@ +@import "lite-youtube-embed/src/lite-yt-embed"; diff --git a/templates/base.html b/templates/base.html index c0a4763..a8c96bd 100644 --- a/templates/base.html +++ b/templates/base.html @@ -16,6 +16,7 @@ + {% block extra_css %}{% endblock %} @@ -40,6 +41,7 @@ + {% block darkmode %} diff --git a/website/common/embed.py b/website/common/embed.py new file mode 100644 index 0000000..a2c0863 --- /dev/null +++ b/website/common/embed.py @@ -0,0 +1,38 @@ +import re + +from django.utils.html import format_html +from wagtail.embeds.finders.oembed import OEmbedFinder +from wagtail.embeds.oembed_providers import youtube + + +class YouTubeLiteEmbedFinder(OEmbedFinder): + """ + A modified OEmbed finder uses lite-youtube-embed instead + + https://github.com/paulirish/lite-youtube-embed + """ + + EMBED_ID_RE = re.compile(r"\/embed\/(.*?)\?") + + def __init__( + self, providers: list[dict] | None = None, options: dict | None = None + ): + super().__init__(providers=[youtube], options=options) + + @classmethod + def _get_video_id(cls, html: str) -> str: + matched = cls.EMBED_ID_RE.search(html) + if matched is None: + raise ValueError(f"Unable to find video id in {html}") + return matched.group(1) + + def find_embed(self, *args: list, **kwargs: dict) -> dict: + result = super().find_embed(*args, **kwargs) + video_id = self._get_video_id(result["html"]) + result["html"] = format_html( + "", + video_id, + result["title"], + result["thumbnail_url"], + ) + return result diff --git a/website/common/tests.py b/website/common/tests.py index 8bbfa72..660a769 100644 --- a/website/common/tests.py +++ b/website/common/tests.py @@ -1,5 +1,6 @@ from django.test import SimpleTestCase +from .embed import YouTubeLiteEmbedFinder from .models import BasePage from .utils import get_page_models @@ -15,3 +16,15 @@ class BasePageTestCase(SimpleTestCase): issubclass(page_model, BasePage), f"{page_model} does not inherit from {BasePage}.", ) + + +class YouTubeLiteEmbedFinderTestCase(SimpleTestCase): + def test_finds_video_id(self) -> None: + self.assertEqual( + YouTubeLiteEmbedFinder._get_video_id( + '' + ), + "dQw4w9WgXcQ", + ) + with self.assertRaises(ValueError): + YouTubeLiteEmbedFinder._get_video_id("something-else") diff --git a/website/settings.py b/website/settings.py index 819cf90..55ee0a1 100644 --- a/website/settings.py +++ b/website/settings.py @@ -144,6 +144,16 @@ DEFAULT_AUTO_FIELD = "django.db.models.AutoField" WAGTAILIMAGES_IMAGE_MODEL = "images.CustomImage" +WAGTAILEMBEDS_FINDERS = [ + { + "class": "website.common.embed.YouTubeLiteEmbedFinder", + }, + { + "class": "wagtail.embeds.finders.oembed", + }, +] + + if DEBUG: # Add django-browser-reload INSTALLED_APPS.append("django_browser_reload")