3 # Copyright © 2019-2021 Nick Bowler
5 # Generate thumbnails for cover art retrieved from MPD.
7 # License GPLv3+: GNU General Public License version 3 or any later version.
8 # This is free software: you are free to change and redistribute it.
9 # There is NO WARRANTY, to the extent permitted by law.
11 : "${XDG_CACHE_HOME=$HOME/.cache}"
12 : "${THUMBNAILDIR=$XDG_CACHE_HOME/mpdthumb}"
14 # Try to find mpdexec...
18 */*) self=$PWD/${argv0#./} ;;
19 *) self=`command -v $argv0` ;;
23 if command -v "$owndir/mpdexec.pl" >/dev/null; then
24 : "${MPDEXEC=$owndir/mpdexec.pl}"
25 elif command -v mpdexec.pl >/dev/null; then
26 : "${MPDEXEC=mpdexec.pl}"
28 : "${MPDEXEC=mpdexec}"
31 if command -v gm >/dev/null; then
32 : "${CONVERT=gm convert}"
34 : "${CONVERT=convert}"
40 Copyright © 2021 Nick Bowler
41 License GPLv3+: GNU General Public License version 3 or any later version.
42 This is free software: you are free to change and redistribute it.
43 There is NO WARRANTY, to the extent permitted by law.
48 test $# -eq 0 || { printf '%s: ' "$argv0"; printf "$@"; }
49 printf 'Usage: %s [options] file [file ...]\n' "$argv0"
50 test $# -eq 0 || printf 'Try %s --help for more information.\n' "$argv0"
56 This is "mpdthumb": a tool to retrieve cover art thumbnails from MPD.
58 One or more filenames may be specified. The cover art (if any) for each
59 file is retrieved and scaled to the requested size. The thumbnails are
60 cached locally so that subsequent lookups return results very quickly.
62 The filenames of the generated thumbnails are printed one per line in
63 the same order of the associated files specified on the command line.
64 If a given file has no cover art, then a blank line is printed.
67 --size=[W][xH] Specify the dimensions (in pixels) of a bounding
68 rectangle into which the image will be scaled to fit.
69 One (but not both) of the dimensions may be omitted
70 to indicate no limit in that direction. The default
72 --small Equivalent to --size=56
74 If ARG is omitted or 'yes', force the use of the
75 binarylimit command to reduce the amount of data
76 transferred in the hot cache case. If ARG is 'no',
77 then the old "large offset" method is used which is
78 incompatible with newer MPD servers. The default is
79 'auto', which queries the server to determine the
81 --no-binarylimit Equivalent to --binarylimit=no
82 --version Print a version message and then exit.
83 --help Print this message and then exit.
85 Report bugs to <nbowler@draconx.ca>.
95 if test ${lastarg:+y}; then
100 case $dashdash$arg in
101 --binarylimit=*) opt_binarylimit=${arg#--*=} ;;
102 --no-binarylimit) opt_binarylimit=no ;;
103 --binarylimit) opt_binarylimit=yes ;;
104 --size=*) size=${arg#--size=} ;;
106 --size) lastarg=$arg ;;
107 --version) print_version; exit ;;
108 --help) print_help; exit ;;
110 -*) print_usage 'unrecognized argument: %s\n' "$arg" 1>&2; exit 1 ;;
111 *) set x "$@" "$arg"; shift
117 w=${size%x*} h=${size#*x}
118 if expr "$w$h" : '[0-9][0-9]*$' >/dev/null; then :; else
119 print_usage 'invalid --size setting: %s\n' "$size" 1>&2
124 print_usage 'at least one filename is required\n' 1>&2
129 exec 3>"$tmp" 4<"$tmp"
133 exec 5>"$tmp" 6<"$tmp"
136 # Test for binarylimit command in MPD server. We want to minimize data
137 # transfer in order to make cache hits as fast as possible.
139 # On older servers we can set a ridiculously large offset which causes the
140 # server to send no data, but this is rejected by new servers. On new servers
141 # we can limit the data transferred explicitly, but not less than 64 bytes
142 # for some reason (probably not a big problem).
145 case $opt_binarylimit in
146 auto) $MPDEXEC binarylimit 64 2>/dev/null || use_binarylimit=false ;;
147 no) use_binarylimit=false ;;
150 print_usage 'invalid --binarylimit argument; must be yes, no or auto' 1>&2
154 if $use_binarylimit; then
155 printf 'binarylimit 64\n' >&3
158 binarylimit_offset=2147483647
163 shift; set x "$@" "$arg"; shift
165 printf '%s\n' "$arg" | sed '/[ \\"'\'']/ {
169 s/.*/albumart & '"$binarylimit_offset"'/' >&3;
172 <&4 $MPDEXEC --ignore-errors >&5 2>&1 || exit
173 while read a b <&6; do
178 *binarylimit*) printf '%s: %s\n' "$argv0" "$a $b" 1>&2; exit 1 ;;
181 echo; shift || exit; continue ;;
185 # We combine the filename and the size to compute the cache key and
186 # hope this suffices to detect stale entries. Unfortunately MPD does
187 # not currently give us the modified date which would be more useful...
188 file=$1; shift || exit
189 cache_id=`printf 'MPD:%s:%s' "$file" "$b" | md5sum`
190 cache_id=${cache_id:+${cache_id%% *}_$size.png}
192 if test ! -f "$THUMBNAILDIR/$cache_id"; then
193 if test ! -f "$THUMBNAILDIR/CACHEDIR.TAG"; then
194 mkdir -p "$THUMBNAILDIR"
195 { cat >"$THUMBNAILDIR/CACHEDIR.TAG~" || exit; } <<'EOF'
196 Signature: 8a477f597d28d172789f06886806bc55
198 mv -f "$THUMBNAILDIR/CACHEDIR.TAG~" "$THUMBNAILDIR/CACHEDIR.TAG"
201 # Not cached, retrieve the entire image
202 $MPDEXEC --binary --download albumart "$file" >&3 || exit
203 <&4 $CONVERT -scale "$size" - "$THUMBNAILDIR/tmp.$cache_id" ||
204 { rc=$? rm -f "$THUMBNAILDIR/tmp.$cache_id"; exit $rc; }
205 mv -f "$THUMBNAILDIR/tmp.$cache_id" "$THUMBNAILDIR/$cache_id"
208 printf '%s\n' "$THUMBNAILDIR/$cache_id"