mpvc
1#!/bin/sh
2
3mpvdsock="/tmp/mpvd.sock"
4xobfifo="/tmp/xob.fifo"
5cmd_socat="$(command -v socat) - '$mpvdsock'"
6cmd_jq="$(command -v jq)"
7cmd_exif="$(command -v exiftool)"
8
9print_help() {
10 echo "Usage: mpvc ACTION [ARGS]\n"
11 echo "ACTIONS:"
12 echo " append PATH append file to playlist."
13 echo " goto INDEX goto track index of playlist."
14 echo " help print this help message."
15 echo " list print playlist."
16 echo " load PATH load file or directory, and override playlist."
17 echo " lower lower volume 10 units"
18 echo " mute toggle mute"
19 echo " next go to next track."
20 echo " plmenu MODE launch playlist menu."
21 echo " prev go to previous track."
22 echo " raise raise volume 10 units"
23 echo " seek (-)N seek (-)n seconds"
24 echo " stop stop playback"
25 echo " toggle toogle play and pause."
26 echo " "
27 echo "EXTRA:"
28 echo " ytdl-format FMT set ytdl-format"
29 echo " "
30 echo "ALSA:"
31 echo " alower alsa lower 5%"
32 echo " amute alsa mute"
33 echo " araise alsa raise 5%"
34}
35
36send_command() {
37 local input=""
38 for var in "$@"; do
39 [ "$input" != "" ] && input="$input, "
40 input="$input\"$var\""
41 done
42 printf '{ "command": [%s] }\n' "$input" | $cmd_socat
43}
44
45get_path() {
46 local path="$(readlink -f "$@")"
47 if [ -f "$path" ] || [ -d "$path" ]; then
48 echo "$path"
49 else
50 echo "$@"
51 fi
52}
53
54notify_vol() {
55 case $1 in
56 master)
57 local level="$(amixer sget Master | awk -F"[][]" '/dB/ { print ($2+0) }')"
58 local muted="$(amixer sget Master | awk -F"[][]" '/dB/ { print ($6) }' | sed 's/off/true/')"
59 ;;
60 mpv)
61 local level="$(send_command "get_property" "volume" | $cmd_jq -rM '.data')"
62 local muted="$(send_command "get_property" "mute" | $cmd_jq -rM '.data')"
63 ;;
64 *) return;;
65 esac
66
67 local marks="$((($level + 5) / 10))"
68 [ "$marks" -gt 10 ] && marks=10
69
70 local output="$1"
71 if [ "$muted" = "true" ]; then
72 local output="$output (muted)"
73 fi
74 local output="$output: [$([ "$marks" -gt 0 ] && printf "%-$((marks))s" "+" | sed 's/\ /+/g')"
75 local output="$output$([ "$marks" -lt 10 ] && printf "%-$((10-marks))s" " ")]"
76 local output="$output$(printf " %3d" "$level")%"
77 ztatus-notify "$output"
78
79 if [ -p "$xobfifo" ]; then
80 printf '%d%s\n' "$level" "$([ "$muted" = "true" ] && echo '!')" \
81 > "$xobfifo"
82 fi
83}
84
85die() {
86 printf "Error: %s\n" "$@"
87 exit 1
88}
89
90# check if prerequisites exist
91[ -z "$cmd_socat" ] && die "could not find 'socat'"
92[ -z "$cmd_jq" ] && die "could not find 'jq'"
93[ -z "$cmd_exif" ] && die "could not find 'exiftool'"
94
95# check if daemonization should apply
96if [ "${0##*/}" = "mpvd" ]; then
97 if pidof -x "$(basename -- "$0")" -o $$ >/dev/null; then
98 die "Another instance of mpvd is already runnig..."
99 fi
100 [ -S "$mpvdsock" ] && rm "$mpvdsock"
101 while true; do
102 mpv --idle=yes \
103 --input-ipc-server="$mpvdsock" \
104 --ytdl-format=best \
105 --load-scripts=yes \
106 2>&1 >/tmp/mpvd.log
107 done
108 exit 0
109fi
110
111# check if socket exists
112[ ! -S "$mpvdsock" ] && die "socket: '$mpvdsock' is missing"
113
114# print help if no argument was passed
115if [ $# -lt 1 ]; then
116 print_help
117 exit 0
118fi
119
120show_playlist() {
121 cmd="fzf"
122 [ "$1" = "x" ] && cmd="dmenu_vtc -l 20 -p playlist:"
123 script="$(realpath -s $0)"
124 list="$($script list)"
125 [ "$list" = "" ] && exit 1
126 entry="$(
127 echo "$list" \
128 | sed -e 's/^[ \t]*/ /' -e 's/^[^\w]*current[^\t]*\t/> /' \
129 | awk '{printf "%-5s %s\n", (NR ": "), $0}' \
130 | $cmd \
131 )"
132 [ "$entry" != "" ] && $script goto "$((${entry%%:*}-1))" || exit 1
133}
134
135exif_extract() {
136 echo "$1" | grep ^"$2" | cut -d':' -f 2- | sed 's/^[ \t]*//'
137}
138
139exif_wrapper() {
140 exif_data="$($cmd_exif -directory -filename -artist -title -duration "$@")"
141 exif_directory="$(exif_extract "$exif_data" "Directory")"
142 exif_filename="$(exif_extract "$exif_data" "File Name")"
143 exif_artist="$(exif_extract "$exif_data" "Artist")"
144 exif_title="$(exif_extract "$exif_data" "Title")"
145 exif_duration="$(exif_extract "$exif_data" "Duration")"
146 exif_artisttitle="$exif_filename"
147
148 if [ "$exif_artist" != "" ] && [ "$exif_title" != "" ]; then
149 exif_artisttitle="$exif_artist - $exif_title"
150 fi
151
152 exif_directory="$(get_path "$exif_directory")"
153
154 # make sure duration is in the format of 'hh:mm:ss' then to seconds (awk)
155 exif_duration="$(echo "$exif_duration" | cut -d' ' -f 1)"
156 exif_duration="$( \
157 printf '0:0:%s' "$exif_duration" \
158 | rev | cut -d':' -f -3 | rev \
159 | awk -F':' '{ print ($1 * 3600) + ($2 * 60) + $3 }' \
160 )"
161
162 echo "$(printf '#EXTINF:%s,%s\n%s/%s\n' \
163 "$exif_duration" \
164 "$exif_artisttitle" \
165 "$exif_directory" \
166 "$exif_filename" \
167 )"
168}
169
170case "$1" in
171 # actions
172 load)
173 shift;
174 playlist="$(get_path "$@")"
175 if [ -d "$playlist" ]; then
176 playlist_tmp="$(mktemp /tmp/pls.mpvc.XXXXXXXXXX)"
177 echo '#EXTM3U' > "$playlist_tmp"
178 find "$playlist" -maxdepth 2 -type f | sort -n | while read -r f; do
179 case "$f" in
180 *.flac | *.mp3 | *.ogg)
181 exif_wrapper "$f" >> "$playlist_tmp" ;;
182 *) continue ;;
183 esac
184 done
185 playlist="$playlist_tmp"
186 fi
187 send_command "loadfile" "$playlist"
188 [ "$playlist_tmp" != "" ] && (sleep 10 && rm "$playlist_tmp") &
189 ;;
190 append)
191 shift; send_command "loadfile" "$(get_path "$@")" "append" ;;
192 stop)
193 send_command "stop" ;;
194 toggle)
195 send_command "cycle" "pause" ;;
196 prev)
197 send_command "playlist-prev" ;;
198 next)
199 send_command "playlist-next" ;;
200 goto)
201 shift; send_command "set_property" "playlist-pos" "$@" ;;
202 list)
203 send_command "get_property" "playlist" \
204 | $cmd_jq -rM '.data[]|(
205 if .current then
206 "\\e[1;37mcurrent\\e[0m\\t"
207 else
208 "\\t" end
209 )+(.title//.filename)' \
210 | sed -e "s/\\\\t/\\t/g" -e "s/\\\\e/`printf "\033"`/g";;
211 plmenu) shift; show_playlist "$@" ;;
212 seek)
213 shift; send_command "seek" "$@" ;;
214 mute)
215 send_command "cycle" "mute"; notify_vol mpv;;
216 raise)
217 send_command "add" "volume" "10"; notify_vol mpv;;
218 lower)
219 send_command "add" "volume" "-10"; notify_vol mpv;;
220
221 # extra
222 ytdl-format)
223 shift; send_command "set" "ytdl-format" "$@" ;;
224
225 # alsa
226 amute)
227 amixer set Master toggle; notify_vol master;;
228 araise)
229 amixer -q sset Master 5%+; notify_vol master;;
230 alower)
231 amixer -q sset Master 5%-; notify_vol master;;
232
233 # default behaviour
234 help)
235 print_help ;;
236 *)
237 echo "Unknown option: $*"; print_help ;;
238esac