diff options
Diffstat (limited to 'radish')
-rwxr-xr-x | radish | 298 |
1 files changed, 298 insertions, 0 deletions
diff --git a/radish b/radish new file mode 100755 index 0000000..d8c55b1 --- /dev/null +++ b/radish | |||
@@ -0,0 +1,298 @@ | |||
1 | #!/bin/sh | ||
2 | # RADISH | ||
3 | # a new and improved RADIO that can play local files and noise. | ||
4 | # XXX: WIP | ||
5 | |||
6 | usage() { | ||
7 | cat <<EOF | ||
8 | RADISH: radio, music, static | ||
9 | USAGE: radish [-h|-k|-r|-s|-S] | ||
10 | radish -l [NAME] | ||
11 | radish [STATION] | ||
12 | |||
13 | FLAGS: | ||
14 | -h Show this help and exit. | ||
15 | -s Show radish's status and exit. | ||
16 | -S Show radish's status indefinitely. | ||
17 | -k Kill the currently-playing radish invocation. | ||
18 | -r Replay most recently-played station. | ||
19 | |||
20 | OPTIONS: | ||
21 | -l [NAME] List available stations. | ||
22 | If NAME is given, narrow the list to those matching it. | ||
23 | |||
24 | PARAMETERS: | ||
25 | STATION Which configured station to play. | ||
26 | Stations are defined in the \$RADISH_STATION_FILE | ||
27 | (default: $RADISH_STATION_FILE). | ||
28 | If STATION is not present, or if it matches | ||
29 | more than one station, radish will present a menu. | ||
30 | EOF | ||
31 | exit ${1:-0} | ||
32 | } | ||
33 | |||
34 | config() { | ||
35 | : "${RADISH_STATION_FILE:=${XDG_CONFIG_HOME:-$HOME/.config}/radish/stations}" | ||
36 | RADISH_PID_FILE=/tmp/radish.pid | ||
37 | RADISH_STATUS_FILE=/tmp/radish.status | ||
38 | RADISH_LP_FILE="${XDG_CACHE_HOME:-$HOME/.cache}/radish.lp" | ||
39 | } | ||
40 | |||
41 | main() { | ||
42 | config | ||
43 | while getopts :hkrsSl: opt; do | ||
44 | case "$opt" in | ||
45 | h) usage ;; | ||
46 | k) radish_kill ;; | ||
47 | r) set -- "$(cat "$RADISH_LP_FILE")" ;; | ||
48 | s) radish_status ;; | ||
49 | S) radish_status -follow ;; | ||
50 | l) radish_list "$OPTARG" ;; | ||
51 | :) | ||
52 | case "$OPTARG" in | ||
53 | l) radish_list ;; | ||
54 | *) | ||
55 | echo >&2 "Option -$OPTARG requires an argument" | ||
56 | usage 1 | ||
57 | ;; | ||
58 | esac | ||
59 | ;; | ||
60 | \?) | ||
61 | echo >&2 "Unknown option: -$OPTARG" | ||
62 | usage 1 | ||
63 | ;; | ||
64 | esac | ||
65 | done | ||
66 | shift "$((OPTIND - 1))" | ||
67 | radish_play "$@" | ||
68 | } | ||
69 | |||
70 | cleanup() { | ||
71 | rm /tmp/radish.* | ||
72 | } | ||
73 | |||
74 | ### "Private" functions | ||
75 | |||
76 | _radish_player() { | ||
77 | command -v "${1:-}" || | ||
78 | command -v "${RADISH_PLAYER:-}" || | ||
79 | command -v mpv || | ||
80 | command -v vlc || | ||
81 | { | ||
82 | echo >&2 "No suitable player found." | ||
83 | echo >&2 "Set \$RADISH_PLAYER." | ||
84 | exit 3 | ||
85 | } | ||
86 | } | ||
87 | |||
88 | _radish_stations() { | ||
89 | if [ -f "$RADISH_STATION_FILE" ]; then | ||
90 | sed -e '/^#/d' "$RADISH_STATION_FILE" | ||
91 | else | ||
92 | echo noise: | ||
93 | fi | ||
94 | } | ||
95 | |||
96 | _radish_kill_impl() { | ||
97 | [ -f "$RADISH_PID_FILE" ] || return 1 | ||
98 | x= | ||
99 | while xargs -a "$RADISH_PID_FILE" kill 2>/dev/null; do | ||
100 | case "$x" in | ||
101 | xxx*) | ||
102 | printf . | ||
103 | x= | ||
104 | ;; | ||
105 | *) x=x$x ;; | ||
106 | esac | ||
107 | done | ||
108 | echo | ||
109 | } | ||
110 | |||
111 | _radish_desc() { | ||
112 | cut -f2 "${1:--}" 2>/dev/null | ||
113 | } | ||
114 | _radish_url() { | ||
115 | cut -f1 "${1:--}" 2>/dev/null | ||
116 | } | ||
117 | _radish_tags() { | ||
118 | cut -f3 "${1:--}" 2>/dev/null | ||
119 | } | ||
120 | |||
121 | echo() { printf '%s\n' "$*"; } | ||
122 | |||
123 | ### Main functionality | ||
124 | |||
125 | radish_kill() { | ||
126 | printf >&2 '%s' "Killing radish..." | ||
127 | _radish_kill_impl || { | ||
128 | echo >&2 "I don't think radish is running." | ||
129 | exit 2 | ||
130 | } | ||
131 | cleanup | ||
132 | exit | ||
133 | } | ||
134 | |||
135 | radish_status() { | ||
136 | trap 'echo;exit 0' INT | ||
137 | if [ -n "${1:-}" ]; then | ||
138 | follow=-f | ||
139 | else | ||
140 | follow= | ||
141 | fi | ||
142 | tail $follow "$RADISH_STATUS_FILE" | ||
143 | echo | ||
144 | exit | ||
145 | } | ||
146 | |||
147 | radish_list() { | ||
148 | _radish_stations | grep -i "${1:-}" | awk -F '\t' '{ | ||
149 | desc = $'$_schema_desc' | ||
150 | url = $'$_schema_url' | ||
151 | tags = $'$_schema_tags' | ||
152 | printf "%-23s |", substr(desc,1,20) (length(desc)>20?"...":"") | ||
153 | printf "%-23s |", substr(tags,1,20) (length(tags)>20?"...":"") | ||
154 | printf "%-23s\n", substr(url,1,20) (length(url)>20?"...":"") | ||
155 | }' | ||
156 | exit | ||
157 | } | ||
158 | |||
159 | radish_choose_station() { | ||
160 | cands="$(_radish_stations | grep -i "$1")" | ||
161 | [ -z "$cands" ] && cands="$(_radish_stations)" | ||
162 | |||
163 | if [ "$(echo "$cands" | wc -l)" -gt 1 ]; then | ||
164 | echo "$cands" | _radish_desc | cat -n - | ||
165 | while true; do | ||
166 | printf "Radish> " | ||
167 | read station | ||
168 | if (echo "$station" | grep -qE '^[0-9]+$'); then | ||
169 | station="$(echo "$cands" | sed -n "${station}p;${station}q")" | ||
170 | if [ -z "$station" ]; then | ||
171 | echo >&2 "No station with that number." | ||
172 | continue | ||
173 | else | ||
174 | break | ||
175 | fi | ||
176 | fi | ||
177 | echo >&2 "Please input a station number." | ||
178 | done | ||
179 | else | ||
180 | station="$(echo "$cands")" | ||
181 | fi | ||
182 | echo >&2 "Selected: $(echo "$station" | _radish_desc)" | ||
183 | station="$(echo "$station" | _radish_url)" | ||
184 | } | ||
185 | |||
186 | ### Player functions | ||
187 | |||
188 | radish_play() { | ||
189 | case "${1:-}" in | ||
190 | https:* | http:*) | ||
191 | _radish_kill_impl || : | ||
192 | echo >&2 "Streaming $1..." | ||
193 | radish_play_web "$1" | ||
194 | ;; | ||
195 | noise:*) | ||
196 | _radish_kill_impl || : | ||
197 | echo >&2 "Playing noise: ${1#noise:}" | ||
198 | radish_play_noise "${1:-}" | ||
199 | ;; | ||
200 | shuf:*) | ||
201 | _radish_kill_impl || : | ||
202 | echo >&2 "Shuffling from ${1#shuf:}" | ||
203 | SHUF=1 radish_play_file "$1" 2>&1 | ||
204 | ;; | ||
205 | file:*) | ||
206 | _radish_kill_impl || : | ||
207 | echo >&2 "Playing from ${1#file:}" | ||
208 | SHUF=0 radish_play_file "$1" 2>&1 | ||
209 | ;; | ||
210 | *) | ||
211 | if [ -f "$RADISH_STATION_FILE" ]; then | ||
212 | radish_choose_station "${1:-}" | ||
213 | _radish_kill_impl || : | ||
214 | radish_play "$station" | ||
215 | else | ||
216 | _radish_kill_impl || : | ||
217 | radish_play_noise | ||
218 | fi | ||
219 | ;; | ||
220 | esac | ||
221 | |||
222 | echo "${1:-}" | _radish_url >"$RADISH_LP_FILE" | ||
223 | exit | ||
224 | } | ||
225 | |||
226 | radish_play_web() { | ||
227 | "$(_radish_player)" "$1" >"$RADISH_STATUS_FILE" 2>&1 & | ||
228 | echo $! >"$RADISH_PID_FILE" | ||
229 | } | ||
230 | |||
231 | radish_play_file() { | ||
232 | dir="$(echo "$1" | sed 's@^[^:]*:\(//\)\?@@')" | ||
233 | find "$dir" -depth -print0 \ | ||
234 | -iname '*flac' -o \ | ||
235 | -iname '*mp3' -o \ | ||
236 | -iname '*ogg' -o \ | ||
237 | -iname '*opus' | | ||
238 | xargs -0 realpath | | ||
239 | case "$SHUF" in | ||
240 | 1) shuf ;; | ||
241 | 0) sort ;; | ||
242 | esac >/tmp/radish.m3u | ||
243 | player="$(_radish_player)" | ||
244 | case "$player" in | ||
245 | *vlc | *mpv) args="--no-video" ;; | ||
246 | *) args= ;; | ||
247 | esac | ||
248 | "$player" $args /tmp/radish.m3u "$1" >"$RADISH_STATUS_FILE" 2>&1 & | ||
249 | echo $! >"$RADISH_PID_FILE" | ||
250 | } | ||
251 | |||
252 | radish_play_noise() { | ||
253 | # REQUIRES PLAY (from SOX) -- based on https://gist.github.com/rsvp/1209835 | ||
254 | play="$(command -v play)" || { | ||
255 | echo >&2 "Noise playback requires sox(1)." | ||
256 | exit 3 | ||
257 | } | ||
258 | # URL format: noise:type/center?time=60;wave=0.033;volume=1 | ||
259 | url_format='noise:\([^/]\+\)/\([^?]\+\)?\(.*\)' | ||
260 | noise_type="$(echo "${1:-}" | sed -n "s@${url_format}@\1@")" | ||
261 | noise_center="$(echo "${1:-}" | sed -n "s@${url_format}@\2@")" | ||
262 | noise_params="$(echo "${1:-}" | sed -n "s@${url_format}@\3@")" | ||
263 | time=60 | ||
264 | wave=0.03333333 | ||
265 | volume=1 | ||
266 | if [ -z "$noise_type" ] && [ -z "$noise_center" ]; then | ||
267 | noise_type=brown | ||
268 | noise_center=1786 | ||
269 | elif [ -z "$noise_type" ] || [ -z "$noise_center" ]; then | ||
270 | echo >&2 "URL format: 'noise:TYPE/CENTER?[PARAMS]" | ||
271 | echo >&2 "TYPE is one of 'brown','white','pink','tpdf'." | ||
272 | echo >&2 "CENTER is the center of the band-pass filter." | ||
273 | echo >&2 "PARAMS are TIME to generate noise;" | ||
274 | echo >&2 "WAVE, denoting volume variation; and VOLUME." | ||
275 | exit 4 | ||
276 | else | ||
277 | eval "$noise_params" | ||
278 | fi | ||
279 | |||
280 | export RADISH_PLAYER=play | ||
281 | echo >&2 "$(_radish_player)" \ | ||
282 | -c 2 --null synth "$time" "${noise_type}noise" \ | ||
283 | band -n "$noise_center" 499 \ | ||
284 | tremolo "$wave" 43 reverb 19 \ | ||
285 | bass -11 treble -1 \ | ||
286 | vol 14dB vol "$volume" \ | ||
287 | repeat "$(expr $time - 1)" | ||
288 | "$(_radish_player)" \ | ||
289 | -c 2 --null synth "$time" "${noise_type}noise" \ | ||
290 | band -n "$noise_center" 499 \ | ||
291 | tremolo "$wave" 43 reverb 19 \ | ||
292 | bass -11 treble -1 \ | ||
293 | vol 14dB vol "$volume" \ | ||
294 | repeat "$(expr $time - 1)" >"$RADISH_STATUS_FILE" 2>&1 & | ||
295 | echo $! >"$RADISH_PID_FILE" | ||
296 | } | ||
297 | |||
298 | main "$@" | ||