Shoutcast + radionomy search with recording option

User avatar
johnraff
Sperminator
Posts: 199
Joined: Wed Oct 17, 2012 6:38 pm
Location: Japan
Contact:

Shoutcast + radionomy search with recording option

Unread post by johnraff » Tue Sep 17, 2013 6:57 pm

EDIT 140911 Please see below: http://linuxbbq.org/bbs/viewtopic.php?f ... 725#p30725 for version 3.0 which works with Shoutcast again.

Two existing scripts rolled together and recording added. Type in one or more keywords, search shoutcast and radionomy together, choose a station, listen on radiotray and record with streamripper. Listening uses radiotray by preference as a running instance will take a url via dbus and load it. Recording uses streamripper - in the repos and a fairly small package. (A cool utility btw.) Aficionados might appreciate the associative array and Bash's 'select'. Image

Code: Select all

#!/bin/bash
# shoutcast_radionomy_search.sh
# search shoutcast and radionomy, send url to radiotray or another player
# send url to streamripper to record
# needs Bash 4, curl, [radiotray, dbus], streamripper, [xsel]
# xsel enables pasting from the X selection (to a config file etc.)
# comment out line 117 "printf '%s'..." if you don't use it

# choose player (& options if necessary): radio url will be sent to it.
# If radiotray is running, it will load the url and return to the script.
radioplay() {
    radiotray "$1"
}

# start up radiotray in background if it's not already running
# Comment out this line if you don't use radiotray.
# The first version shuts down radiotray when the terminal is closed.
# The second one leaves radiotray running.
#pgrep radiotray >/dev/null || ( radiotray >/dev/null 2>&1 & )
pgrep radiotray >/dev/null || nohup radiotray >/dev/null 2>&1 &

# - code to record a radio stream (url is $1) in a new terminal -
# Add your own options to streamripper,
# edit ~/.config/streamripper/streamripper.ini,
# change urxvt to another terminal
# or use a different command altogether.
recorder() {
    nohup urxvt -e streamripper "$1" >/dev/null 2>&1 &
}

# amend if necessary
required_commands='curl radiotray streamripper'
##########################################################################
error_exit() {
    echo "$0 error: $1" >&2
    exit 1
}

missing_commands=
for i in $required_commands
do
    hash $i || missing_commands+=" $i"
done
[[ $missing_commands ]] && error_exit "This script requires the following commands: $missing_commands
Please install the packages containing the missing commands
and rerun the script."

