Last active
November 11, 2024 21:38
-
-
Save grenade/b79e520c7f4de6decf613648f16f4589 to your computer and use it in GitHub Desktop.
create gnome wallpaper with different random images for each screen
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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