-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsend-music.sh
executable file
·306 lines (246 loc) · 8.61 KB
/
send-music.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
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
#!/bin/bash
# Perform cleanup operations on exit (e.g. stopping the web server)
trap "exit" INT TERM
trap "kill 0" EXIT
# Functions for printing colored symbols to the console
printCross() {
printf -- '[\e[1;31m✗\e[0m] \e[1;31m%b\e[0m\n' "${*}"
}
printInfo() {
printf -- '[\e[1;93mi\e[0m] %b\n' "${*}"
}
printQuestion() {
printf -- '\e[0m[\e[1;94m?\e[0m] %b\n' "${*}"
}
printTick() {
printf -- '[\e[1;32m✓\e[0m] \e[1;32m%b\e[0m\n' "${*}"
}
# Print an error message on invalid input
invalidArgument() {
printCross "Invalid argument!"
exit 22
}
# Ask the user to pick the order in which the tracks will be played
askTrack() {
printf -- "Track name : %s\n" "${file##*/}"
printf -- "Number %-8s : " "[1-$maxNum]"
read -r num
}
# Ask the user to input the robot's IP address
askIP() {
printf -- " Robot IP : "
read -r ip
}
# Determine if the provided IP is valid
# NOTE: An SSH server must be listening to this IP address
checkIP() {
if nc -z -w 5 "$ip" 22 2>/dev/null; then
printTick 'Robot SSH port open'
else
# Print a failure message and exit
printCross 'The robot is unreachable. Make sure you are able to connect to it via SSH and try again!'
exit 113 # No route to host
fi
}
# Function that sorts the imported tracks
sortTracks() {
# Ask the user to select a sorting method
printQuestion 'How do you want to sort the files?'
printf ' (1) Automatically\n (2) Manually'
read -r -s -n 1
printf '\n\n'
# Remove all previously created files (e.g. symlinks)
rm -rf data/www/*
# Sort tracks manually, according to user input
if [[ $REPLY == [2] ]]; then
# Count the number of files inside the music directory
# shellcheck disable=SC2012
maxNum=$(ls data/music/*.ogg | wc -l)
# Loop over all files inside the music directory
for file in data/music/*.ogg; do
# Ask the user for input
askTrack
# Run until the given number is valid and create a symlink named after it
until [[ "$num" -gt "0" && "$num" -le "$maxNum" ]] && ln -s ../../"$file" data/www/"$num" 2>/dev/null; do
# Print an error if the number is invalid
printf '\n'
printCross 'Invalid input. Please try again!\n'
# Ask the user again
askTrack
done
printf '\n'
done
# Otherwise, sort tracks automatically
else
local i=1
# Create a symlink for each file inside the directory
for file in data/music/*.ogg; do
ln -s ../../"$file" data/www/$i
i=$(( i + 1 ))
done
fi
}
# Import tracks from a directory or online service
importTracks() {
# Exit if ffmpeg is not installed
if [[ ! $(command -v ffmpeg) ]]; then
printCross 'ffmpeg is not installed!'
exit 2
fi
# Ask the user to select an import method
printQuestion 'Where would you like to import tracks from?'
printf ' (1) From a directory\n (2) From an online service (e.g. YouTube)'
read -r -s -n 1
printf '\n\n'
if [[ $REPLY == [1] ]]; then
printf -- ' Directory path : '
read -r dirname
# Convert each file to a format that the robot can play
for file in "$dirname"/*; do
ffmpeg -hide_banner -loglevel error -i "$file" -c:a libvorbis -vn -ar 48000 "data/music/${file##*/}.ogg"
done
elif [[ $REPLY == [2] ]]; then
# Exit if yt-dlp is not installed
if [[ ! $(command -v yt-dlp) ]]; then
printCross 'yt-dlp is not installed!'
exit 2
fi
# Ask the user to provide a URL
printf -- ' Video or playlist URL : '
read -r url
# Download the file using yt-dlp and then convert it to vorbis, without keeping the original file
yt-dlp -q --progress -o 'data/music/%(title)s.%(ext)s' -x --audio-format vorbis "$url"
printf '\n'
fi
# Sort the tracks after an import has been completed
sortTracks
}
# Show the order in which the tracks will be played
showTrackList() {
local i=1
printf -- '%s\n' \
"+--------+--------------------------------+" \
"| Number | Track name |" \
"+--------+--------------------------------+" \
# Loop over all symlinks and print their target as well as their order
for file in data/www/*; do
# Find symlink target
symlink=$(readlink "$file")
# Remove the path
symlink=${symlink##*/}
# Make sure the filename isn't too big
symlink=${symlink:0:30}
# Print number & track name
printf -- "| %-7s|%31s |\n" "$i" "$symlink"
i=$(( i + 1 ))
done
# Print bottom row
printf -- '+--------+--------------------------------+\n\n'
}
# Initialize the web server
webserverInit() {
# Check if python3 is installed
printInfo 'Checking Python version...'
if [[ $(command -v python3) ]]; then
printTick "$(python3 -V)"
else
printCross 'Python 3 is not installed!'
exit 2
fi
# Choose a random port and check if it is being used by another process
printInfo 'Searching for available port...'
while nc -z 127.0.0.1 "$port" 2>/dev/null || [ -z "$port" ]; do
port=$(( 1024 + RANDOM % 65535 ))
done
# Announce that a port has been found and we are ready to start
printInfo "$port" 'is available!'
# Start the web server
printInfo 'Starting web server...'
python3 -m http.server "$port" -d data/www 2>/dev/null 1>&2 &
# Create a temporary variable
local i
# Test the server
until nc -z 127.0.0.1 "$port"; do
# If there have been 50 attempts (>5sec) without a response from the server, throw an error
# For context, on my machine the counter reaches up to 10 under normal circumstances
if [[ $i == 50 ]]; then
printCross 'The server is unreachable. The cause of the issue could not be determined.'
exit 113
fi
# Small delay to allow for the server to start
sleep 0.1
# Increment the counter on each run
i=$(( i + 1 ))
done
# If the above loop exits, print a success message
printTick 'The server is running!\n'
}
# Connect to the robot via SSH and run the payload
sshConnect() {
printInfo 'Please specify the robot IP.'
# Check if git is installed
if [[ $(command -v git) ]]; then
# Provide a hint
printInfo 'Tip: Leave the field empty for the last IP to be used.'
# Ask the user for input
askIP
# Check if the variable is empty
if [[ -z $ip ]]; then
# Fetch the IP from the config file (if available)
ip=$(git config -f data/.config network.ip)
printf -- "\033[1A\033[15C%s\n" "$ip"
# Check if the IP is valid (after autocompletion)
checkIP
# If the user has provided an IP, store it for future use
else
# Check if the IP is valid (before storing to config)
checkIP
# Store the IP in the config file
git config -f data/.config network.ip "$ip"
fi
# If git is not installed, just ask the user for an IP each time the script is run
else
# Ask the user for input
askIP
# Check if the provided IP is valid
checkIP
fi
# SSH into the robot and stop the music
if [[ $1 == 'stop' ]]; then
# Kill ogg123
ssh "$ip" -l root 'pkill ogg123'
# Print a success message
printTick 'The music has been stopped.'
# SSH into the robot and run the payload
else
# Run the listener script
ssh "$ip" -l root 'sh -s' < play-music.sh "$(hostname -I | cut -d' ' -f1)":"$port"
fi
}
# Use script's directory as root
cd "$(dirname "$0")" || exit
# Create the required directories
mkdir -p {data/music,data/www}
# Run only if an argument has been passed
if [[ -n $1 ]]; then
# Check for arguments
case "$1" in
(--import|-i) importTracks ;;
(--sort|-s) sortTracks; showTrackList ;;
(--stop|-p) sshConnect stop ;;
(*) invalidArgument ;;
esac
exit 0
fi
# Check if the music directory is empty
if [[ -z "$(ls -A data/music)" ]]; then
printInfo 'Music directory seems empty. Launching import wizard...'
# Call the function for importing tracks
importTracks
fi
# Initialize the web server
webserverInit
# Show the tracks in their respective order
showTrackList
# Connect to the robot over SSH and execute the payload
sshConnect