Loading twitter status...

XMonline in Ruby!

dnite / 25.Mar.2007

Who wants to log in and go through the hassle of navigating a website when you can type a simple command to get the same thing? Not me! I usually use the MediaPlayerConnectivity Extension which lets me launch streaming media outside of the browser, but I wanted to go a step further and remove the browser completely from my XM Radio listening when I’m outside of my car. I created a nice little Ruby script for doing just that. With the wonderful Mechanize gem it’s quite easy to traverse websites without every looking at them.

The first thing (I mean, you obviously need Ruby, so go get that if you don’t have it yet) your going to want to do, is install the mechanize gem.

gem install mechanize -y

If you get permission errors, you probably need to run sudo gem install mechanize -y instead.

After that, you can copy the code below and paste it into your own xmradio.rb file. It’s pretty easy to use too. The first time you run the script, it will ask you a few questions about your login information and what type of media player you want to use. After this, you won’t see that message again unless you delete your ~/.xmradio file. You’ll probably need to make sure to chmod a+x xmradio.rb and you may want to throw the file somewhere like /usr/bin or somewhere that’s in your PATH so that you can execute it easily. The rest is easy!

xmradio.rb 202

The above command will launch the script, connect to listen.xmradio.com, log you in and start your preferred player. The number you specify after the xmradio.rb is the channel you wish to listen to. If you don’t specify a channel, it will play whatever default channel you chose when you first configured. If you ever want to reconfigure the script, either edit ~/.xmradio or just delete it and the next time you launch xmradio.rb, you’ll be asked the same questions again.

In the near future, I think it will be cool to add a couple of features, such as the ability to change channels while the script is running, or maybe the ability to save the stream with certain players. The recommended player is mplayer right now, but I also tested it a little bit with xine. Code is after the jump! Go get it!

#!/usr/bin/env ruby
# Filename : xmradio.rm
# Author   : Steve Ehrenberg
# URL      : http://blog.dnite.org

# listen to xmonline from your command line
#
# Make sure you install the mechanize gem. This is the only gem
# required by this script.
# 
# configuration is stored in ~/.xmradio
#
# This file is a yaml file and needs to stay correctly formatted.
# If you edit the file by hand, don't add any extra spaces or tabs.
# You can always just delete ~/.xmradio and this script will prompt
# you for all the information again.
#
# usage: xmradio [channel]

require "fileutils" 
require "net/http" 
require "uri" 
require 'yaml'
require "rubygems" 
require "mechanize" 

def main
  # load and parse configuration
  config = load_config
  config['channel'] = ARGV[0] unless ARGV[0] == '' || ARGV[0].nil?

  # log in to your xmonline account with Mechanize.
  agent = WWW::Mechanize.new
  page = agent.get 'http://listen.xmradio.com'
  login_form = page.form('login')
  login_form.user_id = config['email']
  login_form.pword = config['password']
  page = agent.submit(login_form)

  # exit if the email and password were invalid.
  exit_now 'You entered a wrong email and password.' if page.body =~ /Invalid email and\/or password\./

  # Display the user's name just to show we care.
  puts '', page.search("//td[@class='tBlack']//span[@class='tDkRed']").innerHTML, ''

  # We'll find the stream URL below.
  page = agent.get "http://player.xmradio.com/player/2ft/playMedia.jsp?ch=#{config['channel']}&speed=#{config['quality']}" 
  stream = page.at("//object[@id='xmmediaplayer']//param[@name='FileName']")['value'].gsub(/\&/, "\\\\&")
  puts "Streaming from #{stream}...", ''

  # Start a new thread with the player in it
  player = Thread.new(config['player']) do |player|
    `#{player} #{stream} 2> /dev/null`
  end

  # Display the initial information
  puts 'Now Listening To...'
  print "Artist  ::\nTitle   ::\nAlbum   ::\nChannel ::\n" 

  # Loop while the script stays running and keep fetching song info
  while true
    info = now_playing_info(
      agent.get("http://player.xmradio.com/padData/pad_data_servlet.jsp?channel=#{config['channel']}") )
    channel = "#{info['ch_name']} | #{info['ch_num']}" 
    print "#{ansi_esc channel} #{ansi_esc info['album']} #{ansi_esc info['title']} #{ansi_esc info['artist']} #{reset_cursor}" 
    $stdout.sync = true
    sleep 2
    trap("SIGINT") { exit_now 'XM online stopped...' }
  end
