A module for Wagtail that provides functionality similar to wagtail.documents
module,
but for audio and video files.
wagtailmedia requires the following:
- Python (3.9, 3.10, 3.11, 3.12, 3.13)
- Django (4.2, 5.0, 5.1)
- Wagtail (5.2, 6.2, 6.3)
Install using pip:
pip install wagtailmedia
wagtailmedia
is compatible with Wagtail 4.1 and above. Check out older releases for compatibility with older versions of Wagtail.
In your settings file, add wagtailmedia
to INSTALLED_APPS
:
INSTALLED_APPS = [
# ...
"wagtailmedia",
# ...
]
All wagtailmedia settings are defined in a single WAGTAILMEDIA
dictionary in your settings file. The
defaults are:
# settings.py
WAGTAILMEDIA = {
"MEDIA_MODEL": "wagtailmedia.Media", # string, dotted-notation.
"MEDIA_FORM_BASE": "", # string, dotted-notation. Defaults to an empty string
"AUDIO_EXTENSIONS": [
"aac",
"aiff",
"flac",
"m4a",
"m4b",
"mp3",
"ogg",
"wav",
], # list of extensions
"VIDEO_EXTENSIONS": [
"avi",
"h264",
"m4v",
"mkv",
"mov",
"mp4",
"mpeg",
"mpg",
"ogv",
"webm",
], # list of extensions
}
Your project needs to be set up to serve user-uploaded files from MEDIA_ROOT
.
Your Django project may already have this in place, but if not, add the following snippet to urls.py
:
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [
# ... the rest of your URLconf goes here ...
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
Note that this only works in development mode (DEBUG = True
);
in production, you will need to configure your web server to serve files from MEDIA_ROOT
.
For further details, see the Django documentation: Serving files uploaded by a user during development
and Deploying static files.
With this configuration in place, you are ready to run ./manage.py migrate
to create the database tables used by wagtailmedia
.
wagtailmedia
loads additional assets for the chooser panel interface.
Run ./manage.py collectstatic
after the migrations step to collect all the required assets.
The Media
model can be customised. To do this, you need
to add a new model to your project that inherits from wagtailmedia.models.AbstractMedia
.
Then set the MEDIA_MODEL
attribute in the WAGTAILMEDIA
settings dictionary to point to it:
# settings.py
WAGTAILMEDIA = {
"MEDIA_MODEL": "my_app.CustomMedia",
# ...
}
You can customize the model form used with your Media
model using the MEDIA_FORM_BASE
setting.
It should be the dotted path to the form and will be used as the base form passed to modelform_factory()
when constructing the media form.
# settings.py
WAGTAILMEDIA = {
"MEDIA_FORM_BASE": "my_app.forms.CustomMediaForm",
# ...
}
Called when rendering the media chooser view, to allow the media listing QuerySet to be customised. The callable passed into the hook will receive the current media QuerySet and the request object, and must return a Media QuerySet (either the original one, or a new one).
from wagtail import hooks
@hooks.register("construct_media_chooser_queryset")
def show_my_uploaded_media_only(media, request):
# Only show uploaded media
media = media.filter(uploaded_by_user=request.user)
return media
You can use Media
as a regular Django field. Here’s an example:
from django.db import models
from wagtail.fields import RichTextField
from wagtail.models import Page
from wagtail.admin.panels import FieldPanel
from wagtailmedia.edit_handlers import MediaChooserPanel
class BlogPageWithMedia(Page):
author = models.CharField(max_length=255)
date = models.DateField("Post date")
body = RichTextField(blank=False)
featured_media = models.ForeignKey(
"wagtailmedia.Media",
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name="+",
)
content_panels = Page.content_panels + [
FieldPanel("author"),
FieldPanel("date"),
FieldPanel("body"),
MediaChooserPanel("featured_media"),
]
The MediaChooserPanel
accepts the media_type
keyword argument (kwarg) to limit the types of media that can be chosen or uploaded.
At the moment only "audio" (MediaChooserPanel(media_type="audio")
) and "video" (MediaChooserPanel(media_type="audio")
) are supported,
and any other type will make the chooser behave as if it did not get any kwarg.
Do not name the field media
. When rendering the admin UI, Wagtail uses a media
property for its fields’ CSS & JS assets loading.
Using media
as a field name breaks the admin UI (#54).
You can use Media
in StreamField. To do this, you need
to add a new block class that inherits from wagtailmedia.blocks.AbstractMediaChooserBlock
and implement your own render_basic
method.
Here is an example:
from django.db import models
from django.forms.utils import flatatt
from django.utils.html import format_html, format_html_join
from wagtail import blocks
from wagtail.admin.panels import FieldPanel
from wagtail.fields import StreamField
from wagtail.models import Page
from wagtailmedia.blocks import AbstractMediaChooserBlock
class TestMediaBlock(AbstractMediaChooserBlock):
def render_basic(self, value, context=None):
if not value:
return ""
if value.type == "video":
player_code = """
<div>
<video width="{1}" height="{2}" controls>
{0}
Your browser does not support the video tag.
</video>
</div>
"""
else:
player_code = """
<div>
<audio controls>
{0}
Your browser does not support the audio element.
</audio>
</div>
"""
return format_html(
player_code,
format_html_join(
"\n", "<source{0}>", [[flatatt(s)] for s in value.sources]
),
value.width,
value.height,
)
class BlogPage(Page):
author = models.CharField(max_length=255)
date = models.DateField("Post date")
body = StreamField(
[
("heading", blocks.CharBlock(classname="title", icon="title")),
("paragraph", blocks.RichTextBlock(icon="pilcrow")),
("media", TestMediaBlock(icon="media")),
]
)
content_panels = Page.content_panels + [
FieldPanel("author"),
FieldPanel("date"),
FieldPanel("body"),
]
You can also use audio or video-specific choosers:
# ...
from wagtail.models import Page
from wagtail.fields import StreamField
from wagtailmedia.blocks import AudioChooserBlock, VideoChooserBlock
class BlogPage(Page):
# ...
body = StreamField(
[
# ... other block definitions
("audio", AudioChooserBlock()),
("video", VideoChooserBlock()),
]
)
To expose media items in the API, you can follow the Wagtail documentation guide for API configuration with wagtailmedia specifics:
# api.py
from wagtail.api.v2.router import WagtailAPIRouter
from wagtailmedia.api.views import MediaAPIViewSet
# Register the router
api_router = WagtailAPIRouter("wagtailapi")
# add any other enpoints you need, plus the wagtailmedia one
api_router.register_endpoint("media", MediaAPIViewSet)
wagtailmedia has translations in French and Chinese. More translations welcome!
All contributions are welcome!
When upgrading the Wagtail version, it is good practice to also check that the template styles and formatting are up-to-date with the current supported version of Wagtail.
The following templates should be checked:
src/wagtailmedia/templates/wagtailmedia/media/add.html
src/wagtailmedia/templates/wagtailmedia/media/confirm_delete.html
src/wagtailmedia/templates/wagtailmedia/media/edit.html
src/wagtailmedia/templates/wagtailmedia/media/index.html
src/wagtailmedia/templates/wagtailmedia/media/media_chooser.html
src/wagtailmedia/templates/wagtailmedia/media/media_permissions_formset.html
src/wagtailmedia/templates/wagtailmedia/media/usage.html
To make changes to this project, first clone this repository:
git clone [email protected]:torchbox/wagtailmedia.git
cd wagtailmedia
With your preferred virtualenv activated, install testing dependencies:
pip install -e '.[testing]' -U
Note that this project uses pre-commit. To set up locally:
# if you don't have it yet, globally
$ pip install pre-commit
# go to the project directory
$ cd wagtailmedia
# initialize pre-commit
$ pre-commit install
# Optional, run all checks once for this, then the checks will run only on the changed files
$ pre-commit run --all-files
Now you can run tests as shown below:
tox
or, you can run them for a specific environment tox -e py310-dj41-wagtail41
or specific test
tox -e py310-dj41-wagtail41 -- tests.test_views.TestMediaChooserUploadView
To run the test app interactively, use tox -e interactive
, visit http://127.0.0.1:8020/admin/
and log in with admin
/changeme
.