#!/usr/bin/env python """pyexiftool.py A small wrapper around the perl 'exiftool' command. This library has an understand of ExifTag 'types', and has some intelligence dealing with MakerNote tags. It was written in haste by jason moiron (email: 'am1vaXJvbkBqbW9pcm9uLm5ldA==\n'.decode('base64')) in order to have some exif support for an online gallery. EXIF.py is monolithic and outdated, and there haven't been many attempts at creating an EXIF library that works with many makernote tags. There was a LONG attempt at making something that could snarf out perl's "exiftool" tables, but the amount of effort that went into that is probably better put towards making the actual gallery. """ import subprocess, os PIPE = subprocess.PIPE STDOUT = subprocess.STDOUT def which(program): """Similar to command line tool "which" """ for path in os.environ['PATH'].split(os.pathsep): if not path.endswith('/'): path += '/' if os.path.exists(path + program): return path + program return None errormsg = """\ Error: exiftool not found (apt-get install libimage-exiftool-perl)""" exiftool = which('exiftool') if not exiftool: raise Exception(errormsg) def int_unit(s): spl = [tok.strip() for tok in s.split()] return int(spl[0]), spl[1] def float_unit(s): spl = [tok.strip() for tok in s.split()] return float(spl[0]), spl[1] def list_type(s): return s.split(' ') tags = { int : ['Image Width', 'ImageHeight', 'Preview Image Start', 'Preview Image Length', 'X Resolution', 'Y Resolution', 'ISO', 'Focal Units', 'Max Aperture', 'Min Aperture', 'Flash Activity', 'Zoom Source Width', 'Zoom Target Width', 'Target Aperture', 'Exposure Compensation', 'AEB Bracket Value', 'Num AF Points', 'Self-timer 2', 'Bulb Duration'], float : ['Red Balance', 'Blue Balance', 'Aperture', 'Focal Plane X Resolution', 'Focal Plane Y Resolution', 'Scale Factor To 35mm Equivalent', 'Focus Distance Upper', 'Focus Distance Lower'], int_unit : [''], float_unit : ['File Size'], list_type : ['Bits Per Sample'], } types = { int : 'int', float : 'float', int_unit : 'int (unit)', float_unit : 'float (unit)', list_type : 'list', } class ExifTag: def __init__(self, line): split = [sp.strip() for sp in line.split(':')] self.name = split[0] if len(split) == 1: self.value = '' else: self.value = self.parse_value(':'.join(split[1:])) def parse_value(self, value): for type,names in tags.items(): if self.name in names: self.type = types[type] return type(value) self.type = 'string' return value def __str__(self): return str(self.value) + ' (%s)' % self.type class ExifData: def __init__(self, filename): if not os.path.exists(filename): raise IOError("No such file: '%s'" % filename) self.maxTagLen = 0 self.filename = filename self.cmd = [exiftool, filename] self.output = self.run() self.generate_cache() def run(self): process = subprocess.Popen(self.cmd, stdout=PIPE, stderr=STDOUT) process.wait() # it is custom to return 0 on success return process.stdout.readlines() def generate_cache(self): self.output = [line.strip() for line in self.output] self._cache = {} for line in self.output: tag = ExifTag(line) self._cache[tag.name] = tag if len(tag.name) > self.maxTagLen: self.maxTagLen = len(tag.name) def dump(self): for tag, value in self._cache.items(): print tag.ljust(self.maxTagLen) + ': ' + str(value) if __name__ == '__main__': import sys, optparse parser = optparse.OptionParser() (options, args) = parser.parse_args() if not len(args): parser.print_usage() filename = args[0] ed = ExifData(filename) ed.dump()