unset playing_name playing_url
while true
do
echo "Please enter keyword(s)"
read keyword
keyword="${keyword// /%20}" # escape spaces for url
results_sh=$( curl -s "http://www.shoutcast.com/Internet-Radio/$keyword" |awk '
BEGIN {
    RS="<div class=\"dirlist\">"
    FS=">"
}
NR < 2 {next}
{url = name = $2
sub(/^.*title=\"/,"",name)
sub(/\".*$/,"",name)
sub(/^.*href=\"/,"",url)
sub(/\".*$/,"",url)
print url,name }
' )
results_ra=$( curl -s "http://www.radionomy.com/en/search?q=$keyword" |awk '
BEGIN {
    RS="<h2 class=\"radio-title-list\"><a href=\"/en/radio/"
    FS="</a></h2>"
}
NR < 2 {next}
{url = name = $1
sub(/^.*>/,"",name)
sub(/\/index\".*$/,"",url)
url="http://listen.radionomy.com/" url ".m3u"
print url,name }
' )

if [[ $results_sh ]] && [[ $results_ra ]]
then
    results="$results_sh"$'\n'"$results_ra"
elif [[ $results_sh ]]
then
    echo "No results for $keyword on radionomy"
    results="$results_sh"
elif [[ $results_ra ]]
then
    echo "No results for $keyword on shoutcast"
    results="$results_ra"
else
    echo "Sorry, no results for $keyword"
    continue
fi

unset list
declare -A list # make associative array
while read -r url name # read in awk's output
do
    list["$name"]="$url"
done <<< "$results"

PS3='Please enter the number of your choice > '
while true
do
    menu=("${!list[@]}")
    [[ $playing_name && $playing_url ]] && menu+=("RECORD \"$playing_name\"")
    select station in "${menu[@]}" 'SEARCH AGAIN' QUIT
    do
        [[ $station = "RECORD \"$playing_name\"" ]] && {
            recorder "$playing_url"
            break
        }
        [[ $station = 'SEARCH AGAIN' ]] && break 2
        [[ $station = QUIT ]] && { echo 'Goodbye...'; exit; }
        [[ $station ]] && {
            printf '%s' "${list[$station]}" | xsel --input #--clipboard  # can paste url
            radioplay "${list[$station]}"
            playing_name=$station
            playing_url=${list[$station]}
            break
        }
    done
echo "
Last station chosen was $playing_name ( $playing_url )
"
done

done # closes loop started at line 50
exit
EDIT See new version below.
Last edited by johnraff on Thu Sep 11, 2014 10:43 am, edited 2 times in total.
All code is one.

User avatar
xaos52
The Good Doctor
Posts: 190
Joined: Thu Aug 15, 2013 11:59 am
Location: Eernegem, Belgium

Re: Shoutcast + radionomy search with recording option

Unread post by xaos52 » Tue Sep 24, 2013 1:15 pm

John,
the script needs 'xsel' as well.
I did not have it installed.

Great script :)
Connected. Take this REPL, brother, and may it serve you well.

User avatar
johnraff
Sperminator
Posts: 199
Joined: Wed Oct 17, 2012 6:38 pm
Location: Japan
Contact:

Re: Shoutcast + radionomy search with recording option

Unread post by johnraff » Wed Sep 25, 2013 6:10 pm

Hi xaos. thanks for checking it out! Re xsel, I should perhaps have written it up a bit more clearly - xsel is optional. To quote lines 6 & 7:
# xsel enables pasting from the X selection (to a config file etc.)
# comment out line 117 "printf '%s'..." if you don't use it
It was included to make it easier to add the current station to radiotray's bookmarks.

I'm just now working on a new version which allows the use of mpg123 or mplayer instead of radiotray. Another day or two with luck.
All code is one.

User avatar
johnraff
Sperminator
Posts: 199
Joined: Wed Oct 17, 2012 6:38 pm
Location: Japan
Contact:

Re: Shoutcast + radionomy search with recording option

Unread post by johnraff » Sat Oct 05, 2013 6:58 am

EDIT 140911 Please see V3.0 here: http://linuxbbq.org/bbs/viewtopic.php?f ... 725#p30725 which deals with the new Shoutcast interface, and has a couple of other minor tweaks.

Ah... it took a bit longer than a day or two.
In the process of setting up remote control of mpg123 and mplayer I was obliged to dig down into processes, forking, pipes, signals...

OK the new version is amazingly bloated (don't look Rho The Bloathound) because it has code for radiotray, mpg123 and mplayer built in, selected by changing a config variable at the top. This makes the file longer, but not really slower because most of the code gets ignored. A lot of checking is done to try and make sure it's not too easy to break, but please report back if you hit a problem. In particular, I haven't run this on Sid so as usual ymmv. (I don't know if some of the code might be re-usable in a general purpose bbq radio app.)

It should be easy enough to add code for mpd or moc since they seem to support remote access too, but I'll leave that as a task for those who want to use those players. Image Replace the top section of the script down to line 145 with your plugin, making sure you supply the required_commands variable and start_player, radioplay and cleanup functions. Look at what's there now to get the idea.

Anyway default player is mpg123 - nice and fast - and default behaviour is keep_player=true so you can close the script and terminal and the music will continue. Rerun the script if you want to change the station or shut down the player. Record the stream with streamripper if you want.

KEYBOARD SHORTCUTS:
Ctrl+C to exit normally
Ctrl+\ to terminate and close player
Ctrl+Z to start recording current station (usurps SIGTSTP)

Anyway, try it out if you have a free moment, anyone. Hope you like it :)

Code: Select all

#!/bin/bash
# shoutcast_radionomy_search.sh
# search shoutcast and radionomy,
# send url to radiotray, mpg123, mplayer or another player
# send url to streamripper to record
#
# needs curl, [radiotray, dbus | mpg123 | mplayer], streamripper, [xsel]
# xsel enables pasting from the X selection (to a config file etc.)
# Comment out line 246 "printf '%s'..." if you don't use it.
#
# KEYBOARD SHORTCUTS:
# Ctrl+C to exit normally
# Ctrl+\ to terminate and close player
# Ctrl+Z to start recording current station (handles SIGTSTP)

##### choose from radiotray, mpg123 or mplayer #####
player=mpg123

# Set this to something other than 'true'
# to have audio player exit with script.
# Otherwise player will continue till closed separately.
# Even with 'keep_player=true', if script is stopped with Ctrl+\
# then player will exit too.
keep_player=true

##### code to record a radio stream (url is $1) in a new terminal #####
# Add your own options to streamripper's command line,
# edit ~/.config/streamripper/streamripper.ini,
# change urxvt to another terminal
# or use a different command altogether.
recorder() {
    ( setsid urxvt -e streamripper "$1" >/dev/null 2>&1 & )
}

# where to put player control fifo
# (radiotray doesn't use this)
rpipe=/tmp/radio_pipe

##########################################################################
case $player in

##### RADIOTRAY SETTINGS #####
radiotray)
required_commands='curl streamripper radiotray'
start_player() {
    if pgrep radiotray >/dev/null
    then
        echo "$player is already running"
    else
        ( radiotray >/dev/null 2>&1 & )
    fi
}
radioplay() {
    radiotray $1
}
cleanup() { # run just before exit
    [[ $player_ok = true ]] && [[ $keep_player = true ]] && {
        echo "$player will continue to play.
You can control it from the system tray icon
or run the script again to choose another station."
        sleep 4
        return
    }
    pkill radiotray && echo "Closed radiotray."
}
;;
##### END RADIOTRAY #####

##### MPLAYER SETTINGS #####
mplayer)
required_commands='curl streamripper mplayer'
player_regex="^mplayer .*-input file=$rpipe"
launch_player() {
    [[ -p $rpipe ]] || { mkfifo "$rpipe" || error_exit "cannot make fifo $rpipe"; }
    ( setsid sh -c "mplayer -really-quiet -idle -slave -input file=$rpipe; rm -f $rpipe;" >/dev/null 2>&1 & )
    sleep 4 & launching_player=$!
}
load_url() {
    echo "loadlist $1" >"$rpipe"
}
;;&
##### END MPLAYER #####

##### MPG123 SETTINGS #####
mpg123)
required_commands='curl streamripper mpg123'
player_regex="^mpg123 .*--fifo $rpipe"
launch_player() { # mpg123 will make fifo if necessary
    ( setsid sh -c "mpg123 --remote --fifo $rpipe; rm -f $rpipe;" >/dev/null 2>&1 & )
    (sleep 2; echo 'silence' >"$rpipe") & launching_player=$!
}
load_url() {
    echo "loadlist 1 $1" >"$rpipe"
}
;;&
##### END MPG123 #####

##### COMMON TO MPLAYER AND MPG123 #####
mplayer|mpg123)
start_player() {
    if pgrep -f "$player_regex" >/dev/null
    then
        echo "$player is already running"
        [[ -p $rpipe ]] || error_exit "fifo missing $rpipe"
        (:>"$rpipe") & test_pipe=$!
        (sleep 2; kill $test_pipe 2>/dev/null && kill -s SIGPIPE $selfpid) &
    else
        launch_player
    fi
}
radioplay() {
    wait $launching_player
    [[ -p $rpipe ]] || error_exit "fifo missing $rpipe"
    pgrep -f "$player_regex" >/dev/null || error_exit "$player not running"
    load_url "$1"
}
cleanup() { # run just before exit
    [[ -p $rpipe ]] || { player_ok=false; echo "Script error: fifo $rpipe does not exist." >&2 ;}
    pgrep -f "$player_regex" >/dev/null || { player_ok=false; echo "Script error: $player not running" >&2 ;}
    [[ $player_ok = true ]] && {
        [[ $keep_player = true ]] && {
            echo "$player will continue to play.
You can stop it with the command:
echo quit >$rpipe
or run the script again to choose another station."
            sleep 4
            return
        }
        echo "closing $player..."
        echo 'quit' >"$rpipe" # try to close player nicely
        sleep 2 # time for player to quit
    }
    pkill -f "$player_regex" && echo "$player close forced."
    echo "removing $rpipe"
    rm -f "$rpipe" # in case it has become a normal file
}
;;
##### END COMMON TO MPLAYER AND MPG123 #####

*)
echo "$0: chosen player $player has not been configured.
Please check line 17 of the script" >&2
exit 1
;;
esac

##########################################################################
selfpid=$$
player_ok=true
error_exit() {
    echo "Script error: $1" >&2
    player_ok=false
    exit 1
}
trap 'cleanup' EXIT
trap 'echo " Exit script
Goodbye..."; exit' SIGHUP SIGINT
trap 'echo " Exit script
($player will be shut down)
Goodbye..."; keep_player=false; exit' SIGQUIT
trap 'error_exit "script terminated"' SIGTERM
trap 'error_exit "broken pipe"' SIGPIPE
trap 'recorder "${playing_url%.m3u}"' SIGTSTP

missing_commands=
for i in $required_commands
do
    hash $i || missing_commands+=" $i"
done
[[ $missing_commands ]] && error_exit "This script requires the following commands: $missing_commands
Please install the packages containing the missing commands
and rerun the script."

start_player

unset playing_name playing_url
while true
do
echo "Please enter keyword(s)"
read keyword
keyword="${keyword// /%20}" # escape spaces for url
results_sh=$( curl -s "http://www.shoutcast.com/Internet-Radio/$keyword" |awk '
BEGIN {
    RS="<div class=\"dirlist\">"
    FS=">"
}
NR < 2 {next}
{url = name = $2
sub(/^.*title=\"/,"",name)
sub(/\".*$/,"",name)
sub(/^.*href=\"/,"",url)
sub(/\".*$/,"",url)
print url,name }
' )
results_ra=$( curl -s "http://www.radionomy.com/en/search?q=$keyword" |awk '
BEGIN {
    RS="<h2 class=\"radio-title-list\"><a href=\"/en/radio/"
    FS="</a></h2>"
}
NR < 2 {next}
{url = name = $1
sub(/^.*>/,"",name)
sub(/\/index\".*$/,"",url)
url="http://listen.radionomy.com/" url ".m3u"
print url,name }
' )

if [[ $results_sh ]] && [[ $results_ra ]]
then
    results="$results_sh"$'\n'"$results_ra"
elif [[ $results_sh ]]
then
    echo "No results for $keyword on radionomy"
    results="$results_sh"
elif [[ $results_ra ]]
then
    echo "No results for $keyword on shoutcast"
    results="$results_ra"
else
    echo "Sorry, no results for $keyword"
    continue
fi

unset list
declare -A list # make associative array
while read -r url name # read in awk's output
do
    list["$name"]="$url"
done <<< "$results"

PS3='Please enter the number of your choice > '
while true
do
    menu=("${!list[@]}")
    [[ $playing_name && $playing_url ]] && menu+=("RECORD \"$playing_name\"")
    select station in "${menu[@]}" 'SEARCH AGAIN' QUIT
    do
        [[ $station = "RECORD \"$playing_name\"" ]] && {
            recorder "${playing_url%.m3u}" # streamripper won't take m3u urls
            break
        }
        [[ $station = 'SEARCH AGAIN' ]] && break 2
        [[ $station = QUIT ]] && { echo 'Goodbye...'; exit; }
        [[ $station ]] && {
# comment out next line if you don't use xsel
            printf '%s' "${list[$station]}" | xsel --input #--clipboard  # can paste url
            radioplay "${list[$station]}"
            playing_name=$station
            playing_url=${list[$station]}
            break
        }
    done
echo "
Station last chosen was \"$playing_name\" ( $playing_url )
"
done

done # closes loop started at line 178
exit
EDIT Added setsid to launch command for mpg123. At first I thought it wasn't necessary, but it seems for some versions at least, like mplayer, it needs that extra layer of insulation from the launching shell's signals. setsid makes a very forked off process - it even continues after you log out!
EDIT 121015 Couple of minor tweaks to UI and comments.
Attachments
shoutcast_radionomy_search.sh-v2.2.tar.gz
(2.7 KiB) Downloaded 237 times
Last edited by johnraff on Thu Sep 11, 2014 10:42 am, edited 4 times in total.
All code is one.

User avatar
johnraff
Sperminator
Posts: 199
Joined: Wed Oct 17, 2012 6:38 pm
Location: Japan
Contact:

Re: Shoutcast + radionomy search with recording option

Unread post by johnraff » Sat Oct 05, 2013 2:55 pm

btw once the cli player (mpg123 or mplayer) has been launched, and the script exited, the player can be controlled from anywhere, not just by running the script again. eg to close the player you can do 'echo quit>/tmp/radio_pipe' in a terminal, another script or from a keybinding. 'quit' works for both mpg123 and mplayer, but the other comands are different, depending.

Of course you don't need X to use the script with either of those players.
All code is one.

machinebacon
Baconator
Posts: 10253
Joined: Thu Sep 16, 2010 11:03 am
Location: Pfälzerwald
Contact:

Re: Shoutcast + radionomy search with recording option

Unread post by machinebacon » Sat Oct 05, 2013 5:18 pm

I like it a lot. Thanks Raffles :)
It's great, absolutely user-friendly, quick, light, and does what it says on the tin. I love it. Will definitely find its place on future releases, if you don't mind :)
..gnutella..

User avatar
johnraff
Sperminator
Posts: 199
Joined: Wed Oct 17, 2012 6:38 pm
Location: Japan
Contact:

Re: Shoutcast + radionomy search with recording option

Unread post by johnraff » Sat Oct 05, 2013 5:38 pm

^no of course not - I'd be honoured. Image

PS see the slight edit I'm just about to add, so please use the v2.1.tar.gz version.
All code is one.

User avatar
xaos52
The Good Doctor
Posts: 190
Joined: Thu Aug 15, 2013 11:59 am
Location: Eernegem, Belgium

Re: Shoutcast + radionomy search with recording option

Unread post by xaos52 » Tue Oct 08, 2013 7:29 pm

I am taking my hat off for you, John.
Nice script, could not find anything wrong with it.
Compact and featurefull.
Connected. Take this REPL, brother, and may it serve you well.

machinebacon
Baconator
Posts: 10253
Joined: Thu Sep 16, 2010 11:03 am
Location: Pfälzerwald
Contact:

Re: Shoutcast + radionomy search with recording option

Unread post by machinebacon » Tue Oct 08, 2013 7:45 pm

Little info: 'raffles' (how I call the script) is included in Neckbeard and Solyanka (and everything that follows). It's a lovely script, really.
..gnutella..

User avatar
johnraff
Sperminator
Posts: 199
Joined: Wed Oct 17, 2012 6:38 pm
Location: Japan
Contact:

Re: Shoutcast + radionomy search with recording option

Unread post by johnraff » Wed Oct 09, 2013 2:09 pm

Thank you both for those kind words!
All code is one.

User avatar
johnraff
Sperminator
Posts: 199
Joined: Wed Oct 17, 2012 6:38 pm
Location: Japan
Contact:

Re: Shoutcast + radionomy search with recording option

Unread post by johnraff » Tue Oct 15, 2013 1:41 pm

@mb Please note the minor changes in v2.2 if you might use the script in the future. Using Ctrl+\ to close the player no longer reports an error, and line nos. in comments corrected.
All code is one.

machinebacon
Baconator
Posts: 10253
Joined: Thu Sep 16, 2010 11:03 am
Location: Pfälzerwald
Contact:

Re: Shoutcast + radionomy search with recording option

Unread post by machinebacon » Tue Oct 15, 2013 1:46 pm

Thank you, John!
..gnutella..

User avatar
xaos52
The Good Doctor
Posts: 190
Joined: Thu Aug 15, 2013 11:59 am
Location: Eernegem, Belgium

Re: Shoutcast + radionomy search with recording option

Unread post by xaos52 » Tue Oct 15, 2013 2:46 pm

Suggestions for improvement:

Make it possible to run the script without user intervention, so that the script can be started from, for instance the openbox autostart file, by adding arguments to the script: player, keyword and station.

Add a menu entry for restarting the search without asking for the keyword if the keyword is known already.
My keyword is, and stays 'jazz', but I have to re-enter it every time.

Just a couple suggestions to keep you busy :)
No hurry...

Oh, and why punish the mpg123 user with checking that pipe - think of the cost of spawing a process -when it is really only necessary for the mplayer user?
Connected. Take this REPL, brother, and may it serve you well.

User avatar
johnraff
Sperminator
Posts: 199
Joined: Wed Oct 17, 2012 6:38 pm
Location: Japan
Contact:

Re: Shoutcast + radionomy search with recording option

Unread post by johnraff » Tue Oct 15, 2013 4:59 pm

Hi xaos, thanks for your input. To take your comments in order:

*) You mean, start the script with a preseeded search term, sent by an argument in the command? Should be easy enough. What do you mean by "station"? Surely the station isn't known till it's chosen from the search results?

*) Add menu item to redo same search - can do. Do you get different results each time you run the same search? With the kind of searches I run (eg "Bombay") it's the same every time so there's no point in searching again.

*) There is a need to check the pipe for either mpg123 or mplayer. If the pipe is broken the script will just hang as the echo command waits for something to read the other end of the FIFO. The system returns no error (I had hoped for a SIGPIPE), just hangs. This is very annoying for the user.

If for some reason the FIFO file is deleted, eg after running the script once, it will be recreated - no problem. However if the FIFO is deleted and a new one made (with 'mkfifo') the player will no longer be listening and the pipe will be broken. As the hanging is so annoying I went to some trouble to make sure it wouldn't happen, even though the chance is probably very small. The test is only run once, when the script is started, so not too punishing really?
All code is one.

User avatar
xaos52
The Good Doctor
Posts: 190
Joined: Thu Aug 15, 2013 11:59 am
Location: Eernegem, Belgium

Re: Shoutcast + radionomy search with recording option

Unread post by xaos52 » Tue Oct 15, 2013 5:40 pm

(1) Yes, something like:

Code: Select all

/usr/bin/raffles -pmpg123 -kjazz -sjazz41
to use player mpg123, keyword 'jazz' and station jazz41
with extra difficulty: don't start the player when the station is not available.
For the keyword 'jazz' the stations do seem to come and go a bit over time - perhaps depending on the strenght of the signal received by the server?

With 'station' I mean the names that you show in the menu, which correspond to an url via the associative array.

BTW:it is possible for the servers (shoutcast or radionomy) to have more than one link to a station - different url's for the same name.
Like this

Code: Select all

.++ result_sh='http://yp.shoutcast.com/sbin/tunein-station.pls?id=226817 Cruise FM probably the best black music ...
http://yp.shoutcast.com/sbin/tunein-station.pls?id=153866 Sushi Network Smooth
http://yp.shoutcast.com/sbin/tunein-station.pls?id=110913 jazz41
http://yp.shoutcast.com/sbin/tunein-station.pls?id=68293 Jazz4Jazz
http://yp.shoutcast.com/sbin/tunein-station.pls?id=195679 Jazz4Jazz
http://yp.shoutcast.com/sbin/tunein-station.pls?id=648483 1.FM - ADORE JAZZ RADIO
http://yp.shoutcast.com/sbin/tunein-station.pls?id=86433 Absolutely Smooth Jazz - SKY.FM - the wo...'
.
Jazz4Jazz is there twice with a different id. In that case, you will lose (drop from the menu) the duplicate(s) and only the last one will remain. And since the keys of an associative array come in random order, that will not correspond with the last one listed by the server.
Solution would be to test for the existence of a key before replacing it with the duplicate, and in case of duplicates add a suffix to the key.
(2) yes
(3) I commented out the pipe verification, and have no problems. Could be I never broke the pipe... will do that and keep the pieces anyway

Hope I have made myself somewhat clearer...
Connected. Take this REPL, brother, and may it serve you well.

User avatar
xaos52
The Good Doctor
Posts: 190
Joined: Thu Aug 15, 2013 11:59 am
Location: Eernegem, Belgium

Re: Shoutcast + radionomy search with recording option

Unread post by xaos52 » Sat Oct 19, 2013 11:23 am

I just pushed my version of the raffles script to my github repo for review.
It has the additional functionality I suggested earlier.

WARNING: I have only tested it with mpg123.

I have modified the script considerably. Made it more modular and thus better readable and easier to maintain.
I also tried to limit the use of strings as much as possible, piping results into an associated array as they are obtained by scanning the music servers.

I am not saying it is better than the original script. It is a different approach.

Remarks, objections, errors are welcomed.

You can find it here: https://github.com/xaosfiftytwo/bash-sc ... er/raffles.
Connected. Take this REPL, brother, and may it serve you well.

User avatar
johnraff
Sperminator
Posts: 199
Joined: Wed Oct 17, 2012 6:38 pm
Location: Japan
Contact:

Re: Shoutcast + radionomy search with recording option

Unread post by johnraff » Tue Oct 22, 2013 6:05 am

Hi xaos, thanks as always for your input.

I don't have time right now to make any detailed comments, but your version of the script does have some good improvements - like putting more actions into functions. I also liked your idea of a regex station search, though by default you're only choosing from 10-15 stations so there isn't much scope. (To get the full choice of stations offered on page 2, 3... of the Shoutcast search would need javascript, which is extremely difficult to implement in a shell script.)

I don't necessarily agree with your approach of putting so much into command-line options, and I still stand by the necessity of checking the fifo and waiting for the player to start up before trying to send it a command. I added that code because on my machine those issues actually came up! Maybe this only matters on older boxes. (A 0.1 second sleep is no way enough over here, but in an interactive situation the user will probably be taking a few seconds to enter their search, giving plenty of time for the system to catch up.)

Anyway, I'll be back in a day or two...
All code is one.

machinebacon
Baconator
Posts: 10253
Joined: Thu Sep 16, 2010 11:03 am
Location: Pfälzerwald
Contact:

Re: Shoutcast + radionomy search with recording option

Unread post by machinebacon » Tue Oct 22, 2013 6:09 am

I like xaos version for the options, and raffles' version for the 'start & play' mentality. It's like Les Paul vs. Chet Atkins :D
..gnutella..

User avatar
johnraff
Sperminator
Posts: 199
Joined: Wed Oct 17, 2012 6:38 pm
Location: Japan
Contact:

Re: Shoutcast + radionomy search with recording option

Unread post by johnraff » Thu Sep 11, 2014 10:37 am

Shoutcast changed their interface, breaking the script's search function. However, a fix was not too hard - the query goes via a POST request to http://www.shoutcast.com/Search/UpdateSearch and fetches back a JSON file instead of html. The good news is that you get a lot more results - maybe all of them. The shoutcast and radionomy searches are now functions so adding other directories would be easier. There is also now a --help/-h option.

I've just had another look at Dr Xaos' comments and intend to add two of his suggestions - redoing the same search and dealing with multiple entries of the same station - soon, but haven't yet. Meanwhile, this version seems to be working OK. Please report any problems! Image

Code: Select all

#!/bin/bash
# shoutcast_radionomy_search.sh
# search shoutcast and radionomy,
# send url to radiotray, mpg123, mplayer or another player
# send url to streamripper to record
#
# version 3.1
#
# needs curl, [radiotray, dbus | mpg123 | mplayer], streamripper, [xsel], [perl]
# xsel enables pasting from the X selection (to a config file etc.)
# Comment out line 288 "printf '%s'..." if you don't use it.
# perl is used to urlencode the query.
# Comment out line 246 and uncomment line 245 to escape spaces only
# if your system doesn't have perl.
#
# KEYBOARD SHORTCUTS:
# Ctrl+C to exit normally
# Ctrl+\ to terminate and close player
# Ctrl+Z to start recording current station (handles SIGTSTP)

##### choose from radiotray, mpg123 or mplayer #####
#player=radiotray
player=mpg123
#player=mplayer

# Set this to something other than 'true'
# to have audio player exit with script.
# Otherwise player will continue till closed separately.
# Even with 'keep_player=true', if script is stopped with Ctrl+\
# then player will exit too.
keep_player=true

##### code to record a radio stream (url is $1) in a new terminal #####
# Add your own options to streamripper's command line,
# edit ~/.config/streamripper/streamripper.ini,
# change urxvt to another terminal
# or use a different command altogether.
recorder() {
    ( setsid urxvt -e streamripper "$1" >/dev/null 2>&1 & )
}

# where to put player control fifo
# (radiotray doesn't use this)
rpipe=/tmp/radio_pipe

HELP="This is an interactive script to query the Shoutcast and Radionomy listings,
put the results in a menu,
and load the chosen radio station in radiotray, mpg123 or mplayer.
There is also an option to record with streamripper.

If you exit the script and leave mpg123 or mplayer running,
you can close either of them with the command:
echo quit >$rpipe

KEYBOARD SHORTCUTS:
Ctrl+C to exit normally
Ctrl+\ to terminate and close player
Ctrl+Z to start recording current station (handles SIGTSTP)"

##########################################################################

case $1 in
--help|-h)
    echo "$HELP"
    exit
    ;;
esac

case $player in

##### RADIOTRAY SETTINGS #####
radiotray)
required_commands='curl streamripper radiotray'
start_player() {
    if pgrep radiotray >/dev/null
    then
        echo "$player is already running"
    else
        ( setsid radiotray >/dev/null 2>&1 & )
    fi
}
radioplay() {
    radiotray "$1"
}
cleanup() { # run just before exit
    [[ $player_ok = true ]] && [[ $keep_player = true ]] && {
        echo "$player will continue to play.
You can control it from the system tray icon
or run the script again to choose another station."
        sleep 4
        return
    }
    pkill radiotray && echo "Closed radiotray."
    sleep 4
}
;;
##### END RADIOTRAY #####

##### MPLAYER SETTINGS #####
mplayer)
required_commands='curl streamripper mplayer'
player_regex="^mplayer .*-input file=$rpipe"
launch_player() {
    [[ -p $rpipe ]] || { mkfifo "$rpipe" || error_exit "cannot make fifo $rpipe"; }
    ( setsid sh -c "mplayer -really-quiet -idle -slave -input file=$rpipe; rm -f $rpipe;" >/dev/null 2>&1 & )
    sleep 4 & launching_player=$!
}
load_url() {
    echo "loadlist $1" >"$rpipe"
}
;;&
##### END MPLAYER #####

##### MPG123 SETTINGS #####
mpg123)
required_commands='curl streamripper mpg123'
player_regex="^mpg123 .*--fifo $rpipe"
launch_player() { # mpg123 will make fifo if necessary
    ( setsid sh -c "mpg123 --remote --fifo $rpipe; rm -f $rpipe;" >/dev/null 2>&1 & )
    (sleep 2; echo 'silence' >"$rpipe") & launching_player=$!
}
load_url() {
    echo "loadlist 1 $1" >"$rpipe"
}
;;&
##### END MPG123 #####

##### COMMON TO MPLAYER AND MPG123 #####
mplayer|mpg123)
start_player() {
    if pgrep -f "$player_regex" >/dev/null
    then
        echo "$player is already running"
        [[ -p $rpipe ]] || error_exit "fifo missing $rpipe"
        (:>"$rpipe") & test_pipe=$!
        (sleep 2; kill $test_pipe 2>/dev/null && kill -s SIGPIPE $selfpid) &
    else
        launch_player
    fi
}
radioplay() {
    wait $launching_player
    [[ -p $rpipe ]] || error_exit "fifo missing $rpipe"
    pgrep -f "$player_regex" >/dev/null || error_exit "$player not running"
    load_url "$1"
}
cleanup() { # run just before exit
    [[ -p $rpipe ]] || { player_ok=false; echo "Script error: fifo $rpipe does not exist." >&2 ;}
    pgrep -f "$player_regex" >/dev/null || { player_ok=false; echo "Script error: $player not running" >&2 ;}
    [[ $player_ok = true ]] && {
        [[ $keep_player = true ]] && {
            echo "$player will continue to play.
You can stop it with the command:
echo quit >$rpipe
or run the script again to choose another station."
            sleep 4
            return
        }
        echo "closing $player..."
        echo 'quit' >"$rpipe" # try to close player nicely
        sleep 2 # time for player to quit
    }
    pkill -f "$player_regex" && echo "$player close forced."
    echo "removing $rpipe"
    rm -f "$rpipe" # in case it has become a normal file
}
;;
##### END COMMON TO MPLAYER AND MPG123 #####

*)
echo "$0: chosen player $player has not been configured.
Please check line 17 of the script" >&2
exit 1
;;
esac

##########################################################################
selfpid=$$
player_ok=true
error_exit() {
    echo "Script error: $1" >&2
    player_ok=false
    exit 1
}
trap 'cleanup' EXIT
trap 'echo " Exit script
Goodbye..."; exit' SIGHUP SIGINT
trap 'echo " Exit script
($player will be shut down)
Goodbye..."; keep_player=false; exit' SIGQUIT
trap 'error_exit "script terminated"' SIGTERM
trap 'error_exit "broken pipe"' SIGPIPE
trap 'recorder "${playing_url%.m3u}"' SIGTSTP

missing_commands=
for i in $required_commands
do
    hash $i || missing_commands+=" $i"
done
[[ $missing_commands ]] && error_exit "This script requires the following commands: $missing_commands
Please install the packages containing the missing commands
and rerun the script."

query_shoutcast() {
    curl -s --data "query=$1" "http://www.shoutcast.com/Search/UpdateSearch" | awk '
    BEGIN {
        RS="},{"
    }
    {
        url = name = $0
        if($0=="[]") {exit}
        sub(/^.*\"ID\":/,"",url)
        sub(/,.*$/,"",url)
        url = "http://yp.shoutcast.com/sbin/tunein-station.pls?id=" url
        sub(/^.*\"Name\":\"/,"",name)
        sub(/\".*$/,"",name)
        print url,name
    }
    '
}
query_radionomy() {
    curl -sL "http://www.radionomy.com/en/search?q=$1" |awk '
    BEGIN {
        RS="<h2 class=\"radio-title-list\"><a href=\"/en/radio/"
        FS="</a></h2>"
    }
    NR < 2 {next}
    {
        url = name = $1
        sub(/^.*>/,"",name)
        sub(/\/index\".*$/,"",url)
        url="http://listen.radionomy.com/" url ".m3u"
        print url,name
    }
    '
}

start_player

unset playing_name playing_url
while true
do
echo "Please enter keyword(s)"
read keyword
#keyword_esc="${keyword// /%20}" # escape spaces for url
keyword_esc=$(perl -MURI::Escape -e 'print uri_escape($ARGV[0]);' "$keyword")
results_sh=$( query_shoutcast "$keyword_esc" )
results_ra=$( query_radionomy "$keyword_esc" )

if [[ $results_sh ]] && [[ $results_ra ]]
then
    results="$results_sh"$'\n'"$results_ra"
elif [[ $results_sh ]]
then
    echo "No results for $keyword on radionomy"
    results="$results_sh"
elif [[ $results_ra ]]
then
    echo "No results for $keyword on shoutcast"
    results="$results_ra"
else
    echo "Sorry, no results for $keyword"
    continue
fi

unset list
declare -A list # make associative array
while read -r url name # read in awk's output
do
    list["$name"]="$url"
done <<< "$results"

PS3='Please enter the number of your choice > '
while true
do
    menu=("${!list[@]}")
    [[ $playing_name && $playing_url ]] && menu+=("RECORD \"$playing_name\"")
    select station in "${menu[@]}" 'SEARCH AGAIN' QUIT
    do
        [[ $station = "RECORD \"$playing_name\"" ]] && {
            recorder "${playing_url%.m3u}" # streamripper won't take m3u urls
            break
        }
        [[ $station = 'SEARCH AGAIN' ]] && break 2
        [[ $station = QUIT ]] && { echo 'Goodbye...'; exit; }
        [[ $station ]] && {
# comment out next line if you don't use xsel
            printf '%s' "${list[$station]}" | xsel --input #--clipboard  # can paste url
            radioplay "${list[$station]}"
            playing_name=$station
            playing_url=${list[$station]}
            break
        }
    done
echo "
Station last chosen was \"$playing_name\" ( $playing_url )
"
done # closes loop started at line 274

done # closes loop started at line 241

exit
Attachments
shoutcast_radionomy_search.sh-v3.1.tar.gz
(3.07 KiB) Downloaded 217 times
Last edited by johnraff on Fri Oct 03, 2014 5:54 am, edited 1 time in total.
All code is one.

machinebacon
Baconator
Posts: 10253
Joined: Thu Sep 16, 2010 11:03 am
Location: Pfälzerwald
Contact:

Re: Shoutcast + radionomy search with recording option

Unread post by machinebacon » Thu Sep 11, 2014 11:23 am

Thank you John! It works well.

If somebody gets this message:

Code: Select all

Can't locate URI/Escape.pm in @INC (you may need to install the URI::Escape module) (@INC contains: /etc/perl /usr/local/lib/perl/5.18.2 /usr/local/share/perl/5.18.2 /usr/lib/perl5 /usr/share/perl5 /usr/lib/perl/5.18 /usr/share/perl/5.18 /usr/local/lib/site_perl .).
where 5.18 might also be 5.20 or any other number for the perl version, just install the liburi-perl package.
..gnutella..

Post Reply