Creating YouTube Thumbnails with a Shell Script

It’s almost as good as an embed, but without Google’s spyware JavaScript.

Thu, 21 Jul 2022

There are times when I want to include a YouTube video in a post, but I’m not content with a regular link. YouTube provides embeds for this purpose, but they use <iframe> and come with a hefty JavaScript payload. Among other things, this payload tracks visitors on my site if they play a video embedded in one of my posts. I don’t particularly like the idea of Google tracking people visiting my website; if I wanted Google to have any information about my visitors I’d run Google Analytics.

yttget: An Alternative to YouTube Embeds

If I don’t want embeds because of JavaScript and analytics, but plain links are too bland, what can I do? I can wrap a thumbnail inside a link with a tooltip and caption advising visitors to click the image to watch the video, and have the link open in a new tab.

Every video has an unique identifier, such as “o0W91FrTlYk”. This identifier represents the official video for “End of the Beginning” by Black Sabbath. You can get the ID out of the URL for a particular video, which in this case is https://www.youtube.com/watch?v=o0W91FrTlYk. Once you have the ID, you can retrieve its associated thumbnail, which currently lives at http://i3.ytimg.com/vi/o0W91FrTlYk/hqdefault.jpg

If you’re on a Unix-like machine1, you can automate the process of extracting the ID from a given YouTube URL and fetching its thumbnail with a reasonably small shell script. You can even have this script create HTML for displaying the thumbnail and pipe it to your clipboard. I did that myself with a little script I call yttget, for “YouTube Thumbnail GET”2. Here’s the code, sans license text.

#!/bin/sh -e

URL="$1"
ID=$(echo $URL | cut -d = -f 2)
FILENAME="youtube-${ID}"
CWD=$(pwd)
JPG="${CWD}/${FILENAME}.jpg"
THUMB_URL="http://i3.ytimg.com/vi/${ID}/hqdefault.jpg"
CAPTION="click to view"

curl ${THUMB_URL} --output ${JPG}

echo "<figure>
  <a href=\"https://www.youtube.com/watch?v=${ID}\" 
     title=\"${CAPTION}\"
     target=\"_blank\"
     rel=\"noreferrer noopener nofollow\">
    <picture>
      <source srcset=\"/media/${FILENAME}.avif\" type=\"image/avif\">
      <source srcset=\"/media/${FILENAME}.webp\" type=\"image/webp\">
      <source srcset=\"/media/${FILENAME}.jpg\" type=\"image/jpeg\"> 
      <img src=\"media/${FILENAME}.jpg\" 
           alt=\"preview image for YouTube video ID ${ID}\" 
           width=\"480\"
           loading=\"lazy\">
    </picture>
  </a>
  <figcaption>(${CAPTION})</figcaption>
</figure>" | pbcopy
source code for yttget

It should run on any system that provides a POSIX-compliant shell, and requires curl. Since I’m on a Mac I use pbcopy to pipe standard output into the system’s clipboard, but people using GNU/Linux or BSD can easily replace it with xclip or xsel. Hell, you could probably replace curl with wget if you wanted to, but I couldn’t be bothered.

Using yttget

Here’s how you use yttget. (Don’t forget to replace the URL unless you really like Black Sabbath.)

  1. Navigate to the directory in which you want to place the thumbnail.
  2. Type yttget "https://www.youtube.com/watch?v=o0W91FrTlYk"3
  3. Paste the resulting HTML into your editor.

The script generates HTML, particularly a <picture> element wrapped inside a <figure>. You should edit this to taste, especially if you don’t want to deal with generating WEBP and AVIF files from JPEG images. You can paste it directly into a HTML or Markdown file. You can also use this with Org mode in GNU Emacs, but you’ll need to wrap it in a #+BEGIN_EXPORT html block. You should also add text inside the <figcaption> for your readers’ benefit.

Once you do, it should look like something like this when rendered.

preview image for YouTube video ID o0W91FrTlYk
“End of the Beginning” by Black Sabbath (from 13) (click to view)

The link has rel="noreferrer noopener nofollow" for security and SEO reasons. The first two, noreferrer and noopener, mitigate exploits involving the JavaScipt function window.open(). The use of nofollow means that I’m not giving YouTube any “link juice”, which it shouldn’t need anyway because it’s user-generated content and Google owns it.

Getting yttget

You can grab a copy here. It’s available under the zero-clase BSD license.

Tools Used in yttget

Extra: Generating Alternate Image Formats

If you want to use the <picture> element to serve more modern image formats like WEBP and AVIF, you’ll need to generate these formats from the JPEG you downloaded from YouTube. You can do this with ImageMagick, which provides a suite of command-line image processing tools. The patch looks something like this:

diff --git a/media/yttget.sh b/Users/starbreaker/bin/yttget
index a692369..beb2458 100755
--- a/media/yttget.sh
+++ b/Users/starbreaker/bin/yttget
@@ -21,11 +21,16 @@ ID=$(echo $URL | cut -d = -f 2)
 FILENAME="youtube-${ID}"
 CWD=$(pwd)
 JPG="${CWD}/${FILENAME}.jpg"
+WEBP="${CWD}/${FILENAME}.webp"
+AVIF="${CWD}/${FILENAME}.avif"
 THUMB_URL="http://i3.ytimg.com/vi/${ID}/hqdefault.jpg"
 CAPTION="click to view"
 
 curl ${THUMB_URL} --output ${JPG}
 
+magick -quality 80 ${JPG} ${WEBP}
+magick -quality 80 ${JPG} ${AVIF}
+
 echo "<figure>
   <a href=\"https://www.youtube.com/watch?v=${ID}\" 
    title=\"${CAPTION}\"
my patch to yttget for image conversion

You need not do your image conversion piecemeal, of course. If you have some experience in writing and editing makefiles and you’re using one to build and deploy your website, it’s easy to implement batch conversion. I did it myself when customizing the makefile in my copy of pblog.


  1. This includes GNU/Linux, the various BSD operating systems, and macOS↩︎

  2. It might not be the best name for a shell script in history, but it made sense to me at the time. You can name your copy whatever you want.↩︎

  3. I have the URL in quotes because the invocation without quotes doesn’t work in my Mac’s version of zsh. Other shells may not require this.↩︎