// See https://developers.google.com/accounts/docs/OAuth2InstalledApp . func main() { flagID := flag.String("id", os.Getenv("CLIENT_ID"), "application client ID") flagSecret := flag.String("secret", os.Getenv("CLIENT_SECRET"), "application client secret") flagCode := flag.String("code", os.Getenv("AUTH_CODE"), "authorization code") flagTokenCache := flag.String("cache", "token-cache.json", "token cache filename") flagDir := flag.String("dir", "", "directory to download images to") flagDebugDir := flag.String("debug", "", "set to a valid path to save the response XMLs there") flag.Parse() picago.DebugDir = *flagDebugDir userid := flag.Arg(0) client, err := picago.NewClient(*flagID, *flagSecret, *flagCode, *flagTokenCache) if err != nil { log.Fatalf("error with authorization: %v", err) } user, err := picago.GetUser(client, "") log.Printf("user=%#v err=%v", user, err) albums, err := picago.GetAlbums(client, userid) if err != nil { log.Fatalf("error listing albums: %v", err) } log.Printf("user %s has %d albums.", userid, len(albums)) download := *flagDir != "" dir, fn := "", "" for _, album := range albums { albumJ, err := json.Marshal(album) if err != nil { log.Fatalf("error marshaling %#v: %v", album, err) } if download { dir = filepath.Join(*flagDir, album.Name) if err = os.MkdirAll(dir, 0750); err != nil { log.Fatalf("cannot create directory %s: %v", dir, err) } fn = filepath.Join(dir, "album-"+album.Name+".json") if err = ioutil.WriteFile(fn, albumJ, 0750); err != nil { log.Fatalf("error writing %s: %v", fn, err) } } log.Printf("downloading album %s.", albumJ) photos, err := picago.GetPhotos(client, userid, album.ID) if err != nil { log.Printf("error listing photos of %s: %v", album.ID, err) continue } log.Printf("album %s contains %d photos.", album.ID, len(photos)) for _, photo := range photos { photoJ, err := json.Marshal(photo) if err != nil { log.Fatalf("error marshaling %#v: %v", photo, err) } log.Printf("Photo: %s", photoJ) if !download { continue } fn = filepath.Join(dir, photo.Filename) if err = ioutil.WriteFile(fn+".json", photoJ, 0750); err != nil { log.Fatalf("error writing %s.json: %v", fn, err) } if err = downloadTo(fn, client, photo.URL); err != nil { log.Fatalf("downloading %s: %v", photo.URL, err) } } } }
func (r *run) importAlbum(ctx context.Context, albumsNode *importer.Object, album picago.Album) (ret error) { if album.ID == "" { return errors.New("album has no ID") } albumNode, err := albumsNode.ChildPathObject(album.ID) if err != nil { return fmt.Errorf("importAlbum: error listing album: %v", err) } dateMod := schema.RFC3339FromTime(album.Updated) // Data reference: https://developers.google.com/picasa-web/docs/2.0/reference // TODO(tgulacsi): add more album info changes, err := albumNode.SetAttrs2( attrPicasaId, album.ID, nodeattr.Type, "picasaweb.google.com:album", nodeattr.Title, album.Title, nodeattr.DatePublished, schema.RFC3339FromTime(album.Published), nodeattr.LocationText, album.Location, nodeattr.Description, album.Description, nodeattr.URL, album.URL, ) if err != nil { return fmt.Errorf("error setting album attributes: %v", err) } if !changes && r.incremental && albumNode.Attr(nodeattr.DateModified) == dateMod { return nil } defer func() { // Don't update DateModified on the album node until // we've successfully imported all the photos. if ret == nil { ret = albumNode.SetAttr(nodeattr.DateModified, dateMod) } }() log.Printf("Importing album %v: %v/%v (published %v, updated %v)", album.ID, album.Name, album.Title, album.Published, album.Updated) // TODO(bradfitz): GetPhotos does multiple HTTP requests to // return a slice of all photos. My "InstantUpload/Auto // Backup" album has 6678 photos (and growing) and this // currently takes like 40 seconds. Fix. photos, err := picago.GetPhotos(ctxutil.Client(ctx), "default", album.ID) if err != nil { return err } log.Printf("Importing %d photos from album %q (%s)", len(photos), albumNode.Attr(nodeattr.Title), albumNode.PermanodeRef()) var grp syncutil.Group for i := range photos { select { case <-ctx.Done(): return ctx.Err() default: } photo := photos[i] r.photoGate.Start() grp.Go(func() error { defer r.photoGate.Done() return r.updatePhotoInAlbum(ctx, albumNode, photo) }) } return grp.Err() }