#!/usr/bin/perl ############################################# # Description: Setup and create the MythTV menus for the xamp XM streamer script # # Author: Jarett # Last Updated: Nov 27st, 2007 # Version: 0.1.0 # # This script must be run _after_ the "xamp setup" bash script is # run, because it depends on the user's config file being there # # Please send me bugs if you find them and I'll do what I can ############################################# use strict; use warnings; ############################################################################## # CONFIGURATION # if DISPLAY_ORDER is 'custom' in the XM config file, this is the order the # menu will be in as long as the categories match; any categories not in this list # will be put into the menu after those defined in the order they're pulled in our @CUSTOM_ORDER = ("Pop and Hits","XM Exclusives","Jazz and Blues","Classical","Rock","Country"); # Favorite channels to display on the main menu (NOT IMPLEMENTED YET) #our $FAVORITES = (); # debug variable - will print output to stdout for debugging our $DEBUG = 1; ############################################################################## # Shouldn't need to modify anything beyond here, unless you've specially # installed MythTV or something... (which is probably a lot of people) # The directory where the user's config file is stored our $XM_HOME = $ENV{'HOME'} ."/.xmonline"; our $CONFIG = $ENV{'HOME'} ."/.xmonline/config"; # MythTV Home directory of the user who runs mythfrontend our $MYTH_HOME = $ENV{'HOME'} ."/.mythtv"; # The location where MythTV stores the media XML menu by default # NOTE: this gets modified later on based on what is stored in the # user's config file ($CONFIG), if something is there; # this is only a default value if the config value is missing my $MYTH_XML_DIR = "/usr/share/mythtv"; # the name of the MythTV library xml file my $MYTH_LIBRARY_FILE = "library.xml"; # Order to display - 'alpha', 'custom', or 'orderPulled'<-(default) # NOTE: this gets modified later on based on what is stored in the # user's config file ($CONFIG) my $DISPLAY_ORDER = ""; # XM special URL vars my $XM_LISTINGS_URL = "http://xmro.xmradio.com/xstream/service/account/channel_lineup.jsp"; my $XM_LOGIN_URL = "http://xmro.xmradio.com/xstream/login_servlet.jsp"; # tmp files my $XM_COOKIE_FILE = $XM_HOME."/mythMenuSetup.cookies"; my $XM_OUTPUT_FILE = $XM_HOME."/mythMenuSetup.out"; # hopefully where the xamp scripts are my $XAMP_DIR = ""; ############################################################################## my $password = ""; my $username = ""; # grab the username and password from the users XM Config File if(! -r $CONFIG) { print "ERROR: Could not read from the XM config file! Have you run the 'xamp setup' script yet? Config file specified at: ${CONFIG}\n"; exit 1; } else { # read in all of the lines from the config file open(CONFIG, "<$CONFIG"); my @lines = ; close(CONFIG); # get the config vars from the config file foreach my $line (@lines) { chomp($line); # parse them if ($line =~ /^PASS/) { $password = $line; $password =~ s/^PASS=//; } elsif ($line =~ /^USER/) { $username = $line; $username =~ s/^USER=//; } elsif ($line =~/^XAMP_DIR/) { $XAMP_DIR = $line."/"; $XAMP_DIR =~ s/^XAMP_DIR=//; } elsif ($line =~/^MYTH_XML_DIR/) { $MYTH_XML_DIR = $line."/"; $MYTH_XML_DIR =~ s/^MYTH_XML_DIR=//; } elsif ($line =~/^DISPLAY_ORDER/) { $DISPLAY_ORDER = $line; $DISPLAY_ORDER =~ s/^DISPLAY_ORDER=//; } } print "XM LOGIN CREDINTIALS:\n\tUser: $username\n\tPass: $password\n" if $DEBUG; ##################### # change directories to the XM_HOME chdir($XM_HOME); # define the curl command to login to the XM site, and run it my $loginCurlCmd = "curl -s -c $XM_COOKIE_FILE -d \"user_id=$username\" -d \"pword=$password\" $XM_LOGIN_URL > $XM_OUTPUT_FILE"; print "Running Cmd to login: \n\t$loginCurlCmd\n" if $DEBUG; my $out = system($loginCurlCmd); # check output if($out != 0) { print "ERROR: Problem while trying to login to the XM site with the 'curl' command, output: ".$!."\n"; exit 1; } # check for an invalid username or password open(OUT, "<$XM_OUTPUT_FILE"); @lines = ; close(OUT); unlink($XM_OUTPUT_FILE); foreach my $line (@lines) { if($line =~ /Invalid email and\/or password/) { print "ERROR: Invalid email and/or password\n"; exit 1 } } ##################### # now grab the HTML page with all of the listings on it my $getListingsCurlCmd = "curl -s -b $XM_COOKIE_FILE -d \"\" \"$XM_LISTINGS_URL\" > $XM_OUTPUT_FILE"; print "\nRunning Cmd to get listings: \n\t$getListingsCurlCmd\n" if $DEBUG; $out = system($getListingsCurlCmd); # check output if($out != 0) { print "ERROR: Problem while trying to grab listings from the XM site with the 'curl' command, output: ".$!."\n"; exit 1; } # parse those listings, and return a hash of: category -> channelNum => channelName print "\nPARSE HTML LISTINGS\n" if $DEBUG; my $mythXmMenuHash = parseHtmlListingsHtml($XM_OUTPUT_FILE); my %mythXmMenuHash = %$mythXmMenuHash; ##################### # change to the user's MythTV home dir chdir($MYTH_HOME); # if the library XML file does not exist yet in the user's MythTV dir, make it if(! -e $MYTH_LIBRARY_FILE) { print "\"$MYTH_LIBRARY_FILE\" does not exist in user's MythTV home dir ($MYTH_HOME), so we will copy it over\n" if $DEBUG; # make sure the library.xml file exists to copy if(! -e "${MYTH_XML_DIR}/${MYTH_LIBRARY_FILE}") { print "ERROR: Could not find the the MythTV \"$MYTH_LIBRARY_FILE\" file at: \"${MYTH_XML_DIR}/${MYTH_LIBRARY_FILE}\"\n"; exit 1; } # copy over the library.xml file so we can change it locally my $cpCmd = "cp \"${MYTH_XML_DIR}/${MYTH_LIBRARY_FILE}\" ./"; $out = system($cpCmd); if($out != 0) { print "ERROR: Problem coping the \"$MYTH_LIBRARY_FILE\" file at: \"${MYTH_XML_DIR}/${MYTH_LIBRARY_FILE}\" locally to: \"$MYTH_HOME\"\n"; exit 1; } } else { print "\"$MYTH_LIBRARY_FILE\" already exists in the user's MythTV home dir ($MYTH_HOME)\n" if $DEBUG; } ##################### # check if the library.xml file has the entry for "Stream XM" my $xmStreamFound = 0; open(FILE, "<${MYTH_LIBRARY_FILE}"); @lines = ; close(FILE); foreach my $line (@lines) { if($line =~ /Stream XM/) { $xmStreamFound = 1; } } # if we didn't find the "Stream XM" entry, make one at the end if(! $xmStreamFound) { print "\"$MYTH_LIBRARY_FILE\" does not have the \"Stream XM\" link, create it.\n" if $DEBUG; # rip contents from existing file open(LIB_XML, "<${MYTH_LIBRARY_FILE}"); @lines = ; close(LIB_XML); # write those contents to a tmp file, with the Stream XM link open(TMP_FILE, ">${MYTH_LIBRARY_FILE}.tmp"); foreach my $line (@lines) { # if this is the last line, write the Stream XM link first if($line =~ /\/mythmenu/) { print TMP_FILE "\t\n\n"; } # put the orig line into the TMP file print TMP_FILE $line; } close(TMP_FILE); # copy over the tmp file to the real one my $cpCmd = "cp -f \"${MYTH_LIBRARY_FILE}.tmp\" \"${MYTH_LIBRARY_FILE}\""; $out = system($cpCmd); if($out != 0) { print "ERROR: Problem coping the \"${MYTH_LIBRARY_FILE}.tmp\" file over \"$MYTH_LIBRARY_FILE\"\n"; exit 1; } unlink($MYTH_LIBRARY_FILE.".tmp"); } else { print "\"$MYTH_LIBRARY_FILE\" already has the \"Stream XM\" link\n" if $DEBUG; } ##################### # SORT THE CATEGORIES print "\nORDERING XM MENU - $DISPLAY_ORDER\n" if $DEBUG; my %newMythXmMenuHash = (); my $counter = 0; # alphabetic sorting if($DISPLAY_ORDER eq "alphabetical") { for my $cat (sort alphabetically keys %mythXmMenuHash) { my $hash = $mythXmMenuHash{$cat}; my %hash = %$hash; delete($hash{'ORDER'}); while (my($chanNum, $chanName) = each(%hash)) { $newMythXmMenuHash{$counter}{$cat}{$chanNum} = $chanName; } $counter++; } # custom sorting } elsif ($DISPLAY_ORDER eq "custom") { if(scalar(@CUSTOM_ORDER) > 0) { foreach my $custCat (@CUSTOM_ORDER) { # if the custCat exists in the hash, stick it in if($mythXmMenuHash{$custCat}) { print "Custom Order: $custCat\n" if $DEBUG; $newMythXmMenuHash{$counter}{$custCat} = $mythXmMenuHash{$custCat}; delete($newMythXmMenuHash{$counter}{$custCat}{'ORDER'}); delete($mythXmMenuHash{$custCat}); } else { print "WARNING: Custom Order category \"$custCat\" not found in available categories.\n"; } $counter++; } # place in all the rest of the categories while (my($cat, $hash) = each(%mythXmMenuHash)) { print "Filling with rest: $cat\n" if $DEBUG; my %hash = %$hash; # add the counter to the pulled order so they end up _after_ the custom order categories my $pulledOrder = $counter + $hash{'ORDER'}; delete($hash{'ORDER'}); while (my($chanNum, $chanName) = each(%hash)) { $newMythXmMenuHash{$pulledOrder}{$cat}{$chanNum} = $chanName; } } } # leave in the order they were pulled in } else { while (my($cat, $hash) = each(%mythXmMenuHash)) { my %hash = %$hash; my $pulledOrder = $hash{'ORDER'}; delete($hash{'ORDER'}); print "Adding in category: $pulledOrder - $cat\n" if $DEBUG; while (my($chanNum, $chanName) = each(%hash)) { $newMythXmMenuHash{$pulledOrder}{$cat}{$chanNum} = $chanName; } } } ##################### # open up the XM_menu file to fill print "\nREWRITE THE XM XML FILES\n" if $DEBUG; open(XM_MENU, ">XM_menu.xml"); print XM_MENU "\n"; # go through content and write XML files for my $cnt (sort numerically keys %newMythXmMenuHash) { my $catHash = $newMythXmMenuHash{$cnt}; my %catHash = %$catHash; while (my($cat, $chanHash) = each(%catHash)) { print "Adding Category: $cnt\t\t$cat\n" if $DEBUG; my %chanHash = %$chanHash; # strip spaces for the file name my $catFileName = "XM_".$cat; $catFileName =~ s/\s//g; # write this entry in the XM_menu print XM_MENU "\n\t\n"; # start the file off open(XM_XML, ">$catFileName"); print XM_XML "\n"; # print all channels that are apart of this category for my $chanNum ( sort numerically keys %chanHash) { my $chanName = $chanHash{$chanNum}; print XM_XML "\n\t\n"; } # close this menu print XM_XML "\n"; close(XM_XML); } } # finish the XM Menu with the Stop Listening entry print XM_MENU "\n\t\n"; # close the XM_MENU XML file print XM_MENU "\n"; close(XM_MENU); # finally, get rid of the cookie and output files unlink($XM_COOKIE_FILE); unlink($XM_OUTPUT_FILE); print "\nALL DONE\n" if $DEBUG; } ##################### # parseHtmlListingsHtml # # with the given file name, parse the HTML file into # a hash that has all XM online channels in their # respective group (ie 'Country', 'Pop & Hits', etc) sub parseHtmlListingsHtml { my $htmlFile = shift; my %listingsHash = (); my $chanNum = ""; my $chanName = ""; my $chanCat = ""; my $count = 0; # open the file and grab it's contents and put them in an array open(FILE, "<$htmlFile"); my @lines = ; close($htmlFile); # go through each line in the HTML file foreach my $line (@lines) { chomp($line); # we have the channel number and name, this line completes the # trio of different data we're looking for if($chanNum ne "" && $chanName ne "" && $chanCat ne "") { #$chanCat = $line; #$chanCat =~ s/^.*?tLgBlack.*?\>(.*?)\<.*/$1/; # load up the hash with the data (if everything is complete) #if(($chanCat ne "") && ($chanName ne "") && ($chanNum ne "")) { $count++; $listingsHash{$chanCat}{$chanNum} = $chanName; $listingsHash{$chanCat}{'ORDER'} = $count; # keep track of the order they were pulled in print "FOUND CHANNEL: $chanNum - $chanName - $chanCat\n\n" if $DEBUG; #} # reset these variables to nothing $chanNum = ""; $chanName = ""; $chanCat = ""; # we have the channel number, now this line has the name # we found a brand new channel } elsif( $line =~ /tLgRed.*\/\w+\/index\.xmc"\>(.*?)\(.*?)\(.*?)\s+\(.*?)\<.*/$1/; $chanName =~ s/&/and/g; $chanName =~ s/'/'/g; $chanName =~ s/ / /g; print "CHANNEL NAME FOUND = $chanName\n" if $DEBUG; } } return \%listingsHash; } # used to sort with sub numerically { $a <=> $b } sub alphabetically {lc $a cmp lc $b}