-
Notifications
You must be signed in to change notification settings - Fork 0
/
track_change.sh
executable file
·251 lines (213 loc) · 7.34 KB
/
track_change.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
#!/usr/bin/env bash
# $HOME/.ncmpcpp/track_change.sh
# © Andy Smith <andy@strugglers.net>
# SPDX-License-Identifier: 0BSD
set -eu
### Some things user might want to change.
# File name that each album's art is stored under.
candidate_name="cover.jpg"
# Directory to place the current track's cover in. Can be some temporary or
# cache directory - we'll create it if it doesn't exist already.
# This will typically be something like /run/user/1000/ncmpcpp
cover_dir="$XDG_RUNTIME_DIR/ncmpcpp"
# Where all your ncmpcpp files live, though we only use this as part of the
# "default" cover image path.
ncmpcpp_home="$HOME/.ncmpcpp"
# Path to an image to use when we don't have a better album cover image.
default_cover="$ncmpcpp_home/default_cover.jpg"
# This path we'll copy the correct cover image to.
ncmpcpp_cover="$cover_dir/current_cover.jpg"
# How many milliseconds to keep the desktop notification visible for. A lot of
# notiifcation daemons don't support this, e.g. GNOME just ignores it, but
# Dunst and Wired do. On GNOME you can use this extension to change the timeout
# for all notification popups:
# https://extensions.gnome.org/extension/3795/notification-timeout/
notification_timeout=5000
### No user-serviceable parts below here.
error() {
logger -t "$(basename "$0")" "${*}"
printf "%s\\n" "${*}" 1>&2
}
warning() {
logger -t "$(basename "$0")" "${*}"
}
show_usage() {
printf "Usage: %s [-m music_dir]\n" "$(basename "$0")"
printf " -m Specify directory that MPD knows your music is in\n"
printf " Defaults to \$HOME/Music\n"
}
parse_args() {
while getopts 'hm:' opt; do
case "$opt" in
m)
music_base="$OPTARG"
;;
h|?)
show_usage
exit
esac
done
shift "$((OPTIND -1))"
if [[ -z "$music_base" ]]; then
music_base="$HOME/Music"
fi
}
use_album_cover() {
# Always copy the default cover, because otherwise the inotify in the
# poller won't trigger and album info for things with no cover will never
# be displayed.
# Otherwise only bother if the files are actually different (think -
# playing consecutive tracks of same album).
if [[ "$1" = "$default_cover" ]] || ! cmp --silent "$1" "$ncmpcpp_cover"; then
cp "$1" "$ncmpcpp_cover"
fi
}
do_desktop_notification() {
# ${meta[0]} = file
# ${meta[1]} = artist
# ${meta[2]} = title
# ${meta[3]} = albumartist
# ${meta[4]} = album
# ${meta[5]} = date
# ${meta[6]} = originaldate
local -n meta="$1"
local cover_art="$2"
local artist
local title
local albumartist
local year
local album
artist="${meta[1]}"
title="${meta[2]}"
# If we don't have an album artist then we'll just assume the track artist.
if [[ -n "${meta[3]}" ]]; then
albumartist="${meta[3]}"
else
albumartist="$artist"
fi
# If we know the original date, use that, else the date, else nothing.
if [[ "${meta[6]+x}" ]]; then
year="${meta[6]}"
elif [[ "${meta[5]+x}" ]]; then
year="${meta[5]}"
fi
if [[ -z "${meta[4]+x}" ]]; then
album="Unknown Album"
else
album="${meta[4]}"
fi
# Add the year on the end of the album if we have it.
if [[ -n "$year" ]]; then
album="$album • $year"
fi
# libnotify as supported by most desktops. e.g. in Debian it's from
# libnotify-bin package. Your typical GNOME desktop notification.
#
# Set normal priority as otherwise GNOME hides it away in the notification
# tray.
#
# Set transient so it doesn't stack them up inside the tray.
#
# Transient hint not necessary on GNOME but might be elsewhere.
#
# image-path hint uses the album cover as the notification icon. This seems
# quite unreliable on GNOME, like it caches an old one for ages, but it
# seems to do that by file path so we can bust the cache by sending the
# path to the original file (not the one that the poller script is
# watching).
notify-send \
--app-name MPD \
--urgency normal \
--expire-time="$notification_timeout" \
--transient \
--hint="int:transient:1" \
--hint="string:image-path:$cover_art" \
"$artist • $title" \
"$albumartist • $album"
}
music_base=
parse_args "$@"
if [[ ! -d "$music_base" ]]; then
error "$music_base doesn't seem to be a directory. Do you need to set -m?"
show_usage
use_album_cover "$default_cover"
exit 1
fi
# Get all the metadata from mpd.
# I sure hope none of it ever contains a tab…
while IFS= read -r mpc_line; do
# This grossness needed to handle the case where a field might be
# empty. If we did the usual IFS=$'\t' then it would skip over the
# empty field, so we have to use mapfile to explode it into an array
# instead.
mapfile -td $'\t' mpc < <(printf %s "$mpc_line")
done < <(mpc \
-f '%file%\t%artist%\t%title%\t%albumartist%\t%album%\t%date%\t%originaldate%' \
current)
track_file="${mpc[0]}"
full_track_file="$music_base/$track_file"
# Sanity checks - can't work out an album cover without knowing an existing
# directory path.
if [[ ! -r "$full_track_file" ]]; then
error "mpd said that file '$full_track_file' is currently playing but it" \
"doesn't seem to exist"
error "Is your music_dir set correctly with -m?"
show_usage
chosen_cover="$default_cover"
use_album_cover "$default_cover"
else
if [[ ! -d "$cover_dir" ]]; then
mkdir -p "$cover_dir"
fi
# The $track_file can be in these various formats:
#
# a) Artist/Album/Track.mp3
# b) Various Artists/Album/Track.mp3
# c) Artist/Track.mp3
#
# Case (c) is not handled yet as this is for album art and we don't know
# the album. In future we might try to extract cover art from the file, or
# look for it in a separate file once we know where to look on a per-file
# basis.
# This removes everything that's NOT a "/" out of the track file path.
only_slashes="${track_file//[^\/]}"
# So we can count them.
slash_count="${#only_slashes}"
case "$slash_count" in
"2")
containing_dir="$(dirname "$full_track_file")"
if [[ -r "$containing_dir/$candidate_name" ]]; then
chosen_cover="$containing_dir/$candidate_name"
use_album_cover "$chosen_cover"
else
chosen_cover="$default_cover"
use_album_cover "$chosen_cover"
fi
;;
"1")
warning "non-album track '$track_file' not handled yet"
chosen_cover="$default_cover"
use_album_cover "$chosen_cover"
;;
*)
warning "Don't know how to handle track path '$full_track_file'"
chosen_cover="$default_cover"
use_album_cover "$chosen_cover"
;;
esac
fi
# ${mpc[0]} = file
# ${mpc[1]} = artist
# ${mpc[2]} = title
# ${mpc[3]} = albumartist
# ${mpc[4]} = album
# ${mpc[5]} = date
# ${mpc[6]} = originaldate
#
# We need at least an artist and a title to do a desktop notification.
if [[ -z "${mpc[1]}" || -z "${mpc[2]}" ]]; then
warning "Current track '$full_track_file' didn't give us an artist and title,
so no desktop notification"
else
do_desktop_notification mpc "$chosen_cover"
fi