def getMeta(path, args): m = Metadata(path) if args.print: print( 'getMeta: {} exif_tags, {} iptc_tags, {} xmp_tags, path {}, dateFromDirname {}' .format( len(m.get_exif_tags()), len(m.get_iptc_tags()), len(m.get_xmp_tags()), path, dateFromDirname.strftime(DATE_FORMAT) if dateFromDirname else None)) for t in m.get_tags(): # if t != 'Exif.Photo.MakerNote': # avoid big binary? item if any(x in t for x in [ 'Date', 'Image.Make', 'Model', 'Categories', 'GPS', 'Latitude', 'Longitude' ]): print('getMeta: {} -> {}'.format(t, m.get(t))) if any(x in t for x in [ 'Tags', 'LastKeywordXMP', 'HierarchicalSubject', 'CatalogSets', 'Subject', 'Keywords' ]): print('getMeta: {} => [ {} ]'.format( t, ', '.join(m.get_tag_multiple(t)))) return m
def get_property_pages(self, files): # test file type if len(files) != 1: return file = files[0] if file.get_uri_scheme() != 'file': return # if mimetype corresponds to an image, read exif tags #if file.get_mime_type() in ('image/jpeg' 'image/png'): mimetype = file.get_mime_type().split('/') if mimetype[0] == "image": # create label and grid self.property_label = Gtk.Label('EXIF') self.property_label.show() self.grid = Gtk.Grid() self.grid.set_margin_start(10) self.grid.set_margin_end(10) self.grid.set_margin_top(5) self.grid.set_margin_bottom(5) self.grid.show() # display title self.dislayTag("<b>Tag</b>", "<b>Readable value</b>", "<b>Raw value</b>", 0, 0) gtk_separator = Gtk.Separator( orientation=Gtk.Orientation.HORIZONTAL) self.grid.attach(gtk_separator, 0, 1, 3, 1) # read metadata from file filename = unquote(file.get_uri()[7:]) self.tags = Metadata() self.tags.open_path(filename) # loop thru the tags index = 0 for name in self.tags: interpreted = self.tags.get_tag_interpreted_string(name)[0:64] string_raw = self.tags.get_tag_string(name)[0:64] if interpreted == string_raw: string_raw = "" else: string_raw = "<i>" + string_raw + "</i>" name = "<b>" + name + "</b>" self.dislayTag(name, interpreted, string_raw, 0, index + 2) index = index + 1 # declare main scrolled window self.window = Gtk.ScrolledWindow() self.window.add_with_viewport(self.grid) self.window.show_all() # return label and tab content return Nautilus.PropertyPage(name="NautilusPython::tags_info", label=self.property_label, page=self.window),
def metadata_comp_table(files): # Extract tags for each file tags = {} for f in files: exif = Metadata() exif.open_path(f) tags[f] = {(x, exif[x]) for x in exif.get_tags()} # Compute common tags intersecting all sets commontags = tags[files[0]] for f in tags: commontags = commontags.intersection(tags[f]) # Delete common tags for each entry for f in tags: tags[f] = tags[f] - commontags # Print results head = ["Tag"] + [os.path.basename(x) for x in files] tab = [] alluniquetags = set() for f in tags: alluniquetags |= ({x[0] for x in tags[f]}) for t in alluniquetags: aux = [t] for f in files: if t in [x[0] for x in tags[f]]: aux.append(dict(tags[f])[t][:200]) else: aux.append("-") tab.append(aux) t = tt.Texttable() t.header(head) t.add_rows(tab, header=False) t.set_deco(t.HEADER) t.set_chars(['-', '|', '+', '-']) maxw = len(max(alluniquetags, key=len)) if alluniquetags else 5 arrw = [maxw] + [25] * len(files) t.set_cols_width(arrw) print() print(t.draw()) print("\n(Unique fields only. Common EXIF tags have been omitted)") print()
def update_file_info(self, file): # test file type if file.get_uri_scheme() != 'file': return # read data only if image file mimetype = file.get_mime_type().split('/') if mimetype[0] == "image": # format filename filename = unquote(file.get_uri()[7:]) # get metadata self.tags = Metadata() self.tags.open_path(filename) # image width tag_value = "" if self.tags.has_tag('Exif.Photo.PixelXDimension'): tag_value = self.tags.get_tag_string( 'Exif.Photo.PixelXDimension') if tag_value == "" and self.tags.has_tag('Exif.Image.ImageWidth'): tag_value = self.tags.get_tag_string('Exif.Image.ImageWidth') file.add_string_attribute('exif_width', tag_value) # image height tag_value = "" if self.tags.has_tag('Exif.Photo.PixelYDimension'): tag_value = self.tags.get_tag_string( 'Exif.Photo.PixelYDimension') if tag_value == "" and self.tags.has_tag('Exif.Image.ImageLength'): tag_value = self.tags.get_tag_string('Exif.Image.ImageLength') file.add_string_attribute('exif_height', tag_value) # camera manufacturer tag_value = "" if self.tags.has_tag('Xmp.VendorInfo.Manufacturer'): tag_value = self.tags.get_tag_interpreted_string( 'Xmp.VendorInfo.Manufacturer') if tag_value == "" and self.tags.has_tag('Exif.Image.Make'): tag_value = self.tags.get_tag_interpreted_string( 'Exif.Image.Make') file.add_string_attribute('exif_maker', tag_value) # camera model tag_value = "" if self.tags.has_tag('Xmp.VendorInfo.Model'): tag_value = self.tags.get_tag_interpreted_string( 'Xmp.VendorInfo.Model') if tag_value == "" and self.tags.has_tag('Exif.Image.Model'): tag_value = self.tags.get_tag_interpreted_string( 'Exif.Image.Model') file.add_string_attribute('exif_model', tag_value) # camera focal length tag_value = "" if self.tags.has_tag('Xmp.Exif.FocalLengthIn35mmFormat'): tag_value = self.tags.get_tag_interpreted_string( 'Xmp.Exif.FocalLengthIn35mmFormat') if tag_value == "" and self.tags.has_tag( 'Exif.Photo.FocalLengthIn35mmFilm'): tag_value = self.tags.get_tag_interpreted_string( 'Exif.Photo.FocalLengthIn35mmFilm') if tag_value == "" and self.tags.has_tag('Exif.Photo.FocalLength'): tag_value = self.tags.get_tag_interpreted_string( 'Exif.Photo.FocalLength') file.add_string_attribute('exif_focal', tag_value) # camera aperture tag_value = "" if self.tags.has_tag('Xmp.Exif.ApertureValue'): tag_value = self.tags.get_tag_interpreted_string( 'Xmp.Exif.ApertureValue') if tag_value == "" and self.tags.has_tag( 'Exif.Photo.ApertureValue'): tag_value = self.tags.get_tag_interpreted_string( 'Exif.Photo.ApertureValue') if tag_value == "" and self.tags.has_tag('Exif.Photo.FNumber'): tag_value = self.tags.get_tag_interpreted_string( 'Exif.Photo.FNumber') file.add_string_attribute('exif_apert', tag_value) # city tag tag_value = "" if self.tags.has_tag('Xmp.City'): tag_value = self.tags.get_tag_interpreted_string('Xmp.City') if tag_value == "" and self.tags.has_tag('Xmp.photoshop.City'): tag_value = self.tags.get_tag_interpreted_string( 'Xmp.photoshop.City') if tag_value == "" and self.tags.has_tag('Iptc.City'): tag_value = self.tags.get_tag_interpreted_string('Iptc.City') file.add_string_attribute('exif_city', tag_value) # country tag tag_value = "" if self.tags.has_tag('Xmp.CountryName'): tag_value = self.tags.get_tag_interpreted_string( 'Xmp.CountryName') if tag_value == "" and self.tags.has_tag('Xmp.photoshop.Country'): tag_value = self.tags.get_tag_interpreted_string( 'Xmp.photoshop.Country') file.add_string_attribute('exif_country', tag_value) # GPS tag tag_value = "no" if self.tags.has_tag('Xmp.Exif.GPSLatitude'): tag_value = "yes" if tag_value == "no" and self.tags.has_tag( 'Xmp.LocationDetails.GPSLatitude'): tag_value = "yes" if tag_value == "no" and self.tags.has_tag( 'Exif.GPSInfo.GPSLatitude'): tag_value = "yes" file.add_string_attribute('exif_gps', tag_value) # else, file is not an image else: file.add_string_attribute('exif_maker', "") file.add_string_attribute('exif_model', "") file.add_string_attribute('exif_focal', "") file.add_string_attribute('exif_city', "") file.add_string_attribute('exif_country', "") file.add_string_attribute('exif_gps', "")
def get_property_pages(self, files): # default map size and zoom factor sizeMap = "512x512" zoomMap = "7" # test file type file = files[0] if len(files) != 1: return if file.get_uri_scheme() != 'file': return # if mimetype corresponds to JPG image, read data and populate tab mimetype = file.get_mime_type().split('/') if mimetype[0] in ('image'): # create tab self.tab = Gtk.Label('GPS') self.tab.show() # create grid self.grid = Gtk.Grid() self.grid.set_margin_start(10) self.grid.set_margin_end(10) self.grid.set_margin_top(5) self.grid.set_margin_bottom(5) self.grid.show() # create main scrolled window self.window = Gtk.ScrolledWindow() self.window.add_with_viewport(self.grid) self.window.show_all() # read metadata from file filename = unquote(file.get_uri()[7:]) self.tags = Metadata() self.tags.open_path(filename) # get signed GPS position latitude = self.tags.get_gps_latitude() longitude = self.tags.get_gps_longitude() altitude = self.tags.get_gps_altitude() # if no GPS data, return if latitude == 0 and longitude == 0 and altitude == 0: return # trunk GPS data to 6 digits parts = str(latitude).split(".") strLatitude = parts[0] + "." + parts[1][:6] parts = str(longitude).split(".") strLongitude = parts[0] + "." + parts[1][:6] strAltitude = str(altitude) # generate GPS position strPosition = strLatitude + ',' + strLongitude # generate Google Maps links urlMaps = 'https://www.google.com/maps/place/' + strPosition + '/@' + strPosition + ',' + zoomMap + 'z/' urlJpeg = 'https://maps.googleapis.com/maps/api/staticmap?maptype=hybrid&zoom=' + zoomMap + '&size=' + sizeMap urlJpeg += '¢er=' + strPosition + '&markers=' + strPosition + '&key=' + apikey # generate cache folder, and create if needed dirHomeCache = os.environ['HOME'] + '/.cache' dirGeotagCache = os.getenv('XDG_CACHE_HOME', dirHomeCache) + '/geotag' if not os.path.exists(dirGeotagCache): os.makedirs(dirGeotagCache) # generate cache file names fileMap = dirGeotagCache + '/map_' + strLatitude + '_' + strLongitude + '_' + sizeMap + '_' + zoomMap + '.png' fileDesc = dirGeotagCache + '/map_' + strLatitude + '_' + strLongitude + '.txt' # if description is not in the cache, retrieve it from Nominatim if not os.path.exists(fileDesc): # retrieve place description geolocator = Nominatim(user_agent="nautilus-exif-geotag") location = geolocator.reverse(strPosition) strDescription = location.address # write description to cache file = codecs.open(fileDesc, "w", "utf-8") file.write(strDescription) file.close() # if map is not in the cache, retrieve it from Google Maps if not os.path.exists(fileMap): urllib.request.urlretrieve(urlJpeg, fileMap) # retrieve description from cache file = codecs.open(fileDesc, "r", "utf-8") strDescription = file.read() file.close() # dislay GPS data self.DisplayText("<b>latitude</b>", 0, 0, 1, 1, "right") self.DisplayText(strLatitude, 1, 0, 1, 1, "left") self.DisplayText("<b>longitude</b>", 0, 1, 1, 1, "right") self.DisplayText(strLongitude, 1, 1, 1, 1, "left") self.DisplayText("<b>altitude</b>", 0, 2, 1, 1, "right") self.DisplayText(strAltitude, 1, 2, 1, 1, "left") # dislay address value = re.compile(',').sub('\n', strDescription) self.DisplayText("<b>address</b>", 2, 0, 1, 3, "right") self.DisplayText("<a href='" + urlMaps + "'>" + value + "</a>", 3, 0, 1, 5, "left") # dislay gmaps image self.DisplayImage(fileMap, 0, 5, 4, 1) # return label and tab content return Nautilus.PropertyPage( name="NautilusPython::geotag_info", label=self.tab, page=self.window ),
def metadata_summary(path): exif = Metadata() exif.open_path(path) taglist = exif.get_tags() # Date date = [] if 'Exif.Photo.DateTimeOriginal' in taglist: date.append(exif['Exif.Photo.DateTimeOriginal']) if 'Xmp.exif.DateTimeOriginal' in taglist: date.append(exif['Xmp.exif.DateTimeOriginal']) #date.append(time.ctime(os.path.getmtime(path))) if len(date) > 0: date = time.strftime("%d/%m/%Y %H:%M:%S", time.strptime(date[0], "%Y:%m:%d %H:%M:%S")) else: date = "" # Orientation ori = exif.get('Exif.Image.Orientation', "?") # Tags tags = [] if 'Iptc.Application2.Keywords' in taglist: tags.append(exif['Iptc.Application2.Keywords']) if 'Xmp.dc.subject' in taglist: tags += exif['Xmp.dc.subject'].split(",") if 'Xmp.digiKam.TagsList' in taglist: tags += exif['Xmp.digiKam.TagsList'].split(",") if 'Xmp.MicrosoftPhoto.LastKeywordXMP' in taglist: tags += exif['Xmp.MicrosoftPhoto.LastKeywordXMP'].split(",") tags = [x.strip() for x in tags] tags = list(set(tags)) tags.sort() # Title aux = [] title = "" if 'Iptc.Application2.Caption' in taglist: aux.append(exif['Iptc.Application2.Caption']) if 'Xmp.dc.title' in taglist: aux.append(exif['Xmp.dc.title']) if 'Iptc.Application2.Headline' in taglist: aux.append(exif['Iptc.Application2.Headline']) if len(aux) > 0: title = aux[0] # Software aux = [] soft = "" if 'Exif.Image.Software' in taglist: aux.append(exif['Exif.Image.Software']) if 'Iptc.Application2.Program' in taglist: aux.append(exif['Iptc.Application2.Program']) if len(aux) > 0: soft = list(set(aux))[0] # Shorten title and soft if len(title) > 13: title = title[:10] + "..." if len(soft) > 15: soft = soft[:12] + "..." dinfo = { "date": date, "orientation": ori, "tags": tags, "title": title, "software": soft } return dinfo