end

# adds the ansi escape sequences to move up 1 line, go to character 12, 
# erase the line after character 12, and turn on and off bright.
def ansi_esc(string)
  "\e[A\e[12G\e[K\e[1m#{string}\e[0m" 
end

# turns off bright, moves the cursor down 4 lines and jumps to beginning of line.
def reset_cursor
  "\e[0m\e[4B\e[1G" 
end

# Parse the page and throw the information into a hash
def now_playing_info(page)
  info = {
    'title'   => page.at('songtitle').innerHTML,
    'artist'  => page.at('artist').innerHTML,
    'album'   => page.at('album').innerHTML,
    'ch_num'  => page.at('channelnumber').innerHTML,
    'ch_name' => page.at('channelname').innerHTML
  }
end

# Load the config from ~/.xmradio or start creation of a new config.
def load_config
  new_config unless FileTest.exist?("#{ENV['HOME']}/.xmradio")
  config = open("#{ENV['HOME']}/.xmradio") { |f| YAML.load(f) }
  rescue
    exit_now 'Problem loading your config..'
end

# Save the configuration to ~/.xmradio
def save_config(config)
  config.to_yaml
  return true if open("#{ENV['HOME']}/.xmradio", 'w') { |f| YAML.dump(config, f) }
  false
end

# A very user friendly configuration method. Asks the user all the
# questions it needs very nicely and sends the information over to
# be saved.
def new_config
  puts 'Looks like this is your first time running this script. I need','a few small bits of information to get your started.','','You need to have a valid account for listen.xmradio.com to use','this. What\'s the email address you use to login? [REQUIRED]'
  email = $stdin.gets.chomp
  exit_now 'You have to enter an email address to continue' if email == ''
  puts '', 'Ok, how about the password? [REQUIRED]'
  password = $stdin.gets.chomp
  exit_now 'You need to enter your password to continue' if password == ''
  puts '', 'Great. XMonline allows you to listen to either a low','quality stream, or high. Which one would you like? high/low? [default: high]'
  quality = $stdin.gets.chomp
  quality = quality =~ /^l/i ? 'low' : 'high'
  puts '', 'Almost done. What media player would you like to use to','stream XMonline? [mplayer/xine/xmms/alsaplayer] [default: mplayer]'
  player = $stdin.gets.chomp
  exit_now "You need to specify a valid player.\nSupported played are mplayer, xine, xmms and alsaplayer." unless player =~ /^m/i || player =~ /^xi/i || player =~ /^xm/i || player =~ /^a/i || player == ''
  player = 'mplayer -msglevel all=-1 -playlist' if player =~ /^m/i || player == ''
  player = 'xine -I --no-logo' if player =~ /^xi/i
  player = 'xmms' if player =~ /^xm/i
  player = 'alsaplayer' if player =~ /^a/i
  puts '', 'Last question. This script allows you to use a default channel','that will always start when you don\'t enter a different channel','at the command line. What\'s your favorite XM channel? [default: 202]'
  channel = $stdin.gets.chomp
  channel = '202' if channel == ''
  config = {'email' => email, 'password' => password, 'quality' => quality, 'player' => player, 'channel' => channel}
  return puts('','All done with your setup! Enjoy XMonline!') if save_config config
  exit_now 'There was a problem saving your config.'
end

# Display an error message and exit.
def exit_now(message="error!")
  puts '',message,''
  exit
end

main

Leave comments with any sort of problems or suggestions you might have.

3 Comments

Steve, Any way to run this thing in the background with no text output other than errors (maybe to a separate log) so you could run it as an rc.d script? I’m running the ruby script on the slackware box that controls my phone system so it’s on our music on hold (we have a business account) but it won’t run in the background. I tried cutting all the output from the script but it just logs in, connects and then stops.

Helpful script, thanks. For those interested, mplayer also lets you save the stream to a file, so edit the ‘player’ line in ~/.xmradio to something like this (all on one line):

player: mplayer -msglevel all=-1 -dumpstream -dumpfile /home/jim/tmp/xmradio_stream -vc dummy -vo null -playlist

(Which I found here:) http://grimthing.com/archives/2004/05/20/recording-streaming-audio-with-mplayer/

Thanks, Steve. This is just what I need; works like a charm. (And thanks for the suggestion too, Jim.)

JJ

Leave a Comment

back to top

micro theme by seaofclouds, edited by me, and powered with Mephisto