How I used Google Photos to host my website picture gallery

Valentin Hervieu
5 min readJan 28, 2019

--

While building the new version of my personal website, I wanted to create a gallery to display some pictures I like. However, I didn’t want to host them myself and was looking for an easy way to manage them. Then I thought: why not using Google Photos for this?

The final result on my website

Why using Google Photos?

On my previous website, I already had a gallery with a few pictures. I had to manually edit them, crop them to have a common size, generate a thumbnail for each of them. Each time I wanted to add a new one, I would have to do this again and upload them on my server. Pretty cumbersome…

Since I’m already a big user of Google Photos and its shared album feature, I thought: wouldn’t it be nice to use a public album as the backend of my picture gallery? This way, I would be able to add/remove pictures easily from any computer or directly from my phone. Also, I would be able to edit them with a ton of nice tools and maybe have a neat way to handle my thumbnails issue (wait for it 😎).

The problem: as I write this article (January 2019), there is no public API to retrieve the list of images from a shared album (without being authenticated). But then I told myself: wait, maybe I can build my own?

Building my own API to get my shared album pictures

First, I started by creating a new album on my Google Photos account and generating a public link for it:

Create a link dialog

It is basically a link like https://photos.app.goo.gl/XYZ123 where XYZ123 is my album ID. Also, when you open one of the album pictures in a new tab, you get a url like this: https://lh3.googleusercontent.com/A_VERY_LONG_ID

Then, I tried to fetch the album page from an Express app using Axios:

async function getAlbum(id) {
const response = await axios.get(`https://photos.app.goo.gl/${id}`)
return response.data
}

Not surprisingly, it returns a ton of content mixing HTML and JS. But if you look closely, you can see something interesting:

Somewhere near the end of the response

There are some links inside JS arrays that have the same shape as those of my album images: "https://lh3.googleusercontent.com/pw/A_VERY_LONG_ID"

After trying to open them, I noticed that almost all of them were actually the url of my pictures. 😎

Let’s extract them using a good old regex:

const regex = /"(https:\/\/lh3\.googleusercontent\.com\/pw\/[a-zA-Z0-9\-_]*)"/gfunction extractPhotos(content) {
const links = []
let match
while (match = regex.exec(content)) {
links.push(match[1])
}
return links
}

It worked pretty well! However, there were more links than the number of pictures of my album. Some links were duplicated and others were pointing to other images of the page (my profile picture for example). No problem, let’s try to find something to exclude those wrong links…

I actually got lucky: I’ve noticed that the album pictures were the only ones to always be at the beginning of a JS array! 😀 What I had to do was only look for parts with the following shape: ["https://lh3.googleusercontent.com/pw/A_VERY_LONG_ID" (don’t forget the [😉)

So I updated my regex accordingly and used a Set to remove the duplicates:

const regex = /\["(https:\/\/lh3\.googleusercontent\.com\/pw\/[a-zA-Z0-9\-_]*)"/g // the only difference is the [ at the beginningfunction extractPhotos(content) {
const links = new Set()
let match
while (match = regex.exec(content)) {
links.add(match[1])
}
return Array.from(links)
}

🎉 I now only have the links of my album pictures 🎉 The whole API code is on Glitch. The endpoint for the demo is: https://google-photos-album-demo2.glitch.me/YOUR_ALBUM_ID

Integrating this API in a React app

Now that we have an endpoint that returns a list of pictures, let’s build a small app to display them. For this, I found a nice React component: https://github.com/xiaolin/react-image-gallery.

Here’s how to call the API and render the pictures using the ImageGallery component:

import React from "react";
import ReactDOM from "react-dom";
import axios from "axios";
import "react-image-gallery/styles/css/image-gallery.css";
import ImageGallery from "react-image-gallery";
const ImagesGallery = () => {
const [images, setImages] = React.useState(null)
React.useEffect(() => {
let shouldCancel = false
const call = async () => {
const response = await axios.get(
'https://google-photos-album-demo.glitch.me/4eXXxxG3rYwQVf948'
);
if (!shouldCancel && response.data && response.data.length > 0) {
setImages(response.data.map(url => ({
original: `${url}=w1024`,
thumbnail: `${url}=w100`
})))
}
}
call()
return () => shouldCancel = true
}, [])
return images ? <ImageGallery items={images} /> : null
}
export default ImagesGallery

A handy trick I noticed in the Google Photos images urls is that you can add =w1024 or =w100 at the end to change the width of the output image (respectively for 1024px and 100px). 😍 That way we can use different sizes for the original and thumbnail images. Edit: this is actually documented here: https://developers.google.com/photos/library/guides/access-media-items#image-base-urls

Here’s the result:

Conclusion

This is completely hacky but it was fun to build so I thought it was a good topic to write my first article about.

Thanks to it, I can now simply manage my gallery using a shared album. I can crop, rotate and adjust the colors and I have an automatic way to generate each required size including the thumbnail ones. The best part is to be able to do all of this directly from my phone! 😍

I don’t know for how long it will work because there are many ways this can break! 😁 But I had a lot of fun working things out and I hope you found this interesting.

👋Hi! I’m Valentin Hervieu. I work at Ricardo.ch as a frontend engineer and I live in the French Riviera. I enjoy coding, especially using React.

--

--

Valentin Hervieu

I’m working as a frontend engineer at Ricardo.ch in the French Riviera. I enjoy coding, especially using React. Also available for freelancing.