diff options
Diffstat (limited to 'X11/mpd/ncmpcpp/scripts/album-art')
-rwxr-xr-x | X11/mpd/ncmpcpp/scripts/album-art | 248 |
1 files changed, 248 insertions, 0 deletions
diff --git a/X11/mpd/ncmpcpp/scripts/album-art b/X11/mpd/ncmpcpp/scripts/album-art new file mode 100755 index 0000000..d852f34 --- /dev/null +++ b/X11/mpd/ncmpcpp/scripts/album-art | |||
@@ -0,0 +1,248 @@ | |||
1 | #!/usr/bin/env bash | ||
2 | |||
3 | ## Copyright (C) 2020-2021 Aditya Shakya <adi1090x@gmail.com> | ||
4 | ## Everyone is permitted to copy and distribute copies of this file under GNU-GPL3 | ||
5 | |||
6 | ## Cover art script for ncmpcpp | ||
7 | |||
8 | # SETTINGS | ||
9 | music_library="$HOME/Music" | ||
10 | padding_top=3 | ||
11 | padding_bottom=0 | ||
12 | padding_right=0 | ||
13 | max_width=40 | ||
14 | reserved_playlist_cols=31 | ||
15 | reserved_cols_in_percent="false" | ||
16 | force_square="true" | ||
17 | square_alignment="top" | ||
18 | left_aligned="true" | ||
19 | padding_left=0 | ||
20 | |||
21 | # Only set this if the geometries are wrong or ncmpcpp shouts at you to do it. | ||
22 | # Visually select/highlight a character on your terminal, zoom in an image | ||
23 | # editor and count how many pixels a character's width and height are. | ||
24 | font_height= | ||
25 | font_width= | ||
26 | |||
27 | main() { | ||
28 | kill_previous_instances >/dev/null 2>&1 | ||
29 | find_cover_image >/dev/null 2>&1 | ||
30 | display_cover_image 2>/dev/null | ||
31 | dunstify -u low --replace=69 -i "$cover_path" "$(mpc current)" | ||
32 | detect_window_resizes >/dev/null 2>&1 | ||
33 | } | ||
34 | |||
35 | # ==== Main functions ========================================================= | ||
36 | |||
37 | kill_previous_instances() { | ||
38 | script_name=$(basename "$0") | ||
39 | for pid in $(pidof -x "$script_name"); do | ||
40 | if [ "$pid" != $$ ]; then | ||
41 | kill -15 "$pid" | ||
42 | fi | ||
43 | done | ||
44 | } | ||
45 | |||
46 | find_cover_image() { | ||
47 | |||
48 | # First we check if the audio file has an embedded album art | ||
49 | ext="$(mpc --format %file% current | sed 's/^.*\.//')" | ||
50 | if [ "$ext" = "flac" ]; then | ||
51 | # since FFMPEG cannot export embedded FLAC art we use metaflac | ||
52 | metaflac --export-picture-to=/tmp/mpd_cover.jpg \ | ||
53 | "$(mpc --format "$music_library"/%file% current)" && | ||
54 | cover_path="/tmp/mpd_cover.jpg" && return | ||
55 | else | ||
56 | ffmpeg -y -i "$(mpc --format "$music_library"/%file% | head -n 1)" \ | ||
57 | /tmp/mpd_cover.jpg && | ||
58 | cover_path="/tmp/mpd_cover.jpg" && return | ||
59 | fi | ||
60 | |||
61 | # If no embedded art was found we look inside the music file's directory | ||
62 | album="$(mpc --format %album% current)" | ||
63 | file="$(mpc --format %file% current)" | ||
64 | album_dir="${file%/*}" | ||
65 | album_dir="$music_library/$album_dir" | ||
66 | found_covers="$(find "$album_dir" -type d -exec find {} -maxdepth 1 -type f \ | ||
67 | -iregex ".*/.*\(${album}\|cover\|folder\|artwork\|front\).*[.]\\(jpe?g\|png\|gif\|bmp\)" \; )" | ||
68 | cover_path="$(echo "$found_covers" | head -n1)" | ||
69 | if [ -n "$cover_path" ]; then | ||
70 | return | ||
71 | fi | ||
72 | |||
73 | # If we still failed to find a cover image, we use the fallback | ||
74 | if [ -z "$cover_path" ]; then | ||
75 | cover_path="$HOME/.ncmpcpp/scripts/music.png" | ||
76 | fi | ||
77 | } | ||
78 | |||
79 | display_cover_image() { | ||
80 | compute_geometry | ||
81 | |||
82 | send_to_ueberzug \ | ||
83 | action "add" \ | ||
84 | identifier "mpd_cover" \ | ||
85 | path "$cover_path" \ | ||
86 | x "$ueber_left" \ | ||
87 | y "$padding_top" \ | ||
88 | height "$ueber_height" \ | ||
89 | width "$ueber_width" \ | ||
90 | synchronously_draw "True" \ | ||
91 | scaler "forced_cover" \ | ||
92 | scaling_position_x "0.5" | ||
93 | } | ||
94 | |||
95 | detect_window_resizes() { | ||
96 | { | ||
97 | trap 'display_cover_image' WINCH | ||
98 | while :; do sleep .1; done | ||
99 | } & | ||
100 | } | ||
101 | |||
102 | |||
103 | # ==== Helper functions ========================================================= | ||
104 | |||
105 | compute_geometry() { | ||
106 | unset LINES COLUMNS # Required in order for tput to work in a script | ||
107 | term_lines=$(tput lines) | ||
108 | term_cols=$(tput cols) | ||
109 | if [ -z "$font_height" ] || [ -z "$font_height" ]; then | ||
110 | guess_font_size | ||
111 | fi | ||
112 | |||
113 | ueber_height=$(( term_lines - padding_top - padding_bottom )) | ||
114 | # Because Ueberzug uses characters as a unit we must multiply | ||
115 | # the line count (height) by the font size ratio in order to | ||
116 | # obtain an equivalent width in column count | ||
117 | ueber_width=$(( ueber_height * font_height / font_width )) | ||
118 | ueber_left=$(( term_cols - ueber_width - padding_right )) | ||
119 | |||
120 | if [ "$left_aligned" = "true" ]; then | ||
121 | compute_geometry_left_aligned | ||
122 | else | ||
123 | compute_geometry_right_aligned | ||
124 | fi | ||
125 | |||
126 | apply_force_square_setting | ||
127 | } | ||
128 | |||
129 | compute_geometry_left_aligned() { | ||
130 | ueber_left=$padding_left | ||
131 | max_width_chars=$(( term_cols * max_width / 100 )) | ||
132 | if [ "$max_width" != 0 ] && | ||
133 | [ $(( ueber_width + padding_right + padding_left )) -gt "$max_width_chars" ]; then | ||
134 | ueber_width=$(( max_width_chars - padding_left - padding_right )) | ||
135 | fi | ||
136 | } | ||
137 | |||
138 | compute_geometry_right_aligned() { | ||
139 | if [ "$reserved_cols_in_percent" = "true" ]; then | ||
140 | ueber_left_percent=$(printf "%.0f\n" $(calc "$ueber_left" / "$term_cols" '*' 100)) | ||
141 | if [ "$ueber_left_percent" -lt "$reserved_playlist_cols" ]; then | ||
142 | ueber_left=$(( term_cols * reserved_playlist_cols / 100 )) | ||
143 | ueber_width=$(( term_cols - ueber_left - padding_right )) | ||
144 | fi | ||
145 | else | ||
146 | if [ "$ueber_left" -lt "$reserved_playlist_cols" ]; then | ||
147 | ueber_left=$reserved_playlist_cols | ||
148 | ueber_width=$(( term_cols - ueber_left - padding_right )) | ||
149 | fi | ||
150 | |||
151 | fi | ||
152 | |||
153 | if [ "$max_width" != 0 ] && [ "$ueber_width" -gt "$max_width" ]; then | ||
154 | ueber_width=$max_width | ||
155 | ueber_left=$(( term_cols - ueber_width - padding_right )) | ||
156 | fi | ||
157 | } | ||
158 | |||
159 | apply_force_square_setting() { | ||
160 | if [ $force_square = "true" ]; then | ||
161 | ueber_height=$(( ueber_width * font_width / font_height )) | ||
162 | case "$square_alignment" in | ||
163 | center) | ||
164 | area=$(( term_lines - padding_top - padding_bottom )) | ||
165 | padding_top=$(( padding_top + area / 2 - ueber_height / 2 )) | ||
166 | ;; | ||
167 | bottom) | ||
168 | padding_top=$(( term_lines - padding_bottom - ueber_height )) | ||
169 | ;; | ||
170 | *) ;; | ||
171 | esac | ||
172 | fi | ||
173 | } | ||
174 | |||
175 | guess_font_size() { | ||
176 | # A font width and height estimate is required to | ||
177 | # properly compute the cover width (in columns). | ||
178 | # We are reproducing the arithmetic used by Ueberzug | ||
179 | # to guess font size. | ||
180 | # https://github.com/seebye/ueberzug/blob/master/ueberzug/terminal.py#L24 | ||
181 | |||
182 | guess_terminal_pixelsize | ||
183 | |||
184 | approx_font_width=$(( term_width / term_cols )) | ||
185 | approx_font_height=$(( term_height / term_lines )) | ||
186 | |||
187 | term_xpadding=$(( ( - approx_font_width * term_cols + term_width ) / 2 )) | ||
188 | term_ypadding=$(( ( - approx_font_height * term_lines + term_height ) / 2 )) | ||
189 | |||
190 | font_width=$(( (term_width - 2 * term_xpadding) / term_cols )) | ||
191 | font_height=$(( (term_height - 2 * term_ypadding) / term_lines )) | ||
192 | } | ||
193 | |||
194 | guess_terminal_pixelsize() { | ||
195 | # We are re-using the same Python snippet that | ||
196 | # Ueberzug utilizes to retrieve terminal window size. | ||
197 | # https://github.com/seebye/ueberzug/blob/master/ueberzug/terminal.py#L10 | ||
198 | |||
199 | python <<END | ||
200 | import sys, struct, fcntl, termios | ||
201 | |||
202 | def get_geometry(): | ||
203 | fd_pty = sys.stdout.fileno() | ||
204 | farg = struct.pack("HHHH", 0, 0, 0, 0) | ||
205 | fretint = fcntl.ioctl(fd_pty, termios.TIOCGWINSZ, farg) | ||
206 | rows, cols, xpixels, ypixels = struct.unpack("HHHH", fretint) | ||
207 | return "{} {}".format(xpixels, ypixels) | ||
208 | |||
209 | output = get_geometry() | ||
210 | f = open("/tmp/ncmpcpp_geometry.txt", "w") | ||
211 | f.write(output) | ||
212 | f.close() | ||
213 | END | ||
214 | |||
215 | # ioctl doesn't work inside $() for some reason so we | ||
216 | # must use a temporary file | ||
217 | term_width=$(awk '{print $1}' /tmp/ncmpcpp_geometry.txt) | ||
218 | term_height=$(awk '{print $2}' /tmp/ncmpcpp_geometry.txt) | ||
219 | rm "/tmp/ncmpcpp_geometry.txt" | ||
220 | |||
221 | if ! is_font_size_successfully_computed; then | ||
222 | echo "Failed to guess font size, try setting it in `basename $0` settings" | ||
223 | fi | ||
224 | } | ||
225 | |||
226 | is_font_size_successfully_computed() { | ||
227 | [ -n "$term_height" ] && [ -n "$term_width" ] && | ||
228 | [ "$term_height" != "0" ] && [ "$term_width" != "0" ] | ||
229 | } | ||
230 | |||
231 | |||
232 | calc() { | ||
233 | awk "BEGIN{print $*}" | ||
234 | } | ||
235 | |||
236 | send_to_ueberzug() { | ||
237 | old_IFS="$IFS" | ||
238 | |||
239 | # Ueberzug's "simple parser" uses tab-separated | ||
240 | # keys and values so we separate words with tabs | ||
241 | # and send the result to the wrapper's FIFO | ||
242 | IFS="$(printf "\t")" | ||
243 | echo "$*" > "$FIFO_UEBERZUG" | ||
244 | |||
245 | IFS=${old_IFS} | ||
246 | } | ||
247 | |||
248 | main | ||