Created
January 22, 2013 21:54
-
-
Save joar/4598837 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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