Commit 4098444c authored by Seb's avatar Seb
Browse files

Got rid of extra cruft. Added in error handling for ffmpeg

parent 6d9645f9
from django.utils.html import escape
from wagtail.utils.apps import get_app_submodules
from .shortcuts import get_rendition_or_not_found
class Format(object):
def __init__(self, name, label, classnames, filter_spec):
self.name = name
self.label = label
self.classnames = classnames
self.filter_spec = filter_spec
def editor_attributes(self, video, alt_text):
"""
Return string of additional attributes to go on the HTML element
when outputting this image within a rich text editor field
"""
return 'data-embedtype="image" data-id="%d" data-format="%s" data-alt="%s" ' % (
video.id, self.name, alt_text
)
def video_to_editor_html(self, image, alt_text):
return self.image_to_html(
image, alt_text, self.editor_attributes(image, alt_text)
)
def video_to_html(self, image, alt_text, extra_attributes=''):
rendition = get_rendition_or_not_found(image, self.filter_spec)
if self.classnames:
class_attr = 'class="%s" ' % escape(self.classnames)
else:
class_attr = ''
return '<img %s%ssrc="%s" width="%d" height="%d" alt="%s">' % (
extra_attributes, class_attr,
escape(rendition.url), rendition.width, rendition.height, alt_text
)
FORMATS = []
FORMATS_BY_NAME = {}
def register_video_format(format):
if format.name in FORMATS_BY_NAME:
raise KeyError("Image format '%s' is already registered" % format.name)
FORMATS_BY_NAME[format.name] = format
FORMATS.append(format)
def unregister_video_format(format_name):
global FORMATS
# handle being passed a format object rather than a format name string
try:
format_name = format_name.name
except AttributeError:
pass
try:
del FORMATS_BY_NAME[format_name]
FORMATS = [fmt for fmt in FORMATS if fmt.name != format_name]
except KeyError:
raise KeyError("Image format '%s' is not registered" % format_name)
def get_video_formats():
search_for_video_formats()
return FORMATS
def get_video_format(name):
search_for_video_formats()
return FORMATS_BY_NAME[name]
_searched_for_video_formats = False
def search_for_video_formats():
global _searched_for_video_formats
if not _searched_for_video_formats:
list(get_app_submodules('video_formats'))
_searched_for_video_formats = True
# Define default image formats
register_video_format(Format('fullwidth', 'Full width', 'richtext-image full-width', 'width-800'))
register_video_format(Format('left', 'Left-aligned', 'richtext-image left', 'width-500'))
register_video_format(Format('right', 'Right-aligned', 'richtext-image right', 'width-500'))
...@@ -7,7 +7,6 @@ from wagtail.wagtailadmin.forms import (BaseCollectionMemberForm, ...@@ -7,7 +7,6 @@ from wagtail.wagtailadmin.forms import (BaseCollectionMemberForm,
from enumchoicefield.forms import EnumField from enumchoicefield.forms import EnumField
from wagtailvideos.fields import WagtailVideoField from wagtailvideos.fields import WagtailVideoField
from wagtailvideos.formats import get_video_formats
from wagtailvideos.models import MediaFormats, 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
...@@ -64,15 +63,7 @@ class VideoTranscodeAdminForm(forms.Form): ...@@ -64,15 +63,7 @@ class VideoTranscodeAdminForm(forms.Form):
class VideoInsertionForm(forms.Form): class VideoInsertionForm(forms.Form):
""" pass
Form for selecting parameters of the image (e.g. format) prior to insertion
into a rich text area
"""
format = forms.ChoiceField(
choices=[(format.name, format.label) for format in get_video_formats()],
widget=forms.RadioSelect
)
alt_text = forms.CharField()
class URLGeneratorForm(forms.Form): class URLGeneratorForm(forms.Form):
......
# -*- coding: utf-8 -*-
# Generated by Django 1.9.7 on 2016-07-06 01:53
from __future__ import unicode_literals
from django.db import migrations, models
import wagtailvideos.models
class Migration(migrations.Migration):
dependencies = [
('wagtailvideos', '0003_auto_20160705_1646'),
]
operations = [
migrations.AlterField(
model_name='videotranscode',
name='file',
field=models.FileField(blank=True, null=True, upload_to=wagtailvideos.models.get_upload_to, verbose_name='file'),
),
]
# -*- coding: utf-8 -*-
# Generated by Django 1.9.7 on 2016-07-06 04:59
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('wagtailvideos', '0004_auto_20160706_1153'),
]
operations = [
migrations.AddField(
model_name='videotranscode',
name='error_message',
field=models.TextField(blank=True),
),
]
...@@ -185,7 +185,8 @@ class AbstractVideo(CollectionMember, TagSearchable): ...@@ -185,7 +185,8 @@ class AbstractVideo(CollectionMember, TagSearchable):
transcode.processing = True transcode.processing = True
transcode.save(update_fields=['processing']) # Lock the transcode model transcode.save(update_fields=['processing']) # Lock the transcode model
TranscodingThread(transcode).start() TranscodingThread(transcode).start()
return None else:
pass # TODO Queue?
class Meta: class Meta:
abstract = True abstract = True
...@@ -216,66 +217,56 @@ class TranscodingThread(threading.Thread): ...@@ -216,66 +217,56 @@ class TranscodingThread(threading.Thread):
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')
args = ['ffmpeg', '-hide_banner', '-i', input_file]
try: try:
if media_format is MediaFormats.ogg: if media_format is MediaFormats.ogg:
subprocess.check_call([ subprocess.check_output(args + [
'ffmpeg',
'-i', input_file,
'-codec:v', 'libtheora', '-codec:v', 'libtheora',
'-qscale:v', '7', '-qscale:v', '7',
'-codec:a', 'libvorbis', '-codec:a', 'libvorbis',
'-qscale:a', '5', '-qscale:a', '5',
output_file, output_file,
], stdin=FNULL) ], stdin=FNULL, stderr=subprocess.STDOUT)
elif media_format is MediaFormats.mp4: elif media_format is MediaFormats.mp4:
subprocess.check_call([ subprocess.check_call(args + [
'ffmpeg',
'-i', input_file,
'-codec:v', 'libx264', '-codec:v', 'libx264',
'-preset', 'slow', # TODO Checkout other presets '-preset', 'slow', # TODO Checkout other presets
'-crf', '22', '-crf', '22',
'-codec:a', 'copy', '-codec:a', 'copy',
output_file, output_file,
]) ], stdin=FNULL, stderr=subprocess.STDOUT)
elif media_format is MediaFormats.webm: elif media_format is MediaFormats.webm:
subprocess.check_call([ subprocess.check_call(args + [
'ffmpeg',
'-i', input_file,
'-codec:v', 'libvpx', '-codec:v', 'libvpx',
'-crf', '10', '-crf', '10',
'-b:v', '1M', '-b:v', '1M',
'-codec:a', 'libvorbis', '-codec:a', 'libvorbis',
output_file, output_file,
]) ], stdin=FNULL, stderr=subprocess.STDOUT)
else: self.transcode.file = ContentFile(
return None
transcoded_file = ContentFile(
open(output_file, 'rb').read(), transcode_name) open(output_file, 'rb').read(), transcode_name)
self.transcode.error_message = ''
except subprocess.CalledProcessError: except subprocess.CalledProcessError as error:
return None self.transcode.error_message = error.output
finally: finally:
self.transcode.processing = False
self.transcode.save()
shutil.rmtree(output_dir, ignore_errors=True) shutil.rmtree(output_dir, ignore_errors=True)
self.transcode.processing = False
self.transcode.file = transcoded_file
self.transcode.save(update_fields=[
'processing', 'file'
])
# Delete files when model is deleted # Delete files when model is deleted
@receiver(pre_delete, sender=Video) @receiver(pre_delete, sender=Video)
def video_delete(sender, instance, **kwargs): def video_delete(sender, instance, **kwargs):
print('Video pre delete received')
instance.file.delete(False) instance.file.delete(False)
class AbstractVideoTranscode(models.Model): class AbstractVideoTranscode(models.Model):
media_format = EnumChoiceField(MediaFormats) media_format = EnumChoiceField(MediaFormats)
processing = models.BooleanField(default=False) processing = models.BooleanField(default=False)
file = models.FileField(null=True, file = models.FileField(null=True, blank=True,
verbose_name=_('file'), upload_to=get_upload_to) verbose_name=_('file'), upload_to=get_upload_to)
error_message = models.TextField(blank=True)
@property @property
def url(self): def url(self):
......
# coding=utf-8
def get_rendition_or_not_found(image, specs):
"""
Tries to get / create the rendition for the image or renders a not-found image if it does not exist.
:param image: AbstractImage
:param specs: str or Filter
:return: Rendition
"""
try:
return image.get_rendition(specs)
except image.DoesNotExist:
# Image file is (probably) missing from /media/original_images - generate a dummy
# rendition so that we just output a broken image, rather than crashing out completely
# during rendering.
Rendition = image.renditions.model # pick up any custom Image / Rendition classes that may be in use
rendition = Rendition(image=image, width=0, height=0)
rendition.file.name = 'not-found'
return rendition
...@@ -57,11 +57,11 @@ ...@@ -57,11 +57,11 @@
<h3>Available Transcodes</h3> <h3>Available Transcodes</h3>
<ul> <ul>
{% for transcode in transcodes %} {% for transcode in transcodes %}
{% if transcode.processing %} <li>
<li>{{ transcode.media_format }} (Processing... hold tight) </li> {{ transcode.media_format }}
{%else %} {% if transcode.processing %} (Processing... hold tight) {% endif %}
<li>{{ transcode.media_format }}</li> {% if transcode.error_message %} <pre> {{ transcode.error_message }}</pre> {% endif %}
{% endif %} </li>
{% endfor %} {% endfor %}
</ul> </ul>
{% else %} {% else %}
......
...@@ -38,8 +38,8 @@ class VideoNode(template.Node): ...@@ -38,8 +38,8 @@ class VideoNode(template.Node):
video = self.video.resolve(context) video = self.video.resolve(context)
sources = ["<source src='{1}' type='video/{2}'>" sources = ["<source src='{1}' type='video/{2}'>"
.format(flatatt(self.attrs), video.url, video.file_ext)] # TODO get mimetype properly (extension is not always reliable) .format(flatatt(self.attrs), video.url, video.file_ext)] # TODO get mimetype properly (extension is not always reliable)
for transcode in video.transcodes.filter(processing=False): for transcode in video.transcodes.exclude(processing=True, file=''):
sources.append("<source src='{0}' type='video/{1}' >".format(transcode.url, transcode.media_format.name)) sources.append("<source src='{0}' type='video/{1}' >".format(transcode.media_format.value, transcode.media_format.name))
sources.append("<p>Sorry, your browser doesn't support playback for this video</p>") sources.append("<p>Sorry, your browser doesn't support playback for this video</p>")
return mark_safe( return mark_safe(
"<video {0}>{1}</video".format(flatatt(self.attrs), "\n".join(sources))) "<video {0}>{1}</video>".format(flatatt(self.attrs), "\n".join(sources)))
...@@ -8,7 +8,6 @@ from wagtail.wagtailadmin.modal_workflow import render_modal_workflow ...@@ -8,7 +8,6 @@ from wagtail.wagtailadmin.modal_workflow import render_modal_workflow
from wagtail.wagtailcore.models import Collection from wagtail.wagtailcore.models import Collection
from wagtail.wagtailsearch.backends import get_search_backends from wagtail.wagtailsearch.backends import get_search_backends
from wagtailvideos.formats import get_video_format
from wagtailvideos.forms import VideoInsertionForm, get_video_form from wagtailvideos.forms import VideoInsertionForm, get_video_form
from wagtailvideos.models import Video from wagtailvideos.models import Video
...@@ -144,14 +143,11 @@ def chooser_select_format(request, video_id): ...@@ -144,14 +143,11 @@ def chooser_select_format(request, video_id):
form = VideoInsertionForm(request.POST, initial={'alt_text': video.default_alt_text}) form = VideoInsertionForm(request.POST, initial={'alt_text': video.default_alt_text})
if form.is_valid(): if form.is_valid():
format = get_video_format(form.cleaned_data['format'])
preview_video = video.get_rendition(format.filter_spec) preview_video = video.get_rendition(format.filter_spec)
video_json = json.dumps({ video_json = json.dumps({
'id': image.id, 'id': image.id,
'title': image.title, 'title': image.title,
'format': format.name,
'alt': form.cleaned_data['alt_text'],
'class': format.classnames, 'class': format.classnames,
'edit_link': reverse('wagtailvideos:edit', args=(video.id,)), 'edit_link': reverse('wagtailvideos:edit', args=(video.id,)),
'preview': { 'preview': {
......
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