Integrate Sirius

From MythTV Official Wiki
Revision as of 08:11, 29 November 2009 by Newacct (talk | contribs)

Jump to: navigation, search

This article is going to give you all of the info you need in order to pipe your online Sirius Satellite Radio account directly through your MythTV system. The first draft of this article is based around a Fedora Core 6 setup but it should be EASILY adapted to any distro. Please, READ THE ENTIRE ARTICLE CAREFULLY all the way to the end before you start anything.


Important.png Note: this is NOT a plug-in and is basically a hack but when completed it looks just like a plug-in.

Important.png Note: article has been significantly updated for new version of Sipie


Article from 10,000 feet

What this article is...

This article IS about:

  • Setting up MythTV to play streams from Sirius Radio Online
  • How-To use programs that other people wrote (and are not affiliated with MythTV)
  • How-To integrate everything with the default MythTV menu theme

What this article is not...

This article IS NOT:

  • How-To get free access to the streams (although a Sirius online guest account will work fine)
  • How-To perfectly integrate with your particular MythTV GUI (we only cover the default menu theme)
  • A Sirius Radio Plug-in for MythTV
  • An audio stream capture how-to, but you can check out Eli's example recorder right here.

What you need to have

Setup

  • You only need to have an account setup at Sirius.com.

Installed

Required:

Optional:

Introducing SIPIE

Pronounced SY PIE, like sirius python

sipie is a command line player for sirius on-line Internet streaming. It requires a login to sirius's streaming, both
guest and subscriber logins are supported. Streams are entered on the command line or are asked for. TAB twice will 
list all streams, type part of a stream and hit TAB to complete it.  You can change streams without restarting 
(and thus not re-authenticating) by hitting CTRL-C while a stream is playing.

sipie is written in python, and uses mplayer for the streams. Beautiful Soup is the only extra library you'll need. 
See the README on how to get it.

Eli Criffield

Let's get started!

Installing Sipie Requirements

I (TwoOneSix) have recently written a player for sipie (mythPlayer) that is for usage in MythTV, so we'll need to get the version from SVN instead of the one listed in the README file (details below). Remember that ONLY when you get sipie working OUTSIDE of MythTV, will it work INSIDE of MythTV. Also, make sure when you do your initial sipie setup that you do it under your mythtv account (or whatever account runs the front end).

VIA YUM

You should only need the extras repository on FC6 to install... This is based on my install.

Make sure this is done in your GUI or you won't be able to see the captcha

Remember that wxPython, notify-python, and dbus-x11 are not required, but I suggest making sure it's installed... also I added subversion to the command to save a step later in the process.

# yum install mplayer python python-setuptools wxPython notify-python dbus-x11 subversion

Dependencies Resolved

=============================================================================
 Package                 Arch       Version          Repository        Size
=============================================================================
Installing:
 wxPython                i386       2.6.3.2-2.fc6    extras            7.4 M
Installing for dependencies:
 wxGTK                   i386       2.6.3-2.6.3.2.3.fc6  extras            3.5 M
 wxGTK-gl                i386       2.6.3-2.6.3.2.3.fc6  extras             28 k

Transaction Summary
=============================================================================
Install      3 Package(s)
Update       0 Package(s)
Remove       0 Package(s)

Total download size: 11 M
<snip>
Completed!

FEDORA 8 USERS

Python tools are compiled under devel... here is your install command:

# yum install mplayer python python-setuptools-devel wxPython notify-python dbus-x11 subversion

END FEDORA 8 USERS

VIA Ubuntu

This is from the README excluding the easy_install portion at the end!

Make sure mutliverse is enabled, if not enable it
    grep multiverse /etc/apt/sources.list

if nothing returns go to System -> Administration -> Synaptic Package Manager
        Settings
        -> Repositories
        -> Ubuntu Software
        -> Software restricted by copyright or legal issues (multiverse) 
            CHECK THAT

then 
    # python-wxgtk2.6 is option but it currently works better then the pure
    # gtk interface
    apt-get install mplayer python-setuptools python-wxgtk2.6 subversion

Note: Should you already have python-setuptools, you may have to remove first with:

apt-get remove python-setuptools

Installing Beautiful Soup

