Skip to content

Instantly share code, notes, and snippets.

@grenade
Last active November 11, 2024 21:38
Show Gist options
  • Save grenade/b79e520c7f4de6decf613648f16f4589 to your computer and use it in GitHub Desktop.
Save grenade/b79e520c7f4de6decf613648f16f4589 to your computer and use it in GitHub Desktop.
create gnome wallpaper with different random images for each screen
#!/usr/bin/env bash
# usage: curl -sLH 'Cache-Control: no-cache, no-store' https://gist.github.com/grenade/b79e520c7f4de6decf613648f16f4589/raw/random-wallpaper.sh | bash
app_name=random-wallpaper
# path to temporarily store images for resize and stitching
temp_dir=/tmp/${app_name}
# path to store photos database
state_dir=${HOME}/.local/state/${app_name}
# path to store stitched background images
backgrounds_dir=${HOME}/.local/share/backgrounds/${app_name}
# url for the unsplash archive
lite_archive_url=https://unsplash.com/data/lite/latest
# path to store unsplash archive
lite_archive_path=${state_dir}/unsplash-research-dataset-lite-latest.zip
# name of the tsv db in the archive
photos_tsv_archive_path=photos.tsv000
# path to store tsv db file
photos_tsv_path=${HOME}/.local/state/${app_name}/photos.tsv
# directory path of the tsv db file
photos_tsv_dir_path=$(dirname ${photos_tsv_path})
# ignore unsplash photos with fewer downloads than this
minimum_popularity=5000
# metadata text annotation color
photo_annotation_color=white
# metadata text annotation font
photo_annotation_font=Hack-Nerd-Font-Regular
# metadata text annotation placement
photo_annotation_placement=SouthWest
# screen resolutions for each monitor
declare -a screen_resolutions=()
screen_resolutions+=(3840x2160)
screen_resolutions+=(3840x2160)
screen_resolutions+=(1920x1080)
# observe or create temporary folder
if [ -d ${temp_dir} ]; then
echo "temp folder: ${temp_dir}, observed"
elif mkdir -p ${temp_dir}; then
echo "temp folder: ${temp_dir}, created"
else
echo "failed to create temp folder: ${temp_dir}"
exit 1
fi
# observe or create state folder
if [ -d ${state_dir} ]; then
echo "state folder: ${state_dir}, observed"
elif mkdir -p ${state_dir}; then
echo "state folder: ${state_dir}, created"
else
echo "failed to create state folder: ${state_dir}"
exit 1
fi
# observe or create backgrounds folder
if [ -d ${backgrounds_dir} ]; then
echo "backgrounds folder: ${backgrounds_dir}, observed"
elif mkdir -p ${backgrounds_dir}; then
echo "backgrounds folder: ${backgrounds_dir}, created"
else
echo "failed to create backgrounds folder: ${backgrounds_dir}"
exit 1
fi
# observe or download unsplash archive
if [ -f ${lite_archive_path} ]; then
echo "archive: ${lite_archive_path}, observed"
else
if curl \
--silent \
--location \
--output ${lite_archive_path} \
--url ${lite_archive_url}; then
echo "archive: ${lite_archive_url} downloaded to ${lite_archive_path}"
else
echo "failed to download archive: ${lite_archive_url} to ${lite_archive_path}"
exit 2
fi
fi
# observe or extract unsplash db
if [ -f ${photos_tsv_path} ]; then
echo "database: ${photos_tsv_path}, observed"
else
if unzip ${lite_archive_path} ${photos_tsv_archive_path} -d ${photos_tsv_dir_path} && mv ${photos_tsv_dir_path}/${photos_tsv_archive_path} ${photos_tsv_path}; then
echo "${lite_archive_path}/${photos_tsv_archive_path} extracted to ${photos_tsv_path}"
else
echo "failed to extract database: ${lite_archive_path}/${photos_tsv_archive_path} to ${photos_tsv_path}"
exit 3
fi
fi
photos_tsv_lines=$(wc -l ${photos_tsv_path} | cut -d ' ' -f 1)
# create a random wallpaper consisting of as many images as we have screens
declare -a photos
wallpaper_slug=combined
screen_index=0
for screen_resolution in ${screen_resolutions[@]}; do
screen_number=$((screen_index + 1))
screen_width=$(echo ${screen_resolution} | cut -d 'x' -f 1)
screen_height=$(echo ${screen_resolution} | cut -d 'x' -f 2)
screen_aspect_ratio=$(awk "BEGIN { rounded = sprintf(\"%.2f\", ${screen_width}/${screen_height}); print rounded }")
echo "- screen ${screen_number}: ${screen_width} × ${screen_height} (${screen_aspect_ratio})"
qualifier_found=false
while [ "${qualifier_found}" = "false" ]; do
random_line_number=$(shuf -i 2-${photos_tsv_lines} -n 1)
random_line=$(sed "${random_line_number}q;d" ${photos_tsv_path})
# read all tsv values, including empty values
readarray -d $'\t' -t candidate <<< ${random_line}
photo_slug=${candidate[0]}
photo_source=${candidate[1]}
photo_url=${candidate[2]}
photo_submitted=${candidate[3]}
case ${candidate[4]} in
f)
photo_featured=true
;;
*)
photo_featured=false
;;
esac
photo_width=${candidate[5]}
photo_height=${candidate[6]}
photo_aspect_ratio=$(printf "%.2f" ${candidate[7]})
photo_description=${candidate[8]}
photo_camera_make=${candidate[12]}
photo_camera_model=${candidate[13]}
photo_iso=${candidate[14]}
photo_aperture=${candidate[15]}
photo_focal_length=${candidate[16]}
photo_exposure_time=${candidate[17]}
photo_location_name=${candidate[18]}
photo_location_latitude=${candidate[19]}
photo_location_longitude=${candidate[20]}
photo_location_country=${candidate[21]}
photo_location_city=${candidate[22]}
photo_stats_views=${candidate[23]}
photo_stats_downloads=${candidate[24]}
photo_ai_description=${candidate[25]}
photo_ai_landmark_name=${candidate[26]}
photo_ai_landmark_latitude=${candidate[27]}
photo_ai_landmark_longitude=${candidate[28]}
photo_ai_landmark_confidence=${candidate[29]}
photo_blur_hash=${candidate[30]}
# qualify if photo and screen have the same aspect ratio
if [ ${screen_aspect_ratio} = ${photo_aspect_ratio} ]; then
# qualify if photo is landscape oriented and screen is landscape oriented
if ((screen_width > screen_height)) && ((photo_width > photo_height)); then
qualifier_found=true
# qualify if photo is portrait oriented and screen is portrait oriented
elif ((screen_width < screen_height)) && ((photo_width < photo_height)); then
qualifier_found=true
fi
if [ ${qualifier_found} = true ]; then
# disqualify if original photo width is less than half the width of the screen
min_width=$((screen_width / 2))
if ((photo_width < min_width)); then
qualifier_found=false
fi
# disqualify if original photo is not yet popular
if ((photo_stats_downloads < minimum_popularity)); then
qualifier_found=false
fi
# disqualify if original photo is already included on a previous screen
photo_resize_path=${temp_dir}/${photo_slug}-resize-${screen_resolution}.jpg
photo_screen_path=${temp_dir}/${photo_slug}-${screen_resolution}.jpg
if [[ " ${photos[*]} " =~ [[:space:]]${photo_screen_path}[[:space:]] ]]; then
qualifier_found=false
fi
# disqualify if original photo contains a person
if [[ ${photo_ai_description} =~ person ]] || [[ ${photo_ai_description} =~ man ]]; then
qualifier_found=false
fi
fi
fi
if [ ${qualifier_found} = true ]; then
# extract image metadata for annotation
metadata_path=${state_dir}/${photo_slug}.yml
echo "---" | tee ${metadata_path}
echo "slug: ${photo_slug}" | tee -a ${metadata_path}
echo "source: ${photo_source}" | tee -a ${metadata_path}
echo "url: ${photo_url}" | tee -a ${metadata_path}
echo "submitted: ${photo_submitted}" | tee -a ${metadata_path}
echo "featured: ${photo_featured}" | tee -a ${metadata_path}
echo "width: ${photo_width}" | tee -a ${metadata_path}
echo "height: ${photo_height}" | tee -a ${metadata_path}
echo "aspect-ratio: ${photo_aspect_ratio}" | tee -a ${metadata_path}
echo "description: ${photo_description}" | tee -a ${metadata_path}
echo "exif:" | tee -a ${metadata_path}
echo " camera:" | tee -a ${metadata_path}
echo " make: ${photo_camera_make}" | tee -a ${metadata_path}
echo " model: ${photo_camera_model}" | tee -a ${metadata_path}
echo " iso: ${photo_iso}" | tee -a ${metadata_path}
echo " aperture: ${photo_aperture}" | tee -a ${metadata_path}
echo " focal-length: ${photo_focal_length}" | tee -a ${metadata_path}
echo " exposure: ${photo_exposure_time}" | tee -a ${metadata_path}
echo "location:" | tee -a ${metadata_path}
echo " name: ${photo_location_name}" | tee -a ${metadata_path}
echo " latitude: ${photo_location_latitude}" | tee -a ${metadata_path}
echo " longitude: ${photo_location_longitude}" | tee -a ${metadata_path}
echo " country: ${photo_location_country}" | tee -a ${metadata_path}
echo " city: ${photo_location_city}" | tee -a ${metadata_path}
echo "stats:" | tee -a ${metadata_path}
echo " downloads: ${photo_stats_downloads}" | tee -a ${metadata_path}
echo " views: ${photo_stats_views}" | tee -a ${metadata_path}
echo "ai:"
echo " description: ${photo_ai_description}" | tee -a ${metadata_path}
echo " landmark:" | tee -a ${metadata_path}
echo " name: ${photo_ai_landmark_name}" | tee -a ${metadata_path}
echo " latitude: ${photo_ai_landmark_latitude}" | tee -a ${metadata_path}
echo " longitude: ${photo_ai_landmark_longitude}" | tee -a ${metadata_path}
echo " confidence: ${photo_ai_landmark_confidence}" | tee -a ${metadata_path}
echo "hash: ${photo_blur_hash}" | tee -a ${metadata_path}
if [ -n "${photo_location_name}" ]; then
photo_annotation=${photo_location_name}
elif [ -n "${photo_location_country}" ]; then
if [ -n "${photo_location_city}" ]; then
photo_annotation="${photo_location_city}, ${photo_location_country}"
else
photo_annotation=${photo_location_country}
fi
elif [ -n "${photo_ai_landmark_name}" ]; then
photo_annotation=${photo_ai_landmark_name}
#elif [ -n "${photo_description}" ]; then
# photo_annotation=${photo_description}
else
unset photo_annotation
fi
fi
done
wallpaper_slug="${wallpaper_slug}_${photo_slug}"
photo_path=${backgrounds_dir}/${photo_slug}.jpg
photos+=(${photo_screen_path})
# observe or download photo
if [ -s ${photo_path} ]; then
echo "${photo_path} found"
elif curl \
--silent \
--location \
--output ${photo_path} \
--url ${photo_url}; then
echo "${photo_url} downloaded to file://${photo_path}"
else
echo "failed to download ${photo_url} to ${photo_path}"
exit 4
fi
if [ -n "${photo_annotation}" ]; then
# annotate and resize photo
if magick ${photo_path} -resize ${screen_resolution}\! ${photo_resize_path}; then
echo "resized file://${photo_path} to file://${photo_resize_path}"
if magick ${photo_resize_path} \
-gravity ${photo_annotation_placement} \
-fill ${photo_annotation_color} \
-font ${photo_annotation_font} \
-pointsize $((screen_height / 72)) \
-annotate +10+10 "${photo_annotation}" \
${photo_screen_path}; then
echo "annotated file://${photo_resize_path} to file://${photo_screen_path}"
else
echo "failed to annotate file://${photo_resize_path} to file://${photo_screen_path}"
exit 5
fi
else
echo "failed to resize file://${photo_path} to file://${photo_resize_path}"
exit 6
fi
else
# resize photo when we haven't computed an annotation
if magick ${photo_path} -resize ${screen_resolution}\! ${photo_screen_path}; then
echo "resized file://${photo_path} to file://${photo_screen_path}"
else
echo "failed to resize file://${photo_path} to file://${photo_screen_path}"
exit 6
fi
fi
screen_index=$((screen_index + 1))
done
wallpaper_path=${state_dir}/${wallpaper_slug}.jpg
if rm -f ${temp_dir}/combined_*.jpg && magick ${photos[@]} +append ${wallpaper_path}; then
echo "created file://${wallpaper_path}"
rm ${photos[@]}
rm -f ${temp_dir}/*-resize-*.jpg
else
echo "failed to create file://${wallpaper_path}"
exit 7
fi
gsettings set org.gnome.desktop.background picture-uri ${wallpaper_path}
gsettings set org.gnome.desktop.background picture-uri-dark ${wallpaper_path}
gsettings set org.gnome.desktop.background picture-options 'spanned'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment