{"id":13531326,"url":"https://github.com/torchbox/wagtailmedia","last_synced_at":"2025-05-15T01:04:19.913Z","repository":{"id":8354477,"uuid":"56688576","full_name":"torchbox/wagtailmedia","owner":"torchbox","description":"A Wagtail module for managing video and audio files within the admin","archived":false,"fork":false,"pushed_at":"2025-01-29T16:52:42.000Z","size":623,"stargazers_count":238,"open_issues_count":21,"forks_count":72,"subscribers_count":17,"default_branch":"main","last_synced_at":"2025-04-06T19:05:47.483Z","etag":null,"topics":["audio","django","hacktoberfest","streamfield","video","wagtail","wagtail-plugin"],"latest_commit_sha":null,"homepage":"https://pypi.org/project/wagtailmedia/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/torchbox.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-04-20T13:20:38.000Z","updated_at":"2025-03-30T00:42:44.000Z","dependencies_parsed_at":"2023-10-14T15:50:38.183Z","dependency_job_id":"49aba6f2-0f28-459e-b053-223fe384d87c","html_url":"https://github.com/torchbox/wagtailmedia","commit_stats":{"total_commits":444,"total_committers":43,"mean_commits":"10.325581395348838","dds":0.5990990990990991,"last_synced_commit":"7e3277a19871a12ab14d78a9614161c24b5ac935"},"previous_names":[],"tags_count":30,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/torchbox%2Fwagtailmedia","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/torchbox%2Fwagtailmedia/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/torchbox%2Fwagtailmedia/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/torchbox%2Fwagtailmedia/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/torchbox","download_url":"https://codeload.github.com/torchbox/wagtailmedia/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248786321,"owners_count":21161431,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["audio","django","hacktoberfest","streamfield","video","wagtail","wagtail-plugin"],"created_at":"2024-08-01T07:01:02.066Z","updated_at":"2025-05-15T01:04:19.855Z","avatar_url":"https://github.com/torchbox.png","language":"Python","funding_links":[],"categories":["Python","django","Apps"],"sub_categories":["Media"],"readme":"# [wagtailmedia](https://pypi.org/project/wagtailmedia/)\n\n[![PyPI](https://img.shields.io/pypi/v/wagtailmedia.svg)](https://pypi.org/project/wagtailmedia/)\n[![PyPI downloads](https://img.shields.io/pypi/dm/wagtailmedia.svg)](https://pypi.org/project/wagtailmedia/)\n[![Build Status](https://github.com/torchbox/wagtailmedia/workflows/CI/badge.svg)](https://github.com/torchbox/wagtailmedia/actions)\n[![Coverage](https://codecov.io/github/torchbox/wagtailmedia/coverage.svg?branch=master)](https://codecov.io/github/torchbox/wagtailmedia?branch=master)\n[![pre-commit.ci status](https://results.pre-commit.ci/badge/github/torchbox/wagtailmedia/main.svg)](https://results.pre-commit.ci/latest/github/torchbox/wagtailmedia/main)\n\nA module for Wagtail that provides functionality similar to `wagtail.documents` module,\nbut for audio and video files.\n\n## Requirements\n\nwagtailmedia requires the following:\n\n- Python (3.9, 3.10, 3.11, 3.12, 3.13)\n- Django (4.2, 5.1, 5.2)\n- Wagtail (6.3, 6.4, 7.0)\n\n## Install\n\nInstall using pip:\n\n```sh\npip install wagtailmedia\n```\n\n`wagtailmedia` is compatible with Wagtail 4.1 and above. Check out older releases for compatibility with older versions of Wagtail.\n\n### Settings\n\nIn your settings file, add `wagtailmedia` to `INSTALLED_APPS`:\n\n```python\nINSTALLED_APPS = [\n    # ...\n    \"wagtailmedia\",\n    # ...\n]\n```\n\nAll wagtailmedia settings are defined in a single `WAGTAILMEDIA` dictionary in your settings file. The\ndefaults are:\n\n```python\n# settings.py\n\nWAGTAILMEDIA = {\n    \"MEDIA_MODEL\": \"wagtailmedia.Media\",  # string, dotted-notation.\n    \"MEDIA_FORM_BASE\": \"\",  # string, dotted-notation. Defaults to an empty string\n    \"AUDIO_EXTENSIONS\": [\n        \"aac\",\n        \"aiff\",\n        \"flac\",\n        \"m4a\",\n        \"m4b\",\n        \"mp3\",\n        \"ogg\",\n        \"wav\",\n    ],  # list of extensions\n    \"VIDEO_EXTENSIONS\": [\n        \"avi\",\n        \"h264\",\n        \"m4v\",\n        \"mkv\",\n        \"mov\",\n        \"mp4\",\n        \"mpeg\",\n        \"mpg\",\n        \"ogv\",\n        \"webm\",\n    ],  # list of extensions\n}\n```\n\n### URL configuration\n\nYour project needs to be set up to serve user-uploaded files from `MEDIA_ROOT`.\nYour Django project may already have this in place, but if not, add the following snippet to `urls.py`:\n\n```python\nfrom django.conf import settings\nfrom django.conf.urls.static import static\n\nurlpatterns = [\n    # ... the rest of your URLconf goes here ...\n] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)\n```\n\nNote that this only works in development mode (`DEBUG = True`);\nin production, you will need to configure your web server to serve files from `MEDIA_ROOT`.\nFor further details, see the Django documentation: [Serving files uploaded by a user during development](https://docs.djangoproject.com/en/stable/howto/static-files/#serving-files-uploaded-by-a-user-during-development)\nand [Deploying static files](https://docs.djangoproject.com/en/stable/howto/static-files/deployment/).\n\nWith this configuration in place, you are ready to run `./manage.py migrate` to create the database tables used by `wagtailmedia`.\n\n`wagtailmedia` loads additional assets for the chooser panel interface.\nRun `./manage.py collectstatic` after the migrations step to collect all the required assets.\n\n### Custom `Media` model\n\nThe `Media` model can be customised. To do this, you need\nto add a new model to your project that inherits from `wagtailmedia.models.AbstractMedia`.\n\nThen set the `MEDIA_MODEL` attribute in the `WAGTAILMEDIA` settings dictionary to point to it:\n\n```python\n# settings.py\nWAGTAILMEDIA = {\n    \"MEDIA_MODEL\": \"my_app.CustomMedia\",\n    # ...\n}\n```\n\nYou can customize the model form used with your `Media` model using the `MEDIA_FORM_BASE` setting.\nIt 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.\n\n```python\n# settings.py\n\nWAGTAILMEDIA = {\n    \"MEDIA_FORM_BASE\": \"my_app.forms.CustomMediaForm\",\n    # ...\n}\n```\n\n### Hooks\n\n#### `construct_media_chooser_queryset`\n\nCalled when rendering the media chooser view, to allow the media listing QuerySet to be customised.\nThe callable passed into the hook will receive the current media QuerySet and the request object,\nand must return a Media QuerySet (either the original one, or a new one).\n\n```python\nfrom wagtail import hooks\n\n\n@hooks.register(\"construct_media_chooser_queryset\")\ndef show_my_uploaded_media_only(media, request):\n    # Only show uploaded media\n    media = media.filter(uploaded_by_user=request.user)\n\n    return media\n```\n\n## How to use\n\n### As a regular Django field\n\nYou can use `Media` as a regular Django field. Here’s an example:\n\n```python\nfrom django.db import models\n\nfrom wagtail.fields import RichTextField\nfrom wagtail.models import Page\nfrom wagtail.admin.panels import FieldPanel\n\nfrom wagtailmedia.edit_handlers import MediaChooserPanel\n\n\nclass BlogPageWithMedia(Page):\n    author = models.CharField(max_length=255)\n    date = models.DateField(\"Post date\")\n    body = RichTextField(blank=False)\n    featured_media = models.ForeignKey(\n        \"wagtailmedia.Media\",\n        null=True,\n        blank=True,\n        on_delete=models.SET_NULL,\n        related_name=\"+\",\n    )\n\n    content_panels = Page.content_panels + [\n        FieldPanel(\"author\"),\n        FieldPanel(\"date\"),\n        FieldPanel(\"body\"),\n        MediaChooserPanel(\"featured_media\"),\n    ]\n```\n\nThe `MediaChooserPanel` accepts the `media_type` keyword argument (kwarg) to limit the types of media that can be chosen or uploaded.\nAt the moment only \"audio\" (`MediaChooserPanel(media_type=\"audio\")`) and \"video\" (`MediaChooserPanel(media_type=\"audio\")`) are supported,\nand any other type will make the chooser behave as if it did not get any kwarg.\n\n#### Name clash with Wagtail\n\nDo not name the field `media`. When rendering the admin UI, Wagtail uses a `media` property for its fields’ CSS \u0026 JS assets loading.\nUsing `media` as a field name breaks the admin UI ([#54](https://github.com/torchbox/wagtailmedia/issues/54)).\n\n### In StreamField\n\nYou can use `Media` in StreamField. To do this, you need\nto add a new block class that inherits from `wagtailmedia.blocks.AbstractMediaChooserBlock`\nand implement your own `render_basic` method.\n\nHere is an example:\n\n```python\nfrom django.db import models\nfrom django.forms.utils import flatatt\nfrom django.utils.html import format_html, format_html_join\n\nfrom wagtail import blocks\nfrom wagtail.admin.panels import FieldPanel\nfrom wagtail.fields import StreamField\nfrom wagtail.models import Page\n\nfrom wagtailmedia.blocks import AbstractMediaChooserBlock\n\n\nclass TestMediaBlock(AbstractMediaChooserBlock):\n    def render_basic(self, value, context=None):\n        if not value:\n            return \"\"\n\n        if value.type == \"video\":\n            player_code = \"\"\"\n            \u003cdiv\u003e\n                \u003cvideo width=\"{1}\" height=\"{2}\" controls\u003e\n                    {0}\n                    Your browser does not support the video tag.\n                \u003c/video\u003e\n            \u003c/div\u003e\n            \"\"\"\n        else:\n            player_code = \"\"\"\n            \u003cdiv\u003e\n                \u003caudio controls\u003e\n                    {0}\n                    Your browser does not support the audio element.\n                \u003c/audio\u003e\n            \u003c/div\u003e\n            \"\"\"\n\n        return format_html(\n            player_code,\n            format_html_join(\n                \"\\n\", \"\u003csource{0}\u003e\", [[flatatt(s)] for s in value.sources]\n            ),\n            value.width,\n            value.height,\n        )\n\n\nclass BlogPage(Page):\n    author = models.CharField(max_length=255)\n    date = models.DateField(\"Post date\")\n    body = StreamField(\n        [\n            (\"heading\", blocks.CharBlock(classname=\"title\", icon=\"title\")),\n            (\"paragraph\", blocks.RichTextBlock(icon=\"pilcrow\")),\n            (\"media\", TestMediaBlock(icon=\"media\")),\n        ]\n    )\n\n    content_panels = Page.content_panels + [\n        FieldPanel(\"author\"),\n        FieldPanel(\"date\"),\n        FieldPanel(\"body\"),\n    ]\n```\n\nYou can also use audio or video-specific choosers:\n\n```python\n# ...\nfrom wagtail.models import Page\nfrom wagtail.fields import StreamField\nfrom wagtailmedia.blocks import AudioChooserBlock, VideoChooserBlock\n\n\nclass BlogPage(Page):\n    # ...\n\n    body = StreamField(\n        [\n            # ... other block definitions\n            (\"audio\", AudioChooserBlock()),\n            (\"video\", VideoChooserBlock()),\n        ]\n    )\n```\n\n### API\n\nTo expose media items in the API, you can follow the [Wagtail documentation guide](https://docs.wagtail.org/en/stable/advanced_topics/api/v2/configuration.html#api-v2-configuration)\nfor API configuration with wagtailmedia specifics:\n\n```python\n# api.py\nfrom wagtail.api.v2.router import WagtailAPIRouter\nfrom wagtailmedia.api.views import MediaAPIViewSet\n\n\n# Register the router\napi_router = WagtailAPIRouter(\"wagtailapi\")\n# add any other enpoints you need, plus the wagtailmedia one\napi_router.register_endpoint(\"media\", MediaAPIViewSet)\n```\n\n## Translations\n\nwagtailmedia has translations in French and Chinese. More translations welcome!\n\n## Contributing\n\nAll contributions are welcome!\n\n## Upgrading\n\nWhen 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.\n\nThe following templates should be checked:\n\n- `src/wagtailmedia/templates/wagtailmedia/media/add.html`\n- `src/wagtailmedia/templates/wagtailmedia/media/confirm_delete.html`\n- `src/wagtailmedia/templates/wagtailmedia/media/edit.html`\n- `src/wagtailmedia/templates/wagtailmedia/media/index.html`\n- `src/wagtailmedia/templates/wagtailmedia/media/media_chooser.html`\n- `src/wagtailmedia/templates/wagtailmedia/media/media_permissions_formset.html`\n- `src/wagtailmedia/templates/wagtailmedia/media/usage.html`\n\n### Install\n\nTo make changes to this project, first clone this repository:\n\n```sh\ngit clone git@github.com:torchbox/wagtailmedia.git\ncd wagtailmedia\n```\n\nWith your preferred virtualenv activated, install testing dependencies:\n\n```sh\npip install -e '.[testing]' -U\n```\n\n### pre-commit\n\nNote that this project uses [pre-commit](https://github.com/pre-commit/pre-commit). To set up locally:\n\n```shell\n# if you don't have it yet, globally\n$ pip install pre-commit\n# go to the project directory\n$ cd wagtailmedia\n# initialize pre-commit\n$ pre-commit install\n\n# Optional, run all checks once for this, then the checks will run only on the changed files\n$ pre-commit run --all-files\n```\n\n### How to run tests\n\nNow you can run tests as shown below:\n\n```sh\ntox\n```\n\nor, you can run them for a specific environment `tox -e py313-dj51-wagtail70` or specific test\n`tox -e py313-dj51-wagtail70 -- tests.test_views.TestMediaChooserUploadView`\n\nTo run the test app interactively, use `tox -e interactive`, visit `http://127.0.0.1:8020/admin/` and log in with `admin`/`changeme`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftorchbox%2Fwagtailmedia","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftorchbox%2Fwagtailmedia","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftorchbox%2Fwagtailmedia/lists"}