Current version of BeautifulSoup will now generate a stack trace. Appears to be reported as a bug in BeautifulSoup [1] Sirius.com doesn't put quotes around one style tag and BeautifulSoup crashes.

3.0.7 still works for Sipie so use the following.:

# cd ~
# wget http://www.crummy.com/software/BeautifulSoup/download/3.x/BeautifulSoup-3.0.7.tar.gz
# tar xvf BeautifulSoup-3.0.7.tar.gz
# cd BeautifulSoup-3.0.7
# sudo easy_intall .

Installing Sipie

Since the Sipie player we want is currently in SVN, we have to checkout the code and easy_install from a local copy. This also allows us to tweak the file settings prior to running the setup. Here we go... You need to have subversion installed (see above).

First, we need to find (or make) a place to put the code (I use /usr/src for my sources) and download the code:

# cd /usr/src

Now we'll pull down the latest and greatest version of sipie from SVN:

# svn co https://sipie.svn.sourceforge.net/svnroot/sipie sipie

OPTIONAL SETUP

If you're OK with seeing the standard pop-up and think that leaving the pop-up on the screen for 2 minutes is OK, then simply skip this step.

# vi /usr/src/sipie/contrib/mythPlayer.py

Edit the following lines:

26: tryPopups = True # Set as False to disable pop-ups
27: notifySeconds = 120000 # This is a MAX time (in ms) to show a single song name. Default: 120000 (2 min)

END OPTIONAL SETUP

OK, now we need to easy_install sipie for use. You should see similar (but not exactly the same) output as listed below, if you see a stack trace or a "code dump" make sure you have python-setuptools (Fedora 8: python-setuptools-devel) installed correctly.

Move into the source directory:

# cd /usr/src/sipie

Now install sipie:

# easy_install .

Expected Output:

Processing .
Running setup.py -q bdist_egg --dist-dir /usr/src/sipie/egg-dist-tmp-e9BrUR
Sipie 0.1196144357 is already the active version in easy-install.pth
Installing sipie.py script to /usr/bin
Installing cliSipie script to /usr/bin
Installing gtkSipie script to /usr/bin
Installing wxSipie script to /usr/bin

Installed /usr/lib/python2.5/site-packages/Sipie-0.1196144357-py2.5.egg
Processing dependencies for Sipie==0.1196144357
Finished processing dependencies for Sipie==0.1196144357

Now, we need to move my contributed player to a better place. In this example, I am moving the file to /usr/bin because that's the same place the other files were moved (see the above output: ... scipt to /usr/bin).

# cp /usr/src/sipie/contrib/mythPlayer.py /usr/bin

Alright, now sipie is installed and ready to go!

Configuring Sipie

You need to ensure that the configuration is done under the same user account that MythTV runs under, in my case it's the mythtv user, simple enough.

$ cd ~
$ sipie.py

username and a crypted password will be stored in ~/.sipie/config
no plain text passwords are stored
if you want to change your password remove ~/.sipie/config
then run sipie and it'll ask you for username and password again

Enter username: MySiriusUsername
Enter password:

Login Type, type guest or subscriber
Enter login type: subscriber
  • Enter the captcha in the popup window or if sipie can't find wxGTK you will have to open the image file it tells you to open and enter the captcha at the command prompt.
  • You should now be prompted to select a station, if you have a GUI, pick one from the drop-down and chose "play"... you should hear the stream...

Important.png Note: it doesn't matter if you see a GUI app window... in a minute, we'll force it to run with no user input. We're currently only focused on hearing the stream.

  • Stop the stream VIA the "stop" button on the GUI or Ctrl+C twice on the command line.

Let's also quickly test my mythPlayer.py code.

$ mythPlayer.py octane

You should hear the stream and see the pop-up window along with the song info back on the command line. Great! Now hit ctrl+c and it should kill the stream and exit out to the command line.

Let's hack it together

OK, now we are going to write a simple shell script that we will use to make MythTV happy while talking to sipie.

Create sipie_myth

We need to create a single executable file so find a nice place you can execute it from. Personally, I use /usr/bin. You can replace vi with gedit or kedit if you prefer a GUI text editor.

# vi /usr/bin/sipie_myth

