Exemple #1
0
 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)
Exemple #2
0
    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)
Exemple #3
0
    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)
Exemple #4
0
    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)
Exemple #5
0
    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)
Exemple #6
0
    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))
Exemple #7
0
    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))
Exemple #8
0
 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
Exemple #9
0
 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
Exemple #10
0
    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
Exemple #11
0
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))
Exemple #12
0
    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)
Exemple #13
0
    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)
Exemple #14
0
    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
Exemple #15
0
    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)