Often I'll want to share a screenshot and over the years I've used a number of different tools, but I'm always beholden to when they eventually become unsupported or the company "pivots" and the pricing model goes silly. So after all this time, I finally worked out how to roll my own.

Forewarning though, this is MacOS specific, but I'm sure same thinking can be applied to other operating systems.

UK EVENTAttend ffconf.org 2024

The conference for people who are passionate about the web. 8 amazing speakers with real human interaction and content you can't just read in a blog post or watch on a tiktok!

The spec

I want to be able to take a screenshot and then, without any interaction, have a sharable URL in my clipboard.

The steps to achieve this (on my mac) are:

  1. When a new file appears in my screenshot folder, perform an action
  2. Read the latest file in the folder and upload to a hosting service
  3. Copy the URL of that image to my clipboard

There's a couple of bonus features that I can add to this:

  1. Compress the image first then upload it
  2. Play some kind of notification so I know the upload is complete
  3. Only apply this action to new screenshots - not a file I might have renamed

This lose outline gives me the power to use my own screenshot software, whether it be a plain native screenshot, or shottr (which I really like for quick annotations before getting dropped into my screenshot folder). If (or when) shottr stops working, my process can still work.

I'm also free to decide how I host the images. For the time being, this is via AWS S3.

Finally, I want a fancy url for the images, so I'll lean on Netlify for this.

The implementation

I've tried to break this down into readable blocks, but this isn't intended as a copy and paste solution (though you probably could copy all the blocks).

1. When a new file appears

I originally tried to achieve this using MacOS's Automator app but frankly I've no idea how it works, where configuration files live or anything.

However, a simpler solution (to me!) is a WatchPath service to monitor a directory for MacOS. This means when the directory contents changes, it will let me run a terminal command.

The command I run will then do all the heavy lifting to work out what's changed and what to do:

I created ~/Library/LaunchAgents/upload_screenshot.plist and registered it with:

$ launchctl load ~/Library/LaunchAgents/upload_screenshot.plist

The upload_screenshot.plist file contains the following XML (note that ~/Desktop/Screenshots is where my screenshots land):

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC -//Apple Computer//DTD PLIST 1.0//EN
http://www.apple.com/DTDs/PropertyList-1.0.dtd>
<plist version="1.0">
<dict>
  <key>Label</key>
  <string>Upload Screenshot</string>
  <key>ProgramArguments</key>
  <array>
    <string>~/bin/upload_screenshot</string>
  </array>
  <key>WatchPaths</key>
  <array>
    <string>~/Desktop/Screenshots</string>
  </array>
</dict>
</plist>

2. Doing a thing with that screenshot

What this script does is:

  1. if the file hasn't been processed already, then –
  2. copy the predicted url to the clipboard
  3. compress the file (adding -min.png to the end)
  4. remove the old image
  5. upload the file
  6. play a notification sound

The file lives in ~/bin/upload_screenshot (~ being short hand for my $HOME directory), and it has execute properties, i.e. chmod +x ~/bin/upload_screenshot.

There's also a few external programs being used:

  • pbcopy a macos command to copy a string to the clipboard
  • afplay another macos native command, plays audio - the Blow.aiff was just a sound I found in the operating system
  • pingquant a third party png compressor - it helped upload speeds to compress the image (and of course, bandwidth, costs, etc)
  • aws the command line tool to actually run the upload to AWS using a profile I created called screenshot

Below is the script itself:

DIR=/Users/remy/Desktop/Screenshots

# get the latest file
FILE=$(ls -rAt $DIR | tail -n 1)

# if it's a junk file, or it's already processed, exit early
if [[ "$FILE" == .DS_Store ]]; then exit; fi
if [[ "$FILE" == *-min.png ]]; then exit; fi

# copy the URL to the clipboard as early as possible
echo https://remysharp.com/shot/$(basename $FILE .png)-min.png | pbcopy

# compress the original and output as $FILE-min.png
/Users/remy/bin/pngquant $DIR/$FILE --ext -min.png

# remove the original
rm $DIR/$FILE

# upload to AWS under my "screenshot" profile
AWS_PROFILE=screenshot /usr/local/bin/aws s3 cp $DIR/$(basename $FILE .png)-min.png s3://screenshots.remysharp.com

# play tone so I know it's ready
afplay /System/Library/Sounds/Blow.aiff

3. AWS policy

AWS policies always bite me, so I've included it in this write up. I created a bucket called screenshots.remysharp.com which the policy below can write to. The policy was created against a new user called screenshot.

Note that I also configured the AWS bucket to be public so that when I shared a URL the image could be actually read.

 {
  "Version": "2012-10-17",
  "Id": "Policy1397632521960",
  "Statement": [
    {
      "Sid": "Stmt1397633323327",
      "Effect": "Allow",
      "Principal": {
        "AWS": "*"
      },
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::screenshots.remysharp.com/*"
    }
  ]
}

4. Fancy URLs

Finally, when the images are uploaded, you might have noticed that I didn't copy the AWS URL (because they're ugly), but a "nice" URL under my own domain.

To achieve this, since my blog (the domain remysharp.com) is hosted on Netlify, and Netlify offers a really superb redirect service, I'm able to use their "splats" to hide the AWS URL and make it nice.

It also means should I ever move to a new image hosting provider, and I wanted to keep my screenshots, I can keep supporting these URLs as I have full control of them.

In my _redirects file I include the following line:

/shot/* https://s3.eu-west-2.amazonaws.com/screenshots.remysharp.com/:splat 200

There you go

I think it's pretty neat 😉 https://remysharp.com/shot/SCR-20221128-doc-min.png

neat