degal/exif.py
author Tero Marttila <terom@fixme.fi>
Sun, 14 Jun 2009 23:43:40 +0300
changeset 120 55cb7fc9c8fb
child 121 3bed35e79f41
permissions -rw-r--r--
add new exif.py to abstract between different exif libraries, and add partially working support for pyexiv2 and EXIFpy
"""
    The various Exif backends that we have
"""

from __future__ import with_statement

import utils

class ExifHandler (object) :
    """
        Our generic interface for Exif data
    """

    def __init__ (self, file) :
        """
            Load Exif data from the given File.
        """

        self.file = file
    
    def image_tag (self, tag, default=None) :
        """
            Load and return the human-readable unicode-safe value for the given main image tag.
        """

        abstract

    def image_tags (self, tags) :
        """
            Load and return the given tags for the main image as a sequence of (tag, value) tuples , following the same
            rules as image_tag, except there should mainly be only one call to this function, encompassing all the
            relevant tags.

            The returned list should omit those values which were not found.
        """
    
        for tag in tags :
            # try and fetch value
            value = self.image_tag(tag)

            # filter out default not-found
            if value is not None :
                yield tag, value

try :
    import pyexiv2

except ImportError :
    PyExiv2_Handler = None

else :
    class PyExiv2_Handler (ExifHandler) :
        NAME = 'pyexiv2'

        def __init__ (self, file) :
            """
                Load as pyexiv2.Image
            """
            
            # create
            self.image = pyexiv2.Image(file.path)

            # load
            self.image.readMetadata()
        
        def image_tag (self, tag, default=None) :
            try :
                # XXX: this is wrong
                return self.image.interpretedExifValue("Exif.Photo.%s" % tag)
            
            except (IndexError, KeyError) :
                return default
    
try :
    from lib import EXIF

except ImportError :
    EXIF_Handler = None

else :
    class EXIF_Handler (ExifHandler) :
        NAME = 'EXIFpy'

        def __init__ (self, file) :
            """
                Load using EXIF.process_file
            """
            
            with file.open('rb') as fh :
                self.tags = EXIF.process_file(fh)
        
        def image_tag (self, tag, default=None) :
            # XXX: this is wrong
            return self.tags.get('Image %s' % (tag, ), default)

# ExifHandler implementations to use, in preference order
EXIF_HANDLERS = [
    # reasonably fast, but requires the native library and extension module
    PyExiv2_Handler,

    # pure-python, but very, very slow
    EXIF_Handler,
]

# default ExifHandler to use
EXIF_HANDLER = utils.first(EXIF_HANDLERS)

# ExifHandler implementations by name
EXIF_HANDLERS_BY_NAME = dict((h.NAME, h) for h in EXIF_HANDLERS)

def load (config, file) :
    """
        Load Exif data using the configured Exif handler, and return the instance.

        Returns None if there wasn't any Exif handler available for use.
    """

    if config.exif_handler :
        # explicit
        exif_handler = config.exif_handler

    elif config.exif_handler_name :
        # by name
        exif_handler = EXIF_HANDLERS_BY_NAME[config.exif_handler_name]

    else :
        # default
        exif_handler = EXIF_HANDLER
    
    if exif_handler :
        # found one, use it
        return exif_handler(file)

    else :
        # nothing :(
        return None