├── README.md └── bashttpd /README.md: -------------------------------------------------------------------------------- 1 | # bashttpd 2 | Simple Bash based web server, fork of https://github.com/avleen/bashttpd by Avleen 3 | 4 | To run it just fire up 5 | ```shell 6 | $> bashttpd -s -m 7 | ``` 8 | Then visit http://your.ip.addr:8080/ in your favorite web browser! 9 | 10 | You can browse the filesystem and with this version you can also view pictures and stream multimedia content from the remote host. 11 | -------------------------------------------------------------------------------- /bashttpd: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################# 4 | ########################################################################### 5 | ### bashttpd v 1.23 6 | ### 7 | ### Original author: Avleen Vig, 2012 8 | ### Reworked by: Josh Cartwright, 2012 9 | ### Modified by: A.M.Danischewski, 2024 10 | ### Issues: If you find any issues leave me a comment at 11 | ### http://scriptsandoneliners.blogspot.com/2015/04/bashttpd-self-contained-bash-webserver.html 12 | ### 13 | ### This is a simple Bash based webserver. By default it will browse files and allows for 14 | ### retrieving binary files. 15 | ### 16 | ### It has been tested successfully to view and stream files including images, mp3s, 17 | ### mp4s and downloading files of any type including binary and compressed files via 18 | ### any web browser. 19 | ### 20 | ### Successfully tested on various browsers on Windows, Linux and Android devices (including the 21 | ### Android Smartwatch ZGPAX S8). 22 | ### 23 | ### It handles favicon requests by hardcoded favicon image -- by default a marathon 24 | ### runner; change it to whatever you want! By base64 encoding your favorit favicon 25 | ### and changing the global variable below this header. 26 | ### 27 | ### Make sure if you have a firewall it allows connections to the port you plan to 28 | ### listen on (8080 by default). 29 | ### 30 | ### By default this program will allow for the browsing of files from the 31 | ### computer where it is run. 32 | ### 33 | ### Make sure you are allowed connections to the port you plan to listen on 34 | ### (8080 by default). Then just drop it on a host machine (that has bash) 35 | ### and start it up like this: 36 | ### 37 | ### $192.168.1.101> bashttpd -s 38 | ### 39 | ### On the remote machine you should be able to browse and download files from the host 40 | ### server via any web browser by visiting: 41 | ### 42 | ### http://192.168.1.101:8080 43 | ### 44 | ### This program will generate a micro media server, if you start it with the -m option 45 | ### 46 | ### $192.168.1.101> bashttpd -s -m 47 | ### 48 | ### Afterward source the alias file: source /tmp/m_player.aliases 49 | ### Create a playlist: cd /fav/mp3/collection; 102mpl mp3 50 | ### Adds 10 mp3 files are added to the playlist. 51 | ### 52 | ### You should be able to loop stream the content on vlc by opening the url (mrl) 53 | ### and hitting repeat song loop, each time it loops a the next track will be served. 54 | ### 55 | ### On the remote machine you should be able to use vlc to play the media, copy the 56 | ### url (e.g. from notepad) and then paste it into an empty playlist in vlc 57 | ### e.g. C-x-C-c http://192.168.1.101:8080/tmp/lnk.mp4 58 | ### 59 | ### Note: This does not work for Windows media player under its default configuration 60 | ### because wmp caches the files locally and does not refetch automatically. 61 | ### If you find a workaround, contact me at scriptsandoneliners.blogspot.com. 62 | ### 63 | #### This program requires (to work to full capacity) by default: 64 | ### socat or netcat (w/ '-e' option - on Ubuntu netcat-traditional) 65 | ### tree - useful for pretty directory listings 66 | ### If you are using socat, you can type: bashttpd -s 67 | ### 68 | ### to start listening on the LISTEN_PORT (default is 8080), you can change 69 | ### the port below. 70 | ### E.g. nc -lp 8080 -e ./bashttpd ## <-- If your nc has the -e option. 71 | ### E.g. nc.traditional -lp 8080 -e ./bashttpd 72 | ### E.g. bashttpd -s -or- socat TCP4-LISTEN:8080,fork EXEC:bashttpd 73 | ### 74 | ### Copyright (C) 2012, Avleen Vig 75 | ### 76 | ### Permission is hereby granted, free of charge, to any person obtaining a copy of 77 | ### this software and associated documentation files (the "Software"), to deal in 78 | ### the Software without restriction, including without limitation the rights to 79 | ### use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 80 | ### the Software, and to permit persons to whom the Software is furnished to do so, 81 | ### subject to the following conditions: 82 | ### 83 | ### The above copyright notice and this permission notice shall be included in all 84 | ### copies or substantial portions of the Software. 85 | ### 86 | ### THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 87 | ### IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 88 | ### FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 89 | ### COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 90 | ### IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 91 | ### CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 92 | ### 93 | ########################################################################### 94 | ############################################################################# 95 | 96 | ### CHANGE THIS TO WHERE YOU WANT THE CONFIGURATION FILE TO RESIDE 97 | declare -r BASHTTPD_CONF="/tmp/bashttpd.conf" 98 | 99 | ### CHANGE THIS TO WHERE YOU WANT THE MEDIA DAEMON TO RESIDE 100 | ### This is optional, only if you want to broadcast a media collection 101 | ### to e.g. vlc or any other streaming media player, simply add the media 102 | ### file and put your player on loop (the same track, yet it will change the 103 | ### the content automagically). 104 | declare -r MEDIA_DAEMON="/tmp/m_play.bsh" 105 | declare -r M_ALIASES="/tmp/m_player.aliases" 106 | declare -r M_LNK="/tmp/lnk.mp4" ## The mp4 extension works in vlc for both mp3/mp4 files. 107 | declare -r M_DIR="" ## Default dir for media in playlist 108 | ## w/out abs paths (defaults to pwd). 109 | declare -r M_PLAYLIST="/tmp/_mpl.txt" 110 | declare -i M_SLEEP=30 ### How long to wait to check if tracks are being played/pulled. 111 | declare -r M_PID="/tmp/_mpl_pid_" 112 | 113 | function exit_gracefully() { 114 | if [[ -f "${M_PID}_$$" ]]; then 115 | IFS=$(echo -en "\n\b") && for a in $(< "${M_PID}_$$"); do 116 | echo "Killing media server with pid $a"; 117 | ps -ef | grep -i "${MEDIA_DAEMON}" | grep "$a" | awk "{print $2}" | xargs -I {} kill -9 {}; 118 | done 119 | rm "${M_PID}_$$" 120 | fi 121 | rm -f "${MEDIA_DAEMON}" 122 | exit 0 123 | } 124 | trap "{ exit_gracefully; }" EXIT SIGINT SIGHUP SIGQUIT SIGTERM 125 | 126 | ### CHANGE THIS IF YOU WOULD LIKE TO LISTEN ON A DIFFERENT PORT 127 | declare -i LISTEN_PORT=8080 128 | 129 | ## If you are on AIX, IRIX, Solaris, or a hardened system redirecting 130 | ## to /dev/random will probably break, you can change it to /dev/null. 131 | declare -a DISCARD_DEV="/dev/random" 132 | 133 | ## Just base64 encode your favorite favicon and change this to whatever you want. 134 | declare -r FAVICON="AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAADg4+3/srjc/5KV2P+ortn/xMrj/6Ch1P+Vl9f/jIzc/3572f+CgNr/fnzP/3l01f+Ih9r/h4TZ/8fN4//P1Oj/3uPr/7O+1v+xu9X/u8XY/9bi6v+UmdD/XV26/3F1x/+GitT/VVXC/3x/x/+HjNT/lp3Z/6633f/E0eD/2ePr/+bt8v/U4+v/0uLp/9Xj6//Z5e3/oKbX/0pJt/9maML/cHLF/3p8x//T3+n/3Ofu/9vo7//W5Oz/0uHq/9zn7f/j6vD/1OLs/8/f6P/R4Oj/1OPr/7jA4f9KSbf/Skm3/3p/yf/U4ez/1ePq/9rn7//Z5e3/0uHp/87e5//a5Ov/5Ovw/9Hf6v/T4uv/1OLp/9bj6/+kq9r/Skq3/0pJt/+cotb/zdnp/9jl7f/a5u//1+Ts/9Pi6v/O3ub/2uXr/+bt8P/Q3un/0eDq/9bj7P/Z5u7/r7jd/0tKt/9NTLf/S0u2/8zW6v/c5+//2+fv/9bj6//S4un/zt3m/9zm7P/k7PD/1OPr/9Li7P/V5Oz/2OXt/9jl7v+HjM3/lZvT/0tKt/+6w+L/2ebu/9fk7P/V4+v/0uHq/83d5v/a5ev/5ezw/9Pi6v/U4+z/1eXs/9bj6//b5+//vsjj/1hYvP9JSLb/horM/9nk7P/X5e3/1eTs/9Pi6v/P3uf/2eXr/+Tr7//O3+n/0uLr/9Xk7P/Y5e3/w8/k/7XA3/9JR7f/SEe3/2lrw//G0OX/1uLr/9Xi7P/T4ev/0N/o/9zn7f/k7PD/zN3p/8rd5v/T4ur/1ePt/5We0/+0w9//SEe3/0pKt/9OTrf/p7HZ/7fD3//T4uv/0N/o/9Hg6f/d5+3/5ezw/9Li6//T4uv/2ubu/8PQ5f9+hsr/ucff/4eOzv+Ei8z/rLja/8zc6P/I1+b/0OLq/8/f6P/Q4Oj/3eft/+bs8f/R4On/0+Lq/9Tj6v/T4Ov/wM7h/9Df6f/M2uf/z97q/9Dg6f/Q4On/1OPr/9Tj6//S4ur/0ODp/93o7f/n7vH/0N/o/8/f5//P3+b/2OXt/9zo8P/c6fH/zdjn/7fB3/+3weD/1eLs/9nn7//V5Oz/0+Lr/9Pi6//e6O7/5u3x/9Pi6v/S4en/0uLp/9Tj6//W4+v/3Ojw/9rm7v9vccT/wcvm/9rn7//X5Oz/0uHq/9Hg6f/S4er/3uju/+bt8f/R4On/0uHp/9Xk6//Y5u7/1OTs/9bk7P/W5Ov/XFy9/2lrwf/a5+//1uPr/9Pi6v/U4er/0eHq/93o7v/v8vT/5ezw/+bt8f/o7vL/6e/z/+jv8v/p7/L/6e/y/9XZ6//IzOX/6e7y/+nv8v/o7vL/5+7x/+ft8f/r8PP/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==" 135 | 136 | declare -i DEBUG=1 137 | declare -i VERBOSE=0 138 | declare -a REQUEST_HEADERS 139 | declare REQUEST_URI="" 140 | declare -a HTTP_RESPONSE=( 141 | [200]="OK" 142 | [400]="Bad Request" 143 | [403]="Forbidden" 144 | [404]="Not Found" 145 | [405]="Method Not Allowed" 146 | [500]="Internal Server Error") 147 | declare DATE=$(date +"%a, %d %b %Y %H:%M:%S %Z") 148 | declare -a RESPONSE_HEADERS=( 149 | "Date: $DATE" 150 | "Expires: $DATE" 151 | "Server: Slash Bin Slash Bash" 152 | ) 153 | 154 | function warn() { ((${VERBOSE})) && echo "WARNING: $@" >&2; } 155 | 156 | function chk_conf_file() { 157 | [ -r "${BASHTTPD_CONF}" ] || { 158 | cat >"${BASHTTPD_CONF}" <<'EOF' 159 | # 160 | # bashttpd.conf - configuration for bashttpd 161 | # 162 | # The behavior of bashttpd is dictated by the evaluation 163 | # of rules specified in this configuration file. Each rule 164 | # is evaluated until one is matched. If no rule is matched, 165 | # bashttpd will serve a 500 Internal Server Error. 166 | # 167 | # The format of the rules are: 168 | # on_uri_match REGEX command [args] 169 | # unconditionally command [args] 170 | # 171 | # on_uri_match: 172 | # On an incoming request, the URI is checked against the specified 173 | # (bash-supported extended) regular expression, and if encounters a match the 174 | # specified command is executed with the specified arguments. 175 | # 176 | # For additional flexibility, on_uri_match will also pass the results of the 177 | # regular expression match, ${BASH_REMATCH[@]} as additional arguments to the 178 | # command. 179 | # 180 | # unconditionally: 181 | # Always serve via the specified command. Useful for catchall rules. 182 | # 183 | # The following commands are available for use: 184 | # 185 | # serve_file FILE 186 | # Statically serves a single file. 187 | # 188 | # serve_dir_with_tree DIRECTORY 189 | # Statically serves the specified directory using 'tree'. It must be 190 | # installed and in the PATH. 191 | # 192 | # serve_dir_with_ls DIRECTORY 193 | # Statically serves the specified directory using 'ls -al'. 194 | # 195 | # serve_dir DIRECTORY 196 | # Statically serves a single directory listing. Will use 'tree' if it is 197 | # installed and in the PATH, otherwise, 'ls -al' 198 | # 199 | # serve_dir_or_file_from DIRECTORY 200 | # Serves either a directory listing (using serve_dir) or a file (using 201 | # serve_file). Constructs local path by appending the specified root 202 | # directory, and the URI portion of the client request. 203 | # 204 | # serve_static_string STRING 205 | # Serves the specified static string with Content-Type text/plain. 206 | # 207 | # Examples of rules: 208 | # 209 | # on_uri_match '^/issue$' serve_file "/etc/issue" 210 | # 211 | # When a client's requested URI matches the string '/issue', serve them the 212 | # contents of /etc/issue 213 | # 214 | # on_uri_match 'root' serve_dir / 215 | # 216 | # When a client's requested URI has the word 'root' in it, serve up 217 | # a directory listing of / 218 | # 219 | # DOCROOT=/var/www/html 220 | # on_uri_match '/(.*)' serve_dir_or_file_from "$DOCROOT" 221 | # When any URI request is made, attempt to serve a directory listing 222 | # or file content based on the request URI, by mapping URI's to local 223 | # paths relative to the specified "$DOCROOT" 224 | # 225 | 226 | #unconditionally serve_static_string 'Hello, world! You can configure bashttpd by modifying bashttpd.conf.' 227 | DOCROOT=/ 228 | on_uri_match '/(.*)' serve_dir_or_file_from 229 | 230 | # More about commands: 231 | # 232 | # It is possible to somewhat easily write your own commands. An example 233 | # may help. The following example will serve "Hello, $x!" whenever 234 | # a client sends a request with the URI /say_hello_to/$x: 235 | # 236 | # serve_hello() { 237 | # add_response_header "Content-Type" "text/plain" 238 | # send_response_ok_exit <<< "Hello, $2!" 239 | # } 240 | # on_uri_match '^/say_hello_to/(.*)$' serve_hello 241 | # 242 | # Like mentioned before, the contents of ${BASH_REMATCH[@]} are passed 243 | # to your command, so its possible to use regular expression groups 244 | # to pull out info. 245 | # 246 | # With this example, when the requested URI is /say_hello_to/Josh, serve_hello 247 | # is invoked with the arguments '/say_hello_to/Josh' 'Josh', 248 | # (${BASH_REMATCH[0]} is always the full match) 249 | EOF 250 | warn "Created bashttpd.conf using defaults. Please review and configure bashttpd.conf before running bashttpd again." 251 | # exit 1 252 | } 253 | } 254 | 255 | function gen_media_daemon() { 256 | cat < "${MEDIA_DAEMON}" 257 | #!/usr/bin/env bash 258 | 259 | declare -r m_lnk="${M_LNK}" 260 | declare -r m_dir="${M_DIR:-$(pwd)}" 261 | declare -r m_playlist="${M_PLAYLIST}" 262 | ## Sleep in case streams aren't being pulled, should be shorter than your shortest media. 263 | declare -i m_sleep=${M_SLEEP} 264 | declare -i m_count=0 ## Total playlist entries. 265 | declare -i m_track=1 ## Current track selection. 266 | while :; do 267 | if lsof "\${m_lnk}" 2>"${DISCARD_DEV}" | { grep -q "\${m_lnk##*/}"; } || [[ ! -f "\${m_lnk}" ]]; then 268 | if [[ -f "\${m_playlist}" ]] && m_count=\$(wc -l <"\${m_playlist}") && ((\${m_count} > 0)); then 269 | ((\${m_track}>\${m_count})) && m_track=1 ## Reset to first track if track selection is over. 270 | song=\$(sed -n \${m_track}p "\${m_playlist}") ## Very minor race condition. 271 | song_l="\${#song}" 272 | np_song_l="\${song##*/}" 273 | (( \${#np_song_l} == \${song_l} )) && song="\${m_dir}/\${song}" 274 | while :; do 275 | ## Hack idea: echo the to be cp'd song name to a tmp file, store it in a global and add a new alias 276 | ## to display the current playing track listing. Make it yours! =) 277 | lsof "\${m_lnk}" 2>"${DISCARD_DEV}" | { ! grep -q "\${m_lnk##*/}"; } && 278 | [[ -f "\${song}" ]] && { rm -f "\${m_lnk}"; cp "\${song}" "\${m_lnk}"; break; } || sleep 1; 279 | done 280 | m_track+=1 281 | else 282 | sleep \${m_sleep} ## No playlist. 283 | fi 284 | else 285 | sleep 2 ## No stream being pulled. 286 | fi 287 | done 288 | EOF 289 | chmod +x "${MEDIA_DAEMON}" 290 | } 291 | 292 | function recv() { ((${VERBOSE})) && echo "< $@" >&2; } 293 | 294 | function send() { ((${VERBOSE})) && echo "> $@" >&2; echo "$*"; } 295 | 296 | function add_response_header() { RESPONSE_HEADERS+=("$1: $2"); } 297 | 298 | function send_response_binary() { 299 | local code="${1}" 300 | local file="${2}" 301 | local transfer_stats="" 302 | local tmp_stat_file="/tmp/_send_response_$$_" 303 | send "HTTP/1.0 $1 ${HTTP_RESPONSE[$1]}" 304 | for i in "${RESPONSE_HEADERS[@]}"; do 305 | send "$i" 306 | done 307 | send 308 | if ((${VERBOSE})); then 309 | ## Use dd since it handles null bytes 310 | dd 2>"${tmp_stat_file}" < "${file}" 311 | transfer_stats=$(<"${tmp_stat_file}") 312 | echo -en ">> Transferred: ${file}\n>> $(awk '/copied/{print}' <<< "${transfer_stats}")\n" >&2 313 | rm "${tmp_stat_file}" 314 | else 315 | ## Use dd since it handles null bytes 316 | dd 2>"${DISCARD_DEV}" < "${file}" 317 | fi 318 | } 319 | 320 | function send_response() { 321 | local code="$1" 322 | send "HTTP/1.0 $1 ${HTTP_RESPONSE[$1]}" 323 | for i in "${RESPONSE_HEADERS[@]}"; do 324 | send "$i" 325 | done 326 | send 327 | while IFS= read -r line; do 328 | send "${line}" 329 | done 330 | } 331 | 332 | function send_response_ok_exit() { send_response 200; exit 0; } 333 | 334 | function send_response_ok_exit_binary() { send_response_binary 200 "${1}"; exit 0; } 335 | 336 | function fail_with() { send_response "$1" <<< "$1 ${HTTP_RESPONSE[$1]}"; exit 1; } 337 | 338 | function serve_file() { 339 | local file="$1" 340 | local CONTENT_TYPE="" 341 | case "${file}" in 342 | *\.css) 343 | CONTENT_TYPE="text/css" 344 | ;; 345 | *\.js) 346 | CONTENT_TYPE="text/javascript" 347 | ;; 348 | *) 349 | CONTENT_TYPE=$(file -b --mime-type "${file}") 350 | ;; 351 | esac 352 | add_response_header "Content-Type" "${CONTENT_TYPE}" 353 | CONTENT_LENGTH=$(stat -c'%s' "${file}") 354 | add_response_header "Content-Length" "${CONTENT_LENGTH}" 355 | ## Use binary safe transfer method since text doesn't break. 356 | send_response_ok_exit_binary "${file}" 357 | } 358 | 359 | function serve_dir_with_tree() { 360 | local dir="$1" tree_vers tree_opts basehref x 361 | ## HTML 5 compatible way to avoid tree html from generating favicon 362 | ## requests in certain browsers, such as browsers in android smartwatches. =) 363 | local no_favicon=" " 364 | local tree_page="" 365 | local base_server_path="/${2%/}" 366 | [ "$base_server_path" = "/" ] && base_server_path=".." 367 | local tree_opts="--du -h -a --dirsfirst" 368 | add_response_header "Content-Type" "text/html" 369 | # The --du option was added in 1.6.0. "/${2%/*}" 370 | read _ tree_vers x < <(tree --version) 371 | tree_page=$(tree -H "$base_server_path" -L 1 ${tree_opts} -D "${dir}") 372 | tree_page=$(sed "5 i ${no_favicon}" <<< "${tree_page}") 373 | tree_page=$(sed '6 i ' <<< "${tree_page}") 374 | [[ "${tree_vers}" == v1.6* ]] 375 | send_response_ok_exit <<< "${tree_page}" 376 | } 377 | 378 | function serve_dir_with_ls() { 379 | local dir="${1}" 380 | add_response_header "Content-Type" "text/plain" 381 | send_response_ok_exit < \ 382 | <(ls -la "${dir}") 383 | } 384 | 385 | function serve_dir() { 386 | local dir="${1}" 387 | # If `tree` is installed, use that for pretty output. 388 | which tree &>"${DISCARD_DEV}" && \ 389 | serve_dir_with_tree "$@" 390 | serve_dir_with_ls "$@" 391 | fail_with 500 392 | } 393 | 394 | function urldecode() { [ "${1%/}" = "" ] && echo "/" || echo -e "$(sed 's/%\([[:xdigit:]]\{2\}\)/\\\x\1/g' <<< "${1%/}")"; } 395 | 396 | function serve_dir_or_file_from() { 397 | local URL_PATH="${1}/${3}" 398 | shift 399 | URL_PATH=$(urldecode "${URL_PATH}") 400 | [[ $URL_PATH == *..* ]] && fail_with 400 401 | # Serve index file if exists in requested directory 402 | [[ -d "${URL_PATH}" && -f "${URL_PATH}/index.html" && -r "${URL_PATH}/index.html" ]] && \ 403 | URL_PATH="${URL_PATH}/index.html" 404 | if [[ -f "${URL_PATH}" ]]; then 405 | [[ -r "${URL_PATH}" ]] && \ 406 | serve_file "${URL_PATH}" "$@" || fail_with 403 407 | elif [[ -d "${URL_PATH}" ]]; then 408 | [[ -x "${URL_PATH}" ]] && \ 409 | serve_dir "${URL_PATH}" "$@" || fail_with 403 410 | fi 411 | fail_with 404 412 | } 413 | 414 | function serve_static_string() { 415 | add_response_header "Content-Type" "text/plain" 416 | send_response_ok_exit <<< "$1" 417 | } 418 | 419 | function on_uri_match() { 420 | local regex="$1" 421 | shift 422 | [[ "${REQUEST_URI}" =~ $regex ]] && \ 423 | "$@" "${BASH_REMATCH[@]}" 424 | } 425 | 426 | function unconditionally() { "$@" "$REQUEST_URI"; } 427 | 428 | function gen_playlist_aliases() { 429 | [[ -f "${M_ALIASES}" ]] && rm -f "${M_ALIASES}" 430 | echo "alias add2mpl='_() { song_l=\"\${#1}\"; np_song_l=\"\${1##*/}\"; [[ ! \${#np_song_l} -lt \${song_l} ]] && song=\"\$(pwd)/\${1}\" || song=\"\${1}\"; echo \"\${song}\" >> \"${M_PLAYLIST}\"; }; _'" >> "${M_ALIASES}" 431 | echo "alias rmmpl='rm \"${M_PLAYLIST}\"'" >> "${M_ALIASES}" 432 | echo "alias lsmpl='cat \"${M_PLAYLIST}\"'" >> "${M_ALIASES}" 433 | echo "alias 102mpl='_() { IFS=\$(echo -en \"\n\b\") && for a in \$(ls -f1 *\"\${1}\" | shuf | tail -10); do add2mpl \"\${a}\"; done; }; _'" >> "${M_ALIASES}" 434 | echo "alias rempl='rmmpl; 102mpl'" >> "${M_ALIASES}" 435 | echo "echo -e \"\\nThe following aliases allow for tracks to be added and removed from the playlist: \\n\"" >> "${M_ALIASES}" 436 | echo "echo \" alias add2mpl: Adds a new media track to the current playlist\"" >> "${M_ALIASES}" 437 | echo "echo \" alias rmmpl: Deletes the current playlist\"" >> "${M_ALIASES}" 438 | echo "echo \" alias lsmpl: Lists the current playlist\"" >> "${M_ALIASES}" 439 | echo "echo -e \" alias rempl: Recreates the current playlist with 10 random tracks, you may provide \\n an optional filter arg.\"" >> "${M_ALIASES}" 440 | echo "echo -e \" alias 102mpl: Adds 10 random tracks from the current directory to the current playlist.\"" >> "${M_ALIASES}" 441 | echo "echo -e \" Optionally you may provide a filter argument (mp4,avi,mp3 etc), to add 10 random \\n mp3 tracks from current dir to the current playlist: e.g. $> 102mpl mp3 \\n\"" >> "${M_ALIASES}" 442 | echo "echo -e \"Now just add some tracks and you're ready to start vlc on a remote machine: \\n e.g. \\\$> vlc \\\"http://192.168.1.101:8080/tmp/lnk.mp4\\\"\"" >> "${M_ALIASES}" 443 | echo "echo \"Put vlc on single song loop, all playlist tracks should play through looping the entire playlist.\"" >> "${M_ALIASES}" 444 | echo "echo -e \"Hint: To skip a song, hit stop on vlc after at least 2 seconds then play.\\n\"" >> "${M_ALIASES}" 445 | chmod +x "${M_ALIASES}" 446 | } 447 | 448 | function start_media_daemon() { 449 | [[ ! -f "${MEDIA_DAEMON}" ]] && gen_media_daemon && echo "Created media daemon config file.." 450 | [[ ! -f "${MEDIA_DAEMON}" ]] && echo "Error: No media daemon script found, wanted: $MEDIA_DAEMON" && exit 0 451 | gen_playlist_aliases 452 | rm -f "${M_LNK}" 453 | echo "You can add media to the playlist with the following aliases .." 454 | "${M_ALIASES}" 455 | echo "You can source the aliases file from other terminals with: \$> source \"${M_ALIASES}\" " 456 | echo "Starting media daemon.." && "${MEDIA_DAEMON}" & 457 | echo "$!" >> "${M_PID}_$$" 458 | } 459 | 460 | function main() { 461 | local recv="" 462 | local line="" 463 | local REQUEST_METHOD="" 464 | local REQUEST_HTTP_VERSION="" 465 | chk_conf_file 466 | [[ ${UID} = 0 ]] && warn "It is not recommended to run bashttpd as root." 467 | # Request-Line HTTP RFC 2616 $5.1 468 | read -r line || fail_with 400 469 | line=${line%%$'\r'} 470 | recv "${line}" 471 | read -r REQUEST_METHOD REQUEST_URI REQUEST_HTTP_VERSION <<< "${line}" 472 | [ -n "${REQUEST_METHOD}" ] && [ -n "${REQUEST_URI}" ] && \ 473 | [ -n "${REQUEST_HTTP_VERSION}" ] || fail_with 400 474 | # Only GET is supported at this time 475 | [ "${REQUEST_METHOD}" = "GET" ] || fail_with 405 476 | while IFS= read -r line; do 477 | line=${line%%$'\r'} 478 | recv "${line}" 479 | # If we've reached the end of the headers, break. 480 | [ -z "${line}" ] && break 481 | REQUEST_HEADERS+=("${line}") 482 | done 483 | } 484 | 485 | [[ ! -z "${2}" ]] && [[ "${2}" = "-m" ]] && start_media_daemon 486 | 487 | if [[ ! -z "${1}" ]] && [ "${1}" = "-s" ]; then 488 | socat TCP4-LISTEN:${LISTEN_PORT},fork EXEC:"${0}" 2>"${DISCARD_DEV}" 489 | M_SEM=0 490 | else 491 | main 492 | source "${BASHTTPD_CONF}" 493 | fail_with 500 494 | fi 495 | --------------------------------------------------------------------------------