Wednesday, September 17, 2025

Lightweight Google Photos API Command Line Access

I'm ticking off the boxes needed to automate archiving images from a new camera I picked up. I have taken care of SD card access, and now I'm dealing with pushing files to Google Photos.

What I wanted was a lightweight, curl-based wrapper around the Google Photos Library API. By now, I've written many of these REST wrappers, so I wasn't especially daunted.

I set up new OAuth Credentials and went to work on the first API endpoint: /v1/albums/list. And that's where I hit a roadblock. Despite having hundreds of albums in my Google Photos account, I kept getting a response with this shape:

$ curl -s -H 'Authorization: Bearer <token>' -G https://photoslibrary.googleapis.com/v1/albums
{
  "nextPageToken": "CkAKPnR5cGUuZ29vZ2xlYXBpcy5jb20vZ29vZ2xlLnBob3Rvcy5saWJyYXJ5LnYxLkxpc3RBbGJ1bXNSZXF1ZXN0EowDQUhfdVE0M0Qtd2FKY0FTNkNNSUl3RUdqdHFIQmRuY1FSTy0wTjAxa1lLMUVyWVNFQWFBTnowOE9UZTdlS3dlRTlDd3JRTktvMUxsanhoc3lQZFJ4UWtUdzhUbXlMcm1PYm45Ymk0TVdLSktjalV0dHpSUzZ4OUdoOGpSc29EZ1VQQV9CVUJiVTFOdWNUSVZETVJhRWE1bnFrX3JNQWsta3NuQUFfYmZrVzZnaG5QNXJzMWVNN2FGYWlta1ozMGZ1cGs2MEVLRjRBdm11ek5FRUxvb2M3VXJVQmZtSjhwUkRZbFNrd3dzYzBXT2k4VzdmOGpzeHY1VUxVbThHSkdLZWtSQ0lRUGREamlNMURud2hXZzk2SGc1djJwTFlJdmhXOGRGMzdFUG44OFBVQTRsQy16RlBROXVUeWVJYW52cF9WQ1c5OTJRbk1lLWZ2UjF6bkRxVVJDd0o5V1l1Mzh0a3Q2Q1FpYUxuZHAzUTlQQkJ5WFhwT00tQnJpaENFdlF1RGdQel9raUtJY095GgA"
}

Where's My Stuff?

I found this response baffling: not only were the albums in my Google Photos account not shown, there wasn't even an albums entry in the JSON response. What gives?

Ahhh, the joy of fine print. Taking a moment to actually read the docs for albums/list revealed this description: "Lists all albums created by your app" (emphasis is mine). Wait, what? My app? Do I even have an app?

There's also this note, flagged as a change since April 1st, 2025:

Listing, searching, and retrieving media items and albums

What's changing: You can now only list, search, and retrieve albums and media items that were created by your app.

... If your app relies on accessing the user's entire library, you may need to re-evaluate your app or consider alternative approaches.

So the list of albums is blank because the newly created OAuth configuration I set up has never created any albums. Got it. It's still a bit strange that Google doesn't even return an empty albums array in the JSON response. But, I suppose that's a minor detail I can work around.

Once this sandboxing of images came into focus, I had no problem coding against the API. After all, my albums-list function was working just fine; I just assumed it should return something else.

You can grab gphotoassist here. As always, feel free to customize this script however you would like. Here's a transcript of me using gphotoassist. As a bonus, feel free to pick up geminiassists, too.

Happy hacking!

