gphotos

package module
v1.0.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 22, 2025 License: BSD-3-Clause Imports: 16 Imported by: 0

README

gphotos

Golang Google Photos package and cli utilizing the Picker API. Includes the ability to copy media from Google Photos to S3.

Install

go get github.com/polastre/gphotos

Requires go 1.24.0 or higher.

Why this package?

There's two reasons this exists:

  1. Google stopped producing a Google Photos golang package a long time ago, and...
  2. Google deprecated their Google Photos album/mediaItems APIs in favor of a Picker API. The Picker API is not well documented and it would be helpful to have a library that actually works.

About the Picker API

Google's move to the Picker API for Google Photos completely changes how apps integrate with photos.

Apps now have two ways of interacting via the API:

  1. Media and albums created by the app itself can be accessed through the normal Library API. If the app is something like a photo frame or media backup utility, it is highly unlikely your app created the album AND the media inside it. Even moving photos to an album created by your app doesn't allow your app to see them because the photos themselves weren't uploaded by your app.
  2. Apps can redirect users to a "Media Picker" at Google Photos by creating a "picker session" for that user. After the user picks their media to share with the app, the app has access to those media items via the Picker API until the picker session expires. The session usually expires within 24 hours, which means the app only has access to the picked media for a short time.

Based on these observations, I've come to the conclusion that the only logical solution is to copy off the media from Google Photos while the picker session is still valid, for use after the picker session is complete. AWS S3 is a logical place to store this data so that your apps can fetch the media when they need it.

This repo and package provides implementations to support this workflow. See the auth and picker utilities below.

Gettings started with the API

The picker utility in cmd/picker is a good place to start to understand the flow.

The first thing needed is Google OAuth credentials. If you don't have them, there's a utility in auth that fetches the credentials from the command line by providing a sign in link and the capturing the results in a local callback.

With Google Credentials in hand (largely out of scope of this package), create credentials:

creds := gphotos.Credentials{
    ClientID:     args.GoogleClientID,
    ClientSecret: args.GoogleClientSecret,
    RefreshToken: args.Token,
}

Then create a new Picker session for that user with Google Photos:

sesh, err := creds.NewPickerSession()
if err != nil {
    panic(err)
}
fmt.Printf("Visit this URL to pick photos for the app:\n%s\n\n", sesh.PickerURI)

If you're building this into a webapp, you can redirect the user at this point to the PickerURI. Be sure to do this in a new tab because Google doesn't send the user back to your application when they're done, instead they see a screen that says "Done" and nothing else.

From this point, poll the Google Photos Picker API for the session to see when it is complete. You can optionally add callbacks to abort the process or print status.

photos, err := sesh.Poll(context.Background())
if err != nil {
    panic(err)
}
fmt.Printf("%d total items, now uploading to S3\n", len(photos))

If you'd like to upload the media to S3 when you're done, optionally use the UploadToS3 func.

s3options := gphotos.NewS3Options(bucketName)
err := creds.UploadToS3(photos, s3options)

Utility: auth

The auth cli will perform a new OAuth authentication with the Google auth service. This is useful to get a refresh token that can be used with the Google Photos Picker for a specific user.

% GOOGLE_CLIENT_ID=your_id GOOGLE_CLIENT_SECRET=your_secret go run cmd/auth/main.go
Visit the following URL to authorize the app:
https://accounts.google.com/o/oauth2/auth...

You can get your client ID and secret from the Google API Console. See the Google Photos configuration documentation.

The refresh_token returned will be needed as the --token input for the picker utility.

Utility: picker

The picker cli allows you to pick some photos from Google Photos and then copy them to a S3 bucket of your choice.

Run the command to see all the options. Note that all of the options are required including Google config, AWS config, token, and bucket.

% go run cmd/picker/main.go
Usage: main --client-id CLIENT-ID --client-secret CLIENT-SECRET --awsaccesskeyid AWSACCESSKEYID --awssecretaccesskey AWSSECRETACCESSKEY --region REGION --token TOKEN --bucket BUCKET

Options:
  --client-id CLIENT-ID [env: GOOGLE_CLIENT_ID]
  --client-secret CLIENT-SECRET [env: GOOGLE_CLIENT_SECRET]
  --awsaccesskeyid AWSACCESSKEYID [env: AWS_ACCESS_KEY_ID]
  --awssecretaccesskey AWSSECRETACCESSKEY [env: AWS_SECRET_ACCESS_KEY]
  --region REGION [env: AWS_REGION]
  --token TOKEN, -t TOKEN
                         Google OAuth Refresh Token
  --bucket BUCKET, -b BUCKET
                         Destination S3 Bucket
  --help, -h             display this help and exit

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	TypeUnspecified = MediaType("TYPE_UNSPECIFIED")
	TypePhoto       = MediaType("PHOTO")
	TypeVideo       = MediaType("VIDEO")
)
View Source
var (
	ErrPollingCallbackFalse = errors.New("callback returned false, so polling was halted")
)

Functions

func S3Key

func S3Key[T any](bucket string, filename string) ([]T, error)

func SetS3Key

func SetS3Key[T any](bucket string, filename string, photos []T) error

Types

type Credentials

type Credentials struct {
	ClientID     string // ClientID is your app's client ID from Google
	ClientSecret string // ClientSecret is your app's client secret from Google
	RefreshToken string // Refresh token is the _user's_ refresh token from first authentication that can be used to get a new access token
	AccessToken  *Token // Optionally supply a valid access token, which will be used if provided
}

