storage_class = string(default="mediagoblin.storage.filestorage:BasicFileStorage")
base_dir = string(default="%(here)s/user_dev/media/queue")
+ [media:medium]
+ # Dimensions used when creating media display images.
+ max_width = integer(default=640)
+ max_height = integer(default=640)
+
+ [media:thumb]
+ # Dimensions used when creating media thumbnails
+ # This is unfortunately not implemented in the media
+ # types yet. You can help!
+ # TODO: Make plugins follow the media size settings
+ max_width = integer(default=180)
+ max_height = integer(default=180)
- # Should we keep the original file?
[media_type:mediagoblin.media_types.video]
+ # Should we keep the original file?
keep_original = boolean(default=False)
-create_spectrogram = boolean(default=False)
+ [media_type:mediagoblin.media_types.audio]
+ # vorbisenc qualiy
+ quality = float(default=0.3)
++create_spectrogram = boolean(default=True)
+
[beaker.cache]
type = string(default="file")
from mediagoblin import mg_globals as mgg
from mediagoblin.processing import BadMediaFail, \
- create_pub_filepath, THUMB_SIZE, MEDIUM_SIZE, FilenameBuilder
- create_pub_filepath
++ create_pub_filepath, FilenameBuilder
from mediagoblin.tools.exif import exif_fix_image_orientation, \
- extract_exif, clean_exif, get_gps_data, get_useful
+ extract_exif, clean_exif, get_gps_data, get_useful, \
+ exif_image_needs_rotation
- size_limits (optional) -- image is only resized if it exceeds this size
-
+ _log = logging.getLogger(__name__)
+
++
+def resize_image(entry, filename, new_path, exif_tags, workdir, new_size,
+ size_limits=(0, 0)):
+ """Store a resized version of an image and return its pathname.
+
+ Arguments:
+ entry -- the entry for the image to resize
+ filename -- the filename of the original image being resized
+ new_path -- public file path for the new resized image
+ exif_tags -- EXIF data for the original image
+ workdir -- directory path for storing converted image files
+ new_size -- 2-tuple size for the resized image
-
- if ((resized.size[0] > size_limits[0]) or
- (resized.size[1] > size_limits[1])):
- resized.thumbnail(new_size, Image.ANTIALIAS)
+ """
+ try:
+ resized = Image.open(filename)
+ except IOError:
+ raise BadMediaFail()
+ resized = exif_fix_image_orientation(resized, exif_tags) # Fix orientation
++ resized.thumbnail(new_size, Image.ANTIALIAS)
+
+ # Copy the new file to the conversion subdir, then remotely.
+ tmp_resized_filename = os.path.join(workdir, new_path[-1])
+ with file(tmp_resized_filename, 'w') as resized_file:
+ resized.save(resized_file)
+ mgg.public_store.copy_local_to_storage(tmp_resized_filename, new_path)
+
++
+ SUPPORTED_FILETYPES = ['png', 'gif', 'jpg', 'jpeg']
+
+
+ def sniff_handler(media_file, **kw):
+ if kw.get('media') is not None: # That's a double negative!
+ name, ext = os.path.splitext(kw['media'].filename)
+ clean_ext = ext[1:].lower() # Strip the . from ext and make lowercase
+
+ _log.debug('name: {0}\next: {1}\nlower_ext: {2}'.format(
+ name,
+ ext,
+ clean_ext))
+
+ if clean_ext in SUPPORTED_FILETYPES:
+ _log.info('Found file extension in supported filetypes')
+ return True
+ else:
+ _log.debug('Media present, extension not found in {0}'.format(
+ SUPPORTED_FILETYPES))
+ else:
+ _log.warning('Need additional information (keyword argument \'media\')'
+ ' to be able to handle sniffing')
+
+ return False
+
+
def process_image(entry):
"""
Code to process an image
exif_tags = extract_exif(queued_filename)
gps_data = get_gps_data(exif_tags)
- try:
- thumb = Image.open(queued_filename)
- except IOError:
- raise BadMediaFail()
-
- thumb = exif_fix_image_orientation(thumb, exif_tags)
-
- thumb.thumbnail(
- (mgg.global_config['media:thumb']['max_width'],
- mgg.global_config['media:thumb']['max_height']),
- Image.ANTIALIAS)
-
- # Copy the thumb to the conversion subdir, then remotely.
- thumb_filename = 'thumbnail' + extension
- thumb_filepath = create_pub_filepath(entry, thumb_filename)
-
- tmp_thumb_filename = os.path.join(
- conversions_subdir, thumb_filename)
-
- with file(tmp_thumb_filename, 'w') as thumb_file:
- thumb.save(thumb_file)
-
- mgg.public_store.copy_local_to_storage(
- tmp_thumb_filename, thumb_filepath)
+ # Always create a small thumbnail
+ thumb_filepath = create_pub_filepath(
+ entry, name_builder.fill('{basename}.thumbnail{ext}'))
+ resize_image(entry, queued_filename, thumb_filepath,
- exif_tags, conversions_subdir, THUMB_SIZE)
++ exif_tags, conversions_subdir,
++ (mgg.global_config['media:thumb']['max_width'],
++ mgg.global_config['media:thumb']['max_height']))
# If the size of the original file exceeds the specified size of a `medium`
- # file, a `medium.jpg` files is created and later associated with the media
+ # file, a `.medium.jpg` files is created and later associated with the media
# entry.
medium = Image.open(queued_filename)
- if medium.size[0] > MEDIUM_SIZE[0] or medium.size[1] > MEDIUM_SIZE[1] \
- or exif_image_needs_rotation(exif_tags):
-
- # Fix orientation
- medium = exif_fix_image_orientation(medium, exif_tags)
-
+ if medium.size[0] > mgg.global_config['media:medium']['max_width'] \
- or medium.size[1] > mgg.global_config['media:medium']['max_height']:
- medium.thumbnail(
++ or medium.size[1] > mgg.global_config['media:medium']['max_height'] \
++ or exif_image_needs_rotation(exif_tags):
+ medium_filepath = create_pub_filepath(
+ entry, name_builder.fill('{basename}.medium{ext}'))
+ resize_image(
+ entry, queued_filename, medium_filepath,
- exif_tags, conversions_subdir, MEDIUM_SIZE, MEDIUM_SIZE)
++ exif_tags, conversions_subdir,
+ (mgg.global_config['media:medium']['max_width'],
- mgg.global_config['media:medium']['max_height']),
- Image.ANTIALIAS)
-
- medium_filename = 'medium' + extension
- medium_filepath = create_pub_filepath(entry, medium_filename)
-
- tmp_medium_filename = os.path.join(
- conversions_subdir, medium_filename)
-
- with file(tmp_medium_filename, 'w') as medium_file:
- medium.save(medium_file)
-
- mgg.public_store.copy_local_to_storage(
- tmp_medium_filename, medium_filepath)
++ mgg.global_config['media:medium']['max_height']))
+ else:
+ medium_filepath = None
# we have to re-read because unlike PIL, not everything reads
# things in string representation :)
queued_file = file(queued_filename, 'rb')
with queued_file:
- #create_pub_filepath(entry, queued_filepath[-1])
- original_filepath = create_pub_filepath(entry, basename + extension)
+ original_filepath = create_pub_filepath(
- entry, name_builder.fill('{basename}{ext}') )
++ entry, name_builder.fill('{basename}{ext}'))
with mgg.public_store.get_file(original_filepath, 'wb') \
as original_file:
MEDIA_MANAGER = {
"human_readable": "Video",
-- "processor": process_video, # alternately a string,
-- # 'mediagoblin.media_types.image.processing'?
++ "processor": process_video, # alternately a string,
++ # 'mediagoblin.media_types.image.processing'?
+ "sniff_handler": sniff_handler,
"display_template": "mediagoblin/media_displays/video.html",
"default_thumb": "images/media_thumbs/video.jpg",
- # TODO: This list should be autogenerated based on gst plugins
"accepted_extensions": [
- "mp4", "mov", "webm", "avi", "3gp", "3gpp", "mkv", "ogv", "ogg",
- "m4v"]}
- "mp4", "mov", "webm", "avi", "3gp", "3gpp", "mkv", "ogv"]}
++ "mp4", "mov", "webm", "avi", "3gp", "3gpp", "mkv", "ogv", "m4v"]}
import os
from mediagoblin import mg_globals as mgg
-from mediagoblin.processing import create_pub_filepath
+from mediagoblin.processing import mark_entry_failed, \
- THUMB_SIZE, MEDIUM_SIZE, create_pub_filepath, FilenameBuilder
++ create_pub_filepath, FilenameBuilder
from . import transcoders
logging.basicConfig()
from mediagoblin.tools.translate import lazy_pass_to_ugettext as _
-from mediagoblin.media_types import get_media_manager
-
_log = logging.getLogger(__name__)
- THUMB_SIZE = 180, 180
- MEDIUM_SIZE = 640, 640
-
def create_pub_filepath(entry, filename):
return mgg.public_store.get_unique_filepath(
--- /dev/null
+ /**
+ * 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/>.
+ */
+
+ var audioPlayer = new Object();
+
+ (function (audioPlayer) {
+ audioPlayer.init = function (audioElement) {
+ audioPlayer.audioElement = audioElement;
+ console.log(audioElement);
+ attachEvents();
+ $(audioElement).hide();
+ };
+
+ function attachEvents () {
+ audioPlayer.audioElement.addEventListener('durationchange', audioPlayer.durationChange, true);
+ audioPlayer.audioElement.addEventListener('timeupdate', audioPlayer.timeUpdate, true);
+ audioPlayer.audioElement.addEventListener('progress', audioPlayer.onProgress, true);
+ $(document).ready( function () {
+ $('.audio-spectrogram').delegate('.seekbar', 'click', audioPlayer.onSeek);
+ $('.audio-spectrogram').delegate('.audio-control-play-pause', 'click', audioPlayer.playPause);
+ });
+ }
+
+ audioPlayer.onProgress = function(a, b, c) {
+ console.log(a, b, c);
+ buffered = audioPlayer.audioElement.buffered;
+
+ ranges = new Array();
+
+ for (i = 0; i < buffered.length; i++) {
+ ranges[i] = new Array();
+ ranges[i][0] = buffered.start(i);
+ ranges[i][1] = buffered.end(i);
+ }
+ console.log('ranges', ranges);
+ $('.audio-spectrogram .buffered').width(
+ (ranges[0][1] / audioPlayer.audioElement.duration) * audioPlayer.imageElement.width());
+ };
+
+ audioPlayer.onSeek = function (e) {
+ console.log('onSeek', e);
+ im = audioPlayer.imageElement;
+ pos = e.offsetX / im.width();
+ audioPlayer.audioElement.currentTime = pos * audioPlayer.audioElement.duration;
+ audioPlayer.audioElement.play();
+ audioPlayer.setState(audioPlayer.PLAYING);
+ };
+
+ audioPlayer.playPause = function (e) {
+ console.log('playPause', e);
+ if (audioPlayer.audioElement.paused) {
+ audioPlayer.audioElement.play();
+ audioPlayer.setState(audioPlayer.PLAYING);
+ } else {
+ audioPlayer.audioElement.pause();
+ audioPlayer.setState(audioPlayer.PAUSED);
+ }
+ };
+
+ audioPlayer.NULL = null;
+ audioPlayer.PLAYING = 2;
+ audioPlayer.PAUSED = 4;
+
+ audioPlayer.state = audioPlayer.NULL;
+
+ audioPlayer.setState = function (state) {
+ if (state == audioPlayer.state) {
+ return;
+ }
+
+ switch (state) {
+ case audioPlayer.PLAYING:
+ $('.audio-spectrogram .audio-control-play-pause')
+ .removeClass('paused').addClass('playing')
+ .text('■');
+ break;
+ case audioPlayer.PAUSED:
+ $('.audio-spectrogram .audio-control-play-pause')
+ .removeClass('playing').addClass('paused')
+ .text('▶');
+ break;
+ }
+ };
+
+ audioPlayer.durationChange = function () {
+ duration = audioPlayer.audioElement.duration;
+ };
+
+ audioPlayer.timeUpdate = function () {
+ currentTime = audioPlayer.audioElement.currentTime;
+ playhead = audioPlayer.imageElement.parent().find('.playhead');
+ playhead.css('width', (currentTime / audioPlayer.audioElement.duration) * audioPlayer.imageElement.width());
+ time = formatTime(currentTime);
+ duration = formatTime(audioPlayer.audioElement.duration);
+ audioPlayer.imageElement.parent().find('.audio-currentTime').text(time + '/' + duration);
+ };
+
+ function formatTime(seconds) {
+ var h = Math.floor(seconds / (60 * 60));
+ var m = Math.floor((seconds - h * 60 * 60) / 60);
+ var s = Math.round(seconds - h * 60 * 60 - m * 60);
+ return '' + (h ? (h < 10 ? '0' + h : h) + ':' : '') + (m < 10 ? '0' + m : m) + ':' + (s < 10 ? '0' + s : s);
+ }
+
+ audioPlayer.attachToImage = function (imageElement) {
+ /**
+ * Attach the player to an image element
+ */
+ console.log(imageElement);
+ im = $(imageElement);
+ audioPlayer.imageElement = im;
+ $('<div class="playhead"></div>').appendTo(im.parent());
+ $('<div class="buffered"></div>').appendTo(im.parent());
+ $('<div class="seekbar"></div>').appendTo(im.parent());
+ $('<div class="audio-control-play-pause paused">▶</div>').appendTo(im.parent());
+ $('<div class="audio-currentTime">00:00</div>').appendTo(im.parent());
+ };
+ })(audioPlayer);
+
+ $(document).ready(function () {
++ if (!$('.audio-media').length) {
++ return;
++ }
++
++ console.log('Initializing audio player');
++
+ audioElements = $('.audio-media .audio-player');
+ audioPlayer.init(audioElements[0]);
+ audioPlayer.attachToImage($('.audio-spectrogram img')[0]);
+ });
+
*/
$(document).ready(function () {
++ if (!$('#tile-map').length) {
++ return;
++ }
++ console.log('Initializing map');
++
var longitude = Number(
$('#tile-map #gps-longitude').val());
var latitude = Number(
from cgi import FieldStorage
from celery import registry
--import urllib,urllib2
++import urllib
++import urllib2
import logging
_log = logging.getLogger(__name__)
from mediagoblin.tools.translate import pass_to_ugettext as _
from mediagoblin.tools.response import render_to_response, redirect
from mediagoblin.decorators import require_active_login
-from mediagoblin.submit import forms as submit_forms, security
-from mediagoblin.processing import mark_entry_failed, ProcessMedia
+from mediagoblin.submit import forms as submit_forms
+from mediagoblin.processing import mark_entry_failed
+from mediagoblin.processing.task import ProcessMedia
from mediagoblin.messages import add_message, SUCCESS
- from mediagoblin.media_types import get_media_type_and_manager, \
+ from mediagoblin.media_types import sniff_media, \
InvalidFileType, FileTypeNotSupported
raise
if mg_globals.app_config["push_urls"]:
-- feed_url=request.urlgen(
++ feed_url = request.urlgen(
'mediagoblin.user_pages.atom_feed',
-- qualified=True,user=request.user.username)
++ qualified=True,
++ user=request.user.username)
hubparameters = {
'hub.mode': 'publish',
'hub.url': feed_url}
user=request.user.username)
except Exception as e:
'''
-- This section is intended to catch exceptions raised in
++ This section is intended to catch exceptions raised in
mediagobling.media_types
'''
-
if isinstance(e, InvalidFileType) or \
isinstance(e, FileTypeNotSupported):
submit_form.file.errors.append(
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import urlparse
-import pkg_resources
+import os
import re
- import time
from nose.tools import assert_equal, assert_true, assert_false
+from pkg_resources import resource_filename
--from mediagoblin.tests.tools import setup_fresh_app, get_test_app, \
++from mediagoblin.tests.tools import get_test_app, \
fixture_add_user
from mediagoblin import mg_globals
- from mediagoblin.processing import create_pub_filepath
--from mediagoblin.tools import template, common
-
-GOOD_JPG = pkg_resources.resource_filename(
- 'mediagoblin.tests', 'test_submission/good.jpg')
-GOOD_PNG = pkg_resources.resource_filename(
- 'mediagoblin.tests', 'test_submission/good.png')
-EVIL_FILE = pkg_resources.resource_filename(
- 'mediagoblin.tests', 'test_submission/evil')
-EVIL_JPG = pkg_resources.resource_filename(
- 'mediagoblin.tests', 'test_submission/evil.jpg')
-EVIL_PNG = pkg_resources.resource_filename(
- 'mediagoblin.tests', 'test_submission/evil.png')
++from mediagoblin.tools import template
++
+
+def resource(filename):
+ return resource_filename('mediagoblin.tests', 'test_submission/' + filename)
+
++
+GOOD_JPG = resource('good.jpg')
+GOOD_PNG = resource('good.png')
+EVIL_FILE = resource('evil')
+EVIL_JPG = resource('evil.jpg')
+EVIL_PNG = resource('evil.png')
+BIG_BLUE = resource('bigblue.png')
GOOD_TAG_STRING = 'yin,yang'
BAD_TAG_STRING = 'rage,' + 'f' * 26 + 'u' * 26
+FORM_CONTEXT = ['mediagoblin/submit/start.html', 'submit_form']
+REQUEST_CONTEXT = ['mediagoblin/user_pages/user.html', 'request']
+
+
class TestSubmission:
def setUp(self):
self.test_app = get_test_app()
def logout(self):
self.test_app.get('/auth/logout/')
-
+ def do_post(self, data, *context_keys, **kwargs):
+ url = kwargs.pop('url', '/submit/')
+ do_follow = kwargs.pop('do_follow', False)
+ template.clear_test_template_context()
+ response = self.test_app.post(url, data, **kwargs)
+ if do_follow:
+ response.follow()
+ context_data = template.TEMPLATE_TEST_CONTEXT
+ for key in context_keys:
+ context_data = context_data[key]
+ return response, context_data
++
+ def upload_data(self, filename):
+ return {'upload_files': [('file', filename)]}
+
+ def check_comments(self, request, media, count):
+ comments = request.db.MediaComment.find({'media_entry': media._id})
+ assert_equal(count, len(list(comments)))
+
def test_missing_fields(self):
# Test blank form
# ---------------
# Test blank file
# ---------------
- template.clear_test_template_context()
- response = self.test_app.post(
- '/submit/', {
- 'title': 'test title'})
- context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/submit/start.html']
- form = context['submit_form']
- assert form.file.errors == [u'You must provide a file.']
-
-
- def test_normal_uploads(self):
- # Test JPG
- # --------
- template.clear_test_template_context()
- response = self.test_app.post(
- '/submit/', {
- 'title': 'Normal upload 1'
- }, upload_files=[(
- 'file', GOOD_JPG)])
+ response, form = self.do_post({'title': 'test title'}, *FORM_CONTEXT)
+ assert_equal(form.file.errors, [u'You must provide a file.'])
- # User should be redirected
- response.follow()
- assert_equal(
- urlparse.urlsplit(response.location)[2],
- '/u/chris/')
- assert template.TEMPLATE_TEST_CONTEXT.has_key(
- 'mediagoblin/user_pages/user.html')
+ def check_url(self, response, path):
+ assert_equal(urlparse.urlsplit(response.location)[2], path)
- assert_true(context.has_key('mediagoblin/user_pages/user.html'))
+ def check_normal_upload(self, title, filename):
+ response, context = self.do_post({'title': title}, do_follow=True,
+ **self.upload_data(filename))
+ self.check_url(response, '/u/{0}/'.format(self.test_user.username))
++ assert_true('mediagoblin/user_pages/user.html' in context)
# Make sure the media view is at least reachable, logged in...
- self.test_app.get('/u/chris/m/normal-upload-1/')
+ url = '/u/{0}/m/{1}/'.format(self.test_user.username,
+ title.lower().replace(' ', '-'))
+ self.test_app.get(url)
# ... and logged out too.
self.logout()
- self.test_app.get('/u/chris/m/normal-upload-1/')
- # Log back in for the remaining tests.
- self.login()
+ self.test_app.get(url)
- # Test PNG
- # --------
- template.clear_test_template_context()
- response = self.test_app.post(
- '/submit/', {
- 'title': 'Normal upload 2'
- }, upload_files=[(
- 'file', GOOD_PNG)])
+ def test_normal_jpg(self):
+ self.check_normal_upload('Normal upload 1', GOOD_JPG)
- response.follow()
- assert_equal(
- urlparse.urlsplit(response.location)[2],
- '/u/chris/')
- assert template.TEMPLATE_TEST_CONTEXT.has_key(
- 'mediagoblin/user_pages/user.html')
+ def test_normal_png(self):
+ self.check_normal_upload('Normal upload 2', GOOD_PNG)
+
+ def check_media(self, request, find_data, count=None):
+ media = request.db.MediaEntry.find(find_data)
+ if count is not None:
+ assert_equal(media.count(), count)
+ if count == 0:
+ return
+ return media[0]
def test_tags(self):
# Good tag string
# Confirm deletion
# ---------------------------------------------------
- response = self.test_app.post(
- request.urlgen('mediagoblin.user_pages.media_confirm_delete',
- # No work: user=media.uploader().username,
- user=self.test_user.username,
- media=media._id),
- {'confirm': 'y'})
-
- response.follow()
-
- request = template.TEMPLATE_TEST_CONTEXT[
- 'mediagoblin/user_pages/user.html']['request']
+ response, request = self.do_post({'confirm': 'y'}, *REQUEST_CONTEXT,
+ do_follow=True, url=delete_url)
+ self.check_media(request, {'_id': media._id}, 0)
+ self.check_comments(request, media, 0)
- # Does media entry still exist?
- assert_false(
- request.db.MediaEntry.find(
- {'_id': media._id}).count())
+ def test_evil_file(self):
+ # Test non-suppoerted file with non-supported extension
+ # -----------------------------------------------------
+ response, form = self.do_post({'title': 'Malicious Upload 1'},
+ *FORM_CONTEXT,
+ **self.upload_data(EVIL_FILE))
+ assert_equal(len(form.file.errors), 1)
+ assert_true(re.match(
+ r'^Could not extract any file extension from ".*?"$',
+ str(form.file.errors[0])))
- def test_malicious_uploads(self):
- # Test non-suppoerted file with non-supported extension
- # -----------------------------------------------------
- template.clear_test_template_context()
- response = self.test_app.post(
- '/submit/', {
- 'title': 'Malicious Upload 1'
- }, upload_files=[(
- 'file', EVIL_FILE)])
-
- context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/submit/start.html']
- form = context['submit_form']
- assert 'Sorry, I don\'t support that file type :(' == \
- str(form.file.errors[0])
- assert len(form.file.errors) == 1
-
+ def test_sniffing(self):
+ '''
+ Test sniffing mechanism to assert that regular uploads work as intended
+ '''
+ template.clear_test_template_context()
+ response = self.test_app.post(
+ '/submit/', {
+ 'title': 'UNIQUE_TITLE_PLS_DONT_CREATE_OTHER_MEDIA_WITH_THIS_TITLE'
+ }, upload_files=[(
+ 'file', GOOD_JPG)])
+
+ response.follow()
+
+ context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/user_pages/user.html']
+
+ request = context['request']
+
+ media = request.db.MediaEntry.find_one({
+ u'title': u'UNIQUE_TITLE_PLS_DONT_CREATE_OTHER_MEDIA_WITH_THIS_TITLE'})
+
+ assert media.media_type == 'mediagoblin.media_types.image'
+
- # NOTE: These images should ultimately fail, but they
+ def check_false_image(self, title, filename):
+ # NOTE: The following 2 tests will ultimately fail, but they
# *will* pass the initial form submission step. Instead,
# they'll be caught as failures during the processing step.
+ response, context = self.do_post({'title': title}, do_follow=True,
+ **self.upload_data(filename))
+ self.check_url(response, '/u/{0}/'.format(self.test_user.username))
+ entry = mg_globals.database.MediaEntry.find_one({'title': title})
+ assert_equal(entry.state, 'failed')
+ assert_equal(entry.fail_error, u'mediagoblin.processing:BadMediaFail')
+ def test_evil_jpg(self):
# Test non-supported file with .jpg extension
# -------------------------------------------
- template.clear_test_template_context()
- response = self.test_app.post(
- '/submit/', {
- 'title': 'Malicious Upload 2'
- }, upload_files=[(
- 'file', EVIL_JPG)])
- response.follow()
- assert_equal(
- urlparse.urlsplit(response.location)[2],
- '/u/chris/')
-
- entry = mg_globals.database.MediaEntry.find_one(
- {'title': 'Malicious Upload 2'})
- assert_equal(entry.state, 'failed')
- assert_equal(
- entry['fail_error'],
- u'mediagoblin.processing:BadMediaFail')
+ self.check_false_image('Malicious Upload 2', EVIL_JPG)
+ def test_evil_png(self):
# Test non-supported file with .png extension
# -------------------------------------------
- template.clear_test_template_context()
- response = self.test_app.post(
- '/submit/', {
- 'title': 'Malicious Upload 3'
- }, upload_files=[(
- 'file', EVIL_PNG)])
- response.follow()
- assert_equal(
- urlparse.urlsplit(response.location)[2],
- '/u/chris/')
-
- entry = mg_globals.database.MediaEntry.find_one(
- {'title': 'Malicious Upload 3'})
- assert_equal(entry.state, 'failed')
- assert_equal(
- entry['fail_error'],
- u'mediagoblin.processing:BadMediaFail')
+ self.check_false_image('Malicious Upload 3', EVIL_PNG)
+
+ def test_processing(self):
+ data = {'title': 'Big Blue'}
+ response, request = self.do_post(data, *REQUEST_CONTEXT, do_follow=True,
+ **self.upload_data(BIG_BLUE))
+ media = self.check_media(request, data, 1)
+ last_size = 1024 ** 3 # Needs to be larger than bigblue.png
+ for key, basename in (('original', 'bigblue.png'),
+ ('medium', 'bigblue.medium.png'),
+ ('thumb', 'bigblue.thumbnail.png')):
+ # Does the processed image have a good filename?
+ filename = resource_filename(
+ 'mediagoblin.tests',
+ os.path.join('test_user_dev/media/public',
+ *media['media_files'].get(key, [])))
+ assert_true(filename.endswith('_' + basename))
+ # Is it smaller than the last processed image we looked at?
+ size = os.stat(filename).st_size
+ assert_true(last_size > size)
+ last_size = size