Skip to content

Instantly share code, notes, and snippets.

@joar
Created January 22, 2013 21:54
Show Gist options
  • Save joar/4598837 to your computer and use it in GitHub Desktop.
Save joar/4598837 to your computer and use it in GitHub Desktop.
diff --git a/mediagoblin/config_spec.ini b/mediagoblin/config_spec.ini
index bee67d4..5d7ac9a 100644
--- a/mediagoblin/config_spec.ini
+++ b/mediagoblin/config_spec.ini
@@ -86,6 +86,10 @@ max_height = integer(default=640)
max_width = integer(default=180)
max_height = integer(default=180)
+[media_type:mediagoblin.media_types.image]
+# One of BICUBIC, BILINEAR, NEAREST, ANTIALIAS
+resize_filter = string(default="ANTIALIAS")
+
[media_type:mediagoblin.media_types.video]
# Should we keep the original file?
keep_original = boolean(default=False)
@@ -97,6 +101,24 @@ vp8_quality = integer(default=8)
# Range: -0.1..1
vorbis_quality = float(default=0.3)
+[[skip_transcode]]
+# This section defines the enforements on input video files
+# If the input matches these rules, it won't be transcoded.
+#
+# Enforce mimetype is one of
+mime_types = string_list(default=list("video/webm"))
+# Enforce container format is one of
+container_formats = string_list(default=list("Matroska"))
+# Enforce video codec is one of
+video_codecs = string_list(default=list("VP8 video"))
+# Enforce audio codec is one of
+audio_codecs = string_list(default=list("Vorbis"))
+# Enforce check against [media:medium] dimensions
+dimensions_match = boolean(default=True)
+
+# If this is set to true, MediaGoblin will ingore any defined rules and
+# transcode *all videos*
+force_transcode = boolean(default=False)
[media_type:mediagoblin.media_types.audio]
keep_original = boolean(default=True)
diff --git a/mediagoblin/media_types/image/processing.py b/mediagoblin/media_types/image/processing.py
index e6a34ca..f5fb9a7 100644
--- a/mediagoblin/media_types/image/processing.py
+++ b/mediagoblin/media_types/image/processing.py
@@ -47,7 +47,25 @@ def resize_image(entry, filename, new_path, exif_tags, workdir, new_size,
except IOError:
raise BadMediaFail()
resized = exif_fix_image_orientation(resized, exif_tags) # Fix orientation
- resized.thumbnail(new_size, Image.ANTIALIAS)
+
+ pil_filters = {
+ 'NEAREST': Image.NEAREST,
+ 'BILINEAR': Image.BILINEAR,
+ 'BICUBIC': Image.BICUBIC,
+ 'ANTIALIAS': Image.ANTIALIAS}
+
+ filter_config = \
+ mgg.global_config['media_type:mediagoblin.media_types.image']\
+ ['resize_filter']
+
+ try:
+ resize_filter = pil_filters[filter_config.upper()]
+ except KeyError:
+ raise Exception('Filter "{0}" not found, choose one of {1}'.format(
+ unicode(filter_config),
+ u', '.join(pil_filters.keys())))
+
+ resized.thumbnail(new_size, resize_filter)
# Copy the new file to the conversion subdir, then remotely.
tmp_resized_filename = os.path.join(workdir, new_path[-1])
diff --git a/mediagoblin/media_types/video/processing.py b/mediagoblin/media_types/video/processing.py
index 4c9f013..53fe1a7 100644
--- a/mediagoblin/media_types/video/processing.py
+++ b/mediagoblin/media_types/video/processing.py
@@ -24,6 +24,8 @@ from mediagoblin.processing import \
from mediagoblin.tools.translate import lazy_pass_to_ugettext as _
from . import transcoders
+from .util import skip_transcode
+
_log = logging.getLogger(__name__)
_log.setLevel(logging.DEBUG)
@@ -80,24 +82,43 @@ def process_video(entry, workbench=None):
with tmp_dst:
# Transcode queued file to a VP8/vorbis file that fits in a 640x640 square
progress_callback = ProgressCallback(entry)
- transcoder = transcoders.VideoTranscoder()
- transcoder.transcode(queued_filename, tmp_dst.name,
- vp8_quality=video_config['vp8_quality'],
- vp8_threads=video_config['vp8_threads'],
- vorbis_quality=video_config['vorbis_quality'],
- progress_callback=progress_callback)
- # Push transcoded video to public storage
- _log.debug('Saving medium...')
- mgg.public_store.copy_local_to_storage(tmp_dst.name, medium_filepath)
- _log.debug('Saved medium')
+ dimensions = (
+ mgg.global_config['media:medium']['max_width'],
+ mgg.global_config['media:medium']['max_height'])
+
+ metadata = transcoders.VideoTranscoder().discover(queued_filename)
+
+ if skip_transcode(metadata):
+ _log.debug('Skipping transcoding')
+ # Just push the submitted file to the tmp_dst
+ open(tmp_dst.name, 'wb').write(open(queued_filename, 'rb').read())
+
+ dst_dimensions = metadata['videowidth'], metadata['videoheight']
+ else:
+ transcoder = transcoders.VideoTranscoder()
+
+ transcoder.transcode(queued_filename, tmp_dst.name,
+ vp8_quality=video_config['vp8_quality'],
+ vp8_threads=video_config['vp8_threads'],
+ vorbis_quality=video_config['vorbis_quality'],
+ progress_callback=progress_callback,
+ dimensions=dimensions)
+
+ dst_dimensions = transcoder.dst_data.videowidth,\
+ transcoder.dst_data.videoheight
+
+ # Push transcoded video to public storage
+ _log.debug('Saving medium...')
+ mgg.public_store.copy_local_to_storage(tmp_dst.name, medium_filepath)
+ _log.debug('Saved medium')
- entry.media_files['webm_640'] = medium_filepath
+ entry.media_files['webm_640'] = medium_filepath
- # Save the width and height of the transcoded video
- entry.media_data_init(
- width=transcoder.dst_data.videowidth,
- height=transcoder.dst_data.videoheight)
+ # Save the width and height of the transcoded video
+ entry.media_data_init(
+ width=dst_dimensions[0],
+ height=dst_dimensions[1])
# Temporary file for the video thumbnail (cleaned up with workbench)
tmp_thumb = NamedTemporaryFile(dir=workbench.dir, suffix='.jpg', delete=False)
@@ -109,10 +130,10 @@ def process_video(entry, workbench=None):
tmp_thumb.name,
180)
- # Push the thumbnail to public storage
- _log.debug('Saving thumbnail...')
- mgg.public_store.copy_local_to_storage(tmp_thumb.name, thumbnail_filepath)
- entry.media_files['thumb'] = thumbnail_filepath
+ # Push the thumbnail to public storage
+ _log.debug('Saving thumbnail...')
+ mgg.public_store.copy_local_to_storage(tmp_thumb.name, thumbnail_filepath)
+ entry.media_files['thumb'] = thumbnail_filepath
if video_config['keep_original']:
# Push original file to public storage
diff --git a/mediagoblin/media_types/video/transcoders.py b/mediagoblin/media_types/video/transcoders.py
index 152de28..8aa7121 100644
--- a/mediagoblin/media_types/video/transcoders.py
+++ b/mediagoblin/media_types/video/transcoders.py
@@ -673,6 +673,7 @@ class VideoTranscoder:
self._setup()
self._run()
+ # XXX: This could be a static method.
def discover(self, src):
'''
Discover properties about a media file
@@ -793,7 +794,8 @@ class VideoTranscoder:
self.audioconvert = gst.element_factory_make('audioconvert', 'audioconvert')
self.pipeline.add(self.audioconvert)
- self.audiocapsfilter = gst.element_factory_make('capsfilter', 'audiocapsfilter')
+ self.audiocapsfilter = gst.element_factory_make('capsfilter',
+ 'audiocapsfilter')
audiocaps = ['audio/x-raw-float']
self.audiocapsfilter.set_property(
'caps',
diff --git a/mediagoblin/media_types/video/util.py b/mediagoblin/media_types/video/util.py
new file mode 100644
index 0000000..070157f
--- /dev/null
+++ b/mediagoblin/media_types/video/util.py
@@ -0,0 +1,66 @@
+# GNU MediaGoblin -- federated, autonomous media hosting
+# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import logging
+
+from mediagoblin import mg_globals as mgg
+
+_log = logging.getLogger(__name__)
+
+
+def skip_transcode(metadata):
+ '''
+ Checks video metadata against configuration values for skip_transcode.
+
+ Returns True if the video matches the requirements in the configuration.
+ '''
+ config = mgg.global_config['media_type:mediagoblin.media_types.video']\
+ ['skip_transcode']
+
+ medium_config = mgg.global_config['media:medium']
+
+ _log.debug('skip_transcode config: {0}'.format(config))
+
+ if config['mime_types'] and metadata.get('mimetype'):
+ if not metadata['mimetype'] in config['mime_types']:
+ return False
+
+ if config['container_formats'] and metadata['tags'].get('audio-codec'):
+ if not metadata['tags']['container-format'] in config['container_formats']:
+ return False
+
+ if config['video_codecs'] and metadata['tags'].get('audio-codec'):
+ if not metadata['tags']['video-codec'] in config['video_codecs']:
+ return False
+
+ if config['audio_codecs'] and metadata['tags'].get('audio-codec'):
+ if not metadata['tags']['audio-codec'] in config['audio_codecs']:
+ return False
+
+ if config['dimensions_match']:
+ if not metadata['videoheight'] <= medium_config['max_height']:
+ return False
+ if not metadata['videowidth'] <= medium_config['max_width']:
+ return False
+
+ if config['force_transcode']:
+ _log.info(
+ 'skip_transcode: Input passed tests, '
+ 'but force_transcode is enabled.')
+ return False
+
+ return True
+
diff --git a/mediagoblin/templates/mediagoblin/user_pages/media.html b/mediagoblin/templates/mediagoblin/user_pages/media.html
index 29d7874..7e18425 100644
--- a/mediagoblin/templates/mediagoblin/user_pages/media.html
+++ b/mediagoblin/templates/mediagoblin/user_pages/media.html
@@ -105,9 +105,6 @@
<form action="{{ request.urlgen('mediagoblin.user_pages.media_post_comment',
user= media.get_uploader.username,
media_id=media.id) }}" method="POST" id="form_comment">
- <p>
- {% trans %}You can use <a href="http://daringfireball.net/projects/markdown/basics">Markdown</a> for formatting.{% endtrans %}
- </p>
{{ wtforms_util.render_divs(comment_form) }}
<div class="form_submit_buttons">
<input type="submit" value="{% trans %}Add this comment{% endtrans %}" class="button_action" />
diff --git a/mediagoblin/user_pages/forms.py b/mediagoblin/user_pages/forms.py
index 9e8ccf0..c7398d8 100644
--- a/mediagoblin/user_pages/forms.py
+++ b/mediagoblin/user_pages/forms.py
@@ -20,8 +20,11 @@ from mediagoblin.tools.translate import fake_ugettext_passthrough as _
class MediaCommentForm(wtforms.Form):
comment_content = wtforms.TextAreaField(
- '',
- [wtforms.validators.Required()])
+ _('Comment'),
+ [wtforms.validators.Required()],
+ description=_(u'You can use '
+ u'<a href="http://daringfireball.net/projects/markdown/basics">'
+ u'Markdown</a> for formatting.'))
class ConfirmDeleteForm(wtforms.Form):
confirm = wtforms.BooleanField(
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment