#!/bin/bash # # copyright Wulf Coulmann # modified by Stefan Hummert # for best work with streamtuner # # GNU GPL # http://www.gnu.org/licenses/gpl.html # # Download the original version here: http://gpl.coulmann.de/radio_rip # Download the modified version here: http://www.donneker.de/projects/radio_rip # # last modified 29.07.2007 by Stefan Hummert # Config -------------------------------------------------- TMP='/tmp' # location of the temporery fifo buffer RETRY=200 # how often we retry if we don't get a streaming port OVERWRITEMODE="OFF" # OFF don't overwrite already existing files # ON overwrites already existing files APPENDDATETORIPDIR="OFF" # ON inserts all ripped files in an extra directory with the actual date # OFF all files in one directory RIPTOSINGLEFILE="OFF" # ON the whole stream to one filename # OFF every new track to an extra filename and the filename is the titlename MAXRIPSIZE=400 # max space in MB used for ripping, on a per session basis DEFAULTLENGTH=90 # default time, the script rips the stream, in minutes DEFAULTDIR="/home/don/incoming/rips" # default directory where the rips are saved DEFAULTEXT=".ogg" # default file extension (this is also the default file format) DEFAULTNAME="\$DEFAULTDIR/radio_rip_\$(date +'%Y%m%d_%H%M')\$DEFAULTEXT" MYNAME=$(basename $0) # name of the script itself CMDMPLAYER=$(which mplayer) # normal mplayer location SOX=$(which sox) # sox location # let's rock'n roll show_manpage() { cat | less << 'EOD' RADIO_RIP(1) User Manuals RADIO_RIP(1) NAME radio_rip - dumps every audio stream you can listen with your mplayer installation (yes, it's a possibility to record real streams) and encode them to everything sox can manage. Ogg Vorbis, mp3 ... SYNOPSIS radio_rip [ --help | stop | clean | [STREAM_URL | PLAYLIST] [RECORDING_TIME] ] DESCRIPTION radio_rip connect via mplayer to a audio stream send the raw output to a named pipe. Sox reads from the named pipe and transform to a format you refer in OUTPUT_FILE by the last 3 characters. If there is no free streaming port available radio_rip retry that often you define in the config section of the radio_rip script. The default is 200. It's a god idea to use radio_rip in combination with cron or at and sometimes http://gpl.coulmann.de/day_of_month.html is very helpful. Of corse, you need a mplayer and a sox installation. MODIFIED: this modified version of radio_rip also plays the stream in default modus. It saves internet bandwith, by playing the recording file. This leads to better usage of your bandwith, but there is a bit higher delay between the incoming stream and the playing file (buffer reasons) Also in default modus the songs will be saved by title in your rips directory you can enter in the settings at the begin of the script. The split of the music by titles is not 100% exactly. The reasons lie in the communication handling between the server and the client, and the temporary buffer used. This version is also optimized for work with streamtuner. For this use, modify the config of streamtuner and set the command to work with radio_rip. Setting is for example: radio_rip %q in streamtuner, assumes radio_rip is in your default path. Then you can click on play in streamtuner, and radio_rip gets called with the STREAM_URL or PLAYLIST as parameter. A explanation with screen shots you can see at http://www.donneker.de/projects/radio_rip stop stops an active radio_rip instance to terminate clean clean sometimes if all get scrambled this is to clean up pipes STREAM_URL is the url of any audio stream your mplayer installation can handle please refer to mplayer (1) PLAYLIST you can pass a playlist as argument, the playlist must have the file extension .m3u the playlist can have multiple entries for streaming URLs, that will be used in reconnect tries. RECORDING_TIME optional argument is the duration of the recording in minutes OPTIONS -h print this help EXAMPLES main program call radio_rip rtsp://stream01.rbb-online.de/broadcast/multikulti or radio_rip rtsp://stream01.rbb-online.de/broadcast/multikulti 30 or radio_rip /tmp/mystreamlist.m3u 30 crontab - every sunday 9:04 4 09 * * 7 radio_rip rtsp://stream01.rbb-online.de/broadcast/multikulti crontab and day_of_month - every second sunday of the month 3 16 * * 7 day_of_month 2 "radio_rip rtsp://stream01.rbb-online.de/broadcast/multikulti" at for single recordings echo "radio_rip rtsp://stream01.rbb-online.de/broadcast/multikulti" | at 16:05 AUTHOR Wulf Coulmann modified by Stefan Hummert for best work with streamtuner SEE ALSO mplayer(1), sox(1), cron(8), at(1), http://gpl.coulmann.de/day_of_month.html http://www.donneker.de/projects/radio_rip Linux Last change: July 2007 EOD } # show_manpage end # # calc_rip_size # calculates the size of the ripped file, to see if MAXRIPSIZE is reached # # RIPPEDSIZE itself is calculated in KB for beeing more exactly for many small files calc_rip_size() { if [[ ! -r "$ZIEL" ]]; then # file not readable or not written/no file exists return 1 fi if [[ "$RIPTOSINGLEFILE" = "ON" ]]; then RIPPEDSIZE=$(echo "$(stat --format=%s "$ZIEL" )/1024" | bc) else if [[ "$actfilename" = "$ZIEL" ]]; then lastfilesize=$actfilesize else actfilename="$ZIEL" lastfilesize=0 fi actfilesize=$(echo "$(stat --format=%s "$ZIEL" )/1024" | bc) RIPPEDSIZE=$(($RIPPEDSIZE + $actfilesize - $lastfilesize)) fi } # # clean_up # cleans up temp files (pipes) that are/was used # clean_up() { rm -rf $TMP/*_radio_rip.* } # # starts cat that INFOPIPE doesnt run full # start_cat() { if [[ "$CATPROC" = "" ]] && [[ "$CREATE_INFOFIFO" = "DONE" ]]; then cat <&4 & CATPROC=$! fi } # # stops cat # stop_cat() { if [[ "$CATPROC" != "" ]]; then kill $CATPROC CATPROC="" fi } # # start_mplayer # run mplayer in background # start_mplayer() { # run mplayer in background if [[ "$CREATE_INFOFIFO" = "" ]]; then exec 4<>$INFOFIFO exec 5>$TMP/$$_radio_rip.tmp exec 5>&4 CREATE_INFOFIFO="DONE" fi # because direct redirect into infopipe doesnt work cause of fork -> workarround nohup "$CMDMPLAYER" -ao "pcm:file=$FIFO" -cache 32 "$URL" >&5 & MPLAYER=$! echo $MPLAYER >$PIDFILE start_cat } # # check_mplayer # checks if mplayer already runs # # returns 0 when mplayer runs # returns 1 when mplayer doesnt run check_mplayer() { if [[ "$MPLAYER" = "" ]]; then return 1 fi MPLAYER=$(ps -p $MPLAYER|grep "$(basename $CMDMPLAYER)"|grep -v grep|awk '{ print $1 }') if [ -n "$MPLAYER" ] ; then return 0 fi return 1 } # # check radio_rip # checks if radio_rip already runs # # returns 0 when program runs # returns 1 when program doesnt run check_radio_rip() { if [[ "$1" = "" ]]; then return 1 fi RADIORIP=$(ps -ef|grep "$1"|grep -v $$|grep -v grep|awk '{ print $2 }') if [ -n "$RADIORIP" ] ; then return 0 fi return 1 } # # check other programms like sox, ... # diff to check_radio_rip is not to exclude $$ (because its parent process id) # # returns 0 when program runs # returns 1 when program doesnt run check_prog() { if [[ "$1" = "" ]]; then return 1 fi RADIORIP=$(ps -ef|grep "$1"|grep -v grep|awk '{ print $2 }') if [ -n "$RADIORIP" ] ; then return 0 fi return 1 } # # check_URL # checks URL, if its a playlist, extract the entries # check_URL() { FILEEXT=${URL##*.} if [[ "$FILEEXT" = "m3u" ]] && [[ -r $URL ]]; then # its a playlist, so extract entries from it URLISLIST=1 ANZURLS=$(cat $URL | grep -v "^#" | wc -l) # get first URL ACTURL=1 PLAYLIST="$URL" URL=$(cat $PLAYLIST | grep -v "^#" | awk -vACTURL=$ACTURL '{ if (NR == ACTURL) print $0 }' ) else URLISLIST=0 ANZURLS=1 fi } # # get_next_URL # returns next URL to try # get_next_URL() { if [[ $URLISLIST -eq 1 ]]; then let ACTURL=ACTURL+1 if [[ "$ACTURL" = "" ]] || [[ $ACTURL -gt $ANZURLS ]] || [[ $ACTURL -lt 1 ]]; then ACTURL=1 fi URL=$(cat $PLAYLIST | grep -v "^#" | awk -vACTURL=$ACTURL '{ if (NR == ACTURL) print $0 }' ) fi } # # start sox # starts or restarts the sox process # start_sox() { # convert raw data to ogg if [[ "$1" = "" ]]; then eval ZIEL=$DEFAULTNAME else ZIEL="$1" fi check_prog "$SOX " if [[ $? -ne 0 ]]; then # sox not running nohup "$SOX" $FIFO "$ZIEL" & else # sox already running, but if TARGET was not FOUND (TARGETFOUND=0) then restart # because we dont know the target (ZIEL) if [[ $TARGETFOUND -ne 1 ]]; then kill $RADIORIP sleep 1 nohup "$SOX" $FIFO "$ZIEL" & fi fi } # write_cmd # write to infofifo # # parameter 1: command to write write_cmd() { exec 6<>$INFOFIFO echo "[RADIO_RIP_CMD:$1]" >&6 exec 6>&- exec 6<&- } # # stop_running_radio_rip # stops an actual running version without killing it (the right way) # stop_running_radio_rip() { check_radio_rip "$MYNAME " if [[ $? -eq 0 ]]; then # radio_rip running # stop it using a command over the INFOFIFO INFOFIFO="$(ls -rt $TMP/*_radio_rip.info | tail -1)" if [[ ! -r "$INFOFIFO" ]]; then echo "ERROR: can't read $INFOFIFO - stop of $MYNAME not possible normal way. killing $RADIORIP ..." kill $RADIORIP return 0 fi write_cmd "STOP" else # radio_rip not running echo "ERROR: can't stop $MYNAME - it's not running." return 1 fi } # # kick all running processes # if no regular stop worked kick_all_running() { FOUNDMPLAYER=1 while [ $FOUNDMPLAYER -eq 1 ] do FOUNDMPLAYER=0 check_radio_rip "$MYNAME " if [[ "$RADIORIP" != "" ]]; then kill $RADIORIP FOUNDMPLAYER=1 fi check_prog "$CMDMPLAYER " if [[ "$RADIORIP" != "" ]]; then kill $RADIORIP FOUNDMPLAYER=1 fi check_prog "$SOX " if [[ "$RADIORIP" != "" ]]; then kill $RADIORIP FOUNDMPLAYER=1 fi done } # # main script # # mymain() { URL=$1 DAUER=$2 if [[ "$1" = "" ]]; then show_manpage return fi eval ZIEL=$DEFAULTNAME TARGETFOUND=0 if [[ "$DAUER" = "" ]]; then DAUER=$DEFAULTLENGTH fi PIDFILE="$TMP/$(basename $0).pid" if [[ -r $PIDFILE ]]; then MPLAYER=$(cat $PIDFILE) fi FIFOFILE="$TMP/$(basename $0).fifo" if [[ -r $FIFOFILE ]]; then #FIFO=$(head -1 $FIFOFILE) #if [[ ! -r $FIFO ]]; then # FIFO="" #else #TARGETFOUND=1 # ZIEL="" #fi FIFO="" ZIEL="" fi if [[ "$URL" = "stop" ]]; then stop_running_radio_rip return 0 fi if [[ "$URL" = "clean" ]]; then clean_up return 0 fi if [[ "$ZIEL" = "" ]]; then eval ZIEL=$DEFAULTNAME TARGETFOUND=0 fi if [[ ! -d "$DEFAULTDIR" ]]; then echo "ERROR: $DEFAULTDIR does not exist! - Aborting." >&2 exit 2 fi if [[ "$APPENDDATETORIPDIR" = "ON" ]]; then DEFAULTDIR="$DEFAULTDIR/$(date +'%Y%m%d')" if [[ ! -d "$DEFAULTDIR" ]]; then mkdir "$DEFAULTDIR" chmod 755 "$DEFAULTDIR" fi fi # check for running radio_rip / mplayer and stop them first check_mplayer FOUNDMPLAYER=$? if [[ $FOUNDMPLAYER -ne 0 ]]; then check_prog "$CMDMPLAYER " FOUNDMPLAYER=$? MPLAYER="$RADIORIP" fi if [[ $FOUNDMPLAYER -eq 0 ]]; then # mplayer already running, stop and restart # first stop mplayer, then the other radio_rip instance # kill $MPLAYER SECONDSPASSED=0 while [ 1 = 1 ] do check_radio_rip "$MYNAME " FOUNDMPLAYER=$? if [[ $FOUNDMPLAYER -eq 0 ]] && [[ $(($SECONDSPASSED % 5)) -eq 0 ]]; then # kill $RADIORIP # stop it the easy way if [[ "$SECONDSPASSED" -eq 0 ]]; then $0 stop else $0 clean kick_all_running fi else if [[ $FOUNDMPLAYER -ne 0 ]]; then break fi fi sleep 1 let SECONDSPASSED=SECONDSPASSED+1 done fi # now if old is stopped, start new check_URL if [[ "$FIFO" = "" ]]; then FIFO=$TMP/$(date +'%s')_radio_rip.wav mkfifo $FIFO # echo $FIFO >$FIFOFILE fi INFOFIFO="$TMP/$(basename $FIFO .wav).info" if [[ ! -r $INFOFIFO ]]; then mkfifo $INFOFIFO fi # play the song # and wait for requested time SECONDSPASSED=0 RECONNECT=0 while [[ $SECONDSPASSED -lt $(($DAUER * 60)) ]] do if [[ $(($SECONDSPASSED % 30)) -eq 0 ]]; then calc_rip_size if [[ $(($RIPPEDSIZE / 1024)) -gt $MAXRIPSIZE ]]; then # stop ripping DAUER=0 continue fi fi # first check if recording runs, and check if target (ZIEL) is already there check_mplayer FOUNDMPLAYER=$? if [[ $FOUNDMPLAYER -ne 0 ]] || [[ ! -r "$ZIEL" ]]; then # make sure we get a port of the streaming server, if not try to reconnect start_cat if [[ $FOUNDMPLAYER -ne 0 ]]; then CHECK=0 if [ $CHECK -lt 1 ] && [ $RECONNECT -lt $RETRY ]; then echo "reconnect $RECONNECT" if [[ $ANZURLS -gt 1 ]]; then get_next_URL fi start_mplayer RECONNECT=$(($RECONNECT + 1)) fi CHECK=$(($CHECK + 1)) echo "connected." fi if [[ ! -r "$ZIEL" ]]; then check_prog "sox $FIFO" if [[ $? -ne 0 ]]; then # convert raw data to ogg with sox start_sox "$ZIEL" fi fi sleep 3 let SECONDSPASSED=SECONDSPASSED+3 continue fi # if thats ok, then check if player is running check_prog "$CMDMPLAYER $LASTTITLE -cache 32" if [[ $? -eq 0 ]]; then stop_cat line="-" # thats only to get into while loop while [ "$line" != "" ] do # clear line, read it timed (1 second) and process it while [ "$line" != "" ] do line="" read -n 1024 -t 1 line <&4 if [[ "$(echo "$line" | grep "ICY Info:" | wc -l)" -gt 0 ]]; then break fi if [[ "$(echo "$line" | grep "\[RADIO_RIP_CMD:STOP\]" | wc -l)" -gt 0 ]]; then # do a normal exit, easy to reach by setting remaining play time to 0 DAUER=0 line="" break fi done # process output if [[ "$(echo "$line" | grep "ICY Info:" | wc -l)" -gt 0 ]]; then STREAMTITLE="$(echo "$line" | awk "-F'" '{ print $2 }' | \ awk '{ gsub("[^[:alnum:]. -]"," "); while ( index($0," ") > 0) { gsub(" "," "); }; print $0 }')" if [[ "$ACTTITLE" = "$STREAMTITLE" ]]; then # nothing to do echo "nothing to do ACT=STREAM" >/dev/null else if [[ "$RIPTOSINGLEFILE" != "ON" ]]; then # new title playing calc_rip_size # add size of last file ZIEL="$DEFAULTDIR/${STREAMTITLE}${DEFAULTEXT}" if [[ ! -r "$ZIEL" ]] || [[ "$OVERWRITEMODE" = "ON" ]]; then start_sox "$ZIEL" ACTTITLE="$STREAMTITLE" fi fi fi if [[ "$(basename "$ZIEL" .ogg)" = "$STREAMTITLE" ]]; then # nothing to do echo "nothing to do ZIEL=STREAM" >/dev/null else mv "$ZIEL" "$DEFAULTDIR/${STREAMTITLE}${DEFAULTEXT}" fi fi # end processing done let SECONDSPASSED=SECONDSPASSED+1 else start_cat $CMDMPLAYER "$ZIEL" -cache 32 >/dev/null 2>&1 & PLAYER=$! LASTTITLE="$ZIEL" sleep 3 let SECONDSPASSED=SECONDSPASSED+3 fi done # close infopipe (fd 4 and 5) stop_cat exec 4>&- exec 4<&- exec 5>&- # stop mplayer check_mplayer if [[ $? -eq 0 ]]; then kill $MPLAYER fi check_prog "$CMDMPLAYER " if [[ $? -eq 0 ]]; then kill $PLAYER kill $RADIORIP fi #stop sox check_prog "sox $FIFO" if [[ $? -eq 0 ]]; then # this kills now sox kill $RADIORIP fi # remove fifo rm -rf $FIFO rm -rf $INFOFIFO # remove temp files rm -f $PIDFILE rm -f $FIFOFILE } # initializing of not customizable variables ACTURL=0 RIPPEDSIZE=0 # remove comment # to enable debug output # set -x # exec 1>/home/don/scripts/radio_rip.$$.out # exec 2>&1 # Hauptswitch case "$1" in -h|--help) show_manpage exit 0 ;; *) mymain $* ;; esac