Credentials represents a Google Photos OAuth2 credential that can be used to get a valid access token.

func (*Credentials) NewPickerSession

func (c *Credentials) NewPickerSession() (*GooglePhotosPickerSession, error)

func (Credentials) NewUserAuthorization

func (c Credentials) NewUserAuthorization()

func (*Credentials) Token

func (c *Credentials) Token() (*Token, error)

Token fetches an access token for the provided credentials. Also sets the AccessToken field of the provided credentials.

func (*Credentials) UploadToS3

func (c *Credentials) UploadToS3(photos []GooglePhotosPickedItem, opts S3Options) error

UploadToS3 writes the photos to an S3 bucket.

S3 environment variables _must_ be set, including:

  • AWS_ACCESS_KEY_ID
  • AWS_SECRET_ACCESS_KEY
  • AWS_REGION

type Duration

type Duration time.Duration

func (*Duration) UnmarshalJSON

func (d *Duration) UnmarshalJSON(b []byte) error

type GoogleOAuthError

type GoogleOAuthError struct {
	ErrorCode string `json:"error"`
	Message   string `json:"error_description"`
}

GoogleOAuthError represents an error during an OAuth exchange with Google

func (GoogleOAuthError) Error

func (e GoogleOAuthError) Error() string

type GooglePhotosError

type GooglePhotosError struct {
	Code    int    `json:"code"`
	Status  string `json:"status"`
	Message string `json:"message"`
	Details any    `json:"details"`
}

GooglePhotosError is the content of the error message returned in JSON responses from the API.

func (GooglePhotosError) Error

func (e GooglePhotosError) Error() string

type GooglePhotosPickedItem

type GooglePhotosPickedItem struct {
	ID         string
	CreateTime string
	Type       MediaType
	Media      GooglePhotosPickedMedia `json:"mediaFile"`
}

type GooglePhotosPickedItems

type GooglePhotosPickedItems struct {
	Items         []GooglePhotosPickedItem `json:"mediaItems"`
	NextPageToken string                   `json:"nextPageToken"`
	Error         *GooglePhotosError       `json:"error"`
}

type GooglePhotosPickedMedia

type GooglePhotosPickedMedia struct {
	BaseURL  string
	MimeType string
	Filename string
	Metadata GooglePhotosPickedMetadata `json:"mediaFileMetadata"`
}

type GooglePhotosPickedMetadata

type GooglePhotosPickedMetadata struct {
	Width       int
	Height      int
	CameraMake  string
	CameraModel string
}

type GooglePhotosPickerSession

type GooglePhotosPickerSession struct {
	ID            string                    // ID of the session created by Google for this user
	PickerURI     string                    // URI to send the user to pick photos
	PollingURI    string                    `json:"-"` // URI to poll to find out when the user is done
	PollingConfig GooglePhotosPollingConfig // Recommended polling configuration for the Polling URI from google
	ExpireTime    time.Time                 // Time that the session expires
	MediaItemsSet bool                      // True if the user has finished picking photos
	Credentials   *Credentials              `json:"-"`     // Credentials used to create this session
	Error         *GooglePhotosError        `json:"error"` // Only present if there's been an error returned by the API
}

GooglePhotosPickerSession represents a session where a user can pick photos from the Google Photos Picker UI.

func (*GooglePhotosPickerSession) Poll

Poll polls the Google Photos session API until MediaItemsSet is true or an error occurs.

Provide a callback func if to show progress to the user or interrupt the polling. Returning `false` from a callback will stop the polling with an `ErrPollingCallbackFalse` error.

type GooglePhotosPollingConfig

type GooglePhotosPollingConfig struct {
	PollInterval Duration // How often the polling uri should be polled
	TimeoutIn    string   // when the picker session times out
}

GooglePhotosPollingConfig is google's recommended polling config

type MediaType

type MediaType string

type S3Options

type S3Options struct {
	Bucket        string // Required. s3 bucket to upload content.
	PhotosJSONKey string // s3 key for a json dump of all the photos info, default to `photos.json`
	PhotosPrefix  string // s3 key prefix for where to put the photos without the trailing slash, defaults to `photos`
	Width         int    // width of the image to request from Google Photos. If not provided, gets full width
	Height        int    // height of the image to request from Google Photos. If not provided, gets full height
	AddExtension  bool   // add the extension of the file onto the s3 key. Defaults to false, uploading by Google Photos ID
}

S3Options provide configuration over where photos should be stored in S3

func NewS3Options added in v1.0.1

func NewS3Options(bucket string) S3Options

NewS3Options creates a new S3Options object with defaults

func (S3Options) PhotoJSON

func (o S3Options) PhotoJSON() ([]GooglePhotosPickedItem, error)

PhotoJSON returns the photos metadata json file stored in S3

func (S3Options) SetPhotoJSON added in v1.0.1

func (opts S3Options) SetPhotoJSON(photos []GooglePhotosPickedItem) error

type Token

type Token struct {
	AccessToken string `json:"access_token"`
	ExpiresIn   int    `json:"expires_in"`
	ExpiresAt   time.Time
	Scope       string `json:"scope"`
	TokenType   string `json:"token_type"`
}

Token is a Google OAuth2 Access Token

Directories

Path Synopsis
cmd

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL