bing-wallpaper.sh 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  1. #!/bin/sh
  2. PATH=/usr/local/bin:/usr/local/sbin:~/bin:/usr/bin:/bin:/usr/sbin:/sbin
  3. readonly SCRIPT=$(basename "$0")
  4. readonly VERSION='1.1.1'
  5. RESOLUTIONS=(1920x1200 1920x1080 1024x768 1280x720 1366x768 UHD)
  6. MONITOR="0" # 0 means all monitors
  7. PLIST_FILE="$HOME/Library/LaunchAgents/com.bing-wallpaper-daily-mac-multimonitor"
  8. AUTO_UPDATE_NAME="default"
  9. ARGS=$@
  10. usage() {
  11. cat <<EOF
  12. Usage:
  13. $SCRIPT [options]
  14. $SCRIPT -h | --help
  15. $SCRIPT --version
  16. Options:
  17. enable-auto-update Enable automatic update of wallpapers every day
  18. the picture if the filename already exists.
  19. disable-auto-update Disable automatic update of wallpapers every day
  20. the picture if the filename already exists.
  21. info Show description of current wallpaper.
  22. --auto-update-name <name> Name of your auto update when enabling/disabling
  23. Using custom name enables setting multiple automatic update configurations.
  24. Eg. Set on monitor 1 todays wallpaper and on monitor 2 wallpaper from yesterday
  25. -f --force Force download of picture. This will overwrite
  26. the picture if the filename already exists.
  27. -s --ssl Communicate with bing.com over SSL.
  28. -q --quiet Do not display log messages.
  29. -c --country <coutry-tag> Specify market country/region eg. en-US, cs-CZ
  30. Pictures may be different for markets on some days.
  31. See full list of countries on https://learn.microsoft.com/en-us/previous-versions/bing/search/dd251064(v=msdn.10)
  32. -d --day <number> Day for which you want to get the picture.
  33. 0 is current day, 1 is yesterday etc.
  34. Default is 0.
  35. -n --filename <file name> The name of the downloaded picture. Defaults to
  36. the upstream name.
  37. -p --picturedir <picture dir> The full path to the picture download dir.
  38. Will be created if it does not exist.
  39. [default: $HOME/Pictures/bing-wallpapers/]
  40. -r --resolution <resolution> The resolution of the image to retrieve.
  41. Supported resolutions: ${RESOLUTIONS[*]}
  42. --resolutions <resolutions> The resolutions of the image try to retrieve.
  43. eg.: --resolutions "1920x1200 1920x1080 UHD"
  44. -m --monitor <num> Set wallpaper only on certain monitor (1,2,3...)
  45. --all-desktops-experimental Set wallpaper on all desktops
  46. Fixing osascript bug when wallpaper is not set for Desktop 2.
  47. Known issue: Minimized apps are removed from Dock.
  48. If something goes wrong delete Library/Application Support/Dock/desktoppicture.db
  49. and restart your Mac.
  50. -h --help Show this screen.
  51. --version Show version.
  52. EOF
  53. }
  54. create_plist_in_users_agents_folder() {
  55. local SCRIPT_PATH=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )/$(basename "${BASH_SOURCE:-$0}")
  56. local REST_ARGS=$(echo "$ARGS" | sed -e "s/enable-auto-update//")
  57. if [ $RUN_USING_NPX ]; then
  58. local COMMANDS="<string>source ~/.bashrc && npx --yes bing-wallpaper-daily-mac-multimonitor@latest $REST_ARGS</string>"
  59. else
  60. local COMMANDS="<string>$SCRIPT_PATH $REST_ARGS</string>"
  61. fi
  62. cat > $PLIST_FILE <<- EOM
  63. <?xml version="1.0" encoding="UTF-8"?>
  64. <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
  65. <plist version="1.0">
  66. <dict>
  67. <key>Label</key>
  68. <string>com.bing-wallpaper-daily-mac-multimonitor.plist</string>
  69. <key>OnDemand</key>
  70. <true/>
  71. <key>ProgramArguments</key>
  72. <array>
  73. <string>/bin/sh</string>
  74. <string>-c</string>
  75. $COMMANDS
  76. </array>
  77. <key>EnvironmentVariables</key>
  78. <dict>
  79. <key>PATH</key>
  80. <string>/usr/local/bin:/usr/local/sbin:/usr/bin:/bin:/usr/sbin:/sbin</string>
  81. </dict>
  82. <key>StandardErrorPath</key>
  83. <string>/tmp/bing-wallpaper-daily-mac-multimonitor-plist.err</string>
  84. <key>StandardOutPath</key>
  85. <string>/tmp/bing-wallpaper-daily-mac-multimonitor-plist.out</string>
  86. <key>StartInterval</key>
  87. <integer>1800</integer>
  88. <key>RunAtLoad</key>
  89. <true/>
  90. </dict>
  91. </plist>
  92. EOM
  93. launchctl unload -w $PLIST_FILE 2>/dev/null
  94. launchctl load -w $PLIST_FILE
  95. }
  96. remove_plist_in_users_agents_folder() {
  97. launchctl unload -w "$PLIST_FILE" 2>/dev/null
  98. rm "$PLIST_FILE"
  99. }
  100. show_info_text() {
  101. # Parse HPImageArchive API and acquire picture BASE URL
  102. COPYRIGHT=$(cat "$PICTURE_DIR/info.xml" | \
  103. grep -Eo "<copyright>.*<\/copyright>")
  104. COPYRIGHT=$(echo "$COPYRIGHT" | sed -e "s/<copyright>//")
  105. COPYRIGHT=$(echo "$COPYRIGHT" | sed -e "s/<\/copyright>//")
  106. echo $COPYRIGHT
  107. }
  108. print_message() {
  109. if [ ! "$QUIET" ]; then
  110. printf "%s\n" "$(date): ${1}"
  111. fi
  112. }
  113. download_image_curl () {
  114. local RES=$1
  115. FILEURLWITHRES="${FILEURL}_${RES}.jpg"
  116. echo $FILEURLWITHRES
  117. FILENAME=${FILEURLWITHRES/\/th\?id=/}
  118. FILENAME_LOCAL="${AUTO_UPDATE_NAME}-${FILENAME}"
  119. FILEWHOLEURL="$PROTO://bing.com/$FILEURLWITHRES"
  120. if [ $FORCE ] || [ ! -f "$PICTURE_DIR/$FILENAME_LOCAL" ]; then
  121. find $PICTURE_DIR -type f -iname $AUTO_UPDATE_NAME-\*.jpg -delete
  122. print_message "Downloading: $FILENAME..."
  123. curl --fail -Lo "$PICTURE_DIR/$FILENAME_LOCAL" "$FILEWHOLEURL"
  124. curl --fail -Lo "$PICTURE_DIR/info.xml" "$BING_HP_IMAGE_ARCHIVE_URL"
  125. if [ "$?" == "0" ]; then
  126. FILEPATH="$PICTURE_DIR/$FILENAME_LOCAL"
  127. return
  128. fi
  129. FILEPATH=""
  130. return
  131. else
  132. print_message "Skipping download: $FILENAME_LOCAL..."
  133. FILEPATH="$PICTURE_DIR/$FILENAME_LOCAL"
  134. DOWNLOAD_SKIPPED=true
  135. return
  136. fi
  137. }
  138. set_wallpaper () {
  139. local FILEPATH=$1
  140. local MONITOR=$2
  141. if [ "$MONITOR" -ge 1 ] 2>/dev/null; then
  142. print_message "Setting wallpaper for monitor: $MONITOR"
  143. osascript - << EOF
  144. set tlst to {}
  145. tell application "System Events"
  146. set tlst to a reference to every desktop
  147. set picture of item $MONITOR of tlst to "$FILEPATH"
  148. end tell
  149. EOF
  150. else
  151. osascript -e 'tell application "System Events" to tell every desktop to set picture to "'$FILEPATH'"'
  152. fi
  153. }
  154. set_wallpaper_experimental () {
  155. local db_file="Library/Application Support/Dock/desktoppicture.db"
  156. local db_path="$HOME/$db_file"
  157. # Put the image path in the database
  158. local sql="insert into data values(\"$FILEPATH\"); "
  159. sqlite3 "$db_path" "$sql"
  160. # Get the index of the new entry
  161. local sql="select max(rowid) from data;"
  162. local new_entry=$(sqlite3 "$db_path" "$sql")
  163. local new_entry=$(echo $new_entry|tr -d '\n')
  164. # Get all picture ids (monitor/space pairs)
  165. local sql="select rowid from pictures;"
  166. local pictures_string=$(sqlite3 "$db_path" "$sql")
  167. local IFS=$'\n'
  168. local pictures=($pictures_string)
  169. # Clear all existing preferences
  170. local sql="select max(rowid) from data; delete from preferences; "
  171. for pic in "${pictures[@]}"
  172. do
  173. if [ "$pic" ]; then
  174. local sql+="insert into preferences (key, data_id, picture_id) "
  175. local sql+="values(1, $new_entry, $pic); "
  176. fi
  177. done
  178. sqlite3 "$db_path" "$sql"
  179. killall "Dock"
  180. }
  181. # Defaults
  182. PICTURE_DIR="$HOME/Pictures/bing-wallpapers/"
  183. # Option parsing
  184. while [[ $# -gt 0 ]]; do
  185. key="$1"
  186. case $key in
  187. enable-auto-update)
  188. ENABLE_AUTOMATIC_UPDATE=true
  189. ;;
  190. disable-auto-update)
  191. DISABLE_AUTOMATIC_UPDATE=true
  192. ;;
  193. info)
  194. INFO=true
  195. ;;
  196. --auto-update-name)
  197. AUTO_UPDATE_NAME="$2"
  198. shift
  199. ;;
  200. -r|--resolution)
  201. RESOLUTION="$2"
  202. shift
  203. ;;
  204. -p|--picturedir)
  205. PICTURE_DIR="$2"
  206. shift
  207. ;;
  208. -c|--country)
  209. COUNTRY="$2"
  210. shift
  211. ;;
  212. -d|--day)
  213. DAY="$2"
  214. shift
  215. ;;
  216. -n|--filename)
  217. FILENAME="$2"
  218. shift
  219. ;;
  220. -m|--monitor)
  221. MONITOR="$2"
  222. shift
  223. ;;
  224. -f|--force)
  225. FORCE=true
  226. ;;
  227. -s|--ssl)
  228. SSL=true
  229. ;;
  230. -q|--quiet)
  231. QUIET=true
  232. ;;
  233. -h|--help)
  234. usage
  235. exit 0
  236. ;;
  237. --resolutions)
  238. RESOLUTIONS="$2"
  239. shift
  240. ;;
  241. --all-desktops-experimental)
  242. EXPERIMENTAL=true
  243. ;;
  244. --version)
  245. printf "%s\n" $VERSION
  246. exit 0
  247. ;;
  248. *)
  249. (>&2 printf "Unknown parameter: %s\n" "$1")
  250. usage
  251. exit 1
  252. ;;
  253. esac
  254. shift
  255. done
  256. # Set options
  257. [ $QUIET ] && CURL_QUIET='-s'
  258. [ $SSL ] && PROTO='https' || PROTO='http'
  259. [ $DAY ] && IDX=$DAY || IDX='0'
  260. BING_HP_IMAGE_ARCHIVE_URL="https://www.bing.com/HPImageArchive.aspx?format=xml&idx=${IDX}&n=1"
  261. [ $COUNTRY ] && BING_HP_IMAGE_ARCHIVE_URL="${BING_HP_IMAGE_ARCHIVE_URL}&mkt=${COUNTRY}"
  262. PLIST_FILE="${PLIST_FILE}-${AUTO_UPDATE_NAME}.plist"
  263. PARENT_COMMAND=$(ps -o comm= $PPID)
  264. if [[ "$PARENT_COMMAND" == *"npm exec"* ]]; then
  265. RUN_USING_NPX=true
  266. fi
  267. if [ "$ENABLE_AUTOMATIC_UPDATE" ]; then
  268. # enable update
  269. create_plist_in_users_agents_folder
  270. echo "Automatic wallpaper update enabled"
  271. exit 1
  272. fi
  273. if [ "$DISABLE_AUTOMATIC_UPDATE" ]; then
  274. # disable update
  275. remove_plist_in_users_agents_folder
  276. echo "Automatic wallpaper update disabled"
  277. exit 1
  278. fi
  279. if [ "$INFO" ]; then
  280. show_info_text
  281. exit 1
  282. fi
  283. # Create picture directory if it doesn't already exist
  284. mkdir -p "${PICTURE_DIR}"
  285. # Parse HPImageArchive API and acquire picture BASE URL
  286. FILEURL=( $(curl -sL $BING_HP_IMAGE_ARCHIVE_URL | \
  287. grep -Eo "<urlBase>.*?</urlBase>") )
  288. FILEURL=$(echo "$FILEURL" | sed -e "s/<urlBase>//")
  289. FILEURL=$(echo "$FILEURL" | sed -e "s/<\/urlBase>//")
  290. if [ $RESOLUTION ]; then
  291. download_image_curl $RESOLUTION
  292. if [ "$FILEPATH" ]; then
  293. if [ "$EXPERIMENTAL" ]; then
  294. if [ ! "$DOWNLOAD_SKIPPED" ]; then
  295. set_wallpaper_experimental $FILEPATH
  296. fi
  297. else
  298. set_wallpaper $FILEPATH $MONITOR
  299. fi
  300. fi
  301. exit 1
  302. fi
  303. for RESOLUTION in "${RESOLUTIONS[@]}"
  304. do
  305. download_image_curl $RESOLUTION
  306. if [ "$FILEPATH" ]; then
  307. if [ "$EXPERIMENTAL" ]; then
  308. if [ ! "$DOWNLOAD_SKIPPED" ]; then
  309. set_wallpaper_experimental $
  310. fi
  311. else
  312. set_wallpaper $FILEPATH $MONITOR
  313. fi
  314. exit 1
  315. fi
  316. done