|
| 1 | +# -*-*- encoding: utf-8 -*-*- |
| 2 | +# |
| 3 | +# This is gdata.photos.exif, implementing the exif namespace in gdata |
| 4 | +# |
| 5 | +# $Id: __init__.py 81 2007-10-03 14:41:42Z havard.gulldahl $ |
| 6 | +# |
| 7 | +# Copyright 2007 Håvard Gulldahl |
| 8 | +# Portions copyright 2007 Google Inc. |
| 9 | +# |
| 10 | +# Licensed under the Apache License, Version 2.0 (the "License"); |
| 11 | +# you may not use this file except in compliance with the License. |
| 12 | +# You may obtain a copy of the License at |
| 13 | +# |
| 14 | +# http://www.apache.org/licenses/LICENSE-2.0 |
| 15 | +# |
| 16 | +# Unless required by applicable law or agreed to in writing, software |
| 17 | +# distributed under the License is distributed on an "AS IS" BASIS, |
| 18 | +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 19 | +# See the License for the specific language governing permissions and |
| 20 | +# limitations under the License. |
| 21 | + |
| 22 | +"""This module maps elements from the {EXIF} namespace[1] to GData objects. |
| 23 | +These elements describe image data, using exif attributes[2]. |
| 24 | +
|
| 25 | +Picasa Web Albums uses the exif namespace to represent Exif data encoded |
| 26 | +in a photo [3]. |
| 27 | +
|
| 28 | +Picasa Web Albums uses the following exif elements: |
| 29 | +exif:distance |
| 30 | +exif:exposure |
| 31 | +exif:flash |
| 32 | +exif:focallength |
| 33 | +exif:fstop |
| 34 | +exif:imageUniqueID |
| 35 | +exif:iso |
| 36 | +exif:make |
| 37 | +exif:model |
| 38 | +exif:tags |
| 39 | +exif:time |
| 40 | +
|
| 41 | +[1]: http://schemas.google.com/photos/exif/2007. |
| 42 | +[2]: http://en.wikipedia.org/wiki/Exif |
| 43 | +[3]: http://code.google.com/apis/picasaweb/reference.html#exif_reference |
| 44 | +""" |
| 45 | + |
| 46 | + |
| 47 | +__author__ = u'havard@gulldahl.no'# (Håvard Gulldahl)' #BUG: pydoc chokes on non-ascii chars in __author__ |
| 48 | +__license__ = 'Apache License v2' |
| 49 | + |
| 50 | +try: |
| 51 | + from xml.etree import cElementTree as ElementTree |
| 52 | +except ImportError: |
| 53 | + try: |
| 54 | + import cElementTree as ElementTree |
| 55 | + except ImportError: |
| 56 | + from elementtree import ElementTree |
| 57 | +import atom |
| 58 | +import gdata |
| 59 | + |
| 60 | +EXIF_NAMESPACE = 'http://schemas.google.com/photos/exif/2007' |
| 61 | + |
| 62 | +class ExifBaseElement(atom.AtomBase): |
| 63 | + """Base class for elements in the EXIF_NAMESPACE (%s). To add new elements, you only need to add the element tag name to self._tag |
| 64 | + """ % EXIF_NAMESPACE |
| 65 | + |
| 66 | + _tag = '' |
| 67 | + _namespace = EXIF_NAMESPACE |
| 68 | + _children = atom.AtomBase._children.copy() |
| 69 | + _attributes = atom.AtomBase._attributes.copy() |
| 70 | + |
| 71 | + def __init__(self, name=None, extension_elements=None, |
| 72 | + extension_attributes=None, text=None): |
| 73 | + self.name = name |
| 74 | + self.text = text |
| 75 | + self.extension_elements = extension_elements or [] |
| 76 | + self.extension_attributes = extension_attributes or {} |
| 77 | + |
| 78 | +class Distance(ExifBaseElement): |
| 79 | + "(float) The distance to the subject, e.g. 0.0" |
| 80 | + |
| 81 | + _tag = 'distance' |
| 82 | +def DistanceFromString(xml_string): |
| 83 | + return atom.CreateClassFromXMLString(Distance, xml_string) |
| 84 | + |
| 85 | +class Exposure(ExifBaseElement): |
| 86 | + "(float) The exposure time used, e.g. 0.025 or 8.0E4" |
| 87 | + |
| 88 | + _tag = 'exposure' |
| 89 | +def ExposureFromString(xml_string): |
| 90 | + return atom.CreateClassFromXMLString(Exposure, xml_string) |
| 91 | + |
| 92 | +class Flash(ExifBaseElement): |
| 93 | + """(string) Boolean value indicating whether the flash was used. |
| 94 | + The .text attribute will either be `true' or `false' |
| 95 | +
|
| 96 | + As a convenience, this object's .bool method will return what you want, |
| 97 | + so you can say: |
| 98 | +
|
| 99 | + flash_used = bool(Flash) |
| 100 | +
|
| 101 | + """ |
| 102 | + |
| 103 | + _tag = 'flash' |
| 104 | + def __bool__(self): |
| 105 | + if self.text.lower() in ('true','false'): |
| 106 | + return self.text.lower() == 'true' |
| 107 | +def FlashFromString(xml_string): |
| 108 | + return atom.CreateClassFromXMLString(Flash, xml_string) |
| 109 | + |
| 110 | +class Focallength(ExifBaseElement): |
| 111 | + "(float) The focal length used, e.g. 23.7" |
| 112 | + |
| 113 | + _tag = 'focallength' |
| 114 | +def FocallengthFromString(xml_string): |
| 115 | + return atom.CreateClassFromXMLString(Focallength, xml_string) |
| 116 | + |
| 117 | +class Fstop(ExifBaseElement): |
| 118 | + "(float) The fstop value used, e.g. 5.0" |
| 119 | + |
| 120 | + _tag = 'fstop' |
| 121 | +def FstopFromString(xml_string): |
| 122 | + return atom.CreateClassFromXMLString(Fstop, xml_string) |
| 123 | + |
| 124 | +class ImageUniqueID(ExifBaseElement): |
| 125 | + "(string) The unique image ID for the photo. Generated by Google Photo servers" |
| 126 | + |
| 127 | + _tag = 'imageUniqueID' |
| 128 | +def ImageUniqueIDFromString(xml_string): |
| 129 | + return atom.CreateClassFromXMLString(ImageUniqueID, xml_string) |
| 130 | + |
| 131 | +class Iso(ExifBaseElement): |
| 132 | + "(int) The iso equivalent value used, e.g. 200" |
| 133 | + |
| 134 | + _tag = 'iso' |
| 135 | +def IsoFromString(xml_string): |
| 136 | + return atom.CreateClassFromXMLString(Iso, xml_string) |
| 137 | + |
| 138 | +class Make(ExifBaseElement): |
| 139 | + "(string) The make of the camera used, e.g. Fictitious Camera Company" |
| 140 | + |
| 141 | + _tag = 'make' |
| 142 | +def MakeFromString(xml_string): |
| 143 | + return atom.CreateClassFromXMLString(Make, xml_string) |
| 144 | + |
| 145 | +class Model(ExifBaseElement): |
| 146 | + "(string) The model of the camera used,e.g AMAZING-100D" |
| 147 | + |
| 148 | + _tag = 'model' |
| 149 | +def ModelFromString(xml_string): |
| 150 | + return atom.CreateClassFromXMLString(Model, xml_string) |
| 151 | + |
| 152 | +class Time(ExifBaseElement): |
| 153 | + """(int) The date/time the photo was taken, e.g. 1180294337000. |
| 154 | + Represented as the number of milliseconds since January 1st, 1970. |
| 155 | + |
| 156 | + The value of this element will always be identical to the value |
| 157 | + of the <gphoto:timestamp>. |
| 158 | +
|
| 159 | + Look at this object's .isoformat() for a human friendly datetime string: |
| 160 | +
|
| 161 | + photo_epoch = Time.text # 1180294337000 |
| 162 | + photo_isostring = Time.isoformat() # '2007-05-27T19:32:17.000Z' |
| 163 | +
|
| 164 | + Alternatively: |
| 165 | + photo_datetime = Time.datetime() # (requires python >= 2.3) |
| 166 | + """ |
| 167 | + |
| 168 | + _tag = 'time' |
| 169 | + def isoformat(self): |
| 170 | + """(string) Return the timestamp as a ISO 8601 formatted string, |
| 171 | + e.g. '2007-05-27T19:32:17.000Z' |
| 172 | + """ |
| 173 | + import time |
| 174 | + epoch = float(self.text)/1000 |
| 175 | + return time.strftime('%Y-%m-%dT%H:%M:%S.000Z', time.gmtime(epoch)) |
| 176 | + |
| 177 | + def datetime(self): |
| 178 | + """(datetime.datetime) Return the timestamp as a datetime.datetime object |
| 179 | +
|
| 180 | + Requires python 2.3 |
| 181 | + """ |
| 182 | + import datetime |
| 183 | + epoch = float(self.text)/1000 |
| 184 | + return datetime.datetime.fromtimestamp(epoch) |
| 185 | + |
| 186 | +def TimeFromString(xml_string): |
| 187 | + return atom.CreateClassFromXMLString(Time, xml_string) |
| 188 | + |
| 189 | +class Tags(ExifBaseElement): |
| 190 | + """The container for all exif elements. |
| 191 | + The <exif:tags> element can appear as a child of a photo entry. |
| 192 | + """ |
| 193 | + |
| 194 | + _tag = 'tags' |
| 195 | + _children = atom.AtomBase._children.copy() |
| 196 | + _children['{%s}fstop' % EXIF_NAMESPACE] = ('fstop', Fstop) |
| 197 | + _children['{%s}make' % EXIF_NAMESPACE] = ('make', Make) |
| 198 | + _children['{%s}model' % EXIF_NAMESPACE] = ('model', Model) |
| 199 | + _children['{%s}distance' % EXIF_NAMESPACE] = ('distance', Distance) |
| 200 | + _children['{%s}exposure' % EXIF_NAMESPACE] = ('exposure', Exposure) |
| 201 | + _children['{%s}flash' % EXIF_NAMESPACE] = ('flash', Flash) |
| 202 | + _children['{%s}focallength' % EXIF_NAMESPACE] = ('focallength', Focallength) |
| 203 | + _children['{%s}iso' % EXIF_NAMESPACE] = ('iso', Iso) |
| 204 | + _children['{%s}time' % EXIF_NAMESPACE] = ('time', Time) |
| 205 | + _children['{%s}imageUniqueID' % EXIF_NAMESPACE] = ('imageUniqueID', ImageUniqueID) |
| 206 | + |
| 207 | + def __init__(self, extension_elements=None, extension_attributes=None, text=None): |
| 208 | + ExifBaseElement.__init__(self, extension_elements=extension_elements, |
| 209 | + extension_attributes=extension_attributes, |
| 210 | + text=text) |
| 211 | + self.fstop=None |
| 212 | + self.make=None |
| 213 | + self.model=None |
| 214 | + self.distance=None |
| 215 | + self.exposure=None |
| 216 | + self.flash=None |
| 217 | + self.focallength=None |
| 218 | + self.iso=None |
| 219 | + self.time=None |
| 220 | + self.imageUniqueID=None |
| 221 | +def TagsFromString(xml_string): |
| 222 | + return atom.CreateClassFromXMLString(Tags, xml_string) |
| 223 | + |
0 commit comments