Now, paste in the following code:

Script.png /usr/bin/sipie_myth

#!/bin/bash
# Script By: Josh <TwoOneSix AT thatclothingco DOT com>
# Used to control sipie from MythTV menu

pid_file=/tmp/sipie_myth.pid

die_mythSipie()
{
	# Kill off the child processes first...
	pkill -P `cat $pid_file`
	# Now kill off the parent process...
	kill `cat $pid_file`
	rm -f $pid_file
	return 0
}

play_mythSipie()
{
	echo "I'm about to play:" $1
	/usr/bin/mythPlayer.py $1 & # you may have to tweak this line if you copied the player to another location
	echo $! > $pid_file
	return 0
}

# If we see the kill command, kill the old processes/streams
if [ "$1" = "kill" ]; then
	die_mythSipie

# If we see the play command, kill any existing streams then play the stream
elif [ "$1" = "play" ] && [ "$2" != "" ]; then
        if [ ! -e $pid_file ] ; then 
		play_mythSipie $2
	else
		die_mythSipie
		# Just to be sure it's gone...
		if [ ! -e $pid_file ] ; then
			play_mythSipie $2
		else
			echo 1>&2 '******FATAL ERROR STARTING STREAM PLAY******'
			exit 1
		fi
        fi
	exit 0
else
         echo 1>&2 'Usage: ' $0 '{kill | play} stream (stream only required on play)'
         exit 127
fi

Now you will want to make the file executable:

# chmod a+x /usr/bin/sipie_myth

Test scripts

Now, as your mythtv user, pull up a command window and run the command we just "made" with the stream to test.

  • Usage: /usr/bin/sipie_myth {kill | play} stream (stream only required on play)
$ sipie_myth play octane

Do you hear the stream without having to enter a single thing in the command window? Do you see the song names in the command window and in the pop-up notifier (if you left tryPopus=True)? That's good, now use this to kill the stream:

$ sipie_myth kill

Did you have to enter information? That's bad. Make sure you configured sipie as your mythtv user and try again.

Did the kill process not end the stream? This is caused by the mythPlayer.py not cleaning up properly on exit. Modify /usr/bin/mythPlayer.py with the following

#!/usr/bin/python
# -*- coding: utf-8 -*-
# Josh Zeno <TwoOneSix@thatclothingco.com>
# mythPlayer.py: This is a sipie player that it tweaked specifically for MythTV usage
#               and is designed to be run as a background process that is managed by the script
#               sipie_myth (found at: http://www.mythtv.org/wiki/index.php/Integrate_Sirius)
# Licensed under GPLv2 See: http://www.gnu.org/licenses/gpl.txt
####################################
# What makes this player different?
# --You must send the stream name as an argument: i.e. mythSipie octane
# --Is able to run as a "friendly" background process using sipie_myth as mentioned above
# --Gives the user the option to disable pynotify popups and set the popup timeout
# --Adds the sipie icon to the pynotify pop-up window
# --Does not keep your Sirius session open so you have to be careful how often you change streams
import atexit
import sys,signal,os

def mythPlayer():
    from Sipie.Config import Config
    from Sipie.Player import Player
    from Sipie import StreamHandler
    import time

    global sipie

    tryPopups = True # Set as False to disable pop-ups
    notifySeconds = 120000 # This is a MAX time (in ms) to show a single song name. Default: 120000 (2 min)

    if tryPopups:
        try:
            import pynotify
            from pkg_resources import Requirement, resource_filename
            pynotify.init("mythSipie")
        except:
            print "WARN: Not found! Is pynotify installed?"
            tryPopups = False

    def getIcon():
        icon_file = resource_filename(Requirement.parse("Sipie"),"Sipie/data/sipie.png")
        if os.path.isfile(icon_file):
            return icon_file

    configdir = '%s/.sipie'%os.environ['HOME']
    streamHandler = StreamHandler.mplayerHandler('/usr/bin/mplayer') # haven't tried other handlers yet
    config = Config(configdir)
    sipie = Player(config.items())
    sipie.setPlayer(streamHandler)

    FirstLoop = True
    while True:
        if len(sys.argv) == 2 and FirstLoop:
            stream = sys.argv[1].lower()
        elif "sipie" not in sys.argv[0].lower() and FirstLoop:
            stream = os.path.basename(sys.argv[0])

        try:
            sipie.setStream(stream)
        except :
            FirstLoop = False
            print "******FATAL: Invalid Stream!******"
            sipie.close()
            sys.exit()

        sipie.play()

        # We have to handle pynotify differently from the command line to keep it from crashing the player
        while True:
            playing = sipie.nowPlaying()
            if playing['new']:
                if tryPopups:
                    nPop = pynotify.Notification(playing['stream'], playing['playing'], getIcon())
                    nPop.set_timeout(notifySeconds)
                    nPop.show()
                print playing['logfmt']

            try:
                time.sleep(30)
            except KeyboardInterrupt:
                print "Thanks for playing!"
                sipie.close()
                sys.exit()
            FirstLoop = False

