def init_from_options(self, options): """Populates the UI from options.""" self.iphoto_library.set(options.iphoto) self.export_folder.set(options.export) self.albums.set(su.fsdec(options.albums)) self.events.set(su.fsdec(options.events)) self.smarts.set(su.fsdec(options.smarts)) self.foldertemplate.set(su.unicode_string(options.foldertemplate)) self.nametemplate.set(su.unicode_string(options.nametemplate)) self.captiontemplate.set(su.unicode_string(options.captiontemplate)) self.update_var.set(_int_from_bool(options.update)) self.delete_var.set(_int_from_bool(options.delete)) self.originals_var.set(_int_from_bool(options.originals)) self.link_var.set(_int_from_bool(options.link)) self.folder_hints_var.set(_int_from_bool(options.folderhints)) self.faces_var.set(_int_from_bool(options.faces) and self.exiftool) self.face_keywords_var.set(_int_from_bool(options.face_keywords) and self.exiftool) self.face_albums_var.set(_int_from_bool(options.facealbums)) self.face_albums_text.set(options.facealbum_prefix) if options.iptc and self.exiftool: self.iptc_var.set(1) if options.iptc == 2: self.iptc_all_var.set(1) self.gps_var.set(_int_from_bool(options.gps) and self.exiftool)
def load_album(self, options): """Loads an existing album (export folder).""" online_albums = {} for album in self.client.gd_client.GetUserFeed().entry: if online_albums.has_key(album.title.text): self.delete_online_album(album, "duplicate album", options) else: online_albums[su.unicode_string(album.title.text)] = album album_directories = {} for folder_name in sorted(self.named_folders): folder = self.named_folders.get(folder_name) if self._check_abort(): return album_directories[folder.name] = True folder.load_album(self.client, online_albums, options) ignore_pattern = None if options.ignore: ignore_pattern = re.compile(su.fsdec(options.ignore)) for album_name in online_albums: if self._check_abort(): return if album_name in album_directories: continue if ignore_pattern and ignore_pattern.match(album_name): continue # We won't touch some albums if imageutils.is_ignore(album_name): continue self.delete_online_album(online_albums[album_name], "obsolete album", options)
def delete_online_album(self, album, msg, options): """Delete an online album.""" album_name = su.unicode_string(album.title.text) if msg: print "%s: %s" % (msg, su.fsenc(album_name)) if not options.delete: if not options.dryrun: print "Invoke Phoshare with the -d option to delete this album." return False if options.dryrun: return True if (self.confirm_manager.confirm( album_name, "Delete " + msg + " " + album_name + "? ", "ny") != 1): print >> sys.stderr, 'Not deleted because not confirmed.' return False try: self.client.throttle() self.client.gd_client.Delete(album) return True except gdata.photos.service.GooglePhotosException, e: print >> sys.stderr, "Could not delete %s: %s" % ( su.fsenc(album_name), e)
def delete_online_album(self, album, msg, options): """Delete an online album.""" album_name = su.unicode_string(album.title.text) if msg: print "%s: %s" % (msg, su.fsenc(album_name)) if not options.delete: if not options.dryrun: print "Invoke Phoshare with the -d option to delete this album." return False if options.dryrun: return True if (self.confirm_manager.confirm(album_name, "Delete " + msg + " " + album_name + "? ", "ny") != 1): print >> sys.stderr, 'Not deleted because not confirmed.' return False try: self.client.throttle() self.client.gd_client.Delete(album) return True except gdata.photos.service.GooglePhotosException, e: print >> sys.stderr, "Could not delete %s: %s" % ( su.fsenc(album_name), e)
def load_album(self, client, online_albums, options): """Walks the album directory tree, and scans it for existing files.""" if options.verbose: print 'Reading online album ' + self.name comments = self.iphoto_container.getcommentwithouthints().strip() album_date = self.iphoto_container.date if album_date: # Adjust to just a date album_date = datetime.datetime(album_date.year, album_date.month, album_date.day) timestamp = get_picasaweb_date(album_date) else: timestamp = None self.online_album = online_albums.get(self.name) if not self.online_album: print "Creating album: " + su.fsenc(self.name) if not options.dryrun: client.throttle() self.online_album = client.gd_client.InsertAlbum( title=self.name, summary=comments, access='private', timestamp=timestamp) online_albums[self.name] = self.online_album return # Check the properties of the online album changed = False online_album_summary_text = su.unicode_string( self.online_album.summary.text) if online_album_summary_text != comments: print 'Updating summary for online album %s (%s vs. %s)' % ( su.fsenc(self.name), su.fsenc(online_album_summary_text), su.fsenc(comments)) self.online_album.summary.text = comments changed = True if (timestamp and timestamp != self.online_album.timestamp.text): print 'Updating timestamp for online album %s (%s/%s)' % ( su.fsenc(self.name), self.online_album.timestamp.datetime(), album_date) self.online_album.timestamp.text = timestamp changed = True if changed and not options.dryrun: client.throttle() try: self.online_album = client.gd_client.Put( self.online_album, self.online_album.GetEditLink().href, converter=gdata.photos.AlbumEntryFromString) except gdata.photos.service.GooglePhotosException, e: print 'Failed to update data for online album %s: %s' % ( self.name, str(e))
def load_album(self, client, online_albums, options): """Walks the album directory tree, and scans it for existing files.""" if options.verbose: print 'Reading online album ' + self.name comments = self.iphoto_container.getcommentwithouthints().strip() album_date = self.iphoto_container.date if album_date: # Adjust to just a date album_date = datetime.datetime(album_date.year, album_date.month, album_date.day) timestamp = get_picasaweb_date(album_date) else: timestamp = None self.online_album = online_albums.get(self.name) if not self.online_album: print "Creating album: " + su.fsenc(self.name) if not options.dryrun: client.throttle() self.online_album = client.gd_client.InsertAlbum( title=self.name, summary=comments, access='private', timestamp=timestamp) online_albums[self.name] = self.online_album return # Check the properties of the online album changed = False online_album_summary_text = su.unicode_string( self.online_album.summary.text) if online_album_summary_text != comments: print 'Updating summary for online album %s (%s vs. %s)' % ( su.fsenc(self.name), su.fsenc(online_album_summary_text), su.fsenc(comments)) self.online_album.summary.text = comments changed = True if (timestamp and timestamp != self.online_album.timestamp.text): print 'Updating timestamp for online album %s (%s/%s)' % (su.fsenc( self.name), self.online_album.timestamp.datetime(), album_date) self.online_album.timestamp.text = timestamp changed = True if changed and not options.dryrun: client.throttle() try: self.online_album = client.gd_client.Put( self.online_album, self.online_album.GetEditLink().href, converter=gdata.photos.AlbumEntryFromString) except gdata.photos.service.GooglePhotosException, e: print 'Failed to update data for online album %s: %s' % ( self.name, str(e))
def endElement(self, name): #IGNORE:C0103 '''callback for the end of a parsed XML element''' if name == "key": self.key = self.chars elif name == 'string': self.add_object( unicodedata.normalize("NFC", su.unicode_string(self.chars))) elif name in ("integer", "real", "date"): self.add_object(self.chars) elif name == "true": self.add_object(True) elif name == "false": self.add_object(False) elif name == "data": self.add_object(self.chars) elif name == "dict" or name == "array": self.parse_stack.pop() elif name == "plist": self.top_node = self.parse_stack.pop() else: print "unrecognized element in XML data: " + name self.chars = None
def process_albums(self, albums, album_types, folder_prefix, includes, excludes, options, matched=False): """Walks trough an iPhoto album tree, and discovers albums (directories).""" entries = 0 include_pattern = re.compile(su.unicode_string(includes)) exclude_pattern = None if excludes: exclude_pattern = re.compile(su.unicode_string(excludes)) # first, do the sub-albums for sub_album in albums: if self._check_abort(): return sub_name = sub_album.name if not sub_name: print "Found an album with no name: " + sub_album.albumid sub_name = "xxx" # check the album type if sub_album.albumtype == "Folder" or sub_album.albums: sub_matched = matched if include_pattern.match(sub_name): sub_matched = True new_name = folder_prefix if sub_album.albumtype == "Folder": new_name += imageutils.make_foldername(sub_name) + "/" self.process_albums(sub_album.albums, album_types, new_name, includes, excludes, options, sub_matched) continue elif (sub_album.albumtype == "None" or not sub_album.albumtype in album_types): # print "Ignoring " + sub_album.name + " of type " + \ # sub_album.albumtype continue if not matched and not include_pattern.match(sub_name): _logger.debug( u'Skipping "%s" because it does not match pattern.', sub_name) continue if exclude_pattern and exclude_pattern.match(sub_name): _logger.debug(u'Skipping "%s" because it is excluded.', sub_name) continue _logger.debug(u'Loading "%s".', sub_name) folder_hint = None if options.folderhints: folder_hint = sub_album.getfolderhint() prefix = folder_prefix if folder_hint is not None: prefix = prefix + imageutils.make_foldername(folder_hint) + "/" formatted_name = imageutils.format_album_name( sub_album, options.foldertemplate) sub_name = prefix + imageutils.make_foldername(formatted_name) sub_name = self._find_unused_folder(sub_name) # first, do the sub-albums if self.process_albums(sub_album.albums, album_types, folder_prefix, includes, excludes, options, matched) > 0: entries += 1 # now the album itself picture_directory = ExportDirectory( sub_name, sub_album, os.path.join(self.albumdirectory, sub_name)) if picture_directory.add_iphoto_images(sub_album.images, options) > 0: self.named_folders[sub_name] = picture_directory entries += 1 return entries
class PicasaAlbum(object): """Tracks an album folder in the export location.""" def __init__(self, name, iphoto_container): self.name = name self.iphoto_container = iphoto_container self.files = {} # name -> PicasaFile self.online_album = None self.image_suffix = re.compile( r'\.(jpeg|jpg|mpg|mpeg|mov|png|tif|tiff)$', re.IGNORECASE) def add_iphoto_images(self, images, options): """Works through an image folder tree, and builds data for exporting.""" entries = 0 template = options.nametemplate if images is not None: entry_digits = len(str(len(images))) for image in images: if image.ismovie() and not options.movies: continue if _NOUPLOAD_KEYWORD in image.keywords: continue entries += 1 image_basename = self.make_album_basename( image, entries, str(entries).zfill(entry_digits), template) picture_file = PicasaFile(image, self.name, image_basename, options) self.files[image_basename] = picture_file return len(self.files) def make_album_basename(self, photo, index, padded_index, name_template): """creates unique file name.""" base_name = imageutils.format_photo_name(photo, self.iphoto_container.name, index, padded_index, name_template) index = 0 while True: album_basename = base_name if index > 0: album_basename += "_%d" % (index) if self.files.get(album_basename) is None: return album_basename index += 1 return base_name def load_album(self, client, online_albums, options): """Walks the album directory tree, and scans it for existing files.""" if options.verbose: print 'Reading online album ' + self.name comments = self.iphoto_container.getcommentwithouthints().strip() timestamp = get_picasaweb_date(self.iphoto_container.date) self.online_album = online_albums.get(self.name) if not self.online_album: print "Creating album: " + su.fsenc(self.name) if not options.dryrun: client.throttle() self.online_album = client.gd_client.InsertAlbum( title=self.name, summary=comments, access='private', timestamp=timestamp) online_albums[self.name] = self.online_album return # Check the properties of the online album changed = False online_album_summary_text = su.unicode_string( self.online_album.summary.text) if online_album_summary_text != comments: print 'Updating summary for online album %s (%s vs. %s)' % ( su.fsenc(self.name), su.fsenc(online_album_summary_text), su.fsenc(comments)) self.online_album.summary.text = comments changed = True if (timestamp and timestamp != self.online_album.timestamp.text): print 'Updating timestamp for online album %s (%s/%s)' % ( su.fsenc(self.name), self.online_album.timestamp.datetime(), self.iphoto_container.date) self.online_album.timestamp.text = timestamp changed = True if changed and not options.dryrun: client.throttle() try: self.online_album = client.gd_client.Put( self.online_album, self.online_album.GetEditLink().href, converter=gdata.photos.AlbumEntryFromString) except gdata.photos.service.GooglePhotosException, e: print 'Failed to update data for online album %s: %s' % ( self.name, str(e)) # Check the pictures in the online album try: photos = client.gd_client.GetFeed( '/data/feed/api/user/%s/albumid/%s?kind=photo' % ('default', self.online_album.gphoto_id.text)) for photo in photos.entry: # we won't touch some files if imageutils.is_ignore(photo.title.text): continue photo_name = su.unicode_string(photo.title.text) base_name = su.getfilebasename(photo_name) master_file = self.files.get(base_name) # everything else must have a master, or will have to go if master_file is None: delete_online_photo(client, photo, self.name, "Obsolete online photo", options) elif master_file.picasa_photo: delete_online_photo(client, photo, self.name, "Duplicate online photo", options) else: master_file.picasa_photo = photo except gdata.photos.service.GooglePhotosException, e: print 'Failed to load pictures for online album %s: %s' % ( self.name, str(e))
def generate_update(self, client, options): """Attempts to update a photo. If the media file needs updating, deletes it first, then adds it back in. Args: client - the PicasaWeb client album_id - the id of the album for this photo """ # check albumFile self.picasa_photo = check_media_update(client, self.picasa_photo, self.photo, self.export_file, options) picasa_photo = self.picasa_photo # Now check if any of the meta data needs to be updated. needs_update = False picasa_title = su.unicode_string(picasa_photo.title.text) if self.title != picasa_title: print( 'Updating meta data for %s because it has Caption "%s" ' 'instead of "%s".') % (su.fsenc( self.export_file), su.fsenc(picasa_title), su.fsenc(self.title)) picasa_photo.title.text = self.title needs_update = True # Combine title and description because PicasaWeb does not show the # title anywhere. comment = imageutils.get_photo_caption(self.photo, options.captiontemplate) online_summary = su.unicode_string(picasa_photo.summary.text) if not su.equalscontent(comment, online_summary): print("Updating meta data for " + su.fsenc(self.export_file) + ' because it has description "' + su.fsenc(online_summary) + '" instead of "' + su.fsenc(comment) + '".') picasa_photo.summary.text = comment.strip() needs_update = True if self.photo.date: photo_time = get_picasaweb_date(self.photo.date) if photo_time != picasa_photo.timestamp.text: print( 'Updating meta data for %s because it has timestamp "' '%s" instead of "%s"') % (su.fsenc( self.export_file), picasa_photo.timestamp.datetime(), self.photo.date) picasa_photo.timestamp.text = photo_time needs_update = True export_keywords = self.get_export_keywords(options) picasa_keywords = [] if (picasa_photo.media and picasa_photo.media.keywords and picasa_photo.media.keywords.text): picasa_keywords = su.unicode_string( picasa_photo.media.keywords.text).split(', ') else: picasa_keywords = [] if not imageutils.compare_keywords(export_keywords, picasa_keywords): print("Updating meta data for " + su.fsenc(self.export_file) + " because of keywords (" + su.fsenc(",".join(picasa_keywords)) + ") instead of (" + su.fsenc(",".join(export_keywords)) + ").") if not picasa_photo.media: picasa_photo.media = gdata.media.Group() if not picasa_photo.media.keywords: picasa_photo.media.keywords = gdata.media.Keywords() picasa_photo.media.keywords.text = ', '.join(export_keywords) needs_update = True if options.gps and self.photo.gps: if picasa_photo.geo and picasa_photo.geo.Point: picasa_location = imageutils.GpsLocation().from_gdata_point( picasa_photo.geo.Point) else: picasa_location = imageutils.GpsLocation() if not picasa_location.is_same(self.photo.gps): print("Updating meta data for " + su.fsenc(self.export_file) + " because of GPS " + picasa_location.to_string() + " vs " + self.photo.gps.to_string()) set_picasa_photo_pos(picasa_photo, self.photo.gps) needs_update = True if not needs_update: return if not options.update: print "Needs update: " + su.fsenc(self.export_file) + "." print "Use the -u option to update this file." return print("Updating metadata: " + self.export_file) if options.dryrun: return retry = 0 wait_time = 1.0 while True: try: client.throttle() picasa_photo = client.gd_client.UpdatePhotoMetadata( picasa_photo) return except gdata.photos.service.GooglePhotosException, e: retry += 1 if retry == 10: raise e if str(e).find("17 REJECTED_USER_LIMIT") == -1: raise e wait_time = wait_time * 2 print("Retrying after " + wait_time + "s because of " + str(e)) time.sleep(wait_time)
def process_albums(self, albums, album_types, folder_prefix, includes, excludes, options, matched=False): """Walks trough an iPhoto album tree, and discovers albums (directories).""" include_pattern = re.compile(su.unicode_string(includes)) exclude_pattern = None if excludes: exclude_pattern = re.compile(su.unicode_string(excludes)) # Figure out the folder patterns (if any) folderpatterns = [] if options.folderpatterns: for pattern in su.unicode_string(options.folderpatterns).split(','): (expression, folder) = pattern.split('/', 2) folderpatterns.append((re.compile(expression), folder)) # first, do the sub-albums for sub_album in albums: if self._check_abort(): return sub_name = sub_album.name if not sub_name: print "Found an album with no name: " + sub_album.albumid sub_name = "xxx" # check the album type if sub_album.albumtype == "Folder" or sub_album.albums: sub_matched = matched if include_pattern.match(sub_name): sub_matched = True new_name = folder_prefix if sub_album.albumtype == "Folder": new_name += imageutils.make_foldername(sub_name) + "/" self.process_albums(sub_album.albums, album_types, new_name, includes, excludes, options, sub_matched) continue elif (sub_album.albumtype == "None" or not sub_album.albumtype in album_types): # print "Ignoring " + sub_album.name + " of type " + \ # sub_album.albumtype continue if not matched and not include_pattern.match(sub_name): _logger.debug(u'Skipping "%s" because it does not match pattern.', sub_name) continue if exclude_pattern and exclude_pattern.match(sub_name): _logger.debug(u'Skipping "%s" because it is excluded.', sub_name) continue _logger.debug(u'Loading "%s".', sub_name) folder_hint = None if sub_name.find('/') != -1: (folder_hint, sub_name) = sub_name.split('/', 1) if not folder_hint and options.folderhints: folder_hint = sub_album.getfolderhint() if not folder_hint and folderpatterns: for (pattern, folder) in folderpatterns: if pattern.match(sub_album.name): if options.verbose: su.pout("Using folder %s for album %s." % (folder, sub_album.name)) folder_hint = folder break prefix = folder_prefix if folder_hint is not None: prefix = prefix + imageutils.make_foldername(folder_hint) + "/" formatted_name = imageutils.format_album_name( sub_album, sub_name, options.foldertemplate) sub_name = prefix + imageutils.make_foldername(formatted_name, options.enablesubfolders) sub_name = self._find_unused_folder(sub_name) # first, do the sub-albums self.process_albums(sub_album.albums, album_types, folder_prefix, includes, excludes, options, matched) # now the album itself picture_directory = ExportDirectory( sub_name, sub_album, os.path.join(self.albumdirectory, sub_name)) if picture_directory.add_iphoto_images(sub_album.images, options) > 0: self.named_folders[sub_name] = picture_directory return len(self.named_folders)
def process_albums(self, albums, album_types, folder_prefix, includes, excludes, options, matched=False): """Walks trough an iPhoto album tree, and discovers albums (directories).""" entries = 0 include_pattern = re.compile(su.unicode_string(includes)) exclude_pattern = None if excludes: exclude_pattern = re.compile(su.unicode_string(excludes)) # first, do the sub-albums for sub_album in albums: if self._check_abort(): return sub_name = sub_album.name if not sub_name: print "Found an album with no name: " + sub_album.albumid sub_name = "xxx" # check the album type if sub_album.albumtype == "Folder" or sub_album.albums: sub_matched = matched if include_pattern.match(sub_name): sub_matched = True new_name = folder_prefix if sub_album.albumtype == "Folder": new_name += imageutils.make_foldername(sub_name) + "/" self.process_albums(sub_album.albums, album_types, new_name, includes, excludes, options, sub_matched) continue elif (sub_album.albumtype == "None" or not sub_album.albumtype in album_types): # print "Ignoring " + sub_album.name + " of type " + \ # sub_album.albumtype continue if not matched and not include_pattern.match(sub_name): _logger.debug(u'Skipping "%s" because it does not match pattern.', sub_name) continue if exclude_pattern and exclude_pattern.match(sub_name): _logger.debug(u'Skipping "%s" because it is excluded.', sub_name) continue _logger.debug(u'Loading "%s".', sub_name) folder_hint = None if options.folderhints: folder_hint = sub_album.getfolderhint() prefix = folder_prefix if folder_hint is not None: prefix = prefix + imageutils.make_foldername(folder_hint) + "/" formatted_name = imageutils.format_album_name( sub_album, options.foldertemplate) sub_name = prefix + imageutils.make_foldername(formatted_name) sub_name = self._find_unused_folder(sub_name) # first, do the sub-albums if self.process_albums(sub_album.albums, album_types, folder_prefix, includes, excludes, options, matched) > 0: entries += 1 # now the album itself picture_directory = ExportDirectory( sub_name, sub_album, os.path.join(self.albumdirectory, sub_name)) if picture_directory.add_iphoto_images(sub_album.images, options) > 0: self.named_folders[sub_name] = picture_directory entries += 1 return entries
def generate_update(self, client, options): """Attempts to update a photo. If the media file needs updating, deletes it first, then adds it back in. Args: client - the PicasaWeb client album_id - the id of the album for this photo """ # check albumFile self.picasa_photo = check_media_update(client, self.picasa_photo, self.photo, self.export_file, options) picasa_photo = self.picasa_photo # Now check if any of the meta data needs to be updated. needs_update = False picasa_title = su.unicode_string(picasa_photo.title.text) if self.title != picasa_title: print ('Updating meta data for %s because it has Caption "%s" ' 'instead of "%s".') % ( su.fsenc(self.export_file), su.fsenc(picasa_title), su.fsenc(self.title), ) picasa_photo.title.text = self.title needs_update = True # Combine title and description because PicasaWeb does not show the # title anywhere. comment = imageutils.get_photo_caption(self.photo, options.captiontemplate) online_summary = su.unicode_string(picasa_photo.summary.text) if not su.equalscontent(comment, online_summary): print ( "Updating meta data for " + su.fsenc(self.export_file) + ' because it has description "' + su.fsenc(online_summary) + '" instead of "' + su.fsenc(comment) + '".' ) picasa_photo.summary.text = comment.strip() needs_update = True if self.photo.date: photo_time = get_picasaweb_date(self.photo.date) if photo_time != picasa_photo.timestamp.text: print ('Updating meta data for %s because it has timestamp "' '%s" instead of "%s"') % ( su.fsenc(self.export_file), picasa_photo.timestamp.datetime(), self.photo.date, ) picasa_photo.timestamp.text = photo_time needs_update = True export_keywords = self.get_export_keywords(options) picasa_keywords = [] if picasa_photo.media and picasa_photo.media.keywords and picasa_photo.media.keywords.text: picasa_keywords = su.unicode_string(picasa_photo.media.keywords.text).split(", ") else: picasa_keywords = [] if not imageutils.compare_keywords(export_keywords, picasa_keywords): print ( "Updating meta data for " + su.fsenc(self.export_file) + " because of keywords (" + su.fsenc(",".join(picasa_keywords)) + ") instead of (" + su.fsenc(",".join(export_keywords)) + ")." ) if not picasa_photo.media: picasa_photo.media = gdata.media.Group() if not picasa_photo.media.keywords: picasa_photo.media.keywords = gdata.media.Keywords() picasa_photo.media.keywords.text = ", ".join(export_keywords) needs_update = True if options.gps and self.photo.gps: if picasa_photo.geo and picasa_photo.geo.Point: picasa_location = imageutils.GpsLocation().from_gdata_point(picasa_photo.geo.Point) else: picasa_location = imageutils.GpsLocation() if not picasa_location.is_same(self.photo.gps): print ( "Updating meta data for " + su.fsenc(self.export_file) + " because of GPS " + picasa_location.to_string() + " vs " + self.photo.gps.to_string() ) set_picasa_photo_pos(picasa_photo, self.photo.gps) needs_update = True if not needs_update: return if not options.update: print "Needs update: " + su.fsenc(self.export_file) + "." print "Use the -u option to update this file." return print ("Updating metadata: " + self.export_file) if options.dryrun: return retry = 0 wait_time = 1.0 while True: try: client.throttle() picasa_photo = client.gd_client.UpdatePhotoMetadata(picasa_photo) return except gdata.photos.service.GooglePhotosException, e: retry += 1 if retry == 10: raise e if str(e).find("17 REJECTED_USER_LIMIT") == -1: raise e wait_time = wait_time * 2 print ("Retrying after " + wait_time + "s because of " + str(e)) time.sleep(wait_time)
def process_albums(self, albums, album_types, folder_prefix, includes, excludes, options, matched=False): """Walks trough an iPhoto album tree, and discovers albums (directories).""" include_pattern = re.compile(su.unicode_string(includes)) exclude_pattern = None if excludes: exclude_pattern = re.compile(su.unicode_string(excludes)) # Figure out the folder patterns (if any) folderpatterns = [] if options.folderpatterns: for pattern in su.unicode_string(options.folderpatterns).split(','): (expression, folder) = pattern.split('/', 2) folderpatterns.append((re.compile(expression), folder)) # first, do the sub-albums for sub_album in albums: if self._check_abort(): return sub_name = sub_album.name if not sub_name: print "Found an album with no name: " + sub_album.albumid sub_name = "xxx" # check the album type if sub_album.albumtype == "Folder" or sub_album.albums: sub_matched = matched if include_pattern.match(sub_name): sub_matched = True new_name = folder_prefix if sub_album.albumtype == "Folder": new_name += imageutils.make_foldername(sub_name) + "/" self.process_albums(sub_album.albums, album_types, new_name, includes, excludes, options, sub_matched) continue elif (sub_album.albumtype == "None" or not sub_album.albumtype in album_types): # print "Ignoring " + sub_album.name + " of type " + \ # sub_album.albumtype continue if not matched and not include_pattern.match(sub_name): _logger.debug(u'Skipping "%s" because it does not match pattern.', sub_name) continue if exclude_pattern and exclude_pattern.match(sub_name): _logger.debug(u'Skipping "%s" because it is excluded.', sub_name) continue _logger.debug(u'Loading "%s".', sub_name) folder_hint = None if sub_name.find('/') != -1: (folder_hint, sub_name) = sub_name.split('/', 1) if not folder_hint and options.folderhints: folder_hint = sub_album.getfolderhint() if not folder_hint and folderpatterns: for (pattern, folder) in folderpatterns: if pattern.match(sub_album.name): if options.verbose: su.pout("Using folder %s for album %s." % (folder, sub_album.name)) folder_hint = folder break prefix = folder_prefix if folder_hint is not None: prefix = prefix + imageutils.make_foldername(folder_hint) + "/" formatted_name = imageutils.format_album_name( sub_album, sub_name, options.foldertemplate) sub_name = prefix + imageutils.make_foldername(formatted_name) sub_name = self._find_unused_folder(sub_name) # first, do the sub-albums self.process_albums(sub_album.albums, album_types, folder_prefix, includes, excludes, options, matched) # now the album itself picture_directory = ExportDirectory( sub_name, sub_album, os.path.join(self.albumdirectory, sub_name)) if picture_directory.add_iphoto_images(sub_album.images, options) > 0: self.named_folders[sub_name] = picture_directory return len(self.named_folders)