From b83f60b7f7f2863c1a8be5593e264583f76e8bb0 Mon Sep 17 00:00:00 2001 From: Raffael Mancini Date: Sat, 30 Nov 2024 16:01:18 +0100 Subject: [PATCH] feat: Implemented HLS stream source as new plugin type. Also implemented show_controls and autoplay attributes on the video element. These are particularly useful when showing a live stream. --- djangocms_video/cms_plugins.py | 31 ++++++++- .../migrations/0012_hlsstreamsource.py | 23 +++++++ ...ayer_autoplay_videoplayer_show_controls.py | 23 +++++++ djangocms_video/models.py | 27 ++++++++ .../default/hls_stream_source.html | 64 +++++++++++++++++++ .../djangocms_video/default/video_player.html | 6 +- 6 files changed, 171 insertions(+), 3 deletions(-) create mode 100644 djangocms_video/migrations/0012_hlsstreamsource.py create mode 100644 djangocms_video/migrations/0013_videoplayer_autoplay_videoplayer_show_controls.py create mode 100644 djangocms_video/templates/djangocms_video/default/hls_stream_source.html diff --git a/djangocms_video/cms_plugins.py b/djangocms_video/cms_plugins.py index ecc9b2d..300e5a9 100644 --- a/djangocms_video/cms_plugins.py +++ b/djangocms_video/cms_plugins.py @@ -10,7 +10,7 @@ class VideoPlayerPlugin(CMSPluginBase): name = _('Video player') text_enabled = True allow_children = True - child_classes = ['VideoSourcePlugin', 'VideoTrackPlugin'] + child_classes = ['VideoSourcePlugin', 'VideoTrackPlugin', 'HlsStreamSourcePlugin'] form = forms.VideoPlayerPluginForm fieldsets = [ @@ -32,6 +32,8 @@ class VideoPlayerPlugin(CMSPluginBase): 'fields': ( 'poster', 'attributes', + 'show_controls', + 'autoplay', ) }) ] @@ -39,6 +41,8 @@ class VideoPlayerPlugin(CMSPluginBase): def render(self, context, instance, placeholder): context = super().render(context, instance, placeholder) context['video_template'] = instance.template + context['show_controls'] = instance.show_controls + context['autoplay'] = instance.autoplay return context def get_render_template(self, context, instance, placeholder): @@ -72,6 +76,30 @@ def get_render_template(self, context, instance, placeholder): return 'djangocms_video/{}/source.html'.format(context.get('video_template', 'default')) +class HlsStreamSourcePlugin(CMSPluginBase): + model = models.HlsStreamSource + name = _('HLS Stream Source') + module = _('Video player') + require_parent = True + parent_classes = ['VideoPlayerPlugin'] + + fieldsets = [ + (None, { + 'fields': ( + 'hls_source_url', + ) + }), + ] + + def render(self, context, instance, placeholder): + context = super().render(context, instance, placeholder) + context['source_id'] = instance.id + return context + + def get_render_template(self, context, instance, placeholder): + return 'djangocms_video/{}/hls_stream_source.html'.format(context.get('video_template', 'default')) + + class VideoTrackPlugin(CMSPluginBase): model = models.VideoTrack name = _('Track') @@ -101,5 +129,6 @@ def get_render_template(self, context, instance, placeholder): plugin_pool.register_plugin(VideoPlayerPlugin) +plugin_pool.register_plugin(HlsStreamSourcePlugin) plugin_pool.register_plugin(VideoSourcePlugin) plugin_pool.register_plugin(VideoTrackPlugin) diff --git a/djangocms_video/migrations/0012_hlsstreamsource.py b/djangocms_video/migrations/0012_hlsstreamsource.py new file mode 100644 index 0000000..80b579f --- /dev/null +++ b/djangocms_video/migrations/0012_hlsstreamsource.py @@ -0,0 +1,23 @@ +# Generated by Django 5.0.9 on 2024-11-30 13:11 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('cms', '0035_auto_20230822_2208_squashed_0036_auto_20240311_1028'), + ('djangocms_video', '0011_alter_videoplayer_cmsplugin_ptr_and_more'), + ] + + operations = [ + migrations.CreateModel( + name='HlsStreamSource', + fields=[ + ('cmsplugin_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, related_name='%(app_label)s_%(class)s', serialize=False, to='cms.cmsplugin')), + ('hls_source_url', models.CharField(max_length=1024, verbose_name='HLS Source URL')), + ], + bases=('cms.cmsplugin',), + ), + ] diff --git a/djangocms_video/migrations/0013_videoplayer_autoplay_videoplayer_show_controls.py b/djangocms_video/migrations/0013_videoplayer_autoplay_videoplayer_show_controls.py new file mode 100644 index 0000000..d91e216 --- /dev/null +++ b/djangocms_video/migrations/0013_videoplayer_autoplay_videoplayer_show_controls.py @@ -0,0 +1,23 @@ +# Generated by Django 5.0.9 on 2024-11-30 14:56 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('djangocms_video', '0012_hlsstreamsource'), + ] + + operations = [ + migrations.AddField( + model_name='videoplayer', + name='autoplay', + field=models.BooleanField(default=False, help_text='If enabled, the video will automatically play once the page is loaded. This might not work depending on how the user has configured their browser.', verbose_name='Autoplay'), + ), + migrations.AddField( + model_name='videoplayer', + name='show_controls', + field=models.BooleanField(default=True, help_text='If enabled, the video will be shown with Play, Pause and Seek elements that allow the user to control playback.', verbose_name='Show controls'), + ), + ] diff --git a/djangocms_video/models.py b/djangocms_video/models.py index 323f0ba..63f4dd1 100644 --- a/djangocms_video/models.py +++ b/djangocms_video/models.py @@ -80,6 +80,23 @@ class VideoPlayer(CMSPlugin): verbose_name=_('Attributes'), blank=True, ) + show_controls = models.BooleanField( + verbose_name=_('Show controls'), + default=True, + help_text=_( + 'If enabled, the video will be shown with Play, Pause and Seek ' + 'elements that allow the user to control playback.' + ), + ) + autoplay = models.BooleanField( + verbose_name=_('Autoplay'), + default=False, + help_text=_( + 'If enabled, the video will automatically play once the page is ' + 'loaded. This might not work depending on how the user has ' + 'configured their browser.' + ), + ) # Add an app namespace to related_name to avoid field name clashes # with any other plugins that have a field with the same name as the @@ -164,6 +181,16 @@ def copy_relations(self, oldinstance): self.source_file = oldinstance.source_file +class HlsStreamSource(CMSPlugin): + """ + Renders the HTML element inside of