OSX

自動で(大量の)壁紙を切り替えたい on Mac

More than 1 year has passed since last update.

はじめに

なんやかんやあって大量の壁紙画像があり、気分にあわせて切り替えられる量をはるかに越え、"デスクトップとスクリーンセーバー"から画像フォルダを選択すると落ちる、Finderもサムネイル生成で固まる、そういった自分向けの記事です。本記事執筆時点で31000枚の画像が壁紙フォルダにある。

落とさないための戦略

Finder関係で死ぬのは要はファイルがいっぱいあるからです。分けましょう。
ハッシュ値と、その先頭数桁を使ったサブフォルダに割り振る、みたいなのがいいと思います。
Zshだと以下みたいにやるとできます。

BGROOT=/path/to/new/wallpapers/
function mmd5 (){
    pic="$1"
    #ext=$(identify -format "%m" "$pic""[0]" | tr "[A-Z]" "[a-z]")
    ext=${pic##*.}
    md5sum=$(md5 -q "$pic")t
    target=$BGROOT/$(echo $md5sum | head -c 2| sed -e 's/./&\//')/$md5sum"."$ext
    mv "$pic" $target
}
zargs -l -- /path/to/wallpapers/**/*(.) -- mmd5

自動で壁紙を切り替える

サブフォルダに分けてしまうと、"デスクトップとスクリーンセーバー"から最早制御できないので、直接コマンドを叩いて切り替える。AppleScriptでFinderのdesktop pictureにパスを与えればいい。切り替え時のフェードイン・フェードアウトができなくなるが、致し方ない。毎回素直に全ファイルランダムにソートするとそれだけで重いので、手を抜いている。昔はMac起動時に全ファイルをランダムに並べたものをOSX標準のmemcachedに載せておいて取得するようなものを書いていたが、いつからかmemcachedがプリインストールされなくなったのでやめた。一時ファイルをつくりたくないという縛りをしていたので、一時ファイルやsqliteのデータベースなどあればもっと楽にできるであろう。

BGROOT=$HOME/Pictures/wallpaper

BGDGT=$BGROOT/$(printf "%04x" $(jot -r 1 0 255) | perl -pe 's/.*(.)(.)$/$1\/$2/')
NOFBG=$(ls $BGDGT | wc -l)

find $BGDGT | tail -n $(jot -r 1 1 $NOFBG) | head -n 1 \
  | xargs -I{} osascript -e "tell application \"Finder\" to set desktop picture to (POSIX file \"{}\" as alias)"

上記スクリプトを適当に纏めて、LaunchAgentに食わせればOK。以下のようなplistで30秒ごとに切り替わる。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.example.desktop.changerr</string>
    <key>OnDemand</key>
    <true/>
    <key>Program</key>
    <string>/path/to/script</string>
    <key>RunAtLoad</key>
    <true/>
    <key>StartInterval</key>
    <integer>30</integer>
</dict>
</plist>

壁紙の参照

以下のスクリプトでPreview.appに出る。手違いで入ってしまった画像に気付いたらこれで呼び出せばフルパスがわかるので削除できる。PreviewがLionぐらいからファイルを1枚しか開いてないときに削除ができなくなったのは改悪だと思う。

tell application "Finder"
    set randomImage to desktop picture as alias
end tell

tell application "Preview"
    launch
    open randomImage
end tell

メモリリークについて

長時間前述の自動切り替えplistを回していると、Dock.appの使用メモリが徐々に増えていく(ことがある)。どうも無圧縮TIFFのあつかいが正しくないらしく、気がつくとメモリ圧縮前で8GBとかまで食っていることがある。JPGにすると解決する、との報告もあるが、JPGやPNGばっかりでも起きるように思う。この場合、Dockをkillして対応する。数回に一回killall Dockをするようスクリプトに書いてもよさそう。