From: Jessica Tallon Date: Thu, 11 Dec 2014 11:29:03 +0000 (+0000) Subject: Fix #1053 - Add height and width attributes and MetadataProcess task X-Git-Url: https://vcs.fsf.org/?a=commitdiff_plain;h=4a09d5956aed00b6dbc1dea53e217bee1f9dc0f0;p=mediagoblin.git Fix #1053 - Add height and width attributes and MetadataProcess task Added "height" and "width" attributes to "image" and "fullImage" in the API where possible. The height and width of images wasn't being stored anywhere so I've created a task to add or update the metadata on images and also started adding those to new images when they're submitted in the InitialProcessor. --- diff --git a/mediagoblin/db/models.py b/mediagoblin/db/models.py index 98a91ccc..440aa682 100644 --- a/mediagoblin/db/models.py +++ b/mediagoblin/db/models.py @@ -591,6 +591,22 @@ class MediaEntry(Base, MediaEntryMixin): ), } + # Add image height and width if possible. We didn't use to store this + # data and we're not able (and maybe not willing) to re-process all + # images so it's possible this might not exist. + if self.get_file_metadata("thumb", "height"): + height = self.get_file_metadata("thumb", "height") + context["image"]["height"] = height + if self.get_file_metadata("thumb", "width"): + width = self.get_file_metadata("thumb", "width") + context["image"]["width"] = width + if self.get_file_metadata("original", "height"): + height = self.get_file_metadata("original", "height") + context["fullImage"]["height"] = height + if self.get_file_metadata("original", "height"): + width = self.get_file_metadata("original", "width") + context["fullImage"]["width"] = width + return context def unserialize(self, data): diff --git a/mediagoblin/media_types/image/processing.py b/mediagoblin/media_types/image/processing.py index e0ddfe87..951a720d 100644 --- a/mediagoblin/media_types/image/processing.py +++ b/mediagoblin/media_types/image/processing.py @@ -225,19 +225,32 @@ class CommonImageProcessor(MediaProcessor): self.entry, self.process_filename, self.name_builder.fill('{basename}{ext}')) - def extract_metadata(self): - # Is there any GPS data + def extract_metadata(self, file): + """ Extract all the metadata from the image and store """ + # Extract GPS data and store in Location gps_data = get_gps_data(self.exif_tags) + if len(gps_data): + Location.create({"position": gps_data}, self.entry) + # Insert exif data into database exif_all = clean_exif(self.exif_tags) if len(exif_all): self.entry.media_data_init(exif_all=exif_all) - if len(gps_data): - Location.create({"position": gps_data}, self.entry) - self.entry.media_data_init(**gps_data) + # Extract file metadata + try: + im = Image.open(self.process_filename) + except IOError: + raise BadMediaFail() + + metadata = { + "width": im.size[0], + "height": im.size[1], + } + + self.entry.set_file_metadata(file, **metadata) class InitialProcessor(CommonImageProcessor): @@ -252,6 +265,9 @@ class InitialProcessor(CommonImageProcessor): """ Determine if this media type is eligible for processing """ + if entry is None and state is None: + raise ValueError("Must specify either entry or state") + if not state: state = entry.state return state in ( @@ -301,7 +317,7 @@ class InitialProcessor(CommonImageProcessor): quality=quality) self.generate_thumb(size=thumb_size, filter=filter, quality=quality) self.copy_original() - self.extract_metadata() + self.extract_metadata('original') self.delete_queue_file() @@ -318,6 +334,9 @@ class Resizer(CommonImageProcessor): """ Determine if this media type is eligible for processing """ + if entry is None and state is None: + raise ValueError("Must specify either entry or state") + if not state: state = entry.state return state in 'processed' @@ -366,13 +385,52 @@ class Resizer(CommonImageProcessor): elif file == 'thumb': self.generate_thumb(size=size, filter=filter, quality=quality) + self.extract_metadata(file) + +class MetadataProcessing(CommonImageProcessor): + """ Extraction and storage of media's metadata for processed images """ + + name = 'metadatabuilder' + description = 'Add or update image metadata' + + @classmethod + def media_is_eligible(cls, entry=None, state=None): + if entry is None and state is None: + raise ValueError("Must specify either entry or state") + + if state is None: + state = entry.state + + return state == 'processed' + + @classmethod + def generate_parser(cls): + parser = argparse.ArgumentParser( + description=cls.description, + prog=cls.name + ) + + parser.add_argument( + 'file', + choices=['original', 'medium', 'thumb'] + ) + + return parser + + @classmethod + def args_to_request(cls, args): + return request_from_args(args, ['file']) + + def process(self, file): + self.common_setup() + self.extract_metadata(file) class ImageProcessingManager(ProcessingManager): def __init__(self): super(ImageProcessingManager, self).__init__() self.add_processor(InitialProcessor) self.add_processor(Resizer) - + self.add_processor(MetadataProcessing) if __name__ == '__main__': import sys