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.
God bless you