For reasons that I’ll save for another blog post, I decided recently to ditch pretty much the entire Apple ecosystem I’d been using for the last decade. That’s meant gradually transitioning from macOS to Ubuntu, and from iOS to Android. Of course, to ditch iOS for Android required a new phone; after some research, I opted for a Google Pixel 2.

The Pixel 2’s been a great phone and has lots of interesting features, but one of the more esoteric features is called Moving Images. These are Google’s take on Apple’s Live Photos: when you take a photo, a very small amount of video is also recorded, yielding a kind of Harry Potter-like effect. In general, I don’t honestly care all that much about the video bits of these, but every once and awhile, you capture a really unique moment by happenstance where a Live Photo or Moving Image is really special, and on those occasions, I’m incredibly thankful someone at Apple came up with this idea.

In general, I use Google Photos to manage my photo collection, in part because it hits a sweet spot on my convenience/safety metric: the web application and mobile clients are incredibly easy-to-use for day-to-day work, and keeping a local copy of all your photos is as trivial as clicking a checkbox in Google Drive and then downloading them with the Google Backup & Sync tool (or InSync or rclone on Linux). The ease of getting a local mirror of my Google Photos data is great not just for offline access, but also for both offsite backup (in case I ever lose access to my Google account) and trivial rich editing with The GIMP, Lightroom, darktable, Acorn, or any of the other heavier-duty photo editors when I want to. It’s genuinely been one of the better cloud/local hybrids I’ve used.

I was very happy with this setup until just a few days ago, when I made an annoying discovery: Moving Images are very difficult to back up. In fact, the only way I ultimately managed to get everything automatically backed up was to use a tool not from Google, but from Microsoft.

The lost 110 photographs

I wouldn’t honestly have even noticed there was a problem in the first place except that I realized that Backup & Sync failed for exactly 110 files—on all of my machines. macOS, Windows, whatever, didn’t matter, those 110 files wouldn’t download. I could click “Retry All,” I could reinstall Backup & Sync, I could even utterly remove all the downloaded data and retry from absolute scratch, but those 110 files refused to budge. Google is Google, so there was no way for me to really reach out and get genuine tech support,[1] but I did poke through their forums. And promptly felt my heart drop as I found three things very quickly:

  1. I was hardly the only one with this issue.
  2. The Google Drive team would move posts on this topic to the Google Photos forums, and the Google Photos team would move them to the Google Drive forums, because each team generally said it was the other’s problem. As far as I could tell, no matter which forum ultimately ended up being the thread’s home, nothing was resolved (see e.g. this thread, which ended up in the Drive forum).
  3. Many of the affected users mentioned Pixel phones.

This caused me to look at whether there was a pattern to what wasn’t getting downloaded, and I spotted the issue instantly: all 110 files started with MVIMG, the prefix for Moving Images. At that point, I found that there had been topics going back months about Moving Images not syncing properly (e.g. this post from early January). But the good news was that multiple people were saying that newer Moving Images were backing up properly, and it was trivial for me to verify that, indeed, more recent Moving Images I’d taken had downloaded, and some spot-checks showed happy little JPEGs all right where I wanted them to be on my local disk.

Okay, I thought to myself. That stinks, but it’s just those 110 photos; new ones are downloading just fine. So, worst-case, you download 110 photos by hand. Not the end of the world.

I went to sleep and didn’t think more about it.

The “moving” part of Moving Images is optional

It wasn’t until the next morning that I realized something was wrong. When I’d spot-checked more recent Moving Images to verify they had backed up, I of course didn’t actually check on the actual “Moving” part of the Moving Image; while Moving Images are technically JPEGs, the video is stored in such a way that nothing I’ve got can (currently) see it. That didn’t faze me too much, mind—changes were overwhelmingly high that someone else would reverse-engineer the format, and failing that, the chance the thing was just an MPEG concatenated to, or stored inside, a JPEG was extremely high. That’s well inside the realm of things I’ve reverse-engineered in the past. But it did mean that I hadn’t explicitly verified whether a video stream was present.

Over breakfast, a little detail I’d missed finally registered: the files were just too damn small. The Pixel 2 has a 12 megapixel camera. Photos it takes, even with really good compression, really ought to be at least a couple megabytes by themselves; throw in video, and they should be at least 6-10 MB. Yet every file I was looking at was, tops, in the 4 to 5 MB range. That was simply insufficient to store both a high-resolution photo, and a video stream. Something was up.

I picked one of the Moving Images at random. On my Pixel 2, and on the Google Photos website, it showed up as 6.4 MB; my local copy was only 3.4 MB. Another Moving Image showed the same pattern: 7.2 MB on Photos and on my phone, but only 3.7 MB locally. Indeed, a quick sanity check seemed to reveal that all the Moving Images had suffered the same fate. And it wasn’t local to just the official Backup & Sync tool, either: InSync and rclone both showed the exact same behavior, too. Yet downloading the pictures manually from the Google Photos website gave the original, larger image. The only conclusion I could reach: the Google Drive service itself was stripping out the Moving part of the Moving Image.[2]