def handler(signum, frame):
        sipie.close()
        sys.exit()

signal.signal(signal.SIGTERM, handler)

if __name__ == '__main__':
    mythPlayer()

Setting up the MythTV GUI

At this point, you should be able to listen to (and kill) streams from the command line with no user intervention... that's 1/2 the battle. Now, we are going to hack a couple of buttons onto the default menu theme.


Important.png Note: If you are currently running .20+ chances are that you're using the default menu theme. No, not the actual MythTV theme, just the menu theme. To check out which one you're using do this in Myth: Utilities / Setup (or Setup) -> Setup -> Appearance -> and at the bottom of the first options page you will see the menu themes selection box, we will be hacking into "Default" so make sure it's set to that one. If you use classic or DVR you can change some of the commands below so that your work is being done on that particular theme (just add /themes/classic or /themes/DVR respectively to the "cd" commands below).

Backup the default theme

# cd /usr/share/mythtv
# tar cfvz default_backup.tar.gz *.xml

I use the -v option because it's an easy way to verify (VIA verbose) what files were put into the tar ball.

Hack the default theme

I'm only going to show you the 2 required changes to get this hack to work.


Important.png Note: If you're running a MythDora release these files might be located in a different directory.

Now, add the following to mainmenu.xml file.

# cd /usr/share/mythtv
# vi mainmenu.xml
   <button>
      <type>MUSIC</type>
      <text>Play Sirius Radio</text>
      <action>MENU siriusmenu.xml</action>
   </button>

Then, create the siriusmenu.xml file (be sure to add all of the streams you want access to):

# vi siriusmenu.xml
<mythmenu name="SIRIUS">

   <button>
      <type>MUSIC</type>
      <text>Howard 100</text>
      <action>EXEC /usr/bin/sipie_myth play howardstern100</action>
   </button>

   <button>
      <type>MUSIC</type>
      <text>Octane</text>
      <action>EXEC /usr/bin/sipie_myth play octane</action>
   </button>

   <button>
      <type>MUSIC</type>
      <text>Alt Nation</text>
      <action>EXEC /usr/bin/sipie_myth play altnation</action>
   </button>

   <button>
      <type>MUSIC</type>
      <text>Super Shuffle</text>
      <action>EXEC /usr/bin/sipie_myth play supershuffle</action>
   </button>

   <button>
      <type>MUSIC</type>
      <text>Big 80s</text>
      <action>EXEC /usr/bin/sipie_myth play big80s</action>
   </button>

   <button>
      <type>MUSIC</type>
      <text>Lithium</text>
      <action>EXEC /usr/bin/sipie_myth play 90salternative</action>
   </button>

   <button>
      <type>MUSIC</type>
      <text>Sirius Hits 1</text>
      <action>EXEC /usr/bin/sipie_myth play siriushits1</action>
   </button>

   <button>
      <type>MUSIC</type>
      <text>Coffee House</text>
      <action>EXEC /usr/bin/sipie_myth play coffeehouse</action>
   </button>

   <button>
      <type>MUSIC</type>
      <text>NFL Radio</text>
      <action>EXEC /usr/bin/sipie_myth play siriusnflradio</action>
   </button>

   <button>
      <type>TV_DELETE</type>
      <text>Stop Listening</text>
      <action>EXEC /usr/bin/sipie_myth kill</action>
   </button>

