Пример #1
0
    def _add_photo(self, id, dry_run=False):
        """Add the given photo to the working copy."""
        # Gather necessary info.
        info = self.api.photos_getInfo(photo_id=id)[0]  # <photo> elem
        datedir = info.find("dates").get("taken")[:7]
        dir = join(self.base_dir, datedir)
        url, filename = self._download_info_from_info(info, size=self.size)
        path = join(dir, filename)
        title = info.findtext("title")
        log.info("A  %s  [%s]", path,
                 utils.one_line_summary_from_text(title, 40))
        last_update = _photo_last_update_from_info(info)

        if not dry_run:
            # Create the dirs, as necessary.
            pics_dir = join(dir, ".pics")
            if not exists(dir):
                self.fs.mkdir(dir)
            if not exists(pics_dir):
                self.fs.mkdir(pics_dir, hidden=True)

            # Get the photo itself.
            #TODO: add a reporthook for progressbar (unless too quick to bother)
            #TODO: handle ContentTooShortError (py2.5)
            fname, headers = urllib.urlretrieve(url, path)
            mtime = utils.timestamp_from_datetime(last_update)
            os.utime(path, (mtime, mtime))

            # Gather and save all metadata.
            if _photo_num_comments_from_info(info):
                comments = self.api.photos_comments_getList(photo_id=id)[0]
            else:
                comments = None
            self._save_photo_data(dir, id, info, comments)
        return datedir, last_update
Пример #2
0
    def _do_flickr(self, subcmd, opts, method, *args):
        """${cmd_name}: Call the given Flickr API method (for debugging).

        ${cmd_usage}
        ${cmd_option_list}
        Examples:
            pics flickr reflection.getMethods
            pics flickr reflection.getMethodInfo method_name=flickr.photos.getInfo
            pics flickr photos.getInfo photo_id=140542114
            pics flickr -d photos.recentlyUpdated min_date=2007-02-11 extras=last_update
        """
        api_key = utils.get_flickr_api_key()
        secret = utils.get_flickr_secret()
        flickr = simpleflickrapi.SimpleFlickrAPI(api_key, secret)
        auth_token = flickr.get_auth_token(perms="read")
        kwargs = dict(a.split('=', 1) for a in args)
        if opts.format_dates:
            if method == "photos.recentlyUpdated" and "min_date" in kwargs:
                d = kwargs["min_date"]
                if re.match("\d+-\d+-\d+", d):
                    dt = datetime.datetime.strptime(d, "%Y-%m-%d")
                    t = str(int(utils.timestamp_from_datetime(dt)))
                    log.debug("min_date: %r -> %r", kwargs["min_date"], t)
                    kwargs["min_date"] = t
        if opts.paging:
            per_page = int(kwargs.get("per_page", 100))
            for i, item in enumerate(flickr.paging_call("flickr."+method, **kwargs)):
                if i and i % per_page == 0:
                    raw_input("Press <Enter> for next page of results...")
                utils.xpprint(item)
        else:
            xml = flickr.call("flickr."+method, **kwargs)
            utils.xpprint(xml)
Пример #3
0
    def update(self, dry_run=False):
        #TODO: when support local edits, need to check for conflicts
        #      and refuse to update if hit one

        # Determine start date from which we need to update.
        last_update = self.get_last_update()
        if last_update:
            min_date = last_update
        elif self.base_date:
            min_date = self.base_date
        else:
            #TODO: Determine first appropriate date for this user via
            #      (a) user's NSID from get_auth_token response -- need
            #          to save that and provide it via the SimpleFlickrAPI.
            #      (b) Using people.getInfo user_id=NSID.
            min_date = datetime.date(1980, 1, 1)  # before Flickr's time
        d = min_date
        min_date = int(utils.timestamp_from_datetime(min_date))
        min_date += 1 # To avoid always re-updating the latest changed photo.
        log.debug("update: min_date=%s (%s)", min_date, d)

        with self.db.connect(not dry_run) as cu:
            permission = self.db.get_meta("permission", cu=cu)

            # Gather all updates to do.
            # After commiting this it is okay if this script is aborted
            # during the actual update: a subsequent 'pics up' will
            # continue where we left off.
            recents = self.api.paging_call(
                "flickr.photos.recentlyUpdated",
                min_date=min_date,
                extras="last_update")
            for elem in recents:
                id = elem.get("id")
                isfamily = bool(int(elem.get("isfamily")))
                isfriend = bool(int(elem.get("isfriend")))
                ispublic = bool(int(elem.get("ispublic")))
                if permission == "all":
                    pass
                elif ispublic:
                    pass
                elif permission == "family" and isfamily:
                    pass
                elif permission == "friend" and isfriend:
                    pass
                else:
                    continue
                cu.execute("INSERT OR REPLACE INTO pics_update VALUES (?)", (id,))
            if not dry_run:
                cu.connection.commit()

            # Do each update.
            cu.execute("SELECT id FROM pics_update")
            ids = [row[0] for row in cu]
            for id in ids:
                # Determine if this is an add, update, conflict, merge or delete.
                #TODO: test a delete (does recent updates show that?)
                cu.execute("SELECT * FROM pics_photo WHERE id=?", (id,))
                row = cu.fetchone()
                if row is None:
                    action = "A" # adding a new photo
                else:
                    local_datedir = row[1]
                    local_info = self._get_photo_data(local_datedir, id, "info")
                    if local_info is None:
                        #TODO: might have been a locally deleted file
                        action = "A"  # restore?
                    else:
                        #TODO: support local changes would be handled here:
                        #  Maintain MD5 of photo and info files and
                        #  detect changes that way.
                        action = "U"

                # Handle the action.
                if action == "A":
                    datedir, last_update = self._add_photo(id, dry_run=dry_run)
                    cu.execute("INSERT INTO pics_photo VALUES (?,?)", (id, datedir))
                elif action == "U":
                    datedir, last_update = self._update_photo(
                        id, local_datedir, local_info, dry_run=dry_run)
                    if datedir != local_datedir:
                        XXX # test this case
                        cu.execute("UPDATE pics_photo SET datedir=? WHERE id=?",
                                   (datedir, id))
                else:
                    raise PicsError("unexpected update action: %r" % action)

                # Note this update.
                self.set_last_update(last_update, cu)
                cu.execute("DELETE FROM pics_update WHERE id=?", (id,))
                if not dry_run:
                    cu.connection.commit()

        log.info("Up to date (latest update: %s UTC).",
                 self.get_last_update().strftime("%Y %b %d, %H:%M:%S"))