API? What API?

My first thought was I’d just write my own backup client. After all, while the Drive integration was nice, all I really wanted was automatic offsite backup. While writing something myself wasn’t quite my first pick, I didn’t anticipate it’d be that hard, and since I could download the full, untrimmed files from the Photos website, I knew the raw files existed; it was just a matter of using the proper Google Photos API.

Except…well, there is no Photos API, as far as I can tell. The Picasa Web Albums API has been deprecated since Picasa sunset in 2016, and Google doesn’t list a Photos API anywhere on its developer portal. In other words, the Drive API seemed to be the only official way to go. But I knew from InSync and rclone that the Drive API was exactly where the problem lay in the first place.

Okay, back to the drawing board.

Backup backup options

The second idea I had was to try another photo synchronization service. The raw data was obviously on the phone; I just needed something that could get them off. My first stop was Dropbox: I’d used it for years previously, I knew they had a nice Linux client, and I still used it actively.

Dropbox completely failed here, on two levels: first, it suffered the same trimming issue Google Photos did, so in a narrow sense, it obviously didn’t solve my problem. No biggie.

But Dropbox also failed because it has become downright slimy when it comes to letting you downgrade your account. When I was in Dropbox, I realized I’d fallen below the storage threshold for a free account, so I decided to cancel my paid membership. Dropbox made this incredibly difficult: first, when you click on “Change Plan,” your only option is to upgrade; there is no way to downgrade. You instead have to scroll to the very bottom of the window and click a tiny “Cancel” link. After that, you then have to choose to cancel three or four more times, being interrupted to be told why leaving’s a bad idea on screens where the default button keeps alternating between the “continuing closing my account” option and the “haha no actually I totally want to keep my account, thank you for asking” choice. It took me a couple of tries before I finally extricated myself. Never again, Dropbox. If you have to play that dirty to keep customers, then I’m definitely not sending any business your way.

My next thought was to see if someone had written a photo uploader for Upspin, but they haven’t, and that’s considerably more time than I’ve got right now, so that was it for that idea. I also thought about using Perkeep, since that does have an Android photo uploader, but my Perkeep installation is behind my firewall, and AT&T’s modem prevents my old OpenIKED-based VPN setup from working, so that route was also out.

The final tool I reached for before giving up was Microsoft OneDrive, and I was pleasantly surprised to find that OneDrive just worked. As far as I can tell, OneDrive uploads the unaltered original files, verbatim; if I copy the raw file off my phone via USB, the hashes match.

That said, while I have had very good experiences with OneDrive in the past, simply moving to OneDrive isn’t really an option for me right now: my family all heavily use Google Photos, and we make extensive use of shared albums. Getting everyone moved onto a new service just isn’t feasible, so I was going to have to find a way to make both OneDrive and Google Photos play together somehow.

Time for a short shell script.

The “solution”

I ended up putting together a process that is very gross, but does work: first, I have both Google Drive (via rclone) and OneDrive (via the excellent open-source onedrive client) syncing locally. I create a copy of the Google Photos folder structure in a different location, and then hardlink all of the photos from the InSync folder to the copy. Next, I look for any photos in the copy whose name start with MVIMG_. For each photo I find, I look for a corresponding, larger file in the Microsoft OneDrive camera roll, and, if I find one, move that image over to the new folder structure in place of the Google Drive one.

It’s not ideal, and the resulting Ruby script is not exactly the best code I’ve ever written, but it does work.

Moving forward

Currently, I’m in an unhappy place: I’m generally still using Google Photos, but I’ve also got camera shots going to OneDrive, and I have a gross Ruby script that tries to sanitize this mess. Further, I’m not actually fully confident that these larger files do in fact have the video information I need; I’ll need to learn more about the JPEG file format to figure out if my hunch is correct—and if so, to figure out how to extract the data.

Meanwhile, I’m going to hope that Google either just makes an API for doing this, or otherwise, fixes the Drive API to allow fetching the original files. But at least I don’t have to worry about losing any raw data in the meantime.

  1. This is, strictly speaking, in my particular case, a lie; I know enough people at Google that I can usually just play a game of telephone until I find someone who both works on a relevant team and cares enough to help resolve my problem. But a) normal people cannot do this, and b) this actually was not helpful this time around. ↩︎

  2. To be clear here, it’s possible that’s not quite what’s happening; it’s tricky for me to tell, since I haven’t yet reverse-engineered the file, and Google hasn’t (as far as I can tell) documented what they’re doing. But Photos/Drive editing the file between my phone and my machine means regardless that it’s not trustworthy as a backup option. ↩︎