Commit ead24890 authored by Seb's avatar Seb
Browse files

Generating of transcodes from admin

parent 5c30deb9
...@@ -5,9 +5,10 @@ from wagtail.wagtailadmin import widgets ...@@ -5,9 +5,10 @@ from wagtail.wagtailadmin import widgets
from wagtail.wagtailadmin.forms import (BaseCollectionMemberForm, from wagtail.wagtailadmin.forms import (BaseCollectionMemberForm,
collection_member_permission_formset_factory) collection_member_permission_formset_factory)
from enumchoicefield.forms import EnumField
from wagtailvideos.fields import WagtailVideoField from wagtailvideos.fields import WagtailVideoField
from wagtailvideos.formats import get_video_formats from wagtailvideos.formats import get_video_formats
from wagtailvideos.models import Video from wagtailvideos.models import MediaFormats, Video
from wagtailvideos.permissions import \ from wagtailvideos.permissions import \
permission_policy as video_permission_policy permission_policy as video_permission_policy
...@@ -49,6 +50,19 @@ def get_video_form(model): ...@@ -49,6 +50,19 @@ def get_video_form(model):
}) })
class VideoTranscodeAdminForm(forms.Form):
media_format = EnumField(MediaFormats)
def __init__(self, data=None, *, video, **kwargs):
super().__init__(data, **kwargs)
self.video = video
def save(self):
media_format = self.cleaned_data['media_format']
self.video.do_transcode(media_format)
class VideoInsertionForm(forms.Form): class VideoInsertionForm(forms.Form):
""" """
Form for selecting parameters of the image (e.g. format) prior to insertion Form for selecting parameters of the image (e.g. format) prior to insertion
......
...@@ -33,6 +33,7 @@ class MediaFormats(ChoiceEnum): ...@@ -33,6 +33,7 @@ class MediaFormats(ChoiceEnum):
mp4 = 'H.264 and MP3 in Mp4' mp4 = 'H.264 and MP3 in Mp4'
ogg = 'Theora and Voris in Ogg' ogg = 'Theora and Voris in Ogg'
class VideoQuerySet(SearchableQuerySetMixin, models.QuerySet): class VideoQuerySet(SearchableQuerySetMixin, models.QuerySet):
pass pass
...@@ -66,7 +67,6 @@ class AbstractVideo(CollectionMember, TagSearchable): ...@@ -66,7 +67,6 @@ class AbstractVideo(CollectionMember, TagSearchable):
""" """
try: try:
self.file.path self.file.path
return True return True
except NotImplementedError: except NotImplementedError:
return False return False
...@@ -145,13 +145,15 @@ class AbstractVideo(CollectionMember, TagSearchable): ...@@ -145,13 +145,15 @@ class AbstractVideo(CollectionMember, TagSearchable):
def url(self): def url(self):
return self.file.url return self.file.url
@property def filename(self, include_ext=True):
def filename(self): if include_ext:
return os.path.basename(self.file.name) return os.path.basename(self.file.name)
else:
return os.path.splitext(os.path.basename(self.file.name))[0]
@property @property
def file_ext(self): def file_ext(self):
return os.path.splitext(self.filename)[1][1:] return os.path.splitext(self.filename())[1][1:]
def is_editable_by_user(self, user): def is_editable_by_user(self, user):
from wagtail.wagtailimages.permissions import permission_policy from wagtail.wagtailimages.permissions import permission_policy
...@@ -165,30 +167,19 @@ class AbstractVideo(CollectionMember, TagSearchable): ...@@ -165,30 +167,19 @@ class AbstractVideo(CollectionMember, TagSearchable):
return cls.transcodes.related.related_model return cls.transcodes.related.related_model
def get_transcode(self, media_format): def get_transcode(self, media_format):
# TODO check media_format is MediaFormat
Transcode = self.get_transcode_model() Transcode = self.get_transcode_model()
try: try:
return self.transcodes.get(media_format=media_format) return self.transcodes.get(media_format=media_format)
except Transcode.DoesNotExist: except Transcode.DoesNotExist:
output_dir = tempfile.mkdtemp() return self.do_transcode(media_format)
transcode_filename = os.path.splitext(self.filename)[0] + '.' + media_format.name def do_transcode(self, media_format, force=False):
input_file = self.file.path
output_dir = tempfile.mkdtemp()
transcode_name = "{0}.{1}".format(
self.filename(include_ext=False),
media_format.name)
transcoded_file = self.do_transcode(
media_format, self.file.path, output_dir, transcode_filename)
if transcoded_file:
transcode, created = self.transcodes.get_or_create(
media_format=media_format,
defaults={'file': transcoded_file}
)
return transcode
else:
# TODO handle transcode failure
return None
def do_transcode(self, media_format, input_file, output_dir, transcode_name):
output_file = os.path.join(output_dir, transcode_name) output_file = os.path.join(output_dir, transcode_name)
FNULL = open(os.devnull, 'r') FNULL = open(os.devnull, 'r')
try: try:
...@@ -224,7 +215,8 @@ class AbstractVideo(CollectionMember, TagSearchable): ...@@ -224,7 +215,8 @@ class AbstractVideo(CollectionMember, TagSearchable):
]) ])
else: else:
return None return None
transcoded_file = ContentFile(open(output_file, 'rb').read(), transcode_name) transcoded_file = ContentFile(
open(output_file, 'rb').read(), transcode_name)
except subprocess.CalledProcessError: except subprocess.CalledProcessError:
return None return None
...@@ -232,8 +224,12 @@ class AbstractVideo(CollectionMember, TagSearchable): ...@@ -232,8 +224,12 @@ class AbstractVideo(CollectionMember, TagSearchable):
finally: finally:
shutil.rmtree(output_dir, ignore_errors=True) shutil.rmtree(output_dir, ignore_errors=True)
return transcoded_file transcode, created = self.transcodes.get_or_create(
media_format=media_format,
defaults={'file': transcoded_file}
)
return transcode
class Meta: class Meta:
abstract = True abstract = True
......
{% extends "wagtailadmin/base.html" %} {% extends "wagtailadmin/base.html" %}
{% load wagtailimages_tags staticfiles i18n %} {% load wagtailimages_tags staticfiles i18n wagtailvideos_tags %}
{% block titletag %}{% blocktrans with title=video.title %}Editing video {{ title }}{% endblocktrans %}{% endblock %} {% block titletag %}{% blocktrans with title=video.title %}Editing video {{ title }}{% endblocktrans %}{% endblock %}
{% block extra_js %} {% block extra_js %}
...@@ -47,10 +47,12 @@ ...@@ -47,10 +47,12 @@
</div> </div>
<div class="col5 divider-after"> <div class="col5 divider-after">
<h2 class="label">{% trans "Video preview" %}</h2> <h2 class="label">{% trans "Video preview" %}</h2>
{% video video controls style=max-width:100% %}
<video style='max-width:100%;height:auto;' {# FIXME Inline styles #} preload="auto" controls="true" poster='{{video.thumbnail.url}}'> <form action="{% url 'wagtailvideos:create_transcode' video.id %}" method="POST">
<source src="{{video.file.url}}" type='video/mp4'/> {% csrf_token %}
</video> {{ transcode_form.media_format }}
<input type='submit' />
</form>
{% if transcodes %} {% if transcodes %}
<h3>Available Transcodes</h3> <h3>Available Transcodes</h3>
<ul> <ul>
...@@ -60,8 +62,7 @@ ...@@ -60,8 +62,7 @@
</ul> </ul>
{% else %} {% else %}
<h3>No transcodes found</h3> <h3>No transcodes found</h3>
<p>If you wish to generate HTML5 compliant transcodes use the button below. This may take a while.</p> <p>If you wish to generate HTML5 compliant transcodes use the form above. This may take a while.</p>
<button>Generate Transcodes</button>
{% endif %} {% endif %}
</div> </div>
<div class="col2 "> <div class="col2 ">
......
...@@ -32,6 +32,7 @@ def video(parser, token): ...@@ -32,6 +32,7 @@ def video(parser, token):
extra_attrs[param] = '' # attributes without values e.g. autoplay, controls extra_attrs[param] = '' # attributes without values e.g. autoplay, controls
return VideoNode(video_expr, html5, extra_attrs) return VideoNode(video_expr, html5, extra_attrs)
class VideoNode(template.Node): class VideoNode(template.Node):
def __init__(self, video, html5=False, attrs={}): def __init__(self, video, html5=False, attrs={}):
self.video = template.Variable(video) self.video = template.Variable(video)
...@@ -42,11 +43,11 @@ class VideoNode(template.Node): ...@@ -42,11 +43,11 @@ class VideoNode(template.Node):
video = self.video.resolve(context) video = self.video.resolve(context)
if not self.html5: if not self.html5:
return mark_safe("<video {0}><source src='{1}' type='video/{2}'></video>" return mark_safe("<video {0}><source src='{1}' type='video/{2}'></video>"
.format(flatatt(self.attrs), video.url, video.file_ext)) # FIXME get mimetype properly .format(flatatt(self.attrs), video.url, video.file_ext)) # FIXME get mimetype properly (extension is not always reliable)
else: else:
transcodes = [] transcodes = []
for media_format in MediaFormats: for media_format in MediaFormats:
transcode = video.get_transcode(media_format) # FIXME this is blocking transcode = video.get_transcode(media_format) # FIXME this is blocking when no transcodes are found
transcodes.append("<source src='{0}' type='video/{1}' >".format(transcode.url, transcode.media_format.name)) transcodes.append("<source src='{0}' type='video/{1}' >".format(transcode.url, transcode.media_format.name))
return mark_safe( return mark_safe(
"<video {0}>{1}</video".format(flatatt(self.attrs), "\n".join(transcodes))) "<video {0}>{1}</video".format(flatatt(self.attrs), "\n".join(transcodes)))
from django.conf.urls import url from django.conf.urls import url
from wagtailvideos.views import chooser, multiple, videos from wagtailvideos.views import chooser, multiple, videos
urlpatterns = [ urlpatterns = [
...@@ -7,6 +8,9 @@ urlpatterns = [ ...@@ -7,6 +8,9 @@ urlpatterns = [
url(r'^(\d+)/delete/$', videos.delete, name='delete'), url(r'^(\d+)/delete/$', videos.delete, name='delete'),
url(r'^(\d+)/generate_url/$', videos.url_generator, name='url_generator'), url(r'^(\d+)/generate_url/$', videos.url_generator, name='url_generator'),
url(r'^(\d+)/generate_url/(.*)/$', videos.generate_url, name='generate_url'), url(r'^(\d+)/generate_url/(.*)/$', videos.generate_url, name='generate_url'),
url(r'^(\d+)/create_transcode/$', videos.create_transcode, name='create_transcode'),
#url(r'^(\d+)/preview/(.*)/$', videos.preview, name='preview'), #url(r'^(\d+)/preview/(.*)/$', videos.preview, name='preview'),
url(r'^add/$', videos.add, name='add'), url(r'^add/$', videos.add, name='add'),
url(r'^usage/(\d+)/$', videos.usage, name='video_usage'), url(r'^usage/(\d+)/$', videos.usage, name='video_usage'),
......
import os import os
from django.core.urlresolvers import NoReverseMatch, reverse from django.core.urlresolvers import NoReverseMatch, reverse
from django.http import JsonResponse from django.http import HttpResponseNotAllowed, JsonResponse
from django.shortcuts import get_object_or_404, redirect, render from django.shortcuts import get_object_or_404, redirect, render
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.views.decorators.vary import vary_on_headers from django.views.decorators.vary import vary_on_headers
...@@ -11,7 +11,8 @@ from wagtail.wagtailadmin.forms import SearchForm ...@@ -11,7 +11,8 @@ from wagtail.wagtailadmin.forms import SearchForm
from wagtail.wagtailcore.models import Collection, Site from wagtail.wagtailcore.models import Collection, Site
from wagtail.wagtailsearch.backends import get_search_backends from wagtail.wagtailsearch.backends import get_search_backends
from wagtailvideos.forms import URLGeneratorForm, get_video_form from wagtailvideos.forms import (URLGeneratorForm, VideoTranscodeAdminForm,
get_video_form)
from wagtailvideos.models import get_video_model from wagtailvideos.models import get_video_model
...@@ -124,9 +125,24 @@ def edit(request, video_id): ...@@ -124,9 +125,24 @@ def edit(request, video_id):
'url_generator_enabled': url_generator_enabled, 'url_generator_enabled': url_generator_enabled,
'filesize': video.get_file_size(), 'filesize': video.get_file_size(),
'transcodes': video.transcodes.all(), 'transcodes': video.transcodes.all(),
'transcode_form': VideoTranscodeAdminForm(video=video)
}) })
def create_transcode(request, video_id):
if request.method != 'POST':
return HttpResponseNotAllowed(['POST'])
Video = get_video_model()
video = get_object_or_404(Video, id=video_id)
transcode_form = VideoTranscodeAdminForm(request.POST, video=video)
if transcode_form.is_valid():
transcode_form.save()
return redirect('wagtailvideos:edit', video_id)
# TODO was dis?
def url_generator(request, image_id): def url_generator(request, image_id):
image = get_object_or_404(get_video_model(), id=image_id) image = get_object_or_404(get_video_model(), id=image_id)
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment