Automatically get a random wallpaper from Unsplash or bing’s-photo-of-the-day using bash scripting

In this post we develop some bash scripts to fetch a new wallpaper from Unsplash or bing's-photo-of-the-day, set it as the desktop background, and send a desktop notification. Then we automate this process using a crontab job.

In this post I’ll be sharing 2 bash scripts to fetch a new wallpaper from the internet, set it as the desktop background, and send a notification. We use 2 sources, one from Unsplash, and the other bing’s-wallpaper-of-the-day. With these scripts, one can install a crontab job to automatically change the wallpaper on a given interval.

Script to download wallpaper from Unsplash

This bash script downloads a random wallpaper from Unsplash, a popular free-to-use image gallery, sets the desktop background and sends out a notification when done:

#!/bin/bash

WP_FILE=$HOME/.config/wallpaper.jpg

wget -q -O "$WP_FILE" https://source.unsplash.com/1920x1080/?city,night,ocean,space,fire,hell

if [[ -f "${WP_FILE}" ]]; then
	xwallpaper --stretch "$WP_FILE" && \
		notify-send -i emblem-photos "Wallpaper" "Wallpaper changed" \
		-h int:suppress-sound:1 -h int:transient:1
fi

We first define a location to save the fetched wallpaper, using WP_FILE=$HOME/.config/wallpaper.jpg. This is an arbitrary choice, you can choose other locations, like $HOME/Pictures/wallpaper.jpg.

The next line downloads a new random wallpaper by querying a special URL, using the wget command. The -q flag suppresses wget‘s stdout messages, and the -O flag specifies the target saving location.

The URL to query is the key part of the game. The 1920x1080 part specifies the desired resolution. The section afterwards specifies the desired theme of the wallpaper by giving a comma separated keyword list: ?city,night,ocean,space,fire,hell. My experiences tell that city and ocean are the more frequent themes, fire or hell (I don’t even know they have such a keyword in their dataset) never appeared.

There are a few other ways to compose the query URL:

  • https://source.unsplash.com/daily: random daily wallpaper.
  • https://source.unsplash.com/weekly?water: random from a search keyword.
  • https://source.unsplash.com/1600x900/?nature,water: to specify a resolution, put it after the base URL. This can be followed by theme keywords.
  • https://source.unsplash.com/random/1920x1080: random but with a specified size.

In the following part, if the specified wallpaper file is found, we set it as the desktop background using the xwallpaper command, which is a lightweight wallpaper setting utility for X. The --stretch flag sets the scaling option. In addition to --stretch which stretches the file to fully cover the screen, other scaling options include --center, --tile and --maximize.

Other than xwallpaper, one can also use feh to set the wallpaper. The syntax would be feh --bg-scale "$WP_FILE". However, feh is a command line image viewer software that can also be used to set wallpapers, so for the purpose of setting wallpapers only, xwallpaper is a more lighter weight solution.

Lastly, if the call to xwallpaper executes successfully, we send a desktop notification using the notify-send command:

notify-send -i emblem-photos "Wallpaper" "Wallpaper changed"  \
-h int:suppress-sound:1 -h int:transient:1

Many desktop environments in Linux have their built-in notification servers, for instance the Cinnamon, GNOME and KDE Plasma DEs. If your DE is using a different implementation of notification server other than notify-send, you will have to replace the notify-send command accordingly.

In the command above, the -i emblem-photos option specifies an icon to display with the notification. You can get a list of the default available icon names from here. The "Wallpaper" part is the subject of the notification, and the "Wallpaper changed" a more detailed description. The -h int:suppress-sound:1 part provides some extra arguments to the notification server, specifically, it suppresses the bell sound of the notification. Another argument passing is done using -h int:transient:1, this one makes the notification transient in that it won’t stay in the notification stack list after popping up for a couple of seconds. The figure below shows a screenshot of running this script. A notification box shows up at the top-right corner of the desktop.

Script to download wallpaper from bing’s-photo-of-the-day

This script is modified from a similar one from a StackExchange answer:

#!/bin/sh

WP_FILE=$HOME/.config/wallpaper.jpg

bing="www.bing.com"

# $xmlURL is needed to get the xml data from which
# the relative URL for the Bing pic of the day is extracted
#
# The mkt parameter determines which Bing market you would like to
# obtain your images from.
# Valid values are: en-US, zh-CN, ja-JP, en-AU, en-UK, de-DE, en-NZ, en-CA or opted out.
#
# The idx parameter determines where to start from. 0 is the current day,
# 1 the previous day, etc.
xmlURL="http://www.bing.com/HPImageArchive.aspx?format=xml&idx=0&n=1&mkt=en-US"

# The desired Bing picture resolution to download
# Valid options: "_1024x768" "_1280x720" "_1366x768" "_1920x1200"
desiredPicRes="_1920x1080"

# The file extension for the Bing pic
picExt=".jpg"

# Extract the relative URL of the Bing pic of the day from
# the XML data retrieved from xmlURL, form the fully qualified
# URL for the pic of the day, and store it in $picURL

# Form the URL for the desired pic resolution
desiredPicURL=$bing$(curl -s $xmlURL | grep -oP "(?<=<urlBase>)(.*?)(?=</urlBase>)")$desiredPicRes$picExt

# Form the URL for the default pic resolution
defaultPicURL=$bing$(curl -s $xmlURL | grep -oP "(?<=<url>)(.*?)(?=</url>)")

# Attempt to download the desired image resolution. If it doesn't
# exist then download the default image resolution
if wget --quiet --spider "$desiredPicURL"
then

    # Download the Bing pic of the day at desired resolution
    curl -s -o "$WP_FILE" "$desiredPicURL"
else
    # Download the Bing pic of the day at default resolution
    curl -s -o "$WP_FILE" "$defaultPicURL"
fi

if [[ -f "${WP_FILE}" ]]; then
	xwallpaper --stretch "$WP_FILE" && \
		notify-send -i emblem-photos "Wallpaper" "Wallpaper changed" \
		-h int:suppress-sound:1 -h int:transient:1
fi

Again, we start by specifying a location (WP_FILE) to save the downloaded wallpaper.

The bulk of the script is about fetching the correct URL of the image. This starts by downloading the xml data containing the information of bing’s-photo-of-the-day wallpaper:

curl -s $xmlURL

An example of the fetched xml data is given below:

<?xml version="1.0" encoding="utf-8" ?><images><image><startdate>20201031</startdate>
<fullstartdate>202010310900</fullstartdate><enddate>20201101</enddate><url>
/th?id=OHR.GorgeSavoie_ZH-CN9079188802_1920x1080.jpg&rf=LaDigue_1920x1080.jpg&
pid=hp</url><urlBase>/th?id=OHR.GorgeSavoie_ZH-CN9079188802</urlBase><copyright>
阿尔卑斯山Cheran峡谷中流淌的河流,法国萨瓦 (© Jean-Philippe Delobelle/Minden)</copyright>
<copyrightlink>https://www.bing.com/search?q=%E9%98%BF%E5%B0%94%E5%8D%91%E6
%96%AF%E5%B1%B1&form=hpcapt&mkt=zh-cn</copyrightlink><headline></headline>
<drk>1</drk><top>1</top><bot>1</bot><hotspots></hotspots></image><tooltips><loadMessage>
<message>Loading...</message></loadMessage><previousImage><text>Previous image</text>
</previousImage><nextImage><text>Next image</text></nextImage><play><text>Play video</text>
</play><pause><text>Pause video</text></pause></tooltips></images>

Then we use grep to fetch the contents inside the <urlBase> tag:

grep -oP "(?<=<urlBase>)(.*?)(?=</urlBase>)"

The -o option prints only the matched part of the string, and the -P flag specifies Perl-compatible regular expressions. If you are not familiar with regular expression (regex), just know that the cryptic strings inside double quotes "" is the magic regex that searches in a string (in this case the xml data shown above) for a pattern that starts with a <urlBase> and ends with a </urlBase>. Upon locating such pattern(s), it returns whatever in-between as a captured group, indicated by (.*?). Here, a non-greedy matching is used (note the question mark ? in (.*?)) instead of a greedy matching (which would be (.*)).

These 2 commands, curl -s $xmlURL and grep -oP "(?<=<urlBase>)(.*?)(?=</urlBase>)" are chained together using the Linux pipe |. Running it would give the following result (assuming the xml data is as shown above):

/th?id=OHR.GorgeSavoie_ZH-CN9079188802

You can verity yourself that this is indeed what’s inside the <usrlBase> tag.

This string is then used to construct the desiredPicURL by concatenating the strings stored in bing, desiredPicRes and picExt:

desiredPicURL=$bing$(curl -s $xmlURL | grep -oP "(?<=<urlBase>)(.*?)(?=</urlBase>)")$desiredPicRes$picExt

Using the above example, desiredPicURL would have a value of www.bing.com/th?id=OHR.GorgeSavoie_ZH-CN9079188802_1920x1080.jpg.

A similar command is called without specifying a resolution:

defaultPicURL=$bing$(curl -s $xmlURL | grep -oP "(?<=<url>)(.*?)(?=</url>)")

The URL in defaultPicURL would be used to get the wallpaper should the desired resolution is not available. The availability is checked using a wget command:

wget --quiet --spider "$desiredPicURL"

The --quiet flag suppresses stdout outputs, and the --spider option makes wget do a availability check without actually downloading data. Depending on the return value, either the desired resolution wallpaper URL (desiredPicURL) or the default one (defaultPicURL) is used to download the image using curl:

if wget --quiet --spider "$desiredPicURL"
then
    # Download the Bing pic of the day at desired resolution
    curl -s -o "$WP_FILE" "$desiredPicURL"
else
    # Download the Bing pic of the day at default resolution
    curl -s -o "$WP_FILE" "$defaultPicURL"
fi

Lastly, we set the obtained wallpaper using xwallpaper as in the Unsplash example.

Set an automatic wallpaper change task using crontab

One can use crontab to schedule some repetitive tasks. Many Linux distros have some implementation of crontab pre-installed. To define a new task, run the following in the terminal:

crontab -e

This will open the default text editor for you to edit crontab jobs. Depending on implementation, it might show you a template in a comment line, so you can modify to your heart’s desire. In case not already included, here is the syntax of a crontab job:

<Minute> <Hour> <Day> <Month> <Day of Week> <command>

For instance,

10 7 * * * /home/user/alarm.sh

executes the script /home/user/alarm.sh at 7:10, everyday. And

0 */6 * * 1-5 /home/user/log_something.sh

runs the /home/user/log_something.sh script every 6 hours, during workdays (1-5, 0 and 7 are both Sunday).

Therefore, to automatically fetch a new random wallpaper every few hours, we can define a crontab job like this:

10 */6 * * * sudo -u guangzhi DISPLAY=:0 DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus /home/guangzhi/.dotfiles/rand_bing_wallpaper.sh

This defines a repetitive task that runs at 10 min after every 6 hours, everyday. Note that we are adding some extra, cryptic options:

sudo -u guangzhi DISPLAY=:0 DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus

This is because in the rand_bing_wallpaper.sh script we are sending notifications via notify-send, which needs to send data into the correct dbus. Otherwise the notification pop-up won’t show. More information regarding this work-around can be found in this Archlinux wiki page.

One comment

  1. God bless you

Leave a Reply