The Script In Action

  # Prepare a directory to store files locally
  $ photos_dir=$HOME/dl/pics
  $ rm -rf $photos_dir
  $ mkdir -p $photos_dir

  # My reference photo
  $ us=$HOME/dl/20250905_172927.jpg

  # Generate some fresh images to upload
  $ themes="sci-fi jungle western"
  $ for t in $themes ; do \
    echo $t \
    geminiassist -a generate-image -f $us -p "Edit this photo so the couple is in a $t themed photo. Their clothes and background should be updated to match the theme." > $photos_dir/$t.png \
  done
  sci-fi
  jungle
  western

  $ album_id=$(gphotosassist -a album-create -n "Fun Us Pics" | cut -d'|' -f1)
  $ echo "Album Created: $album_id"
  Album Created: AHnaBgu1ZNHo6zvalh4C7EFq4HZJO5jPXVhC39PZcfsW8qNVfRskSNwta7lJ4zldnNcB0BAY1mzw

  $ for f in $HOME/dl/pics/* ; do \
    gphotosassist -a album-add -i $album_id -f $f > /dev/null \
    echo "Added $f" \
  done
  Added /home/ben/dl/pics/jungle.png
  Added /home/ben/dl/pics/sci-fi.png
  Added /home/ben/dl/pics/western.png

  # Confirm the photos are now in the album
  $ gphotosassist -a media-list -i $album_id | while read line ; do \
    name=$(echo $line | cut -d'|' -f 2) \
    echo "$name" \
  done
  jungle.png
  sci-fi.png
  western.png

And here's the newly created pics in Google Photos:

Monday, September 15, 2025

Easy Command Line SD Card Access

I have a firm distrust of all things hardware. A side effect of this is that I tend to interact with a number of different devices and operating systems. Most of the time I live in an OS-agnostic browser, Emacs, or bash prompt. But every once in a while I need to do something OS-specific.

One such task is interacting with Micro SD cards. Years ago, I left myself a note describing how to access an SD card on Windows WSL2. I still refer back to that post every time I need to access a card from within WSL2.

No longer! I've codified accessing an SD card from all three OSs I use: Windows WSL2, Mint Linux, and macOS. Forget searching around; I can now type:

  $ sdcardassist -a root
  /mnt/sdcard

This provides me with the root directory of the SD card that is plugged into the device. It may mount the card if the OS hasn't already done this.

You can grab sdcardassist here. Enjoy!

Up next: My plan is to create gphotosassist, a script to streamline pushing images from an SD card to Google Photos.

Thursday, September 11, 2025

Label 228's - Let's Make Some Street Art!

Shira thinks it's vandalism. I think it's clever. Sticker-street art is often a bit of both. Within this dubious world is the uber-clever subgenre of Label 228 sticker art. 228 is the product code for the United States Post Office's Priority Mail Labels. It was only a matter of time before these blank address labels were repurposed as mini sticker canvases. Not only are they relatively large, high quality, mostly blank space, and available for free in the post office, the government will ship them to you free of charge.

Inspired, back in 2020 I ordered a pack and promised myself that I'd create something. The stickers arrived, and I promptly shoved them in a drawer planning to return to them shortly. Five years later, they continue to taunt me.

No longer! It's time. Let's make some art!

Inspiration Finally Strikes

For years, various ideas would start to take shape, but none came into focus. A couple of weeks ago, I found myself watching a video on choosing a graffiti street name and inspiration struck. Two names quickly came to mind: DRY and FLUX. Instead of creating a 228 masterpiece, perhaps I could scrawl one of these words on a label and finally call this project complete?

The story of FLUX will have to wait for another day, but let's dive into why I was attracted to DRY.

What's DRY?

DRY, of course, means many things. But the context that struck me was as a programming principle: Don't Repeat Yourself.

Here's an example of DRY in action. Say you've been tasked to write AddAndShow, a function that adds two numbers and shows the result. You think, no problem, and write out the following:

  AddAndShow:
  - Take in two numbers
  - Add them
  - Format the result with two decimal places
  - Print the result to the screen

Some time later, you're tasked with coding MultiplyAndShow. It's tempting to copy AddAndShow and replace 'Add' with 'Multiply.'

  MultiplyAndShow:
  - Take in two numbers
  - Multiply them
  - Format the result with two decimal places
  - Print the result to the screen

The problem is that you've duplicated code—you've repeated yourself. This isn't an issue in the short term. But, imagine you're asked to show the results to 4 decimal places? Now you need to make this change in two or perhaps more places.

A better approach is to follow the DRY principle and instead of copying the code, you rework it. Here's what that looks like:

  PerformOperationAndShow
  - Take in two numbers and an operation
  - Perform the operation
  - Format the result with two decimal places
  - Print the result to the screen

  AddAndShow
  - Call PerformOperationAndShow with two numbers and the add
  operation

  MultiplyAndShow
  - Call PerformOperationAndShow with two numbers and the multiply
  operation

Tweaking the number of decimal places can be done in a single place. Success!

For this project, DRY is more than a coding principle; it's a life principle. A call to avoid short-term solutions that lead to long-term headaches in favor of doing the thoughtful work now for a better tomorrow.

Turning DRY Into Art

As I considered how I was going to render DRY on a sticker, it occurred to me that with a bit of coding, I could add another dimension to my creation. The idea was to write a script that would fill the letters of DRY with random noise. If I ran this script 10 times, I'd end up with 10 unique stickers. In other words, I wouldn't repeat myself.

I got pretty far down this path, creating the mkdry script that outputs the following:

As I worked on the script-based solution, I started to wonder how I could accomplish the same thing in analog form. One approach that came to mind was to write out the word DRY and then roll a pair of dice. For each letter, I would get two random numbers. I could then embellish each letter with precisely this many flourishes. The result would be 66, or 46,656, possible variations. That seemed plenty random for the handful of stickers I intended to create.

While further from my comfort zone, the analog solution seems like a better fit for this project. Label 228s are all about pithy elegance; relying on little more than a Sharpie and a pair of dice seems to match that spirit.

Now Comes the Hard Part

With a Sharpie and dice in hand, I created the following stickers:

I'm only lukewarm about how they came out. My inner critic has much to say about them, none of it any good. They fail as graffiti tags, they aren't as polished or amusing as 'real' sticker art, and they generally suck. He thinks I should chuck them in the trash and delete this post. But, I've come to appreciate that inner critics can't be trusted. So while the results don't blow me away, they are unique and were created with intention. So I'm, if you'll pardon the pun, sticking with them.

And so now comes the hard part: having the chutzpah to actually put the stickers in public. I'm pretty sure Shira will bail me out of jail for vandalism charges, but only after I've had a good long stretch in jail to think about my actions. That should be fun.

Wish me luck!

Friday, September 05, 2025

Review: Endurance: Shackleton's Incredible Voyage

Over the years, I've consumed the story of Shackleton's successfully failed Antarctic expedition a number of times in a number of formats: from books to movies to museum exhibitions. Even with all that background, I still found Endurance: Shackleton's Incredible Voyage by Alfred Lansing to be a page turner. Part of this is due to Lansing's skill and thoroughness, and part of this is due to the sheer miraculousness of Shackleton's self-rescue.

There are so many tiny moments of joy, fear, misery, and countless other emotions in the 550+ days that the crew of the Endurance worked to survive that it's impossible to catalog them all. Still, Lansing manages to bring us as many of these moments, both big and small, as possible. Other than exhaustively reading the crew's diaries and published works, I can't imagine getting this level of detail anywhere else.

At times, it could feel like a bit much. When Lansing belabors how cold, hungry, or just plain bored the crew is, he does it in a way that can be borderline tedious. Surely, however, that's also the point. The survival of the expedition was in the extreme, so a few extra pages putting this into context is ultimately a win.

Lansing gives us a view into both the genius and flaws of Shackleton and his crew. In the text, Shackleton lives up to, if not exceeds, his reputation for being a leader. We appreciate how he prioritizes the survival of his crew above all else; he tirelessly works to make this happen. We also appreciate that he could be impulsive and how his optimism could lead him astray. In short, he's a human who managed to pull off a superhuman feat.

Lansing also puts the crew into context. I often imagined Shackleton and his crew as being hearty explorers, ready to endure all. And that may be true for some of the crew. But for others, they were far outmatched by the conditions. The fact that Shackleton got them all home is all the more impressive in this light.

Shackleton's self-rescue is one of mankind's greatest feats. And yet, a nagging question remains: did Shackleton set himself up for failure in the first place? Did he do a sloppy job of picking the crew? Did he venture off into the Antarctic when the conditions were less than ideal? Did hubris put the lives of 28 men in the balance?

Maybe. Lansing doesn't dwell on this question, though he does give you plenty of facts so you can. Ultimately, it's complicated.

Take Frank Worsley, the captain of the Endurance, for example. Worsley is inspired to join Shackleton's expedition thanks to a dream. Shackleton selects him as captain after only a few minute interviews. At the start of their journey, Worsley captained the Endurance from England to Buenos Aires without Shackleton aboard. Lansing recounts a number of incidents on this sailing that show Worsley as a less-than-stellar leader.

Is Shackleton phased? Not especially. He keeps Worsley on. In the end, Worsley distinguishes himself as perhaps one of the greatest navigators to ever live. A rescue often attributed to Shackleton could only be made possible through Worsley's exceptional seamanship.

Does that make Shackleton a genius for selecting Worsley? Or just lucky? This kind of twist is common in this story, and it's one that makes it such a joy to follow.

Another question in the back of my head as I made my way through Endurance was: what does it take to survive the seemingly unsurvivable? Here are six characteristics that came up over and over throughout the story. I offer them in no particular order.

Patience. For large swaths of time, the men are just plain bored. Putting up with utter tedium was a significant challenge and one that was a surprisingly necessary skill.

Discipline. Between limited supplies and unpredictable conditions, the men had to be incredibly disciplined about nearly every action they took. Despite being a private expedition, Shackleton maintained the role of master and commander, and his crew were his subordinates. Food, sleep, and all activities were at Shackleton's discretion, and his crew, for the most part, dutifully obeyed. Shackleton gets credit for the crew's survival because nearly every decision was his. It was a rigorous way to live, but it allowed the crew to survive.

Ingenuity. The resources at the crew's disposal were extremely limited. And yet, when challenges were encountered, solutions were constructed. From turning lifeboats into ocean sailing vessels, to constructing a surgical suite from a dilapidated shelter, to a thousand tiny hacks. Without creative construction, the crew would certainly have been lost. It brings to mind this classic scene in Apollo 13, except there was no support team for Shackleton and his crew. Every problem they encountered, they needed to MacGyver on their own.

Fortitude. As the self-rescue progressed, men went through every misfortune possible: frostbite, being drenched with freezing water, sleep-deprived, thirsty, and starving to name a few. At times—probably most of the time—there was almost no hope of survival. And yet, they all managed to push through. Here an extreme level of physical and mental fortitude made the difference.

Expertise. As mentioned above, without the masterful navigation and seamanship of Frank Worsley, making it home simply wouldn't have been possible. Yes, the crew had to be flexible and improvise on the fly, but expert-level experience absolutely made a key difference in their survival.

Luck. Consider Shackleton's vessel, the Endurance: she may have looked like a normal ship but she was massively overbuilt. Her sides were between 18 and 30 inches thick, and her bow was 52 inches thick. She was specifically designed to endure the ice of the Arctic. Once trapped in pack ice, however, the ship became the ice's play toy. Her fate was to snap like a twig. And that was the reality of the crew's situation: they were insignificant compared to the forces around them. The fact that they survived comes down to luck as much as anything else.

The story of Shackleton and his crew's extraordinary self-rescue is a worth knowing. And while you could read just the highlights, Lansing's text adds much appreciated depth and context to a tale that only improves with detail.