X-Git-Url: https://vcs.fsf.org/?a=blobdiff_plain;f=mediagoblin%2Fmedia_types%2Fimage%2Fprocessing.py;h=e6a34ca004e44a3f011f3fcdcba96b1d27629948;hb=45ab3e07ef26199572207f5d826e6d912eb5b336;hp=e493eb2bdc560de93b1d44ecf6b5052263d13c7f;hpb=4535f7597f112443d8997bbd6b8a445612c2440d;p=mediagoblin.git diff --git a/mediagoblin/media_types/image/processing.py b/mediagoblin/media_types/image/processing.py index e493eb2b..e6a34ca0 100644 --- a/mediagoblin/media_types/image/processing.py +++ b/mediagoblin/media_types/image/processing.py @@ -1,5 +1,5 @@ # GNU MediaGoblin -- federated, autonomous media hosting -# Copyright (C) 2011 MediaGoblin contributors. See AUTHORS. +# 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 @@ -16,94 +16,151 @@ import Image import os +import logging from mediagoblin import mg_globals as mgg - +from mediagoblin.decorators import get_workbench from mediagoblin.processing import BadMediaFail, \ - create_pub_filepath, THUMB_SIZE, MEDIUM_SIZE + create_pub_filepath, FilenameBuilder +from mediagoblin.tools.exif import exif_fix_image_orientation, \ + extract_exif, clean_exif, get_gps_data, get_useful, \ + exif_image_needs_rotation -################################ -# Media processing initial steps -################################ +_log = logging.getLogger(__name__) -def process_image(entry): +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 """ - Code to process an image + 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 + + 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 + + +@get_workbench +def process_image(entry, workbench=None): + """Code to process an image. Will be run by celery. + + A Workbench() represents a local tempory dir. It is automatically + cleaned up when this function exits. """ - workbench = mgg.workbench_manager.create_workbench() # Conversions subdirectory to avoid collisions conversions_subdir = os.path.join( workbench.dir, 'conversions') os.mkdir(conversions_subdir) - - queued_filepath = entry['queued_media_file'] + queued_filepath = entry.queued_media_file queued_filename = workbench.localized_file( mgg.queue_store, queued_filepath, 'source') + name_builder = FilenameBuilder(queued_filename) - filename_bits = os.path.splitext(queued_filename) - basename = os.path.split(filename_bits[0])[1] - extension = filename_bits[1].lower() - - try: - thumb = Image.open(queued_filename) - except IOError: - raise BadMediaFail() + # EXIF extraction + exif_tags = extract_exif(queued_filename) + gps_data = get_gps_data(exif_tags) - thumb.thumbnail(THUMB_SIZE, 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, + (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) - medium_processed = False - - if medium.size[0] > MEDIUM_SIZE[0] or medium.size[1] > MEDIUM_SIZE[1]: - medium.thumbnail(MEDIUM_SIZE, Image.ANTIALIAS) + if medium.size[0] > mgg.global_config['media:medium']['max_width'] \ + 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, + (mgg.global_config['media:medium']['max_width'], + mgg.global_config['media:medium']['max_height'])) + else: + medium_filepath = None + + # Copy our queued local workbench to its final destination + original_filepath = create_pub_filepath( + entry, name_builder.fill('{basename}{ext}')) + mgg.public_store.copy_local_to_storage(queued_filename, original_filepath) + + # Remove queued media file from storage and database + mgg.queue_store.delete_file(queued_filepath) + entry.queued_media_file = [] - medium_filename = 'medium' + extension - medium_filepath = create_pub_filepath(entry, medium_filename) - tmp_medium_filename = os.path.join( - conversions_subdir, medium_filename) + # Insert media file information into database + media_files_dict = entry.setdefault('media_files', {}) + media_files_dict[u'thumb'] = thumb_filepath + media_files_dict[u'original'] = original_filepath + if medium_filepath: + media_files_dict[u'medium'] = medium_filepath - with file(tmp_medium_filename, 'w') as medium_file: - medium.save(medium_file) + # Insert exif data into database + exif_all = clean_exif(exif_tags) - mgg.public_store.copy_local_to_storage( - tmp_medium_filename, medium_filepath) + if len(exif_all): + entry.media_data_init(exif_all=exif_all) - medium_processed = True + if len(gps_data): + for key in list(gps_data.keys()): + gps_data['gps_' + key] = gps_data.pop(key) + entry.media_data_init(**gps_data) - # 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) +if __name__ == '__main__': + import sys + import pprint - with mgg.public_store.get_file(original_filepath, 'wb') \ - as original_file: - original_file.write(queued_file.read()) + pp = pprint.PrettyPrinter() - mgg.queue_store.delete_file(queued_filepath) - entry['queued_media_file'] = [] - media_files_dict = entry.setdefault('media_files', {}) - media_files_dict['thumb'] = thumb_filepath - media_files_dict['original'] = original_filepath - if medium_processed: - media_files_dict['medium'] = medium_filepath + result = extract_exif(sys.argv[1]) + gps = get_gps_data(result) + clean = clean_exif(result) + useful = get_useful(clean) - # clean up workbench - workbench.destroy_self() + print pp.pprint( + clean)