Пример #4
0
    def _update_photo(self, id, local_datedir, local_info, dry_run=False):
        """Update the given photo in the working copy."""
        info = self._fetch_info_from_photo_id(id)
        datedir = info.find("dates").get("taken")[:7]
        last_update = _photo_last_update_from_info(info)

        # Figure out what work needs to be done.
        # From *experimentation* it looks like the "secret" attribute
        # changes if the photo itself changes (i.e. is replaced or
        # "Edited" or rotated).
        todos = []
        if datedir != local_datedir:
            log.debug("update %s: datedir change: %r -> %r",
                      id, local_datedir, datedir)
            todos.append("remove-old")
            todos.append("photo")
        elif info.get("secret") != local_info.get("secret"):
            log.debug("update %s: photo secret change: %r -> %r",
                      id, local_info.get("secret"), info.get("secret"))
            todos.append("photo")
        todos.append("info")
        if _photo_num_comments_from_info(info):
            todos.append("comments")
        if not todos:
            return datedir, last_update

        # Do the necessary updates.
        XXX # Figure out ext for videos. _download_info_from_info is wrong
            # here b/c is isn't using  *local* info.
        url, filename = self._download_info_from_info(info, size=self.size)
        #XXX
        #size_ext = (self.size != "original" and "."+self.size or "")
        #ext = (self.size != "original" and ".jpg"
        #       or "."+local_info.get("originalformat"))

        # - Remove the old bits, if the datedir has changed.
        if "remove-old" in todos:
            d = join(self.base_dir, local_datedir)
            path = join(d, filename)
            log.info("D  %s  [%s]", path,
                utils.one_line_summary_from_text(local_info.findtext("title"), 40))
            if not dry_run:
                log.debug("rm `%s'", path)
                os.remove(path)
                self._remove_photo_data(d, id)
                remaining_paths = set(os.listdir(d))
                remaining_paths.difference_update(set([".pics"]))
                if not remaining_paths:
                    log.info("D  %s", d)
                    self.fs.rm(d)

        # - Add the new stuff.
        d = join(self.base_dir, datedir)
        action_str = ("photo" in todos and "U " or " u")
        path = join(d, filename)
        log.info("%s %s  [%s]", action_str, path,
            utils.one_line_summary_from_text(info.findtext("title"), 40))
        if not dry_run:
            if not exists(d):
                self.fs.mkdir(d)
                pics_dir = join(d, ".pics")
                if not exists(pics_dir):
                    self.fs.mkdir(pics_dir, hidden=True)
            if "photo" in todos:
                fname, headers = urllib.urlretrieve(url, path)
                mtime = utils.timestamp_from_datetime(last_update)
                os.utime(path, (mtime, mtime))
            if "comments" in todos:
                comments = self.api.photos_comments_getList(photo_id=id)[0]
            else:
                comments = None
            self._save_photo_data(d, id, info, comments=comments)

        #print "... %s" % id
        #print "originalsecret: %s <- %s" % (info.get("originalsecret"), local_info.get("originalsecret"))
        #print "secret: %s <- %s" % (info.get("secret"), local_info.get("secret"))
        #print "rotation: %s <- %s" % (info.get("rotation"), local_info.get("rotation"))
        return datedir, last_update