]> git.draconx.ca Git - scripts.git/blob - mpdscripts.sh
Import the scripts directory from my website.
[scripts.git] / mpdscripts.sh
1 #!/bin/zsh
2 #
3 # Collection of shell functions for manipulating MPD.  Functions prefixed with
4 # __ are intended for use in these scripts, whereas functions prefixed with
5 # "mpd" are intended to be called by users.
6 #
7 # This file may be sourced by your shell startup scripts in order to load all
8 # the functions into the environment, or it may be invoked as an executable.
9 # The first argument to the script designates the function, with or without
10 # the leading "mpd".  Subsequent arguments are passed to the called function.
11 #
12 # For example, you might run:
13 #   `source mpdscripts.sh' followed by `mpdlist mycoolsong'
14 # or equivalently:
15 #   `./mpdscripts.sh list mycoolsong'.
16 # The latter form is useful when not invoking the scripts from a shell.
17 #
18 # This version specifies the functions:
19 #   mpdnext, mpdbetter, mpdlist, mpdalbum, and mpdshow.
20 #
21 # See the comments above each function for more details.
22 #
23 # Requires mpc, GNU grep and GNU sed.
24 # Works with zsh or bash.
25 #
26 # Send questions or comments to nbowler@draconx.ca, or find me on IRC as
27 # Draconx on irc.freenode.net.
28 #
29 # Licensed under the terms of the Do What The Fuck You Want To Public License.
30 #
31 ##############################################################################
32
33 __mpd_playlist()
34 {
35         mpc --format='%position%) %artist% - %title%' playlist
36 }
37
38 # __mpd_trackid [regexp]
39 # With a regular expression as an argument, search the MPD playlist for the
40 # track matching the expression and print the index of that track.  Otherwise,
41 # print the index of the currently playing track.
42 __mpd_trackid()
43 {
44         local id
45
46         if [ -z "$1" ]; then
47                 id=`mpc | sed -n "2{s/.*#//;s/\/.*//p}"`
48         else
49                 id=`__mpd_playlist | sed -n \
50                         "/${1//\//\\\/}/I{s/^[^[:digit:]]//;s/).*//p;q}"`
51         fi
52
53         [ -z "$id" ] && return 1
54
55         echo "$id"
56         return 0
57 }
58
59 # __mpd_trackname [id]
60 # With a track ID as an argument, print the title of that track.  Otherwise,
61 # print the title of the currently playing track.
62 __mpd_trackname()
63 {
64         local title
65
66         if [ -z "$1" ]; then
67                 title=`mpc | sed -n "1h;2{g;p;q}"`
68         else
69                 title=`__mpd_playlist | sed -n \
70                         "/^[^[:digit:]]*$1)/s/^.[0-9]*) //p"`
71         fi
72
73         [ -z "$title" ] && return 1
74
75         echo "$title"
76         return 0
77 }
78
79 # mpdnext <regexp> [advance]
80 # Searches the playlist for a track matching <regexp>, then moves it to the
81 # position following the currently playing track.  If [advance] is a non-empty
82 # string, also run `mpc next' to play the moved track immediately.
83 mpdnext()
84 {
85         local index target name
86
87         if [ -z "$1" ]; then
88                 echo "usage: mpdnext <regexp>"
89                 return 1
90         fi
91
92         if ! index=`__mpd_trackid`; then
93                 echo "mpdnext: MPD is currently stopped."
94                 return 1
95         fi
96
97         if ! target=`__mpd_trackid "$1"`; then
98                 echo "mpdnext: target not found."
99                 return 1
100         fi
101
102         name=`__mpd_trackname $((target))`
103
104         if [ $target -lt $index ]; then
105                 mpc move $target $index
106         elif [ $target -gt $index ]; then
107                 mpc move $target $((index+1))
108         else
109                 echo "mpdnext: selected the playing track"
110                 return 1
111         fi
112
113         echo "mpdnext: $name moved."
114
115         # Play track if desired
116         [ -n "$2" ] && mpc next
117
118         return 0
119 }
120
121 # mpdbetter [sedscript]
122 # Processes the title of the currently playing song through the sed program
123 # specified by [sedscript] (or ~/.mpdbetter if not specified).  If the
124 # resulting title is different, attempt to find it in the playlist and use
125 # mpdnext to play it immediately.
126 mpdbetter()
127 {
128         local title better script="$HOME/.mpdbetter"
129         [ -n "$1" ] && script="$1"
130
131         if ! title=`__mpd_trackname`; then
132                 echo "mpdbetter: MPD is currently stopped."
133                 return 1
134         fi
135
136         if ! better=`echo "$title" | sed -f "$script"`; then
137                 echo "mpdbetter: error in script: \`$script'"
138                 return 1
139         fi
140
141         if [ "$better" = "$title" ]; then
142                 echo "mpdbetter: nothing to be done."
143                 return 1
144         fi
145
146         mpdnext ") $better\$" next
147 }
148
149 # mpdlist [regexp]
150 # Simply search the playlist for tracks matching [regexp], or print the entire
151 # playlist if [regexp] is not specified.
152 mpdlist()
153 {
154         if [ -z "$1" ]; then
155                 __mpd_playlist
156         else
157                 __mpd_playlist | grep --color=auto -i -- "$1"
158         fi
159 }
160
161 # mpdshow
162 # Display the currently playing track as well as the part of the playlist
163 # surrounding that track.
164 mpdshow()
165 {
166         local index
167
168         if ! index=`__mpd_trackid`; then
169                 echo "mpdshow: MPD is currently stopped."
170                 return 1
171         fi
172
173         mpc
174         echo
175         __mpd_playlist | grep -C 5 --color=auto "^$index)"
176 }
177
178 # mpdalbum <regexp>
179 # Search the playlist for tracks belonging to an album matching <regexp>.
180 # Print those tracks grouped by album and sorted by track number.
181 mpdalbum()
182 {
183         local current list
184
185         if [ -z "$1" ]; then
186                 echo "usage: mpdalbum <regexp>"
187                 return 1
188         fi
189
190         # This horrible hack grabs the relevant playlist entries and separates
191         # desired fields with @!@ and !@!.
192         list="`mpc --format $'\n%album%\n%track%\n%artist% - %title%' playlist \
193                 | sed -n "
194                         /^.[0-9]*) $/{
195                                 h;n
196                                 /${1//\//\\\/}/I{
197                                         x;G;s/$/@!@/
198                                         N;s/$/!@!/
199                                         N;s/\n//g
200                                         p
201                                 }
202                         }
203                 "
204         `"
205
206 #       list="`mpc --format '%album%@!@%track%!@!%artist% - %title%' playlist |\
207 #               sed -n 'h;s/@!@.*//;'"/${1//\//\\\/}/I{g;p}" | sort -k 2`"
208         
209         if [ -z "$list" ]; then
210                 echo "mpdalbum: no matches for \`$1'"
211                 return 1
212         fi
213         
214         echo "$list" | while read i; do
215                 local album="`echo $i | sed 's/.[0-9]*) \(.*\)@!@.*/\1/'`"
216                 local index="`echo $i | sed 's/[# >]\?\([0-9]*\)).*/\1/'`"
217                 local title="`echo $i | sed 's/.*!@!//'`"
218
219                 if [ "$album" != "$current" ]; then
220                         [ -n "$current" ] && echo
221                         echo "$album"
222                         current="$album"
223                 fi
224
225                 echo -e " $index)\t$title"
226         done
227 }
228
229 # For executable-like invocation...
230
231 # It would be nice if we could detect whether we are invoked as an executable
232 # and print a usage message if not given any arguments.  I have yet to find
233 # a clean way to do this, however.
234
235 if [ $# -gt 0 ]; then
236         func="mpd${1#mpd}"
237         shift
238         "$func" $@
239 fi