Automated Screenshot Hosting with jsDelivr
In Which I Make a Cool Script
As I was writing about the Zonos TTS, I took a screenshot of the interface, then realized I didn’t have a method for displaying it in my post.
I did a lot of blogging a long time ago, but it’s been a while - what’s the best way to display an image file, anyway?
Claude gave me several recommendations. One was Cloudinary, which I have used before, but it’s proprietary and I’d rather go open source when I can. It also recommended a unique hack:
3. GitHub Issues "hack" (Super simple):
- Open a new issue in any repo
- Drag & drop or paste screenshot
- GitHub generates a permanent CDN URL
- Copy URL, close issue without saving
- Use URL in your markdown
Who says AI can’t be creative?
It also recommended jsDelivr, which I’ve never heard about. It’s a free CDN for open source projects. Sweet.
In my local directory for code repos, I creatively created a new repo, changed into it, then made it a git repo:
mkdir media
cd media
git init
This repo is public, and now I can easily link anything in the repo using a link like this:
https://cdn.jsdelivr.net/gh/[username]/[repo@branch]/path to file
I like the simplicity of the solution, but of course I don’t want to be manually adding screenshots into the Github repo, committing then pushing then copying the url. No no no I need to automate this.
So I asked Claude for help. After a few iterations, here’s the bash script we settled on:
#!/bin/bash
REPO_PATH="MY LOCAL PATH"
POST_NAME="$1"
MEDIA_PATH="$2"
MEDIA_TYPE="$3" # 'image' or 'audio'
if [ -z "$POST_NAME" ] || [ -z "$MEDIA_PATH" ] || [ -z "$MEDIA_TYPE" ]; then
echo "Usage: ./publish_media.sh <post-name> <file-path> <image|audio>"
exit 1
fi
# Check file size (50MB limit for jsDelivr)
FILE_SIZE=$(stat -c %s "$MEDIA_PATH")
MAX_SIZE=$((50 * 1024 * 1024)) # 50MB in bytes
if [ "$FILE_SIZE" -gt "$MAX_SIZE" ]; then
echo "Error: File size exceeds 50MB limit for jsDelivr"
exit 1
fi
# Create directories if they don't exist
mkdir -p "$REPO_PATH/posts/$POST_NAME/$MEDIA_TYPE"
# Copy file to repo
cp "$MEDIA_PATH" "$REPO_PATH/posts/$POST_NAME/$MEDIA_TYPE/"
# Get filename
FILENAME=$(basename "$MEDIA_PATH")
# Generate markdown
JSDELIVR_URL="https://cdn.jsdelivr.net/gh/sampatt/media@main/posts/$POST_NAME/$MEDIA_TYPE/$FILENAME"
if [ "$MEDIA_TYPE" = "image" ]; then
MARKDOWN=""
elif [ "$MEDIA_TYPE" = "audio" ]; then
MARKDOWN="<audio src=\"$JSDELIVR_URL\" controls></audio>"
fi
# Copy to clipboard
echo "$MARKDOWN" | xclip -selection clipboard
# Git commands
cd "$REPO_PATH"
git add "posts/$POST_NAME/$MEDIA_TYPE/$FILENAME"
git commit -m "Add $MEDIA_TYPE: $POST_NAME/$FILENAME"
git push
echo "Markdown copied to clipboard!"
echo "URL: $JSDELIVR_URL"
Nifty. This puts the file in the repo, adds it, commits and pushes it, then copies the jsDelivr url - in markdown format - into my clipboard so that I can just Ctrl+V into the editor where I’m writing my articles (Obsidian).
For example, here’s exactly what is automatically loaded into my clipboard after using the tool for a screenshot below:

But how will this script trigger? I need it to only do this after a screenshot, and only when I say yes. I don’t want all my screenshots posted to a public Github!
Solution? Another bash script:
#!/bin/bash
WATCH_DIR="screenshots/temp"
MEDIA_SCRIPT="/publish_media.sh"
LAST_POST_FILE="Scripts/.last_post_name"
# Create the file if it doesn't exist with a default value
if [ ! -f "$LAST_POST_FILE" ]; then
echo "2025-02-10-zonos" > "$LAST_POST_FILE"
fi
echo "Watching $WATCH_DIR for new screenshots..."
inotifywait -m "$WATCH_DIR" -e create -e moved_to |
while read -r directory events filename; do
if [[ "$filename" =~ .*png$ ]]; then
FULL_PATH="$WATCH_DIR/$filename"
LAST_POST=$(cat "$LAST_POST_FILE")
notify-send "New Screenshot" "Screenshot saved: $filename"
if zenity --question --text="Publish $filename to blog?"; then
NEW_POST_NAME=$(zenity --entry --title="Post Name" \
--text="Enter post name" \
--entry-text="$LAST_POST")
if [ -n "$NEW_POST_NAME" ]; then
# Save the new post name for next time
echo "$NEW_POST_NAME" > "$LAST_POST_FILE"
"$MEDIA_SCRIPT" "$NEW_POST_NAME" "$FULL_PATH" "image"
if zenity --question --text="Delete original file?"; then
rm "$FULL_PATH"
notify-send "Screenshot" "Original file deleted"
else
notify-send "Screenshot" "Original file kept"
fi
fi
fi
fi
done
This watches the temp screenshot directory I created specifically for this flow, and when it sees a new file, it triggers a notification. This tells me about the new screenshot, then it asks me (using zenity) if I want to publish it or not. If yes, it uses the publishing script above.
Now I don’t actually want this for my main screenshot flow, it would be a bit annoying to be asked if I want to publish each time. So instead, I kept my default screenshot tool bound to the Print Screen button, but I added a new custom keyboard shortcut.
It calls flameshot
, a screenshot tool which allows me to set a custom path for the screenshots. That way when I use shift + Print Screen
I’ll get this upload specific screenshot flow.
I tested it, and it works great. Now I need to automate this. Claude suggests an autostart entry.
I implemented it, and now I don’t need to start these scripts.
So far, it’s working like a charm, it cost me $0, and it has a keyboard binding - what more could a man want?