</mythmenu>

EDIT: Alternate Menu Options
After reviewing the channel guide provided by Sirius, I decided i wanted a repeatable way to update the channels provided by Sirius. After looking at the Channel Guide, I noticed that not all channels were playable online. I wrote this script to scrap the Sirius channel listing and generate mythtv menus that accounts for the channels that are playable online as well as breaking the channels out by category and genre. This is my first python script, so please go easy on it.

Instructions:
1. Setup all requirements as stated above and test to verify that you can hear music
2. Execute the script below in any directory (this script requires python lxml bindings)
3. copy the xml files to /usr/share/mythtv if not executing the script in that directory.
4. Modify an existing menu to open siriusmenu.xml as indicated above.

Output Hierarchy

  siriusmenu.xml
    |---Category
      |---Genre
        |---channels listed here
      |---Genre
        |---channels listed here
    |---Category
      |---Genre
        |---channels listed here
      |---Genre
        |---channels listed here
    |---Category
      |---Genre
        |---channels listed here
      |---Genre
        |---channels listed here
    |---Category
      |---Genre
        |---channels listed here
      |---Genre
        |---channels listed here

Script

from lxml import etree
import lxml.html
import os.path
import urllib2

# Open a url and read the page
url = 'http://www.sirius.com/servlet/ContentServer?pagename=Sirius/CachedPage&c=ChannelLineup&cid=1218563499691'
res = urllib2.urlopen(url)
page = res.read();

# get html 
html = lxml.html.fromstring(page)

# get channel table
channel_table = html.get_element_by_id('channel_guide_tbody')
#print(channel_table.tag)

table = []
for tr in channel_table:
    #print(tr.tag)
    row = []
    for td in tr:
        if len(td) == 1:
            a = td[0]
            if a.get("title") == 'Listen':
                row.append(a.get("href"))
                row.append('Listen')
            elif len(a) == 1:
                span = a[0]
                row.append(span.text)
            else:
                row.append(a.text)
        else:
            row.append(td.text)
    if row[-1] is not None:
        table.append(row)
        #print(row)
#print('\n')

categories = []
genres = []
channels = []
for row in table:
    channel = row[2]
    name = row[3]
    category = row[4]
    genre = row[5]
    description = row[6]
    url = row[7]

    # clean the url
    url = url.replace("','','');", '').replace("javascript:%20launchPlayer('",'')
    words = url.split(",", 1)
    url = words[1]
    url = url[1:]
    #print(url)

    # build categories
    if not category in categories:
        categories.append(category)
        
        new = [category]
        genres.append(new)

    # build genres
    for cat in genres:
        if cat[0] == category and not genre in cat:
            cat.append(genre)

            new = [genre]
            channels.append(new)

    # build the channels
    for chan in channels:
        if chan[0] == genre:
            chan.append([channel + "-" + name, url])
     
categories.sort()
#print(categories)
#print('\n')

#print(genres)
#print('\n')

#print(channels)
#print('\n')

categories_xml = etree.Element("mythmenu", name="SIRIUS")
for category in categories:
    button = etree.SubElement(categories_xml, "button")
    menu_type = etree.SubElement(button, "type")
    menu_type.text = "MUSIC"
    menu_text = etree.SubElement(button, "text")
    menu_text.text = category
    menu_action = etree.SubElement(button, "action")
    menu_action.text = "MENU sirius_" + category.replace('/', '-').lower() + ".xml"

button = etree.SubElement(categories_xml, "button")
menu_type = etree.SubElement(button, "type")
menu_type.text = "TV_DELETE"
menu_text = etree.SubElement(button, "text")
menu_text.text = "Stop Listening"
menu_action = etree.SubElement(button, "action")
menu_action.text = "EXEC /usr/bin/sipie_myth kill"

#print(etree.tostring(categories_xml, pretty_print=True))

categories_file = "siriusmenu.xml"
FILE = open(categories_file, "w")
FILE.write(etree.tostring(categories_xml, pretty_print=True))
FILE.close()

for row in genres:
    genre_xml = etree.Element("mythmenu", name="SIRIUS-" + row[0].replace('/', '-'))
    for genre in row:
        if genre in categories:
            continue

        button = etree.SubElement(genre_xml, "button")
        menu_type = etree.SubElement(button, "type")
        menu_type.text = "MUSIC"
        menu_text = etree.SubElement(button, "text")
        menu_text.text = genre
        menu_action = etree.SubElement(button, "action")
        menu_action.text = "MENU sirius_" + genre.replace('/', '-').replace(' ', '_').replace('&', 'and').replace('\'', '').lower() + ".xml"

    button = etree.SubElement(genre_xml, "button")
    menu_type = etree.SubElement(button, "type")
    menu_type.text = "TV_DELETE"
    menu_text = etree.SubElement(button, "text")
    menu_text.text = "Stop Listening"
    menu_action = etree.SubElement(button, "action")
    menu_action.text = "EXEC /usr/bin/sipie_myth kill"

    #print(etree.tostring(genre_xml, pretty_print=True))

    genre_file = "sirius_" + row[0].replace('/', '-').lower() + ".xml"
    FILE = open(genre_file, "w")
    FILE.write(etree.tostring(genre_xml, pretty_print=True))
    FILE.close()

for row in channels:
    channel_xml = etree.Element("mythmenu", name="SIRIUS-" + row[0].replace('/', '-'))
    button = etree.SubElement(channel_xml, "button")
    menu_type = etree.SubElement(button, "type")
    menu_type.text = "TV_DELETE"
    menu_text = etree.SubElement(button, "text")
    menu_text.text = "Stop Listening"
    menu_action = etree.SubElement(button, "action")
    menu_action.text = "EXEC /usr/bin/sipie_myth kill"

    for channel in row:
        #print(channel)
        found = "false"
        for genre in genres:
            #print(genre)
            if channel in genre:
                found = "true"
        
        if found == "true":
            continue

        button = etree.SubElement(channel_xml, "button")
        menu_type = etree.SubElement(button, "type")
        menu_type.text = "MUSIC"
        menu_text = etree.SubElement(button, "text")
        menu_text.text = channel[0]
        menu_action = etree.SubElement(button, "action")
        menu_action.text = "EXEC /usr/bin/sipie_myth play " + channel[1]

    button = etree.SubElement(channel_xml, "button")
    menu_type = etree.SubElement(button, "type")
    menu_type.text = "TV_DELETE"
    menu_text = etree.SubElement(button, "text")
    menu_text.text = "Stop Listening"
    menu_action = etree.SubElement(button, "action")
    menu_action.text = "EXEC /usr/bin/sipie_myth kill"

    #print(etree.tostring(channel_xml, pretty_print=True))

    channel_file = "sirius_" + row[0].replace('/', '-').replace(' ', '_').replace('&', 'and').replace('\'', '').lower() + ".xml"
    FILE = open(channel_file, "w")
    FILE.write(etree.tostring(channel_xml, pretty_print=True))
    FILE.close()

--Dmfrey 05:43, 20 December 2008 (UTC)

Tag definitions

OK, now the tag definitions for the above xml file

  • "button" tells the theme to paint a button on the screen
  • "type" tells the theme what image to use for the button
  • "text" tells MythTV what button text to put on the screen
  • "action" tells MythTV what to do when you select the button

Why it works

You have to wrap sipie_myth around the stream because when MythTV runs the EXEC it waits for a response before it releases the button. sipie_myth simply kicks off the stream and tells MythTV that it's finished executing. You are going to have to modify the XML menu files if you want to add another language tag or if you want to add additional channels.

Additional Setup and Usage Notes

  1. Be patient after you choose a stream as sometimes it will take 30 - 45 seconds to start playing
  2. The stream will play until you choose the "Stop Listening" button on the GUI (or sipie_myth kill is executed)
  3. When you're listening to a stream, it locks up the audio output device for MythTV which can be good and it can be bad, see below.
GOOD
  • If you want to watch a Football game on TV and listen to the commentators from Sirius instead of the TV commentators
BAD
  • If you wanted to play 2 audio streams at once to make your own mix tapes. ;-)

Screen shots

The new default main menu: Main Menu

The Sirius stream selection menu: Sirius Menu

Showing the song name in a pop-up w/ icon: Sirius song name pop-up