Bryan Grohman

All Writing

How to Migrate Away From Google Photos

2018-02-02

Google Takeout

Table of Contents

Why

When I first started my photography hobby years ago, I eventually found my way to Adobe Lightroom for photo editing and organization. After several years of using it, Google Photos was released, and I switched to it for photos from my mobile device. I was still using Lightroom for photos from my "real" cameras on an old MacBook followed by a Mac Mini. Over time, the Mac started to show it's age, and Lightroom began to feel cumbersome, so I switched to Google Photos full time. I exported my Lightroom Collections to Google Photos and switched to a combination of mobile apps (Snapseed, Google Photos itself, others) on an iPad for editing.

In Lightroom, I organized my favorite photos into a handful of Lightroom Collections - virtual directories of photos with links to the photos instead of copies. In Google Photos, I created albums to replace each Lightroom Collection.

Fast forward a few years, and I'm making another change. Instead of Google Photos, I'll use good old fashioned files and directories on a laptop running Linux. It's a simpler photo management approach, I'll be using open source tools, and my photos will be easily accessible on all of my devices, not just through the Google Photos app. Plus, there are several open source photo management and editing tools for Linux that I'm interested to try.

I'll move my favorite photo collections into directories within Dropbox so that they're synced across devices. Until I find a laptop with a screen with acceptable color accuracy, I'll continue using the iPad for photo editing.

Download Your Photos with Google Takeout

Navigate to Google Takeout, click the "Select None" button to unselect all Google services, and then turn on only the Google Photos option.

Make sure the "All photo albums" option is selected. Internally, Google Photos organizes all of your uploaded photos into albums by date automatically. The app doesn't display these albums, but they will be present in your archives from Google Takeout.

Click "Next" at the bottom of the page, and choose your file type, archive size, and delivery method. I used the default of 2GB zip files, but I changed my delivery method to "Add to Dropbox" and connected my Dropbox account. As each archive is prepared by Google, this will give you the option to have it automatically synced to your computer via the Dropbox client.

Depending on the number of photos you have stored in Google Photos, the archive creation and download can take a while. I had 142GB spread across 71 zip files, and it took most of a weekend for the archives to be made available within Dropbox and then downloaded to my laptop. The archive creation process seemed to pause several times along the way. Google will send you an email confirmation once the archives have all been created in your Dropbox account.

Extract the Zip Files

After the archives were finished downloading from Dropbox, I moved them out of Dropbox into a local-only directory so they weren't taking up any of my Dropbox storage space.

I didn't want to manually unzip all 71 zip files, so I wrote a small bash script to handle it. The last line removes the json metadata files included in the archives - you should remove that line if you want to keep the metadata for your own purposes. You'll want to replace the paths accordingly.

#! /bin/bash
archives=~/Downloads/google_takeout_photos_archives/*.zip
extraction_dir=~/Downloads/google_takeout_photos_extracted/
mkdir -p $extraction_dir

for f in $archives
do
    echo ""
    echo "Extracting $f ..."
    unzip $f -d $extraction_dir
    echo "Extracting $f complete!"
done

find $extraction_dir -name "*.json" -delete

After running that script, you'll have all of your photos extracted. Note that they will still be organized inside of directories corresponding to the Google Photos album they were a part of. You might want to pipe the output from the script to a log file so that you can check for any errors.

Review the Takeout Report

Google creates a nice summary of your data as an index.html file within the Takeout directory inside the extracted files. It shows you the list of directories exported as well as any errors in the process. I had one file with a retrieval error that was not included in the archive, but the index.html file included a link to download it manually.

Reorganize as Needed

At this point, you can reorganize your photos to fit into your preferred structure. I've moved my favorite photos directories into Dropbox, but I'm still deciding if I want to create a script to reorganize the extracted photos to fit into my existing organization approach or just leave them as they are within a "Google Photos" directory on my laptop. Either way, I now have all of my photos stored locally again as well as the most important photos synced to Dropbox.

Delete From Google Photos

Once you've extracted and reorganized all of your files, you can safely delete them from Google Photos. It's important that you've downloaded every photo you want to keep and stored them locally - deleting them from Google Photos is permanent.

Unfortunately, Google doesn't provide a way to delete all of your photos at once. The suggestion from Google's forums and other sites is to use the Google Photos UI to select multiple photos at once using the shift key and delete them in batches. But you can only select a few hundred at a time. With a large photo library, that's awfully tedious work.

Instead of deleting them manually, I wrote a small JavaScript utility that will delete all of the photos for you. Just open Google Photos in your web browser, use the browser to zoom out to 50% so more photos are rendered at a time, and paste the script into the JavaScript console. You'll want to keep the Google Photos browser window focused while the script is running. Occasionally the script will seem to get stuck, but reloading the page and running the script again did the trick for me.

var BG = (function(exports) {

    function sleep(ms) {
        return new Promise(resolve => setTimeout(resolve, ms));
    }

    function waitAndClick(selector) {
        return new Promise(resolve => {
            function check() {
                var elements = document.querySelectorAll(selector);
                if (elements.length > 0) {
                    elements[0].click();
                    resolve();
                } else {
                    setTimeout(check, 500);
                }
            }
            check();
        });
    }

    async function deleteRenderedPhotos() {
        var result = false;
        var checkboxes = document.querySelectorAll('.QcpS9c.ckGgle[role="checkbox"]');

        if (checkboxes.length > 0) {
            result = true;
            checkboxes.forEach(function(e) {e.click();});

            await sleep(3000);

            // delete button
            await waitAndClick('[aria-label="Delete"] content');

            // delete confirmation
            await waitAndClick('.O0WRkf.oG5Srb.HQ8yf.C0oVfc.kHssdc.HvOprf.M9Bg4d content');
        }

        return result;
    }

    async function deleteAllPhotos() {
        var keepGoing = true;

        while (keepGoing) {
            keepGoing = await deleteRenderedPhotos();
            await sleep(1000);
        }
    }

    exports.deleteAllPhotos = deleteAllPhotos;
    return exports;
}({}));

BG.deleteAllPhotos();