Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
Websites UFRPE
Wagtail Videos
Commits
cc90aa21
Unverified
Commit
cc90aa21
authored
Jan 28, 2021
by
seb-b
Committed by
GitHub
Jan 28, 2021
Browse files
Merge pull request #52 from neon-jungle/custom_video_model
Added ability to change the video model
parents
6d7c3bd8
392515fa
Changes
17
Hide whitespace changes
Inline
Side-by-side
.gitlab-ci.yml
View file @
cc90aa21
...
...
@@ -27,4 +27,15 @@ lts_211:
image
:
python:3.8
extends
:
.python_test
before_script
:
-
pip install .['testing'] wagtail~=2.11 django~=3.1
\ No newline at end of file
-
pip install .['testing'] wagtail~=2.11 django~=3.1
build
:
image
:
python:3.8
stage
:
release
before_script
:
-
pip install --upgrade setuptools wheel twine
script
:
-
./setup.py sdist bdist_wheel
-
twine upload dist/*
only
:
-
tags
README.rst
View file @
cc90aa21
...
...
@@ -43,6 +43,7 @@ Implement as a ``ForeignKey`` relation, same as wagtailimages.
from wagtailvideos.edit_handlers import VideoChooserPanel
class HomePage(Page):
body = RichtextField()
header_video = models.ForeignKey('wagtailvideos.Video',
...
...
@@ -68,6 +69,7 @@ A VideoChooserBlock is included
from wagtailvideos.blocks import VideoChooserBlock
class ContentPage(Page):
body = StreamField([
('video', VideoChooserBlock()),
...
...
@@ -90,6 +92,8 @@ tag. The original video and all extra transcodes are added as
{% load wagtailvideos_tags %}
{% video self.header_video autoplay controls width=256 %}
Jinja2 extensions are also included.
How to transcode using ffmpeg:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
...
...
@@ -99,8 +103,46 @@ be used to create new transcodes. It is assumed that your compiled
version of ffmpeg has the matching codec libraries required for the
transcode.
Custom Video models:
~~~~~~~~~~~~~~~~~~~~
Same as Wagtail Images, a custom model can be used to replace the built in Video model using the
``WAGTAILVIDEOS_VIDEO_MODEL`` setting.
.. code:: django
# settings.py
WAGTAILVIDEOS_VIDEO_MODEL = 'videos.AttributedVideo'
# app.videos.models
from django.db import models
from wagtailvideos.models import AbstractVideo, AbstractVideoTranscode
class AttributedVideo(AbstractVideo):
attribution = models.TextField()
admin_form_fields = (
'title',
'attribution',
'file',
'collection',
'thumbnail',
'tags',
)
class CustomTranscode(AbstractVideoTranscode):
video = models.ForeignKey(AttributedVideo, related_name='transcodes', on_delete=models.CASCADE)
class Meta:
unique_together = (
('video', 'media_format')
)
Future features
---------------
- Some docs
- Richtext embed
- Transcoding via amazon service rather than ffmpeg
settings.py
View file @
cc90aa21
...
...
@@ -10,3 +10,5 @@ DATABASES = {
INSTALLED_APPS
+=
[
'wagtail.contrib.styleguide'
,
]
WAGTAILVIDEOS_VIDEO_MODEL
=
'app.CustomVideoModel'
\ No newline at end of file
tests/app/migrations/0001_initial.py
View file @
cc90aa21
# -*- coding: utf-8 -*-
# Generated by Django 1.9.7 on 2016-07-11 02:19
from
__future__
import
unicode_literals
# Generated by Django 2.2.17 on 2021-01-28 00:22
import
django.
db.models.deletion
from
django.
conf
import
settings
from
django.db
import
migrations
,
models
import
django.db.models.deletion
import
enumchoicefield.fields
import
taggit.managers
import
wagtail.core.fields
import
wagtail.core.models
import
wagtail.search.index
import
wagtailvideos.blocks
import
wagtailvideos.models
class
Migration
(
migrations
.
Migration
):
...
...
@@ -11,15 +17,39 @@ class Migration(migrations.Migration):
initial
=
True
dependencies
=
[
(
'wagtailcore'
,
'0028_merge'
),
(
'wagtailvideos'
,
'0007_video_duration'
),
(
'taggit'
,
'0003_taggeditem_add_unique_index'
),
migrations
.
swappable_dependency
(
settings
.
AUTH_USER_MODEL
),
(
'wagtailvideos'
,
'0010_video_ordering'
),
(
'wagtailcore'
,
'0059_apply_collection_ordering'
),
]
operations
=
[
migrations
.
CreateModel
(
name
=
'CustomVideoModel'
,
fields
=
[
(
'id'
,
models
.
AutoField
(
auto_created
=
True
,
primary_key
=
True
,
serialize
=
False
,
verbose_name
=
'ID'
)),
(
'title'
,
models
.
CharField
(
max_length
=
255
,
verbose_name
=
'title'
)),
(
'file'
,
models
.
FileField
(
upload_to
=
wagtailvideos
.
models
.
get_upload_to
,
verbose_name
=
'file'
)),
(
'thumbnail'
,
models
.
ImageField
(
blank
=
True
,
null
=
True
,
upload_to
=
wagtailvideos
.
models
.
get_upload_to
)),
(
'created_at'
,
models
.
DateTimeField
(
auto_now_add
=
True
,
db_index
=
True
,
verbose_name
=
'created at'
)),
(
'duration'
,
models
.
DurationField
(
blank
=
True
,
null
=
True
)),
(
'file_size'
,
models
.
PositiveIntegerField
(
editable
=
False
,
null
=
True
)),
(
'attribution'
,
models
.
TextField
(
blank
=
True
)),
(
'collection'
,
models
.
ForeignKey
(
default
=
wagtail
.
core
.
models
.
get_root_collection_id
,
on_delete
=
django
.
db
.
models
.
deletion
.
CASCADE
,
related_name
=
'+'
,
to
=
'wagtailcore.Collection'
,
verbose_name
=
'collection'
)),
(
'tags'
,
taggit
.
managers
.
TaggableManager
(
blank
=
True
,
help_text
=
None
,
through
=
'taggit.TaggedItem'
,
to
=
'taggit.Tag'
,
verbose_name
=
'tags'
)),
(
'uploaded_by_user'
,
models
.
ForeignKey
(
blank
=
True
,
editable
=
False
,
null
=
True
,
on_delete
=
django
.
db
.
models
.
deletion
.
SET_NULL
,
to
=
settings
.
AUTH_USER_MODEL
,
verbose_name
=
'uploaded by user'
)),
],
options
=
{
'ordering'
:
[
'-created_at'
],
'abstract'
:
False
,
},
bases
=
(
wagtail
.
search
.
index
.
Indexed
,
models
.
Model
),
),
migrations
.
CreateModel
(
name
=
'TestPage'
,
fields
=
[
(
'page_ptr'
,
models
.
OneToOneField
(
auto_created
=
True
,
on_delete
=
django
.
db
.
models
.
deletion
.
CASCADE
,
parent_link
=
True
,
primary_key
=
True
,
serialize
=
False
,
to
=
'wagtailcore.Page'
)),
(
'video_streamfield'
,
wagtail
.
core
.
fields
.
StreamField
([(
'video'
,
wagtailvideos
.
blocks
.
VideoChooserBlock
())],
blank
=
True
)),
(
'video_field'
,
models
.
ForeignKey
(
blank
=
True
,
null
=
True
,
on_delete
=
django
.
db
.
models
.
deletion
.
SET_NULL
,
related_name
=
'+'
,
to
=
'wagtailvideos.Video'
)),
],
options
=
{
...
...
@@ -27,4 +57,19 @@ class Migration(migrations.Migration):
},
bases
=
(
'wagtailcore.page'
,),
),
migrations
.
CreateModel
(
name
=
'CustomVideoTranscode'
,
fields
=
[
(
'id'
,
models
.
AutoField
(
auto_created
=
True
,
primary_key
=
True
,
serialize
=
False
,
verbose_name
=
'ID'
)),
(
'media_format'
,
enumchoicefield
.
fields
.
EnumChoiceField
(
enum_class
=
wagtailvideos
.
models
.
MediaFormats
,
max_length
=
4
)),
(
'quality'
,
enumchoicefield
.
fields
.
EnumChoiceField
(
default
=
wagtailvideos
.
models
.
VideoQuality
(
1
),
enum_class
=
wagtailvideos
.
models
.
VideoQuality
,
max_length
=
7
)),
(
'processing'
,
models
.
BooleanField
(
default
=
False
)),
(
'file'
,
models
.
FileField
(
blank
=
True
,
null
=
True
,
upload_to
=
wagtailvideos
.
models
.
get_upload_to
,
verbose_name
=
'file'
)),
(
'error_message'
,
models
.
TextField
(
blank
=
True
)),
(
'video'
,
models
.
ForeignKey
(
on_delete
=
django
.
db
.
models
.
deletion
.
CASCADE
,
related_name
=
'transcodes'
,
to
=
'app.CustomVideoModel'
)),
],
options
=
{
'unique_together'
:
{(
'video'
,
'media_format'
)},
},
),
]
tests/app/migrations/0002_testpage_video_streamfield.py
deleted
100644 → 0
View file @
6d7c3bd8
# Generated by Django 3.1.5 on 2021-01-21 22:54
from
django.db
import
migrations
import
wagtail.core.fields
import
wagtailvideos.blocks
class
Migration
(
migrations
.
Migration
):
dependencies
=
[
(
'app'
,
'0001_initial'
),
]
operations
=
[
migrations
.
AddField
(
model_name
=
'testpage'
,
name
=
'video_streamfield'
,
field
=
wagtail
.
core
.
fields
.
StreamField
([(
'video'
,
wagtailvideos
.
blocks
.
VideoChooserBlock
())],
blank
=
True
),
),
]
tests/app/models.py
View file @
cc90aa21
...
...
@@ -5,6 +5,30 @@ from wagtail.admin.edit_handlers import StreamFieldPanel
from
wagtailvideos.edit_handlers
import
VideoChooserPanel
from
wagtailvideos.blocks
import
VideoChooserBlock
from
wagtailvideos.models
import
AbstractVideo
,
AbstractVideoTranscode
class
CustomVideoModel
(
AbstractVideo
):
attribution
=
models
.
TextField
(
blank
=
True
)
admin_form_fields
=
(
'title'
,
'attribution'
,
'file'
,
'collection'
,
'thumbnail'
,
'tags'
,
)
class
CustomVideoTranscode
(
AbstractVideoTranscode
):
video
=
models
.
ForeignKey
(
CustomVideoModel
,
related_name
=
'transcodes'
,
on_delete
=
models
.
CASCADE
)
class
Meta
:
unique_together
=
(
(
'video'
,
'media_format'
)
)
class
TestPage
(
Page
):
video_field
=
models
.
ForeignKey
(
...
...
tests/test_custom_model.py
0 → 100644
View file @
cc90aa21
from
django.core.exceptions
import
ImproperlyConfigured
from
django.test
import
TestCase
,
override_settings
from
wagtail.tests.utils
import
WagtailTestUtils
from
wagtailvideos
import
get_video_model
,
get_video_model_string
from
wagtailvideos.models
import
Video
from
tests.app.models
import
CustomVideoModel
class
TestGetVideoModel
(
WagtailTestUtils
,
TestCase
):
@
override_settings
(
WAGTAILVIDEOS_VIDEO_MODEL
=
'app.CustomVideoModel'
)
def
test_custom_get_video_model
(
self
):
self
.
assertIs
(
get_video_model
(),
CustomVideoModel
)
@
override_settings
(
WAGTAILVIDEOS_VIDEO_MODEL
=
'app.CustomVideoModel'
)
def
test_custom_get_video_model_string
(
self
):
self
.
assertEqual
(
get_video_model_string
(),
'app.CustomVideoModel'
)
def
test_standard_get_video_model
(
self
):
self
.
assertIs
(
get_video_model
(),
Video
)
def
test_standard_get_video_model_string
(
self
):
self
.
assertEqual
(
get_video_model_string
(),
'wagtailvideos.Video'
)
@
override_settings
(
WAGTAILVIDEOS_VIDEO_MODEL
=
'app.UnknownModel'
)
def
test_unknown_get_video_model
(
self
):
with
self
.
assertRaises
(
ImproperlyConfigured
):
get_video_model
()
@
override_settings
(
WAGTAILVIDEOS_VIDEO_MODEL
=
'invalid-string'
)
def
test_invalid_get_video_model
(
self
):
with
self
.
assertRaises
(
ImproperlyConfigured
):
get_video_model
()
\ No newline at end of file
wagtailvideos/__init__.py
View file @
cc90aa21
from
django.conf
import
settings
from
django.core.exceptions
import
ImproperlyConfigured
default_app_config
=
'wagtailvideos.apps.WagtailVideosApp'
def
get_video_model_string
():
return
getattr
(
settings
,
'WAGTAILVIDEOS_VIDEO_MODEL'
,
'wagtailvideos.Video'
)
def
get_video_model
():
from
django.apps
import
apps
model_string
=
get_video_model_string
()
try
:
return
apps
.
get_model
(
model_string
)
except
ValueError
:
raise
ImproperlyConfigured
(
"WAGTAILVIDEOS_VIDEO_MODEL must be of the form 'app_label.model_name'"
)
except
LookupError
:
raise
ImproperlyConfigured
(
"WAGTAILVIDEOS_VIDEO_MODEL refers to model '%s' that has not been installed"
%
model_string
)
\ No newline at end of file
wagtailvideos/blocks.py
View file @
cc90aa21
from
wagtail.core.blocks
import
ChooserBlock
from
django.utils.functional
import
cached_property
class
VideoChooserBlock
(
ChooserBlock
):
@
cached_property
def
target_model
(
self
):
from
wagtailvideos
.models
import
Video
return
Video
from
wagtailvideos
import
get_video_model
return
get_video_model
()
@
cached_property
def
widget
(
self
):
...
...
wagtailvideos/jinja2tags.py
View file @
cc90aa21
from
jinja2.ext
import
Extension
from
.models
import
Video
from
.
import
get_video_model
Video
=
get_video_model
()
def
video
(
video
,
**
attrs
):
...
...
wagtailvideos/models.py
View file @
cc90aa21
...
...
@@ -19,10 +19,9 @@ from django.db.models.signals import post_save, pre_delete
from
django.dispatch.dispatcher
import
receiver
from
django.forms.utils
import
flatatt
from
django.urls
import
reverse
from
django.utils.
html
import
mark_safe
from
django.utils.
safestring
import
mark_safe
from
django.utils.translation
import
ugettext_lazy
as
_
from
enumchoicefield
import
ChoiceEnum
,
EnumChoiceField
from
six
import
python_2_unicode_compatible
from
taggit.managers
import
TaggableManager
from
wagtail.core.models
import
CollectionMember
from
wagtail.search
import
index
...
...
@@ -79,7 +78,6 @@ def get_upload_to(instance, filename):
return
instance
.
get_upload_to
(
filename
)
@
python_2_unicode_compatible
class
AbstractVideo
(
CollectionMember
,
index
.
Indexed
,
models
.
Model
):
title
=
models
.
CharField
(
max_length
=
255
,
verbose_name
=
_
(
'title'
))
file
=
models
.
FileField
(
...
...
wagtailvideos/permissions.py
View file @
cc90aa21
from
wagtail.core.permission_policies.collections
import
(
CollectionOwnershipPermissionPolicy
)
from
wagtailvideos
import
get_video_model
from
wagtailvideos.models
import
Video
permission_policy
=
CollectionOwnershipPermissionPolicy
(
Video
,
get_video_model
()
,
auth_model
=
Video
,
owner_field_name
=
'uploaded_by_user'
)
wagtailvideos/views/chooser.py
View file @
cc90aa21
...
...
@@ -11,7 +11,7 @@ from wagtail.images.views.chooser import get_chooser_js_data
from
wagtail.search
import
index
as
search_index
from
wagtailvideos.forms
import
get_video_form
from
wagtailvideos
.models
import
Video
from
wagtailvideos
import
get_video_model
from
wagtailvideos.permissions
import
permission_policy
if
LooseVersion
(
wagtail
.
__version__
)
>=
LooseVersion
(
'2.7'
):
...
...
@@ -41,6 +41,7 @@ def get_video_json(video):
def
chooser
(
request
):
Video
=
get_video_model
()
VideoForm
=
get_video_form
(
Video
)
uploadform
=
VideoForm
()
...
...
@@ -101,7 +102,7 @@ def chooser(request):
def
video_chosen
(
request
,
video_id
):
video
=
get_object_or_404
(
Video
,
id
=
video_id
)
video
=
get_object_or_404
(
get_video_model
()
,
id
=
video_id
)
return
render_modal_workflow
(
request
,
None
,
json_data
=
{
...
...
@@ -112,6 +113,7 @@ def video_chosen(request, video_id):
@
permission_checker
.
require
(
'add'
)
def
chooser_upload
(
request
):
Video
=
get_video_model
()
VideoForm
=
get_video_form
(
Video
)
searchform
=
SearchForm
()
...
...
wagtailvideos/views/multiple.py
View file @
cc90aa21
...
...
@@ -10,7 +10,7 @@ from django.views.decorators.vary import vary_on_headers
from
wagtail.search.backends
import
get_search_backends
from
wagtailvideos.forms
import
get_video_form
from
wagtailvideos
.models
import
Video
from
wagtailvideos
import
get_video_model
from
wagtailvideos.permissions
import
permission_policy
if
LooseVersion
(
wagtail
.
__version__
)
>=
LooseVersion
(
'2.7'
):
...
...
@@ -37,6 +37,7 @@ def get_video_edit_form(VideoModel):
@
vary_on_headers
(
'X-Requested-With'
)
def
add
(
request
):
Video
=
get_video_model
()
VideoForm
=
get_video_form
(
Video
)
collections
=
permission_policy
.
collections_user_has_permission_for
(
request
.
user
,
'add'
)
...
...
@@ -98,6 +99,7 @@ def add(request):
@
require_POST
def
edit
(
request
,
video_id
,
callback
=
None
):
Video
=
get_video_model
()
VideoForm
=
get_video_edit_form
(
Video
)
video
=
get_object_or_404
(
Video
,
id
=
video_id
)
...
...
@@ -133,7 +135,7 @@ def edit(request, video_id, callback=None):
@
require_POST
def
delete
(
request
,
video_id
):
video
=
get_object_or_404
(
Video
,
id
=
video_id
)
video
=
get_object_or_404
(
get_video_model
()
,
id
=
video_id
)
if
not
request
.
is_ajax
():
return
HttpResponseBadRequest
(
"Cannot POST to this view without AJAX"
)
...
...
wagtailvideos/views/videos.py
View file @
cc90aa21
...
...
@@ -12,9 +12,8 @@ from wagtail.admin.forms.search import SearchForm
from
wagtail.core.models
import
Collection
from
wagtail.search.backends
import
get_search_backends
from
wagtailvideos
import
ffmpeg
from
wagtailvideos
import
ffmpeg
,
get_video_model
from
wagtailvideos.forms
import
VideoTranscodeAdminForm
,
get_video_form
from
wagtailvideos.models
import
Video
from
wagtailvideos.permissions
import
permission_policy
if
LooseVersion
(
wagtail
.
__version__
)
>=
LooseVersion
(
'2.7'
):
...
...
@@ -31,6 +30,7 @@ permission_checker = PermissionPolicyChecker(permission_policy)
@
vary_on_headers
(
'X-Requested-With'
)
def
index
(
request
):
# Get Videos (filtered by user permission)
Video
=
get_video_model
()
videos
=
Video
.
objects
.
all
()
# Search
...
...
@@ -80,6 +80,7 @@ def index(request):
@
permission_checker
.
require
(
'change'
)
def
edit
(
request
,
video_id
):
Video
=
get_video_model
()
VideoForm
=
get_video_form
(
Video
)
video
=
get_object_or_404
(
Video
,
id
=
video_id
)
...
...
@@ -134,7 +135,7 @@ def edit(request, video_id):
def
create_transcode
(
request
,
video_id
):
if
request
.
method
!=
'POST'
:
return
HttpResponseNotAllowed
([
'POST'
])
video
=
get_object_or_404
(
Video
,
id
=
video_id
)
video
=
get_object_or_404
(
get_video_model
()
,
id
=
video_id
)
transcode_form
=
VideoTranscodeAdminForm
(
data
=
request
.
POST
,
video
=
video
)
if
transcode_form
.
is_valid
():
...
...
@@ -144,7 +145,7 @@ def create_transcode(request, video_id):
@
permission_checker
.
require
(
'delete'
)
def
delete
(
request
,
video_id
):
video
=
get_object_or_404
(
Video
,
id
=
video_id
)
video
=
get_object_or_404
(
get_video_model
()
,
id
=
video_id
)
if
request
.
POST
:
video
.
delete
()
...
...
@@ -158,6 +159,7 @@ def delete(request, video_id):
@
permission_checker
.
require
(
'add'
)
def
add
(
request
):
Video
=
get_video_model
()
VideoForm
=
get_video_form
(
Video
)
if
request
.
POST
:
...
...
@@ -188,7 +190,7 @@ def add(request):
def
usage
(
request
,
image_id
):
image
=
get_object_or_404
(
Video
,
id
=
image_id
)
image
=
get_object_or_404
(
get_video_model
()
,
id
=
image_id
)
paginator
=
Paginator
(
image
.
get_usage
(),
per_page
=
12
)
page
=
paginator
.
get_page
(
request
.
GET
.
get
(
'p'
))
...
...
wagtailvideos/wagtail_hooks.py
View file @
cc90aa21
...
...
@@ -10,10 +10,12 @@ from django.utils.html import format_html
from
django.templatetags.static
import
static
from
wagtailvideos
import
urls
from
wagtailvideos.forms
import
GroupVideoPermissionFormSet
from
wagtailvideos
.models
import
Video
from
wagtailvideos
import
get_video_model
from
.permissions
import
permission_policy
Video
=
get_video_model
()
@
hooks
.
register
(
'register_admin_urls'
)
def
register_admin_urls
():
...
...
wagtailvideos/widgets.py
View file @
cc90aa21
...
...
@@ -4,7 +4,7 @@ from django.template.loader import render_to_string
from
django.utils.translation
import
ugettext_lazy
as
_
from
wagtail.admin.widgets
import
AdminChooser
from
wagtailvideos
.models
import
Video
from
wagtailvideos
import
get_video_model
class
AdminVideoChooser
(
AdminChooser
):
...
...
@@ -14,7 +14,7 @@ class AdminVideoChooser(AdminChooser):
def
__init__
(
self
,
**
kwargs
):
super
(
AdminVideoChooser
,
self
).
__init__
(
**
kwargs
)
self
.
video_model
=
Video
self
.
video_model
=
get_video_model
()
def
render_html
(
self
,
name
,
value
,
attrs
):
instance
,
value
=
self
.
get_instance_and_id
(
self
.
video_model
,
value
)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment